1 #include <xen/acpi.h>
2 #include <xen/domain_page.h>
3 #include <xen/efi.h>
4 #include <xen/init.h>
5 #include <xen/iommu.h>
6 #include <xen/lib.h>
7 #include <xen/param.h>
8 #include <xen/pfn.h>
9 #include <xen/sched.h>
10 #include <xen/types.h>
11
12 #include <asm/e820.h>
13 #include <asm/fixmap.h>
14 #include <asm/msr.h>
15 #include <asm/page.h>
16 #include <asm/processor.h>
17 #include <asm/setup.h>
18 #include <asm/tboot.h>
19 #include <asm/trampoline.h>
20
21 #include <crypto/vmac.h>
22
23 /* tboot=<physical address of shared page> */
24 static unsigned long __initdata opt_tboot_pa;
25 integer_param("tboot", opt_tboot_pa);
26
27 /* Global pointer to shared data; NULL means no measured launch. */
28 tboot_shared_t *g_tboot_shared;
29
30 static vmac_t domain_mac; /* MAC for all domains during S3 */
31 static vmac_t xenheap_mac; /* MAC for xen heap during S3 */
32 static vmac_t frametable_mac; /* MAC for frame table during S3 */
33
34 /* used by tboot_protect_mem_regions() and/or tboot_parse_dmar_table() */
35 static uint64_t __initdata txt_heap_base, __initdata txt_heap_size;
36 static uint64_t __initdata sinit_base, __initdata sinit_size;
37
38 static bool __ro_after_init is_vtd;
39
40 /*
41 * TXT configuration registers (offsets from TXT_{PUB, PRIV}_CONFIG_REGS_BASE)
42 */
43
44 #define TXT_PUB_CONFIG_REGS_BASE 0xfed30000
45 #define TXT_PRIV_CONFIG_REGS_BASE 0xfed20000
46
47 /* # pages for each config regs space - used by fixmap */
48 #define NR_TXT_CONFIG_PAGES ((TXT_PUB_CONFIG_REGS_BASE - \
49 TXT_PRIV_CONFIG_REGS_BASE) >> PAGE_SHIFT)
50
51 /* offsets from pub/priv config space */
52 #define TXTCR_SINIT_BASE 0x0270
53 #define TXTCR_SINIT_SIZE 0x0278
54 #define TXTCR_HEAP_BASE 0x0300
55 #define TXTCR_HEAP_SIZE 0x0308
56
57 #define SHA1_SIZE 20
58 typedef uint8_t sha1_hash_t[SHA1_SIZE];
59
60 typedef struct __packed {
61 uint32_t version; /* currently 6 */
62 sha1_hash_t bios_acm_id;
63 uint32_t edx_senter_flags;
64 uint64_t mseg_valid;
65 sha1_hash_t sinit_hash;
66 sha1_hash_t mle_hash;
67 sha1_hash_t stm_hash;
68 sha1_hash_t lcp_policy_hash;
69 uint32_t lcp_policy_control;
70 uint32_t rlp_wakeup_addr;
71 uint32_t reserved;
72 uint32_t num_mdrs;
73 uint32_t mdrs_off;
74 uint32_t num_vtd_dmars;
75 uint32_t vtd_dmars_off;
76 } sinit_mle_data_t;
77
tboot_copy_memory(unsigned char * va,uint32_t size,unsigned long pa)78 static void __init tboot_copy_memory(unsigned char *va, uint32_t size,
79 unsigned long pa)
80 {
81 unsigned long map_base = 0;
82 unsigned char *map_addr = NULL;
83 unsigned int i;
84
85 for ( i = 0; i < size; i++ )
86 {
87 if ( map_base != PFN_DOWN(pa + i) )
88 {
89 map_base = PFN_DOWN(pa + i);
90 set_fixmap(FIX_TBOOT_MAP_ADDRESS, map_base << PAGE_SHIFT);
91 map_addr = fix_to_virt(FIX_TBOOT_MAP_ADDRESS);
92 }
93 va[i] = map_addr[pa + i - (map_base << PAGE_SHIFT)];
94 }
95 }
96
tboot_probe(void)97 void __init tboot_probe(void)
98 {
99 tboot_shared_t *tboot_shared;
100 static const uuid_t __initconst tboot_shared_uuid = TBOOT_SHARED_UUID;
101
102 /* Look for valid page-aligned address for shared page. */
103 if ( !opt_tboot_pa || (opt_tboot_pa & ~PAGE_MASK) )
104 return;
105
106 /* Map and check for tboot UUID. */
107 set_fixmap(FIX_TBOOT_SHARED_BASE, opt_tboot_pa);
108 tboot_shared = fix_to_virt(FIX_TBOOT_SHARED_BASE);
109 if ( memcmp(&tboot_shared_uuid, &tboot_shared->uuid, sizeof(uuid_t)) )
110 return;
111
112 /* new tboot_shared (w/ GAS support, integrity, etc.) is not backwards
113 compatible */
114 if ( tboot_shared->version < 4 )
115 {
116 printk("unsupported version of tboot (%u)\n", tboot_shared->version);
117 return;
118 }
119
120 g_tboot_shared = tboot_shared;
121 printk("TBOOT: found shared page at phys addr %#lx:\n", opt_tboot_pa);
122 printk(" version: %d\n", tboot_shared->version);
123 printk(" log_addr: %#x\n", tboot_shared->log_addr);
124 printk(" shutdown_entry: %#x\n", tboot_shared->shutdown_entry);
125 printk(" tboot_base: %#x\n", tboot_shared->tboot_base);
126 printk(" tboot_size: %#x\n", tboot_shared->tboot_size);
127 if ( tboot_shared->version >= 6 )
128 printk(" flags: %#x\n", tboot_shared->flags);
129
130 /* these will be needed by tboot_protect_mem_regions() and/or
131 tboot_parse_dmar_table(), so get them now */
132
133 txt_heap_base = txt_heap_size = sinit_base = sinit_size = 0;
134 /* TXT Heap */
135 tboot_copy_memory((unsigned char *)&txt_heap_base, sizeof(txt_heap_base),
136 TXT_PUB_CONFIG_REGS_BASE + TXTCR_HEAP_BASE);
137 tboot_copy_memory((unsigned char *)&txt_heap_size, sizeof(txt_heap_size),
138 TXT_PUB_CONFIG_REGS_BASE + TXTCR_HEAP_SIZE);
139 /* SINIT */
140 tboot_copy_memory((unsigned char *)&sinit_base, sizeof(sinit_base),
141 TXT_PUB_CONFIG_REGS_BASE + TXTCR_SINIT_BASE);
142 tboot_copy_memory((unsigned char *)&sinit_size, sizeof(sinit_size),
143 TXT_PUB_CONFIG_REGS_BASE + TXTCR_SINIT_SIZE);
144 clear_fixmap(FIX_TBOOT_MAP_ADDRESS);
145 }
146
147 /* definitions from xen/drivers/passthrough/vtd/iommu.h
148 * used to walk through vtd page tables */
149 #define LEVEL_STRIDE (9)
150 #define PTE_NUM (1<<LEVEL_STRIDE)
151 #define dma_pte_present(p) (((p).val & 3) != 0)
152 #define dma_pte_addr(p) ((p).val & PAGE_MASK_4K)
153 #define agaw_to_level(val) ((val)+2)
154 struct dma_pte {
155 u64 val;
156 };
157
update_iommu_mac(vmac_ctx_t * ctx,uint64_t pt_maddr,int level)158 static void update_iommu_mac(vmac_ctx_t *ctx, uint64_t pt_maddr, int level)
159 {
160 int i;
161 struct dma_pte *pt_vaddr, *pte;
162 int next_level = level - 1;
163
164 if ( pt_maddr == 0 )
165 return;
166
167 pt_vaddr = (struct dma_pte *)map_domain_page(_mfn(paddr_to_pfn(pt_maddr)));
168 vmac_update((void *)pt_vaddr, PAGE_SIZE, ctx);
169
170 for ( i = 0; i < PTE_NUM; i++ )
171 {
172 pte = &pt_vaddr[i];
173 if ( !dma_pte_present(*pte) )
174 continue;
175
176 if ( next_level >= 1 )
177 update_iommu_mac(ctx, dma_pte_addr(*pte), next_level);
178 }
179
180 unmap_domain_page(pt_vaddr);
181 }
182
183 #define is_page_in_use(page) \
184 (page_state_is(page, inuse) || page_state_is(page, offlining))
185
186 /* Wipe ctx to ensure key is not left in memory. */
wipe_ctx(vmac_ctx_t * ctx)187 static void wipe_ctx(vmac_ctx_t *ctx)
188 {
189 memset(ctx, 0, sizeof(*ctx));
190 /*
191 * Make sure the compiler won't optimize out the memset(), for the local
192 * variable (at the call sites) going out of scope right afterwards.
193 */
194 asm volatile ( "" :: "m" (*ctx) );
195 }
196
tboot_gen_domain_integrity(const uint8_t key[TB_KEY_SIZE],vmac_t * mac)197 static void tboot_gen_domain_integrity(const uint8_t key[TB_KEY_SIZE],
198 vmac_t *mac)
199 {
200 struct domain *d;
201 struct page_info *page;
202 uint8_t nonce[16] = {};
203 vmac_ctx_t ctx;
204
205 vmac_set_key((uint8_t *)key, &ctx);
206 for_each_domain( d )
207 {
208 if ( !(d->options & XEN_DOMCTL_CDF_s3_integrity) )
209 continue;
210 printk("MACing Domain %u\n", d->domain_id);
211
212 nrspin_lock(&d->page_alloc_lock);
213 page_list_for_each(page, &d->page_list)
214 {
215 void *pg = __map_domain_page(page);
216 vmac_update(pg, PAGE_SIZE, &ctx);
217 unmap_domain_page(pg);
218 }
219 nrspin_unlock(&d->page_alloc_lock);
220
221 if ( is_iommu_enabled(d) && is_vtd )
222 {
223 const struct domain_iommu *dio = dom_iommu(d);
224
225 update_iommu_mac(&ctx, dio->arch.vtd.pgd_maddr,
226 agaw_to_level(dio->arch.vtd.agaw));
227 }
228 }
229
230 /* TODO: MAC all shadow / HAP page tables */
231
232 *mac = vmac(NULL, 0, nonce, NULL, &ctx);
233
234 wipe_ctx(&ctx);
235 }
236
tboot_gen_xenheap_integrity(const uint8_t key[TB_KEY_SIZE],vmac_t * mac)237 static void tboot_gen_xenheap_integrity(const uint8_t key[TB_KEY_SIZE],
238 vmac_t *mac)
239 {
240 unsigned long mfn;
241 uint8_t nonce[16] = {};
242 vmac_ctx_t ctx;
243
244 vmac_set_key((uint8_t *)key, &ctx);
245 for ( mfn = 0; mfn < max_page; mfn++ )
246 {
247 struct page_info *page = mfn_to_page(_mfn(mfn));
248
249 if ( !mfn_valid(_mfn(mfn)) )
250 continue;
251 if ( is_xen_fixed_mfn(_mfn(mfn)) )
252 continue; /* skip Xen */
253 if ( (mfn >= PFN_DOWN(g_tboot_shared->tboot_base - 3 * PAGE_SIZE))
254 && (mfn < PFN_UP(g_tboot_shared->tboot_base
255 + g_tboot_shared->tboot_size
256 + 3 * PAGE_SIZE)) )
257 continue; /* skip tboot and its page tables */
258
259 if ( is_page_in_use(page) && is_special_page(page) )
260 vmac_update(mfn_to_virt(mfn), PAGE_SIZE, &ctx);
261 }
262 *mac = vmac(NULL, 0, nonce, NULL, &ctx);
263
264 wipe_ctx(&ctx);
265 }
266
tboot_gen_frametable_integrity(const uint8_t key[TB_KEY_SIZE],vmac_t * mac)267 static void tboot_gen_frametable_integrity(const uint8_t key[TB_KEY_SIZE],
268 vmac_t *mac)
269 {
270 unsigned int sidx, eidx, nidx;
271 unsigned int max_idx = DIV_ROUND_UP(max_pdx, PDX_GROUP_COUNT);
272 uint8_t nonce[16] = {};
273 vmac_ctx_t ctx;
274
275 vmac_set_key((uint8_t *)key, &ctx);
276 for ( sidx = 0; ; sidx = nidx )
277 {
278 eidx = find_next_zero_bit(pdx_group_valid, max_idx, sidx);
279 nidx = find_next_bit(pdx_group_valid, max_idx, eidx);
280 if ( nidx >= max_idx )
281 break;
282 vmac_update((uint8_t *)pdx_to_page(sidx * PDX_GROUP_COUNT),
283 (eidx - sidx) * PDX_GROUP_COUNT * sizeof(*frame_table),
284 &ctx);
285 }
286 vmac_update((uint8_t *)pdx_to_page(sidx * PDX_GROUP_COUNT),
287 (max_pdx - sidx * PDX_GROUP_COUNT) * sizeof(*frame_table),
288 &ctx);
289
290 *mac = vmac(NULL, 0, nonce, NULL, &ctx);
291
292 wipe_ctx(&ctx);
293 }
294
tboot_shutdown(uint32_t shutdown_type)295 void tboot_shutdown(uint32_t shutdown_type)
296 {
297 mfn_t map_base;
298 uint32_t map_size;
299 int err;
300
301 g_tboot_shared->shutdown_type = shutdown_type;
302
303 /* Create identity map for tboot shutdown code. */
304 /* do before S3 integrity because mapping tboot may change xenheap */
305 map_base = maddr_to_mfn(g_tboot_shared->tboot_base);
306 map_size = PFN_UP(g_tboot_shared->tboot_size);
307
308 err = map_pages_to_xen(mfn_to_maddr(map_base), map_base, map_size,
309 __PAGE_HYPERVISOR);
310 if ( err != 0 )
311 {
312 printk("error (%#x) mapping tboot pages (mfns) @ %"PRI_mfn", %#x\n",
313 err, mfn_x(map_base), map_size);
314 return;
315 }
316
317 /* Disable interrupts as early as possible but not prior to */
318 /* calling map_pages_to_xen */
319 local_irq_disable();
320
321 /* if this is S3 then set regions to MAC */
322 if ( shutdown_type == TB_SHUTDOWN_S3 )
323 {
324 unsigned long s, e;
325
326 /*
327 * Xen regions for tboot to MAC. This needs to remain in sync with
328 * remove_xen_ranges().
329 */
330 g_tboot_shared->num_mac_regions = 3;
331 /* S3 resume code (and other real mode trampoline code) */
332 g_tboot_shared->mac_regions[0].start = bootsym_phys(trampoline_start);
333 g_tboot_shared->mac_regions[0].size = trampoline_end - trampoline_start;
334 /* hypervisor .text + .rodata */
335 g_tboot_shared->mac_regions[1].start = (uint64_t)__pa(&_stext);
336 g_tboot_shared->mac_regions[1].size = __2M_rodata_end - _stext;
337 /* hypervisor .data + .bss */
338 g_tboot_shared->mac_regions[2].start = (uint64_t)__pa(&__2M_rwdata_start);
339 g_tboot_shared->mac_regions[2].size = __2M_rwdata_end - __2M_rwdata_start;
340 if ( efi_boot_mem_unused(&s, &e) )
341 {
342 g_tboot_shared->mac_regions[2].size =
343 s - (unsigned long)__2M_rwdata_start;
344 g_tboot_shared->mac_regions[3].start = __pa(e);
345 g_tboot_shared->mac_regions[3].size =
346 (unsigned long)__2M_rwdata_end - e;
347 g_tboot_shared->num_mac_regions = 4;
348 }
349
350 /*
351 * MAC domains and other Xen memory
352 */
353 /* Xen has no better entropy source for MAC key than tboot's */
354 /* MAC domains first in case it perturbs xenheap */
355 tboot_gen_domain_integrity(g_tboot_shared->s3_key, &domain_mac);
356 tboot_gen_frametable_integrity(g_tboot_shared->s3_key, &frametable_mac);
357 tboot_gen_xenheap_integrity(g_tboot_shared->s3_key, &xenheap_mac);
358 }
359
360 /*
361 * Disable CET - tboot may not be built with endbr, and it doesn't support
362 * shadow stacks.
363 */
364 if ( read_cr4() & X86_CR4_CET )
365 {
366 wrmsrl(MSR_S_CET, 0);
367 write_cr4(read_cr4() & ~X86_CR4_CET);
368 }
369
370 /*
371 * During early boot, we can be called by panic before idle_vcpu[0] is
372 * setup, but in that case we don't need to change page tables.
373 */
374 if ( idle_vcpu[0] )
375 write_ptbase(idle_vcpu[0]);
376
377 ((void(*)(void))(unsigned long)g_tboot_shared->shutdown_entry)();
378
379 BUG(); /* should not reach here */
380 }
381
tboot_in_measured_env(void)382 int tboot_in_measured_env(void)
383 {
384 return (g_tboot_shared != NULL);
385 }
386
tboot_protect_mem_regions(void)387 int __init tboot_protect_mem_regions(void)
388 {
389 int rc;
390
391 if ( !tboot_in_measured_env() )
392 return 1;
393
394 /* TXT Heap */
395 if ( txt_heap_base == 0 )
396 return 0;
397 rc = e820_change_range_type(&e820, txt_heap_base,
398 txt_heap_base + txt_heap_size,
399 E820_RESERVED, E820_UNUSABLE);
400 if ( !rc )
401 return 0;
402
403 /* SINIT */
404 if ( sinit_base == 0 )
405 return 0;
406 rc = e820_change_range_type(&e820, sinit_base,
407 sinit_base + sinit_size,
408 E820_RESERVED, E820_UNUSABLE);
409 if ( !rc )
410 return 0;
411
412 /* TXT Private Space */
413 rc = e820_change_range_type(&e820, TXT_PRIV_CONFIG_REGS_BASE,
414 TXT_PRIV_CONFIG_REGS_BASE + NR_TXT_CONFIG_PAGES * PAGE_SIZE,
415 E820_RESERVED, E820_UNUSABLE);
416 if ( !rc )
417 return 0;
418
419 return 1;
420 }
421
tboot_parse_dmar_table(acpi_table_handler dmar_handler)422 int __init cf_check tboot_parse_dmar_table(acpi_table_handler dmar_handler)
423 {
424 int rc;
425 uint64_t size;
426 uint32_t dmar_table_length;
427 unsigned long pa;
428 sinit_mle_data_t sinit_mle_data;
429 void *dmar_table;
430
431 if ( !tboot_in_measured_env() )
432 return acpi_table_parse(ACPI_SIG_DMAR, dmar_handler);
433
434 /* ACPI tables may not be DMA protected by tboot, so use DMAR copy */
435 /* SINIT saved in SinitMleData in TXT heap (which is DMA protected) */
436
437 if ( txt_heap_base == 0 )
438 return 1;
439
440 is_vtd = true;
441
442 /* walk heap to SinitMleData */
443 pa = txt_heap_base;
444 /* skip BiosData */
445 tboot_copy_memory((unsigned char *)&size, sizeof(size), pa);
446 pa += size;
447 /* skip OsMleData */
448 tboot_copy_memory((unsigned char *)&size, sizeof(size), pa);
449 pa += size;
450 /* skip OsSinitData */
451 tboot_copy_memory((unsigned char *)&size, sizeof(size), pa);
452 pa += size;
453 /* now points to SinitMleDataSize; set to SinitMleData */
454 pa += sizeof(uint64_t);
455 tboot_copy_memory((unsigned char *)&sinit_mle_data, sizeof(sinit_mle_data),
456 pa);
457 /* get addr of DMAR table */
458 pa += sinit_mle_data.vtd_dmars_off - sizeof(uint64_t);
459 tboot_copy_memory((unsigned char *)&dmar_table_length,
460 sizeof(dmar_table_length),
461 pa + sizeof(char) * ACPI_NAME_SIZE);
462 dmar_table = xmalloc_bytes(dmar_table_length);
463 if ( !dmar_table )
464 return -ENOMEM;
465 tboot_copy_memory(dmar_table, dmar_table_length, pa);
466 clear_fixmap(FIX_TBOOT_MAP_ADDRESS);
467
468 rc = dmar_handler(dmar_table);
469 xfree(dmar_table);
470
471 return rc;
472 }
473
474 static vmac_t orig_mac, resume_mac;
475
tboot_s3_resume(void)476 int tboot_s3_resume(void)
477 {
478 if ( !tboot_in_measured_env() )
479 return 0;
480
481 /* need to do these in reverse order of shutdown */
482 tboot_gen_xenheap_integrity(g_tboot_shared->s3_key, &resume_mac);
483 orig_mac = xenheap_mac;
484 if ( resume_mac != xenheap_mac )
485 return -1;
486
487 tboot_gen_frametable_integrity(g_tboot_shared->s3_key, &resume_mac);
488 orig_mac = frametable_mac;
489 if ( resume_mac != frametable_mac )
490 return -2;
491
492 tboot_gen_domain_integrity(g_tboot_shared->s3_key, &resume_mac);
493 orig_mac = domain_mac;
494 if ( resume_mac != domain_mac )
495 return -3;
496
497 return 0;
498 }
499
tboot_s3_error(int error)500 void tboot_s3_error(int error)
501 {
502 const char *what = "???";
503
504 BUG_ON(!error || !tboot_in_measured_env());
505
506 switch ( error )
507 {
508 case -1: what = "Xen heap"; break;
509 case -2: what = "frame table"; break;
510 case -3: what = "domains"; break;
511 }
512
513 printk("MAC for %s before S3 is: 0x%08"PRIx64"\n", what, orig_mac);
514 printk("MAC for %s after S3 is: 0x%08"PRIx64"\n", what, resume_mac);
515 panic("Memory integrity was lost on resume (%d)\n", error);
516 }
517
tboot_wake_ap(int apicid,unsigned long sipi_vec)518 int tboot_wake_ap(int apicid, unsigned long sipi_vec)
519 {
520 if ( g_tboot_shared->version >= 6 &&
521 (g_tboot_shared->flags & TB_FLAG_AP_WAKE_SUPPORT) )
522 {
523 g_tboot_shared->ap_wake_addr = sipi_vec;
524 g_tboot_shared->ap_wake_trigger = apicid;
525 return 0;
526 }
527 return 1;
528 }
529
530 /*
531 * Local variables:
532 * mode: C
533 * c-file-style: "BSD"
534 * c-basic-offset: 4
535 * tab-width: 4
536 * indent-tabs-mode: nil
537 * End:
538 */
539