aboutsummaryrefslogtreecommitdiffstats
path: root/tools/libxc/xc_pagetab.c
diff options
context:
space:
mode:
authorKeir Fraser <keir.fraser@citrix.com>2009-01-05 11:08:25 +0000
committerKeir Fraser <keir.fraser@citrix.com>2009-01-05 11:08:25 +0000
commit446b916ca1380998ddad67325a4257f9a2ea37d9 (patch)
tree3e49e932efacff81fad33edceb75ed0dc2d45d52 /tools/libxc/xc_pagetab.c
parentfcb1a7ac2f408905a48638190b619f59d8ab9cee (diff)
downloadxen-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.c235
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;
}
/*