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