aboutsummaryrefslogtreecommitdiffstats
path: root/xen/arch/x86/tboot.c
diff options
context:
space:
mode:
authorKeir Fraser <keir.fraser@citrix.com>2009-03-03 12:48:16 +0000
committerKeir Fraser <keir.fraser@citrix.com>2009-03-03 12:48:16 +0000
commit6deab1ae316b5f7da431d8add34c676dfca9c4f9 (patch)
tree83d88eac3224e24acc9a6c4e8be971ed681810d3 /xen/arch/x86/tboot.c
parent8aaf870611964a738533c7300e0858e293f5f23a (diff)
downloadxen-6deab1ae316b5f7da431d8add34c676dfca9c4f9.tar.gz
xen-6deab1ae316b5f7da431d8add34c676dfca9c4f9.tar.bz2
xen-6deab1ae316b5f7da431d8add34c676dfca9c4f9.zip
txt: perform per-domain (and frametable and xenheap) MAC on entry into
S3 and verification on resume. The MAC algorithm is called VMAC and was developed by Ted Krovetz and Wei Dai (more details are in the files). It is based on a universal hash function. The universal hash is passed through a pseudo-random function, implemented using AES. More details can be found at http://fastcrypto.org/vmac/. = The AES code comes from the OpenBSD implementation (which is derived from the implementation referenced in VMAC site). As Xen does not have a good source of entropy to generate its own key (for the keyed hash), it uses the key that tboot passes in. Although the code attempts to MAC all of a domain's pages (code/data, VT-d tables) based on its s3_integrity flag, some of a domain's memory may always be MAC'ed, e.g. shadow page tables. Only xenheap pages that are in use are MAC'ed. We believe that the memory MAC'ed by the Xen code and the ranges passed to tboot to MAC cover all of the memory whose integrity needs to be protected on S3. Any suggestions or ranges that we missed are welcome. Signed-off-by: Shane Wang <shane.wang@intel.com> Signed-off-by: Joseph Cihula <joseph.cihula@intel.com>
Diffstat (limited to 'xen/arch/x86/tboot.c')
-rw-r--r--xen/arch/x86/tboot.c235
1 files changed, 213 insertions, 22 deletions
diff --git a/xen/arch/x86/tboot.c b/xen/arch/x86/tboot.c
index 92f509ae95..ed20e9951f 100644
--- a/xen/arch/x86/tboot.c
+++ b/xen/arch/x86/tboot.c
@@ -3,11 +3,14 @@
#include <xen/types.h>
#include <xen/lib.h>
#include <xen/sched.h>
+#include <xen/domain_page.h>
+#include <xen/iommu.h>
#include <asm/fixmap.h>
#include <asm/page.h>
#include <asm/processor.h>
#include <asm/e820.h>
#include <asm/tboot.h>
+#include <crypto/vmac.h>
/* tboot=<physical address of shared page> */
static char opt_tboot[20] = "";
@@ -16,6 +19,10 @@ string_param("tboot", opt_tboot);
/* Global pointer to shared data; NULL means no measured launch. */
tboot_shared_t *g_tboot_shared;
+static vmac_t domain_mac; /* MAC for all domains during S3 */
+static vmac_t xenheap_mac; /* MAC for xen heap during S3 */
+static vmac_t frametable_mac; /* MAC for frame table during S3 */
+
static const uuid_t tboot_shared_uuid = TBOOT_SHARED_UUID;
/* used by tboot_protect_mem_regions() and/or tboot_parse_dmar_table() */
@@ -40,6 +47,7 @@ static uint64_t sinit_base, sinit_size;
#define TXTCR_HEAP_SIZE 0x0308
extern char __init_begin[], __per_cpu_start[], __per_cpu_end[], __bss_start[];
+extern unsigned long allocator_bitmap_end;
#define SHA1_SIZE 20
typedef uint8_t sha1_hash_t[SHA1_SIZE];
@@ -82,8 +90,9 @@ void __init tboot_probe(void)
if ( memcmp(&tboot_shared_uuid, (uuid_t *)tboot_shared, sizeof(uuid_t)) )
return;
- /* new tboot_shared (w/ GAS support) is not backwards compatible */
- if ( tboot_shared->version < 3 ) {
+ /* new tboot_shared (w/ GAS support, integrity, etc.) is not backwards
+ compatible */
+ if ( tboot_shared->version < 4 ) {
printk("unsupported version of tboot (%u)\n", tboot_shared->version);
return;
}
@@ -121,6 +130,143 @@ void __init tboot_probe(void)
(unsigned long)__va((map_base + map_size) << PAGE_SHIFT));
}
+/* definitions from xen/drivers/passthrough/vtd/iommu.h
+ * used to walk through vtd page tables */
+#define LEVEL_STRIDE (9)
+#define PTE_NUM (1<<LEVEL_STRIDE)
+#define dma_pte_present(p) (((p).val & 3) != 0)
+#define dma_pte_addr(p) ((p).val & PAGE_MASK_4K)
+#define agaw_to_level(val) ((val)+2)
+struct dma_pte {
+ u64 val;
+};
+
+static void update_iommu_mac(vmac_ctx_t *ctx, uint64_t pt_maddr, int level)
+{
+ int i;
+ struct dma_pte *pt_vaddr, *pte;
+ int next_level = level - 1;
+
+ if ( pt_maddr == 0 )
+ return;
+
+ pt_vaddr = (struct dma_pte *)map_domain_page(pt_maddr >> PAGE_SHIFT_4K);
+ vmac_update((void *)pt_vaddr, PAGE_SIZE, ctx);
+
+ for ( i = 0; i < PTE_NUM; i++ )
+ {
+ pte = &pt_vaddr[i];
+ if ( !dma_pte_present(*pte) )
+ continue;
+
+ if ( next_level >= 1 )
+ update_iommu_mac(ctx, dma_pte_addr(*pte), next_level);
+ }
+
+ unmap_domain_page(pt_vaddr);
+}
+
+#define is_page_in_use(page) \
+ ((page->count_info & PGC_count_mask) != 0 || page->count_info == 0)
+
+static void update_pagetable_mac(vmac_ctx_t *ctx)
+{
+ unsigned long mfn;
+
+ for ( mfn = 0; mfn < max_page; mfn++ )
+ {
+ struct page_info *page = mfn_to_page(mfn);
+ if ( is_page_in_use(page) && !is_xen_heap_page(page) ) {
+ if ( page->count_info & PGC_page_table ) {
+ void *pg = map_domain_page(mfn);
+ vmac_update(pg, PAGE_SIZE, ctx);
+ unmap_domain_page(pg);
+ }
+ }
+ }
+}
+
+static void tboot_gen_domain_integrity(const uint8_t key[TB_KEY_SIZE],
+ vmac_t *mac)
+{
+ struct domain *d;
+ struct page_info *page;
+ uint8_t nonce[16] = {};
+ vmac_ctx_t ctx;
+
+ vmac_set_key((uint8_t *)key, &ctx);
+ for_each_domain( d )
+ {
+ if ( !d->arch.s3_integrity )
+ continue;
+ printk("MACing Domain %u\n", d->domain_id);
+
+ page_list_for_each(page, &d->page_list)
+ {
+ void *pg;
+ pg = map_domain_page(page_to_mfn(page));
+ vmac_update(pg, PAGE_SIZE, &ctx);
+ unmap_domain_page(pg);
+ }
+
+ if ( !is_idle_domain(d) )
+ {
+ struct hvm_iommu *hd = domain_hvm_iommu(d);
+ update_iommu_mac(&ctx, hd->pgd_maddr, agaw_to_level(hd->agaw));
+ }
+ }
+
+ /* MAC all shadow page tables */
+ update_pagetable_mac(&ctx);
+
+ *mac = vmac(NULL, 0, nonce, NULL, &ctx);
+
+ printk("MAC for domains is: 0x%08"PRIx64"\n", *mac);
+
+ /* wipe ctx to ensure key is not left in memory */
+ memset(&ctx, 0, sizeof(ctx));
+}
+
+static void tboot_gen_xenheap_integrity(const uint8_t key[TB_KEY_SIZE],
+ vmac_t *mac)
+{
+ unsigned long mfn;
+ uint8_t nonce[16] = {};
+ vmac_ctx_t ctx;
+
+ vmac_set_key((uint8_t *)key, &ctx);
+ for ( mfn = 0; mfn < max_page; mfn++ )
+ {
+ struct page_info *page = __mfn_to_page(mfn);
+ if ( is_page_in_use(page) && is_xen_heap_page(page) ) {
+ void *pg = mfn_to_virt(mfn);
+ vmac_update((uint8_t *)pg, PAGE_SIZE, &ctx);
+ }
+ }
+ *mac = vmac(NULL, 0, nonce, NULL, &ctx);
+
+ printk("MAC for xenheap is: 0x%08"PRIx64"\n", *mac);
+
+ /* wipe ctx to ensure key is not left in memory */
+ memset(&ctx, 0, sizeof(ctx));
+}
+
+static void tboot_gen_frametable_integrity(const uint8_t key[TB_KEY_SIZE],
+ vmac_t *mac)
+{
+ uint8_t nonce[16] = {};
+ vmac_ctx_t ctx;
+
+ vmac_set_key((uint8_t *)key, &ctx);
+ *mac = vmac((uint8_t *)frame_table,
+ PFN_UP(max_page * sizeof(*frame_table)), nonce, NULL, &ctx);
+
+ printk("MAC for frametable is: 0x%08"PRIx64"\n", *mac);
+
+ /* wipe ctx to ensure key is not left in memory */
+ memset(&ctx, 0, sizeof(ctx));
+}
+
void tboot_shutdown(uint32_t shutdown_type)
{
uint32_t map_base, map_size;
@@ -130,38 +276,60 @@ void tboot_shutdown(uint32_t shutdown_type)
local_irq_disable();
- /* if this is S3 then set regions to MAC */
- if ( shutdown_type == TB_SHUTDOWN_S3 ) {
- g_tboot_shared->num_mac_regions = 4;
- /* S3 resume code (and other real mode trampoline code) */
- g_tboot_shared->mac_regions[0].start =
- (uint64_t)bootsym_phys(trampoline_start);
- g_tboot_shared->mac_regions[0].end =
- (uint64_t)bootsym_phys(trampoline_end);
- /* hypervisor code + data */
- g_tboot_shared->mac_regions[1].start = (uint64_t)__pa(&_stext);
- g_tboot_shared->mac_regions[1].end = (uint64_t)__pa(&__init_begin);
- /* per-cpu data */
- g_tboot_shared->mac_regions[2].start = (uint64_t)__pa(&__per_cpu_start);
- g_tboot_shared->mac_regions[2].end = (uint64_t)__pa(&__per_cpu_end);
- /* bss */
- g_tboot_shared->mac_regions[3].start = (uint64_t)__pa(&__bss_start);
- g_tboot_shared->mac_regions[3].end = (uint64_t)__pa(&_end);
- }
+ /* we may be called from an interrupt context, so to prevent */
+ /* 'ASSERT(!in_irq());' in alloc_domheap_pages(), decrease count */
+ while ( in_irq() )
+ irq_exit();
/* Create identity map for tboot shutdown code. */
+ /* do before S3 integrity because mapping tboot may change xenheap */
map_base = PFN_DOWN(g_tboot_shared->tboot_base);
map_size = PFN_UP(g_tboot_shared->tboot_size);
err = map_pages_to_xen(map_base << PAGE_SHIFT, map_base, map_size,
__PAGE_HYPERVISOR);
- if ( err != 0 )
- {
+ if ( err != 0 ) {
printk("error (0x%x) mapping tboot pages (mfns) @ 0x%x, 0x%x\n", err,
map_base, map_size);
return;
}
+ /* if this is S3 then set regions to MAC */
+ if ( shutdown_type == TB_SHUTDOWN_S3 ) {
+ /*
+ * Xen regions for tboot to MAC
+ */
+ g_tboot_shared->num_mac_regions = 5;
+ /* S3 resume code (and other real mode trampoline code) */
+ g_tboot_shared->mac_regions[0].start = bootsym_phys(trampoline_start);
+ g_tboot_shared->mac_regions[0].size = bootsym_phys(trampoline_end) -
+ bootsym_phys(trampoline_start);
+ /* hypervisor code + data */
+ g_tboot_shared->mac_regions[1].start = (uint64_t)__pa(&_stext);
+ g_tboot_shared->mac_regions[1].size = __pa(&__init_begin) -
+ __pa(&_stext);
+ /* per-cpu data */
+ g_tboot_shared->mac_regions[2].start = (uint64_t)__pa(&__per_cpu_start);
+ g_tboot_shared->mac_regions[2].size = __pa(&__per_cpu_end) -
+ __pa(&__per_cpu_start);
+ /* bss */
+ g_tboot_shared->mac_regions[3].start = (uint64_t)__pa(&__bss_start);
+ g_tboot_shared->mac_regions[3].size = __pa(&_end) - __pa(&__bss_start);
+ /* boot allocator bitmap */
+ g_tboot_shared->mac_regions[4].start = (uint64_t)__pa(&_end);
+ g_tboot_shared->mac_regions[4].size = allocator_bitmap_end -
+ __pa(&_end);
+
+ /*
+ * MAC domains and other Xen memory
+ */
+ /* Xen has no better entropy source for MAC key than tboot's */
+ /* MAC domains first in case it perturbs xenheap */
+ tboot_gen_domain_integrity(g_tboot_shared->s3_key, &domain_mac);
+ tboot_gen_frametable_integrity(g_tboot_shared->s3_key, &frametable_mac);
+ tboot_gen_xenheap_integrity(g_tboot_shared->s3_key, &xenheap_mac);
+ }
+
write_ptbase(idle_vcpu[0]);
((void(*)(void))(unsigned long)g_tboot_shared->shutdown_entry)();
@@ -264,6 +432,29 @@ int __init tboot_parse_dmar_table(acpi_table_handler dmar_handler)
return rc;
}
+int tboot_s3_resume(void)
+{
+ vmac_t mac;
+
+ if ( !tboot_in_measured_env() )
+ return 0;
+
+ /* need to do these in reverse order of shutdown */
+ tboot_gen_xenheap_integrity(g_tboot_shared->s3_key, &mac);
+ if ( mac != xenheap_mac )
+ return -1;
+
+ tboot_gen_frametable_integrity(g_tboot_shared->s3_key, &mac);
+ if ( mac != frametable_mac )
+ return -2;
+
+ tboot_gen_domain_integrity(g_tboot_shared->s3_key, &mac);
+ if ( mac != domain_mac )
+ return 0; /* -3 */
+
+ return 0;
+}
+
/*
* Local variables:
* mode: C