VirtualBox

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

Last change on this file since 42392 was 35346, checked in by vboxsync, 14 years ago

VMM reorg: Moving the public include files from include/VBox to include/VBox/vmm.

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