Skip to content

Commit 75f3f0f

Browse files
iommu.c: rework initialization function
Test for INVALIDATE_ALL completion is now done inside the initialization function, instead of main.c. Function names were changed to better describe what the function does. Comment describing window in anti-DMA security was updated with new finds. Some bugs were fixed: - EventLogInt wasn't properly cleared (it is write-1-to-clear bit) - CmdBufEn was set on transition to the kernel, which didn't clear it before setting up head/tail pointers, which in turn lead to hang (it is listed as undefined behavior in IOMMU specification) - Command buffer tail/head pointer registers were incorrectly masked Signed-off-by: Krystian Hebel <[email protected]>
1 parent 8c4fea6 commit 75f3f0f

File tree

3 files changed

+85
-33
lines changed

3 files changed

+85
-33
lines changed

include/iommu.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#define IOMMU_DTE_Q0_IW (1ULL << 62)
2929

3030
#define IOMMU_DTE_Q1_I (1ULL << (96 - 64))
31+
#define IOMMU_DTE_Q1_SE (1ULL << (97 - 64))
3132

3233
#define IOMMU_DTE_Q2_IV (1ULL << (128 - 128))
3334
#define IOMMU_DTE_Q2_IG (1ULL << (133 - 128))
@@ -78,8 +79,7 @@
7879
#define IOMMU_CR_BlkStopMrkEn (1ULL << 41)
7980
#define IOMMU_CR_PprAutoRspAon (1ULL << 42)
8081

81-
#define IOMMU_CR_ENABLE_ALL_MASK (IOMMU_CR_IommuEn | \
82-
IOMMU_CR_HtTunEn | \
82+
#define IOMMU_CR_ENABLE_ALL_MASK (IOMMU_CR_HtTunEn | \
8383
IOMMU_CR_EventLogEn | \
8484
IOMMU_CR_EventIntEn | \
8585
IOMMU_CR_ComWaitIntEn | \
@@ -104,6 +104,8 @@
104104

105105
#define IOMMU_EF_IASup (1ULL << 6)
106106

107+
#define IOMMU_SR_EventLogInt (1ULL << 1)
108+
107109
#define COMPLETION_WAIT 1
108110
#define INVALIDATE_DEVTAB_ENTRY 2
109111
#define INVALIDATE_IOMMU_PAGES 3
@@ -135,6 +137,6 @@ extern iommu_command_t command_buf[2];
135137
void disable_memory_protection(void);
136138

137139
u32 iommu_locate(void);
138-
u32 iommu_load_device_table(u32 cap, volatile u64 *completed);
140+
int iommu_init(u32 cap);
139141

140142
#endif /* __IOMMU_H__ */

iommu.c

Lines changed: 62 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,25 @@ static void send_command(u64 *mmio_base, iommu_command_t cmd, u32 *idx)
3939
_u(&command_buf[*idx]) - (_u(&command_buf) & ~0xfff);
4040
}
4141

