VirtualBox

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

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

Updated SELMR3CheckTSS so it won't assert for no good reason.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 87.6 KB
Line 
1/* $Id: SELM.cpp 2721 2007-05-18 15:52:52Z 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 if (!pVM->selm.s.fSyncTSSRing0Stack)
1737 {
1738 RTGCPTR pGuestTSS = pVM->selm.s.GCPtrGuestTss;
1739 uint32_t ESPR0;
1740 int rc = PGMPhysReadGCPtr(pVM, &ESPR0, pGuestTSS + RT_OFFSETOF(VBOXTSS, esp0), sizeof(ESPR0));
1741 if (VBOX_SUCCESS(rc))
1742 {
1743 RTSEL SelSS0;
1744 rc = PGMPhysReadGCPtr(pVM, &SelSS0, pGuestTSS + RT_OFFSETOF(VBOXTSS, ss0), sizeof(SelSS0));
1745 if (VBOX_SUCCESS(rc))
1746 {
1747 if ( ESPR0 == pVM->selm.s.Tss.esp1
1748 && SelSS0 == (pVM->selm.s.Tss.ss1 & ~1))
1749 return true;
1750
1751 RTGCPHYS GCPhys;
1752 uint64_t fFlags;
1753
1754 rc = PGMGstGetPage(pVM, pGuestTSS, &fFlags, &GCPhys);
1755 AssertRC(rc);
1756 AssertMsgFailed(("TSS out of sync!! (%04X:%08X vs %04X:%08X (guest)) Tss=%VGv Phys=%VGp\n",
1757 (pVM->selm.s.Tss.ss1 & ~1), pVM->selm.s.Tss.esp1, SelSS0, ESPR0, pGuestTSS, GCPhys));
1758 }
1759 else
1760 AssertRC(rc);
1761 }
1762 else
1763 /* Happens during early Windows XP boot when it is switching page tables. */
1764 Assert(rc == VINF_SUCCESS || ((rc == VERR_PAGE_TABLE_NOT_PRESENT || rc == VERR_PAGE_NOT_PRESENT) && !(CPUMGetGuestEFlags(pVM) & X86_EFL_IF)));
1765 }
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