2 Append an ACPI S3 Boot Script fragment from the QEMU_LOADER_WRITE_POINTER
3 commands of QEMU's fully processed table linker/loader script.
5 Copyright (C) 2017, Red Hat, Inc.
7 This program and the accompanying materials are licensed and made available
8 under the terms and conditions of the BSD License which accompanies this
9 distribution. The full text of the license may be found at
10 http://opensource.org/licenses/bsd-license.php
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
13 WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
16 #include <Library/MemoryAllocationLib.h>
17 #include <Library/QemuFwCfgLib.h>
18 #include <Protocol/S3SaveState.h>
20 #include "AcpiPlatform.h"
24 // Condensed structure for capturing the fw_cfg operations -- select, skip,
25 // write -- inherent in executing a QEMU_LOADER_WRITE_POINTER command.
28 UINT16 PointerItem
; // resolved from QEMU_LOADER_WRITE_POINTER.PointerFile
29 UINT8 PointerSize
; // copied as-is from QEMU_LOADER_WRITE_POINTER
30 UINT32 PointerOffset
; // copied as-is from QEMU_LOADER_WRITE_POINTER
31 UINT64 PointerValue
; // resolved from QEMU_LOADER_WRITE_POINTER.PointeeFile
32 // and QEMU_LOADER_WRITE_POINTER.PointeeOffset
33 } CONDENSED_WRITE_POINTER
;
37 // Context structure to accumulate CONDENSED_WRITE_POINTER objects from
38 // QEMU_LOADER_WRITE_POINTER commands.
40 // Any pointers in this structure own the pointed-to objects; that is, when the
41 // context structure is released, all pointed-to objects must be released too.
44 CONDENSED_WRITE_POINTER
*WritePointers
; // one array element per processed
45 // QEMU_LOADER_WRITE_POINTER
47 UINTN Allocated
; // number of elements allocated for
49 UINTN Used
; // number of elements populated in
55 // Scratch buffer, allocated in EfiReservedMemoryType type memory, for the ACPI
56 // S3 Boot Script opcodes to work on. We use the buffer to compose and to
57 // replay several fw_cfg select+skip and write operations, using the DMA access
58 // method. The fw_cfg operations will implement the actions dictated by
59 // CONDENSED_WRITE_POINTER objects.
63 FW_CFG_DMA_ACCESS Access
; // filled in from
64 // CONDENSED_WRITE_POINTER.PointerItem,
65 // CONDENSED_WRITE_POINTER.PointerSize,
66 // CONDENSED_WRITE_POINTER.PointerOffset
67 UINT64 PointerValue
; // filled in from
68 // CONDENSED_WRITE_POINTER.PointerValue
74 Allocate an S3_CONTEXT object.
76 @param[out] S3Context The allocated S3_CONTEXT object is returned
77 through this parameter.
79 @param[in] WritePointerCount Number of CONDENSED_WRITE_POINTER elements to
80 allocate room for. WritePointerCount must be
83 @retval EFI_SUCCESS Allocation successful.
85 @retval EFI_OUT_OF_RESOURCES Out of memory.
87 @retval EFI_INVALID_PARAMETER WritePointerCount is zero.
91 OUT S3_CONTEXT
**S3Context
,
92 IN UINTN WritePointerCount
98 if (WritePointerCount
== 0) {
99 return EFI_INVALID_PARAMETER
;
102 Context
= AllocateZeroPool (sizeof *Context
);
103 if (Context
== NULL
) {
104 return EFI_OUT_OF_RESOURCES
;
107 Context
->WritePointers
= AllocatePool (WritePointerCount
*
108 sizeof *Context
->WritePointers
);
109 if (Context
->WritePointers
== NULL
) {
110 Status
= EFI_OUT_OF_RESOURCES
;
114 Context
->Allocated
= WritePointerCount
;
115 *S3Context
= Context
;
126 Release an S3_CONTEXT object.
128 @param[in] S3Context The object to release.
132 IN S3_CONTEXT
*S3Context
135 FreePool (S3Context
->WritePointers
);
136 FreePool (S3Context
);
141 Save the information necessary to replicate a QEMU_LOADER_WRITE_POINTER
142 command during S3 resume, in condensed format.
144 This function is to be called from ProcessCmdWritePointer(), after all the
145 sanity checks have passed, and before the fw_cfg operations are performed.
147 @param[in,out] S3Context The S3_CONTEXT object into which the caller wants
148 to save the information that was derived from
149 QEMU_LOADER_WRITE_POINTER.
151 @param[in] PointerItem The FIRMWARE_CONFIG_ITEM that
152 QEMU_LOADER_WRITE_POINTER.PointerFile was resolved
153 to, expressed as a UINT16 value.
155 @param[in] PointerSize Copied directly from
156 QEMU_LOADER_WRITE_POINTER.PointerSize.
158 @param[in] PointerOffset Copied directly from
159 QEMU_LOADER_WRITE_POINTER.PointerOffset.
161 @param[in] PointerValue The base address of the allocated / downloaded
162 fw_cfg blob that is identified by
163 QEMU_LOADER_WRITE_POINTER.PointeeFile, plus
164 QEMU_LOADER_WRITE_POINTER.PointeeOffset.
166 @retval EFI_SUCCESS The information derived from
167 QEMU_LOADER_WRITE_POINTER has been successfully
168 absorbed into S3Context.
170 @retval EFI_OUT_OF_RESOURCES No room available in S3Context.
173 SaveCondensedWritePointerToS3Context (
174 IN OUT S3_CONTEXT
*S3Context
,
175 IN UINT16 PointerItem
,
176 IN UINT8 PointerSize
,
177 IN UINT32 PointerOffset
,
178 IN UINT64 PointerValue
181 CONDENSED_WRITE_POINTER
*Condensed
;
183 if (S3Context
->Used
== S3Context
->Allocated
) {
184 return EFI_OUT_OF_RESOURCES
;
186 Condensed
= S3Context
->WritePointers
+ S3Context
->Used
;
187 Condensed
->PointerItem
= PointerItem
;
188 Condensed
->PointerSize
= PointerSize
;
189 Condensed
->PointerOffset
= PointerOffset
;
190 Condensed
->PointerValue
= PointerValue
;
191 DEBUG ((DEBUG_VERBOSE
, "%a: 0x%04x/[0x%08x+%d] := 0x%Lx (%Lu)\n",
192 __FUNCTION__
, PointerItem
, PointerOffset
, PointerSize
, PointerValue
,
193 (UINT64
)S3Context
->Used
));
200 Translate and append the information from an S3_CONTEXT object to the ACPI S3
203 The effects of a successful call to this function cannot be undone.
205 @param[in] S3Context The S3_CONTEXT object to translate to ACPI S3 Boot
208 @retval EFI_OUT_OF_RESOURCES Out of memory.
210 @retval EFI_SUCCESS The translation of S3Context to ACPI S3 Boot
211 Script opcodes has been successful.
213 @return Error codes from underlying functions.
216 TransferS3ContextToBootScript (
217 IN CONST S3_CONTEXT
*S3Context
221 EFI_S3_SAVE_STATE_PROTOCOL
*S3SaveState
;
222 SCRATCH_BUFFER
*ScratchBuffer
;
223 FW_CFG_DMA_ACCESS
*Access
;
224 UINT64 BigEndianAddressOfAccess
;
225 UINT32 ControlPollData
;
226 UINT32 ControlPollMask
;
230 // If the following protocol lookup fails, it shall not happen due to an
231 // unexpected DXE driver dispatch order.
233 // Namely, this function is only invoked on QEMU. Therefore it is only
234 // reached after Platform BDS signals gRootBridgesConnectedEventGroupGuid
235 // (see OnRootBridgesConnected() in "EntryPoint.c"). Hence, because
236 // TransferS3ContextToBootScript() is invoked in BDS, all DXE drivers,
237 // including S3SaveStateDxe (producing EFI_S3_SAVE_STATE_PROTOCOL), have been
238 // dispatched by the time we get here. (S3SaveStateDxe is not expected to
239 // have any stricter-than-TRUE DEPEX -- not a DEPEX that gets unblocked only
240 // within BDS anyway.)
242 // Reaching this function also depends on QemuFwCfgS3Enabled(). That implies
243 // S3SaveStateDxe has not exited immediately due to S3 being disabled. Thus
244 // EFI_S3_SAVE_STATE_PROTOCOL can only be missing for genuinely unforeseeable
247 Status
= gBS
->LocateProtocol (&gEfiS3SaveStateProtocolGuid
,
248 NULL
/* Registration */, (VOID
**)&S3SaveState
);
249 if (EFI_ERROR (Status
)) {
250 DEBUG ((DEBUG_ERROR
, "%a: LocateProtocol(): %r\n", __FUNCTION__
, Status
));
254 ScratchBuffer
= AllocateReservedPool (sizeof *ScratchBuffer
);
255 if (ScratchBuffer
== NULL
) {
256 return EFI_OUT_OF_RESOURCES
;
260 // Set up helper variables that we'll use identically for all
261 // CONDENSED_WRITE_POINTER elements.
263 Access
= &ScratchBuffer
->Access
;
264 BigEndianAddressOfAccess
= SwapBytes64 ((UINTN
)Access
);
266 ControlPollMask
= MAX_UINT32
;
269 // For each CONDENSED_WRITE_POINTER, we need six ACPI S3 Boot Script opcodes:
270 // (1) restore an FW_CFG_DMA_ACCESS object in reserved memory that selects
271 // the writeable fw_cfg file PointerFile (through PointerItem), and skips
272 // to PointerOffset in it,
273 // (2) call QEMU with the FW_CFG_DMA_ACCESS object,
274 // (3) wait for the select+skip to finish,
275 // (4) restore a SCRATCH_BUFFER object in reserved memory that writes
276 // PointerValue (base address of the allocated / downloaded PointeeFile,
277 // plus PointeeOffset), of size PointerSize, into the fw_cfg file
278 // selected in (1), at the offset sought to in (1),
279 // (5) call QEMU with the FW_CFG_DMA_ACCESS object,
280 // (6) wait for the write to finish.
282 // EFI_S3_SAVE_STATE_PROTOCOL does not allow rolling back opcode additions,
283 // therefore we treat any failure here as fatal.
285 for (Index
= 0; Index
< S3Context
->Used
; ++Index
) {
286 CONST CONDENSED_WRITE_POINTER
*Condensed
;
288 Condensed
= &S3Context
->WritePointers
[Index
];
291 // (1) restore an FW_CFG_DMA_ACCESS object in reserved memory that selects
292 // the writeable fw_cfg file PointerFile (through PointerItem), and
293 // skips to PointerOffset in it,
295 Access
->Control
= SwapBytes32 ((UINT32
)Condensed
->PointerItem
<< 16 |
296 FW_CFG_DMA_CTL_SELECT
| FW_CFG_DMA_CTL_SKIP
);
297 Access
->Length
= SwapBytes32 (Condensed
->PointerOffset
);
299 Status
= S3SaveState
->Write (
301 EFI_BOOT_SCRIPT_MEM_WRITE_OPCODE
, // OpCode
302 EfiBootScriptWidthUint8
, // Width
303 (UINT64
)(UINTN
)Access
, // Address
304 sizeof *Access
, // Count
307 if (EFI_ERROR (Status
)) {
308 DEBUG ((DEBUG_ERROR
, "%a: Index %Lu opcode 1: %r\n", __FUNCTION__
,
309 (UINT64
)Index
, Status
));
314 // (2) call QEMU with the FW_CFG_DMA_ACCESS object,
316 Status
= S3SaveState
->Write (
318 EFI_BOOT_SCRIPT_IO_WRITE_OPCODE
, // OpCode
319 EfiBootScriptWidthUint32
, // Width
320 (UINT64
)FW_CFG_IO_DMA_ADDRESS
, // Address
322 &BigEndianAddressOfAccess
// Buffer
324 if (EFI_ERROR (Status
)) {
325 DEBUG ((DEBUG_ERROR
, "%a: Index %Lu opcode 2: %r\n", __FUNCTION__
,
326 (UINT64
)Index
, Status
));
331 // (3) wait for the select+skip to finish,
333 Status
= S3SaveState
->Write (
335 EFI_BOOT_SCRIPT_MEM_POLL_OPCODE
, // OpCode
336 EfiBootScriptWidthUint32
, // Width
337 (UINT64
)(UINTN
)&Access
->Control
, // Address
338 &ControlPollData
, // Data
339 &ControlPollMask
, // DataMask
342 if (EFI_ERROR (Status
)) {
343 DEBUG ((DEBUG_ERROR
, "%a: Index %Lu opcode 3: %r\n", __FUNCTION__
,
344 (UINT64
)Index
, Status
));
349 // (4) restore a SCRATCH_BUFFER object in reserved memory that writes
350 // PointerValue (base address of the allocated / downloaded
351 // PointeeFile, plus PointeeOffset), of size PointerSize, into the
352 // fw_cfg file selected in (1), at the offset sought to in (1),
354 Access
->Control
= SwapBytes32 (FW_CFG_DMA_CTL_WRITE
);
355 Access
->Length
= SwapBytes32 (Condensed
->PointerSize
);
356 Access
->Address
= SwapBytes64 ((UINTN
)&ScratchBuffer
->PointerValue
);
357 ScratchBuffer
->PointerValue
= Condensed
->PointerValue
;
358 Status
= S3SaveState
->Write (
360 EFI_BOOT_SCRIPT_MEM_WRITE_OPCODE
, // OpCode
361 EfiBootScriptWidthUint8
, // Width
362 (UINT64
)(UINTN
)ScratchBuffer
, // Address
363 sizeof *ScratchBuffer
, // Count
364 ScratchBuffer
// Buffer
366 if (EFI_ERROR (Status
)) {
367 DEBUG ((DEBUG_ERROR
, "%a: Index %Lu opcode 4: %r\n", __FUNCTION__
,
368 (UINT64
)Index
, Status
));
373 // (5) call QEMU with the FW_CFG_DMA_ACCESS object,
375 Status
= S3SaveState
->Write (
377 EFI_BOOT_SCRIPT_IO_WRITE_OPCODE
, // OpCode
378 EfiBootScriptWidthUint32
, // Width
379 (UINT64
)FW_CFG_IO_DMA_ADDRESS
, // Address
381 &BigEndianAddressOfAccess
// Buffer
383 if (EFI_ERROR (Status
)) {
384 DEBUG ((DEBUG_ERROR
, "%a: Index %Lu opcode 5: %r\n", __FUNCTION__
,
385 (UINT64
)Index
, Status
));
390 // (6) wait for the write to finish.
392 Status
= S3SaveState
->Write (
394 EFI_BOOT_SCRIPT_MEM_POLL_OPCODE
, // OpCode
395 EfiBootScriptWidthUint32
, // Width
396 (UINT64
)(UINTN
)&Access
->Control
, // Address
397 &ControlPollData
, // Data
398 &ControlPollMask
, // DataMask
401 if (EFI_ERROR (Status
)) {
402 DEBUG ((DEBUG_ERROR
, "%a: Index %Lu opcode 6: %r\n", __FUNCTION__
,
403 (UINT64
)Index
, Status
));
408 DEBUG ((DEBUG_VERBOSE
, "%a: boot script fragment saved, ScratchBuffer=%p\n",
409 __FUNCTION__
, (VOID
*)ScratchBuffer
));