42-
u32 iommu_load_device_table(u32 cap, volatile u64 *completed)
42+
int iommu_init(u32 cap)
4343
{
4444
u64 *mmio_base;
45+
volatile u64 completed __attribute__ ((aligned (8))) = 0;
4546
u32 low, hi, i, idx = 0;
4647
iommu_command_t cmd = {0};
4748

48-
for (i = 0; i < ARRAY_SIZE(device_table); i++)
49+
for (i = 0; i < ARRAY_SIZE(device_table); i++) {
4950
device_table[i].a = IOMMU_DTE_Q0_V + IOMMU_DTE_Q0_TV;
51+
/*
52+
* Devices (e.g. USB with legacy keyboard emulation) on some platforms
53+
* can be quite noisy, this limits number of events logged to one page
54+
* fault per device.
55+
*
56+
* NB: there are erratas due to which all entries may still be logged
57+
* despite enabled suppression.
58+
*/
59+
device_table[i].b = IOMMU_DTE_Q1_SE;
60+
}
5061

5162
pci_read(0, IOMMU_PCI_BUS,
5263
PCI_DEVFN(IOMMU_PCI_DEVICE, IOMMU_PCI_FUNCTION),
@@ -70,7 +81,7 @@ u32 iommu_load_device_table(u32 cap, volatile u64 *completed)
7081
print_u64(mmio_base[IOMMU_MMIO_STATUS_REGISTER]);
7182
print("IOMMU_MMIO_STATUS_REGISTER\n");
7283

73-
/* Disable IOMMU and all its features */
84+
/* Disable all features, but don't touch IommuEn */
7485
mmio_base[IOMMU_MMIO_CONTROL_REGISTER] &= ~IOMMU_CR_ENABLE_ALL_MASK;
7586
smp_wmb();
7687

@@ -100,7 +111,8 @@ u32 iommu_load_device_table(u32 cap, volatile u64 *completed)
100111
* command_buf[] to begin with, but we do save almost 4k of space,
101112
* 1/16th of that available to us.
102113
*/
103-
mmio_base[IOMMU_MMIO_COMMAND_BUF_BA] = (u64)(_u(command_buf) & ~0xfff)| (0x9ULL << 56);
114+
mmio_base[IOMMU_MMIO_COMMAND_BUF_BA] = (u64)(_u(command_buf) & ~0xfff) |
115+
(0x9ULL << 56);
104116
mmio_base[IOMMU_MMIO_COMMAND_BUF_HEAD] =
105117
mmio_base[IOMMU_MMIO_COMMAND_BUF_TAIL] = _u(command_buf) & 0xff0;
106118

@@ -121,8 +133,8 @@ u32 iommu_load_device_table(u32 cap, volatile u64 *completed)
121133
print_u64(mmio_base[IOMMU_MMIO_EVENT_LOG_BA]);
122134
print("IOMMU_MMIO_EVENT_LOG_BA\n");
123135

124-
/* Clear EventLogInt set by IOMMU not being able to read command buffer */
125-
mmio_base[IOMMU_MMIO_STATUS_REGISTER] &= ~2;
136+
/* Write 1 to clear EventLogInt set by IOMMU being unable to read command */
137+
mmio_base[IOMMU_MMIO_STATUS_REGISTER] &= IOMMU_SR_EventLogInt;
126138
smp_wmb();
127139
mmio_base[IOMMU_MMIO_CONTROL_REGISTER] |= IOMMU_CR_CmdBufEn | IOMMU_CR_EventLogEn;
128140
smp_wmb();
@@ -145,16 +157,58 @@ u32 iommu_load_device_table(u32 cap, volatile u64 *completed)
145157
print("IOMMU_MMIO_STATUS_REGISTER\n");
146158

147159
/* Write to a variable inside SLB (does not work in the first call) */
148-
cmd.u0 = _u(completed) | 1;
149-
/* This should be '_u(completed)>>32', but SLB can't be above 4GB anyway */
160+
cmd.u0 = _u(&completed) | 1;
161+
/* This should be '_u(&completed)>>32', but SLB can't be above 4GB anyway */
150162
cmd.u1 = 0;
151163

152164
cmd.opcode = COMPLETION_WAIT;
153165
cmd.u2 = 0x656e6f64; /* "done" */
154166
send_command(mmio_base, cmd, &idx);
155167

168+
/* Make sure tail pointer is updated before checking for completion */
169+
smp_wmb();
170+
171+
print("Flushing IOMMU cache");
172+
while ( !completed ) {
173+
print(".");
174+
175+
/*
176+
* On the first run, EventLogInt will be set, but IOMMU won't be able to
177+
* write to the log due to active DEV. After that IOMMU stops fetching
178+
* new commands, so 'completed' won't be ever set. Check if EventCode
179+
* has the initial value of 0 and return instead of waiting indefinitely
180+
* for 'completed'.
181+
*/
182+
if ( mmio_base[IOMMU_MMIO_STATUS_REGISTER] & IOMMU_SR_EventLogInt ) {
183+
if ( event_log[7] == 0 )
184+
return 0;
185+
}
186+
}
187+
print("\n");
188+
189+
if ( mmio_base[IOMMU_MMIO_STATUS_REGISTER] & IOMMU_SR_EventLogInt ) {
190+
print("IOMMU event log not empty:\n");
191+
hexdump(event_log, 512);
192+
}
193+
194+
/*
195+
* If Command Buffer Head Pointer Register is written to by software while
196+
* CmdBufRun = 1b, the IOMMU behavior is undefined, similarly for Event Log
197+
* Tail Pointer and EventLogRun = 1b. DLME may not be so kind to check for
198+
* this, so stop all parsing and logging here, but keep IOMMU enabled.
199+
*
200+
* NB: IOMMU specification doesn't mention undefined behavior when changing
201+
* Command Buffer Base Address, although it does so for Event Log Base
202+
* Address. This may be an overlook, but it doesn't change anything since
203+
* all parsing and logging is disabled.
204+
*/
205+
mmio_base[IOMMU_MMIO_CONTROL_REGISTER] &= ~IOMMU_CR_ENABLE_ALL_MASK;
206+
smp_wmb();
207+
156208
print_u64(mmio_base[IOMMU_MMIO_STATUS_REGISTER]);
157209
print("IOMMU_MMIO_STATUS_REGISTER\n");
158210

211+
print("IOMMU set\n");
212+
159213
return 0;
160214
}

main.c

Lines changed: 18 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -117,10 +117,9 @@ static void do_dma(void)
117117
}
118118
#endif
119119

