VirtualBox

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

Last change on this file was 106061, checked in by vboxsync, 2 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.0 KB
Line 
1/** @file
2 * GCM - Guest Compatibility Manager - All Contexts.
3 */
4
5/*
6 * Copyright (C) 2022-2024 Oracle and/or its affiliates.
7 *
8 * This file is part of VirtualBox base platform packages, as
9 * available from https://www.virtualbox.org.
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation, in version 3 of the
14 * License.
15 *
16 * This program is distributed in the hope that it will be useful, but
17 * WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, see <https://www.gnu.org/licenses>.
23 *
24 * SPDX-License-Identifier: GPL-3.0-only
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#define LOG_GROUP LOG_GROUP_GCM
32#include <VBox/vmm/gcm.h>
33#include "GCMInternal.h"
34#include <VBox/vmm/vmcc.h>
35
36#include <VBox/dis.h> /* For DISSTATE */
37#include <VBox/err.h>
38#include <iprt/string.h>
39
40
41/*********************************************************************************************************************************
42* Defined Constants And Macros *
43*********************************************************************************************************************************/
44#define VMWARE_HYPERVISOR_PORT UINT16_C(0x5658)
45#define VMWARE_HYPERVISOR_PORT_HB UINT16_C(0x5659)
46#define VMWARE_HYPERVISOR_MAGIC UINT32_C(0x564d5868) /**< eax value */
47
48#define VMWARE_HYPERVISOR_CMD_MSG 0x001e
49#define VMWARE_HYPERVISOR_CMD_HB_MSG 0x0000
50#define VMWARE_HYPERVISOR_CMD_OPEN_CHANNEL RT_MAKE_U32(VMWARE_HYPERVISOR_CMD_MSG, 0) /**< ecx */
51#define VMWARE_OC_RPCI_PROTOCOL_NUM UINT32_C(0x49435052) /**< VMWARE_HYPERVISOR_CMD_OPEN_CHANNEL: ebx[30:0] */
52#define VMWARE_OC_GUESTMSG_FLAG_COOKIE UINT32_C(0x80000000) /**< VMWARE_HYPERVISOR_CMD_OPEN_CHANNEL: ebx bit 31 */
53#define VMWARE_HYPERVISOR_CMD_SEND_SIZE RT_MAKE_U32(VMWARE_HYPERVISOR_CMD_MSG, 1) /**< ecx */
54#define VMWARE_HYPERVISOR_CMD_SEND_PAYLOAD RT_MAKE_U32(VMWARE_HYPERVISOR_CMD_MSG, 2) /**< ecx */
55#define VMWARE_HYPERVISOR_CMD_RECV_SIZE RT_MAKE_U32(VMWARE_HYPERVISOR_CMD_MSG, 3) /**< ecx */
56#define VMWARE_HYPERVISOR_CMD_RECV_PAYLOAD RT_MAKE_U32(VMWARE_HYPERVISOR_CMD_MSG, 4) /**< ecx */
57#define VMWARE_HYPERVISOR_CMD_RECV_STATUS RT_MAKE_U32(VMWARE_HYPERVISOR_CMD_MSG, 5) /**< ecx */
58#define VMWARE_HYPERVISOR_CMD_CLOSE_CHANNEL RT_MAKE_U32(VMWARE_HYPERVISOR_CMD_MSG, 6) /**< ecx */
59
60#define VMWARE_HYPERVISOR_CMD_MKS_GUEST_STATS 0x0055
61#define VMWARE_HYPERVISOR_CMD_MKSGS_RESET RT_MAKE_U32(VMWARE_HYPERVISOR_CMD_MKS_GUEST_STATS, 0) /**< ecx */
62#define VMWARE_HYPERVISOR_CMD_MKSGS_ADD_PPN RT_MAKE_U32(VMWARE_HYPERVISOR_CMD_MKS_GUEST_STATS, 1) /**< ecx */
63#define VMWARE_HYPERVISOR_CMD_MKSGS_REMOVE_PPN RT_MAKE_U32(VMWARE_HYPERVISOR_CMD_MKS_GUEST_STATS, 2) /**< ecx */
64
65/** @name Message status return flags (ecx).
66 * @{ */
67#define VMWARE_MSG_STATUS_F_SUCCESS UINT32_C(0x00010000)
68#define VMWARE_MSG_STATUS_F_DO_RECV UINT32_C(0x00020000)
69#define VMWARE_MSG_STATUS_F_CPT UINT32_C(0x00100000)
70#define VMWARE_MSG_STATUS_F_HB UINT32_C(0x00800000)
71/** @} */
72
73
74
75/**
76 * Whether \#DE exceptions in the guest should be intercepted by GCM and
77 * possibly fixed up.
78 *
79 * @returns true if needed, false otherwise.
80 * @param pVCpu The cross context virtual CPU structure.
81 */
82VMM_INT_DECL(bool) GCMIsInterceptingXcptDE(PVMCPUCC pVCpu)
83{
84 /* See if the enabled fixers need to intercept #DE. */
85 PVM const pVM = pVCpu->CTX_SUFF(pVM);
86 bool const fRet = (pVM->gcm.s.fFixerSet & (GCMFIXER_DBZ_DOS | GCMFIXER_DBZ_OS2 | GCMFIXER_DBZ_WIN9X)) != 0;
87 LogFlow(("GCMIsInterceptingXcptDE: returns %d\n", fRet));
88 return fRet;
89}
90
91
92/**
93 * Exception handler for \#DE when registered by GCM.
94 *
95 * @returns VBox status code.
96 * @retval VINF_SUCCESS retry division and continue.
97 * @retval VERR_NOT_FOUND deliver exception to guest.
98 *
99 * @param pVCpu The cross context virtual CPU structure.
100 * @param pCtx Pointer to the guest-CPU context.
101 *
102 * @thread EMT(pVCpu).
103 */
104VMM_INT_DECL(int) GCMXcptDE(PVMCPUCC pVCpu, PCPUMCTX pCtx)
105{
106 PVMCC pVM = pVCpu->CTX_SUFF(pVM);
107 Assert(pVM->gcm.s.fFixerSet & (GCMFIXER_DBZ_DOS | GCMFIXER_DBZ_OS2 | GCMFIXER_DBZ_WIN9X));
108
109 LogRel(("GCM: Intercepted #DE at CS:RIP=%04x:%RX64 (%RX64 linear) RDX:RAX=%RX64:%RX64 RCX=%RX64 RBX=%RX64\n",
110 pCtx->cs.Sel, pCtx->rip, pCtx->cs.u64Base + pCtx->rip, pCtx->rdx, pCtx->rax, pCtx->rcx, pCtx->rbx));
111
112 if (pVM->gcm.s.fFixerSet & GCMFIXER_DBZ_OS2)
113 {
114 if (pCtx->rcx == 0 && pCtx->rdx == 1 && pCtx->rax == 0x86a0)
115 {
116 /* OS/2 1.x drivers loaded during boot: DX:AX = 100,000, CX < 2 causes overflow. */
117 /* Example: OS/2 1.0 KBD01.SYS, 16,945 bytes, dated 10/21/1987, div cx at offset 2:2ffeh */
118 /* Code later merged into BASEDD01.SYS, crash fixed in OS/2 1.30.1; this should
119 * fix all affected versions of OS/2 1.x.
120 */
121 pCtx->rcx = 2;
122 return VINF_SUCCESS;
123 }
124 if ((uint16_t)pCtx->rbx == 0 && (uint16_t)pCtx->rdx == 0 && (uint16_t)pCtx->rax == 0x1000)
125 {
126 /* OS/2 2.1 and later boot loader: DX:AX = 0x1000, zero BX. May have junk in high words of all registers. */
127 /* Example: OS/2 MCP2 OS2LDR, 44,544 bytes, dated 03/08/2002, idiv bx at offset 847ah */
128 pCtx->rbx = (pCtx->rbx & ~0xffff) | 2;
129 return VINF_SUCCESS;
130 }
131 if (pCtx->rbx == 0 && pCtx->rdx == 0 && pCtx->rax == 0x100)
132 {
133 /* OS/2 2.0 boot loader: DX:AX = 0x100, zero BX. May have junk in high words of registers. */
134 /* Example: OS/2 2.0 OS2LDR, 32,256 bytes, dated 03/30/1992, idiv bx at offset 2298h */
135 pCtx->rbx = 2;
136 return VINF_SUCCESS;
137 }
138 }
139
140 if (pVM->gcm.s.fFixerSet & GCMFIXER_DBZ_DOS)
141 {
142 /* NB: For 16-bit DOS software, we must generally only compare 16-bit registers.
143 * The contents of the high words may be unpredictable depending on the environment.
144 * For 32-bit Windows 3.x code that is not the case.
145 */
146 if (pCtx->rcx == 0 && pCtx->rdx == 0 && pCtx->rax == 0x100000)
147 {
148 /* NDIS.386 in WfW 3.11: CalibrateStall, EDX:EAX = 0x100000, zero ECX.
149 * Occurs when NDIS.386 loads.
150 */
151 pCtx->rcx = 0x20000; /* Want a large divisor to shorten stalls. */
152 return VINF_SUCCESS;
153 }
154 if (pCtx->rcx == 0 && pCtx->rdx == 0 && pCtx->rax > 0x100000)
155 {
156 /* NDIS.386 in WfW 3.11: NdisStallExecution, EDX:EAX = 0xYY00000, zero ECX.
157 * EDX:EAX is variable, but low 20 bits of EAX must be zero and EDX is likely
158 * to be zero as well.
159 * Only occurs if NdisStallExecution is called to do a longish stall.
160 */
161 pCtx->rcx = 22;
162 return VINF_SUCCESS;
163 }
164 if ((uint16_t)pCtx->rbx == 0 && (uint16_t)pCtx->rdx == 0 && (uint16_t)pCtx->rax == 0x64)
165 {
166 /* Norton Sysinfo or Diagnostics 8.0 DX:AX = 0x64 (100 decimal), zero BX. */
167 pCtx->rbx = (pCtx->rbx & 0xffff0000) | 1; /* BX = 1 */
168 return VINF_SUCCESS;
169 }
170 if ((uint16_t)pCtx->rbx == 0 && (uint16_t)pCtx->rdx == 0 && (uint16_t)pCtx->rax == 0xff)
171 {
172 /* IBM PC LAN Program 1.3: DX:AX=0xff (255 decimal), zero BX. */
173 /* NETWORK1.CMD, 64,324 bytes, dated 06/06/1988, div bx at offset 0xa400 in file. */
174 pCtx->rbx = (pCtx->rbx & 0xffff0000) | 1; /* BX = 1 */
175 return VINF_SUCCESS;
176 }
177 if ((uint16_t)pCtx->rdx == 0xffff && (uint16_t)pCtx->rax == 0xffff && (uint16_t)pCtx->rcx == 0xa8c0)
178 {
179 /* QNX 2.15C: DX:AX=0xffffffff (-1), constant CX = 0xa8c0 (43200). */
180 /* div cx at e.g. 2220:fa5 and 2220:10a0 in memory. */
181 pCtx->rdx = (pCtx->rdx & 0xffff0000) | 8; /* DX = 8 */
182 return VINF_SUCCESS;
183 }
184 if ((uint16_t)pCtx->rax > 0x1800 && ((uint16_t)pCtx->rax & 0x3f) == 0 && (uint16_t)pCtx->rbx == 0x19)
185 {
186 /* 3C501.COM ODI driver v1.21: AX > ~0x1900 (-1), BX = 0x19 (25). */
187 /* AX was shifted left by 6 bits so low bits must be zero. */
188 /* div bl at e.g. 06b3:2f80 and offset 0x2E80 in file. */
189 pCtx->rax = (pCtx->rax & 0xffff0000) | 0x8c0; /* AX = 0x8c0 */
190 return VINF_SUCCESS;
191 }
192 if ((uint16_t)pCtx->rcx == 0x37 && ((uint16_t)pCtx->rdx > 0x34))
193 {
194 /* Turbo Pascal, classic Runtime Error 200: CX = 55, DX > ~54, AX/BX variable. */
195 /* div cx at variable offset in file. */
196 pCtx->rdx = (pCtx->rdx & 0xffff0000) | 0x30; /* DX = 48 */
197 return VINF_SUCCESS;
198 }
199 }
200
201 if (pVM->gcm.s.fFixerSet & GCMFIXER_DBZ_WIN9X)
202 {
203 if (pCtx->rcx == 0 && pCtx->rdx == 0 && pCtx->rax == 0x100000)
204 {
205 /* NDIS.VXD in Win9x: EDX:EAX = 0x100000, zero ECX. */
206 /* Example: Windows 95 NDIS.VXD, 99,084 bytes, dated 07/11/1994, div ecx at 28:Cxxxx80B */
207 /* Crash fixed in Windows 98 SE. */
208 pCtx->rcx = 0x20000; /* Want a large divisor to shorten stalls. */
209 return VINF_SUCCESS;
210 }
211 if (pCtx->rcx < 3 && pCtx->rdx == 2 && pCtx->rax == 0x540be400)
212 {
213 /* SCSI.PDR, ESDI506.PDR in Win95: EDX:EAX = 0x2540be400 (10,000,000,000 decimal), ECX < 3. */
214 /* Example: Windows 95 SCSIPORT.PDR, 23,133 bytes, dated 07/11/1995, div ecx at 28:Cxxxx876 */
215 /* Example: Win95 OSR2 ESDI506.PDR, 24,390 bytes, dated 04/24/1996, div ecx at 28:Cxxxx8E3 */
216 /* Crash fixed in Windows 98. */
217 pCtx->rcx = 1000;
218 return VINF_SUCCESS;
219 }
220 if (pCtx->rcx == 0 && pCtx->rdx == 0x3d && pCtx->rax == 0x9000000)
221 {
222 /* Unknown source, Win9x shutdown, div ecx. */
223 /* GCM: Intercepted #DE at CS:RIP=0028:c0050f8e RDX:RAX=3d:9000000 (250000*1024*1024) RCX=0 RBX=c19200e8 [RBX variable] */
224 pCtx->rcx = 4096;
225 return VINF_SUCCESS;
226 }
227 }
228
229 /* If we got this far, deliver exception to guest. */
230 return VERR_NOT_FOUND;
231}
232
233
234#if 0
235/**
236 * Whether \#GP exceptions in the guest should be intercepted by GCM and
237 * possibly fixed up.
238 *
239 * @returns true if needed, false otherwise.
240 * @param pVCpu The cross context virtual CPU structure.
241 */
242VMM_INT_DECL(bool) GCMIsInterceptingXcptGP(PVMCPUCC pVCpu)
243{
244 /* See if the enabled fixers require #GP interception. */
245 PVM const pVM = pVCpu->CTX_SUFF(pVM);
246 bool const fRet = (pVM->gcm.s.fFixerSet & GCMFIXER_MESA_VMSVGA_DRV) != 0;
247 LogFlow(("GCMIsInterceptingXcptDE: returns %d\n", fRet));
248 return fRet;
249}
250
251
252/**
253 * Exception handler for \#GP when registered by GCM.
254 *
255 * @returns VBox status code.
256 * @retval VINF_SUCCESS retry the instruction and continue.
257 * @retval VERR_NOT_FOUND deliver exception to guest.
258 *
259 * @param pVCpu The cross context virtual CPU structure.
260 * @param pCtx Pointer to the guest-CPU context.
261 *
262 * @thread EMT(pVCpu).
263 */
264VMM_INT_DECL(int) GCMXcptGP(PVMCPUCC pVCpu, PCPUMCTX pCtx)
265{
266
267}
268#endif
269
270
271/**
272 * Checks if I/O port reads for the given port need GCM attention.
273 *
274 * @returns true if needed, false otherwise.
275 * @param pVCpu The cross context virtual CPU structure.
276 * @param u16Port The port being accessed. UINT16_MAX and cbReg ==
277 * UINT8_MAX for any port.
278 * @param cbReg The access size. UINT8_MAX and u16Port == UINT16_MAX for
279 * any size.
280 */
281VMM_INT_DECL(bool) GCMIsInterceptingIOPortReadSlow(PVMCPUCC pVCpu, uint16_t u16Port, uint8_t cbReg)
282{
283 PVM const pVM = pVCpu->CTX_SUFF(pVM);
284 bool const fRet = (pVM->gcm.s.fFixerSet & GCMFIXER_MESA_VMSVGA_DRV) != 0
285 && ( (u16Port == VMWARE_HYPERVISOR_PORT && cbReg == 4)
286 || (u16Port == UINT16_MAX && cbReg == UINT8_MAX) );
287 LogFlow(("GCMIsInterceptingIOPortReadSlow(,%#x,%#x): returns %d\n", u16Port, cbReg, fRet));
288 return fRet;
289}
290
291
292/**
293 * Processes an intercepted IO port read instruction.
294 *
295 * @returns Strict VBox status code. Only two informational status codes
296 * are returned VINF_GCM_HANDLED and VINF_GCM_HANDLED_ADVANCE_RIP.
297 * @retval VINF_GCM_HANDLED
298 * @retval VINF_GCM_HANDLED_ADVANCE_RIP
299 * @retval VERR_GCM_NOT_HANDLED
300 * @param pVCpu The cross context virtual CPU structure.
301 * @param pCtx The CPU context.
302 * @param u16Port The port being accessed.
303 * @param cbReg The size of the access.
304 */
305VMM_INT_DECL(VBOXSTRICTRC) GCMInterceptedIOPortRead(PVMCPUCC pVCpu, PCPUMCTX pCtx, uint16_t u16Port, uint8_t cbReg)
306{
307 Assert((pVCpu->CTX_SUFF(pVM)->gcm.s.fFixerSet & GCMFIXER_MESA_VMSVGA_DRV) != 0);
308 if (u16Port == VMWARE_HYPERVISOR_PORT && cbReg == 4)
309 {
310 CPUM_IMPORT_EXTRN_WITH_CTX_RET(pVCpu, pCtx,
311 CPUMCTX_EXTRN_RAX | CPUMCTX_EXTRN_RCX | CPUMCTX_EXTRN_RDX
312 | CPUMCTX_EXTRN_RBX | CPUMCTX_EXTRN_RSI | CPUMCTX_EXTRN_RDI);
313 if (pCtx->rax == VMWARE_HYPERVISOR_MAGIC)
314 {
315 switch (pCtx->rcx)
316 {
317 case VMWARE_HYPERVISOR_CMD_OPEN_CHANNEL:
318 Log(("GCMInterceptedIOPortRead: vmware open channel: protocol=%#RX64\n", pCtx->rbx));
319 break;
320 case VMWARE_HYPERVISOR_CMD_SEND_SIZE:
321 Log(("GCMInterceptedIOPortRead: vmware send size\n"));
322 break;
323 case VMWARE_HYPERVISOR_CMD_SEND_PAYLOAD:
324 Log(("GCMInterceptedIOPortRead: vmware send payload\n"));
325 break;
326 case VMWARE_HYPERVISOR_CMD_RECV_SIZE:
327 Log(("GCMInterceptedIOPortRead: vmware recv size\n"));
328 break;
329 case VMWARE_HYPERVISOR_CMD_RECV_PAYLOAD:
330 Log(("GCMInterceptedIOPortRead: vmware recv payload\n"));
331 break;
332 case VMWARE_HYPERVISOR_CMD_RECV_STATUS:
333 Log(("GCMInterceptedIOPortRead: vmware recv status\n"));
334 break;
335 case VMWARE_HYPERVISOR_CMD_CLOSE_CHANNEL:
336 Log(("GCMInterceptedIOPortRead: vmware close channel\n"));
337 break;
338
339 case VMWARE_HYPERVISOR_CMD_MKSGS_RESET:
340 Log(("GCMInterceptedIOPortRead: vmware mks guest stats reset\n"));
341 break;
342 case VMWARE_HYPERVISOR_CMD_MKSGS_ADD_PPN:
343 Log(("GCMInterceptedIOPortRead: vmware mks guest stats add ppn\n"));
344 break;
345 case VMWARE_HYPERVISOR_CMD_MKSGS_REMOVE_PPN:
346 Log(("GCMInterceptedIOPortRead: vmware mks guest stats remove ppn\n"));
347 break;
348
349 default:
350 LogRelMax(64, ("GCMInterceptedIOPortRead: Unknown vmware hypervisor call: rcx=%#RX64 (cmd), rbx=%#RX64 (len/whatever), rsi=%#RX64 (input), rdi=%#RX64 (input), rdx=%#RX64 (flags + chid) at %04x:%08RX64\n",
351 pCtx->rcx, pCtx->rbx, pCtx->rsi, pCtx->rdi, pCtx->rdx, pCtx->cs.Sel, pCtx->rip));
352 return VERR_GCM_NOT_HANDLED;
353 }
354
355 /* Just fail the command. */
356 pCtx->ecx &= ~VMWARE_MSG_STATUS_F_SUCCESS;
357 return VINF_GCM_HANDLED_ADVANCE_RIP;
358 }
359 }
360 return VERR_GCM_NOT_HANDLED;
361}
362
363
364#if 0 /* If we need to deal with high speed vmware hypervisor calls */
365
366/**
367 * Checks if I/O port string reads for the given port need GCM attention.
368 *
369 * @returns true if needed, false otherwise.
370 * @param pVCpu The cross context virtual CPU structure.
371 * @param u16Port The port being accessed. UINT16_MAX and cbReg ==
372 * UINT8_MAX for any port.
373 * @param cbReg The access size. UINT8_MAX and u16Port == UINT16_MAX for
374 * any size.
375 */
376VMM_INT_DECL(bool) GCMIsInterceptingIOPortReadStringSlow(PVMCPUCC pVCpu, uint16_t u16Port, uint8_t cbReg)
377{
378 PVM const pVM = pVCpu->CTX_SUFF(pVM);
379 bool const fRet = (pVM->gcm.s.fFixerSet & GCMFIXER_MESA_VMSVGA_DRV) != 0
380 && ( (u16Port == VMWARE_HYPERVISOR_PORT_HB && cbReg == 1)
381 || (u16Port == UINT16_MAX && cbReg == UINT8_MAX) );
382 LogFlow(("GCMIsInterceptingIOPortReadStringSlow(,%#x,%#x): returns %d\n", u16Port, cbReg, fRet));
383 return fRet;
384}
385
386
387/**
388 * Checks if I/O port string writes for the given port need GCM attention.
389 *
390 * @returns true if needed, false otherwise.
391 * @param pVCpu The cross context virtual CPU structure.
392 * @param u16Port The port being accessed. UINT16_MAX and cbReg ==
393 * UINT8_MAX for any port.
394 * @param cbReg The access size. UINT8_MAX and u16Port == UINT16_MAX for
395 * any size.
396 */
397VMM_INT_DECL(bool) GCMIsInterceptingIOPortWriteStringSlow(PVMCPUCC pVCpu, uint16_t u16Port, uint8_t cbReg)
398{
399 PVM const pVM = pVCpu->CTX_SUFF(pVM);
400 bool const fRet = (pVM->gcm.s.fFixerSet & GCMFIXER_MESA_VMSVGA_DRV) != 0
401 && ( (u16Port == VMWARE_HYPERVISOR_PORT_HB && cbReg == 1)
402 || (u16Port == UINT16_MAX && cbReg == UINT8_MAX) );
403 LogFlow(("GCMIsInterceptingIOPortWriteStringSlow(,%#x,%#x): returns %d\n", u16Port, cbReg, fRet));
404 return fRet;
405}
406
407#endif
408
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