Hi Laszlo Your analysis below is exactly what the code implemented. Now I believe you and me have different understanding on PI specification, not the UDK implementation. :-)
"Normally this would be a violation of the protocol, because the pointer I'm passing to BootScript->Write() doesn't point to runtime memory or ACPI NVS." Per my understanding, the word in UEFI/PI specification is to limit the caller. So caller need follow UEFI/PI specification to make sure call the function correctly. I do not believe callee need to do any check in this case. It is caller's responsibility. You may also find some examples in UEFI spec, such as TPL. If caller did wrong thing, system may crash. "But the implementation should save the pointer that I pass in, not the pointed-to data." Again, since PI spec does not define format, an implementation can choose to save pointer or save data. Personally, I cannot draw to the conclusion that we must save pointer based on current PI specification. My personal understanding for PI specification is to focus on data. Because if PI specification just need save pointer, there is no need to pass InfomrationLength. This argument is not needed. Anyway, your suggest on "save pointer" is another possible implementation for Boot Script. You may add it in your own package and use the new one in your modules. That is possible, since UDK package supports library override. All in all, since there is different understanding on PI specification, if you want, I suggest we could discuss this topic in PIWG meeting - where we discuss the PI specification. Thank you Yao Jiewen -----Original Message----- From: Laszlo Ersek [mailto:ler...@redhat.com] Sent: Wednesday, December 04, 2013 3:51 AM To: Yao, Jiewen Cc: edk2-devel@lists.sourceforge.net Subject: Re: [edk2] Save State protocol violation in S3SaveState.c (MdeModulePkg) Hi! On 12/03/13 16:03, Yao, Jiewen wrote: > Hi Laszlo > "According to the PI spec, Information is a pointer pointing to > runtime memory or ACPI NVS, and the BootScript record will store the > pointer (ie. not the data pointed-to)." > I agree with you on first part - According to the PI spec, Information > is a pointer pointing to runtime memory or ACPI NVS. > And I hold different view on second part - the BootScript record will > store the pointer (ie. not the data pointed-to) I disagree (and I tested it too). My example code goes like: > EFI_STATUS Status; > EFI_S3_SAVE_STATE_PROTOCOL *BootScript; > STATIC CONST UINT8 Info[] = { 0xDE, 0xAD, 0xBE, 0xEF }; > > Status = gBS->LocateProtocol (&gEfiS3SaveStateProtocolGuid, NULL, > (VOID **) &BootScript); > ASSERT_EFI_ERROR (Status); > > // > // Despite the opcode documentation in the PI spec, the protocol > // implementation embeds a deep copy of the info in the boot script, rather > // than storing just a pointer to runtime or NVS storage. > // > Status = BootScript->Write(BootScript, EFI_BOOT_SCRIPT_INFORMATION_OPCODE, > (UINT32) sizeof Info, > (EFI_PHYSICAL_ADDRESS)(UINTN) &Info); > ASSERT_EFI_ERROR (Status); Normally this would be a violation of the protocol, because the pointer I'm passing to BootScript->Write() doesn't point to runtime memory or ACPI NVS. The implementation behind BootScript->Write() is the BootScriptWrite() function in "MdeModulePkg/Universal/Acpi/S3SaveStateDxe/S3SaveState.c". Relevant part: > case EFI_BOOT_SCRIPT_INFORMATION_OPCODE: > VA_START (Marker, OpCode); > Status = BootScriptWriteInformation (Marker); > VA_END (Marker); > break; The BootScriptWriteInformation() function says (still in the same file): > EFI_STATUS > BootScriptWriteInformation ( > IN VA_LIST Marker > ) > { > UINT32 InformationLength; > EFI_PHYSICAL_ADDRESS Information; > > InformationLength = VA_ARG (Marker, UINT32); > Information = VA_ARG (Marker, EFI_PHYSICAL_ADDRESS); > return S3BootScriptSaveInformation (InformationLength, > (VOID*)(UINTN)Information); } The retrieval of "InformationLength" and "Information" from the stack via the outer ellipsis / inner VA_LIST notation is correct. When calling S3BootScriptSaveInformation(), "Information" is converted to a pointer type. I agree that it is not dereferenced (yet), but this is already wrong, because the caller is entitled to pass in a 64-bit EFI_PHYSICAL_ADDRESS, and by converting it to UINTN (and then to VOID*) we could be truncating it, if the DXE phase is 32-bit. Anyway, this is not the main issue; let's suppose that the pointer passed to S3BootScriptSaveInformation() still carries the full EFI_PHYSICAL_ADDRESS. Then, in file "MdeModulePkg/Library/PiDxeS3BootScriptLib/BootScriptSave.c": > RETURN_STATUS > EFIAPI > S3BootScriptSaveInformation ( > IN UINT32 InformationLength, > IN VOID *Information > ) > { > UINT8 Length; > UINT8 *Script; > EFI_BOOT_SCRIPT_INFORMATION ScriptInformation; > > Length = (UINT8)(sizeof (EFI_BOOT_SCRIPT_INFORMATION) + > InformationLength); > > Script = S3BootScriptGetEntryAddAddress (Length); > if (Script == NULL) { > return RETURN_OUT_OF_RESOURCES; > } > // > // Build script data > // > ScriptInformation.OpCode = EFI_BOOT_SCRIPT_INFORMATION_OPCODE; > ScriptInformation.Length = Length; > > > ScriptInformation.InformationLength = InformationLength; > > CopyMem ((VOID*)Script, (VOID*)&ScriptInformation, sizeof > (EFI_BOOT_SCRIPT_INFORMATION)); > CopyMem ((VOID*)(Script + sizeof (EFI_BOOT_SCRIPT_INFORMATION)), > (VOID *) Information, (UINTN) InformationLength); > > SyncBootScript (Script); > > return RETURN_SUCCESS; > > } With my example in mind, the Length local variable is set to sizeof (EFI_BOOT_SCRIPT_INFORMATION) + 4 The "ScriptInformation" structure contains: OpCode = EFI_BOOT_SCRIPT_INFORMATION_OPCODE Length = sizeof (EFI_BOOT_SCRIPT_INFORMATION) + 4 InformationLength = 4 The first CopyMem() call copies this header to the beginning of the new entry in the boot script. The second CopyMem() call copies data into the same new entry, right behind the header. However, it does not copy the address that has been passed in -- it dereferences the address and copies the data. In my case, it copies the four bytes { 0xDE, 0xAD, 0xBE, 0xEF } into the entry. When replaying this entry after resume, the boot script executor calls into the same library, function S3BootScriptExecute(), file "MdeModulePkg/Library/PiDxeS3BootScriptLib/BootScriptExecute.c": S3BootScriptExecute() BootScriptExecuteInformation() > VOID > BootScriptExecuteInformation ( > IN UINT8 *Script > ) > > { > UINT32 Index; > EFI_BOOT_SCRIPT_INFORMATION Information; > UINT8 *InformationData; > > CopyMem ((VOID*)&Information, (VOID*)Script, > sizeof(EFI_BOOT_SCRIPT_INFORMATION)); > > InformationData = Script + sizeof (EFI_BOOT_SCRIPT_INFORMATION); > DEBUG ((EFI_D_INFO, "BootScriptExecuteInformation - 0x%08x\n", > (UINTN) InformationData)); > > DEBUG ((EFI_D_INFO, "BootScriptInformation: ")); > for (Index = 0; Index < Information.InformationLength; Index++) { > DEBUG ((EFI_D_INFO, "%02x ", InformationData[Index])); > } > DEBUG ((EFI_D_INFO, "\n")); > } The replay function for the INFORMATION opcode first copies out the opcode header from the script entry. It finds the start of the information after the header. Then it dumps the data included in the entry, right after the opcode header. In my case it reads: > S3BootScriptExecute: > TableHeader - 0x9C767000 > TableHeader.TableLength - 0x0000001B > ExecuteBootScript - 9C76700D > EFI_BOOT_SCRIPT_INFORMATION_OPCODE > BootScriptExecuteInformation - 0x9C767014 > BootScriptInformation: DE AD BE EF > ExecuteBootScript - 9C767018 > S3_BOOT_SCRIPT_LIB_TERMINATE_OPCODE > S3BootScriptDone - Success The bytes DE AD BE EF are the contents of my array (saved still before the suspend). They are not the address of the array. > NOTE: PI spec does not say anything on boot script format in memory, > this is implementation specific. I agree. But the implementation should save the pointer that I pass in, not the pointed-to data. > For security reason, BIOS S3 module will try its best to save the all > S3 data to lockbox, so current S3 module implementation purposely save > the information part into boot script and into lockbox later. Ah, that's actually a good reason. It deviates from the spec, but I can understand the idea. > Personally, I believe this implementation still follows PI spec, since > Boot Script format is NOT defined by PI spec. It's not the format but how it works. Consider the following: according to the spec, I should be able to: (1) allocate an area in Acpi NVS memory, (2) zero it out, (3) push the address to the boot script with an INFO opcode, (4) *change* the contents of the area. (5) suspend and resume. According to the spec, after resumption I should see the contents written in (4), not the zeroes written in (2). > "The current code also limits the size of the information to less than > 255 bytes, and makes the InformationLength argument redundant." > I agree with you on first part - The current code also limits the size > of the information to less than 255 bytes. And I hold different view > on second part - makes the InformationLength argument redundant. > > Right, current implementation does have such limitation. If you have > concern, I agree to fix 255 bytes limitation. You would have to change the internal header format to do that. Currently EFI_BOOT_SCRIPT_INFORMATION.Length is just an UINT8. > S3 module does use InformationLength field in current implementation. > Would you please point out why it is "redundant"? Because of these assignments in S3BootScriptSaveInformation(): > Length = (UINT8)(sizeof (EFI_BOOT_SCRIPT_INFORMATION) + InformationLength); > ScriptInformation.Length = Length; > ScriptInformation.InformationLength = InformationLength; The Length and the InformationLength fields differ only in a constant. The EFI_BOOT_SCRIPT_INFORMATION header structure is actually correct, it is just not used correctly. The current code does this: +--------+--------+--------+--------+--------+--------+--------+-------------- ... ---------------+ | OpCode | Length | InformationLength | deep copy of user specified data | +--------+--------+--------+--------+--------+--------+--------+-------------- ... ---------------+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^|^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ sizeof (EFI_BOOT_SCRIPT_INFORMATION) bytes | InformationLength bytes ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Length = sizeof (EFI_BOOT_SCRIPT_INFORMATION) + InformationLength bytes The Length and InformationLength fields carry the same information, you can derive one from the other, in either direction. This is why one is redundant. The intended use would look like: +--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+ | OpCode | Length | InformationLength | EFI_PHYSICAL_ADDRESS |----+ +--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^|^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | sizeof (EFI_BOOT_SCRIPT_INFORMATION) bytes | 8 bytes exactly | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | Length = sizeof (EFI_BOOT_SCRIPT_INFORMATION) + 8 == 7 + 8 == 15 bytes, always | | | +--------- ... -----------+ | | User data in ACPI NVS | <---------------------------------------------------------------------------------------------------------------+ +--------- ... -----------+ ^^^^^^^^^^^^^^^^^^^^^^^^^ InformationLength bytes The replay function should check (using the Length field) that the payload is exactly the size of EFI_PHYSICAL_ADDRESS. Then it should CopyMem() the payload out into an EFI_PHYSICAL_ADDRESS variable, convert that to a pointer, and *dereference* it. And dump the data that's there. Thanks, Laszlo ------------------------------------------------------------------------------ Sponsored by Intel(R) XDK Develop, test and display web and hybrid apps with a single code base. Download it for free now! http://pubads.g.doubleclick.net/gampad/clk?id=111408631&iu=/4140/ostg.clktrk _______________________________________________ edk2-devel mailing list edk2-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/edk2-devel