diff -r de38a475ab9e tools/libxc/xc_pagetab.c --- a/tools/libxc/xc_pagetab.c Mon Dec 29 14:13:07 2008 +0000 +++ b/tools/libxc/xc_pagetab.c Mon Jan 05 10:44:00 2009 +0000 @@ -4,181 +4,96 @@ * Function to translate virtual to physical addresses. */ #include "xc_private.h" +#include -#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; + uint64_t paddr, mask, pte = 0; + int size, level, pt_levels = 2; + void *map; - if (pt_levels == 0) { + 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")) + 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 (!(cr0 & CR0_PG)) + return virt; + if (0 /* XXX how to get EFER.LMA? */) pt_levels = 4; - else if (strstr(xen_caps, "xen-3.0-x86_32p")) + else + pt_levels = (cr4 & CR4_PAE) ? 3 : 2; + paddr = cr3 & ((pt_levels == 3) ? ~0x1full : ~0xfffull); + } else { + 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; - 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; + paddr = (((uint64_t) xen_cr3_to_pfn(ctx.x32.ctrlreg[3])) + << PAGE_SHIFT); } - if (pt_levels >= 4) - pdp = pdppage; - 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; - } - } else { - pdpe = pmle; } - /* Page Directory */ + if (pt_levels == 4) { + virt &= 0x0000ffffffffffffull; + mask = 0x0000ff8000000000ull; + } else if (pt_levels == 3) { + virt &= 0x00000000ffffffffull; + mask = 0x0000007fc0000000ull; + } else { + virt &= 0x00000000ffffffffull; + mask = 0x00000000ffc00000ull; + } + size = (pt_levels == 2 ? 4 : 8); - 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; + /* 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; + } + mask >>= (pt_levels == 2 ? 10 : 9); } - - 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; - } - - /* 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; - } - - 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; - } - - 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; } /* diff -r de38a475ab9e tools/libxc/xenctrl.h --- a/tools/libxc/xenctrl.h Mon Dec 29 14:13:07 2008 +0000 +++ b/tools/libxc/xenctrl.h Mon Jan 05 10:44:00 2009 +0000 @@ -703,8 +703,8 @@ void *xc_map_foreign_batch(int xc_handle /** * Translates a virtual address in the context of a given domain and - * vcpu returning the machine page frame number of the associated - * page. + * vcpu returning the GFN containing the address (that is, an MFN for + * PV guests, a PFN for HVM guests). Returns 0 for failure. * * @parm xc_handle a handle on an open hypervisor interface * @parm dom the domain to perform the translation in