VirtualBox

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

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

More stats

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

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