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