VirtualBox

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

Last change on this file since 1224 was 62, checked in by vboxsync, 18 years ago

Fixed incorrect checks for LDT and TSS selector updates (!).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 14.1 KB
Line 
1/* $Id: SELMGC.cpp 62 2007-01-16 13:29:12Z 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.virtualbox.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* Internal Functions *
42*******************************************************************************/
43
44/**
45 * Synchronizes one GDT entry (guest -> shadow).
46 *
47 * @returns VBox status code (appropritate for trap handling and GC return).
48 * @param pVM VM Handle.
49 * @param pRegFrame Trap register frame.
50 * @param iGDTEntry The GDT entry to sync.
51 */
52static int selmGCSyncGDTEntry(PVM pVM, PCPUMCTXCORE pRegFrame, unsigned iGDTEntry)
53{
54 Log2(("GDT %04X LDTR=%04X\n", iGDTEntry, CPUMGetGuestLDTR(pVM)));
55
56 /*
57 * Validate the offset.
58 */
59 VBOXGDTR GdtrGuest;
60 CPUMGetGuestGDTR(pVM, &GdtrGuest);
61 unsigned offEntry = iGDTEntry * sizeof(VBOXDESC);
62 if ( iGDTEntry >= SELM_GDT_ELEMENTS
63 || offEntry > GdtrGuest.cbGdt)
64 return VINF_EM_RAW_EMULATE_INSTR_GDT_FAULT;
65
66 /*
67 * Read the guest descriptor.
68 */
69 VBOXDESC Desc;
70 int rc = MMGCRamRead(pVM, &Desc, (uint8_t *)GdtrGuest.pGdt + offEntry, sizeof(VBOXDESC));
71 if (VBOX_FAILURE(rc))
72 return VINF_EM_RAW_EMULATE_INSTR_GDT_FAULT;
73
74 /*
75 * Check for conflicts.
76 */
77 RTSEL Sel = iGDTEntry << X86_SEL_SHIFT;
78 Assert( !(pVM->selm.s.SelCS & ~X86_SEL_MASK)
79 && !(pVM->selm.s.SelDS & ~X86_SEL_MASK)
80 && !(pVM->selm.s.SelCS64 & ~X86_SEL_MASK)
81 && !(pVM->selm.s.SelTSS & ~X86_SEL_MASK)
82 && !(pVM->selm.s.SelTSSTrap08 & ~X86_SEL_MASK));
83 if ( pVM->selm.s.SelCS == Sel
84 || pVM->selm.s.SelDS == Sel
85 || pVM->selm.s.SelCS64 == Sel
86 || pVM->selm.s.SelTSS == Sel
87 || pVM->selm.s.SelTSSTrap08 == Sel)
88 {
89 if (Desc.Gen.u1Present)
90 {
91 Log(("selmGCSyncGDTEntry: Sel=%d Desc=%.8Vhxs: detected conflict!!\n", Sel, &Desc));
92 return VINF_SELM_SYNC_GDT;
93 }
94 Log(("selmGCSyncGDTEntry: Sel=%d Desc=%.8Vhxs: potential conflict (still not present)!\n", Sel, &Desc));
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 (appropritate 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 (appropritate 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 (appropritate 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 STAM_COUNTER_INC(&pVM->selm.s.StatGCWriteGuestTSSHandled);
287 }
288 else
289 {
290 Assert(VBOX_FAILURE(rc));
291 VM_FF_SET(pVM, VM_FF_SELM_SYNC_TSS);
292 STAM_COUNTER_INC(&pVM->selm.s.StatGCWriteGuestTSSUnhandled);
293 if (rc == VERR_EM_INTERPRETER)
294 rc = VINF_EM_RAW_EMULATE_INSTR_TSS_FAULT;
295 }
296 return rc;
297}
298
299
300
301/**
302 * \#PF Virtual Handler callback for Guest write access to the VBox shadow GDT.
303 *
304 * @returns VBox status code (appropritate for trap handling and GC return).
305 * @param pVM VM Handle.
306 * @param uErrorCode CPU Error code.
307 * @param pRegFrame Trap register frame.
308 * @param pvFault The fault address (cr2).
309 * @param pvRange The base address of the handled virtual range.
310 * @param offRange The offset of the access into this range.
311 * (If it's a EIP range this's the EIP, if not it's pvFault.)
312 */
313SELMGCDECL(int) selmgcShadowGDTWriteHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, void *pvFault, void *pvRange, uintptr_t offRange)
314{
315 /*LogCom(("selmgcShadowGDTWriteHandler: eip=%08X pvFault=%08X pvRange=%08X\r\n", pRegFrame->eip, pvFault, pvRange));*/
316 return VERR_SELM_SHADOW_GDT_WRITE;
317}
318
319/**
320 * \#PF Virtual Handler callback for Guest write access to the VBox shadow LDT.
321 *
322 * @returns VBox status code (appropritate for trap handling and GC return).
323 * @param pVM VM Handle.
324 * @param uErrorCode CPU Error code.
325 * @param pRegFrame Trap register frame.
326 * @param pvFault The fault address (cr2).
327 * @param pvRange The base address of the handled virtual range.
328 * @param offRange The offset of the access into this range.
329 * (If it's a EIP range this's the EIP, if not it's pvFault.)
330 */
331SELMGCDECL(int) selmgcShadowLDTWriteHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, void *pvFault, void *pvRange, uintptr_t offRange)
332{
333 /*LogCom(("selmgcShadowLDTWriteHandler: eip=%08X pvFault=%08X pvRange=%08X\r\n", pRegFrame->eip, pvFault, pvRange));*/
334 Assert(pvFault >= pVM->selm.s.GCPtrLdt && (uintptr_t)pvFault < (uintptr_t)pVM->selm.s.GCPtrLdt + 65536 + PAGE_SIZE);
335 return VERR_SELM_SHADOW_LDT_WRITE;
336}
337
338/**
339 * \#PF Virtual Handler callback for Guest write access to the VBox shadow TSS.
340 *
341 * @returns VBox status code (appropritate for trap handling and GC return).
342 * @param pVM VM Handle.
343 * @param uErrorCode CPU Error code.
344 * @param pRegFrame Trap register frame.
345 * @param pvFault The fault address (cr2).
346 * @param pvRange The base address of the handled virtual range.
347 * @param offRange The offset of the access into this range.
348 * (If it's a EIP range this's the EIP, if not it's pvFault.)
349 */
350SELMGCDECL(int) selmgcShadowTSSWriteHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, void *pvFault, void *pvRange, uintptr_t offRange)
351{
352 /*LogCom(("selmgcShadowTSSWriteHandler: eip=%08X pvFault=%08X pvRange=%08X\r\n", pRegFrame->eip, pvFault, pvRange));*/
353 return VERR_SELM_SHADOW_TSS_WRITE;
354}
355
Note: See TracBrowser for help on using the repository browser.

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