ia64/xen-unstable

view xen/arch/x86/tboot.c @ 19577:7809e0941b38

x86 vtd: Do not exclude freed percpu areas from dom0 vtd tables.

Signed-off-by: Qing He <qing.he@intel.com>
Signed-off-by: Keir Fraser <keir.fraser@citrix.com>
author Keir Fraser <keir.fraser@citrix.com>
date Sat Apr 25 08:19:14 2009 +0100 (2009-04-25)
parents ec3d0937095d
children
line source
1 #include <xen/config.h>
2 #include <xen/init.h>
3 #include <xen/types.h>
4 #include <xen/lib.h>
5 #include <xen/sched.h>
6 #include <xen/domain_page.h>
7 #include <xen/iommu.h>
8 #include <asm/fixmap.h>
9 #include <asm/page.h>
10 #include <asm/processor.h>
11 #include <asm/e820.h>
12 #include <asm/tboot.h>
13 #include <crypto/vmac.h>
15 /* tboot=<physical address of shared page> */
16 static char opt_tboot[20] = "";
17 string_param("tboot", opt_tboot);
19 /* Global pointer to shared data; NULL means no measured launch. */
20 tboot_shared_t *g_tboot_shared;
22 static vmac_t domain_mac; /* MAC for all domains during S3 */
23 static vmac_t xenheap_mac; /* MAC for xen heap during S3 */
24 static vmac_t frametable_mac; /* MAC for frame table during S3 */
26 static const uuid_t tboot_shared_uuid = TBOOT_SHARED_UUID;
28 /* used by tboot_protect_mem_regions() and/or tboot_parse_dmar_table() */
29 static uint64_t txt_heap_base, txt_heap_size;
30 static uint64_t sinit_base, sinit_size;
32 /*
33 * TXT configuration registers (offsets from TXT_{PUB, PRIV}_CONFIG_REGS_BASE)
34 */
36 #define TXT_PUB_CONFIG_REGS_BASE 0xfed30000
37 #define TXT_PRIV_CONFIG_REGS_BASE 0xfed20000
39 /* # pages for each config regs space - used by fixmap */
40 #define NR_TXT_CONFIG_PAGES ((TXT_PUB_CONFIG_REGS_BASE - \
41 TXT_PRIV_CONFIG_REGS_BASE) >> PAGE_SHIFT)
43 /* offsets from pub/priv config space */
44 #define TXTCR_SINIT_BASE 0x0270
45 #define TXTCR_SINIT_SIZE 0x0278
46 #define TXTCR_HEAP_BASE 0x0300
47 #define TXTCR_HEAP_SIZE 0x0308
49 extern char __init_begin[], __per_cpu_start[], __bss_start[];
50 extern unsigned long allocator_bitmap_end;
52 #define SHA1_SIZE 20
53 typedef uint8_t sha1_hash_t[SHA1_SIZE];
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;
73 void __init tboot_probe(void)
74 {
75 tboot_shared_t *tboot_shared;
76 unsigned long p_tboot_shared;
77 uint32_t map_base, map_size;
78 unsigned long map_addr;
80 /* Look for valid page-aligned address for shared page. */
81 p_tboot_shared = simple_strtoul(opt_tboot, NULL, 0);
82 if ( (p_tboot_shared == 0) || ((p_tboot_shared & ~PAGE_MASK) != 0) )
83 return;
85 /* Map and check for tboot UUID. */
86 set_fixmap(FIX_TBOOT_SHARED_BASE, p_tboot_shared);
87 tboot_shared = (tboot_shared_t *)fix_to_virt(FIX_TBOOT_SHARED_BASE);
88 if ( tboot_shared == NULL )
89 return;
90 if ( memcmp(&tboot_shared_uuid, (uuid_t *)tboot_shared, sizeof(uuid_t)) )
91 return;
93 /* new tboot_shared (w/ GAS support, integrity, etc.) is not backwards
94 compatible */
95 if ( tboot_shared->version < 4 ) {
96 printk("unsupported version of tboot (%u)\n", tboot_shared->version);
97 return;
98 }
100 g_tboot_shared = tboot_shared;
101 printk("TBOOT: found shared page at phys addr %lx:\n", p_tboot_shared);
102 printk(" version: %d\n", tboot_shared->version);
103 printk(" log_addr: 0x%08x\n", tboot_shared->log_addr);
104 printk(" shutdown_entry: 0x%08x\n", tboot_shared->shutdown_entry);
105 printk(" tboot_base: 0x%08x\n", tboot_shared->tboot_base);
106 printk(" tboot_size: 0x%x\n", tboot_shared->tboot_size);
108 /* these will be needed by tboot_protect_mem_regions() and/or
109 tboot_parse_dmar_table(), so get them now */
111 map_base = PFN_DOWN(TXT_PUB_CONFIG_REGS_BASE);
112 map_size = PFN_UP(NR_TXT_CONFIG_PAGES * PAGE_SIZE);
113 map_addr = (unsigned long)__va(map_base << PAGE_SHIFT);
114 if ( map_pages_to_xen(map_addr, map_base, map_size, __PAGE_HYPERVISOR) )
115 return;
117 /* TXT Heap */
118 txt_heap_base =
119 *(uint64_t *)__va(TXT_PUB_CONFIG_REGS_BASE + TXTCR_HEAP_BASE);
120 txt_heap_size =
121 *(uint64_t *)__va(TXT_PUB_CONFIG_REGS_BASE + TXTCR_HEAP_SIZE);
123 /* SINIT */
124 sinit_base =
125 *(uint64_t *)__va(TXT_PUB_CONFIG_REGS_BASE + TXTCR_SINIT_BASE);
126 sinit_size =
127 *(uint64_t *)__va(TXT_PUB_CONFIG_REGS_BASE + TXTCR_SINIT_SIZE);
129 destroy_xen_mappings((unsigned long)__va(map_base << PAGE_SHIFT),
130 (unsigned long)__va((map_base + map_size) << PAGE_SHIFT));
131 }
133 /* definitions from xen/drivers/passthrough/vtd/iommu.h
134 * used to walk through vtd page tables */
135 #define LEVEL_STRIDE (9)
136 #define PTE_NUM (1<<LEVEL_STRIDE)
137 #define dma_pte_present(p) (((p).val & 3) != 0)
138 #define dma_pte_addr(p) ((p).val & PAGE_MASK_4K)
139 #define agaw_to_level(val) ((val)+2)
140 struct dma_pte {
141 u64 val;
142 };
144 static void update_iommu_mac(vmac_ctx_t *ctx, uint64_t pt_maddr, int level)
145 {
146 int i;
147 struct dma_pte *pt_vaddr, *pte;
148 int next_level = level - 1;
150 if ( pt_maddr == 0 )
151 return;
153 pt_vaddr = (struct dma_pte *)map_domain_page(pt_maddr >> PAGE_SHIFT_4K);
154 vmac_update((void *)pt_vaddr, PAGE_SIZE, ctx);
156 for ( i = 0; i < PTE_NUM; i++ )
157 {
158 pte = &pt_vaddr[i];
159 if ( !dma_pte_present(*pte) )
160 continue;
162 if ( next_level >= 1 )
163 update_iommu_mac(ctx, dma_pte_addr(*pte), next_level);
164 }
166 unmap_domain_page(pt_vaddr);
167 }
169 #define is_page_in_use(page) \
170 ((page->count_info & PGC_count_mask) != 0 || page->count_info == 0)
172 static void update_pagetable_mac(vmac_ctx_t *ctx)
173 {
174 unsigned long mfn;
176 for ( mfn = 0; mfn < max_page; mfn++ )
177 {
178 struct page_info *page = mfn_to_page(mfn);
179 if ( is_page_in_use(page) && !is_xen_heap_page(page) ) {
180 if ( page->count_info & PGC_page_table ) {
181 void *pg = map_domain_page(mfn);
182 vmac_update(pg, PAGE_SIZE, ctx);
183 unmap_domain_page(pg);
184 }
185 }
186 }
187 }
189 static void tboot_gen_domain_integrity(const uint8_t key[TB_KEY_SIZE],
190 vmac_t *mac)
191 {
192 struct domain *d;
193 struct page_info *page;
194 uint8_t nonce[16] = {};
195 vmac_ctx_t ctx;
197 vmac_set_key((uint8_t *)key, &ctx);
198 for_each_domain( d )
199 {
200 if ( !d->arch.s3_integrity )
201 continue;
202 printk("MACing Domain %u\n", d->domain_id);
204 page_list_for_each(page, &d->page_list)
205 {
206 void *pg;
207 pg = map_domain_page(page_to_mfn(page));
208 vmac_update(pg, PAGE_SIZE, &ctx);
209 unmap_domain_page(pg);
210 }
212 if ( !is_idle_domain(d) )
213 {
214 struct hvm_iommu *hd = domain_hvm_iommu(d);
215 update_iommu_mac(&ctx, hd->pgd_maddr, agaw_to_level(hd->agaw));
216 }
217 }
219 /* MAC all shadow page tables */
220 update_pagetable_mac(&ctx);
222 *mac = vmac(NULL, 0, nonce, NULL, &ctx);
224 printk("MAC for domains is: 0x%08"PRIx64"\n", *mac);
226 /* wipe ctx to ensure key is not left in memory */
227 memset(&ctx, 0, sizeof(ctx));
228 }
230 static void tboot_gen_xenheap_integrity(const uint8_t key[TB_KEY_SIZE],
231 vmac_t *mac)
232 {
233 unsigned long mfn;
234 uint8_t nonce[16] = {};
235 vmac_ctx_t ctx;
237 vmac_set_key((uint8_t *)key, &ctx);
238 for ( mfn = 0; mfn < max_page; mfn++ )
239 {
240 struct page_info *page = __mfn_to_page(mfn);
241 if ( is_page_in_use(page) && is_xen_heap_page(page) ) {
242 void *pg = mfn_to_virt(mfn);
243 vmac_update((uint8_t *)pg, PAGE_SIZE, &ctx);
244 }
245 }
246 *mac = vmac(NULL, 0, nonce, NULL, &ctx);
248 printk("MAC for xenheap is: 0x%08"PRIx64"\n", *mac);
250 /* wipe ctx to ensure key is not left in memory */
251 memset(&ctx, 0, sizeof(ctx));
252 }
254 static void tboot_gen_frametable_integrity(const uint8_t key[TB_KEY_SIZE],
255 vmac_t *mac)
256 {
257 uint8_t nonce[16] = {};
258 vmac_ctx_t ctx;
260 vmac_set_key((uint8_t *)key, &ctx);
261 *mac = vmac((uint8_t *)frame_table,
262 PFN_UP(max_page * sizeof(*frame_table)), nonce, NULL, &ctx);
264 printk("MAC for frametable is: 0x%08"PRIx64"\n", *mac);
266 /* wipe ctx to ensure key is not left in memory */
267 memset(&ctx, 0, sizeof(ctx));
268 }
270 void tboot_shutdown(uint32_t shutdown_type)
271 {
272 uint32_t map_base, map_size;
273 int err;
275 g_tboot_shared->shutdown_type = shutdown_type;
277 local_irq_disable();
279 /* we may be called from an interrupt context, so to prevent */
280 /* 'ASSERT(!in_irq());' in alloc_domheap_pages(), decrease count */
281 while ( in_irq() )
282 irq_exit();
284 /* Create identity map for tboot shutdown code. */
285 /* do before S3 integrity because mapping tboot may change xenheap */
286 map_base = PFN_DOWN(g_tboot_shared->tboot_base);
287 map_size = PFN_UP(g_tboot_shared->tboot_size);
289 err = map_pages_to_xen(map_base << PAGE_SHIFT, map_base, map_size,
290 __PAGE_HYPERVISOR);
291 if ( err != 0 ) {
292 printk("error (0x%x) mapping tboot pages (mfns) @ 0x%x, 0x%x\n", err,
293 map_base, map_size);
294 return;
295 }
297 /* if this is S3 then set regions to MAC */
298 if ( shutdown_type == TB_SHUTDOWN_S3 ) {
299 /*
300 * Xen regions for tboot to MAC
301 */
302 g_tboot_shared->num_mac_regions = 5;
303 /* S3 resume code (and other real mode trampoline code) */
304 g_tboot_shared->mac_regions[0].start = bootsym_phys(trampoline_start);
305 g_tboot_shared->mac_regions[0].size = bootsym_phys(trampoline_end) -
306 bootsym_phys(trampoline_start);
307 /* hypervisor code + data */
308 g_tboot_shared->mac_regions[1].start = (uint64_t)__pa(&_stext);
309 g_tboot_shared->mac_regions[1].size = __pa(&__init_begin) -
310 __pa(&_stext);
311 /* per-cpu data */
312 g_tboot_shared->mac_regions[2].start = (uint64_t)__pa(&__per_cpu_start);
313 g_tboot_shared->mac_regions[2].size =
314 g_tboot_shared->mac_regions[2].start +
315 (((uint64_t)last_cpu(cpu_possible_map) + 1) << PERCPU_SHIFT);
316 /* bss */
317 g_tboot_shared->mac_regions[3].start = (uint64_t)__pa(&__bss_start);
318 g_tboot_shared->mac_regions[3].size = __pa(&_end) - __pa(&__bss_start);
319 /* boot allocator bitmap */
320 g_tboot_shared->mac_regions[4].start = (uint64_t)__pa(&_end);
321 g_tboot_shared->mac_regions[4].size = allocator_bitmap_end -
322 __pa(&_end);
324 /*
325 * MAC domains and other Xen memory
326 */
327 /* Xen has no better entropy source for MAC key than tboot's */
328 /* MAC domains first in case it perturbs xenheap */
329 tboot_gen_domain_integrity(g_tboot_shared->s3_key, &domain_mac);
330 tboot_gen_frametable_integrity(g_tboot_shared->s3_key, &frametable_mac);
331 tboot_gen_xenheap_integrity(g_tboot_shared->s3_key, &xenheap_mac);
332 }
334 write_ptbase(idle_vcpu[0]);
336 ((void(*)(void))(unsigned long)g_tboot_shared->shutdown_entry)();
338 BUG(); /* should not reach here */
339 }
341 int tboot_in_measured_env(void)
342 {
343 return (g_tboot_shared != NULL);
344 }
346 int __init tboot_protect_mem_regions(void)
347 {
348 int rc;
350 if ( !tboot_in_measured_env() )
351 return 1;
353 /* TXT Heap */
354 if ( txt_heap_base == 0 )
355 return 0;
356 rc = e820_change_range_type(
357 &e820, txt_heap_base, txt_heap_base + txt_heap_size,
358 E820_RESERVED, E820_UNUSABLE);
359 if ( !rc )
360 return 0;
362 /* SINIT */
363 if ( sinit_base == 0 )
364 return 0;
365 rc = e820_change_range_type(
366 &e820, sinit_base, sinit_base + sinit_size,
367 E820_RESERVED, E820_UNUSABLE);
368 if ( !rc )
369 return 0;
371 /* TXT Private Space */
372 rc = e820_change_range_type(
373 &e820, TXT_PRIV_CONFIG_REGS_BASE,
374 TXT_PRIV_CONFIG_REGS_BASE + NR_TXT_CONFIG_PAGES * PAGE_SIZE,
375 E820_RESERVED, E820_UNUSABLE);
376 if ( !rc )
377 return 0;
379 return 1;
380 }
382 int __init tboot_parse_dmar_table(acpi_table_handler dmar_handler)
383 {
384 uint32_t map_base, map_size;
385 unsigned long map_vaddr;
386 void *heap_ptr;
387 struct acpi_table_header *dmar_table;
388 int rc;
390 if ( !tboot_in_measured_env() )
391 return acpi_table_parse(ACPI_SIG_DMAR, dmar_handler);
393 /* ACPI tables may not be DMA protected by tboot, so use DMAR copy */
394 /* SINIT saved in SinitMleData in TXT heap (which is DMA protected) */
396 if ( txt_heap_base == 0 )
397 return 1;
399 /* map TXT heap into Xen addr space */
400 map_base = PFN_DOWN(txt_heap_base);
401 map_size = PFN_UP(txt_heap_size);
402 map_vaddr = (unsigned long)__va(map_base << PAGE_SHIFT);
403 if ( map_pages_to_xen(map_vaddr, map_base, map_size, __PAGE_HYPERVISOR) )
404 return 1;
406 /* walk heap to SinitMleData */
407 heap_ptr = __va(txt_heap_base);
408 /* skip BiosData */
409 heap_ptr += *(uint64_t *)heap_ptr;
410 /* skip OsMleData */
411 heap_ptr += *(uint64_t *)heap_ptr;
412 /* skip OsSinitData */
413 heap_ptr += *(uint64_t *)heap_ptr;
414 /* now points to SinitMleDataSize; set to SinitMleData */
415 heap_ptr += sizeof(uint64_t);
416 /* get addr of DMAR table */
417 dmar_table = (struct acpi_table_header *)(heap_ptr +
418 ((sinit_mle_data_t *)heap_ptr)->vtd_dmars_off - sizeof(uint64_t));
420 rc = dmar_handler(dmar_table);
422 destroy_xen_mappings(
423 (unsigned long)__va(map_base << PAGE_SHIFT),
424 (unsigned long)__va((map_base + map_size) << PAGE_SHIFT));
426 /* acpi_parse_dmar() zaps APCI DMAR signature in TXT heap table */
427 /* but dom0 will read real table, so must zap it there too */
428 dmar_table = NULL;
429 acpi_get_table(ACPI_SIG_DMAR, 0, &dmar_table);
430 if ( dmar_table != NULL )
431 ((struct acpi_table_dmar *)dmar_table)->header.signature[0] = '\0';
433 return rc;
434 }
436 int tboot_s3_resume(void)
437 {
438 vmac_t mac;
440 if ( !tboot_in_measured_env() )
441 return 0;
443 /* need to do these in reverse order of shutdown */
444 tboot_gen_xenheap_integrity(g_tboot_shared->s3_key, &mac);
445 if ( mac != xenheap_mac )
446 return -1;
448 tboot_gen_frametable_integrity(g_tboot_shared->s3_key, &mac);
449 if ( mac != frametable_mac )
450 return -2;
452 tboot_gen_domain_integrity(g_tboot_shared->s3_key, &mac);
453 if ( mac != domain_mac )
454 return -3;
456 return 0;
457 }
459 /*
460 * Local variables:
461 * mode: C
462 * c-set-style: "BSD"
463 * c-basic-offset: 4
464 * tab-width: 4
465 * indent-tabs-mode: nil
466 * End:
467 */