VirtualBox

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

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

Null TR means there's no TSS, has to be reloaded first, so clear the forced action.

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