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