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