1 /* SPDX-License-Identifier: GPL-2.0-only */
2 /*
3 * arch/x86/mm/hap/guest_walk.c
4 *
5 * Guest page table walker
6 * Copyright (c) 2007, AMD Corporation (Wei Huang)
7 * Copyright (c) 2007, XenSource Inc.
8 */
9
10 #include <xen/domain_page.h>
11 #include <xen/paging.h>
12 #include <xen/sched.h>
13 #include "private.h" /* for hap_gva_to_gfn_* */
14
15 #define _hap_gva_to_gfn(levels) hap_gva_to_gfn_##levels##_levels
16 #define hap_gva_to_gfn(levels) _hap_gva_to_gfn(levels)
17
18 #define _hap_p2m_ga_to_gfn(levels) hap_p2m_ga_to_gfn_##levels##_levels
19 #define hap_p2m_ga_to_gfn(levels) _hap_p2m_ga_to_gfn(levels)
20
21 #if GUEST_PAGING_LEVELS > CONFIG_PAGING_LEVELS
22 #error GUEST_PAGING_LEVELS must not exceed CONFIG_PAGING_LEVELS
23 #endif
24
25 #include <asm/guest_pt.h>
26 #include <asm/p2m.h>
27
hap_gva_to_gfn(GUEST_PAGING_LEVELS)28 unsigned long cf_check hap_gva_to_gfn(GUEST_PAGING_LEVELS)(
29 struct vcpu *v, struct p2m_domain *p2m, unsigned long gva, uint32_t *pfec)
30 {
31 unsigned long cr3 = v->arch.hvm.guest_cr[3];
32 return hap_p2m_ga_to_gfn(GUEST_PAGING_LEVELS)(v, p2m, cr3, gva, pfec, NULL);
33 }
34
hap_p2m_ga_to_gfn(GUEST_PAGING_LEVELS)35 unsigned long cf_check hap_p2m_ga_to_gfn(GUEST_PAGING_LEVELS)(
36 struct vcpu *v, struct p2m_domain *p2m, unsigned long cr3,
37 paddr_t ga, uint32_t *pfec, unsigned int *page_order)
38 {
39 bool walk_ok;
40 mfn_t top_mfn;
41 void *top_map;
42 p2m_type_t p2mt;
43 walk_t gw;
44 gfn_t top_gfn;
45 struct page_info *top_page;
46
47 /* Get the top-level table's MFN */
48 top_gfn = _gfn(cr3 >> PAGE_SHIFT);
49 top_page = p2m_get_page_from_gfn(p2m, top_gfn, &p2mt, NULL,
50 P2M_ALLOC | P2M_UNSHARE);
51 if ( p2m_is_paging(p2mt) )
52 {
53 ASSERT(p2m_is_hostp2m(p2m));
54 *pfec = PFEC_page_paged;
55 if ( top_page )
56 put_page(top_page);
57 p2m_mem_paging_populate(p2m->domain, gaddr_to_gfn(cr3));
58 return gfn_x(INVALID_GFN);
59 }
60 if ( p2m_is_shared(p2mt) )
61 {
62 *pfec = PFEC_page_shared;
63 if ( top_page )
64 put_page(top_page);
65 return gfn_x(INVALID_GFN);
66 }
67 if ( !top_page )
68 {
69 *pfec &= ~PFEC_page_present;
70 goto out_tweak_pfec;
71 }
72 top_mfn = page_to_mfn(top_page);
73
74 /* Map the top-level table and call the tree-walker */
75 ASSERT(mfn_valid(top_mfn));
76 top_map = map_domain_page(top_mfn);
77 #if GUEST_PAGING_LEVELS == 3
78 top_map += (cr3 & ~(PAGE_MASK | 31));
79 #endif
80 walk_ok = guest_walk_tables(v, p2m, ga, &gw, *pfec,
81 top_gfn, top_mfn, top_map);
82 unmap_domain_page(top_map);
83 put_page(top_page);
84
85 /* Interpret the answer */
86 if ( walk_ok )
87 {
88 gfn_t gfn = guest_walk_to_gfn(&gw);
89 struct page_info *page;
90
91 page = p2m_get_page_from_gfn(p2m, gfn, &p2mt, NULL,
92 P2M_ALLOC | P2M_UNSHARE);
93 if ( page )
94 put_page(page);
95 if ( p2m_is_paging(p2mt) )
96 {
97 ASSERT(p2m_is_hostp2m(p2m));
98 *pfec = PFEC_page_paged;
99 p2m_mem_paging_populate(p2m->domain, gfn);
100 return gfn_x(INVALID_GFN);
101 }
102 if ( p2m_is_shared(p2mt) )
103 {
104 *pfec = PFEC_page_shared;
105 return gfn_x(INVALID_GFN);
106 }
107
108 if ( page_order )
109 *page_order = guest_walk_to_page_order(&gw);
110
111 return gfn_x(gfn);
112 }
113
114 *pfec = gw.pfec;
115
116 out_tweak_pfec:
117 /*
118 * SDM Intel 64 Volume 3, Chapter Paging, PAGE-FAULT EXCEPTIONS:
119 * The PFEC_insn_fetch flag is set only when NX or SMEP are enabled.
120 */
121 if ( !hvm_nx_enabled(v) && !hvm_smep_enabled(v) )
122 *pfec &= ~PFEC_insn_fetch;
123
124 return gfn_x(INVALID_GFN);
125 }
126
127
128 /*
129 * Local variables:
130 * mode: C
131 * c-file-style: "BSD"
132 * c-basic-offset: 4
133 * tab-width: 4
134 * indent-tabs-mode: nil
135 * End:
136 */
137