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