VirtualBox

source: vbox/trunk/src/VBox/Devices/PC/ACPI/VBoxAcpi.cpp@ 105381

Last change on this file since 105381 was 98103, checked in by vboxsync, 2 years ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.2 KB
Line 
1/* $Id: VBoxAcpi.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * VBoxAcpi - VirtualBox ACPI manipulation functionality.
4 */
5
6/*
7 * Copyright (C) 2009-2023 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
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#include <iprt/cdefs.h>
33#if !defined(IN_RING3)
34# error Pure R3 code
35#endif
36
37#define LOG_GROUP LOG_GROUP_DEV_ACPI
38#include <VBox/vmm/pdmdev.h>
39#include <VBox/vmm/pgm.h>
40#include <VBox/log.h>
41#include <VBox/param.h>
42#include <VBox/vmm/cfgm.h>
43#include <VBox/vmm/mm.h>
44#include <iprt/assert.h>
45#include <iprt/alloc.h>
46#include <iprt/string.h>
47#include <iprt/file.h>
48
49#ifdef VBOX_WITH_DYNAMIC_DSDT
50/* vbox.dsl - input to generate proper DSDT on the fly */
51# include <vboxdsl.hex>
52#else
53/* Statically compiled AML */
54# include <vboxaml.hex>
55# include <vboxssdt_standard.hex>
56# include <vboxssdt_cpuhotplug.hex>
57# ifdef VBOX_WITH_TPM
58# include <vboxssdt_tpm.hex>
59# endif
60#endif
61
62#include "VBoxDD.h"
63
64
65#ifdef VBOX_WITH_DYNAMIC_DSDT
66
67static int prepareDynamicDsdt(PPDMDEVINS pDevIns, void **ppvPtr, size_t *pcbDsdt)
68{
69 *ppvPtr = NULL;
70 *pcbDsdt = 0;
71 return 0;
72}
73
74static int cleanupDynamicDsdt(PPDMDEVINS pDevIns, void *pvPtr)
75{
76 return 0;
77}
78
79#else /* VBOX_WITH_DYNAMIC_DSDT */
80
81static int patchAml(PPDMDEVINS pDevIns, uint8_t *pabAml, size_t cbAml)
82{
83 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
84
85 uint16_t cCpus;
86 int rc = pHlp->pfnCFGMQueryU16Def(pDevIns->pCfg, "NumCPUs", &cCpus, 1);
87 if (RT_FAILURE(rc))
88 return rc;
89
90 /* Clear CPU objects at all, if needed */
91 bool fShowCpu;
92 rc = pHlp->pfnCFGMQueryBoolDef(pDevIns->pCfg, "ShowCpu", &fShowCpu, false);
93 if (RT_FAILURE(rc))
94 return rc;
95
96 if (!fShowCpu)
97 cCpus = 0;
98
99 /*
100 * Now search AML for:
101 * AML_PROCESSOR_OP (UINT16) 0x5b83
102 * and replace whole block with
103 * AML_NOOP_OP (UINT16) 0xa3
104 * for VCPU not configured
105 */
106 for (uint32_t i = 0; i < cbAml - 7; i++)
107 {
108 /*
109 * AML_PROCESSOR_OP
110 *
111 * DefProcessor := ProcessorOp PkgLength NameString ProcID PblkAddr PblkLen ObjectList
112 * ProcessorOp := ExtOpPrefix 0x83
113 * ProcID := ByteData
114 * PblkAddr := DwordData
115 * PblkLen := ByteData
116 */
117 if (pabAml[i] == 0x5b && pabAml[i+1] == 0x83)
118 {
119 if (pabAml[i+3] != 'C' || pabAml[i+4] != 'P')
120 /* false alarm, not named starting CP */
121 continue;
122
123 /* Processor ID */
124 if (pabAml[i+7] < cCpus)
125 continue;
126
127 /* Will fill unwanted CPU block with NOOPs */
128 /*
129 * See 18.2.4 Package Length Encoding in ACPI spec
130 * for full format
131 */
132 uint32_t cBytes = pabAml[i + 2];
133 AssertReleaseMsg((cBytes >> 6) == 0,
134 ("So far, we only understand simple package length"));
135
136 /* including AML_PROCESSOR_OP itself */
137 for (uint32_t j = 0; j < cBytes + 2; j++)
138 pabAml[i+j] = 0xa3;
139
140 /* Can increase i by cBytes + 1, but not really worth it */
141 }
142 }
143
144 /* now recompute checksum, whole file byte sum must be 0 */
145 pabAml[9] = 0;
146 uint8_t bSum = 0;
147 for (uint32_t i = 0; i < cbAml; i++)
148 bSum = bSum + pabAml[i];
149 pabAml[9] = (uint8_t)(0 - bSum);
150
151 return VINF_SUCCESS;
152}
153
154/**
155 * Patch the CPU hot-plug SSDT version to
156 * only contain the ACPI containers which may have a CPU
157 */
158static int patchAmlCpuHotPlug(PPDMDEVINS pDevIns, uint8_t *pabAml, size_t cbAml)
159{
160 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
161
162 uint16_t cCpus;
163 int rc = pHlp->pfnCFGMQueryU16Def(pDevIns->pCfg, "NumCPUs", &cCpus, 1);
164 if (RT_FAILURE(rc))
165 return rc;
166
167 /*
168 * Now search AML for:
169 * AML_DEVICE_OP (UINT16) 0x5b82
170 * and replace whole block with
171 * AML_NOOP_OP (UINT16) 0xa3
172 * for VCPU not configured
173 */
174 uint32_t idxAml = 0;
175 while (idxAml < cbAml - 7)
176 {
177 /*
178 * AML_DEVICE_OP
179 *
180 * DefDevice := DeviceOp PkgLength NameString ObjectList
181 * DeviceOp := ExtOpPrefix 0x82
182 */
183 if (pabAml[idxAml] == 0x5b && pabAml[idxAml+1] == 0x82)
184 {
185 /* Check if the enclosed CPU device is configured. */
186 uint8_t *pabAmlPkgLength = &pabAml[idxAml+2];
187 uint32_t cBytes = 0;
188 uint32_t cLengthBytesFollow = pabAmlPkgLength[0] >> 6;
189
190 if (cLengthBytesFollow == 0)
191 {
192 /* Simple package length */
193 cBytes = pabAmlPkgLength[0];
194 }
195 else
196 {
197 unsigned idxLengthByte = 1;
198
199 cBytes = pabAmlPkgLength[0] & 0xF;
200
201 while (idxLengthByte <= cLengthBytesFollow)
202 {
203 cBytes |= pabAmlPkgLength[idxLengthByte] << (4*idxLengthByte);
204 idxLengthByte++;
205 }
206 }
207
208 uint8_t *pabAmlDevName = &pabAmlPkgLength[cLengthBytesFollow+1];
209 uint8_t *pabAmlCpu = &pabAmlDevName[4];
210 bool fCpuConfigured = false;
211 bool fCpuFound = false;
212
213 if ((pabAmlDevName[0] != 'S') || (pabAmlDevName[1] != 'C') || (pabAmlDevName[2] != 'K'))
214 {
215 /* false alarm, not named starting SCK */
216 idxAml++;
217 continue;
218 }
219
220 for (uint32_t idxAmlCpu = 0; idxAmlCpu < cBytes - 7; idxAmlCpu++)
221 {
222 /*
223 * AML_PROCESSOR_OP
224 *
225 * DefProcessor := ProcessorOp PkgLength NameString ProcID
226 PblkAddr PblkLen ObjectList
227 * ProcessorOp := ExtOpPrefix 0x83
228 * ProcID := ByteData
229 * PblkAddr := DwordData
230 * PblkLen := ByteData
231 */
232 if ((pabAmlCpu[idxAmlCpu] == 0x5b) && (pabAmlCpu[idxAmlCpu+1] == 0x83))
233 {
234 if ((pabAmlCpu[idxAmlCpu+4] != 'C') || (pabAmlCpu[idxAmlCpu+5] != 'P'))
235 /* false alarm, not named starting CP */
236 continue;
237
238 fCpuFound = true;
239
240 /* Processor ID */
241 uint8_t const idAmlCpu = pabAmlCpu[idxAmlCpu + 8];
242 if (idAmlCpu < cCpus)
243 {
244 LogFlow(("CPU %u is configured\n", idAmlCpu));
245 fCpuConfigured = true;
246 }
247 else
248 {
249 LogFlow(("CPU %u is not configured\n", idAmlCpu));
250 fCpuConfigured = false;
251 }
252 break;
253 }
254 }
255
256 Assert(fCpuFound);
257
258 if (!fCpuConfigured)
259 {
260 /* Will fill unwanted CPU block with NOOPs */
261 /*
262 * See 18.2.4 Package Length Encoding in ACPI spec
263 * for full format
264 */
265
266 /* including AML_DEVICE_OP itself */
267 for (uint32_t j = 0; j < cBytes + 2; j++)
268 pabAml[idxAml+j] = 0xa3;
269 }
270
271 idxAml++;
272 }
273 else
274 idxAml++;
275 }
276
277 /* now recompute checksum, whole file byte sum must be 0 */
278 pabAml[9] = 0;
279 uint8_t bSum = 0;
280 for (uint32_t i = 0; i < cbAml; i++)
281 bSum = bSum + pabAml[i];
282 pabAml[9] = (uint8_t)(0 - bSum);
283
284 return VINF_SUCCESS;
285}
286
287#endif /* VBOX_WITH_DYNAMIC_DSDT */
288
289/**
290 * Loads an AML file if present in CFGM
291 *
292 * @returns VBox status code
293 * @param pDevIns The device instance
294 * @param pcszCfgName The configuration key holding the file path
295 * @param pcszSignature The signature to check for
296 * @param ppabAmlCode Where to store the pointer to the AML code on success.
297 * @param pcbAmlCode Where to store the number of bytes of the AML code on success.
298 */
299static int acpiAmlLoadExternal(PPDMDEVINS pDevIns, const char *pcszCfgName, const char *pcszSignature,
300 uint8_t **ppabAmlCode, size_t *pcbAmlCode)
301{
302 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
303
304 char *pszAmlFilePath = NULL;
305 int rc = pHlp->pfnCFGMQueryStringAlloc(pDevIns->pCfg, pcszCfgName, &pszAmlFilePath);
306 if (RT_SUCCESS(rc))
307 {
308 /* Load from file. */
309 RTFILE hFileAml = NIL_RTFILE;
310 rc = RTFileOpen(&hFileAml, pszAmlFilePath, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
311 if (RT_SUCCESS(rc))
312 {
313 /*
314 * An AML file contains the raw DSDT or SSDT thus the size of the file
315 * is equal to the size of the DSDT or SSDT.
316 */
317 uint64_t cbAmlFile = 0;
318 rc = RTFileQuerySize(hFileAml, &cbAmlFile);
319
320 /* Don't use AML files over 32MiB. */
321 if ( RT_SUCCESS(rc)
322 && cbAmlFile <= _32M)
323 {
324 size_t const cbAmlCode = (size_t)cbAmlFile;
325 uint8_t *pabAmlCode = (uint8_t *)RTMemAllocZ(cbAmlCode);
326 if (pabAmlCode)
327 {
328 rc = RTFileReadAt(hFileAml, 0, pabAmlCode, cbAmlCode, NULL);
329
330 /*
331 * We fail if reading failed or the identifier at the
332 * beginning is wrong.
333 */
334 if ( RT_FAILURE(rc)
335 || strncmp((const char *)pabAmlCode, pcszSignature, 4))
336 {
337 RTMemFree(pabAmlCode);
338 pabAmlCode = NULL;
339
340 /* Return error if file header check failed */
341 if (RT_SUCCESS(rc))
342 rc = VERR_PARSE_ERROR;
343 }
344 else
345 {
346 *ppabAmlCode = pabAmlCode;
347 *pcbAmlCode = cbAmlCode;
348 rc = VINF_SUCCESS;
349 }
350 }
351 else
352 rc = VERR_NO_MEMORY;
353 }
354 else if (RT_SUCCESS(rc))
355 rc = VERR_OUT_OF_RANGE;
356
357 RTFileClose(hFileAml);
358 }
359 PDMDevHlpMMHeapFree(pDevIns, pszAmlFilePath);
360 }
361
362 return rc;
363}
364
365
366/** No docs, lazy coder. */
367int acpiPrepareDsdt(PPDMDEVINS pDevIns, void **ppvPtr, size_t *pcbDsdt)
368{
369#ifdef VBOX_WITH_DYNAMIC_DSDT
370 return prepareDynamicDsdt(pDevIns, ppvPtr, pcbDsdt);
371#else
372 uint8_t *pabAmlCodeDsdt = NULL;
373 size_t cbAmlCodeDsdt = 0;
374 int rc = acpiAmlLoadExternal(pDevIns, "DsdtFilePath", "DSDT", &pabAmlCodeDsdt, &cbAmlCodeDsdt);
375 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
376 {
377 /* Use the compiled in AML code */
378 cbAmlCodeDsdt = sizeof(AmlCode);
379 pabAmlCodeDsdt = (uint8_t *)RTMemDup(AmlCode, cbAmlCodeDsdt);
380 if (pabAmlCodeDsdt)
381 rc = VINF_SUCCESS;
382 else
383 rc = VERR_NO_MEMORY;
384 }
385 else if (RT_FAILURE(rc))
386 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to read \"DsdtFilePath\""));
387
388 if (RT_SUCCESS(rc))
389 {
390 patchAml(pDevIns, pabAmlCodeDsdt, cbAmlCodeDsdt);
391 *ppvPtr = pabAmlCodeDsdt;
392 *pcbDsdt = cbAmlCodeDsdt;
393 }
394 return rc;
395#endif
396}
397
398/** No docs, lazy coder. */
399int acpiCleanupDsdt(PPDMDEVINS pDevIns, void *pvPtr)
400{
401#ifdef VBOX_WITH_DYNAMIC_DSDT
402 return cleanupDynamicDsdt(pDevIns, pvPtr);
403#else
404 RT_NOREF1(pDevIns);
405 if (pvPtr)
406 RTMemFree(pvPtr);
407 return VINF_SUCCESS;
408#endif
409}
410
411/** No docs, lazy coder. */
412int acpiPrepareSsdt(PPDMDEVINS pDevIns, void **ppvPtr, size_t *pcbSsdt)
413{
414 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
415
416 uint8_t *pabAmlCodeSsdt = NULL;
417 size_t cbAmlCodeSsdt = 0;
418 int rc = acpiAmlLoadExternal(pDevIns, "SsdtFilePath", "SSDT", &pabAmlCodeSsdt, &cbAmlCodeSsdt);
419 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
420 {
421 bool fCpuHotPlug = false;
422 rc = pHlp->pfnCFGMQueryBoolDef(pDevIns->pCfg, "CpuHotPlug", &fCpuHotPlug, false);
423 if (RT_SUCCESS(rc))
424 {
425 if (fCpuHotPlug)
426 {
427 cbAmlCodeSsdt = sizeof(AmlCodeSsdtCpuHotPlug);
428 pabAmlCodeSsdt = (uint8_t *)RTMemDup(AmlCodeSsdtCpuHotPlug, sizeof(AmlCodeSsdtCpuHotPlug));
429 }
430 else
431 {
432 cbAmlCodeSsdt = sizeof(AmlCodeSsdtStandard);
433 pabAmlCodeSsdt = (uint8_t *)RTMemDup(AmlCodeSsdtStandard, sizeof(AmlCodeSsdtStandard));
434 }
435 if (pabAmlCodeSsdt)
436 {
437 if (fCpuHotPlug)
438 patchAmlCpuHotPlug(pDevIns, pabAmlCodeSsdt, cbAmlCodeSsdt);
439 else
440 patchAml(pDevIns, pabAmlCodeSsdt, cbAmlCodeSsdt);
441 }
442 else
443 rc = VERR_NO_MEMORY;
444 }
445 }
446 else if (RT_FAILURE(rc))
447 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to read \"SsdtFilePath\""));
448
449 if (RT_SUCCESS(rc))
450 {
451 *ppvPtr = pabAmlCodeSsdt;
452 *pcbSsdt = cbAmlCodeSsdt;
453 }
454 return rc;
455}
456
457/** No docs, lazy coder. */
458int acpiCleanupSsdt(PPDMDEVINS pDevIns, void *pvPtr)
459{
460 RT_NOREF1(pDevIns);
461 if (pvPtr)
462 RTMemFree(pvPtr);
463 return VINF_SUCCESS;
464}
465
466#ifdef VBOX_WITH_TPM
467/** No docs, lazy coder. */
468int acpiPrepareTpmSsdt(PPDMDEVINS pDevIns, void **ppvPtr, size_t *pcbSsdt)
469{
470 uint8_t *pabAmlCodeSsdt = NULL;
471 size_t cbAmlCodeSsdt = 0;
472 int rc = acpiAmlLoadExternal(pDevIns, "SsdtTpmFilePath", "SSDT", &pabAmlCodeSsdt, &cbAmlCodeSsdt);
473 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
474 {
475 rc = VINF_SUCCESS;
476 cbAmlCodeSsdt = sizeof(AmlCodeSsdtTpm);
477 pabAmlCodeSsdt = (uint8_t *)RTMemDup(AmlCodeSsdtTpm, sizeof(AmlCodeSsdtTpm));
478 if (!pabAmlCodeSsdt)
479 rc = VERR_NO_MEMORY;
480 }
481 else if (RT_FAILURE(rc))
482 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to read \"SsdtFilePath\""));
483
484 if (RT_SUCCESS(rc))
485 {
486 *ppvPtr = pabAmlCodeSsdt;
487 *pcbSsdt = cbAmlCodeSsdt;
488 }
489 return rc;
490}
491
492/** No docs, lazy coder. */
493int acpiCleanupTpmSsdt(PPDMDEVINS pDevIns, void *pvPtr)
494{
495 RT_NOREF1(pDevIns);
496 if (pvPtr)
497 RTMemFree(pvPtr);
498 return VINF_SUCCESS;
499}
500#endif
501
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