VirtualBox

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

Last change on this file since 91958 was 85968, checked in by vboxsync, 4 years ago

SELM: Guest GDT and LDT dumping should be done on the calling EMT to avoid asserting in PGM.

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