VirtualBox

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

Last change on this file since 96335 was 93554, checked in by vboxsync, 3 years ago

VMM: Changed PAGE_SIZE -> GUEST_PAGE_SIZE / HOST_PAGE_SIZE, PAGE_SHIFT -> GUEST_PAGE_SHIFT / HOST_PAGE_SHIFT, and PAGE_OFFSET_MASK -> GUEST_PAGE_OFFSET_MASK / HOST_PAGE_OFFSET_MASK. Also removed most usage of ASMMemIsZeroPage and ASMMemZeroPage since the host and guest page size doesn't need to be the same any more. Some work left to do in the page pool code. bugref:9898

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 23.3 KB
Line 
1/* $Id: SELM.cpp 93554 2022-02-02 22:57:02Z vboxsync $ */
2/** @file
3 * SELM - The Selector Manager.
4 */
5
6/*
7 * Copyright (C) 2006-2022 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 CPUMImportGuestStateOnDemand(pVCpu, CPUMCTX_EXTRN_CR0 | CPUMCTX_EXTRN_CR3 | CPUMCTX_EXTRN_CR4
572 | CPUMCTX_EXTRN_EFER | CPUMCTX_EXTRN_GDTR);
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 & GUEST_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 = VMMGetCpu(pVM);
616 CPUMImportGuestStateOnDemand(pVCpu, CPUMCTX_EXTRN_CR0 | CPUMCTX_EXTRN_CR3 | CPUMCTX_EXTRN_CR4
617 | CPUMCTX_EXTRN_EFER | CPUMCTX_EXTRN_GDTR | CPUMCTX_EXTRN_LDTR);
618
619 uint64_t GCPtrLdt;
620 uint32_t cbLdt;
621 RTSEL SelLdt = CPUMGetGuestLdtrEx(pVCpu, &GCPtrLdt, &cbLdt);
622 if (!(SelLdt & X86_SEL_MASK_OFF_RPL))
623 {
624 pHlp->pfnPrintf(pHlp, "Guest LDT (Sel=%x): Null-Selector\n", SelLdt);
625 return;
626 }
627
628 pHlp->pfnPrintf(pHlp, "Guest LDT (Sel=%x GCAddr=%RX64 limit=%x):\n", SelLdt, GCPtrLdt, cbLdt);
629 unsigned cLdts = (cbLdt + 1) >> X86_SEL_SHIFT;
630 for (unsigned iLdt = 0; iLdt < cLdts; iLdt++, GCPtrLdt += sizeof(X86DESC))
631 {
632 X86DESC LdtE;
633 int rc = PGMPhysSimpleReadGCPtr(pVCpu, &LdtE, GCPtrLdt, sizeof(LdtE));
634 if (RT_SUCCESS(rc))
635 {
636 if (LdtE.Gen.u1Present)
637 {
638 char szOutput[128];
639 selmR3FormatDescriptor(LdtE, (iLdt << X86_SEL_SHIFT) | X86_SEL_LDT, &szOutput[0], sizeof(szOutput));
640 pHlp->pfnPrintf(pHlp, "%s\n", szOutput);
641 }
642 }
643 else if (rc == VERR_PAGE_NOT_PRESENT)
644 {
645 if ((GCPtrLdt & GUEST_PAGE_OFFSET_MASK) + sizeof(X86DESC) - 1 < sizeof(X86DESC))
646 pHlp->pfnPrintf(pHlp, "%04x - page not present (GCAddr=%RGv)\n", (iLdt << X86_SEL_SHIFT) | X86_SEL_LDT, GCPtrLdt);
647 }
648 else
649 pHlp->pfnPrintf(pHlp, "%04x - read error rc=%Rrc GCAddr=%RGv\n", (iLdt << X86_SEL_SHIFT) | X86_SEL_LDT, rc, GCPtrLdt);
650 }
651 NOREF(pszArgs);
652}
653
654
655/**
656 * Dumps the guest GDT
657 *
658 * @param pVM The cross context VM structure.
659 */
660VMMR3DECL(void) SELMR3DumpGuestGDT(PVM pVM)
661{
662 DBGFR3Info(pVM->pUVM, "gdt", NULL, NULL);
663}
664
665
666/**
667 * Dumps the guest LDT
668 *
669 * @param pVM The cross context VM structure.
670 */
671VMMR3DECL(void) SELMR3DumpGuestLDT(PVM pVM)
672{
673 DBGFR3Info(pVM->pUVM, "ldt", NULL, NULL);
674}
675
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