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