VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMR3/GCM.cpp@ 105379

Last change on this file since 105379 was 104516, checked in by vboxsync, 7 months ago

VMM/GCM,IEM,HM: Integrate GCM with IEM, extending it to cover the mesa drv situation and valid ring-0 IN instructions to same port. Untested. TODO: NEM. bugref:9735 bugref:10683

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 8.8 KB
Line 
1/** @file
2 * GCM - Guest Compatibility Manager.
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/** @page pg_gcm GCM - The Guest Compatibility Manager
28 *
29 * The Guest Compatibility Manager provides run-time compatibility fixes for
30 * certain known guest bugs.
31 *
32 * @see grp_gcm
33 *
34 *
35 * @section sec_gcm_fixer Fixers
36 *
37 * A GCM fixer implements a collection of run-time helpers/patches suitable for
38 * a specific guest type. Several fixers can be active at the same time; for
39 * example OS/2 or Windows 9x need their own fixers, but can also runs DOS
40 * applications which need DOS-specific fixers.
41 *
42 * The concept of fixers exists to reduce the number of false positives to a
43 * minimum. Heuristics are used to decide whether a particular fix should be
44 * applied or not; restricting the number of applicable fixes minimizes the
45 * chance that a fix could be misapplied.
46 *
47 * The fixers are invisible to a guest. It is not expected that the set of
48 * active fixers would be changed during the lifetime of the VM.
49 *
50 *
51 * @subsection sec_gcm_fixer_div_by_zero Division By Zero
52 *
53 * A common problem is division by zero caused by a software timing loop which
54 * cannot deal with fast CPUs (where "fast" very much depends on the era when
55 * the software was written). A fixer intercepts division by zero, recognizes
56 * known register contents and code sequence, modifies one or more registers to
57 * avoid a divide error, and restarts the instruction.
58 *
59 */
60
61
62/*********************************************************************************************************************************
63* Header Files *
64*********************************************************************************************************************************/
65#define LOG_GROUP LOG_GROUP_GCM
66#include <VBox/vmm/gcm.h>
67#include <VBox/vmm/ssm.h>
68#include "GCMInternal.h"
69#include <VBox/vmm/vm.h>
70
71#include <VBox/log.h>
72#include <VBox/err.h>
73
74#include <iprt/string.h>
75
76
77/*********************************************************************************************************************************
78* Internal Functions *
79*********************************************************************************************************************************/
80static FNSSMINTSAVEEXEC gcmR3Save;
81static FNSSMINTLOADEXEC gcmR3Load;
82
83
84/**
85 * Initializes the GCM.
86 *
87 * @returns VBox status code.
88 * @param pVM The cross context VM structure.
89 */
90VMMR3_INT_DECL(int) GCMR3Init(PVM pVM)
91{
92 LogFlow(("GCMR3Init\n"));
93
94 /*
95 * Assert alignment and sizes.
96 */
97 AssertCompile(sizeof(pVM->gcm.s) <= sizeof(pVM->gcm.padding));
98
99 /*
100 * Register the saved state data unit.
101 */
102 int rc = SSMR3RegisterInternal(pVM, "GCM", 0 /* uInstance */, GCM_SAVED_STATE_VERSION, sizeof(GCM),
103 NULL /* pfnLivePrep */, NULL /* pfnLiveExec */, NULL /* pfnLiveVote*/,
104 NULL /* pfnSavePrep */, gcmR3Save, NULL /* pfnSaveDone */,
105 NULL /* pfnLoadPrep */, gcmR3Load, NULL /* pfnLoadDone */);
106 if (RT_FAILURE(rc))
107 return rc;
108
109 /*
110 * Read & validate configuration.
111 */
112 static struct { const char *pszName; uint32_t cchName; uint32_t fFlag; } const s_aFixerIds[] =
113 {
114 { RT_STR_TUPLE("DivByZeroDOS"), GCMFIXER_DBZ_DOS },
115 { RT_STR_TUPLE("DivByZeroOS2"), GCMFIXER_DBZ_OS2 },
116 { RT_STR_TUPLE("DivByZeroWin9x"), GCMFIXER_DBZ_WIN9X },
117 { RT_STR_TUPLE("MesaVmsvgaDrv"), GCMFIXER_MESA_VMSVGA_DRV },
118 };
119
120 /* Assemble valid value names for CFMGR3ValidateConfig. */
121 char szValidValues[1024];
122 size_t offValidValues = 0;
123 for (unsigned i = 0; i < RT_ELEMENTS(s_aFixerIds); i++)
124 {
125 AssertReturn(offValidValues + s_aFixerIds[i].cchName + 2 <= sizeof(szValidValues), VERR_INTERNAL_ERROR_2);
126 if (offValidValues)
127 szValidValues[offValidValues++] = '|';
128 memcpy(&szValidValues[offValidValues], s_aFixerIds[i].pszName, s_aFixerIds[i].cchName);
129 offValidValues += s_aFixerIds[i].cchName;
130 }
131 szValidValues[offValidValues] = '\0';
132
133 /* Validate the configuration. */
134 PCFGMNODE pCfgNode = CFGMR3GetChild(CFGMR3GetRoot(pVM), "GCM/");
135 rc = CFGMR3ValidateConfig(pCfgNode,
136 "/GCM/", /* pszNode (for error msgs) */
137 szValidValues,
138 "", /* pszValidNodes */
139 "GCM", /* pszWho */
140 0); /* uInstance */
141 if (RT_FAILURE(rc))
142 return rc;
143
144 /* Read the configuration. */
145 pVM->gcm.s.fFixerSet = 0;
146 for (unsigned i = 0; i < RT_ELEMENTS(s_aFixerIds); i++)
147 {
148 bool fEnabled = false;
149 rc = CFGMR3QueryBoolDef(pCfgNode, s_aFixerIds[i].pszName, &fEnabled, false);
150 if (RT_FAILURE(rc))
151 return VMR3SetError(pVM->pUVM, rc, RT_SRC_POS, "Error reading /GCM/%s as boolean: %Rrc", s_aFixerIds[i].pszName, rc);
152 if (fEnabled)
153 pVM->gcm.s.fFixerSet = s_aFixerIds[i].fFlag;
154 }
155
156#if 0 /* development override */
157 pVM->gcm.s.fFixerSet = GCMFIXER_DBZ_OS2 | GCMFIXER_DBZ_DOS | GCMFIXER_DBZ_WIN9X;
158#endif
159
160 /*
161 * Log what's enabled.
162 */
163 offValidValues = 0;
164 for (unsigned i = 0; i < RT_ELEMENTS(s_aFixerIds); i++)
165 if (pVM->gcm.s.fFixerSet & s_aFixerIds[i].fFlag)
166 {
167 AssertReturn(offValidValues + s_aFixerIds[i].cchName + 4 <= sizeof(szValidValues), VERR_INTERNAL_ERROR_2);
168 if (!offValidValues)
169 {
170 szValidValues[offValidValues++] = ' ';
171 szValidValues[offValidValues++] = '(';
172 }
173 else
174 {
175 szValidValues[offValidValues++] = ',';
176 szValidValues[offValidValues++] = ' ';
177 }
178 memcpy(&szValidValues[offValidValues], s_aFixerIds[i].pszName, s_aFixerIds[i].cchName);
179 offValidValues += s_aFixerIds[i].cchName;
180 }
181 if (offValidValues)
182 szValidValues[offValidValues++] = ')';
183 szValidValues[offValidValues] = '\0';
184 LogRel(("GCM: Initialized - Fixer bits: %#x%s\n", pVM->gcm.s.fFixerSet, szValidValues));
185
186 return VINF_SUCCESS;
187}
188
189
190/**
191 * @callback_method_impl{FNSSMINTSAVEEXEC}
192 */
193static DECLCALLBACK(int) gcmR3Save(PVM pVM, PSSMHANDLE pSSM)
194{
195 AssertReturn(pVM, VERR_INVALID_PARAMETER);
196 AssertReturn(pSSM, VERR_SSM_INVALID_STATE);
197
198 /*
199 * At present there is only configuration to save.
200 */
201 return SSMR3PutU32(pSSM, pVM->gcm.s.fFixerSet);
202}
203
204
205/**
206 * @callback_method_impl{FNSSMINTLOADEXEC}
207 */
208static DECLCALLBACK(int) gcmR3Load(PVM pVM, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
209{
210 if (uPass != SSM_PASS_FINAL)
211 return VINF_SUCCESS;
212 if (uVersion != GCM_SAVED_STATE_VERSION)
213 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
214
215 /*
216 * Load configuration and check it aginst the current (live migration,
217 * general paranoia).
218 */
219 uint32_t fFixerSet = 0;
220 int rc = SSMR3GetU32(pSSM, &fFixerSet);
221 AssertRCReturn(rc, rc);
222
223 if (fFixerSet != pVM->gcm.s.fFixerSet)
224 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Saved GCM fixer set %#X differs from the configured one (%#X)."),
225 fFixerSet, pVM->gcm.s.fFixerSet);
226
227 return VINF_SUCCESS;
228}
229
230
231/**
232 * Terminates the GCM.
233 *
234 * Termination means cleaning up and freeing all resources,
235 * the VM itself is, at this point, powered off or suspended.
236 *
237 * @returns VBox status code.
238 * @param pVM The cross context VM structure.
239 */
240VMMR3_INT_DECL(int) GCMR3Term(PVM pVM)
241{
242 RT_NOREF(pVM);
243 return VINF_SUCCESS;
244}
245
246
247/**
248 * The VM is being reset.
249 *
250 * Do whatever fixer-specific resetting that needs to be done.
251 *
252 * @param pVM The cross context VM structure.
253 */
254VMMR3_INT_DECL(void) GCMR3Reset(PVM pVM)
255{
256 RT_NOREF(pVM);
257}
258
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