VirtualBox

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

Last change on this file since 3778 was 3724, checked in by vboxsync, 17 years ago

removed the obsolete x86context.h (all constants are in VBox/x86.h).

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