VirtualBox

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

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