/root/src/xen/xen/arch/x86/pv/grant_table.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * pv/grant_table.c |
3 | | * |
4 | | * Grant table interfaces for PV guests |
5 | | * |
6 | | * Copyright (C) 2017 Wei Liu <wei.liu2@citrix.com> |
7 | | * |
8 | | * This program is free software; you can redistribute it and/or |
9 | | * modify it under the terms and conditions of the GNU General Public |
10 | | * License, version 2, as published by the Free Software Foundation. |
11 | | * |
12 | | * This program is distributed in the hope that it will be useful, |
13 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | | * General Public License for more details. |
16 | | * |
17 | | * You should have received a copy of the GNU General Public |
18 | | * License along with this program; If not, see <http://www.gnu.org/licenses/>. |
19 | | */ |
20 | | |
21 | | #include <xen/types.h> |
22 | | |
23 | | #include <public/grant_table.h> |
24 | | |
25 | | #include <asm/p2m.h> |
26 | | #include <asm/pv/mm.h> |
27 | | |
28 | | #include "mm.h" |
29 | | |
30 | | /* Override macros from asm/page.h to make them work with mfn_t */ |
31 | | #undef mfn_to_page |
32 | 0 | #define mfn_to_page(mfn) __mfn_to_page(mfn_x(mfn)) |
33 | | #undef page_to_mfn |
34 | | #define page_to_mfn(pg) _mfn(__page_to_mfn(pg)) |
35 | | |
36 | | static unsigned int grant_to_pte_flags(unsigned int grant_flags, |
37 | | unsigned int cache_flags) |
38 | 0 | { |
39 | 0 | unsigned int pte_flags = |
40 | 0 | _PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_DIRTY | _PAGE_GNTTAB | _PAGE_NX; |
41 | 0 |
|
42 | 0 | if ( grant_flags & GNTMAP_application_map ) |
43 | 0 | pte_flags |= _PAGE_USER; |
44 | 0 | if ( !(grant_flags & GNTMAP_readonly) ) |
45 | 0 | pte_flags |= _PAGE_RW; |
46 | 0 |
|
47 | 0 | pte_flags |= MASK_INSR((grant_flags >> _GNTMAP_guest_avail0), _PAGE_AVAIL); |
48 | 0 | pte_flags |= cacheattr_to_pte_flags(cache_flags >> 5); |
49 | 0 |
|
50 | 0 | return pte_flags; |
51 | 0 | } |
52 | | |
53 | | int create_grant_pv_mapping(uint64_t addr, unsigned long frame, |
54 | | unsigned int flags, unsigned int cache_flags) |
55 | 0 | { |
56 | 0 | struct vcpu *curr = current; |
57 | 0 | struct domain *currd = curr->domain; |
58 | 0 | l1_pgentry_t nl1e, ol1e = { }, *pl1e; |
59 | 0 | struct page_info *page; |
60 | 0 | mfn_t gl1mfn; |
61 | 0 | int rc = GNTST_general_error; |
62 | 0 |
|
63 | 0 | nl1e = l1e_from_pfn(frame, grant_to_pte_flags(flags, cache_flags)); |
64 | 0 | nl1e = adjust_guest_l1e(nl1e, currd); |
65 | 0 |
|
66 | 0 | /* |
67 | 0 | * The meaning of addr depends on GNTMAP_contains_pte. It is either a |
68 | 0 | * machine address of an L1e the guest has nominated to be altered, or a |
69 | 0 | * linear address we need to look up the appropriate L1e for. |
70 | 0 | */ |
71 | 0 | if ( flags & GNTMAP_contains_pte ) |
72 | 0 | { |
73 | 0 | /* addr must be suitably aligned, or we will corrupt adjacent ptes. */ |
74 | 0 | if ( !IS_ALIGNED(addr, sizeof(nl1e)) ) |
75 | 0 | { |
76 | 0 | gdprintk(XENLOG_WARNING, |
77 | 0 | "Misaligned PTE address %"PRIx64"\n", addr); |
78 | 0 | goto out; |
79 | 0 | } |
80 | 0 |
|
81 | 0 | gl1mfn = _mfn(addr >> PAGE_SHIFT); |
82 | 0 |
|
83 | 0 | if ( !get_page_from_mfn(gl1mfn, currd) ) |
84 | 0 | goto out; |
85 | 0 |
|
86 | 0 | pl1e = map_domain_page(gl1mfn) + (addr & ~PAGE_MASK); |
87 | 0 | } |
88 | 0 | else |
89 | 0 | { |
90 | 0 | /* Guest trying to pass an out-of-range linear address? */ |
91 | 0 | if ( is_pv_32bit_domain(currd) && addr != (uint32_t)addr ) |
92 | 0 | goto out; |
93 | 0 |
|
94 | 0 | pl1e = map_guest_l1e(addr, &gl1mfn); |
95 | 0 |
|
96 | 0 | if ( !pl1e ) |
97 | 0 | { |
98 | 0 | gdprintk(XENLOG_WARNING, |
99 | 0 | "Could not find L1 PTE for linear address %"PRIx64"\n", |
100 | 0 | addr); |
101 | 0 | goto out; |
102 | 0 | } |
103 | 0 |
|
104 | 0 | if ( !get_page_from_mfn(gl1mfn, currd) ) |
105 | 0 | goto out_unmap; |
106 | 0 | } |
107 | 0 |
|
108 | 0 | page = mfn_to_page(gl1mfn); |
109 | 0 | if ( !page_lock(page) ) |
110 | 0 | goto out_put; |
111 | 0 |
|
112 | 0 | if ( (page->u.inuse.type_info & PGT_type_mask) != PGT_l1_page_table ) |
113 | 0 | goto out_unlock; |
114 | 0 |
|
115 | 0 | ol1e = *pl1e; |
116 | 0 | if ( UPDATE_ENTRY(l1, pl1e, ol1e, nl1e, mfn_x(gl1mfn), curr, 0) ) |
117 | 0 | rc = GNTST_okay; |
118 | 0 |
|
119 | 0 | out_unlock: |
120 | 0 | page_unlock(page); |
121 | 0 | out_put: |
122 | 0 | put_page(page); |
123 | 0 | out_unmap: |
124 | 0 | unmap_domain_page(pl1e); |
125 | 0 |
|
126 | 0 | if ( rc == GNTST_okay ) |
127 | 0 | put_page_from_l1e(ol1e, currd); |
128 | 0 |
|
129 | 0 | out: |
130 | 0 | return rc; |
131 | 0 | } |
132 | | |
133 | | /* |
134 | | * This exists soley for implementing GNTABOP_unmap_and_replace, the ABI of |
135 | | * which is bizarre. This GNTTABOP isn't used any more, but was used by |
136 | | * classic-xen kernels and PVOps Linux before the M2P_OVERRIDE infrastructure |
137 | | * was replaced with something which actually worked. |
138 | | * |
139 | | * Look up the L1e mapping linear, and zap it. Return the L1e via *out. |
140 | | * Returns a boolean indicating success. If success, the caller is |
141 | | * responsible for calling put_page_from_l1e(). |
142 | | */ |
143 | | static bool steal_linear_address(unsigned long linear, l1_pgentry_t *out) |
144 | 0 | { |
145 | 0 | struct vcpu *curr = current; |
146 | 0 | struct domain *currd = curr->domain; |
147 | 0 | l1_pgentry_t *pl1e, ol1e; |
148 | 0 | struct page_info *page; |
149 | 0 | mfn_t gl1mfn; |
150 | 0 | bool okay = false; |
151 | 0 |
|
152 | 0 | ASSERT(is_pv_domain(currd)); |
153 | 0 |
|
154 | 0 | pl1e = map_guest_l1e(linear, &gl1mfn); |
155 | 0 | if ( !pl1e ) |
156 | 0 | { |
157 | 0 | gdprintk(XENLOG_WARNING, |
158 | 0 | "Could not find L1 PTE for linear %"PRIx64"\n", linear); |
159 | 0 | goto out; |
160 | 0 | } |
161 | 0 |
|
162 | 0 | if ( !get_page_from_mfn(gl1mfn, currd) ) |
163 | 0 | goto out_unmap; |
164 | 0 |
|
165 | 0 | page = mfn_to_page(gl1mfn); |
166 | 0 | if ( !page_lock(page) ) |
167 | 0 | goto out_put; |
168 | 0 |
|
169 | 0 | if ( (page->u.inuse.type_info & PGT_type_mask) != PGT_l1_page_table ) |
170 | 0 | goto out_unlock; |
171 | 0 |
|
172 | 0 | ol1e = *pl1e; |
173 | 0 | okay = UPDATE_ENTRY(l1, pl1e, ol1e, l1e_empty(), mfn_x(gl1mfn), curr, 0); |
174 | 0 |
|
175 | 0 | out_unlock: |
176 | 0 | page_unlock(page); |
177 | 0 | out_put: |
178 | 0 | put_page(page); |
179 | 0 | out_unmap: |
180 | 0 | unmap_domain_page(pl1e); |
181 | 0 |
|
182 | 0 | if ( okay ) |
183 | 0 | *out = ol1e; |
184 | 0 |
|
185 | 0 | out: |
186 | 0 | return okay; |
187 | 0 | } |
188 | | |
189 | | /* |
190 | | * Passing a new_addr of zero is taken to mean destroy. Passing a non-zero |
191 | | * new_addr has only ever been available via GNTABOP_unmap_and_replace, and |
192 | | * only when !(flags & GNTMAP_contains_pte). |
193 | | */ |
194 | | int replace_grant_pv_mapping(uint64_t addr, unsigned long frame, |
195 | | uint64_t new_addr, unsigned int flags) |
196 | 0 | { |
197 | 0 | struct vcpu *curr = current; |
198 | 0 | struct domain *currd = curr->domain; |
199 | 0 | l1_pgentry_t nl1e = l1e_empty(), ol1e, *pl1e; |
200 | 0 | struct page_info *page; |
201 | 0 | mfn_t gl1mfn; |
202 | 0 | int rc = GNTST_general_error; |
203 | 0 | unsigned int grant_pte_flags = grant_to_pte_flags(flags, 0); |
204 | 0 |
|
205 | 0 | /* |
206 | 0 | * On top of the explicit settings done by create_grant_pv_mapping() |
207 | 0 | * also open-code relevant parts of adjust_guest_l1e(). Don't mirror |
208 | 0 | * available and cachability flags, though. |
209 | 0 | */ |
210 | 0 | if ( !is_pv_32bit_domain(currd) ) |
211 | 0 | grant_pte_flags |= (grant_pte_flags & _PAGE_USER) |
212 | 0 | ? _PAGE_GLOBAL |
213 | 0 | : _PAGE_GUEST_KERNEL | _PAGE_USER; |
214 | 0 |
|
215 | 0 | /* |
216 | 0 | * addr comes from Xen's active_entry tracking, and was used successfully |
217 | 0 | * to create a grant. |
218 | 0 | * |
219 | 0 | * The meaning of addr depends on GNTMAP_contains_pte. It is either a |
220 | 0 | * machine address of an L1e the guest has nominated to be altered, or a |
221 | 0 | * linear address we need to look up the appropriate L1e for. |
222 | 0 | */ |
223 | 0 | if ( flags & GNTMAP_contains_pte ) |
224 | 0 | { |
225 | 0 | /* Replace not available in this addressing mode. */ |
226 | 0 | if ( new_addr ) |
227 | 0 | goto out; |
228 | 0 |
|
229 | 0 | /* Sanity check that we won't clobber the pagetable. */ |
230 | 0 | if ( !IS_ALIGNED(addr, sizeof(nl1e)) ) |
231 | 0 | { |
232 | 0 | ASSERT_UNREACHABLE(); |
233 | 0 | goto out; |
234 | 0 | } |
235 | 0 |
|
236 | 0 | gl1mfn = _mfn(addr >> PAGE_SHIFT); |
237 | 0 |
|
238 | 0 | if ( !get_page_from_mfn(gl1mfn, currd) ) |
239 | 0 | goto out; |
240 | 0 |
|
241 | 0 | pl1e = map_domain_page(gl1mfn) + (addr & ~PAGE_MASK); |
242 | 0 | } |
243 | 0 | else |
244 | 0 | { |
245 | 0 | if ( is_pv_32bit_domain(currd) ) |
246 | 0 | { |
247 | 0 | if ( addr != (uint32_t)addr ) |
248 | 0 | { |
249 | 0 | ASSERT_UNREACHABLE(); |
250 | 0 | goto out; |
251 | 0 | } |
252 | 0 |
|
253 | 0 | /* Guest trying to pass an out-of-range linear address? */ |
254 | 0 | if ( new_addr != (uint32_t)new_addr ) |
255 | 0 | goto out; |
256 | 0 | } |
257 | 0 |
|
258 | 0 | if ( new_addr && !steal_linear_address(new_addr, &nl1e) ) |
259 | 0 | goto out; |
260 | 0 |
|
261 | 0 | pl1e = map_guest_l1e(addr, &gl1mfn); |
262 | 0 |
|
263 | 0 | if ( !pl1e ) |
264 | 0 | goto out; |
265 | 0 |
|
266 | 0 | if ( !get_page_from_mfn(gl1mfn, currd) ) |
267 | 0 | goto out_unmap; |
268 | 0 | } |
269 | 0 |
|
270 | 0 | page = mfn_to_page(gl1mfn); |
271 | 0 |
|
272 | 0 | if ( !page_lock(page) ) |
273 | 0 | goto out_put; |
274 | 0 |
|
275 | 0 | if ( (page->u.inuse.type_info & PGT_type_mask) != PGT_l1_page_table ) |
276 | 0 | goto out_unlock; |
277 | 0 |
|
278 | 0 | ol1e = *pl1e; |
279 | 0 |
|
280 | 0 | /* |
281 | 0 | * Check that the address supplied is actually mapped to frame (with |
282 | 0 | * appropriate permissions). |
283 | 0 | */ |
284 | 0 | if ( unlikely(l1e_get_pfn(ol1e) != frame) || |
285 | 0 | unlikely((l1e_get_flags(ol1e) ^ grant_pte_flags) & |
286 | 0 | (_PAGE_PRESENT | _PAGE_RW)) ) |
287 | 0 | { |
288 | 0 | gdprintk(XENLOG_ERR, |
289 | 0 | "PTE %"PRIpte" for %"PRIx64" doesn't match grant (%"PRIpte")\n", |
290 | 0 | l1e_get_intpte(ol1e), addr, |
291 | 0 | l1e_get_intpte(l1e_from_pfn(frame, grant_pte_flags))); |
292 | 0 | goto out_unlock; |
293 | 0 | } |
294 | 0 |
|
295 | 0 | if ( unlikely((l1e_get_flags(ol1e) ^ grant_pte_flags) & |
296 | 0 | ~(_PAGE_AVAIL | PAGE_CACHE_ATTRS)) ) |
297 | 0 | gdprintk(XENLOG_WARNING, |
298 | 0 | "PTE flags %x for %"PRIx64" don't match grant (%x)\n", |
299 | 0 | l1e_get_flags(ol1e), addr, grant_pte_flags); |
300 | 0 |
|
301 | 0 | if ( UPDATE_ENTRY(l1, pl1e, ol1e, nl1e, mfn_x(gl1mfn), curr, 0) ) |
302 | 0 | rc = GNTST_okay; |
303 | 0 |
|
304 | 0 | out_unlock: |
305 | 0 | page_unlock(page); |
306 | 0 | out_put: |
307 | 0 | put_page(page); |
308 | 0 | out_unmap: |
309 | 0 | unmap_domain_page(pl1e); |
310 | 0 |
|
311 | 0 | out: |
312 | 0 | /* If there was an error, we are still responsible for the stolen pte. */ |
313 | 0 | if ( rc ) |
314 | 0 | put_page_from_l1e(nl1e, currd); |
315 | 0 |
|
316 | 0 | return rc; |
317 | 0 | } |
318 | | |
319 | | /* |
320 | | * Local variables: |
321 | | * mode: C |
322 | | * c-file-style: "BSD" |
323 | | * c-basic-offset: 4 |
324 | | * tab-width: 4 |
325 | | * indent-tabs-mode: nil |
326 | | * End: |
327 | | */ |