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