VirtualBox

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

Last change on this file since 41896 was 41896, checked in by vboxsync, 12 years ago

SELM.cpp: Fixed assertion in SELMR3SyncTSS because the initial (reset) value of trHid.Attr.n.u4Type changed a while back.

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