120-
static void iommu_setup(void)
120+
static void dma_protection_setup(void)
121121
{
122122
u32 iommu_cap;
123-
volatile u64 iommu_done __attribute__ ((aligned (8))) = 0;
124123

125124
#ifdef TEST_DMA
126125
memset(_p(1), 0xcc, 0x20); //_p(0) gives a null-pointer error
@@ -150,19 +149,24 @@ static void iommu_setup(void)
150149
* When IOMMU is trying to read a command from buffer located in SLB it
151150
* receives COMMAND_HARDWARE_ERROR (master abort).
152151
*
153-
* Luckily, after that error it enters a fail-safe state in which all
154-
* operations originating from devices are blocked. The IOMMU itself can
155-
* still access the memory, so after the SLB protection is lifted, it can
156-
* try to read the data located inside SLB and set up a proper protection.
152+
* After that error IOMMU enters a state in which protections use whatever
153+
* settings are cached. In case of cold boot, all operations originating
154+
* from devices are blocked. The IOMMU itself can still access the memory,
155+
* so after the SLB protection is lifted, it can try to read the commands
156+
* located inside SLB and set up a proper protection.
157+
*
158+
* The same isn't true for IOMMU that was already used earlier. It may
159+
* allow devices to access the SLB memory between time when DEV is disabled
160+
* and when IOMMU cache is flushed. As IOMMU command table is located in
161+
* that memory, the invalidation command may never be executed...
157162
*
158163
* TODO: split iommu_load_device_table() into two parts, before and after
159-
* DEV disabling
164+
* DEV disabling, to minimize vulnerability time window
160165
*
161-
* TODO2: check if IOMMU always blocks the devices, even when it was
162-
* configured before SKINIT
166+
* TODO2: check what IOMMU_PGFSM_CONFIG does and if it exists in newer CPUs
163167
*/
164168

165-
if ( iommu_cap == 0 || iommu_load_device_table(iommu_cap, &iommu_done) )
169+
if ( iommu_cap == 0 || iommu_init(iommu_cap) )
166170
{
167171
if ( iommu_cap )
168172
print("IOMMU disabled by a firmware, please check your settings\n");
@@ -199,12 +203,7 @@ static void iommu_setup(void)
199203
hexdump(_p(0), 0x30);
200204
#endif
201205

202-
iommu_load_device_table(iommu_cap, &iommu_done);
203-
print("Flushing IOMMU cache");
204-
while ( !iommu_done )
205-
print(".");
206-
207-
print("\nIOMMU set\n");
206+
iommu_init(iommu_cap);
208207
}
209208

210209
#ifdef TEST_DMA
@@ -253,15 +252,12 @@ asm_return_t skl_main(void)
253252

254253
/*
255254
* Now in 64b mode, paging is setup. This is the launching point. We can
256-
* now do what we want. First order of business is to setup
257-
* DEV to cover memory from the start of bzImage to the end of the SKL
258-
* "kernel". At the end, trampoline to the PM entry point which will
259-
* include the Secure Launch stub.
255+
* now do what we want. At the end, trampoline to the PM entry point which
256+
* will include the Secure Launch stub.
260257
*/
261-
pci_init();
262258

263259
/* Disable memory protection and setup IOMMU */
264-
iommu_setup();
260+
dma_protection_setup();
265261

266262
/*
267263
* TODO Note these functions can fail but there is no clear way to

0 commit comments

Comments
 (0)