VirtualBox

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

Last change on this file since 106061 was 106061, checked in by vboxsync, 8 weeks ago

Copyright year updates by scm.

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