VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMAll/GCMAll.cpp@ 94991

Last change on this file since 94991 was 94882, checked in by vboxsync, 3 years ago

VMM: First stab at Guest Compatibility Manager, fixing up things like division overflows caused by fast CPUs (see bugref:9735).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 9.4 KB
Line 
1/** @file
2 * GCM - Guest Compatibility Manager - All Contexts.
3 */
4
5/*
6 * Copyright (C) 2022 Oracle Corporation
7 *
8 * This file is part of VirtualBox Open Source Edition (OSE), as
9 * available from http://www.virtualbox.org. This file is free software;
10 * you can redistribute it and/or modify it under the terms of the GNU
11 * General Public License (GPL) as published by the Free Software
12 * Foundation, in version 2 as it comes in the "COPYING" file of the
13 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
14 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
15 */
16
17
18/*********************************************************************************************************************************
19* Header Files *
20*********************************************************************************************************************************/
21#define LOG_GROUP LOG_GROUP_GIM
22#include <VBox/vmm/gcm.h>
23#include <VBox/vmm/em.h> /* For EMInterpretDisasCurrent */
24#include "GCMInternal.h"
25#include <VBox/vmm/vmcc.h>
26
27#include <VBox/dis.h> /* For DISCPUSTATE */
28#include <iprt/errcore.h>
29#include <iprt/string.h>
30
31
32/**
33 * Checks whether GCM is enabled for this VM.
34 *
35 * @retval true if GCM is on.
36 * @retval false if no GCM fixer is enabled.
37 *
38 * @param pVM The cross context VM structure.
39 */
40VMMDECL(bool) GCMIsEnabled(PVM pVM)
41{
42 return pVM->gcm.s.enmFixerIds != GCMFIXER_NONE;
43}
44
45
46/**
47 * Gets the GCM fixers configured for this VM.
48 *
49 * @returns The GCM provider Id.
50 * @param pVM The cross context VM structure.
51 */
52VMMDECL(int32_t) GCMGetFixers(PVM pVM)
53{
54 return pVM->gcm.s.enmFixerIds;
55}
56
57
58/**
59 * Whether \#DE exceptions in the guest should be intercepted by GCM and
60 * possibly fixed up.
61 *
62 * @returns true if needed, false otherwise.
63 * @param pVCpu The cross context virtual CPU structure.
64 */
65VMM_INT_DECL(bool) GCMShouldTrapXcptDE(PVMCPUCC pVCpu)
66{
67 LogFlowFunc(("entered\n"));
68 PVM pVM = pVCpu->CTX_SUFF(pVM);
69 if (!GCMIsEnabled(pVM))
70 return false;
71
72 LogFunc(("GCM checking if #DE needs trapping\n"));
73
74 /* See if the enabled fixers need to intercept #DE. */
75 if ( pVM->gcm.s.enmFixerIds
76 & (GCMFIXER_DBZ_DOS | GCMFIXER_DBZ_OS2 | GCMFIXER_DBZ_WIN9X))
77 {
78 LogRel(("GCM: #DE should be trapped\n"));
79 return true;
80 }
81
82 return false;
83}
84
85
86/**
87 * Exception handler for \#DE when registered by GCM.
88 *
89 * @returns Strict VBox status code.
90 * @retval VINF_SUCCESS retry division and continue.
91 * @retval VERR_NOT_FOUND deliver exception to guest.
92 *
93 * @param pVCpu The cross context virtual CPU structure.
94 * @param pCtx Pointer to the guest-CPU context.
95 * @param pDis Pointer to the disassembled instruction state at RIP.
96 * If NULL is passed, it implies the disassembly of the
97 * the instruction at RIP is the
98 * responsibility of GCM.
99 * @param pcbInstr Where to store the instruction length of
100 * the divide instruction. Optional, can be
101 * NULL.
102 *
103 * @thread EMT(pVCpu).
104 */
105VMM_INT_DECL(VBOXSTRICTRC) GCMXcptDE(PVMCPUCC pVCpu, PCPUMCTX pCtx, PDISCPUSTATE pDis, uint8_t *pcbInstr)
106{
107 PVMCC pVM = pVCpu->CTX_SUFF(pVM);
108 Assert(GCMIsEnabled(pVM));
109 Assert(pDis || pcbInstr);
110 RT_NOREF(pDis);
111 RT_NOREF(pcbInstr);
112
113 LogRel(("GCM: Intercepted #DE at CS:RIP=%04x:%RX64 (%RX64 linear) RDX:RAX=%RX64:%RX64 RCX=%RX64 RBX=%RX64\n",
114 pCtx->cs.Sel, pCtx->rip, pCtx->cs.u64Base + pCtx->rip, pCtx->rdx, pCtx->rax, pCtx->rcx, pCtx->rbx));
115
116 if (pVM->gcm.s.enmFixerIds & GCMFIXER_DBZ_OS2)
117 {
118 if (pCtx->rcx == 0 && pCtx->rdx == 1 && pCtx->rax == 0x86a0)
119 {
120 /* OS/2 1.x drivers loaded during boot: DX:AX = 100,000, CX < 2 causes overflow. */
121 /* Example: OS/2 1.0 KBD01.SYS, 16,945 bytes, dated 10/21/1987, div cx at offset 2:2ffeh */
122 /* Code later merged into BASEDD01.SYS, crash fixed in OS/2 1.30.1; this should
123 * fix all affected versions of OS/2 1.x.
124 */
125 pCtx->rcx = 2;
126 return VINF_SUCCESS;
127 }
128 if ((uint16_t)pCtx->rbx == 0 && (uint16_t)pCtx->rdx == 0 && (uint16_t)pCtx->rax == 0x1000)
129 {
130 /* OS/2 2.1 and later boot loader: DX:AX = 0x1000, zero BX. May have junk in high words of all registers. */
131 /* Example: OS/2 MCP2 OS2LDR, 44,544 bytes, dated 03/08/2002, idiv bx at offset 847ah */
132 pCtx->rbx = (pCtx->rbx & ~0xffff) | 2;
133 return VINF_SUCCESS;
134 }
135 if (pCtx->rbx == 0 && pCtx->rdx == 0 && pCtx->rax == 0x100)
136 {
137 /* OS/2 2.0 boot loader: DX:AX = 0x100, zero BX. May have junk in high words of registers. */
138 /* Example: OS/2 2.0 OS2LDR, 32,256 bytes, dated 03/30/1992, idiv bx at offset 2298h */
139 pCtx->rbx = 2;
140 return VINF_SUCCESS;
141 }
142 }
143
144 if (pVM->gcm.s.enmFixerIds & GCMFIXER_DBZ_DOS)
145 {
146 /* NB: For 16-bit DOS software, we must generally only compare 16-bit registers.
147 * The contents of the high words may be unpredictable depending on the environment.
148 * For 32-bit Windows 3.x code that is not the case.
149 */
150 if (pCtx->rcx == 0 && pCtx->rdx == 0 && pCtx->rax == 0x100000)
151 {
152 /* NDIS.386 in WfW 3.11: CalibrateStall, EDX:EAX = 0x100000, zero ECX.
153 * Occurs when NDIS.386 loads.
154 */
155 pCtx->rcx = 0x20000; /* Want a large divisor to shorten stalls. */
156 return VINF_SUCCESS;
157 }
158 if (pCtx->rcx == 0 && pCtx->rdx == 0 && pCtx->rax > 0x100000)
159 {
160 /* NDIS.386 in WfW 3.11: NdisStallExecution, EDX:EAX = 0xYY00000, zero ECX.
161 * EDX:EAX is variable, but low 20 bits of EAX must be zero and EDX is likely
162 * to be zero as well.
163 * Only occurs if NdisStallExecution is called to do a longish stall.
164 */
165 pCtx->rcx = 22;
166 return VINF_SUCCESS;
167 }
168 if ((uint16_t)pCtx->rbx == 0 && (uint16_t)pCtx->rdx == 0 && (uint16_t)pCtx->rax == 0x64)
169 {
170 /* Norton Sysinfo or Diagnostics 8.0 DX:AX = 0x64 (100 decimal), zero BX. */
171 pCtx->rbx = (pCtx->rbx & 0xffff0000) | 1; /* BX = 1 */
172 return VINF_SUCCESS;
173 }
174 if ((uint16_t)pCtx->rbx == 0 && (uint16_t)pCtx->rdx == 0 && (uint16_t)pCtx->rax == 0xff)
175 {
176 /* IBM PC LAN Program 1.3: DX:AX=0xff (255 decimal), zero BX. */
177 /* NETWORK1.CMD, 64,324 bytes, dated 06/06/1988, div bx at offset 0xa400 in file. */
178 pCtx->rbx = (pCtx->rbx & 0xffff0000) | 1; /* BX = 1 */
179 return VINF_SUCCESS;
180 }
181 if ((uint16_t)pCtx->rdx == 0xffff && (uint16_t)pCtx->rax == 0xffff && (uint16_t)pCtx->rcx == 0xa8c0)
182 {
183 /* QNX 2.15C: DX:AX=0xffffffff (-1), constant CX = 0xa8c0 (43200). */
184 /* div cx at e.g. 2220:fa5 and 2220:10a0 in memory. */
185 pCtx->rdx = (pCtx->rdx & 0xffff0000) | 8; /* DX = 8 */
186 return VINF_SUCCESS;
187 }
188 if ((uint16_t)pCtx->rax > 0x1800 && ((uint16_t)pCtx->rax & 0x3f) == 0 && (uint16_t)pCtx->rbx == 0x19)
189 {
190 /* 3C501.COM ODI driver v1.21: AX > ~0x1900 (-1), BX = 0x19 (25). */
191 /* AX was shifted left by 6 bits so low bits must be zero. */
192 /* div bl at e.g. 06b3:2f80 and offset 0x2E80 in file. */
193 pCtx->rax = (pCtx->rax & 0xffff0000) | 0x8c0; /* AX = 0x8c0 */
194 return VINF_SUCCESS;
195 }
196 if ((uint16_t)pCtx->rcx == 0x37 && ((uint16_t)pCtx->rdx > 0x34))
197 {
198 /* Turbo Pascal, classic Runtime Error 200: CX = 55, DX > ~54, AX/BX variable. */
199 /* div cx at variable offset in file. */
200 pCtx->rdx = (pCtx->rdx & 0xffff0000) | 0x30; /* DX = 48 */
201 return VINF_SUCCESS;
202 }
203 }
204
205 if (pVM->gcm.s.enmFixerIds & GCMFIXER_DBZ_WIN9X)
206 {
207 if (pCtx->rcx == 0 && pCtx->rdx == 0 && pCtx->rax == 0x100000)
208 {
209 /* NDIS.VXD in Win9x: EDX:EAX = 0x100000, zero ECX. */
210 /* Example: Windows 95 NDIS.VXD, 99,084 bytes, dated 07/11/1994, div ecx at 28:Cxxxx80B */
211 /* Crash fixed in Windows 98 SE. */
212 pCtx->rcx = 0x20000; /* Want a large divisor to shorten stalls. */
213 return VINF_SUCCESS;
214 }
215 if (pCtx->rcx < 3 && pCtx->rdx == 2 && pCtx->rax == 0x540be400)
216 {
217 /* SCSI.PDR, ESDI506.PDR in Win95: EDX:EAX = 0x2540be400 (10,000,000,000 decimal), ECX < 3. */
218 /* Example: Windows 95 SCSIPORT.PDR, 23,133 bytes, dated 07/11/1995, div ecx at 28:Cxxxx876 */
219 /* Example: Win95 OSR2 ESDI506.PDR, 24,390 bytes, dated 04/24/1996, div ecx at 28:Cxxxx8E3 */
220 /* Crash fixed in Windows 98. */
221 pCtx->rcx = 1000;
222 return VINF_SUCCESS;
223 }
224 if (pCtx->rcx == 0 && pCtx->rdx == 0x3d && pCtx->rax == 0x9000000)
225 {
226 /* Unknown source, Win9x shutdown, div ecx. */
227 /* GCM: Intercepted #DE at CS:RIP=0028:c0050f8e RDX:RAX=3d:9000000 (250000*1024*1024) RCX=0 RBX=c19200e8 [RBX variable] */
228 pCtx->rcx = 4096;
229 return VINF_SUCCESS;
230 }
231 }
232
233 /* If we got this far, deliver exception to guest. */
234 return VERR_NOT_FOUND;
235}
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