diff options
author | Keir Fraser <keir.fraser@citrix.com> | 2009-01-05 11:08:25 +0000 |
---|---|---|
committer | Keir Fraser <keir.fraser@citrix.com> | 2009-01-05 11:08:25 +0000 |
commit | 446b916ca1380998ddad67325a4257f9a2ea37d9 (patch) | |
tree | 3e49e932efacff81fad33edceb75ed0dc2d45d52 /tools/libxc/xc_pagetab.c | |
parent | fcb1a7ac2f408905a48638190b619f59d8ab9cee (diff) | |
download | xen-446b916ca1380998ddad67325a4257f9a2ea37d9.tar.gz xen-446b916ca1380998ddad67325a4257f9a2ea37d9.tar.bz2 xen-446b916ca1380998ddad67325a4257f9a2ea37d9.zip |
Make xc_translate_foreign_address aware of compat-mode guests and
(32-bit) HVM guests. 64-bit HVM guests are still not supported for
now, pending a sensible way of getting at the guest's EFER.LMA.
Signed-off-by: Tim Deegan <Tim.Deegan@citrix.com>
Diffstat (limited to 'tools/libxc/xc_pagetab.c')
-rw-r--r-- | tools/libxc/xc_pagetab.c | 235 |
1 files changed, 75 insertions, 160 deletions
diff --git a/tools/libxc/xc_pagetab.c b/tools/libxc/xc_pagetab.c index ac8035bf41..8c628b4404 100644 --- a/tools/libxc/xc_pagetab.c +++ b/tools/libxc/xc_pagetab.c @@ -4,181 +4,96 @@ * Function to translate virtual to physical addresses. */ #include "xc_private.h" +#include <strings.h> -#if defined(__i386__) - -#define L1_PAGETABLE_SHIFT_PAE 12 -#define L2_PAGETABLE_SHIFT_PAE 21 -#define L3_PAGETABLE_SHIFT_PAE 30 - -#define L1_PAGETABLE_SHIFT 12 -#define L2_PAGETABLE_SHIFT 22 - -#define L0_PAGETABLE_MASK_PAE 0x00000ffffffff000ULL -#define L1_PAGETABLE_MASK_PAE 0x1ffULL -#define L2_PAGETABLE_MASK_PAE 0x1ffULL -#define L3_PAGETABLE_MASK_PAE 0x3ULL - -#define L0_PAGETABLE_MASK 0xfffff000ULL -#define L1_PAGETABLE_MASK 0x3ffULL -#define L2_PAGETABLE_MASK 0x3ffULL - -#elif defined(__x86_64__) - -#define L1_PAGETABLE_SHIFT_PAE 12 -#define L2_PAGETABLE_SHIFT_PAE 21 -#define L3_PAGETABLE_SHIFT_PAE 30 -#define L4_PAGETABLE_SHIFT_PAE 39 - -#define L1_PAGETABLE_SHIFT L1_PAGETABLE_SHIFT_PAE -#define L2_PAGETABLE_SHIFT L2_PAGETABLE_SHIFT_PAE - -#define L0_PAGETABLE_MASK_PAE 0x000ffffffffff000ULL -#define L1_PAGETABLE_MASK_PAE 0x1ffULL -#define L2_PAGETABLE_MASK_PAE 0x1ffULL -#define L3_PAGETABLE_MASK_PAE 0x1ffULL -#define L4_PAGETABLE_MASK_PAE 0x1ffULL - -#define L0_PAGETABLE_MASK L0_PAGETABLE_MASK_PAE -#define L1_PAGETABLE_MASK L1_PAGETABLE_MASK_PAE -#define L2_PAGETABLE_MASK L2_PAGETABLE_MASK_PAE - -#endif +#define CR0_PG 0x80000000 +#define CR4_PAE 0x20 +#define PTE_PSE 0x80 unsigned long xc_translate_foreign_address(int xc_handle, uint32_t dom, - int vcpu, unsigned long long virt ) + int vcpu, unsigned long long virt) { + xc_dominfo_t dominfo; vcpu_guest_context_any_t ctx; - unsigned long long cr3; - void *pd, *pt, *pdppage = NULL, *pdp, *pml = NULL; - unsigned long long pde, pte, pdpe, pmle; - unsigned long mfn = 0; -#if defined (__i386__) - static int pt_levels = 0; - - if (pt_levels == 0) { + uint64_t paddr, mask, pte = 0; + int size, level, pt_levels = 2; + void *map; + + if (xc_domain_getinfo(xc_handle, dom, 1, &dominfo) != 1 + || dominfo.domid != dom + || xc_vcpu_getcontext(xc_handle, dom, vcpu, &ctx) != 0) + return 0; + + /* What kind of paging are we dealing with? */ + if (dominfo.hvm) { + unsigned long cr0, cr3, cr4; xen_capabilities_info_t xen_caps = ""; - if (xc_version(xc_handle, XENVER_capabilities, &xen_caps) != 0) - goto out; - if (strstr(xen_caps, "xen-3.0-x86_64")) - pt_levels = 4; - else if (strstr(xen_caps, "xen-3.0-x86_32p")) - pt_levels = 3; - else if (strstr(xen_caps, "xen-3.0-x86_32")) - pt_levels = 2; - else - goto out; - } -#elif defined (__x86_64__) -#define pt_levels 4 -#endif - - if (xc_vcpu_getcontext(xc_handle, dom, vcpu, &ctx) != 0) { - DPRINTF("failed to retreive vcpu context\n"); - goto out; - } - cr3 = ((unsigned long long)xen_cr3_to_pfn(ctx.c.ctrlreg[3])) << PAGE_SHIFT; - - /* Page Map Level 4 */ - -#if defined(__i386__) - pmle = cr3; -#elif defined(__x86_64__) - pml = xc_map_foreign_range(xc_handle, dom, PAGE_SIZE, PROT_READ, cr3 >> PAGE_SHIFT); - if (pml == NULL) { - DPRINTF("failed to map PML4\n"); - goto out; - } - pmle = *(unsigned long long *)(pml + 8 * ((virt >> L4_PAGETABLE_SHIFT_PAE) & L4_PAGETABLE_MASK_PAE)); - if((pmle & 1) == 0) { - DPRINTF("page entry not present in PML4\n"); - goto out_unmap_pml; - } -#endif - - /* Page Directory Pointer Table */ - - if (pt_levels >= 3) { - pdppage = xc_map_foreign_range(xc_handle, dom, PAGE_SIZE, PROT_READ, pmle >> PAGE_SHIFT); - if (pdppage == NULL) { - DPRINTF("failed to map PDP\n"); - goto out_unmap_pml; + return 0; + /* HVM context records are always host-sized */ + if (strstr(xen_caps, "xen-3.0-x86_64")) { + cr0 = ctx.x64.ctrlreg[0]; + cr3 = ctx.x64.ctrlreg[3]; + cr4 = ctx.x64.ctrlreg[4]; + } else { + cr0 = ctx.x32.ctrlreg[0]; + cr3 = ctx.x32.ctrlreg[3]; + cr4 = ctx.x32.ctrlreg[4]; } - if (pt_levels >= 4) - pdp = pdppage; + if (!(cr0 & CR0_PG)) + return virt; + if (0 /* XXX how to get EFER.LMA? */) + pt_levels = 4; else - /* PDP is only 32 bit aligned with 3 level pts */ - pdp = pdppage + (pmle & ~(XC_PAGE_MASK | 0x1f)); - - pdpe = *(unsigned long long *)(pdp + 8 * ((virt >> L3_PAGETABLE_SHIFT_PAE) & L3_PAGETABLE_MASK_PAE)); - - if((pdpe & 1) == 0) { - DPRINTF("page entry not present in PDP\n"); - goto out_unmap_pdp; - } + pt_levels = (cr4 & CR4_PAE) ? 3 : 2; + paddr = cr3 & ((pt_levels == 3) ? ~0x1full : ~0xfffull); } else { - pdpe = pmle; - } - - /* Page Directory */ - - pd = xc_map_foreign_range(xc_handle, dom, PAGE_SIZE, PROT_READ, pdpe >> PAGE_SHIFT); - if (pd == NULL) { - DPRINTF("failed to map PD\n"); - goto out_unmap_pdp; + DECLARE_DOMCTL; + domctl.domain = dom; + domctl.cmd = XEN_DOMCTL_get_address_size; + if ( do_domctl(xc_handle, &domctl) != 0 ) + return 0; + if (domctl.u.address_size.size == 64) { + pt_levels = 4; + paddr = ctx.x64.ctrlreg[3] & ~0xfffull; + } else { + pt_levels = 3; + paddr = (((uint64_t) xen_cr3_to_pfn(ctx.x32.ctrlreg[3])) + << PAGE_SHIFT); + } } - if (pt_levels >= 3) - pde = *(unsigned long long *)(pd + 8 * ((virt >> L2_PAGETABLE_SHIFT_PAE) & L2_PAGETABLE_MASK_PAE)); - else - pde = *(unsigned long *)(pd + 4 * ((virt >> L2_PAGETABLE_SHIFT) & L2_PAGETABLE_MASK)); - - if ((pde & 1) == 0) { - DPRINTF("page entry not present in PD\n"); - goto out_unmap_pd; + if (pt_levels == 4) { + virt &= 0x0000ffffffffffffull; + mask = 0x0000ff8000000000ull; + } else if (pt_levels == 3) { + virt &= 0x00000000ffffffffull; + mask = 0x0000007fc0000000ull; + } else { + virt &= 0x00000000ffffffffull; + mask = 0x00000000ffc00000ull; } - - /* Page Table */ - - if (pde & 0x00000080) { /* 4M page (or 2M in PAE mode) */ - DPRINTF("Cannot currently cope with 2/4M pages\n"); - exit(-1); - } else { /* 4k page */ - pt = xc_map_foreign_range(xc_handle, dom, PAGE_SIZE, PROT_READ, - pde >> PAGE_SHIFT); - - if (pt == NULL) { - DPRINTF("failed to map PT\n"); - goto out_unmap_pd; + size = (pt_levels == 2 ? 4 : 8); + + /* Walk the pagetables */ + for (level = pt_levels; level > 0; level--) { + paddr += ((virt & mask) >> (ffsll(mask) - 1)) * size; + map = xc_map_foreign_range(xc_handle, dom, PAGE_SIZE, PROT_READ, + paddr >>PAGE_SHIFT); + if (!map) + return 0; + memcpy(&pte, map + (paddr & (PAGE_SIZE - 1)), size); + munmap(map, PAGE_SIZE); + if (!(pte & 1)) + return 0; + paddr = pte & 0x000ffffffffff000ull; + if (level == 2 && (pte & PTE_PSE)) { + mask = ((mask ^ ~-mask) >> 1); /* All bits below first set bit */ + return ((paddr & ~mask) | (virt & mask)) >> PAGE_SHIFT; } - - if (pt_levels >= 3) - pte = *(unsigned long long *)(pt + 8 * ((virt >> L1_PAGETABLE_SHIFT_PAE) & L1_PAGETABLE_MASK_PAE)); - else - pte = *(unsigned long *)(pt + 4 * ((virt >> L1_PAGETABLE_SHIFT) & L1_PAGETABLE_MASK)); - - if ((pte & 1) == 0) { - DPRINTF("page entry not present in PT\n"); - goto out_unmap_pt; - } - - if (pt_levels >= 3) - mfn = (pte & L0_PAGETABLE_MASK_PAE) >> PAGE_SHIFT; - else - mfn = (pte & L0_PAGETABLE_MASK) >> PAGE_SHIFT; + mask >>= (pt_levels == 2 ? 10 : 9); } - - out_unmap_pt: - munmap(pt, PAGE_SIZE); - out_unmap_pd: - munmap(pd, PAGE_SIZE); - out_unmap_pdp: - munmap(pdppage, PAGE_SIZE); - out_unmap_pml: - munmap(pml, PAGE_SIZE); - out: - return mfn; + return paddr >> PAGE_SHIFT; } /* |