VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMGC/SELMGC.cpp@ 1503

最後變更 在這個檔案從1503是 1503,由 vboxsync 提交於 18 年 前

extra checks

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Id
檔案大小: 15.4 KB
 
1/* $Id: SELMGC.cpp 1503 2007-03-15 10:19:01Z vboxsync $ */
2/** @file
3 * SELM - The Selector Manager, Guest Context.
4 */
5
6/*
7 * Copyright (C) 2006 InnoTek Systemberatung GmbH
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.alldomusa.eu.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License as published by the Free Software Foundation,
13 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
14 * distribution. VirtualBox OSE is distributed in the hope that it will
15 * be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * If you received this file as part of a commercial VirtualBox
18 * distribution, then only the terms of your commercial VirtualBox
19 * license agreement apply instead of the previous paragraph.
20 */
21
22/*******************************************************************************
23* Header Files *
24*******************************************************************************/
25#define LOG_GROUP LOG_GROUP_SELM
26#include <VBox/selm.h>
27#include <VBox/mm.h>
28#include <VBox/em.h>
29#include <VBox/trpm.h>
30#include "SELMInternal.h"
31#include <VBox/vm.h>
32
33#include <VBox/param.h>
34#include <VBox/err.h>
35#include <VBox/log.h>
36#include <iprt/assert.h>
37#include <iprt/asm.h>
38
39
40/**
41 * Synchronizes one GDT entry (guest -> shadow).
42 *
43 * @returns VBox status code (appropriate for trap handling and GC return).
44 * @param pVM VM Handle.
45 * @param pRegFrame Trap register frame.
46 * @param iGDTEntry The GDT entry to sync.
47 */
48static int selmGCSyncGDTEntry(PVM pVM, PCPUMCTXCORE pRegFrame, unsigned iGDTEntry)
49{
50 Log2(("GDT %04X LDTR=%04X\n", iGDTEntry, CPUMGetGuestLDTR(pVM)));
51
52 /*
53 * Validate the offset.
54 */
55 VBOXGDTR GdtrGuest;
56 CPUMGetGuestGDTR(pVM, &GdtrGuest);
57 unsigned offEntry = iGDTEntry * sizeof(VBOXDESC);
58 if ( iGDTEntry >= SELM_GDT_ELEMENTS
59 || offEntry > GdtrGuest.cbGdt)
60 return VINF_EM_RAW_EMULATE_INSTR_GDT_FAULT;
61
62 /*
63 * Read the guest descriptor.
64 */
65 VBOXDESC Desc;
66 int rc = MMGCRamRead(pVM, &Desc, (uint8_t *)GdtrGuest.pGdt + offEntry, sizeof(VBOXDESC));
67 if (VBOX_FAILURE(rc))
68 return VINF_EM_RAW_EMULATE_INSTR_GDT_FAULT;
69
70 /*
71 * Check for conflicts.
72 */
73 RTSEL Sel = iGDTEntry << X86_SEL_SHIFT;
74 Assert( !(pVM->selm.s.aHyperSel[SELM_HYPER_SEL_CS] & ~X86_SEL_MASK)
75 && !(pVM->selm.s.aHyperSel[SELM_HYPER_SEL_DS] & ~X86_SEL_MASK)
76 && !(pVM->selm.s.aHyperSel[SELM_HYPER_SEL_CS64] & ~X86_SEL_MASK)
77 && !(pVM->selm.s.aHyperSel[SELM_HYPER_SEL_TSS] & ~X86_SEL_MASK)
78 && !(pVM->selm.s.aHyperSel[SELM_HYPER_SEL_TSS_TRAP08] & ~X86_SEL_MASK));
79 if ( pVM->selm.s.aHyperSel[SELM_HYPER_SEL_CS] == Sel
80 || pVM->selm.s.aHyperSel[SELM_HYPER_SEL_DS] == Sel
81 || pVM->selm.s.aHyperSel[SELM_HYPER_SEL_CS64] == Sel
82 || pVM->selm.s.aHyperSel[SELM_HYPER_SEL_TSS] == Sel
83 || pVM->selm.s.aHyperSel[SELM_HYPER_SEL_TSS_TRAP08] == Sel)
84 {
85 if (Desc.Gen.u1Present)
86 {
87 Log(("selmGCSyncGDTEntry: Sel=%d Desc=%.8Vhxs: detected conflict!!\n", Sel, &Desc));
88 return VINF_SELM_SYNC_GDT;
89 }
90 Log(("selmGCSyncGDTEntry: Sel=%d Desc=%.8Vhxs: potential conflict (still not present)!\n", Sel, &Desc));
91
92 /* Note: we can't continue below or else we'll change the shadow descriptor!! */
93 /* When the guest makes the selector present, then we'll do a GDT sync. */
94 return VINF_SUCCESS;
95 }
96
97 /*
98 * Code and data selectors are generally 1:1, with the
99 * 'little' adjustment we do for DPL 0 selectors.
100 */
101 PVBOXDESC pShadowDescr = &pVM->selm.s.paGdtGC[iGDTEntry];
102 if (Desc.Gen.u1DescType)
103 {
104 /*
105 * Hack for A-bit against Trap E on read-only GDT.
106 */
107 /** @todo Fix this by loading ds and cs before turning off WP. */
108 Desc.Gen.u4Type |= X86_SEL_TYPE_ACCESSED;
109
110 /*
111 * All DPL 0 code and data segments are squeezed into DPL 1.
112 *
113 * We're skipping conforming segments here because those
114 * cannot give us any trouble.
115 */
116 if ( Desc.Gen.u2Dpl == 0
117 && (Desc.Gen.u4Type & (X86_SEL_TYPE_CODE | X86_SEL_TYPE_CONF))
118 != (X86_SEL_TYPE_CODE | X86_SEL_TYPE_CONF) )
119 Desc.Gen.u2Dpl = 1;
120 }
121 else
122 {
123 /*
124 * System type selectors are marked not present.
125 * Recompiler or special handling is required for these.
126 */
127 /** @todo what about interrupt gates and rawr0? */
128 Desc.Gen.u1Present = 0;
129 }
130 //Log(("O: base=%08X limit=%08X attr=%04X\n", pShadowDescr->Gen.u16BaseLow | (pShadowDescr->Gen.u8BaseHigh1 << 16) | (pShadowDescr->Gen.u8BaseHigh2 << 24), pShadowDescr->Gen.u16LimitLow | (pShadowDescr->Gen.u4LimitHigh << 16), (pShadowDescr->au32[1] >> 8) & 0xFFFF ));
131 //Log(("N: base=%08X limit=%08X attr=%04X\n", Desc.Gen.u16BaseLow | (Desc.Gen.u8BaseHigh1 << 16) | (Desc.Gen.u8BaseHigh2 << 24), Desc.Gen.u16LimitLow | (Desc.Gen.u4LimitHigh << 16), (Desc.au32[1] >> 8) & 0xFFFF ));
132 *pShadowDescr = Desc;
133
134 /* Check if we change the LDT selector */
135 if (Sel == CPUMGetGuestLDTR(pVM))
136 {
137 VM_FF_SET(pVM, VM_FF_SELM_SYNC_LDT);
138 return VINF_EM_RAW_EMULATE_INSTR_LDT_FAULT;
139 }
140
141 /* Or the TR selector */
142 if (Sel == CPUMGetGuestTR(pVM))
143 {
144 VM_FF_SET(pVM, VM_FF_SELM_SYNC_TSS);
145 return VINF_EM_RAW_EMULATE_INSTR_TSS_FAULT;
146 }
147
148 return VINF_SUCCESS;
149}
150
151
152/**
153 * \#PF Virtual Handler callback for Guest write access to the Guest's own GDT.
154 *
155 * @returns VBox status code (appropriate for trap handling and GC return).
156 * @param pVM VM Handle.
157 * @param uErrorCode CPU Error code.
158 * @param pRegFrame Trap register frame.
159 * @param pvFault The fault address (cr2).
160 * @param pvRange The base address of the handled virtual range.
161 * @param offRange The offset of the access into this range.
162 * (If it's a EIP range this's the EIP, if not it's pvFault.)
163 */
164SELMGCDECL(int) selmgcGuestGDTWriteHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, void *pvFault, void *pvRange, uintptr_t offRange)
165{
166 LogFlow(("selmgcGuestGDTWriteHandler errcode=%x fault=%08x offRange=%08x\n", uErrorCode, pvFault, offRange));
167
168 /*
169 * First check if this is the LDT entry.
170 * LDT updates are problemous since an invalid LDT entry will cause trouble during worldswitch.
171 */
172 int rc;
173 if (CPUMGetGuestLDTR(pVM) / sizeof(VBOXDESC) == offRange / sizeof(VBOXDESC))
174 {
175 Log(("LDTR selector change -> fall back to HC!!\n"));
176 rc = VINF_EM_RAW_EMULATE_INSTR_GDT_FAULT;
177 /** @todo We're not handling changed to the selectors in LDTR and TR correctly at all.
178 * We should ignore any changes to those and sync them only when they are loaded by the guest! */
179 }
180 else
181 {
182 /*
183 * Attempt to emulate the instruction and sync the affected entries.
184 */
185 /** @todo should check if any affected selectors are loaded. */
186 uint32_t cb;
187 rc = EMInterpretInstruction(pVM, pRegFrame, pvFault, &cb);
188 if (VBOX_SUCCESS(rc) && cb)
189 {
190 unsigned iGDTE1 = offRange / sizeof(VBOXDESC);
191 int rc2 = selmGCSyncGDTEntry(pVM, pRegFrame, iGDTE1);
192 if (rc2 == VINF_SUCCESS)
193 {
194 Assert(cb);
195 unsigned iGDTE2 = (offRange + cb - 1) / sizeof(VBOXDESC);
196 if (iGDTE1 != iGDTE2)
197 rc2 = selmGCSyncGDTEntry(pVM, pRegFrame, iGDTE2);
198 if (rc2 == VINF_SUCCESS)
199 {
200 STAM_COUNTER_INC(&pVM->selm.s.StatGCWriteGuestGDTHandled);
201 return rc;
202 }
203 }
204 if (rc == VINF_SUCCESS || VBOX_FAILURE(rc2))
205 rc = rc2;
206 }
207 else
208 {
209 Assert(VBOX_FAILURE(rc));
210 if (rc == VERR_EM_INTERPRETER)
211 rc = VINF_EM_RAW_EMULATE_INSTR_GDT_FAULT;
212 }
213 }
214 if ( rc != VINF_EM_RAW_EMULATE_INSTR_LDT_FAULT
215 && rc != VINF_EM_RAW_EMULATE_INSTR_TSS_FAULT)
216 {
217 /* Not necessary when we need to go back to the host context to sync the LDT or TSS. */
218 VM_FF_SET(pVM, VM_FF_SELM_SYNC_GDT);
219 }
220 STAM_COUNTER_INC(&pVM->selm.s.StatGCWriteGuestGDTUnhandled);
221 return rc;
222}
223
224
225/**
226 * \#PF Virtual Handler callback for Guest write access to the Guest's own LDT.
227 *
228 * @returns VBox status code (appropriate for trap handling and GC return).
229 * @param pVM VM Handle.
230 * @param uErrorCode CPU Error code.
231 * @param pRegFrame Trap register frame.
232 * @param pvFault The fault address (cr2).
233 * @param pvRange The base address of the handled virtual range.
234 * @param offRange The offset of the access into this range.
235 * (If it's a EIP range this's the EIP, if not it's pvFault.)
236 */
237SELMGCDECL(int) selmgcGuestLDTWriteHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, void *pvFault, void *pvRange, uintptr_t offRange)
238{
239 /** @todo To be implemented. */
240 ////LogCom(("selmgcGuestLDTWriteHandler: eip=%08X pvFault=%08X pvRange=%08X\r\n", pRegFrame->eip, pvFault, pvRange));
241
242 VM_FF_SET(pVM, VM_FF_SELM_SYNC_LDT);
243 STAM_COUNTER_INC(&pVM->selm.s.StatGCWriteGuestLDT);
244 return VINF_EM_RAW_EMULATE_INSTR_LDT_FAULT;
245}
246
247
248/**
249 * \#PF Virtual Handler callback for Guest write access to the Guest's own current TSS.
250 *
251 * @returns VBox status code (appropriate for trap handling and GC return).
252 * @param pVM VM Handle.
253 * @param uErrorCode CPU Error code.
254 * @param pRegFrame Trap register frame.
255 * @param pvFault The fault address (cr2).
256 * @param pvRange The base address of the handled virtual range.
257 * @param offRange The offset of the access into this range.
258 * (If it's a EIP range this's the EIP, if not it's pvFault.)
259 */
260SELMGCDECL(int) selmgcGuestTSSWriteHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, void *pvFault, void *pvRange, uintptr_t offRange)
261{
262 LogFlow(("selmgcGuestTSSWriteHandler errcode=%x fault=%08x offRange=%08x\n", uErrorCode, pvFault, offRange));
263
264 /*
265 * Try emulate the access and compare the R0 ss:esp with the shadow tss values.
266 *
267 * Note, that it's safe to access the TSS after a successfull instruction emulation,
268 * even if the stuff that was changed wasn't the ss0 or esp0 bits. The CPU insists
269 * on the TSS being all one physical page, so ASSUMING that we're not trapping
270 * I/O map accesses this is safe.
271 */
272 uint32_t cb;
273 int rc = EMInterpretInstruction(pVM, pRegFrame, pvFault, &cb);
274 if (VBOX_SUCCESS(rc) && cb)
275 {
276 PCVBOXTSS pGuestTSS = (PVBOXTSS)pVM->selm.s.GCPtrGuestTss;
277 if ( pGuestTSS->esp0 != pVM->selm.s.Tss.esp1
278 || pGuestTSS->ss0 != (pVM->selm.s.Tss.ss1 & ~1)) /* undo raw-r0 */
279 {
280 Log(("selmgcGuestTSSWriteHandler: R0 stack: %RTsel:%VGv -> %RTsel:%VGv\n",
281 (RTSEL)(pVM->selm.s.Tss.ss1 & ~1), pVM->selm.s.Tss.esp1, (RTSEL)pGuestTSS->ss0, pGuestTSS->esp0));
282 pVM->selm.s.Tss.esp1 = pGuestTSS->esp0;
283 pVM->selm.s.Tss.ss1 = pGuestTSS->ss0 | 1;
284 STAM_COUNTER_INC(&pVM->selm.s.StatGCWriteGuestTSSHandledChanged);
285 }
286 if (CPUMGetGuestCR4(pVM) & X86_CR4_VME)
287 {
288 uint32_t offRedirBitmap = pGuestTSS->offIoBitmap - sizeof(pVM->selm.s.Tss.redirBitmap);
289
290 /** @todo not sure how the partial case is handled; probably not allowed */
291 if ( offRedirBitmap <= offRange
292 && offRedirBitmap + sizeof(pVM->selm.s.Tss.redirBitmap) >= offRange + cb
293 && offRedirBitmap + sizeof(pVM->selm.s.Tss.redirBitmap) <= pVM->selm.s.cbGuestTss)
294 {
295 Log(("offIoBitmap=%x offRedirBitmap=%x cbTSS=%x\n", pGuestTSS->offIoBitmap, offRedirBitmap, pVM->selm.s.cbGuestTss));
296 /** @todo only update the changed part. */
297 for (uint32_t i=0;i<sizeof(pVM->selm.s.Tss.redirBitmap)/8;i++)
298 {
299 rc = MMGCRamRead(pVM, &pVM->selm.s.Tss.redirBitmap[i*8], (uint8_t *)pGuestTSS + offRedirBitmap + i*8, 8);
300 AssertMsg(rc == VINF_SUCCESS, ("MMGCRamRead %VGv failed with %Vrc\n", (uint8_t *)pGuestTSS + offRedirBitmap + i*8, rc));
301 }
302 STAM_COUNTER_INC(&pVM->selm.s.StatGCWriteGuestTSSRedir);
303 }
304
305 }
306 STAM_COUNTER_INC(&pVM->selm.s.StatGCWriteGuestTSSHandled);
307 }
308 else
309 {
310 Assert(VBOX_FAILURE(rc));
311 VM_FF_SET(pVM, VM_FF_SELM_SYNC_TSS);
312 STAM_COUNTER_INC(&pVM->selm.s.StatGCWriteGuestTSSUnhandled);
313 if (rc == VERR_EM_INTERPRETER)
314 rc = VINF_EM_RAW_EMULATE_INSTR_TSS_FAULT;
315 }
316 return rc;
317}
318
319
320
321/**
322 * \#PF Virtual Handler callback for Guest write access to the VBox shadow GDT.
323 *
324 * @returns VBox status code (appropriate for trap handling and GC return).
325 * @param pVM VM Handle.
326 * @param uErrorCode CPU Error code.
327 * @param pRegFrame Trap register frame.
328 * @param pvFault The fault address (cr2).
329 * @param pvRange The base address of the handled virtual range.
330 * @param offRange The offset of the access into this range.
331 * (If it's a EIP range this's the EIP, if not it's pvFault.)
332 */
333SELMGCDECL(int) selmgcShadowGDTWriteHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, void *pvFault, void *pvRange, uintptr_t offRange)
334{
335 /*LogCom(("selmgcShadowGDTWriteHandler: eip=%08X pvFault=%08X pvRange=%08X\r\n", pRegFrame->eip, pvFault, pvRange));*/
336 return VERR_SELM_SHADOW_GDT_WRITE;
337}
338
339/**
340 * \#PF Virtual Handler callback for Guest write access to the VBox shadow LDT.
341 *
342 * @returns VBox status code (appropriate for trap handling and GC return).
343 * @param pVM VM Handle.
344 * @param uErrorCode CPU Error code.
345 * @param pRegFrame Trap register frame.
346 * @param pvFault The fault address (cr2).
347 * @param pvRange The base address of the handled virtual range.
348 * @param offRange The offset of the access into this range.
349 * (If it's a EIP range this's the EIP, if not it's pvFault.)
350 */
351SELMGCDECL(int) selmgcShadowLDTWriteHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, void *pvFault, void *pvRange, uintptr_t offRange)
352{
353 /*LogCom(("selmgcShadowLDTWriteHandler: eip=%08X pvFault=%08X pvRange=%08X\r\n", pRegFrame->eip, pvFault, pvRange));*/
354 Assert(pvFault >= pVM->selm.s.GCPtrLdt && (uintptr_t)pvFault < (uintptr_t)pVM->selm.s.GCPtrLdt + 65536 + PAGE_SIZE);
355 return VERR_SELM_SHADOW_LDT_WRITE;
356}
357
358/**
359 * \#PF Virtual Handler callback for Guest write access to the VBox shadow TSS.
360 *
361 * @returns VBox status code (appropriate for trap handling and GC return).
362 * @param pVM VM Handle.
363 * @param uErrorCode CPU Error code.
364 * @param pRegFrame Trap register frame.
365 * @param pvFault The fault address (cr2).
366 * @param pvRange The base address of the handled virtual range.
367 * @param offRange The offset of the access into this range.
368 * (If it's a EIP range this's the EIP, if not it's pvFault.)
369 */
370SELMGCDECL(int) selmgcShadowTSSWriteHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, void *pvFault, void *pvRange, uintptr_t offRange)
371{
372 /*LogCom(("selmgcShadowTSSWriteHandler: eip=%08X pvFault=%08X pvRange=%08X\r\n", pRegFrame->eip, pvFault, pvRange));*/
373 return VERR_SELM_SHADOW_TSS_WRITE;
374}
375
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette