]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c
OvmfPkg: Allow multiple add-pointer linker commands to same ACPI table
[mirror_edk2.git] / OvmfPkg / AcpiPlatformDxe / QemuFwCfgAcpi.c
CommitLineData
14b0faad
JJ
1/** @file\r
2 OVMF ACPI support using QEMU's fw-cfg interface\r
3\r
4 Copyright (c) 2008 - 2014, Intel Corporation. All rights reserved.<BR>\r
5 Copyright (C) 2012-2014, Red Hat, Inc.\r
6\r
ab63766b
LE
7 This program and the accompanying materials are licensed and made available\r
8 under the terms and conditions of the BSD License which accompanies this\r
9 distribution. The full text of the license may be found at\r
14b0faad
JJ
10 http://opensource.org/licenses/bsd-license.php\r
11\r
ab63766b
LE
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT\r
13 WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
14b0faad
JJ
14\r
15**/\r
16\r
17#include "AcpiPlatform.h"\r
18#include "QemuLoader.h"\r
19#include <Library/BaseMemoryLib.h>\r
20#include <Library/MemoryAllocationLib.h>\r
21#include <Library/QemuFwCfgLib.h>\r
687f7521 22#include <Library/QemuFwCfgS3Lib.h>\r
14b0faad
JJ
23#include <Library/DxeServicesTableLib.h>\r
24#include <Library/PcdLib.h>\r
25#include <Library/OrderedCollectionLib.h>\r
26#include <IndustryStandard/Acpi.h>\r
27\r
28\r
29//\r
30// The user structure for the ordered collection that will track the fw_cfg\r
31// blobs under processing.\r
32//\r
33typedef struct {\r
34 UINT8 File[QEMU_LOADER_FNAME_SIZE]; // NUL-terminated name of the fw_cfg\r
35 // blob. This is the ordering / search\r
36 // key.\r
37 UINTN Size; // The number of bytes in this blob.\r
38 UINT8 *Base; // Pointer to the blob data.\r
39 BOOLEAN HostsOnlyTableData; // TRUE iff the blob has been found to\r
40 // only contain data that is directly\r
41 // part of ACPI tables.\r
42} BLOB;\r
43\r
44\r
45/**\r
46 Compare a standalone key against a user structure containing an embedded key.\r
47\r
48 @param[in] StandaloneKey Pointer to the bare key.\r
49\r
50 @param[in] UserStruct Pointer to the user structure with the embedded\r
51 key.\r
52\r
53 @retval <0 If StandaloneKey compares less than UserStruct's key.\r
54\r
55 @retval 0 If StandaloneKey compares equal to UserStruct's key.\r
56\r
57 @retval >0 If StandaloneKey compares greater than UserStruct's key.\r
58**/\r
59STATIC\r
60INTN\r
61EFIAPI\r
62BlobKeyCompare (\r
63 IN CONST VOID *StandaloneKey,\r
64 IN CONST VOID *UserStruct\r
65 )\r
66{\r
67 CONST BLOB *Blob;\r
68\r
69 Blob = UserStruct;\r
70 return AsciiStrCmp (StandaloneKey, (CONST CHAR8 *)Blob->File);\r
71}\r
72\r
73\r
74/**\r
75 Comparator function for two user structures.\r
76\r
77 @param[in] UserStruct1 Pointer to the first user structure.\r
78\r
79 @param[in] UserStruct2 Pointer to the second user structure.\r
80\r
81 @retval <0 If UserStruct1 compares less than UserStruct2.\r
82\r
83 @retval 0 If UserStruct1 compares equal to UserStruct2.\r
84\r
85 @retval >0 If UserStruct1 compares greater than UserStruct2.\r
86**/\r
87STATIC\r
88INTN\r
89EFIAPI\r
90BlobCompare (\r
91 IN CONST VOID *UserStruct1,\r
92 IN CONST VOID *UserStruct2\r
93 )\r
94{\r
95 CONST BLOB *Blob1;\r
96\r
97 Blob1 = UserStruct1;\r
98 return BlobKeyCompare (Blob1->File, UserStruct2);\r
99}\r
100\r
101\r
072060a6
PDJ
102/**\r
103 Comparator function for two opaque pointers, ordering on (unsigned) pointer\r
104 value itself.\r
105 Can be used as both Key and UserStruct comparator.\r
106\r
107 @param[in] Pointer1 First pointer.\r
108\r
109 @param[in] Pointer2 Second pointer.\r
110\r
111 @retval <0 If Pointer1 compares less than Pointer2.\r
112\r
113 @retval 0 If Pointer1 compares equal to Pointer2.\r
114\r
115 @retval >0 If Pointer1 compares greater than Pointer2.\r
116**/\r
117STATIC\r
118INTN\r
119EFIAPI\r
120PointerCompare (\r
121 IN CONST VOID *Pointer1,\r
122 IN CONST VOID *Pointer2\r
123 )\r
124{\r
125 if (Pointer1 == Pointer2) {\r
126 return 0;\r
127 }\r
128 if ((UINTN)Pointer1 < (UINTN)Pointer2) {\r
129 return -1;\r
130 }\r
131 return 1;\r
132}\r
133\r
134\r
14b0faad
JJ
135/**\r
136 Process a QEMU_LOADER_ALLOCATE command.\r
137\r
138 @param[in] Allocate The QEMU_LOADER_ALLOCATE command to process.\r
139\r
140 @param[in,out] Tracker The ORDERED_COLLECTION tracking the BLOB user\r
141 structures created thus far.\r
142\r
143 @retval EFI_SUCCESS An area of whole AcpiNVS pages has been\r
144 allocated for the blob contents, and the\r
145 contents have been saved. A BLOB object (user\r
146 structure) has been allocated from pool memory,\r
147 referencing the blob contents. The BLOB user\r
148 structure has been linked into Tracker.\r
149\r
150 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name has been found in\r
151 Allocate, or the Allocate command references a\r
152 file that is already known by Tracker.\r
153\r
154 @retval EFI_UNSUPPORTED Unsupported alignment request has been found in\r
155 Allocate.\r
156\r
157 @retval EFI_OUT_OF_RESOURCES Pool allocation failed.\r
158\r
159 @return Error codes from QemuFwCfgFindFile() and\r
160 gBS->AllocatePages().\r
161**/\r
162STATIC\r
163EFI_STATUS\r
164EFIAPI\r
165ProcessCmdAllocate (\r
166 IN CONST QEMU_LOADER_ALLOCATE *Allocate,\r
167 IN OUT ORDERED_COLLECTION *Tracker\r
168 )\r
169{\r
170 FIRMWARE_CONFIG_ITEM FwCfgItem;\r
171 UINTN FwCfgSize;\r
172 EFI_STATUS Status;\r
173 UINTN NumPages;\r
174 EFI_PHYSICAL_ADDRESS Address;\r
175 BLOB *Blob;\r
176\r
177 if (Allocate->File[QEMU_LOADER_FNAME_SIZE - 1] != '\0') {\r
178 DEBUG ((EFI_D_ERROR, "%a: malformed file name\n", __FUNCTION__));\r
179 return EFI_PROTOCOL_ERROR;\r
180 }\r
181\r
182 if (Allocate->Alignment > EFI_PAGE_SIZE) {\r
183 DEBUG ((EFI_D_ERROR, "%a: unsupported alignment 0x%x\n", __FUNCTION__,\r
184 Allocate->Alignment));\r
185 return EFI_UNSUPPORTED;\r
186 }\r
187\r
188 Status = QemuFwCfgFindFile ((CHAR8 *)Allocate->File, &FwCfgItem, &FwCfgSize);\r
189 if (EFI_ERROR (Status)) {\r
190 DEBUG ((EFI_D_ERROR, "%a: QemuFwCfgFindFile(\"%a\"): %r\n", __FUNCTION__,\r
191 Allocate->File, Status));\r
192 return Status;\r
193 }\r
194\r
195 NumPages = EFI_SIZE_TO_PAGES (FwCfgSize);\r
196 Address = 0xFFFFFFFF;\r
197 Status = gBS->AllocatePages (AllocateMaxAddress, EfiACPIMemoryNVS, NumPages,\r
198 &Address);\r
199 if (EFI_ERROR (Status)) {\r
200 return Status;\r
201 }\r
202\r
203 Blob = AllocatePool (sizeof *Blob);\r
204 if (Blob == NULL) {\r
205 Status = EFI_OUT_OF_RESOURCES;\r
206 goto FreePages;\r
207 }\r
208 CopyMem (Blob->File, Allocate->File, QEMU_LOADER_FNAME_SIZE);\r
209 Blob->Size = FwCfgSize;\r
210 Blob->Base = (VOID *)(UINTN)Address;\r
211 Blob->HostsOnlyTableData = TRUE;\r
212\r
213 Status = OrderedCollectionInsert (Tracker, NULL, Blob);\r
214 if (Status == RETURN_ALREADY_STARTED) {\r
215 DEBUG ((EFI_D_ERROR, "%a: duplicated file \"%a\"\n", __FUNCTION__,\r
216 Allocate->File));\r
217 Status = EFI_PROTOCOL_ERROR;\r
218 }\r
219 if (EFI_ERROR (Status)) {\r
220 goto FreeBlob;\r
221 }\r
222\r
223 QemuFwCfgSelectItem (FwCfgItem);\r
224 QemuFwCfgReadBytes (FwCfgSize, Blob->Base);\r
225 ZeroMem (Blob->Base + Blob->Size, EFI_PAGES_TO_SIZE (NumPages) - Blob->Size);\r
226\r
227 DEBUG ((EFI_D_VERBOSE, "%a: File=\"%a\" Alignment=0x%x Zone=%d Size=0x%Lx "\r
228 "Address=0x%Lx\n", __FUNCTION__, Allocate->File, Allocate->Alignment,\r
229 Allocate->Zone, (UINT64)Blob->Size, (UINT64)(UINTN)Blob->Base));\r
230 return EFI_SUCCESS;\r
231\r
232FreeBlob:\r
233 FreePool (Blob);\r
234\r
235FreePages:\r
236 gBS->FreePages (Address, NumPages);\r
237\r
238 return Status;\r
239}\r
240\r
241\r
242/**\r
243 Process a QEMU_LOADER_ADD_POINTER command.\r
244\r
245 @param[in] AddPointer The QEMU_LOADER_ADD_POINTER command to process.\r
246\r
247 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user\r
248 structures created thus far.\r
249\r
250 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name(s) have been found in\r
251 AddPointer, or the AddPointer command references\r
252 a file unknown to Tracker, or the pointer to\r
253 relocate has invalid location, size, or value, or\r
254 the relocated pointer value is not representable\r
255 in the given pointer size.\r
256\r
257 @retval EFI_SUCCESS The pointer field inside the pointer blob has\r
258 been relocated.\r
259**/\r
260STATIC\r
261EFI_STATUS\r
262EFIAPI\r
263ProcessCmdAddPointer (\r
264 IN CONST QEMU_LOADER_ADD_POINTER *AddPointer,\r
265 IN CONST ORDERED_COLLECTION *Tracker\r
266 )\r
267{\r
268 ORDERED_COLLECTION_ENTRY *TrackerEntry, *TrackerEntry2;\r
269 BLOB *Blob, *Blob2;\r
270 UINT8 *PointerField;\r
271 UINT64 PointerValue;\r
272\r
273 if (AddPointer->PointerFile[QEMU_LOADER_FNAME_SIZE - 1] != '\0' ||\r
274 AddPointer->PointeeFile[QEMU_LOADER_FNAME_SIZE - 1] != '\0') {\r
275 DEBUG ((EFI_D_ERROR, "%a: malformed file name\n", __FUNCTION__));\r
276 return EFI_PROTOCOL_ERROR;\r
277 }\r
278\r
279 TrackerEntry = OrderedCollectionFind (Tracker, AddPointer->PointerFile);\r
280 TrackerEntry2 = OrderedCollectionFind (Tracker, AddPointer->PointeeFile);\r
281 if (TrackerEntry == NULL || TrackerEntry2 == NULL) {\r
282 DEBUG ((EFI_D_ERROR, "%a: invalid blob reference(s) \"%a\" / \"%a\"\n",\r
283 __FUNCTION__, AddPointer->PointerFile, AddPointer->PointeeFile));\r
284 return EFI_PROTOCOL_ERROR;\r
285 }\r
286\r
287 Blob = OrderedCollectionUserStruct (TrackerEntry);\r
288 Blob2 = OrderedCollectionUserStruct (TrackerEntry2);\r
289 if ((AddPointer->PointerSize != 1 && AddPointer->PointerSize != 2 &&\r
290 AddPointer->PointerSize != 4 && AddPointer->PointerSize != 8) ||\r
291 Blob->Size < AddPointer->PointerSize ||\r
292 Blob->Size - AddPointer->PointerSize < AddPointer->PointerOffset) {\r
293 DEBUG ((EFI_D_ERROR, "%a: invalid pointer location or size in \"%a\"\n",\r
294 __FUNCTION__, AddPointer->PointerFile));\r
295 return EFI_PROTOCOL_ERROR;\r
296 }\r
297\r
298 PointerField = Blob->Base + AddPointer->PointerOffset;\r
299 PointerValue = 0;\r
300 CopyMem (&PointerValue, PointerField, AddPointer->PointerSize);\r
301 if (PointerValue >= Blob2->Size) {\r
302 DEBUG ((EFI_D_ERROR, "%a: invalid pointer value in \"%a\"\n", __FUNCTION__,\r
303 AddPointer->PointerFile));\r
304 return EFI_PROTOCOL_ERROR;\r
305 }\r
306\r
307 //\r
308 // The memory allocation system ensures that the address of the byte past the\r
309 // last byte of any allocated object is expressible (no wraparound).\r
310 //\r
311 ASSERT ((UINTN)Blob2->Base <= MAX_ADDRESS - Blob2->Size);\r
312\r
313 PointerValue += (UINT64)(UINTN)Blob2->Base;\r
a3be3b65
LE
314 if (AddPointer->PointerSize < 8 &&\r
315 RShiftU64 (PointerValue, AddPointer->PointerSize * 8) != 0) {\r
14b0faad
JJ
316 DEBUG ((EFI_D_ERROR, "%a: relocated pointer value unrepresentable in "\r
317 "\"%a\"\n", __FUNCTION__, AddPointer->PointerFile));\r
318 return EFI_PROTOCOL_ERROR;\r
319 }\r
320\r
321 CopyMem (PointerField, &PointerValue, AddPointer->PointerSize);\r
322\r
323 DEBUG ((EFI_D_VERBOSE, "%a: PointerFile=\"%a\" PointeeFile=\"%a\" "\r
324 "PointerOffset=0x%x PointerSize=%d\n", __FUNCTION__,\r
325 AddPointer->PointerFile, AddPointer->PointeeFile,\r
326 AddPointer->PointerOffset, AddPointer->PointerSize));\r
327 return EFI_SUCCESS;\r
328}\r
329\r
330\r
331/**\r
332 Process a QEMU_LOADER_ADD_CHECKSUM command.\r
333\r
334 @param[in] AddChecksum The QEMU_LOADER_ADD_CHECKSUM command to process.\r
335\r
336 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user\r
337 structures created thus far.\r
338\r
339 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name has been found in\r
340 AddChecksum, or the AddChecksum command\r
341 references a file unknown to Tracker, or the\r
342 range to checksum is invalid.\r
343\r
344 @retval EFI_SUCCESS The requested range has been checksummed.\r
345**/\r
346STATIC\r
347EFI_STATUS\r
348EFIAPI\r
349ProcessCmdAddChecksum (\r
350 IN CONST QEMU_LOADER_ADD_CHECKSUM *AddChecksum,\r
351 IN CONST ORDERED_COLLECTION *Tracker\r
352 )\r
353{\r
354 ORDERED_COLLECTION_ENTRY *TrackerEntry;\r
355 BLOB *Blob;\r
356\r
357 if (AddChecksum->File[QEMU_LOADER_FNAME_SIZE - 1] != '\0') {\r
358 DEBUG ((EFI_D_ERROR, "%a: malformed file name\n", __FUNCTION__));\r
359 return EFI_PROTOCOL_ERROR;\r
360 }\r
361\r
362 TrackerEntry = OrderedCollectionFind (Tracker, AddChecksum->File);\r
363 if (TrackerEntry == NULL) {\r
364 DEBUG ((EFI_D_ERROR, "%a: invalid blob reference \"%a\"\n", __FUNCTION__,\r
365 AddChecksum->File));\r
366 return EFI_PROTOCOL_ERROR;\r
367 }\r
368\r
369 Blob = OrderedCollectionUserStruct (TrackerEntry);\r
370 if (Blob->Size <= AddChecksum->ResultOffset ||\r
371 Blob->Size < AddChecksum->Length ||\r
372 Blob->Size - AddChecksum->Length < AddChecksum->Start) {\r
373 DEBUG ((EFI_D_ERROR, "%a: invalid checksum range in \"%a\"\n",\r
374 __FUNCTION__, AddChecksum->File));\r
375 return EFI_PROTOCOL_ERROR;\r
376 }\r
377\r
378 Blob->Base[AddChecksum->ResultOffset] = CalculateCheckSum8 (\r
379 Blob->Base + AddChecksum->Start,\r
380 AddChecksum->Length\r
381 );\r
382 DEBUG ((EFI_D_VERBOSE, "%a: File=\"%a\" ResultOffset=0x%x Start=0x%x "\r
383 "Length=0x%x\n", __FUNCTION__, AddChecksum->File,\r
384 AddChecksum->ResultOffset, AddChecksum->Start, AddChecksum->Length));\r
385 return EFI_SUCCESS;\r
386}\r
387\r
388\r
9965cbd4
LE
389/**\r
390 Process a QEMU_LOADER_WRITE_POINTER command.\r
391\r
392 @param[in] WritePointer The QEMU_LOADER_WRITE_POINTER command to process.\r
393\r
394 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user\r
395 structures created thus far.\r
396\r
df73df13
LE
397 @param[in,out] S3Context The S3_CONTEXT object capturing the fw_cfg actions\r
398 of successfully processed QEMU_LOADER_WRITE_POINTER\r
399 commands, to be replayed at S3 resume. S3Context\r
400 may be NULL if S3 is disabled.\r
401\r
9965cbd4
LE
402 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name(s) have been found in\r
403 WritePointer. Or, the WritePointer command\r
404 references a file unknown to Tracker or the\r
405 fw_cfg directory. Or, the pointer object to\r
406 rewrite has invalid location, size, or initial\r
407 relative value. Or, the pointer value to store\r
408 does not fit in the given pointer size.\r
409\r
410 @retval EFI_SUCCESS The pointer object inside the writeable fw_cfg\r
df73df13
LE
411 file has been written. If S3Context is not NULL,\r
412 then WritePointer has been condensed into\r
413 S3Context.\r
414\r
415 @return Error codes propagated from\r
416 SaveCondensedWritePointerToS3Context(). The\r
417 pointer object inside the writeable fw_cfg file\r
418 has not been written.\r
9965cbd4
LE
419**/\r
420STATIC\r
421EFI_STATUS\r
422ProcessCmdWritePointer (\r
423 IN CONST QEMU_LOADER_WRITE_POINTER *WritePointer,\r
df73df13
LE
424 IN CONST ORDERED_COLLECTION *Tracker,\r
425 IN OUT S3_CONTEXT *S3Context OPTIONAL\r
9965cbd4
LE
426 )\r
427{\r
428 RETURN_STATUS Status;\r
429 FIRMWARE_CONFIG_ITEM PointerItem;\r
430 UINTN PointerItemSize;\r
431 ORDERED_COLLECTION_ENTRY *PointeeEntry;\r
432 BLOB *PointeeBlob;\r
433 UINT64 PointerValue;\r
434\r
435 if (WritePointer->PointerFile[QEMU_LOADER_FNAME_SIZE - 1] != '\0' ||\r
436 WritePointer->PointeeFile[QEMU_LOADER_FNAME_SIZE - 1] != '\0') {\r
437 DEBUG ((DEBUG_ERROR, "%a: malformed file name\n", __FUNCTION__));\r
438 return EFI_PROTOCOL_ERROR;\r
439 }\r
440\r
441 Status = QemuFwCfgFindFile ((CONST CHAR8 *)WritePointer->PointerFile,\r
442 &PointerItem, &PointerItemSize);\r
443 PointeeEntry = OrderedCollectionFind (Tracker, WritePointer->PointeeFile);\r
444 if (RETURN_ERROR (Status) || PointeeEntry == NULL) {\r
445 DEBUG ((DEBUG_ERROR,\r
446 "%a: invalid fw_cfg file or blob reference \"%a\" / \"%a\"\n",\r
447 __FUNCTION__, WritePointer->PointerFile, WritePointer->PointeeFile));\r
448 return EFI_PROTOCOL_ERROR;\r
449 }\r
450\r
451 if ((WritePointer->PointerSize != 1 && WritePointer->PointerSize != 2 &&\r
452 WritePointer->PointerSize != 4 && WritePointer->PointerSize != 8) ||\r
453 (PointerItemSize < WritePointer->PointerSize) ||\r
454 (PointerItemSize - WritePointer->PointerSize <\r
455 WritePointer->PointerOffset)) {\r
456 DEBUG ((DEBUG_ERROR, "%a: invalid pointer location or size in \"%a\"\n",\r
457 __FUNCTION__, WritePointer->PointerFile));\r
458 return EFI_PROTOCOL_ERROR;\r
459 }\r
460\r
461 PointeeBlob = OrderedCollectionUserStruct (PointeeEntry);\r
462 PointerValue = WritePointer->PointeeOffset;\r
463 if (PointerValue >= PointeeBlob->Size) {\r
464 DEBUG ((DEBUG_ERROR, "%a: invalid PointeeOffset\n", __FUNCTION__));\r
465 return EFI_PROTOCOL_ERROR;\r
466 }\r
467\r
468 //\r
469 // The memory allocation system ensures that the address of the byte past the\r
470 // last byte of any allocated object is expressible (no wraparound).\r
471 //\r
472 ASSERT ((UINTN)PointeeBlob->Base <= MAX_ADDRESS - PointeeBlob->Size);\r
473\r
474 PointerValue += (UINT64)(UINTN)PointeeBlob->Base;\r
a3be3b65
LE
475 if (WritePointer->PointerSize < 8 &&\r
476 RShiftU64 (PointerValue, WritePointer->PointerSize * 8) != 0) {\r
9965cbd4
LE
477 DEBUG ((DEBUG_ERROR, "%a: pointer value unrepresentable in \"%a\"\n",\r
478 __FUNCTION__, WritePointer->PointerFile));\r
479 return EFI_PROTOCOL_ERROR;\r
480 }\r
481\r
df73df13
LE
482 //\r
483 // If S3 is enabled, we have to capture the below fw_cfg actions in condensed\r
484 // form, to be replayed during S3 resume.\r
485 //\r
486 if (S3Context != NULL) {\r
487 EFI_STATUS SaveStatus;\r
488\r
489 SaveStatus = SaveCondensedWritePointerToS3Context (\r
490 S3Context,\r
491 (UINT16)PointerItem,\r
492 WritePointer->PointerSize,\r
493 WritePointer->PointerOffset,\r
494 PointerValue\r
495 );\r
496 if (EFI_ERROR (SaveStatus)) {\r
497 return SaveStatus;\r
498 }\r
499 }\r
500\r
9965cbd4
LE
501 QemuFwCfgSelectItem (PointerItem);\r
502 QemuFwCfgSkipBytes (WritePointer->PointerOffset);\r
503 QemuFwCfgWriteBytes (WritePointer->PointerSize, &PointerValue);\r
504\r
505 //\r
506 // Because QEMU has now learned PointeeBlob->Base, we must mark PointeeBlob\r
507 // as unreleasable, for the case when the whole linker/loader script is\r
508 // handled successfully.\r
509 //\r
510 PointeeBlob->HostsOnlyTableData = FALSE;\r
511\r
512 DEBUG ((DEBUG_VERBOSE, "%a: PointerFile=\"%a\" PointeeFile=\"%a\" "\r
513 "PointerOffset=0x%x PointeeOffset=0x%x PointerSize=%d\n", __FUNCTION__,\r
514 WritePointer->PointerFile, WritePointer->PointeeFile,\r
515 WritePointer->PointerOffset, WritePointer->PointeeOffset,\r
516 WritePointer->PointerSize));\r
517 return EFI_SUCCESS;\r
518}\r
519\r
520\r
521/**\r
522 Undo a QEMU_LOADER_WRITE_POINTER command.\r
523\r
524 This function revokes (zeroes out) a guest memory reference communicated to\r
525 QEMU earlier. The caller is responsible for invoking this function only on\r
526 such QEMU_LOADER_WRITE_POINTER commands that have been successfully processed\r
527 by ProcessCmdWritePointer().\r
528\r
529 @param[in] WritePointer The QEMU_LOADER_WRITE_POINTER command to undo.\r
530**/\r
531STATIC\r
532VOID\r
533UndoCmdWritePointer (\r
534 IN CONST QEMU_LOADER_WRITE_POINTER *WritePointer\r
535 )\r
536{\r
537 RETURN_STATUS Status;\r
538 FIRMWARE_CONFIG_ITEM PointerItem;\r
539 UINTN PointerItemSize;\r
540 UINT64 PointerValue;\r
541\r
542 Status = QemuFwCfgFindFile ((CONST CHAR8 *)WritePointer->PointerFile,\r
543 &PointerItem, &PointerItemSize);\r
544 ASSERT_RETURN_ERROR (Status);\r
545\r
546 PointerValue = 0;\r
547 QemuFwCfgSelectItem (PointerItem);\r
548 QemuFwCfgSkipBytes (WritePointer->PointerOffset);\r
549 QemuFwCfgWriteBytes (WritePointer->PointerSize, &PointerValue);\r
550\r
551 DEBUG ((DEBUG_VERBOSE,\r
552 "%a: PointerFile=\"%a\" PointerOffset=0x%x PointerSize=%d\n", __FUNCTION__,\r
553 WritePointer->PointerFile, WritePointer->PointerOffset,\r
554 WritePointer->PointerSize));\r
555}\r
556\r
557\r
14b0faad
JJ
558//\r
559// We'll be saving the keys of installed tables so that we can roll them back\r
560// in case of failure. 128 tables should be enough for anyone (TM).\r
561//\r
562#define INSTALLED_TABLES_MAX 128\r
563\r
564/**\r
565 Process a QEMU_LOADER_ADD_POINTER command in order to see if its target byte\r
566 array is an ACPI table, and if so, install it.\r
567\r
568 This function assumes that the entire QEMU linker/loader command file has\r
8c0b0b34 569 been processed successfully in a prior first pass.\r
14b0faad
JJ
570\r
571 @param[in] AddPointer The QEMU_LOADER_ADD_POINTER command to process.\r
572\r
573 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user\r
574 structures.\r
575\r
576 @param[in] AcpiProtocol The ACPI table protocol used to install tables.\r
577\r
578 @param[in,out] InstalledKey On input, an array of INSTALLED_TABLES_MAX UINTN\r
579 elements, allocated by the caller. On output,\r
580 the function will have stored (appended) the\r
581 AcpiProtocol-internal key of the ACPI table that\r
582 the function has installed, if the AddPointer\r
583 command identified an ACPI table that is\r
584 different from RSDT and XSDT.\r
585\r
586 @param[in,out] NumInstalled On input, the number of entries already used in\r
587 InstalledKey; it must be in [0,\r
588 INSTALLED_TABLES_MAX] inclusive. On output, the\r
589 parameter is incremented if the AddPointer\r
590 command identified an ACPI table that is\r
591 different from RSDT and XSDT.\r
592\r
072060a6
PDJ
593 @param[in,out] SeenPointers The ORDERED_COLLECTION tracking the absolute\r
594 target addresses that have been pointed-to by\r
595 QEMU_LOADER_ADD_POINTER commands thus far. If a\r
596 target address is encountered for the first\r
597 time, and it identifies an ACPI table that is\r
598 different from RDST and XSDT, the table is\r
599 installed. If a target address is seen for the\r
600 second or later times, it is skipped without\r
601 taking any action.\r
602\r
14b0faad
JJ
603 @retval EFI_INVALID_PARAMETER NumInstalled was outside the allowed range on\r
604 input.\r
605\r
606 @retval EFI_OUT_OF_RESOURCES The AddPointer command identified an ACPI\r
607 table different from RSDT and XSDT, but there\r
608 was no more room in InstalledKey.\r
609\r
072060a6
PDJ
610 @retval EFI_SUCCESS AddPointer has been processed. Either its\r
611 absolute target address has been encountered\r
612 before, or an ACPI table different from RSDT\r
613 and XSDT has been installed (reflected by\r
614 InstalledKey and NumInstalled), or RSDT or\r
615 XSDT has been identified but not installed, or\r
616 the fw_cfg blob pointed-into by AddPointer has\r
617 been marked as hosting something else than\r
618 just direct ACPI table contents.\r
14b0faad
JJ
619\r
620 @return Error codes returned by\r
621 AcpiProtocol->InstallAcpiTable().\r
622**/\r
623STATIC\r
624EFI_STATUS\r
625EFIAPI\r
626Process2ndPassCmdAddPointer (\r
627 IN CONST QEMU_LOADER_ADD_POINTER *AddPointer,\r
628 IN CONST ORDERED_COLLECTION *Tracker,\r
629 IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol,\r
630 IN OUT UINTN InstalledKey[INSTALLED_TABLES_MAX],\r
072060a6
PDJ
631 IN OUT INT32 *NumInstalled,\r
632 IN OUT ORDERED_COLLECTION *SeenPointers\r
14b0faad
JJ
633 )\r
634{\r
635 CONST ORDERED_COLLECTION_ENTRY *TrackerEntry;\r
636 CONST ORDERED_COLLECTION_ENTRY *TrackerEntry2;\r
072060a6 637 ORDERED_COLLECTION_ENTRY *SeenPointerEntry;\r
14b0faad
JJ
638 CONST BLOB *Blob;\r
639 BLOB *Blob2;\r
640 CONST UINT8 *PointerField;\r
641 UINT64 PointerValue;\r
642 UINTN Blob2Remaining;\r
643 UINTN TableSize;\r
644 CONST EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *Facs;\r
645 CONST EFI_ACPI_DESCRIPTION_HEADER *Header;\r
646 EFI_STATUS Status;\r
647\r
648 if (*NumInstalled < 0 || *NumInstalled > INSTALLED_TABLES_MAX) {\r
649 return EFI_INVALID_PARAMETER;\r
650 }\r
651\r
652 TrackerEntry = OrderedCollectionFind (Tracker, AddPointer->PointerFile);\r
653 TrackerEntry2 = OrderedCollectionFind (Tracker, AddPointer->PointeeFile);\r
654 Blob = OrderedCollectionUserStruct (TrackerEntry);\r
655 Blob2 = OrderedCollectionUserStruct (TrackerEntry2);\r
656 PointerField = Blob->Base + AddPointer->PointerOffset;\r
657 PointerValue = 0;\r
658 CopyMem (&PointerValue, PointerField, AddPointer->PointerSize);\r
659\r
660 //\r
661 // We assert that PointerValue falls inside Blob2's contents. This is ensured\r
662 // by the Blob2->Size check and later checks in ProcessCmdAddPointer().\r
663 //\r
664 Blob2Remaining = (UINTN)Blob2->Base;\r
665 ASSERT(PointerValue >= Blob2Remaining);\r
666 Blob2Remaining += Blob2->Size;\r
667 ASSERT (PointerValue < Blob2Remaining);\r
668\r
072060a6
PDJ
669 Status = OrderedCollectionInsert (\r
670 SeenPointers,\r
671 &SeenPointerEntry, // for reverting insertion in error case\r
672 (VOID *)(UINTN)PointerValue\r
673 );\r
674 if (EFI_ERROR (Status)) {\r
675 if (Status == RETURN_ALREADY_STARTED) {\r
676 //\r
677 // Already seen this pointer, don't try to process it again.\r
678 //\r
679 DEBUG ((\r
680 DEBUG_VERBOSE,\r
681 "%a: PointerValue=0x%Lx already processed, skipping.\n",\r
682 __FUNCTION__,\r
683 PointerValue\r
684 ));\r
685 Status = EFI_SUCCESS;\r
686 }\r
687 return Status;\r
688 }\r
689\r
14b0faad
JJ
690 Blob2Remaining -= (UINTN) PointerValue;\r
691 DEBUG ((EFI_D_VERBOSE, "%a: checking for ACPI header in \"%a\" at 0x%Lx "\r
692 "(remaining: 0x%Lx): ", __FUNCTION__, AddPointer->PointeeFile,\r
693 PointerValue, (UINT64)Blob2Remaining));\r
694\r
695 TableSize = 0;\r
696\r
697 //\r
698 // To make our job simple, the FACS has a custom header. Sigh.\r
699 //\r
700 if (sizeof *Facs <= Blob2Remaining) {\r
701 Facs = (EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *)(UINTN)PointerValue;\r
702\r
703 if (Facs->Length >= sizeof *Facs &&\r
704 Facs->Length <= Blob2Remaining &&\r
705 Facs->Signature ==\r
706 EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE) {\r
707 DEBUG ((EFI_D_VERBOSE, "found \"%-4.4a\" size 0x%x\n",\r
708 (CONST CHAR8 *)&Facs->Signature, Facs->Length));\r
709 TableSize = Facs->Length;\r
710 }\r
711 }\r
712\r
713 //\r
714 // check for the uniform tables\r
715 //\r
716 if (TableSize == 0 && sizeof *Header <= Blob2Remaining) {\r
717 Header = (EFI_ACPI_DESCRIPTION_HEADER *)(UINTN)PointerValue;\r
718\r
719 if (Header->Length >= sizeof *Header &&\r
720 Header->Length <= Blob2Remaining &&\r
721 CalculateSum8 ((CONST UINT8 *)Header, Header->Length) == 0) {\r
722 //\r
723 // This looks very much like an ACPI table from QEMU:\r
724 // - Length field consistent with both ACPI and containing blob size\r
725 // - checksum is correct\r
726 //\r
727 DEBUG ((EFI_D_VERBOSE, "found \"%-4.4a\" size 0x%x\n",\r
728 (CONST CHAR8 *)&Header->Signature, Header->Length));\r
729 TableSize = Header->Length;\r
730\r
731 //\r
732 // Skip RSDT and XSDT because those are handled by\r
733 // EFI_ACPI_TABLE_PROTOCOL automatically.\r
734 if (Header->Signature ==\r
735 EFI_ACPI_1_0_ROOT_SYSTEM_DESCRIPTION_TABLE_SIGNATURE ||\r
736 Header->Signature ==\r
737 EFI_ACPI_2_0_EXTENDED_SYSTEM_DESCRIPTION_TABLE_SIGNATURE) {\r
738 return EFI_SUCCESS;\r
739 }\r
740 }\r
741 }\r
742\r
743 if (TableSize == 0) {\r
744 DEBUG ((EFI_D_VERBOSE, "not found; marking fw_cfg blob as opaque\n"));\r
745 Blob2->HostsOnlyTableData = FALSE;\r
746 return EFI_SUCCESS;\r
747 }\r
748\r
749 if (*NumInstalled == INSTALLED_TABLES_MAX) {\r
750 DEBUG ((EFI_D_ERROR, "%a: can't install more than %d tables\n",\r
751 __FUNCTION__, INSTALLED_TABLES_MAX));\r
072060a6
PDJ
752 Status = EFI_OUT_OF_RESOURCES;\r
753 goto RollbackSeenPointer;\r
14b0faad
JJ
754 }\r
755\r
756 Status = AcpiProtocol->InstallAcpiTable (AcpiProtocol,\r
757 (VOID *)(UINTN)PointerValue, TableSize,\r
758 &InstalledKey[*NumInstalled]);\r
759 if (EFI_ERROR (Status)) {\r
760 DEBUG ((EFI_D_ERROR, "%a: InstallAcpiTable(): %r\n", __FUNCTION__,\r
761 Status));\r
072060a6 762 goto RollbackSeenPointer;\r
14b0faad
JJ
763 }\r
764 ++*NumInstalled;\r
765 return EFI_SUCCESS;\r
072060a6
PDJ
766\r
767RollbackSeenPointer:\r
768 OrderedCollectionDelete (SeenPointers, SeenPointerEntry, NULL);\r
769 return Status;\r
14b0faad
JJ
770}\r
771\r
772\r
773/**\r
774 Download, process, and install ACPI table data from the QEMU loader\r
775 interface.\r
776\r
777 @param[in] AcpiProtocol The ACPI table protocol used to install tables.\r
778\r
779 @retval EFI_UNSUPPORTED Firmware configuration is unavailable, or QEMU\r
780 loader command with unsupported parameters\r
781 has been found.\r
782\r
783 @retval EFI_NOT_FOUND The host doesn't export the required fw_cfg\r
784 files.\r
785\r
786 @retval EFI_OUT_OF_RESOURCES Memory allocation failed, or more than\r
787 INSTALLED_TABLES_MAX tables found.\r
788\r
789 @retval EFI_PROTOCOL_ERROR Found invalid fw_cfg contents.\r
790\r
791 @return Status codes returned by\r
792 AcpiProtocol->InstallAcpiTable().\r
793\r
794**/\r
795EFI_STATUS\r
796EFIAPI\r
f186536b 797InstallQemuFwCfgTables (\r
14b0faad
JJ
798 IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol\r
799 )\r
800{\r
801 EFI_STATUS Status;\r
802 FIRMWARE_CONFIG_ITEM FwCfgItem;\r
803 UINTN FwCfgSize;\r
804 QEMU_LOADER_ENTRY *LoaderStart;\r
805 CONST QEMU_LOADER_ENTRY *LoaderEntry, *LoaderEnd;\r
9965cbd4 806 CONST QEMU_LOADER_ENTRY *WritePointerSubsetEnd;\r
8f35eb92
LE
807 ORIGINAL_ATTRIBUTES *OriginalPciAttributes;\r
808 UINTN OriginalPciAttributesCount;\r
df73df13 809 S3_CONTEXT *S3Context;\r
14b0faad
JJ
810 ORDERED_COLLECTION *Tracker;\r
811 UINTN *InstalledKey;\r
812 INT32 Installed;\r
813 ORDERED_COLLECTION_ENTRY *TrackerEntry, *TrackerEntry2;\r
072060a6
PDJ
814 ORDERED_COLLECTION *SeenPointers;\r
815 ORDERED_COLLECTION_ENTRY *SeenPointerEntry, *SeenPointerEntry2;\r
14b0faad
JJ
816\r
817 Status = QemuFwCfgFindFile ("etc/table-loader", &FwCfgItem, &FwCfgSize);\r
818 if (EFI_ERROR (Status)) {\r
819 return Status;\r
820 }\r
821 if (FwCfgSize % sizeof *LoaderEntry != 0) {\r
822 DEBUG ((EFI_D_ERROR, "%a: \"etc/table-loader\" has invalid size 0x%Lx\n",\r
823 __FUNCTION__, (UINT64)FwCfgSize));\r
824 return EFI_PROTOCOL_ERROR;\r
825 }\r
826\r
827 LoaderStart = AllocatePool (FwCfgSize);\r
828 if (LoaderStart == NULL) {\r
829 return EFI_OUT_OF_RESOURCES;\r
830 }\r
8f35eb92 831 EnablePciDecoding (&OriginalPciAttributes, &OriginalPciAttributesCount);\r
14b0faad
JJ
832 QemuFwCfgSelectItem (FwCfgItem);\r
833 QemuFwCfgReadBytes (FwCfgSize, LoaderStart);\r
8f35eb92 834 RestorePciDecoding (OriginalPciAttributes, OriginalPciAttributesCount);\r
14b0faad
JJ
835 LoaderEnd = LoaderStart + FwCfgSize / sizeof *LoaderEntry;\r
836\r
df73df13
LE
837 S3Context = NULL;\r
838 if (QemuFwCfgS3Enabled ()) {\r
839 //\r
840 // Size the allocation pessimistically, assuming that all commands in the\r
841 // script are QEMU_LOADER_WRITE_POINTER commands.\r
842 //\r
843 Status = AllocateS3Context (&S3Context, LoaderEnd - LoaderStart);\r
844 if (EFI_ERROR (Status)) {\r
845 goto FreeLoader;\r
846 }\r
847 }\r
848\r
14b0faad
JJ
849 Tracker = OrderedCollectionInit (BlobCompare, BlobKeyCompare);\r
850 if (Tracker == NULL) {\r
851 Status = EFI_OUT_OF_RESOURCES;\r
df73df13 852 goto FreeS3Context;\r
14b0faad
JJ
853 }\r
854\r
855 //\r
856 // first pass: process the commands\r
857 //\r
9965cbd4
LE
858 // "WritePointerSubsetEnd" points one past the last successful\r
859 // QEMU_LOADER_WRITE_POINTER command. Now when we're about to start the first\r
860 // pass, no such command has been encountered yet.\r
861 //\r
862 WritePointerSubsetEnd = LoaderStart;\r
14b0faad
JJ
863 for (LoaderEntry = LoaderStart; LoaderEntry < LoaderEnd; ++LoaderEntry) {\r
864 switch (LoaderEntry->Type) {\r
865 case QemuLoaderCmdAllocate:\r
866 Status = ProcessCmdAllocate (&LoaderEntry->Command.Allocate, Tracker);\r
867 break;\r
868\r
869 case QemuLoaderCmdAddPointer:\r
870 Status = ProcessCmdAddPointer (&LoaderEntry->Command.AddPointer,\r
871 Tracker);\r
872 break;\r
873\r
874 case QemuLoaderCmdAddChecksum:\r
875 Status = ProcessCmdAddChecksum (&LoaderEntry->Command.AddChecksum,\r
876 Tracker);\r
877 break;\r
878\r
9965cbd4
LE
879 case QemuLoaderCmdWritePointer:\r
880 Status = ProcessCmdWritePointer (&LoaderEntry->Command.WritePointer,\r
df73df13 881 Tracker, S3Context);\r
9965cbd4
LE
882 if (!EFI_ERROR (Status)) {\r
883 WritePointerSubsetEnd = LoaderEntry + 1;\r
884 }\r
885 break;\r
886\r
14b0faad
JJ
887 default:\r
888 DEBUG ((EFI_D_VERBOSE, "%a: unknown loader command: 0x%x\n",\r
889 __FUNCTION__, LoaderEntry->Type));\r
890 break;\r
891 }\r
892\r
893 if (EFI_ERROR (Status)) {\r
9965cbd4 894 goto RollbackWritePointersAndFreeTracker;\r
14b0faad
JJ
895 }\r
896 }\r
897\r
898 InstalledKey = AllocatePool (INSTALLED_TABLES_MAX * sizeof *InstalledKey);\r
899 if (InstalledKey == NULL) {\r
900 Status = EFI_OUT_OF_RESOURCES;\r
9965cbd4 901 goto RollbackWritePointersAndFreeTracker;\r
14b0faad
JJ
902 }\r
903\r
072060a6
PDJ
904 SeenPointers = OrderedCollectionInit (PointerCompare, PointerCompare);\r
905 if (SeenPointers == NULL) {\r
906 Status = EFI_OUT_OF_RESOURCES;\r
907 goto FreeKeys;\r
908 }\r
909\r
14b0faad
JJ
910 //\r
911 // second pass: identify and install ACPI tables\r
912 //\r
913 Installed = 0;\r
914 for (LoaderEntry = LoaderStart; LoaderEntry < LoaderEnd; ++LoaderEntry) {\r
915 if (LoaderEntry->Type == QemuLoaderCmdAddPointer) {\r
072060a6
PDJ
916 Status = Process2ndPassCmdAddPointer (\r
917 &LoaderEntry->Command.AddPointer,\r
918 Tracker,\r
919 AcpiProtocol,\r
920 InstalledKey,\r
921 &Installed,\r
922 SeenPointers\r
923 );\r
14b0faad 924 if (EFI_ERROR (Status)) {\r
df73df13 925 goto UninstallAcpiTables;\r
14b0faad
JJ
926 }\r
927 }\r
928 }\r
929\r
df73df13
LE
930 //\r
931 // Translating the condensed QEMU_LOADER_WRITE_POINTER commands to ACPI S3\r
932 // Boot Script opcodes has to be the last operation in this function, because\r
933 // if it succeeds, it cannot be undone.\r
934 //\r
935 if (S3Context != NULL) {\r
936 Status = TransferS3ContextToBootScript (S3Context);\r
80576225
LE
937 if (EFI_ERROR (Status)) {\r
938 goto UninstallAcpiTables;\r
939 }\r
940 //\r
941 // Ownership of S3Context has been transfered.\r
942 //\r
943 S3Context = NULL;\r
df73df13
LE
944 }\r
945\r
946UninstallAcpiTables:\r
14b0faad
JJ
947 if (EFI_ERROR (Status)) {\r
948 //\r
949 // roll back partial installation\r
950 //\r
951 while (Installed > 0) {\r
952 --Installed;\r
953 AcpiProtocol->UninstallAcpiTable (AcpiProtocol, InstalledKey[Installed]);\r
954 }\r
955 } else {\r
956 DEBUG ((EFI_D_INFO, "%a: installed %d tables\n", __FUNCTION__, Installed));\r
957 }\r
958\r
072060a6
PDJ
959 for (SeenPointerEntry = OrderedCollectionMin (SeenPointers);\r
960 SeenPointerEntry != NULL;\r
961 SeenPointerEntry = SeenPointerEntry2) {\r
962 SeenPointerEntry2 = OrderedCollectionNext (SeenPointerEntry);\r
963 OrderedCollectionDelete (SeenPointers, SeenPointerEntry, NULL);\r
964 }\r
965 OrderedCollectionUninit (SeenPointers);\r
966\r
967FreeKeys:\r
14b0faad
JJ
968 FreePool (InstalledKey);\r
969\r
9965cbd4
LE
970RollbackWritePointersAndFreeTracker:\r
971 //\r
972 // In case of failure, revoke any allocation addresses that were communicated\r
973 // to QEMU previously, before we release all the blobs.\r
974 //\r
975 if (EFI_ERROR (Status)) {\r
976 LoaderEntry = WritePointerSubsetEnd;\r
977 while (LoaderEntry > LoaderStart) {\r
978 --LoaderEntry;\r
979 if (LoaderEntry->Type == QemuLoaderCmdWritePointer) {\r
980 UndoCmdWritePointer (&LoaderEntry->Command.WritePointer);\r
981 }\r
982 }\r
983 }\r
984\r
14b0faad
JJ
985 //\r
986 // Tear down the tracker infrastructure. Each fw_cfg blob will be left in\r
987 // place only if we're exiting with success and the blob hosts data that is\r
988 // not directly part of some ACPI table.\r
989 //\r
990 for (TrackerEntry = OrderedCollectionMin (Tracker); TrackerEntry != NULL;\r
991 TrackerEntry = TrackerEntry2) {\r
992 VOID *UserStruct;\r
993 BLOB *Blob;\r
994\r
995 TrackerEntry2 = OrderedCollectionNext (TrackerEntry);\r
996 OrderedCollectionDelete (Tracker, TrackerEntry, &UserStruct);\r
997 Blob = UserStruct;\r
998\r
999 if (EFI_ERROR (Status) || Blob->HostsOnlyTableData) {\r
1000 DEBUG ((EFI_D_VERBOSE, "%a: freeing \"%a\"\n", __FUNCTION__,\r
1001 Blob->File));\r
1002 gBS->FreePages ((UINTN)Blob->Base, EFI_SIZE_TO_PAGES (Blob->Size));\r
1003 }\r
1004 FreePool (Blob);\r
1005 }\r
1006 OrderedCollectionUninit (Tracker);\r
1007\r
df73df13
LE
1008FreeS3Context:\r
1009 if (S3Context != NULL) {\r
1010 ReleaseS3Context (S3Context);\r
1011 }\r
1012\r
14b0faad
JJ
1013FreeLoader:\r
1014 FreePool (LoaderStart);\r
1015\r
1016 return Status;\r
1017}\r