VirtualBox

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

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

format typo

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