Develop and Download Open Source Software

Browse Subversion Repository

Contents of /branches/ept-devel/vmm/core/cpu_mmu_spt.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 25 - (show annotations) (download) (as text)
Fri May 4 15:02:17 2012 UTC (12 months, 2 weeks ago) by yuichi_xy
File MIME type: text/x-csrc
File size: 9840 byte(s)
BIOS が設定した MTTR の値から物理メモリアドレス領域のキャッシュ属性を取得し、EPT に反映するようにした。不要な関数テーブルである struct cpuid_func と struct msr_func を削除した。
1 /*
2 * Copyright (c) 2007, 2008 University of Tsukuba
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * 3. Neither the name of the University of Tsukuba nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 */
29 /*
30 * Copyright (c) 2010-2012 Yuichi Watanabe
31 */
32
33 /* MMU emulation, Shadow Page Tables (SPT) */
34
35 #include <common/list.h>
36 #include <core/initfunc.h>
37 #include <core/printf.h>
38 #include <core/spinlock.h>
39 #include <core/string.h>
40 #include "asm.h"
41 #include "constants.h"
42 #include "cpu_mmu.h"
43 #include "cpu_mmu_spt.h"
44 #include "current.h"
45 #include "mm.h"
46 #include "mmio.h"
47 #include "msr_pass.h"
48 #include "panic.h"
49 #include "pcpu.h"
50 #include "vmmcall_status.h"
51
52 struct map_page_data1 {
53 unsigned int write : 1;
54 unsigned int user : 1;
55 unsigned int wp : 1;
56 u32 cache_flag;
57 };
58
59 struct map_page_data2 {
60 unsigned int rw : 1;
61 unsigned int us : 1;
62 unsigned int nx : 1;
63 };
64
65 #define GFN_GLOBAL 0xFFFFFFFFFFFFFFFEULL
66 #define GFN_UNUSED 0xFFFFFFFFFFFFFFFFULL
67
68 static bool guest64 (void);
69 static void update_cr3 (void);
70 static void invalidate_page (ulong virtual_addr);
71 static void map_page (u64 v, struct map_page_data1 m1,
72 struct map_page_data2 m2[5], u64 gfns[5], int glvl,
73 phys_t hphys);
74 static void init_vcpu(void);
75
76 static void
77 invalidate_page (ulong v)
78 {
79 pmap_t p;
80 u64 tmp;
81
82 pmap_open_vmm (&p, current->spt.cr3tbl_phys, current->spt.levels);
83 pmap_setvirt (&p, v, 2);
84 tmp = pmap_read (&p);
85 if (tmp & PDE_P_BIT) {
86 if (tmp & PDE_AVAILABLE1_BIT) {
87 pmap_write (&p, tmp & ~PDE_AVAILABLE1_BIT, 0xFFF);
88 pmap_setlevel (&p, 1);
89 pmap_clear (&p);
90 if (current->spt.levels >= 3) {
91 pmap_setvirt (&p, v ^ PAGESIZE2M, 2);
92 tmp = pmap_read (&p);
93 if (tmp & PDE_P_BIT) {
94 pmap_write (&p,
95 tmp & ~PDE_AVAILABLE1_BIT,
96 0xFFF);
97 pmap_setlevel (&p, 1);
98 pmap_clear (&p);
99 }
100 }
101 } else {
102 pmap_setlevel (&p, 1);
103 tmp = pmap_read (&p);
104 pmap_write (&p, 0, 0xFFF);
105 }
106 }
107 pmap_close (&p);
108 }
109
110 static void
111 invalidate_all_spt(void)
112 {
113 pmap_t p;
114 struct spt_page *spt_page;
115
116 pmap_open_vmm (&p, current->spt.cr3tbl_phys, current->spt.levels);
117 pmap_clear (&p);
118 pmap_close (&p);
119 while((spt_page = LIST2_POP(current->spt.shadow_l1, list)) != NULL) {
120 spt_page->pte_virt = NULL;
121 LIST2_ADD(current->spt.shadow_free, list, spt_page);
122 }
123 while((spt_page = LIST2_POP(current->spt.shadow_lu, list)) != NULL) {
124 spt_page->pte_virt = NULL;
125 LIST2_ADD(current->spt.shadow_free, list, spt_page);
126 }
127 }
128
129 static void
130 map_page (u64 v, struct map_page_data1 m1, struct map_page_data2 m2[5],
131 u64 gfns[5], int glvl, phys_t hphys)
132 {
133 pmap_t p;
134 u64 tmp;
135 int l;
136 ulong cr0;
137 struct spt_page *spt_page;
138
139 current->vmctl.read_control_reg (CONTROL_REG_CR0, &cr0);
140 if (!(cr0 & CR0_WP_BIT)) {
141 if (!m1.user && m1.write) {
142 m2[0].us = 0;
143 m2[0].rw = 1;
144 }
145 }
146 pmap_open_vmm (&p, current->spt.cr3tbl_phys, current->spt.levels);
147 pmap_setvirt (&p, v, 1);
148 tmp = pmap_read (&p);
149 for (; (l = pmap_getreadlevel (&p)) > 1; tmp = pmap_read (&p)) {
150 pmap_setlevel (&p, l);
151 spt_page = LIST2_POP(current->spt.shadow_free, list);
152 if (spt_page == NULL) {
153 spt_page = LIST2_POP(current->spt.shadow_l1, list);
154 if (spt_page == NULL) {
155 printf("DEBUG: shadow_free and shadow_l1 are empty.");
156 invalidate_all_spt();
157 continue;
158 }
159 if (spt_page->pte_virt) {
160 *spt_page->pte_virt = 0;
161 }
162 }
163 spt_page->pte_virt = pmap_pointer(&p);
164 if (l == 1) {
165 LIST2_ADD(current->spt.shadow_l1, list, spt_page);
166 } else {
167 LIST2_ADD(current->spt.shadow_lu, list, spt_page);
168 }
169 pmap_write (&p, spt_page->phys | PDE_P_BIT, PDE_P_BIT);
170 pmap_setlevel (&p, l - 1);
171 pmap_clear (&p);
172 pmap_setlevel (&p, 1);
173 }
174 if (glvl > 1 && (gfns[1] == GFN_GLOBAL || gfns[1] == GFN_UNUSED)) {
175 pmap_setlevel (&p, 2);
176 tmp = pmap_read (&p);
177 if (!(tmp & PDE_AVAILABLE1_BIT))
178 pmap_write (&p, tmp | PDE_AVAILABLE1_BIT, 0xFFF);
179 pmap_setlevel (&p, 1);
180 }
181 pmap_write (&p, (hphys & (~PAGESIZE_MASK)) | PTE_P_BIT |
182 (m2[0].rw ? PTE_RW_BIT : 0) |
183 (m2[0].us ? PTE_US_BIT : 0) |
184 (m2[0].nx ? PTE_NX_BIT : 0) |
185 mm_cache_flag_to_pte_attr (m1.cache_flag),
186 PTE_P_BIT | PTE_RW_BIT | PTE_US_BIT |
187 PTE_PWT_BIT | PTE_PCD_BIT | PTE_PAT_BIT);
188 pmap_close (&p);
189 }
190
191 static void
192 update_cr3 (void)
193 {
194 current->spt.levels = guest64 () ? 4 : 3;
195 invalidate_all_spt();
196 current->vmctl.spt_setcr3 (current->spt.cr3tbl_phys);
197 }
198
199 static bool
200 spt_tlbflush (void)
201 {
202 return true;
203 }
204
205 static void
206 init_vcpu(void)
207 {
208 int i;
209 vmmerr_t err;
210
211 err = alloc_page(&current->spt.cr3tbl, &current->spt.cr3tbl_phys);
212 if (err) {
213 panic("Failed to allocate memory for a cr3-table.");
214 }
215 memset(current->spt.cr3tbl, 0, PAGESIZE);
216 LIST2_HEAD_INIT(current->spt.shadow_free);
217 LIST2_HEAD_INIT(current->spt.shadow_l1);
218 LIST2_HEAD_INIT(current->spt.shadow_lu);
219 for (i = 0; i < NUM_OF_SPTTBL; i++) {
220 err = alloc_page(&current->spt.spt_page[i].virt, &current->spt.spt_page[i].phys);
221 if (err) {
222 panic("Failed to allocate memory for a shadow page table.");
223 }
224 current->spt.spt_page[i].pte_virt = NULL;
225 LIST2_ADD(current->spt.shadow_free, list, &current->spt.spt_page[i]);
226 }
227 }
228
229 static bool
230 guest64 (void)
231 {
232 u64 efer;
233
234 current->vmctl.read_msr (MSR_IA32_EFER, &efer);
235 if (efer & MSR_IA32_EFER_LMA_BIT)
236 return true;
237 return false;
238 }
239
240 /* this function is called when shadow page table entries are cleared
241 from TLB. every VM exit in VT. */
242 bool
243 cpu_mmu_spt_tlbflush (void)
244 {
245 return spt_tlbflush ();
246 }
247
248 /* this function is called when a guest sets CR3 */
249 void
250 cpu_mmu_spt_updatecr3 (void)
251 {
252 update_cr3 ();
253 }
254
255 /* this function is called by INVLPG in a guest */
256 /* deletes TLB entry regardless of G bit */
257 void
258 cpu_mmu_spt_invalidate (ulong virtual_addr)
259 {
260 invalidate_page (virtual_addr);
261 }
262
263 static void
264 set_m1 (u64 entry0, bool write, bool user, bool wp, struct map_page_data1 *m1)
265 {
266 m1->write = write;
267 m1->user = user;
268 m1->wp = wp;
269 m1->cache_flag = msr_pte_to_cache_flag(entry0);
270 }
271
272 static void
273 set_m2 (u64 entries[5], int levels, struct map_page_data2 m2[5])
274 {
275 unsigned int rw = 1, us = 1, nx = 0;
276 int i;
277 u64 entry;
278
279 for (i = levels; i >= 0; i--) {
280 entry = entries[i];
281 if (!(entry & PDE_RW_BIT))
282 rw = 0;
283 if (!(entry & PDE_US_BIT))
284 us = 0;
285 if (entry & PDE_NX_BIT)
286 nx = 1;
287 m2[i].rw = rw;
288 m2[i].us = us;
289 m2[i].nx = nx;
290 }
291 if (!(entries[0] & PTE_D_BIT))
292 m2[0].rw = 0;
293 }
294
295 static void
296 set_gfns (u64 entries[5], int levels, u64 gfns[5])
297 {
298 int i;
299 u64 entry;
300
301 for (i = levels; i >= 0; i--) {
302 entry = entries[i];
303 if (i == 1 && (entry & PDE_PS_BIT)) {
304 if (entry & PDE_G_BIT)
305 gfns[i] = GFN_GLOBAL;
306 else
307 gfns[i] = GFN_UNUSED;
308 continue;
309 }
310 gfns[i] = (entry & PTE_ADDR_MASK64) >> PAGESIZE_SHIFT;
311 }
312 }
313
314 /* handling a page fault of a guest */
315 void
316 cpu_mmu_spt_pagefault (ulong err, ulong cr2)
317 {
318 int levels;
319 vmmerr_t ret;
320 bool wr, us, ex, wp;
321 ulong cr0, cr3, cr4;
322 struct map_page_data1 m1;
323 struct map_page_data2 m2[5];
324 u64 efer, gfns[5], entries[5];
325 phys_t hphys;
326
327 current->vmctl.read_control_reg(CONTROL_REG_CR0, &cr0);
328 current->vmctl.read_control_reg(CONTROL_REG_CR3, &cr3);
329 current->vmctl.read_control_reg(CONTROL_REG_CR4, &cr4);
330 current->vmctl.read_msr(MSR_IA32_EFER, &efer);
331 wr = !!(err & PAGEFAULT_ERR_WR_BIT);
332 us = !!(err & PAGEFAULT_ERR_US_BIT);
333 ex = !!(err & PAGEFAULT_ERR_ID_BIT);
334 wp = !!(cr0 & CR0_WP_BIT);
335 ret = mmu_get_pte(cr2, cr0, cr3, cr4, efer, wr, us, ex, entries,
336 &levels);
337 if (ret != VMMERR_SUCCESS) {
338 mmu_generate_pagefault(ret, err & PAGEFAULT_ERR_WR_BIT,
339 err & PAGEFAULT_ERR_US_BIT, cr2);
340 return;
341 }
342 set_m1(entries[0], wr, us, wp, &m1);
343 set_m2(entries, levels, m2);
344 set_gfns(entries, levels, gfns);
345
346 mmio_lock();
347 ret = mmio_pagefault(gfns[0] << PAGESIZE_SHIFT);
348 mmio_unlock();
349 if (ret != VMMERR_NODEV) {
350 if (ret != VMMERR_SUCCESS &&
351 (ret < VMMERR_PAGE_NOT_PRESENT ||
352 ret > VMMERR_PAGE_BAD_RESERVED_BIT)) {
353 panic("Failed to emulate accessing to MMIO area. ret 0x%x",
354 ret);
355 }
356 return;
357 }
358
359 hphys = current->vm->gmm.gp2hp((gfns[0] << PAGESIZE_SHIFT) +
360 (cr2 & PAGESIZE_MASK));
361 if (hphys == GMM_NO_MAPPING) {
362 ret = cpu_interpreter();
363 if (ret != VMMERR_SUCCESS &&
364 (ret < VMMERR_PAGE_NOT_PRESENT ||
365 ret > VMMERR_PAGE_BAD_RESERVED_BIT)) {
366 panic("Failed to emulate accessing to no mapping area. ret 0x%x",
367 ret);
368 }
369 return;
370 }
371 map_page(cr2, m1, m2, gfns, levels, hphys);
372 }
373
374 void
375 cpu_mmu_spt_init(void)
376 {
377 init_vcpu();
378 }

SourceForge.JP is a Japanese version of SourceForge.net. For developments that are not related to Japan, we recommend you to use SourceForge.net.