1 /******************************************************************************
2 * xc_pagetab.c
3 *
4 * Function to translate virtual to physical addresses.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation;
9 * version 2.1 of the License.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "xc_private.h"
21 #include <xen/hvm/save.h>
22
23 #define CR0_PG 0x80000000
24 #define CR4_PAE 0x20
25 #define PTE_PSE 0x80
26 #define EFER_LMA 0x400
27
28
xc_translate_foreign_address(xc_interface * xch,uint32_t dom,int vcpu,unsigned long long virt)29 unsigned long xc_translate_foreign_address(xc_interface *xch, uint32_t dom,
30 int vcpu, unsigned long long virt)
31 {
32 xc_domaininfo_t dominfo;
33 uint64_t paddr, mask, pte = 0;
34 int size, level, pt_levels = 2;
35 void *map;
36
37 if (xc_domain_getinfo_single(xch, dom, &dominfo) < 0)
38 return 0;
39
40 /* What kind of paging are we dealing with? */
41 if (dominfo.flags & XEN_DOMINF_hvm_guest) {
42 struct hvm_hw_cpu ctx;
43 if (xc_domain_hvm_getcontext_partial(xch, dom,
44 HVM_SAVE_CODE(CPU), vcpu,
45 &ctx, sizeof ctx) != 0)
46 return 0;
47 if (!(ctx.cr0 & CR0_PG))
48 return virt >> PAGE_SHIFT;
49 pt_levels = (ctx.msr_efer&EFER_LMA) ? 4 : (ctx.cr4&CR4_PAE) ? 3 : 2;
50 paddr = ctx.cr3 & ((pt_levels == 3) ? ~0x1full : ~0xfffull);
51 } else {
52 unsigned int gwidth;
53 vcpu_guest_context_any_t ctx;
54 if (xc_vcpu_getcontext(xch, dom, vcpu, &ctx) != 0)
55 return 0;
56 if (xc_domain_get_guest_width(xch, dom, &gwidth) != 0)
57 return 0;
58 if (gwidth == 8) {
59 pt_levels = 4;
60 paddr = (uint64_t)xen_cr3_to_pfn_x86_64(ctx.x64.ctrlreg[3])
61 << PAGE_SHIFT;
62 } else {
63 pt_levels = 3;
64 paddr = (uint64_t)xen_cr3_to_pfn_x86_32(ctx.x32.ctrlreg[3])
65 << PAGE_SHIFT;
66 }
67 }
68
69 if (pt_levels == 4) {
70 virt &= 0x0000ffffffffffffull;
71 mask = 0x0000ff8000000000ull;
72 } else if (pt_levels == 3) {
73 virt &= 0x00000000ffffffffull;
74 mask = 0x0000007fc0000000ull;
75 } else {
76 virt &= 0x00000000ffffffffull;
77 mask = 0x00000000ffc00000ull;
78 }
79 size = (pt_levels == 2 ? 4 : 8);
80
81 /* Walk the pagetables */
82 for (level = pt_levels; level > 0; level--) {
83 paddr += ((virt & mask) >> (xc_ffs64(mask) - 1)) * size;
84 map = xc_map_foreign_range(xch, dom, PAGE_SIZE, PROT_READ,
85 paddr >>PAGE_SHIFT);
86 if (!map)
87 return 0;
88 memcpy(&pte, map + (paddr & (PAGE_SIZE - 1)), size);
89 munmap(map, PAGE_SIZE);
90 if (!(pte & 1)) {
91 errno = EADDRNOTAVAIL;
92 return 0;
93 }
94 paddr = pte & 0x000ffffffffff000ull;
95 if ((level == 2 || (level == 3 && pt_levels == 4)) && (pte & PTE_PSE)) {
96 mask = ((mask ^ ~-mask) >> 1); /* All bits below first set bit */
97 return ((paddr & ~mask) | (virt & mask)) >> PAGE_SHIFT;
98 }
99 mask >>= (pt_levels == 2 ? 10 : 9);
100 }
101 return paddr >> PAGE_SHIFT;
102 }
103
104 /*
105 * Local variables:
106 * mode: C
107 * c-file-style: "BSD"
108 * c-basic-offset: 4
109 * tab-width: 4
110 * indent-tabs-mode: nil
111 * End:
112 */
113