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