VirtualBox

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

Last change on this file since 27038 was 26614, checked in by vboxsync, 15 years ago

CPU hotplug: Replace container objects with noop's if the attached CPU can't never show up in the VM

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette