VirtualBox

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

Last change on this file since 80014 was 80014, checked in by vboxsync, 6 years ago

VMM: Kicking out raw-mode (work in progress). bugref:9517

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 22.9 KB
Line 
1/* $Id: SELM.cpp 80014 2019-07-26 16:12:06Z vboxsync $ */
2/** @file
3 * SELM - The Selector Manager.
4 */
5
6/*
7 * Copyright (C) 2006-2019 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/*********************************************************************************************************************************
60* Header Files *
61*********************************************************************************************************************************/
62#define LOG_GROUP LOG_GROUP_SELM
63#include <VBox/vmm/selm.h>
64#include <VBox/vmm/cpum.h>
65#include <VBox/vmm/stam.h>
66#include <VBox/vmm/em.h>
67#include <VBox/vmm/hm.h>
68#include <VBox/vmm/mm.h>
69#include <VBox/vmm/ssm.h>
70#include <VBox/vmm/pgm.h>
71#include <VBox/vmm/trpm.h>
72#include <VBox/vmm/dbgf.h>
73#include "SELMInternal.h"
74#include <VBox/vmm/vm.h>
75#include <VBox/err.h>
76#include <VBox/param.h>
77
78#include <iprt/assert.h>
79#include <VBox/log.h>
80#include <iprt/asm.h>
81#include <iprt/string.h>
82#include <iprt/thread.h>
83#include <iprt/string.h>
84
85#include "SELMInline.h"
86
87
88/*********************************************************************************************************************************
89* Internal Functions *
90*********************************************************************************************************************************/
91static DECLCALLBACK(void) selmR3InfoGdtGuest(PVM pVM, PCDBGFINFOHLP pHlp, const char *pszArgs);
92static DECLCALLBACK(void) selmR3InfoLdtGuest(PVM pVM, PCDBGFINFOHLP pHlp, const char *pszArgs);
93//static DECLCALLBACK(void) selmR3InfoTssGuest(PVM pVM, PCDBGFINFOHLP pHlp, const char *pszArgs);
94
95
96
97/**
98 * Initializes the SELM.
99 *
100 * @returns VBox status code.
101 * @param pVM The cross context VM structure.
102 */
103VMMR3DECL(int) SELMR3Init(PVM pVM)
104{
105 int rc;
106 LogFlow(("SELMR3Init\n"));
107
108 /*
109 * Assert alignment and sizes.
110 * (The TSS block requires contiguous back.)
111 */
112 AssertCompile(sizeof(pVM->selm.s) <= sizeof(pVM->selm.padding)); AssertRelease(sizeof(pVM->selm.s) <= sizeof(pVM->selm.padding));
113 AssertCompileMemberAlignment(VM, selm.s, 32); AssertRelease(!(RT_UOFFSETOF(VM, selm.s) & 31));
114
115 /*
116 * Register the saved state data unit.
117 */
118 rc = SSMR3RegisterStub(pVM, "selm", 1);
119 if (RT_FAILURE(rc))
120 return rc;
121
122 /*
123 * Statistics.
124 */
125 STAM_REG( pVM, &pVM->selm.s.StatLoadHidSelGst, STAMTYPE_COUNTER, "/SELM/LoadHidSel/LoadedGuest", STAMUNIT_OCCURENCES, "SELMLoadHiddenSelectorReg: Loaded from guest tables.");
126 STAM_REG( pVM, &pVM->selm.s.StatLoadHidSelShw, STAMTYPE_COUNTER, "/SELM/LoadHidSel/LoadedShadow", STAMUNIT_OCCURENCES, "SELMLoadHiddenSelectorReg: Loaded from shadow tables.");
127 STAM_REL_REG(pVM, &pVM->selm.s.StatLoadHidSelReadErrors, STAMTYPE_COUNTER, "/SELM/LoadHidSel/GstReadErrors", STAMUNIT_OCCURENCES, "SELMLoadHiddenSelectorReg: Guest table read errors.");
128 STAM_REL_REG(pVM, &pVM->selm.s.StatLoadHidSelGstNoGood, STAMTYPE_COUNTER, "/SELM/LoadHidSel/NoGoodGuest", STAMUNIT_OCCURENCES, "SELMLoadHiddenSelectorReg: No good guest table entry.");
129
130 /*
131 * Register info handlers.
132 */
133 DBGFR3InfoRegisterInternalEx(pVM, "gdt", "Displays the guest GDT. No arguments.", &selmR3InfoGdtGuest, DBGFINFO_FLAGS_RUN_ON_EMT);
134 DBGFR3InfoRegisterInternalEx(pVM, "ldt", "Displays the guest LDT. No arguments.", &selmR3InfoLdtGuest, DBGFINFO_FLAGS_RUN_ON_EMT);
135 //DBGFR3InfoRegisterInternal(pVM, "tss", "Displays the guest TSS. No arguments.", &selmR3InfoTssGuest, DBGFINFO_FLAGS_RUN_ON_EMT);
136
137 return rc;
138}
139
140
141/**
142 * Applies relocations to data and code managed by this
143 * component. This function will be called at init and
144 * whenever the VMM need to relocate it self inside the GC.
145 *
146 * @param pVM The cross context VM structure.
147 */
148VMMR3DECL(void) SELMR3Relocate(PVM pVM)
149{
150 LogFlow(("SELMR3Relocate\n"));
151 RT_NOREF(pVM);
152}
153
154
155/**
156 * Terminates the SELM.
157 *
158 * Termination means cleaning up and freeing all resources,
159 * the VM it self is at this point powered off or suspended.
160 *
161 * @returns VBox status code.
162 * @param pVM The cross context VM structure.
163 */
164VMMR3DECL(int) SELMR3Term(PVM pVM)
165{
166 NOREF(pVM);
167 return VINF_SUCCESS;
168}
169
170
171/**
172 * The VM is being reset.
173 *
174 * For the SELM component this means that any GDT/LDT/TSS monitors
175 * needs to be removed.
176 *
177 * @param pVM The cross context VM structure.
178 */
179VMMR3DECL(void) SELMR3Reset(PVM pVM)
180{
181 LogFlow(("SELMR3Reset:\n"));
182 VM_ASSERT_EMT(pVM);
183 RT_NOREF(pVM);
184}
185
186
187/**
188 * Gets information about a 64-bit selector, SELMR3GetSelectorInfo helper.
189 *
190 * See SELMR3GetSelectorInfo for details.
191 *
192 * @returns VBox status code, see SELMR3GetSelectorInfo for details.
193 *
194 * @param pVCpu The cross context virtual CPU structure.
195 * @param Sel The selector to get info about.
196 * @param pSelInfo Where to store the information.
197 */
198static int selmR3GetSelectorInfo64(PVMCPU pVCpu, RTSEL Sel, PDBGFSELINFO pSelInfo)
199{
200 /*
201 * Read it from the guest descriptor table.
202 */
203/** @todo this is bogus wrt the LDT/GDT limit on long selectors. */
204 X86DESC64 Desc;
205 RTGCPTR GCPtrDesc;
206 if (!(Sel & X86_SEL_LDT))
207 {
208 /* GDT */
209 VBOXGDTR Gdtr;
210 CPUMGetGuestGDTR(pVCpu, &Gdtr);
211 if ((Sel | X86_SEL_RPL_LDT) > Gdtr.cbGdt)
212 return VERR_INVALID_SELECTOR;
213 GCPtrDesc = Gdtr.pGdt + (Sel & X86_SEL_MASK);
214 }
215 else
216 {
217 /* LDT */
218 uint64_t GCPtrBase;
219 uint32_t cbLimit;
220 CPUMGetGuestLdtrEx(pVCpu, &GCPtrBase, &cbLimit);
221 if ((Sel | X86_SEL_RPL_LDT) > cbLimit)
222 return VERR_INVALID_SELECTOR;
223
224 /* calc the descriptor location. */
225 GCPtrDesc = GCPtrBase + (Sel & X86_SEL_MASK);
226 }
227
228 /* read the descriptor. */
229 int rc = PGMPhysSimpleReadGCPtr(pVCpu, &Desc, GCPtrDesc, sizeof(Desc));
230 if (RT_FAILURE(rc))
231 {
232 rc = PGMPhysSimpleReadGCPtr(pVCpu, &Desc, GCPtrDesc, sizeof(X86DESC));
233 if (RT_FAILURE(rc))
234 return rc;
235 Desc.au64[1] = 0;
236 }
237
238 /*
239 * Extract the base and limit
240 * (We ignore the present bit here, which is probably a bit silly...)
241 */
242 pSelInfo->Sel = Sel;
243 pSelInfo->fFlags = DBGFSELINFO_FLAGS_LONG_MODE;
244 pSelInfo->u.Raw64 = Desc;
245 if (Desc.Gen.u1DescType)
246 {
247 /*
248 * 64-bit code selectors are wide open, it's not possible to detect
249 * 64-bit data or stack selectors without also dragging in assumptions
250 * about current CS (i.e. that's we're executing in 64-bit mode). So,
251 * the selinfo user needs to deal with this in the context the info is
252 * used unfortunately.
253 */
254 if ( Desc.Gen.u1Long
255 && !Desc.Gen.u1DefBig
256 && (Desc.Gen.u4Type & X86_SEL_TYPE_CODE))
257 {
258 /* Note! We ignore the segment limit hacks that was added by AMD. */
259 pSelInfo->GCPtrBase = 0;
260 pSelInfo->cbLimit = ~(RTGCUINTPTR)0;
261 }
262 else
263 {
264 pSelInfo->cbLimit = X86DESC_LIMIT_G(&Desc);
265 pSelInfo->GCPtrBase = X86DESC_BASE(&Desc);
266 }
267 pSelInfo->SelGate = 0;
268 }
269 else if ( Desc.Gen.u4Type == AMD64_SEL_TYPE_SYS_LDT
270 || Desc.Gen.u4Type == AMD64_SEL_TYPE_SYS_TSS_AVAIL
271 || Desc.Gen.u4Type == AMD64_SEL_TYPE_SYS_TSS_BUSY)
272 {
273 /* Note. LDT descriptors are weird in long mode, we ignore the footnote
274 in the AMD manual here as a simplification. */
275 pSelInfo->GCPtrBase = X86DESC64_BASE(&Desc);
276 pSelInfo->cbLimit = X86DESC_LIMIT_G(&Desc);
277 pSelInfo->SelGate = 0;
278 }
279 else if ( Desc.Gen.u4Type == AMD64_SEL_TYPE_SYS_CALL_GATE
280 || Desc.Gen.u4Type == AMD64_SEL_TYPE_SYS_TRAP_GATE
281 || Desc.Gen.u4Type == AMD64_SEL_TYPE_SYS_INT_GATE)
282 {
283 pSelInfo->cbLimit = X86DESC64_BASE(&Desc);
284 pSelInfo->GCPtrBase = Desc.Gate.u16OffsetLow
285 | ((uint32_t)Desc.Gate.u16OffsetHigh << 16)
286 | ((uint64_t)Desc.Gate.u32OffsetTop << 32);
287 pSelInfo->SelGate = Desc.Gate.u16Sel;
288 pSelInfo->fFlags |= DBGFSELINFO_FLAGS_GATE;
289 }
290 else
291 {
292 pSelInfo->cbLimit = 0;
293 pSelInfo->GCPtrBase = 0;
294 pSelInfo->SelGate = 0;
295 pSelInfo->fFlags |= DBGFSELINFO_FLAGS_INVALID;
296 }
297 if (!Desc.Gen.u1Present)
298 pSelInfo->fFlags |= DBGFSELINFO_FLAGS_NOT_PRESENT;
299
300 return VINF_SUCCESS;
301}
302
303
304/**
305 * Worker for selmR3GetSelectorInfo32 and SELMR3GetShadowSelectorInfo that
306 * interprets a legacy descriptor table entry and fills in the selector info
307 * structure from it.
308 *
309 * @param pSelInfo Where to store the selector info. Only the fFlags and
310 * Sel members have been initialized.
311 * @param pDesc The legacy descriptor to parse.
312 */
313DECLINLINE(void) selmR3SelInfoFromDesc32(PDBGFSELINFO pSelInfo, PCX86DESC pDesc)
314{
315 pSelInfo->u.Raw64.au64[1] = 0;
316 pSelInfo->u.Raw = *pDesc;
317 if ( pDesc->Gen.u1DescType
318 || !(pDesc->Gen.u4Type & 4))
319 {
320 pSelInfo->cbLimit = X86DESC_LIMIT_G(pDesc);
321 pSelInfo->GCPtrBase = X86DESC_BASE(pDesc);
322 pSelInfo->SelGate = 0;
323 }
324 else if (pDesc->Gen.u4Type != X86_SEL_TYPE_SYS_UNDEFINED4)
325 {
326 pSelInfo->cbLimit = 0;
327 if (pDesc->Gen.u4Type == X86_SEL_TYPE_SYS_TASK_GATE)
328 pSelInfo->GCPtrBase = 0;
329 else
330 pSelInfo->GCPtrBase = pDesc->Gate.u16OffsetLow
331 | (uint32_t)pDesc->Gate.u16OffsetHigh << 16;
332 pSelInfo->SelGate = pDesc->Gate.u16Sel;
333 pSelInfo->fFlags |= DBGFSELINFO_FLAGS_GATE;
334 }
335 else
336 {
337 pSelInfo->cbLimit = 0;
338 pSelInfo->GCPtrBase = 0;
339 pSelInfo->SelGate = 0;
340 pSelInfo->fFlags |= DBGFSELINFO_FLAGS_INVALID;
341 }
342 if (!pDesc->Gen.u1Present)
343 pSelInfo->fFlags |= DBGFSELINFO_FLAGS_NOT_PRESENT;
344}
345
346
347/**
348 * Gets information about a 64-bit selector, SELMR3GetSelectorInfo helper.
349 *
350 * See SELMR3GetSelectorInfo for details.
351 *
352 * @returns VBox status code, see SELMR3GetSelectorInfo for details.
353 *
354 * @param pVCpu The cross context virtual CPU structure.
355 * @param Sel The selector to get info about.
356 * @param pSelInfo Where to store the information.
357 */
358static int selmR3GetSelectorInfo32(PVMCPU pVCpu, RTSEL Sel, PDBGFSELINFO pSelInfo)
359{
360 /*
361 * Read the descriptor entry
362 */
363 pSelInfo->fFlags = 0;
364 if (CPUMIsGuestInProtectedMode(pVCpu))
365 {
366 /*
367 * Read it from the guest descriptor table.
368 */
369 pSelInfo->fFlags = DBGFSELINFO_FLAGS_PROT_MODE;
370
371 RTGCPTR GCPtrDesc;
372 if (!(Sel & X86_SEL_LDT))
373 {
374 /* GDT */
375 VBOXGDTR Gdtr;
376 CPUMGetGuestGDTR(pVCpu, &Gdtr);
377 if ((Sel | X86_SEL_RPL_LDT) > Gdtr.cbGdt)
378 return VERR_INVALID_SELECTOR;
379 GCPtrDesc = Gdtr.pGdt + (Sel & X86_SEL_MASK);
380 }
381 else
382 {
383 /* LDT */
384 uint64_t GCPtrBase;
385 uint32_t cbLimit;
386 CPUMGetGuestLdtrEx(pVCpu, &GCPtrBase, &cbLimit);
387 if ((Sel | X86_SEL_RPL_LDT) > cbLimit)
388 return VERR_INVALID_SELECTOR;
389
390 /* calc the descriptor location. */
391 GCPtrDesc = GCPtrBase + (Sel & X86_SEL_MASK);
392 }
393
394 /* read the descriptor. */
395 X86DESC Desc;
396 int rc = PGMPhysSimpleReadGCPtr(pVCpu, &Desc, GCPtrDesc, sizeof(Desc));
397 if (RT_SUCCESS(rc))
398 {
399 /*
400 * Extract the base and limit or sel:offset for gates.
401 */
402 pSelInfo->Sel = Sel;
403 selmR3SelInfoFromDesc32(pSelInfo, &Desc);
404
405 return VINF_SUCCESS;
406 }
407 return rc;
408 }
409
410 /*
411 * We're in real mode.
412 */
413 pSelInfo->Sel = Sel;
414 pSelInfo->GCPtrBase = Sel << 4;
415 pSelInfo->cbLimit = 0xffff;
416 pSelInfo->fFlags = DBGFSELINFO_FLAGS_REAL_MODE;
417 pSelInfo->u.Raw64.au64[0] = 0;
418 pSelInfo->u.Raw64.au64[1] = 0;
419 pSelInfo->SelGate = 0;
420 return VINF_SUCCESS;
421}
422
423
424/**
425 * Gets information about a selector.
426 *
427 * Intended for the debugger mostly and will prefer the guest descriptor tables
428 * over the shadow ones.
429 *
430 * @retval VINF_SUCCESS on success.
431 * @retval VERR_INVALID_SELECTOR if the selector isn't fully inside the
432 * descriptor table.
433 * @retval VERR_SELECTOR_NOT_PRESENT if the LDT is invalid or not present. This
434 * is not returned if the selector itself isn't present, you have to
435 * check that for yourself (see DBGFSELINFO::fFlags).
436 * @retval VERR_PAGE_TABLE_NOT_PRESENT or VERR_PAGE_NOT_PRESENT if the
437 * pagetable or page backing the selector table wasn't present.
438 * @returns Other VBox status code on other errors.
439 *
440 * @param pVCpu The cross context virtual CPU structure.
441 * @param Sel The selector to get info about.
442 * @param pSelInfo Where to store the information.
443 */
444VMMR3DECL(int) SELMR3GetSelectorInfo(PVMCPU pVCpu, RTSEL Sel, PDBGFSELINFO pSelInfo)
445{
446 AssertPtr(pSelInfo);
447 if (CPUMIsGuestInLongMode(pVCpu))
448 return selmR3GetSelectorInfo64(pVCpu, Sel, pSelInfo);
449 return selmR3GetSelectorInfo32(pVCpu, Sel, pSelInfo);
450}
451
452
453/**
454 * Formats a descriptor.
455 *
456 * @param Desc Descriptor to format.
457 * @param Sel Selector number.
458 * @param pszOutput Output buffer.
459 * @param cchOutput Size of output buffer.
460 */
461static void selmR3FormatDescriptor(X86DESC Desc, RTSEL Sel, char *pszOutput, size_t cchOutput)
462{
463 /*
464 * Make variable description string.
465 */
466 static struct
467 {
468 unsigned cch;
469 const char *psz;
470 } const aTypes[32] =
471 {
472#define STRENTRY(str) { sizeof(str) - 1, str }
473 /* system */
474 STRENTRY("Reserved0 "), /* 0x00 */
475 STRENTRY("TSS16Avail "), /* 0x01 */
476 STRENTRY("LDT "), /* 0x02 */
477 STRENTRY("TSS16Busy "), /* 0x03 */
478 STRENTRY("Call16 "), /* 0x04 */
479 STRENTRY("Task "), /* 0x05 */
480 STRENTRY("Int16 "), /* 0x06 */
481 STRENTRY("Trap16 "), /* 0x07 */
482 STRENTRY("Reserved8 "), /* 0x08 */
483 STRENTRY("TSS32Avail "), /* 0x09 */
484 STRENTRY("ReservedA "), /* 0x0a */
485 STRENTRY("TSS32Busy "), /* 0x0b */
486 STRENTRY("Call32 "), /* 0x0c */
487 STRENTRY("ReservedD "), /* 0x0d */
488 STRENTRY("Int32 "), /* 0x0e */
489 STRENTRY("Trap32 "), /* 0x0f */
490 /* non system */
491 STRENTRY("DataRO "), /* 0x10 */
492 STRENTRY("DataRO Accessed "), /* 0x11 */
493 STRENTRY("DataRW "), /* 0x12 */
494 STRENTRY("DataRW Accessed "), /* 0x13 */
495 STRENTRY("DataDownRO "), /* 0x14 */
496 STRENTRY("DataDownRO Accessed "), /* 0x15 */
497 STRENTRY("DataDownRW "), /* 0x16 */
498 STRENTRY("DataDownRW Accessed "), /* 0x17 */
499 STRENTRY("CodeEO "), /* 0x18 */
500 STRENTRY("CodeEO Accessed "), /* 0x19 */
501 STRENTRY("CodeER "), /* 0x1a */
502 STRENTRY("CodeER Accessed "), /* 0x1b */
503 STRENTRY("CodeConfEO "), /* 0x1c */
504 STRENTRY("CodeConfEO Accessed "), /* 0x1d */
505 STRENTRY("CodeConfER "), /* 0x1e */
506 STRENTRY("CodeConfER Accessed ") /* 0x1f */
507#undef SYSENTRY
508 };
509#define ADD_STR(psz, pszAdd) do { strcpy(psz, pszAdd); psz += strlen(pszAdd); } while (0)
510 char szMsg[128];
511 char *psz = &szMsg[0];
512 unsigned i = Desc.Gen.u1DescType << 4 | Desc.Gen.u4Type;
513 memcpy(psz, aTypes[i].psz, aTypes[i].cch);
514 psz += aTypes[i].cch;
515
516 if (Desc.Gen.u1Present)
517 ADD_STR(psz, "Present ");
518 else
519 ADD_STR(psz, "Not-Present ");
520 if (Desc.Gen.u1Granularity)
521 ADD_STR(psz, "Page ");
522 if (Desc.Gen.u1DefBig)
523 ADD_STR(psz, "32-bit ");
524 else
525 ADD_STR(psz, "16-bit ");
526#undef ADD_STR
527 *psz = '\0';
528
529 /*
530 * Limit and Base and format the output.
531 */
532 uint32_t u32Limit = X86DESC_LIMIT_G(&Desc);
533 uint32_t u32Base = X86DESC_BASE(&Desc);
534
535 RTStrPrintf(pszOutput, cchOutput, "%04x - %08x %08x - base=%08x limit=%08x dpl=%d %s",
536 Sel, Desc.au32[0], Desc.au32[1], u32Base, u32Limit, Desc.Gen.u2Dpl, szMsg);
537}
538
539
540/**
541 * Dumps a descriptor.
542 *
543 * @param Desc Descriptor to dump.
544 * @param Sel Selector number.
545 * @param pszMsg Message to prepend the log entry with.
546 */
547VMMR3DECL(void) SELMR3DumpDescriptor(X86DESC Desc, RTSEL Sel, const char *pszMsg)
548{
549#ifdef LOG_ENABLED
550 if (LogIsEnabled())
551 {
552 char szOutput[128];
553 selmR3FormatDescriptor(Desc, Sel, &szOutput[0], sizeof(szOutput));
554 Log(("%s: %s\n", pszMsg, szOutput));
555 }
556#else
557 RT_NOREF3(Desc, Sel, pszMsg);
558#endif
559}
560
561
562/**
563 * Display the guest gdt.
564 *
565 * @param pVM The cross context VM structure.
566 * @param pHlp The info helpers.
567 * @param pszArgs Arguments, ignored.
568 */
569static DECLCALLBACK(void) selmR3InfoGdtGuest(PVM pVM, PCDBGFINFOHLP pHlp, const char *pszArgs)
570{
571 /** @todo SMP support! */
572 PVMCPU pVCpu = &pVM->aCpus[0];
573
574 VBOXGDTR GDTR;
575 CPUMGetGuestGDTR(pVCpu, &GDTR);
576 RTGCPTR GCPtrGDT = GDTR.pGdt;
577 unsigned cGDTs = ((unsigned)GDTR.cbGdt + 1) / sizeof(X86DESC);
578
579 pHlp->pfnPrintf(pHlp, "Guest GDT (GCAddr=%RGv limit=%x):\n", GCPtrGDT, GDTR.cbGdt);
580 for (unsigned iGDT = 0; iGDT < cGDTs; iGDT++, GCPtrGDT += sizeof(X86DESC))
581 {
582 X86DESC GDTE;
583 int rc = PGMPhysSimpleReadGCPtr(pVCpu, &GDTE, GCPtrGDT, sizeof(GDTE));
584 if (RT_SUCCESS(rc))
585 {
586 if (GDTE.Gen.u1Present)
587 {
588 char szOutput[128];
589 selmR3FormatDescriptor(GDTE, iGDT << X86_SEL_SHIFT, &szOutput[0], sizeof(szOutput));
590 pHlp->pfnPrintf(pHlp, "%s\n", szOutput);
591 }
592 }
593 else if (rc == VERR_PAGE_NOT_PRESENT)
594 {
595 if ((GCPtrGDT & PAGE_OFFSET_MASK) + sizeof(X86DESC) - 1 < sizeof(X86DESC))
596 pHlp->pfnPrintf(pHlp, "%04x - page not present (GCAddr=%RGv)\n", iGDT << X86_SEL_SHIFT, GCPtrGDT);
597 }
598 else
599 pHlp->pfnPrintf(pHlp, "%04x - read error rc=%Rrc GCAddr=%RGv\n", iGDT << X86_SEL_SHIFT, rc, GCPtrGDT);
600 }
601 NOREF(pszArgs);
602}
603
604
605/**
606 * Display the guest ldt.
607 *
608 * @param pVM The cross context VM structure.
609 * @param pHlp The info helpers.
610 * @param pszArgs Arguments, ignored.
611 */
612static DECLCALLBACK(void) selmR3InfoLdtGuest(PVM pVM, PCDBGFINFOHLP pHlp, const char *pszArgs)
613{
614 /** @todo SMP support! */
615 PVMCPU pVCpu = &pVM->aCpus[0];
616
617 uint64_t GCPtrLdt;
618 uint32_t cbLdt;
619 RTSEL SelLdt = CPUMGetGuestLdtrEx(pVCpu, &GCPtrLdt, &cbLdt);
620 if (!(SelLdt & X86_SEL_MASK_OFF_RPL))
621 {
622 pHlp->pfnPrintf(pHlp, "Guest LDT (Sel=%x): Null-Selector\n", SelLdt);
623 return;
624 }
625
626 pHlp->pfnPrintf(pHlp, "Guest LDT (Sel=%x GCAddr=%RX64 limit=%x):\n", SelLdt, GCPtrLdt, cbLdt);
627 unsigned cLdts = (cbLdt + 1) >> X86_SEL_SHIFT;
628 for (unsigned iLdt = 0; iLdt < cLdts; iLdt++, GCPtrLdt += sizeof(X86DESC))
629 {
630 X86DESC LdtE;
631 int rc = PGMPhysSimpleReadGCPtr(pVCpu, &LdtE, GCPtrLdt, sizeof(LdtE));
632 if (RT_SUCCESS(rc))
633 {
634 if (LdtE.Gen.u1Present)
635 {
636 char szOutput[128];
637 selmR3FormatDescriptor(LdtE, (iLdt << X86_SEL_SHIFT) | X86_SEL_LDT, &szOutput[0], sizeof(szOutput));
638 pHlp->pfnPrintf(pHlp, "%s\n", szOutput);
639 }
640 }
641 else if (rc == VERR_PAGE_NOT_PRESENT)
642 {
643 if ((GCPtrLdt & PAGE_OFFSET_MASK) + sizeof(X86DESC) - 1 < sizeof(X86DESC))
644 pHlp->pfnPrintf(pHlp, "%04x - page not present (GCAddr=%RGv)\n", (iLdt << X86_SEL_SHIFT) | X86_SEL_LDT, GCPtrLdt);
645 }
646 else
647 pHlp->pfnPrintf(pHlp, "%04x - read error rc=%Rrc GCAddr=%RGv\n", (iLdt << X86_SEL_SHIFT) | X86_SEL_LDT, rc, GCPtrLdt);
648 }
649 NOREF(pszArgs);
650}
651
652
653/**
654 * Dumps the guest GDT
655 *
656 * @param pVM The cross context VM structure.
657 */
658VMMR3DECL(void) SELMR3DumpGuestGDT(PVM pVM)
659{
660 DBGFR3Info(pVM->pUVM, "gdt", NULL, NULL);
661}
662
663
664/**
665 * Dumps the guest LDT
666 *
667 * @param pVM The cross context VM structure.
668 */
669VMMR3DECL(void) SELMR3DumpGuestLDT(PVM pVM)
670{
671 DBGFR3Info(pVM->pUVM, "ldt", NULL, NULL);
672}
673
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette