1 /*
2 * arch/x86/mm/hap/guest_walk.c
3 *
4 * Guest page table walker
5 * Copyright (c) 2007, AMD Corporation (Wei Huang)
6 * Copyright (c) 2007, XenSource Inc.
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms and conditions of the GNU General Public License,
10 * version 2, as published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
16 *
17 * You should have received a copy of the GNU General Public License along with
18 * this program; If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 /* Allow uniquely identifying static symbols in the 3 generated objects. */
22 asm(".file \"" __OBJECT_FILE__ "\"");
23
24 #include <xen/domain_page.h>
25 #include <xen/paging.h>
26 #include <xen/sched.h>
27 #include "private.h" /* for hap_gva_to_gfn_* */
28
29 #define _hap_gva_to_gfn(levels) hap_gva_to_gfn_##levels##_levels
30 #define hap_gva_to_gfn(levels) _hap_gva_to_gfn(levels)
31
32 #define _hap_p2m_ga_to_gfn(levels) hap_p2m_ga_to_gfn_##levels##_levels
33 #define hap_p2m_ga_to_gfn(levels) _hap_p2m_ga_to_gfn(levels)
34
35 #if GUEST_PAGING_LEVELS > CONFIG_PAGING_LEVELS
36 #error GUEST_PAGING_LEVELS must not exceed CONFIG_PAGING_LEVELS
37 #endif
38
39 #include <asm/guest_pt.h>
40 #include <asm/p2m.h>
41
hap_gva_to_gfn(GUEST_PAGING_LEVELS)42 unsigned long hap_gva_to_gfn(GUEST_PAGING_LEVELS)(
43 struct vcpu *v, struct p2m_domain *p2m, unsigned long gva, uint32_t *pfec)
44 {
45 unsigned long cr3 = v->arch.hvm.guest_cr[3];
46 return hap_p2m_ga_to_gfn(GUEST_PAGING_LEVELS)(v, p2m, cr3, gva, pfec, NULL);
47 }
48
hap_p2m_ga_to_gfn(GUEST_PAGING_LEVELS)49 unsigned long hap_p2m_ga_to_gfn(GUEST_PAGING_LEVELS)(
50 struct vcpu *v, struct p2m_domain *p2m, unsigned long cr3,
51 paddr_t ga, uint32_t *pfec, unsigned int *page_order)
52 {
53 bool walk_ok;
54 mfn_t top_mfn;
55 void *top_map;
56 p2m_type_t p2mt;
57 walk_t gw;
58 gfn_t top_gfn;
59 struct page_info *top_page;
60
61 /* Get the top-level table's MFN */
62 top_gfn = _gfn(cr3 >> PAGE_SHIFT);
63 top_page = p2m_get_page_from_gfn(p2m, top_gfn, &p2mt, NULL,
64 P2M_ALLOC | P2M_UNSHARE);
65 if ( p2m_is_paging(p2mt) )
66 {
67 ASSERT(p2m_is_hostp2m(p2m));
68 *pfec = PFEC_page_paged;
69 if ( top_page )
70 put_page(top_page);
71 p2m_mem_paging_populate(p2m->domain, gaddr_to_gfn(cr3));
72 return gfn_x(INVALID_GFN);
73 }
74 if ( p2m_is_shared(p2mt) )
75 {
76 *pfec = PFEC_page_shared;
77 if ( top_page )
78 put_page(top_page);
79 return gfn_x(INVALID_GFN);
80 }
81 if ( !top_page )
82 {
83 *pfec &= ~PFEC_page_present;
84 goto out_tweak_pfec;
85 }
86 top_mfn = page_to_mfn(top_page);
87
88 /* Map the top-level table and call the tree-walker */
89 ASSERT(mfn_valid(top_mfn));
90 top_map = map_domain_page(top_mfn);
91 #if GUEST_PAGING_LEVELS == 3
92 top_map += (cr3 & ~(PAGE_MASK | 31));
93 #endif
94 walk_ok = guest_walk_tables(v, p2m, ga, &gw, *pfec,
95 top_gfn, top_mfn, top_map);
96 unmap_domain_page(top_map);
97 put_page(top_page);
98
99 /* Interpret the answer */
100 if ( walk_ok )
101 {
102 gfn_t gfn = guest_walk_to_gfn(&gw);
103 struct page_info *page;
104
105 page = p2m_get_page_from_gfn(p2m, gfn, &p2mt, NULL,
106 P2M_ALLOC | P2M_UNSHARE);
107 if ( page )
108 put_page(page);
109 if ( p2m_is_paging(p2mt) )
110 {
111 ASSERT(p2m_is_hostp2m(p2m));
112 *pfec = PFEC_page_paged;
113 p2m_mem_paging_populate(p2m->domain, gfn);
114 return gfn_x(INVALID_GFN);
115 }
116 if ( p2m_is_shared(p2mt) )
117 {
118 *pfec = PFEC_page_shared;
119 return gfn_x(INVALID_GFN);
120 }
121
122 if ( page_order )
123 *page_order = guest_walk_to_page_order(&gw);
124
125 return gfn_x(gfn);
126 }
127
128 *pfec = gw.pfec;
129
130 out_tweak_pfec:
131 /*
132 * SDM Intel 64 Volume 3, Chapter Paging, PAGE-FAULT EXCEPTIONS:
133 * The PFEC_insn_fetch flag is set only when NX or SMEP are enabled.
134 */
135 if ( !hvm_nx_enabled(v) && !hvm_smep_enabled(v) )
136 *pfec &= ~PFEC_insn_fetch;
137
138 return gfn_x(INVALID_GFN);
139 }
140
141
142 /*
143 * Local variables:
144 * mode: C
145 * c-file-style: "BSD"
146 * c-basic-offset: 4
147 * tab-width: 4
148 * indent-tabs-mode: nil
149 * End:
150 */
151