VirtualBox

source: vbox/trunk/src/VBox/VMM/SELM.cpp@ 106

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

style

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 81.9 KB
Line 
1/* $Id: SELM.cpp 106 2007-01-17 15:32:14Z vboxsync $ */
2/** @file
3 * SELM - The Selector manager.
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/cpum.h>
28#include <VBox/stam.h>
29#include <VBox/mm.h>
30#include <VBox/pdm.h>
31#include <VBox/pgm.h>
32#include <VBox/trpm.h>
33#include <VBox/dbgf.h>
34#include "SELMInternal.h"
35#include <VBox/vm.h>
36#include <VBox/err.h>
37#include <VBox/param.h>
38
39#include <iprt/assert.h>
40#include <VBox/log.h>
41#include <iprt/asm.h>
42#include <iprt/string.h>
43#include <iprt/thread.h>
44#include <iprt/string.h>
45#include "x86context.h"
46
47
48/**
49 * Enable or disable tracking of Guest's GDT/LDT/TSS.
50 * @{
51 */
52#define SELM_TRACK_GUEST_GDT_CHANGES
53#define SELM_TRACK_GUEST_LDT_CHANGES
54#define SELM_TRACK_GUEST_TSS_CHANGES
55/** @} */
56
57/**
58 * Enable or disable tracking of Shadow GDT/LDT/TSS.
59 * @{
60 */
61#define SELM_TRACK_SHADOW_GDT_CHANGES
62#define SELM_TRACK_SHADOW_LDT_CHANGES
63#define SELM_TRACK_SHADOW_TSS_CHANGES
64/** @} */
65
66
67/** SELM saved state version. */
68#define SELM_SAVED_STATE_VERSION 5
69
70/*******************************************************************************
71* Internal Functions *
72*******************************************************************************/
73static DECLCALLBACK(int) selmR3Save(PVM pVM, PSSMHANDLE pSSM);
74static DECLCALLBACK(int) selmR3Load(PVM pVM, PSSMHANDLE pSSM, uint32_t u32Version);
75static DECLCALLBACK(void) selmR3InfoGdt(PVM pVM, PCDBGFINFOHLP pHlp, const char *pszArgs);
76static DECLCALLBACK(void) selmR3InfoGdtGuest(PVM pVM, PCDBGFINFOHLP pHlp, const char *pszArgs);
77static DECLCALLBACK(void) selmR3InfoLdt(PVM pVM, PCDBGFINFOHLP pHlp, const char *pszArgs);
78static DECLCALLBACK(void) selmR3InfoLdtGuest(PVM pVM, PCDBGFINFOHLP pHlp, const char *pszArgs);
79//static DECLCALLBACK(void) selmR3InfoTss(PVM pVM, PCDBGFINFOHLP pHlp, const char *pszArgs);
80//static DECLCALLBACK(void) selmR3InfoTssGuest(PVM pVM, PCDBGFINFOHLP pHlp, const char *pszArgs);
81static DECLCALLBACK(int) selmGuestGDTWriteHandler(PVM pVM, RTGCPTR GCPtr, void *pvPhys, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, void *pvUser);
82static DECLCALLBACK(int) selmGuestLDTWriteHandler(PVM pVM, RTGCPTR GCPtr, void *pvPhys, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, void *pvUser);
83static DECLCALLBACK(int) selmGuestTSSWriteHandler(PVM pVM, RTGCPTR GCPtr, void *pvPhys, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, void *pvUser);
84
85
86
87/**
88 * Initializes the SELM.
89 *
90 * @returns VBox status code.
91 * @param pVM The VM to operate on.
92 */
93SELMR3DECL(int) SELMR3Init(PVM pVM)
94{
95 LogFlow(("SELMR3Init\n"));
96
97 /*
98 * Assert alignment and sizes.
99 */
100 AssertRelease(!(RT_OFFSETOF(VM, selm.s) & 31));
101 AssertRelease(!(RT_OFFSETOF(VM, selm.s.Tss) & 15));
102 AssertRelease(sizeof(pVM->selm.s) <= sizeof(pVM->selm.padding));
103
104 /*
105 * Init the structure.
106 */
107 pVM->selm.s.offVM = RT_OFFSETOF(VM, selm);
108 pVM->selm.s.SelCS = (SELM_GDT_ELEMENTS - 0x1) << 3;
109 pVM->selm.s.SelDS = (SELM_GDT_ELEMENTS - 0x2) << 3;
110 pVM->selm.s.SelCS64 = (SELM_GDT_ELEMENTS - 0x3) << 3;
111 pVM->selm.s.SelTSS = (SELM_GDT_ELEMENTS - 0x4) << 3;
112 pVM->selm.s.SelTSSTrap08 = (SELM_GDT_ELEMENTS - 0x5) << 3;
113
114 /*
115 * Allocate GDT table.
116 */
117 int rc = MMR3HyperAllocOnceNoRel(pVM, sizeof(pVM->selm.s.paGdtHC[0]) * SELM_GDT_ELEMENTS,
118 PAGE_SIZE, MM_TAG_SELM, (void **)&pVM->selm.s.paGdtHC);
119 AssertRCReturn(rc, rc);
120
121 /*
122 * Allocate LDT area.
123 */
124 rc = MMR3HyperAllocOnceNoRel(pVM, _64K + PAGE_SIZE, PAGE_SIZE, MM_TAG_SELM, &pVM->selm.s.HCPtrLdt);
125 AssertRCReturn(rc, rc);
126
127 /*
128 * Init Guest's and Shadow GDT, LDT, TSS changes control variables.
129 */
130 pVM->selm.s.cbEffGuestGdtLimit = 0;
131 pVM->selm.s.GuestGdtr.pGdt = ~0;
132 pVM->selm.s.GCPtrGuestLdt = ~0;
133 pVM->selm.s.GCPtrGuestTss = ~0;
134
135 pVM->selm.s.paGdtGC = 0;
136 pVM->selm.s.GCPtrLdt = ~0;
137 pVM->selm.s.GCPtrTss = ~0;
138 pVM->selm.s.GCSelTss = ~0;
139
140 pVM->selm.s.fDisableMonitoring = false;
141 pVM->selm.s.fSyncTSSRing0Stack = false;
142
143 /*
144 * Register the saved state data unit.
145 */
146 rc = SSMR3RegisterInternal(pVM, "selm", 1, SELM_SAVED_STATE_VERSION, sizeof(SELM),
147 NULL, selmR3Save, NULL,
148 NULL, selmR3Load, NULL);
149 if (VBOX_FAILURE(rc))
150 return rc;
151
152 /*
153 * Statistics.
154 */
155 STAM_REG(pVM, &pVM->selm.s.StatGCWriteGuestGDTHandled, STAMTYPE_COUNTER, "/SELM/GC/Write/Guest/GDTInt", STAMUNIT_OCCURENCES, "The number of handled writes to the Guest GDT.");
156 STAM_REG(pVM, &pVM->selm.s.StatGCWriteGuestGDTUnhandled, STAMTYPE_COUNTER, "/SELM/GC/Write/Guest/GDTEmu", STAMUNIT_OCCURENCES, "The number of unhandled writes to the Guest GDT.");
157 STAM_REG(pVM, &pVM->selm.s.StatGCWriteGuestLDT, STAMTYPE_COUNTER, "/SELM/GC/Write/Guest/LDT", STAMUNIT_OCCURENCES, "The number of writes to the Guest LDT was detected.");
158 STAM_REG(pVM, &pVM->selm.s.StatGCWriteGuestTSSHandled, STAMTYPE_COUNTER, "/SELM/GC/Write/Guest/TSSInt", STAMUNIT_OCCURENCES, "The number of handled writes to the Guest TSS.");
159 STAM_REG(pVM, &pVM->selm.s.StatGCWriteGuestTSSHandledChanged,STAMTYPE_COUNTER, "/SELM/GC/Write/Guest/TSSIntChg", STAMUNIT_OCCURENCES, "The number of handled writes to the Guest TSS where the R0 stack changed.");
160 STAM_REG(pVM, &pVM->selm.s.StatGCWriteGuestTSSUnhandled, STAMTYPE_COUNTER, "/SELM/GC/Write/Guest/TSSEmu", STAMUNIT_OCCURENCES, "The number of unhandled writes to the Guest TSS.");
161 STAM_REG(pVM, &pVM->selm.s.StatTSSSync, STAMTYPE_PROFILE, "/PROF/SELM/TSSSync", STAMUNIT_TICKS_PER_CALL, "Profiling of the SELMR3SyncTSS() body.");
162 STAM_REG(pVM, &pVM->selm.s.StatUpdateFromCPUM, STAMTYPE_PROFILE, "/PROF/SELM/UpdateFromCPUM", STAMUNIT_TICKS_PER_CALL, "Profiling of the SELMR3UpdateFromCPUM() body.");
163
164 /*
165 * Default action when entering raw mode for the first time
166 */
167 VM_FF_SET(pVM, VM_FF_SELM_SYNC_TSS);
168 VM_FF_SET(pVM, VM_FF_SELM_SYNC_GDT);
169 VM_FF_SET(pVM, VM_FF_SELM_SYNC_LDT);
170
171 /*
172 * Register info handlers.
173 */
174 DBGFR3InfoRegisterInternal(pVM, "gdt", "Displays the shadow GDT. No arguments.", &selmR3InfoGdt);
175 DBGFR3InfoRegisterInternal(pVM, "gdtguest", "Displays the guest GDT. No arguments.", &selmR3InfoGdtGuest);
176 DBGFR3InfoRegisterInternal(pVM, "ldt", "Displays the shadow LDT. No arguments.", &selmR3InfoLdt);
177 DBGFR3InfoRegisterInternal(pVM, "ldtguest", "Displays the guest LDT. No arguments.", &selmR3InfoLdtGuest);
178 //DBGFR3InfoRegisterInternal(pVM, "tss", "Displays the shadow TSS. No arguments.", &selmR3InfoTss);
179 //DBGFR3InfoRegisterInternal(pVM, "tssguest", "Displays the guest TSS. No arguments.", &selmR3InfoTssGuest);
180
181 return rc;
182}
183
184
185/**
186 * Finalizes HMA page attributes.
187 *
188 * @returns VBox status code.
189 * @param pVM The VM handle.
190 */
191SELMR3DECL(int) SELMR3InitFinalize(PVM pVM)
192{
193 /*
194 * Make Double Fault work with WP enabled?
195 *
196 * The double fault is a task switch and thus requires write access to the GDT of the TSS
197 * (to set it busy), to the old TSS (to store state), and to the Trap 8 TSS for the back link.
198 *
199 * Since we in enabling write access to these pages make ourself vulnerable to attacks,
200 * it is not possible to do this by default.
201 */
202 bool f;
203 int rc = CFGMR3QueryBool(CFGMR3GetRoot(pVM), "DoubleFault", &f);
204#if !defined(DEBUG_bird)
205 if (VBOX_SUCCESS(rc) && f)
206#endif
207 {
208 PVBOXDESC paGdt = pVM->selm.s.paGdtHC;
209 rc = PGMMapSetPage(pVM, MMHyperHC2GC(pVM, &paGdt[pVM->selm.s.SelTSSTrap08 >> 3]), sizeof(paGdt[0]),
210 X86_PTE_RW | X86_PTE_P | X86_PTE_A | X86_PTE_D);
211 AssertRC(rc);
212 rc = PGMMapSetPage(pVM, MMHyperHC2GC(pVM, &paGdt[pVM->selm.s.SelTSS >> 3]), sizeof(paGdt[0]),
213 X86_PTE_RW | X86_PTE_P | X86_PTE_A | X86_PTE_D);
214 AssertRC(rc);
215 rc = PGMMapSetPage(pVM, VM_GUEST_ADDR(pVM, &pVM->selm.s.Tss), sizeof(pVM->selm.s.Tss),
216 X86_PTE_RW | X86_PTE_P | X86_PTE_A | X86_PTE_D);
217 AssertRC(rc);
218 rc = PGMMapSetPage(pVM, VM_GUEST_ADDR(pVM, &pVM->selm.s.TssTrap08), sizeof(pVM->selm.s.TssTrap08),
219 X86_PTE_RW | X86_PTE_P | X86_PTE_A | X86_PTE_D);
220 AssertRC(rc);
221 }
222 return VINF_SUCCESS;
223}
224
225
226/**
227 * Applies relocations to data and code managed by this
228 * component. This function will be called at init and
229 * whenever the VMM need to relocate it self inside the GC.
230 *
231 * @param pVM The VM.
232 */
233SELMR3DECL(void) SELMR3Relocate(PVM pVM)
234{
235 LogFlow(("SELMR3Relocate\n"));
236 PVBOXDESC paGdt = pVM->selm.s.paGdtHC;
237
238 /*
239 * Update GDTR and selector.
240 */
241 CPUMSetHyperGDTR(pVM, MMHyperHC2GC(pVM, paGdt), SELM_GDT_ELEMENTS * sizeof(paGdt[0]) - 1);
242
243 /** @todo selector relocations should be a seperate operation? */
244 CPUMSetHyperCS(pVM, pVM->selm.s.SelCS);
245 CPUMSetHyperDS(pVM, pVM->selm.s.SelDS);
246 CPUMSetHyperES(pVM, pVM->selm.s.SelDS);
247 CPUMSetHyperSS(pVM, pVM->selm.s.SelDS);
248 CPUMSetHyperTR(pVM, pVM->selm.s.SelTSS);
249
250 /*
251 * Set up global code and data descriptors for use in the guest context.
252 * Both are wide open (base 0, limit 4GB)
253 */
254 PVBOXDESC pDesc = &paGdt[pVM->selm.s.SelCS >> 3];
255 pDesc->Gen.u16LimitLow = 0xffff;
256 pDesc->Gen.u4LimitHigh = 0xf;
257 pDesc->Gen.u16BaseLow = 0;
258 pDesc->Gen.u8BaseHigh1 = 0;
259 pDesc->Gen.u8BaseHigh2 = 0;
260 pDesc->Gen.u4Type = X86_SELTYPE_MEM_EXECUTEREAD_ACC;
261 pDesc->Gen.u1DescType = 1; /* not system, but code/data */
262 pDesc->Gen.u2Dpl = 0; /* supervisor */
263 pDesc->Gen.u1Present = 1;
264 pDesc->Gen.u1Available = 0;
265 pDesc->Gen.u1Reserved = 0;
266 pDesc->Gen.u1DefBig = 1; /* def 32 bit */
267 pDesc->Gen.u1Granularity = 1; /* 4KB limit */
268
269 /* data */
270 pDesc = &paGdt[pVM->selm.s.SelDS >> 3];
271 pDesc->Gen.u16LimitLow = 0xffff;
272 pDesc->Gen.u4LimitHigh = 0xf;
273 pDesc->Gen.u16BaseLow = 0;
274 pDesc->Gen.u8BaseHigh1 = 0;
275 pDesc->Gen.u8BaseHigh2 = 0;
276 pDesc->Gen.u4Type = X86_SELTYPE_MEM_READWRITE_ACC;
277 pDesc->Gen.u1DescType = 1; /* not system, but code/data */
278 pDesc->Gen.u2Dpl = 0; /* supervisor */
279 pDesc->Gen.u1Present = 1;
280 pDesc->Gen.u1Available = 0;
281 pDesc->Gen.u1Reserved = 0;
282 pDesc->Gen.u1DefBig = 1; /* big */
283 pDesc->Gen.u1Granularity = 1; /* 4KB limit */
284
285 /* 64-bit mode code (& data?) */
286 pDesc = &paGdt[pVM->selm.s.SelCS64 >> 3];
287 pDesc->Gen.u16LimitLow = 0xffff;
288 pDesc->Gen.u4LimitHigh = 0xf;
289 pDesc->Gen.u16BaseLow = 0;
290 pDesc->Gen.u8BaseHigh1 = 0;
291 pDesc->Gen.u8BaseHigh2 = 0;
292 pDesc->Gen.u4Type = X86_SELTYPE_MEM_EXECUTEREAD_ACC;
293 pDesc->Gen.u1DescType = 1; /* not system, but code/data */
294 pDesc->Gen.u2Dpl = 0; /* supervisor */
295 pDesc->Gen.u1Present = 1;
296 pDesc->Gen.u1Available = 0;
297 pDesc->Gen.u1Reserved = 1; /* The Long (L) attribute bit. */
298 pDesc->Gen.u1DefBig = 0; /* With L=1 this must be 0. */
299 pDesc->Gen.u1Granularity = 1; /* 4KB limit */
300
301 /*
302 * TSS descriptor
303 */
304 pDesc = &paGdt[pVM->selm.s.SelTSS >> 3];
305 RTGCPTR pGCTSS = VM_GUEST_ADDR(pVM, &pVM->selm.s.Tss);
306 pDesc->Gen.u16BaseLow = RT_LOWORD(pGCTSS);
307 pDesc->Gen.u8BaseHigh1 = RT_BYTE3(pGCTSS);
308 pDesc->Gen.u8BaseHigh2 = RT_BYTE4(pGCTSS);
309 pDesc->Gen.u16LimitLow = sizeof(VBOXTSS) - 1;
310 pDesc->Gen.u4LimitHigh = 0;
311 pDesc->Gen.u4Type = X86_SELTYPE_SYS_386_TSS_AVAIL;
312 pDesc->Gen.u1DescType = 0; /* system */
313 pDesc->Gen.u2Dpl = 0; /* supervisor */
314 pDesc->Gen.u1Present = 1;
315 pDesc->Gen.u1Available = 0;
316 pDesc->Gen.u1Reserved = 0;
317 pDesc->Gen.u1DefBig = 0;
318 pDesc->Gen.u1Granularity = 0; /* byte limit */
319
320 /*
321 * TSS descriptor for trap 08
322 */
323 pDesc = &paGdt[pVM->selm.s.SelTSSTrap08 >> 3];
324 pDesc->Gen.u16LimitLow = sizeof(VBOXTSS) - 1;
325 pDesc->Gen.u4LimitHigh = 0;
326 pGCTSS = VM_GUEST_ADDR(pVM, &pVM->selm.s.TssTrap08);
327 pDesc->Gen.u16BaseLow = RT_LOWORD(pGCTSS);
328 pDesc->Gen.u8BaseHigh1 = RT_BYTE3(pGCTSS);
329 pDesc->Gen.u8BaseHigh2 = RT_BYTE4(pGCTSS);
330 pDesc->Gen.u4Type = X86_SELTYPE_SYS_386_TSS_AVAIL;
331 pDesc->Gen.u1DescType = 0; /* system */
332 pDesc->Gen.u2Dpl = 0; /* supervisor */
333 pDesc->Gen.u1Present = 1;
334 pDesc->Gen.u1Available = 0;
335 pDesc->Gen.u1Reserved = 0;
336 pDesc->Gen.u1DefBig = 0;
337 pDesc->Gen.u1Granularity = 0; /* byte limit */
338
339/** @todo SELM must be called when any of the CR3s changes during a cpu mode change. */
340/** @todo PGM knows the proper CR3 values these days, not CPUM. */
341 /*
342 * Update the TSSes.
343 */
344 /* Current TSS */
345 pVM->selm.s.Tss.cr3 = PGMGetHyperCR3(pVM);
346 pVM->selm.s.Tss.ss0 = pVM->selm.s.SelDS;
347 pVM->selm.s.Tss.esp0 = VMMGetStackGC(pVM);
348 pVM->selm.s.Tss.cs = pVM->selm.s.SelCS;
349 pVM->selm.s.Tss.ds = pVM->selm.s.SelDS;
350 pVM->selm.s.Tss.es = pVM->selm.s.SelDS;
351 pVM->selm.s.Tss.offIoBitmap = sizeof(VBOXTSS);
352
353 /* trap 08 */
354 pVM->selm.s.TssTrap08.cr3 = PGMGetInterGCCR3(pVM); /* this should give use better survival chances. */
355 pVM->selm.s.TssTrap08.ss0 = pVM->selm.s.SelDS;
356 pVM->selm.s.TssTrap08.ss = pVM->selm.s.SelDS;
357 pVM->selm.s.TssTrap08.esp0 = VMMGetStackGC(pVM) - PAGE_SIZE / 2; /* upper half can be analysed this way. */
358 pVM->selm.s.TssTrap08.esp = pVM->selm.s.TssTrap08.esp0;
359 pVM->selm.s.TssTrap08.ebp = pVM->selm.s.TssTrap08.esp0;
360 pVM->selm.s.TssTrap08.cs = pVM->selm.s.SelCS;
361 pVM->selm.s.TssTrap08.ds = pVM->selm.s.SelDS;
362 pVM->selm.s.TssTrap08.es = pVM->selm.s.SelDS;
363 pVM->selm.s.TssTrap08.fs = 0;
364 pVM->selm.s.TssTrap08.gs = 0;
365 pVM->selm.s.TssTrap08.selLdt = 0;
366 pVM->selm.s.TssTrap08.eflags = 0x2; /* all cleared */
367 pVM->selm.s.TssTrap08.ecx = VM_GUEST_ADDR(pVM, &pVM->selm.s.Tss); /* setup ecx to normal Hypervisor TSS address. */
368 pVM->selm.s.TssTrap08.edi = pVM->selm.s.TssTrap08.ecx;
369 pVM->selm.s.TssTrap08.eax = pVM->selm.s.TssTrap08.ecx;
370 pVM->selm.s.TssTrap08.edx = VM_GUEST_ADDR(pVM, pVM); /* setup edx VM address. */
371 pVM->selm.s.TssTrap08.edi = pVM->selm.s.TssTrap08.edx;
372 pVM->selm.s.TssTrap08.ebx = pVM->selm.s.TssTrap08.edx;
373 pVM->selm.s.TssTrap08.offIoBitmap = sizeof(VBOXTSS);
374 /* TRPM will be updating the eip */
375
376 if (!pVM->selm.s.fDisableMonitoring)
377 {
378 /*
379 * Update shadow GDT/LDT/TSS write access handlers.
380 */
381 int rc;
382#ifdef SELM_TRACK_SHADOW_GDT_CHANGES
383 if (pVM->selm.s.paGdtGC != 0)
384 {
385 rc = PGMHandlerVirtualDeregister(pVM, pVM->selm.s.paGdtGC);
386 AssertRC(rc);
387 }
388 pVM->selm.s.paGdtGC = MMHyperHC2GC(pVM, paGdt);
389 rc = PGMR3HandlerVirtualRegister(pVM, PGMVIRTHANDLERTYPE_HYPERVISOR, pVM->selm.s.paGdtGC,
390 pVM->selm.s.paGdtGC + SELM_GDT_ELEMENTS * sizeof(paGdt[0]) - 1,
391 0, 0, "selmgcShadowGDTWriteHandler", 0, "Shadow GDT write access handler");
392 AssertRC(rc);
393#endif
394#ifdef SELM_TRACK_SHADOW_TSS_CHANGES
395 if (pVM->selm.s.GCPtrTss != ~0U)
396 {
397 rc = PGMHandlerVirtualDeregister(pVM, pVM->selm.s.GCPtrTss);
398 AssertRC(rc);
399 }
400 pVM->selm.s.GCPtrTss = VM_GUEST_ADDR(pVM, &pVM->selm.s.Tss);
401 rc = PGMR3HandlerVirtualRegister(pVM, PGMVIRTHANDLERTYPE_HYPERVISOR, pVM->selm.s.GCPtrTss,
402 pVM->selm.s.GCPtrTss + sizeof(pVM->selm.s.Tss) - 1,
403 0, 0, "selmgcShadowTSSWriteHandler", 0, "Shadow TSS write access handler");
404 AssertRC(rc);
405#endif
406
407 /*
408 * Update the GC LDT region handler and address.
409 */
410#ifdef SELM_TRACK_SHADOW_LDT_CHANGES
411 if (pVM->selm.s.GCPtrLdt != ~0U)
412 {
413 rc = PGMHandlerVirtualDeregister(pVM, pVM->selm.s.GCPtrLdt);
414 AssertRC(rc);
415 }
416#endif
417 pVM->selm.s.GCPtrLdt = MMHyperHC2GC(pVM, pVM->selm.s.HCPtrLdt);
418#ifdef SELM_TRACK_SHADOW_LDT_CHANGES
419 rc = PGMR3HandlerVirtualRegister(pVM, PGMVIRTHANDLERTYPE_HYPERVISOR, pVM->selm.s.GCPtrLdt,
420 pVM->selm.s.GCPtrLdt + _64K + PAGE_SIZE - 1,
421 0, 0, "selmgcShadowLDTWriteHandler", 0, "Shadow LDT write access handler");
422 AssertRC(rc);
423#endif
424 }
425}
426
427
428/**
429 * Notification callback which is called whenever there is a chance that a CR3
430 * value might have changed.
431 * This is called by PGM.
432 *
433 * @param pVM The VM handle
434 */
435SELMR3DECL(void) SELMR3PagingModeChanged(PVM pVM)
436{
437 pVM->selm.s.Tss.cr3 = PGMGetHyperCR3(pVM);
438 pVM->selm.s.TssTrap08.cr3 = PGMGetInterGCCR3(pVM);
439}
440
441
442/**
443 * Terminates the SELM.
444 *
445 * Termination means cleaning up and freeing all resources,
446 * the VM it self is at this point powered off or suspended.
447 *
448 * @returns VBox status code.
449 * @param pVM The VM to operate on.
450 */
451SELMR3DECL(int) SELMR3Term(PVM pVM)
452{
453 return 0;
454}
455
456
457/**
458 * The VM is being reset.
459 *
460 * For the SELM component this means that any GDT/LDT/TSS monitors
461 * needs to be removed.
462 *
463 * @param pVM VM handle.
464 */
465SELMR3DECL(void) SELMR3Reset(PVM pVM)
466{
467 LogFlow(("SELMR3Reset:\n"));
468 VM_ASSERT_EMT(pVM);
469
470 /*
471 * Uninstall guest GDT/LDT/TSS write access handlers.
472 */
473 int rc;
474#ifdef SELM_TRACK_GUEST_GDT_CHANGES
475 if (pVM->selm.s.GuestGdtr.pGdt != ~0U && pVM->selm.s.fGDTRangeRegistered)
476 {
477 rc = PGMHandlerVirtualDeregister(pVM, pVM->selm.s.GuestGdtr.pGdt);
478 AssertRC(rc);
479 pVM->selm.s.GuestGdtr.pGdt = ~0U;
480 pVM->selm.s.GuestGdtr.cbGdt = 0;
481 }
482 pVM->selm.s.fGDTRangeRegistered = false;
483#endif
484#ifdef SELM_TRACK_GUEST_LDT_CHANGES
485 if (pVM->selm.s.GCPtrGuestLdt != ~0U)
486 {
487 rc = PGMHandlerVirtualDeregister(pVM, pVM->selm.s.GCPtrGuestLdt);
488 AssertRC(rc);
489 pVM->selm.s.GCPtrGuestLdt = ~0U;
490 }
491#endif
492#ifdef SELM_TRACK_GUEST_TSS_CHANGES
493 if (pVM->selm.s.GCPtrGuestTss != ~0U)
494 {
495 rc = PGMHandlerVirtualDeregister(pVM, pVM->selm.s.GCPtrGuestTss);
496 AssertRC(rc);
497 pVM->selm.s.GCPtrGuestTss = ~0U;
498 pVM->selm.s.GCSelTss = ~0;
499 }
500#endif
501
502 /*
503 * Re-initialize other members.
504 */
505 pVM->selm.s.cbLdtLimit = 0;
506 pVM->selm.s.offLdtHyper = 0;
507 pVM->selm.s.cbMonitoredGuestTss = 0;
508
509 pVM->selm.s.fSyncTSSRing0Stack = false;
510
511 /*
512 * Default action when entering raw mode for the first time
513 */
514 VM_FF_SET(pVM, VM_FF_SELM_SYNC_TSS);
515 VM_FF_SET(pVM, VM_FF_SELM_SYNC_GDT);
516 VM_FF_SET(pVM, VM_FF_SELM_SYNC_LDT);
517}
518
519/**
520 * Disable GDT/LDT/TSS monitoring and syncing
521 *
522 * @param pVM The VM to operate on.
523 */
524SELMR3DECL(void) SELMR3DisableMonitoring(PVM pVM)
525{
526 /*
527 * Uninstall guest GDT/LDT/TSS write access handlers.
528 */
529 int rc;
530#ifdef SELM_TRACK_GUEST_GDT_CHANGES
531 if (pVM->selm.s.GuestGdtr.pGdt != ~0U && pVM->selm.s.fGDTRangeRegistered)
532 {
533 rc = PGMHandlerVirtualDeregister(pVM, pVM->selm.s.GuestGdtr.pGdt);
534 AssertRC(rc);
535 pVM->selm.s.GuestGdtr.pGdt = ~0U;
536 pVM->selm.s.GuestGdtr.cbGdt = 0;
537 }
538 pVM->selm.s.fGDTRangeRegistered = false;
539#endif
540#ifdef SELM_TRACK_GUEST_LDT_CHANGES
541 if (pVM->selm.s.GCPtrGuestLdt != ~0U)
542 {
543 rc = PGMHandlerVirtualDeregister(pVM, pVM->selm.s.GCPtrGuestLdt);
544 AssertRC(rc);
545 pVM->selm.s.GCPtrGuestLdt = ~0U;
546 }
547#endif
548#ifdef SELM_TRACK_GUEST_TSS_CHANGES
549 if (pVM->selm.s.GCPtrGuestTss != ~0U)
550 {
551 rc = PGMHandlerVirtualDeregister(pVM, pVM->selm.s.GCPtrGuestTss);
552 AssertRC(rc);
553 pVM->selm.s.GCPtrGuestTss = ~0U;
554 pVM->selm.s.GCSelTss = ~0;
555 }
556#endif
557
558 /*
559 * Unregister shadow GDT/LDT/TSS write access handlers.
560 */
561#ifdef SELM_TRACK_SHADOW_GDT_CHANGES
562 if (pVM->selm.s.paGdtGC != 0)
563 {
564 rc = PGMHandlerVirtualDeregister(pVM, pVM->selm.s.paGdtGC);
565 AssertRC(rc);
566 pVM->selm.s.paGdtGC = 0;
567 }
568#endif
569#ifdef SELM_TRACK_SHADOW_TSS_CHANGES
570 if (pVM->selm.s.GCPtrTss != ~0U)
571 {
572 rc = PGMHandlerVirtualDeregister(pVM, pVM->selm.s.GCPtrTss);
573 AssertRC(rc);
574 pVM->selm.s.GCPtrTss = ~0U;
575 }
576#endif
577#ifdef SELM_TRACK_SHADOW_LDT_CHANGES
578 if (pVM->selm.s.GCPtrLdt != ~0U)
579 {
580 rc = PGMHandlerVirtualDeregister(pVM, pVM->selm.s.GCPtrLdt);
581 AssertRC(rc);
582 pVM->selm.s.GCPtrLdt = ~0U;
583 }
584#endif
585
586 VM_FF_CLEAR(pVM, VM_FF_SELM_SYNC_TSS);
587 VM_FF_CLEAR(pVM, VM_FF_SELM_SYNC_GDT);
588 VM_FF_CLEAR(pVM, VM_FF_SELM_SYNC_LDT);
589
590 pVM->selm.s.fDisableMonitoring = true;
591}
592
593/**
594 * Execute state save operation.
595 *
596 * @returns VBox status code.
597 * @param pVM VM Handle.
598 * @param pSSM SSM operation handle.
599 */
600static DECLCALLBACK(int) selmR3Save(PVM pVM, PSSMHANDLE pSSM)
601{
602 LogFlow(("selmR3Save:\n"));
603
604 /*
605 * Save the basic bits - fortunately all the other things can be resynced on load.
606 */
607 PSELM pSelm = &pVM->selm.s;
608
609 SSMR3PutBool(pSSM, pSelm->fDisableMonitoring);
610 SSMR3PutBool(pSSM, pSelm->fSyncTSSRing0Stack);
611 SSMR3PutSel(pSSM, pSelm->SelCS);
612 SSMR3PutSel(pSSM, pSelm->SelDS);
613 SSMR3PutSel(pSSM, pSelm->SelCS64);
614 SSMR3PutSel(pSSM, pSelm->SelCS64); //reserved for DS64.
615 SSMR3PutSel(pSSM, pSelm->SelTSS);
616 return SSMR3PutSel(pSSM, pSelm->SelTSSTrap08);
617}
618
619
620/**
621 * Execute state load operation.
622 *
623 * @returns VBox status code.
624 * @param pVM VM Handle.
625 * @param pSSM SSM operation handle.
626 * @param u32Version Data layout version.
627 */
628static DECLCALLBACK(int) selmR3Load(PVM pVM, PSSMHANDLE pSSM, uint32_t u32Version)
629{
630 LogFlow(("selmR3Load:\n"));
631
632 /*
633 * Validate version.
634 */
635 if (u32Version != SELM_SAVED_STATE_VERSION)
636 {
637 Log(("selmR3Load: Invalid version u32Version=%d!\n", u32Version));
638 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
639 }
640
641 /*
642 * Do a reset.
643 */
644 SELMR3Reset(pVM);
645
646 /* Get the monitoring flag. */
647 SSMR3GetBool(pSSM, &pVM->selm.s.fDisableMonitoring);
648
649 /* Get the TSS state flag. */
650 SSMR3GetBool(pSSM, &pVM->selm.s.fSyncTSSRing0Stack);
651
652 /*
653 * Get the selectors.
654 */
655 RTSEL SelCS;
656 SSMR3GetSel(pSSM, &SelCS);
657 RTSEL SelDS;
658 SSMR3GetSel(pSSM, &SelDS);
659 RTSEL SelCS64;
660 SSMR3GetSel(pSSM, &SelCS64);
661 RTSEL SelDS64;
662 SSMR3GetSel(pSSM, &SelDS64);
663 RTSEL SelTSS;
664 SSMR3GetSel(pSSM, &SelTSS);
665 RTSEL SelTSSTrap08;
666 SSMR3GetSel(pSSM, &SelTSSTrap08);
667 if (u32Version == 1)
668 {
669 RTSEL SelTSSTrap0a;
670 int rc = SSMR3GetSel(pSSM, &SelTSSTrap0a);
671 if (VBOX_FAILURE(rc))
672 return rc;
673 }
674
675 /* Check that no selectors have be relocated. */
676 PSELM pSelm = &pVM->selm.s;
677 if ( SelCS != pSelm->SelCS
678 || SelDS != pSelm->SelDS
679 || SelCS64 != pSelm->SelCS64
680 || SelDS64 != pSelm->SelCS64
681 || SelTSS != pSelm->SelTSS
682 || SelTSSTrap08 != pSelm->SelTSSTrap08)
683 {
684 AssertMsgFailed(("Some selector have been relocated - this cannot happen!\n"));
685 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
686 }
687
688 /*
689 * Flag everything for resync.
690 */
691 VM_FF_SET(pVM, VM_FF_SELM_SYNC_GDT);
692 VM_FF_SET(pVM, VM_FF_SELM_SYNC_LDT);
693 VM_FF_SET(pVM, VM_FF_SELM_SYNC_TSS);
694
695 return VINF_SUCCESS;
696}
697
698
699
700
701/**
702 * Sets up the virtualization of a guest GDT.
703 *
704 * @returns VBox status code.
705 * @param pVM The VM to operate on.
706 * @param paGDTEs Pointer to GDT array.
707 * @param cGDTEs Number of entries in the GDT array.
708 */
709SELMR3DECL(int) SELMR3GdtSetup(PVM pVM, PCVBOXDESC paGDTEs, unsigned cGDTEs)
710{
711 AssertMsg(cGDTEs <= (unsigned)(pVM->selm.s.SelTSSTrap08 >> 3), ("Oops! the loaded GDT is as large as our.. we assume no clashes!!!\n"));
712
713 /*
714 * Enumerate the array.
715 */
716 PCVBOXDESC pGDTESrc = paGDTEs;
717 PVBOXDESC pGDTEDst = pVM->selm.s.paGdtHC;
718 for (unsigned iGDT = 0; iGDT < cGDTEs; iGDT++, pGDTEDst++, pGDTESrc++)
719 {
720 /* ASSUME no clashes for now - lazy bird!!! */
721 if (pGDTESrc->Gen.u1Present)
722 {
723 pGDTEDst->Gen = pGDTESrc->Gen;
724 /* mark non ring-3 selectors as not present. */
725 if (pGDTEDst->Gen.u2Dpl != 3)
726 pGDTEDst->Gen.u1Present = 0;
727 }
728 else
729 {
730 /* zero it. */
731 pGDTEDst->au32[0] = 0;
732 pGDTEDst->au32[1] = 0;
733 }
734 }
735
736 return VINF_SUCCESS;
737}
738
739
740/**
741 * Updates the Guest GDT & LDT virtualization based on current CPU state.
742 *
743 * @returns VBox status code.
744 * @param pVM The VM to operate on.
745 */
746SELMR3DECL(int) SELMR3UpdateFromCPUM(PVM pVM)
747{
748 int rc = VINF_SUCCESS;
749
750 if (pVM->selm.s.fDisableMonitoring)
751 {
752 VM_FF_CLEAR(pVM, VM_FF_SELM_SYNC_GDT);
753 VM_FF_CLEAR(pVM, VM_FF_SELM_SYNC_LDT);
754 VM_FF_CLEAR(pVM, VM_FF_SELM_SYNC_TSS);
755
756 return VINF_SUCCESS;
757 }
758
759 STAM_PROFILE_START(&pVM->selm.s.StatUpdateFromCPUM, a);
760
761 /*
762 * GDT sync
763 */
764 if (VM_FF_ISSET(pVM, VM_FF_SELM_SYNC_GDT))
765 {
766 /*
767 * Always assume the best
768 */
769 VM_FF_CLEAR(pVM, VM_FF_SELM_SYNC_GDT);
770
771 /* If the GDT was changed, then make sure the LDT is checked too */
772 /** @todo only do this if the actual ldtr selector was changed; this is a bit excessive */
773 VM_FF_SET(pVM, VM_FF_SELM_SYNC_LDT);
774 /* Same goes for the TSS selector */
775 VM_FF_SET(pVM, VM_FF_SELM_SYNC_TSS);
776
777 /*
778 * Get the GDTR and check if there is anything to do (there usually is).
779 */
780 VBOXGDTR GDTR;
781 CPUMGetGuestGDTR(pVM, &GDTR);
782 if (GDTR.cbGdt < sizeof(VBOXDESC))
783 {
784 Log(("No GDT entries...\n"));
785 STAM_PROFILE_STOP(&pVM->selm.s.StatUpdateFromCPUM, a);
786 return VINF_SUCCESS;
787 }
788
789 /*
790 * Read the Guest GDT.
791 * ASSUMES that the entire GDT is in memory.
792 */
793 RTUINT cbEffLimit = GDTR.cbGdt;
794 PVBOXDESC pGDTE = &pVM->selm.s.paGdtHC[1];
795 rc = PGMPhysReadGCPtr(pVM, pGDTE, GDTR.pGdt + sizeof(VBOXDESC), cbEffLimit + 1 - sizeof(VBOXDESC));
796 if (VBOX_FAILURE(rc))
797 {
798 /*
799 * Read it page by page.
800 *
801 * Keep track of the last valid page and delay memsets and
802 * adjust cbEffLimit to reflect the effective size. The latter
803 * is something we do in the belief that the guest will probably
804 * never actually commit the last page, thus allowing us to keep
805 * our selectors in the high end of the GDT.
806 */
807 RTUINT cbLeft = cbEffLimit + 1 - sizeof(VBOXDESC);
808 RTGCPTR GCPtrSrc = (RTGCPTR)GDTR.pGdt + sizeof(VBOXDESC);
809 uint8_t *pu8Dst = (uint8_t *)&pVM->selm.s.paGdtHC[1];
810 uint8_t *pu8DstInvalid = pu8Dst;
811
812 while (cbLeft)
813 {
814 RTUINT cb = PAGE_SIZE - (GCPtrSrc & PAGE_OFFSET_MASK);
815 cb = RT_MIN(cb, cbLeft);
816 rc = PGMPhysReadGCPtr(pVM, pu8Dst, GCPtrSrc, cb);
817 if (VBOX_SUCCESS(rc))
818 {
819 if (pu8DstInvalid != pu8Dst)
820 memset(pu8DstInvalid, 0, pu8Dst - pu8DstInvalid);
821 GCPtrSrc += cb;
822 pu8Dst += cb;
823 pu8DstInvalid = pu8Dst;
824 }
825 else if ( rc == VERR_PAGE_NOT_PRESENT
826 || rc == VERR_PAGE_TABLE_NOT_PRESENT)
827 {
828 GCPtrSrc += cb;
829 pu8Dst += cb;
830 }
831 else
832 {
833 AssertReleaseMsgFailed(("Couldn't read GDT at %RX32, rc=%Vrc!\n", GDTR.pGdt, rc));
834 STAM_PROFILE_STOP(&pVM->selm.s.StatUpdateFromCPUM, a);
835 return VERR_NOT_IMPLEMENTED;
836 }
837 cbLeft -= cb;
838 }
839
840 /* any invalid pages at the end? */
841 if (pu8DstInvalid != pu8Dst)
842 {
843 cbEffLimit = pu8DstInvalid - (uint8_t *)pVM->selm.s.paGdtHC - 1;
844 /* If any GDTEs was invalidated, zero them. */
845 if (cbEffLimit < pVM->selm.s.cbEffGuestGdtLimit)
846 memset(pu8DstInvalid + cbEffLimit + 1, 0, pVM->selm.s.cbEffGuestGdtLimit - cbEffLimit);
847 }
848
849 /* keep track of the effective limit. */
850 if (cbEffLimit != pVM->selm.s.cbEffGuestGdtLimit)
851 {
852 Log(("SELMR3UpdateFromCPUM: cbEffGuestGdtLimit=%#x -> %#x (actual %#x)\n",
853 pVM->selm.s.cbEffGuestGdtLimit, cbEffLimit, GDTR.cbGdt));
854 pVM->selm.s.cbEffGuestGdtLimit = cbEffLimit;
855 }
856 }
857
858 /*
859 * Check if the Guest GDT intrudes on our GDT entries.
860 */
861 // RTSEL aHyperGDT[MAX_NEEDED_HYPERVISOR_GDTS];
862 if (cbEffLimit >= pVM->selm.s.SelTSSTrap08)
863 {
864#if 0
865 PVBOXDESC pGDTEStart = pVM->selm.s.paGdtHC;
866 PVBOXDESC pGDTE = (PVBOXDESC)((char *)pGDTEStart + GDTR.cbGdt + 1 - sizeof(VBOXDESC));
867 int iGDT = 0;
868
869 /* Disabling this for now; previously saw triple faults with OS/2, before fixing the above if statement */
870 Log(("Internal SELM GDT conflict: use non-present entries\n"));
871 while (pGDTE > pGDTEStart && iGDT < MAX_NEEDED_HYPERVISOR_GDTS)
872 {
873 /* We can reuse non-present entries */
874 if (!pGDTE->Gen.u1Present)
875 {
876 aHyperGDT[iGDT] = ((uintptr_t)pGDTE - (uintptr_t)pVM->selm.s.paGdtHC) / sizeof(VBOXDESC);
877 aHyperGDT[iGDT] = aHyperGDT[iGDT] << X86_SEL_SHIFT;
878 Log(("SELM: Found unused GDT %04X\n", aHyperGDT[iGDT]));
879 iGDT++;
880 }
881
882 pGDTE--;
883 }
884 if (iGDT != MAX_NEEDED_HYPERVISOR_GDTS)
885#endif
886 {
887 AssertReleaseMsgFailed(("Internal SELM GDT conflict.\n"));
888 STAM_PROFILE_STOP(&pVM->selm.s.StatUpdateFromCPUM, a);
889 return VERR_NOT_IMPLEMENTED;
890 }
891 }
892
893 /*
894 * Work thru the copied GDT entries adjusting them for correct virtualization.
895 */
896 PVBOXDESC pGDTEEnd = (PVBOXDESC)((char *)pGDTE + cbEffLimit + 1 - sizeof(VBOXDESC));
897 while (pGDTE < pGDTEEnd)
898 {
899 if (pGDTE->Gen.u1Present)
900 {
901 /*
902 * Code and data selectors are generally 1:1, with the
903 * 'little' adjustment we do for DPL 0 selectors.
904 */
905 if (pGDTE->Gen.u1DescType)
906 {
907 /*
908 * Hack for A-bit against Trap E on read-only GDT.
909 */
910 /** @todo Fix this by loading ds and cs before turning off WP. */
911 pGDTE->Gen.u4Type |= X86_SEL_TYPE_ACCESSED;
912
913 /*
914 * All DPL 0 code and data segments are squeezed into DPL 1.
915 *
916 * We're skipping conforming segments here because those
917 * cannot give us any trouble.
918 */
919 if ( pGDTE->Gen.u2Dpl == 0
920 && (pGDTE->Gen.u4Type & (X86_SEL_TYPE_CODE | X86_SEL_TYPE_CONF))
921 != (X86_SEL_TYPE_CODE | X86_SEL_TYPE_CONF) )
922 pGDTE->Gen.u2Dpl = 1;
923 }
924 else
925 {
926 /*
927 * System type selectors are marked not present.
928 * Recompiler or special handling is required for these.
929 */
930 /** @todo what about interrupt gates and rawr0? */
931 pGDTE->Gen.u1Present = 0;
932 }
933 }
934
935 /* Next GDT entry. */
936 pGDTE++;
937 }
938
939#if 0 /** @todo r=bird: The relocation code won't be working right. Start with the IF below. */
940 /*
941 * Check if the Guest GDT intrudes on our GDT entries.
942 */
943 if (cbEffLimit >= pVM->selm.s.SelTSSTrap08)
944 {
945 /* Reinitialize our hypervisor GDTs */
946 pVM->selm.s.SelCS = aHyperGDT[0];
947 pVM->selm.s.SelDS = aHyperGDT[1];
948 pVM->selm.s.SelCS64 = aHyperGDT[2];
949 pVM->selm.s.SelTSS = aHyperGDT[3];
950 pVM->selm.s.SelTSSTrap08 = aHyperGDT[4];
951 SELMR3Relocate(pVM); /** @todo r=bird: Must call VMR3Relocate! */
952 }
953#endif
954
955 /*
956 * Adjust the cached GDT limit.
957 * Any GDT entries which have been removed must be cleared.
958 */
959 if (pVM->selm.s.GuestGdtr.cbGdt != GDTR.cbGdt)
960 {
961 if (pVM->selm.s.GuestGdtr.cbGdt > GDTR.cbGdt)
962 memset(pGDTE, 0, pVM->selm.s.GuestGdtr.cbGdt - GDTR.cbGdt);
963#ifndef SELM_TRACK_GUEST_GDT_CHANGES
964 pVM->selm.s.GuestGdtr.cbGdt = GDTR.cbGdt;
965#endif
966 }
967
968#ifdef SELM_TRACK_GUEST_GDT_CHANGES
969 /*
970 * Check if Guest's GDTR is changed.
971 */
972 if ( GDTR.pGdt != pVM->selm.s.GuestGdtr.pGdt
973 || GDTR.cbGdt != pVM->selm.s.GuestGdtr.cbGdt)
974 {
975 Log(("SELMR3UpdateFromCPUM: Guest's GDT is changed to pGdt=%08X cbGdt=%08X\n", GDTR.pGdt, GDTR.cbGdt));
976
977 /*
978 * [Re]Register write virtual handler for guest's GDT.
979 */
980 if (pVM->selm.s.GuestGdtr.pGdt != ~0U && pVM->selm.s.fGDTRangeRegistered)
981 {
982 rc = PGMHandlerVirtualDeregister(pVM, pVM->selm.s.GuestGdtr.pGdt);
983 AssertRC(rc);
984 }
985
986 rc = PGMR3HandlerVirtualRegister(pVM, PGMVIRTHANDLERTYPE_WRITE, GDTR.pGdt, GDTR.pGdt + GDTR.cbGdt /* already inclusive */,
987 0, selmGuestGDTWriteHandler, "selmgcGuestGDTWriteHandler", 0, "Guest GDT write access handler");
988 if (VBOX_FAILURE(rc))
989 return rc;
990
991 /* Update saved Guest GDTR. */
992 pVM->selm.s.GuestGdtr = GDTR;
993 pVM->selm.s.fGDTRangeRegistered = true;
994 }
995#endif
996 }
997
998 /*
999 * TSS sync
1000 */
1001 if (VM_FF_ISSET(pVM, VM_FF_SELM_SYNC_TSS))
1002 {
1003 SELMR3SyncTSS(pVM);
1004 }
1005
1006 /*
1007 * LDT sync
1008 */
1009 if (VM_FF_ISSET(pVM, VM_FF_SELM_SYNC_LDT))
1010 {
1011 /*
1012 * Always assume the best
1013 */
1014 VM_FF_CLEAR(pVM, VM_FF_SELM_SYNC_LDT);
1015
1016 /*
1017 * LDT handling is done similarly to the GDT handling with a shadow
1018 * array. However, since the LDT is expected to be swappable (at least
1019 * some ancient OSes makes it swappable) it must be floating and
1020 * synced on a per-page basis.
1021 *
1022 * Eventually we will change this to be fully on demand. Meaning that
1023 * we will only sync pages containing LDT selectors actually used and
1024 * let the #PF handler lazily sync pages as they are used.
1025 * (This applies to GDT too, when we start making OS/2 fast.)
1026 */
1027
1028 /*
1029 * First, determin the current LDT selector.
1030 */
1031 RTSEL SelLdt = CPUMGetGuestLDTR(pVM);
1032 if ((SelLdt & X86_SEL_MASK) == 0)
1033 {
1034 /* ldtr = 0 - update hyper LDTR and deregister any active handler. */
1035 CPUMSetHyperLDTR(pVM, 0);
1036#ifdef SELM_TRACK_GUEST_LDT_CHANGES
1037 if (pVM->selm.s.GCPtrGuestLdt != ~0U)
1038 {
1039 rc = PGMHandlerVirtualDeregister(pVM, pVM->selm.s.GCPtrGuestLdt);
1040 AssertRC(rc);
1041 pVM->selm.s.GCPtrGuestLdt = ~0U;
1042 }
1043#endif
1044 STAM_PROFILE_STOP(&pVM->selm.s.StatUpdateFromCPUM, a);
1045 return VINF_SUCCESS;
1046 }
1047
1048 /*
1049 * Get the LDT selector.
1050 */
1051 PVBOXDESC pDesc = &pVM->selm.s.paGdtHC[SelLdt >> X86_SEL_SHIFT];
1052 RTGCPTR GCPtrLdt = pDesc->Gen.u16BaseLow | (pDesc->Gen.u8BaseHigh1 << 16) | (pDesc->Gen.u8BaseHigh2 << 24);
1053 unsigned cbLdt = pDesc->Gen.u16LimitLow | (pDesc->Gen.u4LimitHigh << 16);
1054 if (pDesc->Gen.u1Granularity)
1055 cbLdt = (cbLdt << PAGE_SHIFT) | PAGE_OFFSET_MASK;
1056
1057 /*
1058 * Validate it.
1059 */
1060 if ( !cbLdt
1061 || SelLdt >= pVM->selm.s.GuestGdtr.cbGdt
1062 || pDesc->Gen.u1DescType
1063 || pDesc->Gen.u4Type != X86_SEL_TYPE_SYS_LDT)
1064 {
1065 AssertMsg(!cbLdt, ("Invalid LDT %04x!\n", SelLdt));
1066
1067 /* cbLdt > 0:
1068 * This is quite impossible, so we do as most people do when faced with
1069 * the impossible, we simply ignore it.
1070 */
1071 CPUMSetHyperLDTR(pVM, 0);
1072#ifdef SELM_TRACK_GUEST_LDT_CHANGES
1073 if (pVM->selm.s.GCPtrGuestLdt != ~0U)
1074 {
1075 rc = PGMHandlerVirtualDeregister(pVM, pVM->selm.s.GCPtrGuestLdt);
1076 AssertRC(rc);
1077 pVM->selm.s.GCPtrGuestLdt = ~0U;
1078 }
1079#endif
1080 STAM_PROFILE_STOP(&pVM->selm.s.StatUpdateFromCPUM, a);
1081 return VINF_SUCCESS;
1082 }
1083 /** @todo check what intel does about odd limits. */
1084 AssertMsg(RT_ALIGN(cbLdt + 1, sizeof(VBOXDESC)) == cbLdt + 1 && cbLdt <= 0xffff, ("cbLdt=%d\n", cbLdt));
1085
1086 /*
1087 * Use the cached guest ldt address if the descriptor has already been modified (see below)
1088 * (this is necessary due to redundant LDT updates; see todo above at GDT sync)
1089 */
1090 if (MMHyperIsInsideArea(pVM, GCPtrLdt) == true)
1091 GCPtrLdt = pVM->selm.s.GCPtrGuestLdt; /* use the old one */
1092
1093
1094#ifdef SELM_TRACK_GUEST_LDT_CHANGES
1095 /** @todo Handle only present LDT segments. */
1096 // if (pDesc->Gen.u1Present)
1097 {
1098 /*
1099 * Check if Guest's LDT address/limit is changed.
1100 */
1101 if ( GCPtrLdt != pVM->selm.s.GCPtrGuestLdt
1102 || cbLdt != pVM->selm.s.cbLdtLimit)
1103 {
1104 Log(("SELMR3UpdateFromCPUM: Guest LDT changed to from %VGv:%04x to %VGv:%04x. (GDTR=%VGv:%04x)\n",
1105 pVM->selm.s.GCPtrGuestLdt, pVM->selm.s.cbLdtLimit, GCPtrLdt, cbLdt, pVM->selm.s.GuestGdtr.pGdt, pVM->selm.s.GuestGdtr.cbGdt));
1106
1107 /*
1108 * [Re]Register write virtual handler for guest's GDT.
1109 * In the event of LDT overlapping something, don't install it just assume it's being updated.
1110 */
1111 if (pVM->selm.s.GCPtrGuestLdt != ~0U)
1112 {
1113 rc = PGMHandlerVirtualDeregister(pVM, pVM->selm.s.GCPtrGuestLdt);
1114 AssertRC(rc);
1115 }
1116#ifdef DEBUG
1117 if (pDesc->Gen.u1Present)
1118 Log(("LDT selector marked not present!!\n"));
1119#endif
1120 rc = PGMR3HandlerVirtualRegister(pVM, PGMVIRTHANDLERTYPE_WRITE, GCPtrLdt, GCPtrLdt + cbLdt /* already inclusive */,
1121 0, selmGuestLDTWriteHandler, "selmgcGuestLDTWriteHandler", 0, "Guest LDT write access handler");
1122 if (rc == VERR_PGM_HANDLER_VIRTUAL_CONFLICT)
1123 {
1124 /** @todo investigate the various cases where conflicts happen and try avoid them by enh. the instruction emulation. */
1125 pVM->selm.s.GCPtrGuestLdt = ~0;
1126 Log(("WARNING: Guest LDT (%VGv:%04x) conflicted with existing access range!! Assumes LDT is begin updated. (GDTR=%VGv:%04x)\n",
1127 GCPtrLdt, cbLdt, pVM->selm.s.GuestGdtr.pGdt, pVM->selm.s.GuestGdtr.cbGdt));
1128 }
1129 else if (VBOX_SUCCESS(rc))
1130 pVM->selm.s.GCPtrGuestLdt = GCPtrLdt;
1131 else
1132 {
1133 CPUMSetHyperLDTR(pVM, 0);
1134 STAM_PROFILE_STOP(&pVM->selm.s.StatUpdateFromCPUM, a);
1135 return rc;
1136 }
1137
1138 pVM->selm.s.cbLdtLimit = cbLdt;
1139 }
1140 }
1141#else
1142 pVM->selm.s.cbLdtLimit = cbLdt;
1143#endif
1144
1145 /*
1146 * Calc Shadow LDT base.
1147 */
1148 unsigned off;
1149 pVM->selm.s.offLdtHyper = off = (GCPtrLdt & PAGE_OFFSET_MASK);
1150 RTGCPTR GCPtrShadowLDT = (RTGCPTR)((RTGCUINTPTR)pVM->selm.s.GCPtrLdt + off);
1151 PVBOXDESC pShadowLDT = (PVBOXDESC)((uintptr_t)pVM->selm.s.HCPtrLdt + off);
1152
1153 /*
1154 * Enable the LDT selector in the shadow GDT.
1155 */
1156 pDesc->Gen.u1Present = 1;
1157 pDesc->Gen.u16BaseLow = RT_LOWORD(GCPtrShadowLDT);
1158 pDesc->Gen.u8BaseHigh1 = RT_BYTE3(GCPtrShadowLDT);
1159 pDesc->Gen.u8BaseHigh2 = RT_BYTE4(GCPtrShadowLDT);
1160 pDesc->Gen.u1Available = 0;
1161 pDesc->Gen.u1Reserved = 0;
1162 if (cbLdt > 0xffff)
1163 {
1164 cbLdt = 0xffff;
1165 pDesc->Gen.u4LimitHigh = 0;
1166 pDesc->Gen.u16LimitLow = pDesc->Gen.u1Granularity ? 0xf : 0xffff;
1167 }
1168
1169 /*
1170 * Set Hyper LDTR and notify TRPM.
1171 */
1172 CPUMSetHyperLDTR(pVM, SelLdt);
1173
1174 /*
1175 * Loop synchronising the LDT page by page.
1176 */
1177 /** @todo investigate how intel handle various operations on half present cross page entries. */
1178 off = GCPtrLdt & (sizeof(VBOXDESC) - 1);
1179 AssertMsg(!off, ("LDT is not aligned on entry size! GCPtrLdt=%08x\n", GCPtrLdt));
1180 GCPtrLdt += sizeof(VBOXDESC);
1181 pShadowLDT++;
1182 unsigned cbLeft = cbLdt + 1 - sizeof(VBOXDESC);
1183 PVBOXDESC pLDTE = pShadowLDT;
1184 while (cbLeft)
1185 {
1186 /*
1187 * Read a chunk.
1188 */
1189 unsigned cbChunk = PAGE_SIZE - ((RTGCUINTPTR)GCPtrLdt & PAGE_OFFSET_MASK);
1190 if (cbChunk > cbLeft)
1191 cbChunk = cbLeft;
1192 rc = PGMPhysReadGCPtr(pVM, pShadowLDT, GCPtrLdt, cbChunk);
1193 if (VBOX_SUCCESS(rc))
1194 {
1195 /*
1196 * Mark page
1197 */
1198 rc = PGMMapSetPage(pVM, GCPtrShadowLDT & PAGE_BASE_GC_MASK, PAGE_SIZE, X86_PTE_P | X86_PTE_A | X86_PTE_D);
1199 AssertRC(rc);
1200
1201 /*
1202 * Loop thru the available LDT entries.
1203 * Figure out where to start and end and the potential cross pageness of
1204 * things adds a little complexity. pLDTE is updated there and not in the
1205 * 'next' part of the loop. The pLDTEEnd is inclusive.
1206 */
1207 PVBOXDESC pLDTEEnd = (PVBOXDESC)((uintptr_t)pShadowLDT + cbChunk) - 1;
1208 if (pLDTE + 1 < pShadowLDT)
1209 pLDTE = (PVBOXDESC)((uintptr_t)pShadowLDT + off);
1210 while (pLDTE <= pLDTEEnd)
1211 {
1212 if (pLDTE->Gen.u1Present)
1213 {
1214 /*
1215 * Code and data selectors are generally 1:1, with the
1216 * 'little' adjustment we do for DPL 0 selectors.
1217 */
1218 if (pLDTE->Gen.u1DescType)
1219 {
1220 /*
1221 * Hack for A-bit against Trap E on read-only GDT.
1222 */
1223 /** @todo Fix this by loading ds and cs before turning off WP. */
1224 if (!(pLDTE->Gen.u4Type & X86_SEL_TYPE_ACCESSED))
1225 pLDTE->Gen.u4Type |= X86_SEL_TYPE_ACCESSED;
1226
1227 /*
1228 * All DPL 0 code and data segments are squeezed into DPL 1.
1229 *
1230 * We're skipping conforming segments here because those
1231 * cannot give us any trouble.
1232 */
1233 if ( pLDTE->Gen.u2Dpl == 0
1234 && (pLDTE->Gen.u4Type & (X86_SEL_TYPE_CODE | X86_SEL_TYPE_CONF))
1235 != (X86_SEL_TYPE_CODE | X86_SEL_TYPE_CONF) )
1236 pLDTE->Gen.u2Dpl = 1;
1237 }
1238 else
1239 {
1240 /*
1241 * System type selectors are marked not present.
1242 * Recompiler or special handling is required for these.
1243 */
1244 /** @todo what about interrupt gates and rawr0? */
1245 pLDTE->Gen.u1Present = 0;
1246 }
1247 }
1248
1249 /* Next LDT entry. */
1250 pLDTE++;
1251 }
1252 }
1253 else
1254 {
1255 AssertMsg(rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT, ("rc=%d\n", rc));
1256 rc = PGMMapSetPage(pVM, GCPtrShadowLDT & PAGE_BASE_GC_MASK, PAGE_SIZE, 0);
1257 AssertRC(rc);
1258 }
1259
1260 /*
1261 * Advance to the next page.
1262 */
1263 cbLeft -= cbChunk;
1264 GCPtrShadowLDT += cbChunk;
1265 pShadowLDT = (PVBOXDESC)((char *)pShadowLDT + cbChunk);
1266 GCPtrLdt += cbChunk;
1267 }
1268 }
1269
1270 STAM_PROFILE_STOP(&pVM->selm.s.StatUpdateFromCPUM, a);
1271 return VINF_SUCCESS;
1272}
1273
1274
1275/**
1276 * \#PF Handler callback for virtual access handler ranges.
1277 *
1278 * Important to realize that a physical page in a range can have aliases, and
1279 * for ALL and WRITE handlers these will also trigger.
1280 *
1281 * @returns VINF_SUCCESS if the handler have carried out the operation.
1282 * @returns VINF_PGM_HANDLER_DO_DEFAULT if the caller should carry out the access operation.
1283 * @param pVM VM Handle.
1284 * @param GCPtr The virtual address the guest is writing to. (not correct if it's an alias!)
1285 * @param pvPtr The HC mapping of that address.
1286 * @param pvBuf What the guest is reading/writing.
1287 * @param cbBuf How much it's reading/writing.
1288 * @param enmAccessType The access type.
1289 * @param pvUser User argument.
1290 */
1291static DECLCALLBACK(int) selmGuestGDTWriteHandler(PVM pVM, RTGCPTR GCPtr, void *pvPtr, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, void *pvUser)
1292{
1293 Assert(enmAccessType == PGMACCESSTYPE_WRITE);
1294 Log(("selmGuestGDTWriteHandler: write to %VGv size %d\n", GCPtr, cbBuf));
1295 VM_FF_SET(pVM, VM_FF_SELM_SYNC_GDT);
1296
1297 return VINF_PGM_HANDLER_DO_DEFAULT;
1298}
1299
1300/**
1301 * \#PF Handler callback for virtual access handler ranges.
1302 *
1303 * Important to realize that a physical page in a range can have aliases, and
1304 * for ALL and WRITE handlers these will also trigger.
1305 *
1306 * @returns VINF_SUCCESS if the handler have carried out the operation.
1307 * @returns VINF_PGM_HANDLER_DO_DEFAULT if the caller should carry out the access operation.
1308 * @param pVM VM Handle.
1309 * @param GCPtr The virtual address the guest is writing to. (not correct if it's an alias!)
1310 * @param pvPtr The HC mapping of that address.
1311 * @param pvBuf What the guest is reading/writing.
1312 * @param cbBuf How much it's reading/writing.
1313 * @param enmAccessType The access type.
1314 * @param pvUser User argument.
1315 */
1316static DECLCALLBACK(int) selmGuestLDTWriteHandler(PVM pVM, RTGCPTR GCPtr, void *pvPtr, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, void *pvUser)
1317{
1318 Assert(enmAccessType == PGMACCESSTYPE_WRITE);
1319 Log(("selmGuestLDTWriteHandler: write to %VGv size %d\n", GCPtr, cbBuf));
1320 VM_FF_SET(pVM, VM_FF_SELM_SYNC_LDT);
1321 return VINF_PGM_HANDLER_DO_DEFAULT;
1322}
1323
1324/**
1325 * \#PF Handler callback for virtual access handler ranges.
1326 *
1327 * Important to realize that a physical page in a range can have aliases, and
1328 * for ALL and WRITE handlers these will also trigger.
1329 *
1330 * @returns VINF_SUCCESS if the handler have carried out the operation.
1331 * @returns VINF_PGM_HANDLER_DO_DEFAULT if the caller should carry out the access operation.
1332 * @param pVM VM Handle.
1333 * @param GCPtr The virtual address the guest is writing to. (not correct if it's an alias!)
1334 * @param pvPtr The HC mapping of that address.
1335 * @param pvBuf What the guest is reading/writing.
1336 * @param cbBuf How much it's reading/writing.
1337 * @param enmAccessType The access type.
1338 * @param pvUser User argument.
1339 */
1340static DECLCALLBACK(int) selmGuestTSSWriteHandler(PVM pVM, RTGCPTR GCPtr, void *pvPtr, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, void *pvUser)
1341{
1342 Assert(enmAccessType == PGMACCESSTYPE_WRITE);
1343 Log(("selmGuestTSSWriteHandler: write to %VGv size %d\n", GCPtr, cbBuf));
1344 VM_FF_SET(pVM, VM_FF_SELM_SYNC_TSS);
1345 return VINF_PGM_HANDLER_DO_DEFAULT;
1346}
1347
1348/**
1349 * Check if the TSS ring 0 stack selector and pointer were updated (for now)
1350 *
1351 * @returns VBox status code.
1352 * @param pVM The VM to operate on.
1353 */
1354SELMR3DECL(int) SELMR3SyncTSS(PVM pVM)
1355{
1356 int rc;
1357
1358 if (pVM->selm.s.fDisableMonitoring)
1359 {
1360 VM_FF_CLEAR(pVM, VM_FF_SELM_SYNC_TSS);
1361 return VINF_SUCCESS;
1362 }
1363
1364/** @todo r=bird: SELMR3SyncTSS should be VMMAll code.
1365 * All the base, size, flags and stuff must be kept up to date in the CPUM tr register.
1366 */
1367 STAM_PROFILE_START(&pVM->selm.s.StatTSSSync, a);
1368
1369 Assert(!VM_FF_ISSET(pVM, VM_FF_SELM_SYNC_GDT));
1370 Assert(VM_FF_ISSET(pVM, VM_FF_SELM_SYNC_TSS));
1371
1372 /*
1373 * TSS sync
1374 */
1375 RTSEL SelTss = CPUMGetGuestTR(pVM);
1376 if (SelTss & X86_SEL_MASK)
1377 {
1378 /** @todo r=bird: strictly speaking, this is wrong as we shouldn't bother with changes to
1379 * the TSS selector once its loaded. There are a bunch of this kind of problems (see Sander's
1380 * comment in the unzip defect)
1381 * The first part here should only be done when we're loading TR. The latter part which is
1382 * updating of the ss0:esp0 pair can be done by the access handler now since we can trap all
1383 * accesses, also REM ones. */
1384
1385 /*
1386 * Guest TR is not NULL.
1387 */
1388 PVBOXDESC pDesc = &pVM->selm.s.paGdtHC[SelTss >> X86_SEL_SHIFT];
1389 RTGCPTR GCPtrTss = pDesc->Gen.u16BaseLow | (pDesc->Gen.u8BaseHigh1 << 16) | (pDesc->Gen.u8BaseHigh2 << 24);
1390 unsigned cbTss = pDesc->Gen.u16LimitLow | (pDesc->Gen.u4LimitHigh << 16);
1391 if (pDesc->Gen.u1Granularity)
1392 cbTss = (cbTss << PAGE_SHIFT) | PAGE_OFFSET_MASK;
1393 cbTss++;
1394 pVM->selm.s.cbGuestTss = cbTss;
1395 pVM->selm.s.fGuestTss32Bit = pDesc->Gen.u4Type == X86_SEL_TYPE_SYS_386_TSS_AVAIL
1396 || pDesc->Gen.u4Type == X86_SEL_TYPE_SYS_386_TSS_BUSY;
1397
1398 /* Don't bother with anything but the core structure. (Actually all we care for is the r0 ss.) */
1399 if (cbTss > sizeof(VBOXTSS))
1400 cbTss = sizeof(VBOXTSS);
1401 AssertMsg((GCPtrTss >> PAGE_SHIFT) == ((GCPtrTss + cbTss - 1) >> PAGE_SHIFT),
1402 ("GCPtrTss=%VGv cbTss=%#x - We assume everything is inside one page!\n", GCPtrTss, cbTss));
1403
1404 // All system GDTs are marked not present above. That explains why this check fails.
1405 //if (pDesc->Gen.u1Present)
1406 /** @todo Handle only present TSS segments. */
1407 {
1408 /*
1409 * Check if Guest's TSS is changed.
1410 */
1411 if ( GCPtrTss != pVM->selm.s.GCPtrGuestTss
1412 || cbTss != pVM->selm.s.cbMonitoredGuestTss)
1413 {
1414 Log(("SELMR3UpdateFromCPUM: Guest's TSS is changed to pTss=%08X cbTss=%08X cbGuestTss\n", GCPtrTss, cbTss, pVM->selm.s.cbGuestTss));
1415
1416 /*
1417 * Validate it.
1418 */
1419 if ( SelTss & X86_SEL_LDT
1420 || !cbTss
1421 || SelTss >= pVM->selm.s.GuestGdtr.cbGdt
1422 || pDesc->Gen.u1DescType
1423 || ( pDesc->Gen.u4Type != X86_SEL_TYPE_SYS_286_TSS_AVAIL
1424 && pDesc->Gen.u4Type != X86_SEL_TYPE_SYS_286_TSS_BUSY
1425 && pDesc->Gen.u4Type != X86_SEL_TYPE_SYS_386_TSS_AVAIL
1426 && pDesc->Gen.u4Type != X86_SEL_TYPE_SYS_386_TSS_BUSY) )
1427 {
1428 AssertMsgFailed(("Invalid Guest TSS %04x!\n", SelTss));
1429 }
1430 else
1431 {
1432 /*
1433 * [Re]Register write virtual handler for guest's TSS.
1434 */
1435 if (pVM->selm.s.GCPtrGuestTss != ~0U)
1436 {
1437 rc = PGMHandlerVirtualDeregister(pVM, pVM->selm.s.GCPtrGuestTss);
1438 AssertRC(rc);
1439 }
1440
1441 rc = PGMR3HandlerVirtualRegister(pVM, PGMVIRTHANDLERTYPE_WRITE, GCPtrTss, GCPtrTss + cbTss - 1,
1442 0, selmGuestTSSWriteHandler, "selmgcGuestTSSWriteHandler", 0, "Guest TSS write access handler");
1443 if (VBOX_FAILURE(rc))
1444 {
1445 STAM_PROFILE_STOP(&pVM->selm.s.StatUpdateFromCPUM, a);
1446 return rc;
1447 }
1448
1449 /* Update saved Guest TSS info. */
1450 pVM->selm.s.GCPtrGuestTss = GCPtrTss;
1451 pVM->selm.s.cbMonitoredGuestTss = cbTss;
1452 pVM->selm.s.GCSelTss = SelTss;
1453 }
1454 }
1455
1456 /* Update the ring 0 stack selector and base address */
1457 /* feeling very lazy; reading too much */
1458 VBOXTSS tss;
1459 rc = PGMPhysReadGCPtr(pVM, &tss, GCPtrTss, sizeof(VBOXTSS));
1460 if (VBOX_SUCCESS(rc))
1461 {
1462 #ifdef DEBUG
1463 uint32_t ssr0, espr0;
1464
1465 SELMGetRing1Stack(pVM, &ssr0, &espr0);
1466 ssr0 &= ~1;
1467
1468 if (ssr0 != tss.ss0 || espr0 != tss.esp0)
1469 Log(("SELMR3SyncTSS: Updating TSS ring 0 stack to %04X:%08X\n", tss.ss0, tss.esp0));
1470 Log(("offIoBitmap=%#x\n", tss.offIoBitmap));
1471 #endif
1472 /* Update our TSS structure for the guest's ring 1 stack */
1473 SELMSetRing1Stack(pVM, tss.ss0 | 1, tss.esp0);
1474 }
1475 else
1476 {
1477 /** @note the ring 0 stack selector and base address are updated on demand in this case. */
1478
1479 /** @todo handle these dependencies better! */
1480 TRPMR3ClearHandler(pVM, 0x2E);
1481 TRPMR3ClearHandler(pVM, 0x80);
1482 pVM->selm.s.fSyncTSSRing0Stack = true;
1483 }
1484 VM_FF_CLEAR(pVM, VM_FF_SELM_SYNC_TSS);
1485 }
1486 }
1487
1488 STAM_PROFILE_STOP(&pVM->selm.s.StatTSSSync, a);
1489 return VINF_SUCCESS;
1490}
1491
1492
1493/**
1494 * Compares the Guest GDT and LDT with the shadow tables.
1495 * This is a VBOX_STRICT only function.
1496 *
1497 * @returns VBox status code.
1498 * @param pVM The VM Handle.
1499 */
1500SELMR3DECL(int) SELMR3DebugCheck(PVM pVM)
1501{
1502#ifdef VBOX_STRICT
1503 /*
1504 * Get GDTR and check for conflict.
1505 */
1506 VBOXGDTR GDTR;
1507 CPUMGetGuestGDTR(pVM, &GDTR);
1508 if (GDTR.cbGdt == 0)
1509 return VINF_SUCCESS;
1510
1511#if 0
1512 if (GDTR.cbGdt >= (unsigned)(pVM->selm.s.SelTSSTrap08 >> X86_SEL_SHIFT))
1513 {
1514 AssertReleaseMsgFailed(("Internal SELM GDT conflict.\n"));
1515 return VERR_NOT_IMPLEMENTED;
1516 }
1517#endif
1518
1519 if (GDTR.cbGdt != pVM->selm.s.GuestGdtr.cbGdt)
1520 Log(("SELMR3DebugCheck: limits have changed! new=%d old=%d\n", GDTR.cbGdt, pVM->selm.s.GuestGdtr.cbGdt));
1521
1522 /*
1523 * Loop thru the GDT checking each entry.
1524 */
1525 RTGCPTR GCPtrGDTEGuest = GDTR.pGdt;
1526 PVBOXDESC pGDTE = pVM->selm.s.paGdtHC;
1527 PVBOXDESC pGDTEEnd = (PVBOXDESC)((uintptr_t)pGDTE + GDTR.cbGdt);
1528 while (pGDTE < pGDTEEnd)
1529 {
1530 VBOXDESC GDTEGuest;
1531 int rc = PGMPhysReadGCPtr(pVM, &GDTEGuest, GCPtrGDTEGuest, sizeof(GDTEGuest));
1532 if (VBOX_SUCCESS(rc))
1533 {
1534 if (pGDTE->Gen.u1DescType || pGDTE->Gen.u4Type != X86_SEL_TYPE_SYS_LDT)
1535 {
1536 if ( pGDTE->Gen.u16LimitLow != GDTEGuest.Gen.u16LimitLow
1537 || pGDTE->Gen.u4LimitHigh != GDTEGuest.Gen.u4LimitHigh
1538 || pGDTE->Gen.u16BaseLow != GDTEGuest.Gen.u16BaseLow
1539 || pGDTE->Gen.u8BaseHigh1 != GDTEGuest.Gen.u8BaseHigh1
1540 || pGDTE->Gen.u8BaseHigh2 != GDTEGuest.Gen.u8BaseHigh2
1541 || pGDTE->Gen.u1DefBig != GDTEGuest.Gen.u1DefBig
1542 || pGDTE->Gen.u1DescType != GDTEGuest.Gen.u1DescType)
1543 {
1544 unsigned iGDT = pGDTE - pVM->selm.s.paGdtHC;
1545 SELMR3DumpDescriptor(*pGDTE, iGDT << 3, "SELMR3DebugCheck: GDT mismatch, shadow");
1546 SELMR3DumpDescriptor(GDTEGuest, iGDT << 3, "SELMR3DebugCheck: GDT mismatch, guest");
1547 }
1548 }
1549 }
1550
1551 /* Advance to the next descriptor. */
1552 GCPtrGDTEGuest += sizeof(VBOXDESC);
1553 pGDTE++;
1554 }
1555
1556
1557 /*
1558 * LDT?
1559 */
1560 RTSEL SelLdt = CPUMGetGuestLDTR(pVM);
1561 if ((SelLdt & X86_SEL_MASK) == 0)
1562 return VINF_SUCCESS;
1563 if (SelLdt > GDTR.cbGdt)
1564 {
1565 Log(("SELMR3DebugCheck: ldt is out of bound SelLdt=%#x\n", SelLdt));
1566 return VERR_INTERNAL_ERROR;
1567 }
1568 VBOXDESC LDTDesc;
1569 int rc = PGMPhysReadGCPtr(pVM, &LDTDesc, GDTR.pGdt + (SelLdt & X86_SEL_MASK), sizeof(LDTDesc));
1570 if (VBOX_FAILURE(rc))
1571 {
1572 Log(("SELMR3DebugCheck: Failed to read LDT descriptor. rc=%d\n", rc));
1573 return rc;
1574 }
1575 RTGCPTR GCPtrLDTEGuest = LDTDesc.Gen.u16BaseLow | (LDTDesc.Gen.u8BaseHigh1 << 16) | (LDTDesc.Gen.u8BaseHigh2 << 24);
1576 unsigned cbLdt = LDTDesc.Gen.u16LimitLow | (LDTDesc.Gen.u4LimitHigh << 16);
1577 if (LDTDesc.Gen.u1Granularity)
1578 cbLdt = (cbLdt << PAGE_SHIFT) | PAGE_OFFSET_MASK;
1579
1580 /*
1581 * Validate it.
1582 */
1583 if (!cbLdt)
1584 return VINF_SUCCESS;
1585 /** @todo check what intel does about odd limits. */
1586 AssertMsg(RT_ALIGN(cbLdt + 1, sizeof(VBOXDESC)) == cbLdt + 1 && cbLdt <= 0xffff, ("cbLdt=%d\n", cbLdt));
1587 if ( LDTDesc.Gen.u1DescType
1588 || LDTDesc.Gen.u4Type != X86_SEL_TYPE_SYS_LDT
1589 || SelLdt >= pVM->selm.s.GuestGdtr.cbGdt)
1590 {
1591 Log(("SELmR3DebugCheck: Invalid LDT %04x!\n", SelLdt));
1592 return VERR_INTERNAL_ERROR;
1593 }
1594
1595 /*
1596 * Loop thru the LDT checking each entry.
1597 */
1598 unsigned off = (GCPtrLDTEGuest & PAGE_OFFSET_MASK);
1599 PVBOXDESC pLDTE = (PVBOXDESC)((uintptr_t)pVM->selm.s.HCPtrLdt + off);
1600 PVBOXDESC pLDTEEnd = (PVBOXDESC)((uintptr_t)pGDTE + cbLdt);
1601 while (pLDTE < pLDTEEnd)
1602 {
1603 VBOXDESC LDTEGuest;
1604 int rc = PGMPhysReadGCPtr(pVM, &LDTEGuest, GCPtrLDTEGuest, sizeof(LDTEGuest));
1605 if (VBOX_SUCCESS(rc))
1606 {
1607 if ( pLDTE->Gen.u16LimitLow != LDTEGuest.Gen.u16LimitLow
1608 || pLDTE->Gen.u4LimitHigh != LDTEGuest.Gen.u4LimitHigh
1609 || pLDTE->Gen.u16BaseLow != LDTEGuest.Gen.u16BaseLow
1610 || pLDTE->Gen.u8BaseHigh1 != LDTEGuest.Gen.u8BaseHigh1
1611 || pLDTE->Gen.u8BaseHigh2 != LDTEGuest.Gen.u8BaseHigh2
1612 || pLDTE->Gen.u1DefBig != LDTEGuest.Gen.u1DefBig
1613 || pLDTE->Gen.u1DescType != LDTEGuest.Gen.u1DescType)
1614 {
1615 unsigned iLDT = pLDTE - (PVBOXDESC)((uintptr_t)pVM->selm.s.HCPtrLdt + off);
1616 SELMR3DumpDescriptor(*pLDTE, iLDT << 3, "SELMR3DebugCheck: LDT mismatch, shadow");
1617 SELMR3DumpDescriptor(LDTEGuest, iLDT << 3, "SELMR3DebugCheck: LDT mismatch, guest");
1618 }
1619 }
1620
1621 /* Advance to the next descriptor. */
1622 GCPtrLDTEGuest += sizeof(VBOXDESC);
1623 pLDTE++;
1624 }
1625
1626#else
1627 NOREF(pVM);
1628#endif
1629
1630 return VINF_SUCCESS;
1631}
1632
1633
1634/**
1635 * Validates the RawR0 TSS values against the one in the Guest TSS.
1636 *
1637 * @returns true if it matches.
1638 * @returns false and assertions on mismatch..
1639 * @param pVM VM Handle.
1640 */
1641SELMR3DECL(bool) SELMR3CheckTSS(PVM pVM)
1642{
1643#ifdef VBOX_STRICT
1644
1645 RTSEL SelTss = CPUMGetGuestTR(pVM);
1646 if (SelTss & X86_SEL_MASK)
1647 {
1648 AssertMsg((SelTss & X86_SEL_MASK) == (pVM->selm.s.GCSelTss & X86_SEL_MASK), ("New TSS selector = %04X, old TSS selector = %04X\n", SelTss, pVM->selm.s.GCSelTss));
1649
1650 /*
1651 * Guest TR is not NULL.
1652 */
1653 PVBOXDESC pDesc = &pVM->selm.s.paGdtHC[SelTss >> X86_SEL_SHIFT];
1654 RTGCPTR GCPtrTss = pDesc->Gen.u16BaseLow | (pDesc->Gen.u8BaseHigh1 << 16) | (pDesc->Gen.u8BaseHigh2 << 24);
1655 unsigned cbTss = pDesc->Gen.u16LimitLow | (pDesc->Gen.u4LimitHigh << 16);
1656 if (pDesc->Gen.u1Granularity)
1657 cbTss = (cbTss << PAGE_SHIFT) | PAGE_OFFSET_MASK;
1658 cbTss++;
1659 /* Don't bother with anything but the core structure. (Actually all we care for is the r0 ss.) */
1660 if (cbTss > sizeof(VBOXTSS))
1661 cbTss = sizeof(VBOXTSS);
1662 AssertMsg((GCPtrTss >> PAGE_SHIFT) == ((GCPtrTss + cbTss - 1) >> PAGE_SHIFT),
1663 ("GCPtrTss=%VGv cbTss=%#x - We assume everything is inside one page!\n", GCPtrTss, cbTss));
1664
1665 // All system GDTs are marked not present above. That explains why this check fails.
1666 //if (pDesc->Gen.u1Present)
1667 /** @todo Handle only present TSS segments. */
1668 {
1669 /*
1670 * Check if Guest's TSS was changed.
1671 */
1672 if ( GCPtrTss != pVM->selm.s.GCPtrGuestTss
1673 || cbTss != pVM->selm.s.cbMonitoredGuestTss)
1674 {
1675 AssertMsgFailed(("Guest's TSS (Sel 0x%X) is changed from %RGv:%04x to %RGv:%04x\n",
1676 SelTss, pVM->selm.s.GCPtrGuestTss, pVM->selm.s.cbMonitoredGuestTss,
1677 GCPtrTss, cbTss));
1678 }
1679 }
1680 }
1681
1682 if (!pVM->selm.s.fSyncTSSRing0Stack)
1683 {
1684 RTGCPTR pGuestTSS = pVM->selm.s.GCPtrGuestTss;
1685 uint32_t ESPR0;
1686 int rc = PGMPhysReadGCPtr(pVM, &ESPR0, pGuestTSS + RT_OFFSETOF(VBOXTSS, esp0), sizeof(ESPR0));
1687 if (VBOX_SUCCESS(rc))
1688 {
1689 RTSEL SelSS0;
1690 rc = PGMPhysReadGCPtr(pVM, &SelSS0, pGuestTSS + RT_OFFSETOF(VBOXTSS, ss0), sizeof(SelSS0));
1691 if (VBOX_SUCCESS(rc))
1692 {
1693 if ( ESPR0 == pVM->selm.s.Tss.esp1
1694 && SelSS0 == (pVM->selm.s.Tss.ss1 & ~1))
1695 return true;
1696
1697 RTGCPHYS GCPhys;
1698 uint64_t fFlags;
1699
1700 rc = PGMGstGetPage(pVM, pGuestTSS, &fFlags, &GCPhys);
1701 AssertRC(rc);
1702 AssertMsgFailed(("TSS out of sync!! (%04X:%08X vs %04X:%08X (guest)) Tss=%VGv Phys=%VGp\n",
1703 (pVM->selm.s.Tss.ss1 & ~1), pVM->selm.s.Tss.esp1, SelSS0, ESPR0, pGuestTSS, GCPhys));
1704 }
1705 else
1706 AssertRC(rc);
1707 }
1708 else
1709 /* Happens during early Windows XP boot when it is switching page tables. */
1710 Assert(rc == VINF_SUCCESS || ((rc == VERR_PAGE_TABLE_NOT_PRESENT || rc == VERR_PAGE_NOT_PRESENT) && !(CPUMGetGuestEFlags(pVM) & X86_EFL_IF)));
1711 }
1712 return false;
1713#else
1714 NOREF(pVM);
1715 return true;
1716#endif
1717}
1718
1719
1720/**
1721 * Returns flat address and limit of LDT by LDT selector from guest GDTR.
1722 *
1723 * Fully validate selector.
1724 *
1725 * @returns VBox status.
1726 * @param pVM VM Handle.
1727 * @param SelLdt LDT selector.
1728 * @param ppvLdt Where to store the flat address of LDT.
1729 * @param pcbLimit Where to store LDT limit.
1730 */
1731SELMDECL(int) SELMGetLDTFromSel(PVM pVM, RTSEL SelLdt, PRTGCPTR ppvLdt, unsigned *pcbLimit)
1732{
1733 /* Get guest GDTR. */
1734 VBOXGDTR GDTR;
1735 CPUMGetGuestGDTR(pVM, &GDTR);
1736
1737 /* Check selector TI and GDT limit. */
1738 if ( SelLdt & X86_SEL_LDT
1739 || (SelLdt > GDTR.cbGdt))
1740 return VERR_INVALID_SELECTOR;
1741
1742 /* Read descriptor from GC. */
1743 VBOXDESC Desc;
1744 int rc = PGMPhysReadGCPtr(pVM, (void *)&Desc, (RTGCPTR)(GDTR.pGdt + (SelLdt & X86_SEL_MASK)), sizeof(Desc));
1745 if (VBOX_FAILURE(rc))
1746 {
1747 /* fatal */
1748 AssertMsgFailed(("Can't read LDT descriptor for selector=%04X\n", SelLdt));
1749 return VERR_SELECTOR_NOT_PRESENT;
1750 }
1751
1752 /* Check if LDT descriptor is not present. */
1753 if (Desc.Gen.u1Present == 0)
1754 return VERR_SELECTOR_NOT_PRESENT;
1755
1756 /* Check LDT descriptor type. */
1757 if ( Desc.Gen.u1DescType == 1
1758 || Desc.Gen.u4Type != X86_SEL_TYPE_SYS_LDT)
1759 return VERR_INVALID_SELECTOR;
1760
1761 /* LDT descriptor is ok. */
1762 if (ppvLdt)
1763 {
1764 *ppvLdt = (RTGCPTR)( (Desc.Gen.u8BaseHigh2 << 24)
1765 | (Desc.Gen.u8BaseHigh1 << 16)
1766 | Desc.Gen.u16BaseLow);
1767 *pcbLimit = Desc.Gen.u4LimitHigh << 16 | Desc.Gen.u16LimitLow;
1768 }
1769 return VINF_SUCCESS;
1770}
1771
1772
1773/**
1774 * Gets information about a selector.
1775 * Intended for the debugger mostly and will prefer the guest
1776 * descriptor tables over the shadow ones.
1777 *
1778 * @returns VINF_SUCCESS on success.
1779 * @returns VERR_INVALID_SELECTOR if the selector isn't fully inside the descriptor table.
1780 * @returns VERR_SELECTOR_NOT_PRESENT if the selector wasn't present.
1781 * @returns VERR_PAGE_TABLE_NOT_PRESENT or VERR_PAGE_NOT_PRESENT if the pagetable or page
1782 * backing the selector table wasn't present.
1783 * @returns Other VBox status code on other errors.
1784 *
1785 * @param pVM VM handle.
1786 * @param Sel The selector to get info about.
1787 * @param pSelInfo Where to store the information.
1788 */
1789SELMR3DECL(int) SELMR3GetSelectorInfo(PVM pVM, RTSEL Sel, PSELMSELINFO pSelInfo)
1790{
1791 Assert(pSelInfo);
1792
1793 /*
1794 * Read the descriptor entry
1795 */
1796 VBOXDESC Desc;
1797 if ( !(Sel & X86_SEL_LDT)
1798 && ( pVM->selm.s.SelCS == (Sel & X86_SEL_MASK)
1799 || pVM->selm.s.SelDS == (Sel & X86_SEL_MASK)
1800 || pVM->selm.s.SelCS64 == (Sel & X86_SEL_MASK)
1801 || pVM->selm.s.SelTSS == (Sel & X86_SEL_MASK)
1802 || pVM->selm.s.SelTSSTrap08 == (Sel & X86_SEL_MASK))
1803 )
1804 {
1805 /*
1806 * Hypervisor descriptor.
1807 */
1808 pSelInfo->fHyper = true;
1809 Desc = pVM->selm.s.paGdtHC[Sel >> X86_SEL_SHIFT];
1810 }
1811 else if (CPUMIsGuestInProtectedMode(pVM))
1812 {
1813 /*
1814 * Read it from the guest descriptor table.
1815 */
1816 pSelInfo->fHyper = false;
1817
1818 VBOXGDTR Gdtr;
1819 RTGCPTR GCPtrDesc;
1820 CPUMGetGuestGDTR(pVM, &Gdtr);
1821 if (!(Sel & X86_SEL_LDT))
1822 {
1823 /* GDT */
1824 if ((unsigned)(Sel & X86_SEL_MASK) + sizeof(VBOXDESC) - 1 > (unsigned)Gdtr.cbGdt)
1825 return VERR_INVALID_SELECTOR;
1826 GCPtrDesc = Gdtr.pGdt + (Sel & X86_SEL_MASK);
1827 }
1828 else
1829 {
1830 /*
1831 * LDT - must locate the LDT first...
1832 */
1833 RTSEL SelLdt = CPUMGetGuestLDTR(pVM);
1834 if ( (unsigned)(SelLdt & X86_SEL_MASK) < sizeof(VBOXDESC) /* the first selector is invalid, right? */
1835 || (unsigned)(SelLdt & X86_SEL_MASK) + sizeof(VBOXDESC) - 1 > (unsigned)Gdtr.cbGdt)
1836 return VERR_INVALID_SELECTOR;
1837 GCPtrDesc = Gdtr.pGdt + (SelLdt & X86_SEL_MASK);
1838 int rc = PGMPhysReadGCPtr(pVM, &Desc, GCPtrDesc, sizeof(Desc));
1839 if (VBOX_FAILURE(rc))
1840 return rc;
1841
1842 /* validate the LDT descriptor. */
1843 if (Desc.Gen.u1Present == 0)
1844 return VERR_SELECTOR_NOT_PRESENT;
1845 if ( Desc.Gen.u1DescType == 1
1846 || Desc.Gen.u4Type != X86_SEL_TYPE_SYS_LDT)
1847 return VERR_INVALID_SELECTOR;
1848
1849 unsigned cbLimit = Desc.Gen.u4LimitHigh << 16 | Desc.Gen.u16LimitLow;
1850 if (Desc.Gen.u1Granularity)
1851 cbLimit = (cbLimit << PAGE_SHIFT) | PAGE_OFFSET_MASK;
1852 if ((unsigned)(Sel & X86_SEL_MASK) + sizeof(VBOXDESC) - 1 > cbLimit)
1853 return VERR_INVALID_SELECTOR;
1854
1855 /* calc the descriptor location. */
1856 GCPtrDesc = (Desc.Gen.u8BaseHigh2 << 24)
1857 | (Desc.Gen.u8BaseHigh1 << 16)
1858 | Desc.Gen.u16BaseLow;
1859 GCPtrDesc += (Sel & X86_SEL_MASK);
1860 }
1861
1862 /* read the descriptor. */
1863 int rc = PGMPhysReadGCPtr(pVM, &Desc, GCPtrDesc, sizeof(Desc));
1864 if (VBOX_FAILURE(rc))
1865 return rc;
1866 }
1867 else
1868 {
1869 /*
1870 * We're in real mode.
1871 */
1872 pSelInfo->Sel = Sel;
1873 pSelInfo->GCPtrBase = Sel << 4;
1874 pSelInfo->cbLimit = 0xffff;
1875 pSelInfo->fHyper = false;
1876 pSelInfo->fRealMode = true;
1877 memset(&pSelInfo->Raw, 0, sizeof(pSelInfo->Raw));
1878 return VINF_SUCCESS;
1879 }
1880
1881 /*
1882 * Extract the base and limit
1883 */
1884 pSelInfo->Sel = Sel;
1885 pSelInfo->Raw = Desc;
1886 pSelInfo->cbLimit = Desc.Gen.u4LimitHigh << 16 | Desc.Gen.u16LimitLow;
1887 if (Desc.Gen.u1Granularity)
1888 pSelInfo->cbLimit = (pSelInfo->cbLimit << PAGE_SHIFT) | PAGE_OFFSET_MASK;
1889 pSelInfo->GCPtrBase = (Desc.Gen.u8BaseHigh2 << 24)
1890 | (Desc.Gen.u8BaseHigh1 << 16)
1891 | Desc.Gen.u16BaseLow;
1892 pSelInfo->fRealMode = false;
1893
1894 return VINF_SUCCESS;
1895}
1896
1897
1898/**
1899 * Gets information about a selector from the shadow tables.
1900 *
1901 * This is intended to be faster than the SELMR3GetSelectorInfo() method, but requires
1902 * that the caller ensures that the shadow tables are up to date.
1903 *
1904 * @returns VINF_SUCCESS on success.
1905 * @returns VERR_INVALID_SELECTOR if the selector isn't fully inside the descriptor table.
1906 * @returns VERR_SELECTOR_NOT_PRESENT if the selector wasn't present.
1907 * @returns VERR_PAGE_TABLE_NOT_PRESENT or VERR_PAGE_NOT_PRESENT if the pagetable or page
1908 * backing the selector table wasn't present.
1909 * @returns Other VBox status code on other errors.
1910 *
1911 * @param pVM VM handle.
1912 * @param Sel The selector to get info about.
1913 * @param pSelInfo Where to store the information.
1914 */
1915SELMR3DECL(int) SELMR3GetShadowSelectorInfo(PVM pVM, RTSEL Sel, PSELMSELINFO pSelInfo)
1916{
1917 Assert(pSelInfo);
1918
1919 /*
1920 * Read the descriptor entry
1921 */
1922 VBOXDESC Desc;
1923 if (!(Sel & X86_SEL_LDT))
1924 {
1925 /*
1926 * Global descriptor.
1927 */
1928 Desc = pVM->selm.s.paGdtHC[Sel >> X86_SEL_SHIFT];
1929 pSelInfo->fHyper = pVM->selm.s.SelCS == (Sel & X86_SEL_MASK)
1930 || pVM->selm.s.SelDS == (Sel & X86_SEL_MASK)
1931 || pVM->selm.s.SelCS64 == (Sel & X86_SEL_MASK)
1932 || pVM->selm.s.SelTSS == (Sel & X86_SEL_MASK)
1933 || pVM->selm.s.SelTSSTrap08 == (Sel & X86_SEL_MASK);
1934 /** @todo check that the GDT offset is valid. */
1935 }
1936 else
1937 {
1938 /*
1939 * Local Descriptor.
1940 */
1941 PVBOXDESC paLDT = (PVBOXDESC)((char *)pVM->selm.s.HCPtrLdt + pVM->selm.s.offLdtHyper);
1942 Desc = paLDT[Sel >> X86_SEL_SHIFT];
1943 /** @todo check if the LDT page is actually available. */
1944 /** @todo check that the LDT offset is valid. */
1945 pSelInfo->fHyper = false;
1946 }
1947
1948 /*
1949 * Extract the base and limit
1950 */
1951 pSelInfo->Sel = Sel;
1952 pSelInfo->Raw = Desc;
1953 pSelInfo->cbLimit = Desc.Gen.u4LimitHigh << 16 | Desc.Gen.u16LimitLow;
1954 if (Desc.Gen.u1Granularity)
1955 pSelInfo->cbLimit = (pSelInfo->cbLimit << PAGE_SHIFT) | PAGE_OFFSET_MASK;
1956 pSelInfo->GCPtrBase = (Desc.Gen.u8BaseHigh2 << 24)
1957 | (Desc.Gen.u8BaseHigh1 << 16)
1958 | Desc.Gen.u16BaseLow;
1959 pSelInfo->fRealMode = false;
1960
1961 return VINF_SUCCESS;
1962}
1963
1964
1965/**
1966 * Formats a descriptor.
1967 *
1968 * @param Desc Descriptor to format.
1969 * @param Sel Selector number.
1970 * @param pszOutput Output buffer.
1971 * @param cchOutput Size of output buffer.
1972 */
1973static void selmR3FormatDescriptor(VBOXDESC Desc, RTSEL Sel, char *pszOutput, size_t cchOutput)
1974{
1975 /*
1976 * Make variable description string.
1977 */
1978 static struct
1979 {
1980 unsigned cch;
1981 const char *psz;
1982 } const aTypes[32] =
1983 {
1984 #define STRENTRY(str) { sizeof(str) - 1, str }
1985 /* system */
1986 STRENTRY("Reserved0 "), /* 0x00 */
1987 STRENTRY("TSS16Avail "), /* 0x01 */
1988 STRENTRY("LDT "), /* 0x02 */
1989 STRENTRY("TSS16Busy "), /* 0x03 */
1990 STRENTRY("Call16 "), /* 0x04 */
1991 STRENTRY("Task "), /* 0x05 */
1992 STRENTRY("Int16 "), /* 0x06 */
1993 STRENTRY("Trap16 "), /* 0x07 */
1994 STRENTRY("Reserved8 "), /* 0x08 */
1995 STRENTRY("TSS32Avail "), /* 0x09 */
1996 STRENTRY("ReservedA "), /* 0x0a */
1997 STRENTRY("TSS32Busy "), /* 0x0b */
1998 STRENTRY("Call32 "), /* 0x0c */
1999 STRENTRY("ReservedD "), /* 0x0d */
2000 STRENTRY("Int32 "), /* 0x0e */
2001 STRENTRY("Trap32 "), /* 0x0f */
2002 /* non system */
2003 STRENTRY("DataRO "), /* 0x10 */
2004 STRENTRY("DataRO Accessed "), /* 0x11 */
2005 STRENTRY("DataRW "), /* 0x12 */
2006 STRENTRY("DataRW Accessed "), /* 0x13 */
2007 STRENTRY("DataDownRO "), /* 0x14 */
2008 STRENTRY("DataDownRO Accessed "), /* 0x15 */
2009 STRENTRY("DataDownRW "), /* 0x16 */
2010 STRENTRY("DataDownRW Accessed "), /* 0x17 */
2011 STRENTRY("CodeEO "), /* 0x18 */
2012 STRENTRY("CodeEO Accessed "), /* 0x19 */
2013 STRENTRY("CodeER "), /* 0x1a */
2014 STRENTRY("CodeER Accessed "), /* 0x1b */
2015 STRENTRY("CodeConfEO "), /* 0x1c */
2016 STRENTRY("CodeConfEO Accessed "), /* 0x1d */
2017 STRENTRY("CodeConfER "), /* 0x1e */
2018 STRENTRY("CodeConfER Accessed ") /* 0x1f */
2019 #undef SYSENTRY
2020 };
2021 #define ADD_STR(psz, pszAdd) do { strcpy(psz, pszAdd); psz += strlen(pszAdd); } while (0)
2022 char szMsg[128];
2023 char *psz = &szMsg[0];
2024 unsigned i = Desc.Gen.u1DescType << 4 | Desc.Gen.u4Type;
2025 memcpy(psz, aTypes[i].psz, aTypes[i].cch);
2026 psz += aTypes[i].cch;
2027
2028 if (Desc.Gen.u1Present)
2029 ADD_STR(psz, "Present ");
2030 else
2031 ADD_STR(psz, "Not-Present ");
2032 if (Desc.Gen.u1Granularity)
2033 ADD_STR(psz, "Page ");
2034 if (Desc.Gen.u1DefBig)
2035 ADD_STR(psz, "32-bit ");
2036 else
2037 ADD_STR(psz, "16-bit ");
2038 #undef ADD_STR
2039 *psz = '\0';
2040
2041 /*
2042 * Limit and Base and format the output.
2043 */
2044 uint32_t u32Limit = Desc.Gen.u4LimitHigh << 16 | Desc.Gen.u16LimitLow;
2045 if (Desc.Gen.u1Granularity)
2046 u32Limit = u32Limit << PAGE_SHIFT | PAGE_OFFSET_MASK;
2047 uint32_t u32Base = Desc.Gen.u8BaseHigh2 << 24 | Desc.Gen.u8BaseHigh1 << 16 | Desc.Gen.u16BaseLow;
2048
2049 RTStrPrintf(pszOutput, cchOutput, "%04x - %08x %08x - base=%08x limit=%08x dpl=%d %s",
2050 Sel, Desc.au32[0], Desc.au32[1], u32Base, u32Limit, Desc.Gen.u2Dpl, szMsg);
2051}
2052
2053
2054/**
2055 * Dumps a descriptor.
2056 *
2057 * @param Desc Descriptor to dump.
2058 * @param Sel Selector number.
2059 * @param pszMsg Message to prepend the log entry with.
2060 */
2061SELMR3DECL(void) SELMR3DumpDescriptor(VBOXDESC Desc, RTSEL Sel, const char *pszMsg)
2062{
2063 char szOutput[128];
2064 selmR3FormatDescriptor(Desc, Sel, &szOutput[0], sizeof(szOutput));
2065 Log(("%s: %s\n", pszMsg, szOutput));
2066 NOREF(szOutput[0]);
2067}
2068
2069
2070/**
2071 * Display the shadow gdt.
2072 *
2073 * @param pVM VM Handle.
2074 * @param pHlp The info helpers.
2075 * @param pszArgs Arguments, ignored.
2076 */
2077static DECLCALLBACK(void) selmR3InfoGdt(PVM pVM, PCDBGFINFOHLP pHlp, const char *pszArgs)
2078{
2079 pHlp->pfnPrintf(pHlp, "Shadow GDT (GCAddr=%VGv):\n", MMHyperHC2GC(pVM, pVM->selm.s.paGdtHC));
2080 for (unsigned iGDT = 0; iGDT < SELM_GDT_ELEMENTS; iGDT++)
2081 {
2082 if (pVM->selm.s.paGdtHC[iGDT].Gen.u1Present)
2083 {
2084 char szOutput[128];
2085 selmR3FormatDescriptor(pVM->selm.s.paGdtHC[iGDT], iGDT << X86_SEL_SHIFT, &szOutput[0], sizeof(szOutput));
2086 const char *psz = "";
2087 if (iGDT == ((unsigned)pVM->selm.s.SelCS >> X86_SEL_SHIFT))
2088 psz = " HyperCS";
2089 else if (iGDT == ((unsigned)pVM->selm.s.SelDS >> X86_SEL_SHIFT))
2090 psz = " HyperDS";
2091 else if (iGDT == ((unsigned)pVM->selm.s.SelCS64 >> X86_SEL_SHIFT))
2092 psz = " HyperCS64";
2093 else if (iGDT == ((unsigned)pVM->selm.s.SelTSS >> X86_SEL_SHIFT))
2094 psz = " HyperTSS";
2095 else if (iGDT == ((unsigned)pVM->selm.s.SelTSSTrap08 >> X86_SEL_SHIFT))
2096 psz = " HyperTSSTrap08";
2097 pHlp->pfnPrintf(pHlp, "%s%s\n", szOutput, psz);
2098 }
2099 }
2100}
2101
2102
2103/**
2104 * Display the guest gdt.
2105 *
2106 * @param pVM VM Handle.
2107 * @param pHlp The info helpers.
2108 * @param pszArgs Arguments, ignored.
2109 */
2110static DECLCALLBACK(void) selmR3InfoGdtGuest(PVM pVM, PCDBGFINFOHLP pHlp, const char *pszArgs)
2111{
2112 VBOXGDTR GDTR;
2113 CPUMGetGuestGDTR(pVM, &GDTR);
2114 RTGCPTR pGDTGC = (RTGCPTR)GDTR.pGdt;
2115 unsigned cGDTs = ((unsigned)GDTR.cbGdt + 1) / sizeof(VBOXDESC);
2116
2117 pHlp->pfnPrintf(pHlp, "Guest GDT (GCAddr=%VGv limit=%x):\n", pGDTGC, GDTR.cbGdt);
2118 for (unsigned iGDT = 0; iGDT < cGDTs; iGDT++, pGDTGC += sizeof(VBOXDESC))
2119 {
2120 VBOXDESC GDTE;
2121 int rc = PGMPhysReadGCPtr(pVM, &GDTE, pGDTGC, sizeof(GDTE));
2122 if (VBOX_SUCCESS(rc))
2123 {
2124 if (GDTE.Gen.u1Present)
2125 {
2126 char szOutput[128];
2127 selmR3FormatDescriptor(GDTE, iGDT << X86_SEL_SHIFT, &szOutput[0], sizeof(szOutput));
2128 pHlp->pfnPrintf(pHlp, "%s\n", szOutput);
2129 }
2130 }
2131 else if (rc == VERR_PAGE_NOT_PRESENT)
2132 {
2133 if ((pGDTGC & PAGE_OFFSET_MASK) + sizeof(VBOXDESC) - 1 < sizeof(VBOXDESC))
2134 pHlp->pfnPrintf(pHlp, "%04 - page not present (GCAddr=%VGv)\n", iGDT << X86_SEL_SHIFT, pGDTGC);
2135 }
2136 else
2137 pHlp->pfnPrintf(pHlp, "%04 - read error rc=%Vrc GCAddr=%VGv\n", iGDT << X86_SEL_SHIFT, rc, pGDTGC);
2138 }
2139}
2140
2141
2142/**
2143 * Display the shadow ldt.
2144 *
2145 * @param pVM VM Handle.
2146 * @param pHlp The info helpers.
2147 * @param pszArgs Arguments, ignored.
2148 */
2149static DECLCALLBACK(void) selmR3InfoLdt(PVM pVM, PCDBGFINFOHLP pHlp, const char *pszArgs)
2150{
2151 unsigned cLDTs = ((unsigned)pVM->selm.s.cbLdtLimit + 1) >> X86_SEL_SHIFT;
2152 PVBOXDESC paLDT = (PVBOXDESC)((char *)pVM->selm.s.HCPtrLdt + pVM->selm.s.offLdtHyper);
2153 pHlp->pfnPrintf(pHlp, "Shadow LDT (GCAddr=%VGv limit=%d):\n", pVM->selm.s.GCPtrLdt + pVM->selm.s.offLdtHyper, pVM->selm.s.cbLdtLimit);
2154 for (unsigned iLDT = 0; iLDT < cLDTs; iLDT++)
2155 {
2156 if (paLDT[iLDT].Gen.u1Present)
2157 {
2158 char szOutput[128];
2159 selmR3FormatDescriptor(paLDT[iLDT], (iLDT << X86_SEL_SHIFT) | X86_SEL_LDT, &szOutput[0], sizeof(szOutput));
2160 pHlp->pfnPrintf(pHlp, "%s\n", szOutput);
2161 }
2162 }
2163}
2164
2165
2166/**
2167 * Display the guest ldt.
2168 *
2169 * @param pVM VM Handle.
2170 * @param pHlp The info helpers.
2171 * @param pszArgs Arguments, ignored.
2172 */
2173static DECLCALLBACK(void) selmR3InfoLdtGuest(PVM pVM, PCDBGFINFOHLP pHlp, const char *pszArgs)
2174{
2175 RTSEL SelLdt = CPUMGetGuestLDTR(pVM);
2176 if (!(SelLdt & X86_SEL_MASK))
2177 {
2178 pHlp->pfnPrintf(pHlp, "Guest LDT (Sel=%x): Null-Selector\n", SelLdt);
2179 return;
2180 }
2181
2182 RTGCPTR pLdtGC;
2183 unsigned cbLdt;
2184 int rc = SELMGetLDTFromSel(pVM, SelLdt, &pLdtGC, &cbLdt);
2185 if (VBOX_FAILURE(rc))
2186 {
2187 pHlp->pfnPrintf(pHlp, "Guest LDT (Sel=%x): rc=%Vrc\n", SelLdt, rc);
2188 return;
2189 }
2190
2191 pHlp->pfnPrintf(pHlp, "Guest LDT (Sel=%x GCAddr=%VGv limit=%x):\n", SelLdt, pLdtGC, cbLdt);
2192 unsigned cLdts = (cbLdt + 1) >> X86_SEL_SHIFT;
2193 for (unsigned iLdt = 0; iLdt < cLdts; iLdt++, pLdtGC += sizeof(VBOXDESC))
2194 {
2195 VBOXDESC LdtE;
2196 int rc = PGMPhysReadGCPtr(pVM, &LdtE, pLdtGC, sizeof(LdtE));
2197 if (VBOX_SUCCESS(rc))
2198 {
2199 if (LdtE.Gen.u1Present)
2200 {
2201 char szOutput[128];
2202 selmR3FormatDescriptor(LdtE, (iLdt << X86_SEL_SHIFT) | X86_SEL_LDT, &szOutput[0], sizeof(szOutput));
2203 pHlp->pfnPrintf(pHlp, "%s\n", szOutput);
2204 }
2205 }
2206 else if (rc == VERR_PAGE_NOT_PRESENT)
2207 {
2208 if ((pLdtGC & PAGE_OFFSET_MASK) + sizeof(VBOXDESC) - 1 < sizeof(VBOXDESC))
2209 pHlp->pfnPrintf(pHlp, "%04 - page not present (GCAddr=%VGv)\n", (iLdt << X86_SEL_SHIFT) | X86_SEL_LDT, pLdtGC);
2210 }
2211 else
2212 pHlp->pfnPrintf(pHlp, "%04 - read error rc=%Vrc GCAddr=%VGv\n", (iLdt << X86_SEL_SHIFT) | X86_SEL_LDT, rc, pLdtGC);
2213 }
2214}
2215
2216
2217/**
2218 * Dumps the hypervisor GDT
2219 *
2220 * @param pVM VM handle.
2221 */
2222SELMR3DECL(void) SELMR3DumpHyperGDT(PVM pVM)
2223{
2224 DBGFR3Info(pVM, "gdt", NULL, NULL);
2225}
2226
2227/**
2228 * Dumps the hypervisor LDT
2229 *
2230 * @param pVM VM handle.
2231 */
2232SELMR3DECL(void) SELMR3DumpHyperLDT(PVM pVM)
2233{
2234 DBGFR3Info(pVM, "ldt", NULL, NULL);
2235}
2236
2237/**
2238 * Dumps the guest GDT
2239 *
2240 * @param pVM VM handle.
2241 */
2242SELMR3DECL(void) SELMR3DumpGuestGDT(PVM pVM)
2243{
2244 DBGFR3Info(pVM, "gdtguest", NULL, NULL);
2245}
2246
2247/**
2248 * Dumps the guest LDT
2249 *
2250 * @param pVM VM handle.
2251 */
2252SELMR3DECL(void) SELMR3DumpGuestLDT(PVM pVM)
2253{
2254 DBGFR3Info(pVM, "ldtguest", NULL, NULL);
2255}
2256
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