1 #ifndef __PV_MM_H__
2 #define __PV_MM_H__
3 
4 #include <asm/shadow.h>
5 
6 l1_pgentry_t *map_guest_l1e(unsigned long linear, mfn_t *gl1mfn);
7 
8 int new_guest_cr3(mfn_t mfn);
9 
10 /*
11  * Read the guest's l1e that maps this address, from the kernel-mode
12  * page tables.
13  */
guest_get_eff_kern_l1e(unsigned long linear)14 static inline l1_pgentry_t guest_get_eff_kern_l1e(unsigned long linear)
15 {
16     struct vcpu *curr = current;
17     bool user_mode = !(curr->arch.flags & TF_kernel_mode);
18     l1_pgentry_t l1e;
19 
20     ASSERT(!paging_mode_translate(curr->domain));
21     ASSERT(!paging_mode_external(curr->domain));
22 
23     if ( user_mode )
24         toggle_guest_pt(curr);
25 
26     if ( unlikely(!__addr_ok(linear)) ||
27          get_unsafe(l1e, &__linear_l1_table[l1_linear_offset(linear)]) )
28         l1e = l1e_empty();
29 
30     if ( user_mode )
31         toggle_guest_pt(curr);
32 
33     return l1e;
34 }
35 
paging_write_guest_entry(struct vcpu * v,intpte_t * p,intpte_t new,mfn_t gmfn)36 static inline void paging_write_guest_entry(
37     struct vcpu *v, intpte_t *p, intpte_t new, mfn_t gmfn)
38 {
39     if ( unlikely(paging_mode_shadow(v->domain)) )
40         shadow_write_guest_entry(v, p, new, gmfn);
41     else
42         write_atomic(p, new);
43 }
44 
45 
46 /* Compare and exchange a guest pagetable entry.  Returns the old value. */
paging_cmpxchg_guest_entry(struct vcpu * v,intpte_t * p,intpte_t old,intpte_t new,mfn_t gmfn)47 static inline intpte_t paging_cmpxchg_guest_entry(
48     struct vcpu *v, intpte_t *p, intpte_t old, intpte_t new, mfn_t gmfn)
49 {
50     if ( unlikely(paging_mode_shadow(v->domain)) )
51         return shadow_cmpxchg_guest_entry(v, p, old, new, gmfn);
52     return cmpxchg(p, old, new);
53 }
54 
55 /*
56  * PTE updates can be done with ordinary writes except:
57  *  1. Debug builds get extra checking by using CMPXCHG[8B].
58  */
59 #ifndef NDEBUG
60 #define PTE_UPDATE_WITH_CMPXCHG
61 #else
62 #undef PTE_UPDATE_WITH_CMPXCHG
63 #endif
64 
65 /*
66  * How to write an entry to the guest pagetables.
67  * Returns false for failure (pointer not valid), true for success.
68  */
update_intpte(intpte_t * p,intpte_t old,intpte_t new,mfn_t mfn,struct vcpu * v,bool preserve_ad)69 static inline bool update_intpte(intpte_t *p, intpte_t old, intpte_t new,
70                                  mfn_t mfn, struct vcpu *v, bool preserve_ad)
71 {
72     bool rv = true;
73 
74 #ifndef PTE_UPDATE_WITH_CMPXCHG
75     if ( !preserve_ad )
76         paging_write_guest_entry(v, p, new, mfn);
77     else
78 #endif
79     {
80         for ( ; ; )
81         {
82             intpte_t _new = new, t;
83 
84             if ( preserve_ad )
85                 _new |= old & (_PAGE_ACCESSED | _PAGE_DIRTY);
86 
87             t = paging_cmpxchg_guest_entry(v, p, old, _new, mfn);
88 
89             if ( t == old )
90                 break;
91 
92             /* Allowed to change in Accessed/Dirty flags only. */
93             BUG_ON((t ^ old) & ~(intpte_t)(_PAGE_ACCESSED|_PAGE_DIRTY));
94 
95             old = t;
96         }
97     }
98     return rv;
99 }
100 
101 /*
102  * Macro that wraps the appropriate type-changes around update_intpte().
103  * Arguments are: type, ptr, old, new, mfn, vcpu
104  */
105 #define UPDATE_ENTRY(_t,_p,_o,_n,_m,_v,_ad)                         \
106     update_intpte(&_t ## e_get_intpte(*(_p)),                       \
107                   _t ## e_get_intpte(_o), _t ## e_get_intpte(_n),   \
108                   (_m), (_v), (_ad))
109 
adjust_guest_l1e(l1_pgentry_t l1e,const struct domain * d)110 static always_inline l1_pgentry_t adjust_guest_l1e(l1_pgentry_t l1e,
111                                                    const struct domain *d)
112 {
113     if ( likely(l1e_get_flags(l1e) & _PAGE_PRESENT) &&
114          likely(!is_pv_32bit_domain(d)) )
115     {
116         /* _PAGE_GUEST_KERNEL page cannot have the Global bit set. */
117         if ( (l1e_get_flags(l1e) & (_PAGE_GUEST_KERNEL | _PAGE_GLOBAL)) ==
118              (_PAGE_GUEST_KERNEL | _PAGE_GLOBAL) )
119             gdprintk(XENLOG_WARNING, "Global bit is set in kernel page %lx\n",
120                      l1e_get_pfn(l1e));
121 
122         if ( !(l1e_get_flags(l1e) & _PAGE_USER) )
123             l1e_add_flags(l1e, (_PAGE_GUEST_KERNEL | _PAGE_USER));
124 
125         if ( !(l1e_get_flags(l1e) & _PAGE_GUEST_KERNEL) )
126             l1e_add_flags(l1e, (_PAGE_GLOBAL | _PAGE_USER));
127     }
128 
129     return l1e;
130 }
131 
adjust_guest_l2e(l2_pgentry_t l2e,const struct domain * d)132 static always_inline l2_pgentry_t adjust_guest_l2e(l2_pgentry_t l2e,
133                                                    const struct domain *d)
134 {
135     if ( likely(l2e_get_flags(l2e) & _PAGE_PRESENT) &&
136          likely(!is_pv_32bit_domain(d)) )
137         l2e_add_flags(l2e, _PAGE_USER);
138 
139     return l2e;
140 }
141 
adjust_guest_l3e(l3_pgentry_t l3e,const struct domain * d)142 static always_inline l3_pgentry_t adjust_guest_l3e(l3_pgentry_t l3e,
143                                                    const struct domain *d)
144 {
145     if ( likely(l3e_get_flags(l3e) & _PAGE_PRESENT) )
146         l3e_add_flags(l3e, (likely(!is_pv_32bit_domain(d))
147                             ? _PAGE_USER : _PAGE_USER | _PAGE_RW));
148 
149     return l3e;
150 }
151 
unadjust_guest_l3e(l3_pgentry_t l3e,const struct domain * d)152 static always_inline l3_pgentry_t unadjust_guest_l3e(l3_pgentry_t l3e,
153                                                      const struct domain *d)
154 {
155     if ( unlikely(is_pv_32bit_domain(d)) &&
156          likely(l3e_get_flags(l3e) & _PAGE_PRESENT) )
157         l3e_remove_flags(l3e, _PAGE_USER | _PAGE_RW | _PAGE_ACCESSED);
158 
159     return l3e;
160 }
161 
adjust_guest_l4e(l4_pgentry_t l4e,const struct domain * d)162 static always_inline l4_pgentry_t adjust_guest_l4e(l4_pgentry_t l4e,
163                                                    const struct domain *d)
164 {
165     /*
166      * When shadowing an L4 behind the guests back (e.g. for per-pcpu
167      * purposes), we cannot efficiently sync access bit updates from hardware
168      * (on the shadow tables) back into the guest view.
169      *
170      * We therefore unconditionally set _PAGE_ACCESSED even in the guests
171      * view.  This will appear to the guest as a CPU which proactively pulls
172      * all valid L4e's into its TLB, which is compatible with the x86 ABI.
173      *
174      * At the time of writing, all PV guests set the access bit anyway, so
175      * this is no actual change in their behaviour.
176      */
177     if ( likely(l4e_get_flags(l4e) & _PAGE_PRESENT) )
178         l4e_add_flags(l4e, (_PAGE_ACCESSED |
179                             (is_pv_32bit_domain(d) ? 0 : _PAGE_USER)));
180 
181     return l4e;
182 }
183 
184 #endif /* __PV_MM_H__ */
185