From d6671b1ccc70b11d1d5ab3362ecc838eef03fb4d Mon Sep 17 00:00:00 2001 From: Jon Maloy Date: Sat, 3 Feb 2024 15:24:15 -0500 Subject: [PATCH] * Sat Feb 03 2024 Jon Maloy - 20220126gitbb1bba3d77-10 - edk2-OvmfPkg-VirtNorFlashDxe-clone-ArmPlatformPkg-s-NOR-f.patch [RHEL-17587] - edk2-OvmfPkg-VirtNorFlashDxe-remove-CheckBlockLocked-feat.patch [RHEL-17587] - edk2-OvmfPkg-VirtNorFlashDxe-remove-disk-I-O-protocol-imp.patch [RHEL-17587] - edk2-OvmfPkg-VirtNorFlashDxe-drop-block-I-O-protocol-impl.patch [RHEL-17587] - edk2-OvmfPkg-VirtNorFlashDxe-avoid-array-mode-switch-afte.patch [RHEL-17587] - edk2-OvmfPkg-VirtNorFlashDxe-avoid-switching-between-mode.patch [RHEL-17587] - edk2-OvmfPkg-VirtNorFlashDxe-use-EFI_MEMORY_WC-and-drop-A.patch [RHEL-17587] - edk2-OvmfPkg-VirtNorFlashDxe-map-flash-memory-as-uncachea.patch [RHEL-17587] - edk2-OvmfPkg-VirtNorFlashDxe-stop-accepting-gEfiVariable2.patch [RHEL-17587] - edk2-OvmfPkg-VirtNorFlashDxe-sanity-check-variable2.patch [RHEL-17587] - edk2-OvmfPkg-VirtNorFlashDxe-add-casts-to-UINTN-and-UINT3.patch [RHEL-17587] - edk2-OvmfPkg-VirtNorFlashDxe-clarify-block-write-logic-fi.patch [RHEL-17587] - edk2-OvmfPkg-VirtNorFlashDxe-add-a-loop-for-NorFlashWrite.patch [RHEL-17587] - edk2-OvmfPkg-VirtNorFlashDxe-allow-larger-writes-without-.patch [RHEL-17587] - edk2-OvmfPkg-VirtNorFlashDxe-ValidateFvHeader-unwritten-s.patch [RHEL-17587] - edk2-OvmfPkg-VirtNorFlashDxe-move-DoErase-code-block-into.patch [RHEL-17587] - edk2-ArmVirtPkg-ArmVirtQemu-migrate-to-OVMF-s-VirtNorFlas.patch [RHEL-17587] - edk2-OvmfPkg-clone-NorFlashPlatformLib-into-VirtNorFlashP.patch [RHEL-17587] - Resolves: RHEL-17587 ([rhel8] guest fails to boot due to ASSERT error) --- ...rtQemu-migrate-to-OVMF-s-VirtNorFlas.patch | 149 + ...lashDxe-ValidateFvHeader-unwritten-s.patch | 47 + ...lashDxe-add-a-loop-for-NorFlashWrite.patch | 73 + ...lashDxe-add-casts-to-UINTN-and-UINT3.patch | 55 + ...lashDxe-allow-larger-writes-without-.patch | 65 + ...lashDxe-avoid-array-mode-switch-afte.patch | 89 + ...lashDxe-avoid-switching-between-mode.patch | 303 ++ ...lashDxe-clarify-block-write-logic-fi.patch | 110 + ...lashDxe-clone-ArmPlatformPkg-s-NOR-f.patch | 2973 +++++++++++++++++ ...lashDxe-drop-block-I-O-protocol-impl.patch | 504 +++ ...lashDxe-map-flash-memory-as-uncachea.patch | 67 + ...lashDxe-move-DoErase-code-block-into.patch | 131 + ...lashDxe-remove-CheckBlockLocked-feat.patch | 94 + ...lashDxe-remove-disk-I-O-protocol-imp.patch | 386 +++ ...rtNorFlashDxe-sanity-check-variable2.patch | 216 ++ ...lashDxe-stop-accepting-gEfiVariable2.patch | 49 + ...lashDxe-use-EFI_MEMORY_WC-and-drop-A.patch | 150 + ...rFlashPlatformLib-into-VirtNorFlashP.patch | 80 + edk2.spec | 60 +- 19 files changed, 5600 insertions(+), 1 deletion(-) create mode 100644 edk2-ArmVirtPkg-ArmVirtQemu-migrate-to-OVMF-s-VirtNorFlas.patch create mode 100644 edk2-OvmfPkg-VirtNorFlashDxe-ValidateFvHeader-unwritten-s.patch create mode 100644 edk2-OvmfPkg-VirtNorFlashDxe-add-a-loop-for-NorFlashWrite.patch create mode 100644 edk2-OvmfPkg-VirtNorFlashDxe-add-casts-to-UINTN-and-UINT3.patch create mode 100644 edk2-OvmfPkg-VirtNorFlashDxe-allow-larger-writes-without-.patch create mode 100644 edk2-OvmfPkg-VirtNorFlashDxe-avoid-array-mode-switch-afte.patch create mode 100644 edk2-OvmfPkg-VirtNorFlashDxe-avoid-switching-between-mode.patch create mode 100644 edk2-OvmfPkg-VirtNorFlashDxe-clarify-block-write-logic-fi.patch create mode 100644 edk2-OvmfPkg-VirtNorFlashDxe-clone-ArmPlatformPkg-s-NOR-f.patch create mode 100644 edk2-OvmfPkg-VirtNorFlashDxe-drop-block-I-O-protocol-impl.patch create mode 100644 edk2-OvmfPkg-VirtNorFlashDxe-map-flash-memory-as-uncachea.patch create mode 100644 edk2-OvmfPkg-VirtNorFlashDxe-move-DoErase-code-block-into.patch create mode 100644 edk2-OvmfPkg-VirtNorFlashDxe-remove-CheckBlockLocked-feat.patch create mode 100644 edk2-OvmfPkg-VirtNorFlashDxe-remove-disk-I-O-protocol-imp.patch create mode 100644 edk2-OvmfPkg-VirtNorFlashDxe-sanity-check-variable2.patch create mode 100644 edk2-OvmfPkg-VirtNorFlashDxe-stop-accepting-gEfiVariable2.patch create mode 100644 edk2-OvmfPkg-VirtNorFlashDxe-use-EFI_MEMORY_WC-and-drop-A.patch create mode 100644 edk2-OvmfPkg-clone-NorFlashPlatformLib-into-VirtNorFlashP.patch diff --git a/edk2-ArmVirtPkg-ArmVirtQemu-migrate-to-OVMF-s-VirtNorFlas.patch b/edk2-ArmVirtPkg-ArmVirtQemu-migrate-to-OVMF-s-VirtNorFlas.patch new file mode 100644 index 0000000..43848d5 --- /dev/null +++ b/edk2-ArmVirtPkg-ArmVirtQemu-migrate-to-OVMF-s-VirtNorFlas.patch @@ -0,0 +1,149 @@ +From 9ef10bbe9a03f22aa5c5ff659012794d37ef9839 Mon Sep 17 00:00:00 2001 +From: Ard Biesheuvel +Date: Mon, 24 Oct 2022 18:41:22 +0200 +Subject: [PATCH 17/18] ArmVirtPkg/ArmVirtQemu: migrate to OVMF's + VirtNorFlashDxe + +RH-Author: Gerd Hoffmann +RH-MergeRequest: 43: OvmfPkg/VirtNorFlashDxe backport +RH-Jira: RHEL-17587 +RH-Acked-by: Laszlo Ersek +RH-Commit: [19/20] 2160140b0ea566451ab723e941d2ab91e1ad874e + +Switch to the virt specific NorFlashDxe driver implementation that was +added recently. + +Signed-off-by: Ard Biesheuvel +Reviewed-by: Sunil V L +(cherry picked from commit b92298af8218dd074c231947bc95f2be94af663c) +--- + ArmVirtPkg/ArmVirtQemu.dsc | 4 ++-- + ArmVirtPkg/ArmVirtQemuFvMain.fdf.inc | 2 +- + ArmVirtPkg/ArmVirtQemuKernel.dsc | 4 ++-- + ArmVirtPkg/Library/NorFlashQemuLib/NorFlashQemuLib.c | 12 ++++++------ + .../Library/NorFlashQemuLib/NorFlashQemuLib.inf | 4 ++-- + 5 files changed, 13 insertions(+), 13 deletions(-) + +diff --git a/ArmVirtPkg/ArmVirtQemu.dsc b/ArmVirtPkg/ArmVirtQemu.dsc +index e6fad9f066..2b23becf30 100644 +--- a/ArmVirtPkg/ArmVirtQemu.dsc ++++ b/ArmVirtPkg/ArmVirtQemu.dsc +@@ -67,7 +67,7 @@ + ArmPlatformLib|ArmPlatformPkg/Library/ArmPlatformLibNull/ArmPlatformLibNull.inf + + TimerLib|ArmPkg/Library/ArmArchTimerLib/ArmArchTimerLib.inf +- NorFlashPlatformLib|ArmVirtPkg/Library/NorFlashQemuLib/NorFlashQemuLib.inf ++ VirtNorFlashPlatformLib|ArmVirtPkg/Library/NorFlashQemuLib/NorFlashQemuLib.inf + + CapsuleLib|MdeModulePkg/Library/DxeCapsuleLibNull/DxeCapsuleLibNull.inf + BootLogoLib|MdeModulePkg/Library/BootLogoLib/BootLogoLib.inf +@@ -400,7 +400,7 @@ + + NULL|ArmVirtPkg/Library/ArmVirtTimerFdtClientLib/ArmVirtTimerFdtClientLib.inf + } +- ArmPlatformPkg/Drivers/NorFlashDxe/NorFlashDxe.inf ++ OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.inf + MdeModulePkg/Universal/WatchdogTimerDxe/WatchdogTimer.inf + + # +diff --git a/ArmVirtPkg/ArmVirtQemuFvMain.fdf.inc b/ArmVirtPkg/ArmVirtQemuFvMain.fdf.inc +index f6a538df72..7c655d384d 100644 +--- a/ArmVirtPkg/ArmVirtQemuFvMain.fdf.inc ++++ b/ArmVirtPkg/ArmVirtQemuFvMain.fdf.inc +@@ -73,7 +73,7 @@ READ_LOCK_STATUS = TRUE + + INF ArmPkg/Drivers/ArmGic/ArmGicDxe.inf + INF ArmPkg/Drivers/TimerDxe/TimerDxe.inf +- INF ArmPlatformPkg/Drivers/NorFlashDxe/NorFlashDxe.inf ++ INF OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.inf + INF MdeModulePkg/Universal/WatchdogTimerDxe/WatchdogTimer.inf + + # +diff --git a/ArmVirtPkg/ArmVirtQemuKernel.dsc b/ArmVirtPkg/ArmVirtQemuKernel.dsc +index 656c9d99a3..344e2c4ed9 100644 +--- a/ArmVirtPkg/ArmVirtQemuKernel.dsc ++++ b/ArmVirtPkg/ArmVirtQemuKernel.dsc +@@ -65,7 +65,7 @@ + ArmVirtMemInfoLib|ArmVirtPkg/Library/QemuVirtMemInfoLib/QemuVirtMemInfoLib.inf + + TimerLib|ArmPkg/Library/ArmArchTimerLib/ArmArchTimerLib.inf +- NorFlashPlatformLib|ArmVirtPkg/Library/NorFlashQemuLib/NorFlashQemuLib.inf ++ VirtNorFlashPlatformLib|ArmVirtPkg/Library/NorFlashQemuLib/NorFlashQemuLib.inf + + CapsuleLib|MdeModulePkg/Library/DxeCapsuleLibNull/DxeCapsuleLibNull.inf + BootLogoLib|MdeModulePkg/Library/BootLogoLib/BootLogoLib.inf +@@ -329,7 +329,7 @@ + + NULL|ArmVirtPkg/Library/ArmVirtTimerFdtClientLib/ArmVirtTimerFdtClientLib.inf + } +- ArmPlatformPkg/Drivers/NorFlashDxe/NorFlashDxe.inf ++ OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.inf + MdeModulePkg/Universal/WatchdogTimerDxe/WatchdogTimer.inf + + # +diff --git a/ArmVirtPkg/Library/NorFlashQemuLib/NorFlashQemuLib.c b/ArmVirtPkg/Library/NorFlashQemuLib/NorFlashQemuLib.c +index 271d7f0efb..93a2fed40f 100644 +--- a/ArmVirtPkg/Library/NorFlashQemuLib/NorFlashQemuLib.c ++++ b/ArmVirtPkg/Library/NorFlashQemuLib/NorFlashQemuLib.c +@@ -8,8 +8,8 @@ + + #include + #include +-#include + #include ++#include + + #include + +@@ -18,19 +18,19 @@ + #define MAX_FLASH_BANKS 4 + + EFI_STATUS +-NorFlashPlatformInitialization ( ++VirtNorFlashPlatformInitialization ( + VOID + ) + { + return EFI_SUCCESS; + } + +-NOR_FLASH_DESCRIPTION mNorFlashDevices[MAX_FLASH_BANKS]; ++STATIC VIRT_NOR_FLASH_DESCRIPTION mNorFlashDevices[MAX_FLASH_BANKS]; + + EFI_STATUS +-NorFlashPlatformGetDevices ( +- OUT NOR_FLASH_DESCRIPTION **NorFlashDescriptions, +- OUT UINT32 *Count ++VirtNorFlashPlatformGetDevices ( ++ OUT VIRT_NOR_FLASH_DESCRIPTION **NorFlashDescriptions, ++ OUT UINT32 *Count + ) + { + FDT_CLIENT_PROTOCOL *FdtClient; +diff --git a/ArmVirtPkg/Library/NorFlashQemuLib/NorFlashQemuLib.inf b/ArmVirtPkg/Library/NorFlashQemuLib/NorFlashQemuLib.inf +index 4c3683bf5d..a6b5865be9 100644 +--- a/ArmVirtPkg/Library/NorFlashQemuLib/NorFlashQemuLib.inf ++++ b/ArmVirtPkg/Library/NorFlashQemuLib/NorFlashQemuLib.inf +@@ -14,17 +14,17 @@ + FILE_GUID = 339B7829-4C5F-4EFC-B2DD-5050E530DECE + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 +- LIBRARY_CLASS = NorFlashPlatformLib ++ LIBRARY_CLASS = VirtNorFlashPlatformLib + + [Sources.common] + NorFlashQemuLib.c + + [Packages] + MdePkg/MdePkg.dec +- ArmPlatformPkg/ArmPlatformPkg.dec + ArmPkg/ArmPkg.dec + ArmVirtPkg/ArmVirtPkg.dec + EmbeddedPkg/EmbeddedPkg.dec ++ OvmfPkg/OvmfPkg.dec + + [LibraryClasses] + BaseLib +-- +2.41.0 + diff --git a/edk2-OvmfPkg-VirtNorFlashDxe-ValidateFvHeader-unwritten-s.patch b/edk2-OvmfPkg-VirtNorFlashDxe-ValidateFvHeader-unwritten-s.patch new file mode 100644 index 0000000..adf5b38 --- /dev/null +++ b/edk2-OvmfPkg-VirtNorFlashDxe-ValidateFvHeader-unwritten-s.patch @@ -0,0 +1,47 @@ +From f2aeff31924f6d070d7f8b87550dc6d9820531ad Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Tue, 16 Jan 2024 18:11:04 +0100 +Subject: [PATCH 15/18] OvmfPkg/VirtNorFlashDxe: ValidateFvHeader: unwritten + state is EOL too + +RH-Author: Gerd Hoffmann +RH-MergeRequest: 43: OvmfPkg/VirtNorFlashDxe backport +RH-Jira: RHEL-17587 +RH-Acked-by: Laszlo Ersek +RH-Commit: [17/20] 37220c700ea816c815e0612031e10b7d466b71a2 + +It is possible to find variable entries with State being 0xff, i.e. not +updated since flash block erase. This indicates the variable driver +could not complete the header write while appending a new entry, and +therefore State was not set to VAR_HEADER_VALID_ONLY. + +This can only happen at the end of the variable list, so treat this as +additional "end of variable list" condition. + +Signed-off-by: Gerd Hoffmann +Reviewed-by: Laszlo Ersek +Message-Id: <20240116171105.37831-6-kraxel@redhat.com> +(cherry picked from commit 735d0a5e2e25c1577bf9bea7826da937ca38169d) +--- + OvmfPkg/VirtNorFlashDxe/VirtNorFlashFvb.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/OvmfPkg/VirtNorFlashDxe/VirtNorFlashFvb.c b/OvmfPkg/VirtNorFlashDxe/VirtNorFlashFvb.c +index acc4a413ee..f8e71f88c1 100644 +--- a/OvmfPkg/VirtNorFlashDxe/VirtNorFlashFvb.c ++++ b/OvmfPkg/VirtNorFlashDxe/VirtNorFlashFvb.c +@@ -302,6 +302,11 @@ ValidateFvHeader ( + break; + } + ++ if (VarHeader->State == 0xff) { ++ DEBUG ((DEBUG_INFO, "%a: end of var list (unwritten state)\n", __func__)); ++ break; ++ } ++ + VarName = NULL; + switch (VarHeader->State) { + // usage: State = VAR_HEADER_VALID_ONLY +-- +2.41.0 + diff --git a/edk2-OvmfPkg-VirtNorFlashDxe-add-a-loop-for-NorFlashWrite.patch b/edk2-OvmfPkg-VirtNorFlashDxe-add-a-loop-for-NorFlashWrite.patch new file mode 100644 index 0000000..4fc7c3e --- /dev/null +++ b/edk2-OvmfPkg-VirtNorFlashDxe-add-a-loop-for-NorFlashWrite.patch @@ -0,0 +1,73 @@ +From 00d9e2d6cb03afeef5a1110d6f1fae1389a06f7a Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Tue, 16 Jan 2024 18:11:02 +0100 +Subject: [PATCH 13/18] OvmfPkg/VirtNorFlashDxe: add a loop for + NorFlashWriteBuffer calls. + +RH-Author: Gerd Hoffmann +RH-MergeRequest: 43: OvmfPkg/VirtNorFlashDxe backport +RH-Jira: RHEL-17587 +RH-Acked-by: Laszlo Ersek +RH-Commit: [15/20] 72004a196ea61d627ab528573db657dd7db16de2 + +Replace the two NorFlashWriteBuffer() calls with a loop containing a +single NorFlashWriteBuffer() call. + +With the changes in place the code is able to handle updates larger +than two P30_MAX_BUFFER_SIZE_IN_BYTES blocks, even though the patch +does not actually change the size limit. + +Signed-off-by: Gerd Hoffmann +Reviewed-by: Laszlo Ersek +Message-Id: <20240116171105.37831-4-kraxel@redhat.com> +(cherry picked from commit 28ffd726894f11a587a6ac7f71a4c4af341e24d2) +--- + OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c | 21 ++++++++------------- + 1 file changed, 8 insertions(+), 13 deletions(-) + +diff --git a/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c b/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c +index 88a4d2c23f..3d1343b381 100644 +--- a/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c ++++ b/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c +@@ -521,6 +521,7 @@ NorFlashWriteSingleBlock ( + UINTN BlockAddress; + UINT8 *OrigData; + UINTN Start, End; ++ UINT32 Index, Count; + + DEBUG ((DEBUG_BLKIO, "NorFlashWriteSingleBlock(Parameters: Lba=%ld, Offset=0x%x, *NumBytes=0x%x, Buffer @ 0x%08x)\n", Lba, Offset, *NumBytes, Buffer)); + +@@ -621,23 +622,17 @@ NorFlashWriteSingleBlock ( + goto Exit; + } + +- Status = NorFlashWriteBuffer ( +- Instance, +- BlockAddress + Start, +- P30_MAX_BUFFER_SIZE_IN_BYTES, +- Instance->ShadowBuffer +- ); +- if (EFI_ERROR (Status)) { +- goto Exit; +- } +- +- if ((End - Start) > P30_MAX_BUFFER_SIZE_IN_BYTES) { ++ Count = (End - Start) / P30_MAX_BUFFER_SIZE_IN_BYTES; ++ for (Index = 0; Index < Count; Index++) { + Status = NorFlashWriteBuffer ( + Instance, +- BlockAddress + Start + P30_MAX_BUFFER_SIZE_IN_BYTES, ++ BlockAddress + Start + Index * P30_MAX_BUFFER_SIZE_IN_BYTES, + P30_MAX_BUFFER_SIZE_IN_BYTES, +- Instance->ShadowBuffer + P30_MAX_BUFFER_SIZE_IN_BYTES ++ Instance->ShadowBuffer + Index * P30_MAX_BUFFER_SIZE_IN_BYTES + ); ++ if (EFI_ERROR (Status)) { ++ goto Exit; ++ } + } + + Exit: +-- +2.41.0 + diff --git a/edk2-OvmfPkg-VirtNorFlashDxe-add-casts-to-UINTN-and-UINT3.patch b/edk2-OvmfPkg-VirtNorFlashDxe-add-casts-to-UINTN-and-UINT3.patch new file mode 100644 index 0000000..2494899 --- /dev/null +++ b/edk2-OvmfPkg-VirtNorFlashDxe-add-casts-to-UINTN-and-UINT3.patch @@ -0,0 +1,55 @@ +From e8150ee7fdf1421d2e2801c901e0196496ef599e Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Tue, 16 Jan 2024 18:11:00 +0100 +Subject: [PATCH 11/18] OvmfPkg/VirtNorFlashDxe: add casts to UINTN and UINT32 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Gerd Hoffmann +RH-MergeRequest: 43: OvmfPkg/VirtNorFlashDxe backport +RH-Jira: RHEL-17587 +RH-Acked-by: Laszlo Ersek +RH-Commit: [13/20] fa695acadb9d693242b5221d2bc1958b929718e7 + +This is needed to avoid bit operations being applied to signed integers. + +Suggested-by: László Érsek +Signed-off-by: Gerd Hoffmann +Reviewed-by: Laszlo Ersek +Message-Id: <20240116171105.37831-2-kraxel@redhat.com> +(cherry picked from commit 0395045ae307c43a41f72ca9a8bf4eb8f16b2fe0) +--- + OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c | 2 +- + OvmfPkg/VirtNorFlashDxe/VirtNorFlash.h | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c b/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c +index 1afd60ce66..7f4743b003 100644 +--- a/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c ++++ b/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c +@@ -581,7 +581,7 @@ NorFlashWriteSingleBlock ( + // contents, while checking whether the old version had any bits cleared + // that we want to set. In that case, we will need to erase the block first. + for (CurOffset = 0; CurOffset < *NumBytes; CurOffset++) { +- if (~OrigData[CurOffset] & Buffer[CurOffset]) { ++ if (~(UINT32)OrigData[CurOffset] & (UINT32)Buffer[CurOffset]) { + goto DoErase; + } + +diff --git a/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.h b/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.h +index b7f5d208b2..455eafacc2 100644 +--- a/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.h ++++ b/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.h +@@ -61,7 +61,7 @@ + #define P30_MAX_BUFFER_SIZE_IN_BYTES ((UINTN)128) + #define P30_MAX_BUFFER_SIZE_IN_WORDS (P30_MAX_BUFFER_SIZE_IN_BYTES/((UINTN)4)) + #define MAX_BUFFERED_PROG_ITERATIONS 10000000 +-#define BOUNDARY_OF_32_WORDS 0x7F ++#define BOUNDARY_OF_32_WORDS ((UINTN)0x7F) + + // CFI Addresses + #define P30_CFI_ADDR_QUERY_UNIQUE_QRY 0x10 +-- +2.41.0 + diff --git a/edk2-OvmfPkg-VirtNorFlashDxe-allow-larger-writes-without-.patch b/edk2-OvmfPkg-VirtNorFlashDxe-allow-larger-writes-without-.patch new file mode 100644 index 0000000..f30a257 --- /dev/null +++ b/edk2-OvmfPkg-VirtNorFlashDxe-allow-larger-writes-without-.patch @@ -0,0 +1,65 @@ +From 0193a89b0db837da31301bc1edb8382927842978 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Tue, 16 Jan 2024 18:11:03 +0100 +Subject: [PATCH 14/18] OvmfPkg/VirtNorFlashDxe: allow larger writes without + block erase + +RH-Author: Gerd Hoffmann +RH-MergeRequest: 43: OvmfPkg/VirtNorFlashDxe backport +RH-Jira: RHEL-17587 +RH-Acked-by: Laszlo Ersek +RH-Commit: [16/20] 27ac63b90eb5e6fdc00cbc5a9105c3178ee559cd + +Raise the limit for writes without block erase from two to four +P30_MAX_BUFFER_SIZE_IN_BYTES blocks. With this in place almost all efi +variable updates are handled without block erase. With the old limit +some variable updates (with device paths) took the block erase code +path. + +Signed-off-by: Gerd Hoffmann +Reviewed-by: Laszlo Ersek +Message-Id: <20240116171105.37831-5-kraxel@redhat.com> +(cherry picked from commit b25733c97442513890ae6bb8e10fd340f13844a7) +--- + OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c | 18 ++++++++++-------- + 1 file changed, 10 insertions(+), 8 deletions(-) + +diff --git a/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c b/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c +index 3d1343b381..3d1d20daa1 100644 +--- a/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c ++++ b/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c +@@ -550,13 +550,15 @@ NorFlashWriteSingleBlock ( + return EFI_BAD_BUFFER_SIZE; + } + +- // Pick P30_MAX_BUFFER_SIZE_IN_BYTES (== 128 bytes) as a good start for word +- // operations as opposed to erasing the block and writing the data regardless +- // if an erase is really needed. It looks like most individual NV variable +- // writes are smaller than 128 bytes. +- // To avoid pathological cases were a 2 byte write is disregarded because it +- // occurs right at a 128 byte buffered write alignment boundary, permit up to +- // twice the max buffer size, and perform two writes if needed. ++ // Pick 4 * P30_MAX_BUFFER_SIZE_IN_BYTES (== 512 bytes) as a good ++ // start for word operations as opposed to erasing the block and ++ // writing the data regardless if an erase is really needed. ++ // ++ // Many NV variable updates are small enough for a a single ++ // P30_MAX_BUFFER_SIZE_IN_BYTES block write. In case the update is ++ // larger than a single block, or the update crosses a ++ // P30_MAX_BUFFER_SIZE_IN_BYTES boundary (as shown in the diagram ++ // below), or both, we might have to write two or more blocks. + // + // 0 128 256 + // [----------------|----------------] +@@ -578,7 +580,7 @@ NorFlashWriteSingleBlock ( + Start = Offset & ~BOUNDARY_OF_32_WORDS; + End = ALIGN_VALUE (Offset + *NumBytes, P30_MAX_BUFFER_SIZE_IN_BYTES); + +- if ((End - Start) <= (2 * P30_MAX_BUFFER_SIZE_IN_BYTES)) { ++ if ((End - Start) <= (4 * P30_MAX_BUFFER_SIZE_IN_BYTES)) { + // Check to see if we need to erase before programming the data into NOR. + // If the destination bits are only changing from 1s to 0s we can just write. + // After a block is erased all bits in the block is set to 1. +-- +2.41.0 + diff --git a/edk2-OvmfPkg-VirtNorFlashDxe-avoid-array-mode-switch-afte.patch b/edk2-OvmfPkg-VirtNorFlashDxe-avoid-array-mode-switch-afte.patch new file mode 100644 index 0000000..fc64a93 --- /dev/null +++ b/edk2-OvmfPkg-VirtNorFlashDxe-avoid-array-mode-switch-afte.patch @@ -0,0 +1,89 @@ +From 20ba071dabad6b0f5663083a017799b7a6e684c5 Mon Sep 17 00:00:00 2001 +From: Ard Biesheuvel +Date: Mon, 24 Oct 2022 17:34:09 +0200 +Subject: [PATCH 05/18] OvmfPkg/VirtNorFlashDxe: avoid array mode switch after + each word write + +RH-Author: Gerd Hoffmann +RH-MergeRequest: 43: OvmfPkg/VirtNorFlashDxe backport +RH-Jira: RHEL-17587 +RH-Acked-by: Laszlo Ersek +RH-Commit: [7/20] 274f2ed71a6d5d3f6497129ee3c62f494cc2f067 + +NorFlashWriteSingleWord() switches into programming mode and back into +array mode for every single word that it writes. Under KVM, this +involves tearing down the read-only memslot, and setting it up again, +which is costly and unnecessary. + +Instead, move the array mode switch into the callers, and only make the +switch when the writing is done. + +Signed-off-by: Ard Biesheuvel +Reviewed-by: Sunil V L +(cherry picked from commit ca01e6216a8d1a26c69018e216d1dc3f88a819a4) +--- + OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c | 12 +++--------- + OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.c | 3 +++ + 2 files changed, 6 insertions(+), 9 deletions(-) + +diff --git a/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c b/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c +index f41d9d372f..0a5c5d48c7 100644 +--- a/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c ++++ b/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c +@@ -205,9 +205,6 @@ NorFlashWriteSingleWord ( + SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_CLEAR_STATUS_REGISTER); + } + +- // Put device back into Read Array mode +- SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY); +- + return Status; + } + +@@ -286,8 +283,7 @@ NorFlashWriteBuffer ( + + // The buffer was not available for writing + if (WaitForBuffer == 0) { +- Status = EFI_DEVICE_ERROR; +- goto EXIT; ++ return EFI_DEVICE_ERROR; + } + + // From now on we work in 32-bit words +@@ -337,10 +333,6 @@ NorFlashWriteBuffer ( + SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_CLEAR_STATUS_REGISTER); + } + +-EXIT: +- // Put device back into Read Array mode +- SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY); +- + return Status; + } + +@@ -739,6 +731,8 @@ NorFlashWriteSingleBlock ( + } + + TempStatus = NorFlashWriteSingleWord (Instance, WordAddr, WordToWrite); ++ // Put device back into Read Array mode ++ SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY); + if (EFI_ERROR (TempStatus)) { + return EFI_DEVICE_ERROR; + } +diff --git a/OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.c b/OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.c +index 2ceda22635..f9a41f6aab 100644 +--- a/OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.c ++++ b/OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.c +@@ -280,6 +280,9 @@ NorFlashWriteFullBlock ( + } + + EXIT: ++ // Put device back into Read Array mode ++ SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY); ++ + if (!EfiAtRuntime ()) { + // Interruptions can resume. + gBS->RestoreTPL (OriginalTPL); +-- +2.41.0 + diff --git a/edk2-OvmfPkg-VirtNorFlashDxe-avoid-switching-between-mode.patch b/edk2-OvmfPkg-VirtNorFlashDxe-avoid-switching-between-mode.patch new file mode 100644 index 0000000..783b96f --- /dev/null +++ b/edk2-OvmfPkg-VirtNorFlashDxe-avoid-switching-between-mode.patch @@ -0,0 +1,303 @@ +From 67e26db39c0ec90c164634251da761f649546529 Mon Sep 17 00:00:00 2001 +From: Ard Biesheuvel +Date: Mon, 24 Oct 2022 17:58:07 +0200 +Subject: [PATCH 06/18] OvmfPkg/VirtNorFlashDxe: avoid switching between modes + in a tight loop + +RH-Author: Gerd Hoffmann +RH-MergeRequest: 43: OvmfPkg/VirtNorFlashDxe backport +RH-Jira: RHEL-17587 +RH-Acked-by: Laszlo Ersek +RH-Commit: [8/20] 4923b0fb1586d7955be466b90dce5f790da704ff + +Currently, when dealing with small updates that can be written out +directly (i.e., if they only involve clearing bits and not setting bits, +as the latter requires a block level erase), we iterate over the data +one word at a time, read the old value, compare it, write the new value, +and repeat, unless we encountered a value that we cannot write (0->1 +transition), in which case we fall back to a block level operation. + +This is inefficient for two reasons: +- reading and writing a word at a time involves switching between array +and programming mode for every word of data, which is +disproportionately costly when running under KVM; +- we end up writing some data twice, as we may not notice that a block +erase is needed until after some data has been written to flash. + +So replace this sequence with a single read of up to twice the buffered +write maximum size, followed by one or two buffered writes if the data +can be written directly. Otherwise, fall back to the existing block +level sequence, but without writing out part of the data twice. + +Signed-off-by: Ard Biesheuvel +Reviewed-by: Sunil V L +(cherry picked from commit 25589c4a76e7e3668fd6f794dd1827e958b6719c) +--- + OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c | 214 +++++++++---------------- + 1 file changed, 76 insertions(+), 138 deletions(-) + +diff --git a/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c b/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c +index 0a5c5d48c7..0343131a54 100644 +--- a/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c ++++ b/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c +@@ -576,23 +576,20 @@ NorFlashWriteSingleBlock ( + IN UINT8 *Buffer + ) + { +- EFI_STATUS TempStatus; +- UINT32 Tmp; +- UINT32 TmpBuf; +- UINT32 WordToWrite; +- UINT32 Mask; +- BOOLEAN DoErase; +- UINTN BytesToWrite; ++ EFI_STATUS Status; + UINTN CurOffset; +- UINTN WordAddr; + UINTN BlockSize; + UINTN BlockAddress; +- UINTN PrevBlockAddress; +- +- PrevBlockAddress = 0; ++ UINT8 *OrigData; + + DEBUG ((DEBUG_BLKIO, "NorFlashWriteSingleBlock(Parameters: Lba=%ld, Offset=0x%x, *NumBytes=0x%x, Buffer @ 0x%08x)\n", Lba, Offset, *NumBytes, Buffer)); + ++ // Check we did get some memory. Buffer is BlockSize. ++ if (Instance->ShadowBuffer == NULL) { ++ DEBUG ((DEBUG_ERROR, "FvbWrite: ERROR - Buffer not ready\n")); ++ return EFI_DEVICE_ERROR; ++ } ++ + // Cache the block size to avoid de-referencing pointers all the time + BlockSize = Instance->BlockSize; + +@@ -612,148 +609,89 @@ NorFlashWriteSingleBlock ( + return EFI_BAD_BUFFER_SIZE; + } + +- // Pick 128bytes as a good start for word operations as opposed to erasing the +- // block and writing the data regardless if an erase is really needed. +- // It looks like most individual NV variable writes are smaller than 128bytes. +- if (*NumBytes <= 128) { ++ // Pick P30_MAX_BUFFER_SIZE_IN_BYTES (== 128 bytes) as a good start for word ++ // operations as opposed to erasing the block and writing the data regardless ++ // if an erase is really needed. It looks like most individual NV variable ++ // writes are smaller than 128 bytes. ++ // To avoid pathological cases were a 2 byte write is disregarded because it ++ // occurs right at a 128 byte buffered write alignment boundary, permit up to ++ // twice the max buffer size, and perform two writes if needed. ++ if ((*NumBytes + (Offset & BOUNDARY_OF_32_WORDS)) <= (2 * P30_MAX_BUFFER_SIZE_IN_BYTES)) { + // Check to see if we need to erase before programming the data into NOR. + // If the destination bits are only changing from 1s to 0s we can just write. + // After a block is erased all bits in the block is set to 1. + // If any byte requires us to erase we just give up and rewrite all of it. +- DoErase = FALSE; +- BytesToWrite = *NumBytes; +- CurOffset = Offset; +- +- while (BytesToWrite > 0) { +- // Read full word from NOR, splice as required. A word is the smallest +- // unit we can write. +- TempStatus = NorFlashRead (Instance, Lba, CurOffset & ~(0x3), sizeof (Tmp), &Tmp); +- if (EFI_ERROR (TempStatus)) { +- return EFI_DEVICE_ERROR; +- } + +- // Physical address of word in NOR to write. +- WordAddr = (CurOffset & ~(0x3)) + GET_NOR_BLOCK_ADDRESS ( +- Instance->RegionBaseAddress, +- Lba, +- BlockSize +- ); +- // The word of data that is to be written. +- TmpBuf = *((UINT32 *)(Buffer + (*NumBytes - BytesToWrite))); +- +- // First do word aligned chunks. +- if ((CurOffset & 0x3) == 0) { +- if (BytesToWrite >= 4) { +- // Is the destination still in 'erased' state? +- if (~Tmp != 0) { +- // Check to see if we are only changing bits to zero. +- if ((Tmp ^ TmpBuf) & TmpBuf) { +- DoErase = TRUE; +- break; +- } +- } +- +- // Write this word to NOR +- WordToWrite = TmpBuf; +- CurOffset += sizeof (TmpBuf); +- BytesToWrite -= sizeof (TmpBuf); +- } else { +- // BytesToWrite < 4. Do small writes and left-overs +- Mask = ~((~0) << (BytesToWrite * 8)); +- // Mask out the bytes we want. +- TmpBuf &= Mask; +- // Is the destination still in 'erased' state? +- if ((Tmp & Mask) != Mask) { +- // Check to see if we are only changing bits to zero. +- if ((Tmp ^ TmpBuf) & TmpBuf) { +- DoErase = TRUE; +- break; +- } +- } +- +- // Merge old and new data. Write merged word to NOR +- WordToWrite = (Tmp & ~Mask) | TmpBuf; +- CurOffset += BytesToWrite; +- BytesToWrite = 0; +- } +- } else { +- // Do multiple words, but starting unaligned. +- if (BytesToWrite > (4 - (CurOffset & 0x3))) { +- Mask = ((~0) << ((CurOffset & 0x3) * 8)); +- // Mask out the bytes we want. +- TmpBuf &= Mask; +- // Is the destination still in 'erased' state? +- if ((Tmp & Mask) != Mask) { +- // Check to see if we are only changing bits to zero. +- if ((Tmp ^ TmpBuf) & TmpBuf) { +- DoErase = TRUE; +- break; +- } +- } +- +- // Merge old and new data. Write merged word to NOR +- WordToWrite = (Tmp & ~Mask) | TmpBuf; +- BytesToWrite -= (4 - (CurOffset & 0x3)); +- CurOffset += (4 - (CurOffset & 0x3)); +- } else { +- // Unaligned and fits in one word. +- Mask = (~((~0) << (BytesToWrite * 8))) << ((CurOffset & 0x3) * 8); +- // Mask out the bytes we want. +- TmpBuf = (TmpBuf << ((CurOffset & 0x3) * 8)) & Mask; +- // Is the destination still in 'erased' state? +- if ((Tmp & Mask) != Mask) { +- // Check to see if we are only changing bits to zero. +- if ((Tmp ^ TmpBuf) & TmpBuf) { +- DoErase = TRUE; +- break; +- } +- } +- +- // Merge old and new data. Write merged word to NOR +- WordToWrite = (Tmp & ~Mask) | TmpBuf; +- CurOffset += BytesToWrite; +- BytesToWrite = 0; +- } ++ // Read the old version of the data into the shadow buffer ++ Status = NorFlashRead ( ++ Instance, ++ Lba, ++ Offset & ~BOUNDARY_OF_32_WORDS, ++ (*NumBytes | BOUNDARY_OF_32_WORDS) + 1, ++ Instance->ShadowBuffer ++ ); ++ if (EFI_ERROR (Status)) { ++ return EFI_DEVICE_ERROR; ++ } ++ ++ // Make OrigData point to the start of the old version of the data inside ++ // the word aligned buffer ++ OrigData = Instance->ShadowBuffer + (Offset & BOUNDARY_OF_32_WORDS); ++ ++ // Update the buffer containing the old version of the data with the new ++ // contents, while checking whether the old version had any bits cleared ++ // that we want to set. In that case, we will need to erase the block first. ++ for (CurOffset = 0; CurOffset < *NumBytes; CurOffset++) { ++ if (~OrigData[CurOffset] & Buffer[CurOffset]) { ++ goto DoErase; + } + +- // +- // Write the word to NOR. +- // ++ OrigData[CurOffset] = Buffer[CurOffset]; ++ } + +- BlockAddress = GET_NOR_BLOCK_ADDRESS (Instance->RegionBaseAddress, Lba, BlockSize); +- if (BlockAddress != PrevBlockAddress) { +- TempStatus = NorFlashUnlockSingleBlockIfNecessary (Instance, BlockAddress); +- if (EFI_ERROR (TempStatus)) { +- return EFI_DEVICE_ERROR; +- } ++ // ++ // Write the updated buffer to NOR. ++ // ++ BlockAddress = GET_NOR_BLOCK_ADDRESS (Instance->RegionBaseAddress, Lba, BlockSize); + +- PrevBlockAddress = BlockAddress; +- } ++ // Unlock the block if we have to ++ Status = NorFlashUnlockSingleBlockIfNecessary (Instance, BlockAddress); ++ if (EFI_ERROR (Status)) { ++ goto Exit; ++ } + +- TempStatus = NorFlashWriteSingleWord (Instance, WordAddr, WordToWrite); +- // Put device back into Read Array mode +- SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY); +- if (EFI_ERROR (TempStatus)) { +- return EFI_DEVICE_ERROR; +- } ++ Status = NorFlashWriteBuffer ( ++ Instance, ++ BlockAddress + (Offset & ~BOUNDARY_OF_32_WORDS), ++ P30_MAX_BUFFER_SIZE_IN_BYTES, ++ Instance->ShadowBuffer ++ ); ++ if (EFI_ERROR (Status)) { ++ goto Exit; + } + +- // Exit if we got here and could write all the data. Otherwise do the +- // Erase-Write cycle. +- if (!DoErase) { +- return EFI_SUCCESS; ++ if ((*NumBytes + (Offset & BOUNDARY_OF_32_WORDS)) > P30_MAX_BUFFER_SIZE_IN_BYTES) { ++ BlockAddress += P30_MAX_BUFFER_SIZE_IN_BYTES; ++ ++ Status = NorFlashWriteBuffer ( ++ Instance, ++ BlockAddress + (Offset & ~BOUNDARY_OF_32_WORDS), ++ P30_MAX_BUFFER_SIZE_IN_BYTES, ++ Instance->ShadowBuffer + P30_MAX_BUFFER_SIZE_IN_BYTES ++ ); + } +- } + +- // Check we did get some memory. Buffer is BlockSize. +- if (Instance->ShadowBuffer == NULL) { +- DEBUG ((DEBUG_ERROR, "FvbWrite: ERROR - Buffer not ready\n")); +- return EFI_DEVICE_ERROR; ++Exit: ++ // Put device back into Read Array mode ++ SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY); ++ ++ return Status; + } + ++DoErase: + // Read NOR Flash data into shadow buffer +- TempStatus = NorFlashReadBlocks (Instance, Lba, BlockSize, Instance->ShadowBuffer); +- if (EFI_ERROR (TempStatus)) { ++ Status = NorFlashReadBlocks (Instance, Lba, BlockSize, Instance->ShadowBuffer); ++ if (EFI_ERROR (Status)) { + // Return one of the pre-approved error statuses + return EFI_DEVICE_ERROR; + } +@@ -762,8 +700,8 @@ NorFlashWriteSingleBlock ( + CopyMem ((VOID *)((UINTN)Instance->ShadowBuffer + Offset), Buffer, *NumBytes); + + // Write the modified buffer back to the NorFlash +- TempStatus = NorFlashWriteBlocks (Instance, Lba, BlockSize, Instance->ShadowBuffer); +- if (EFI_ERROR (TempStatus)) { ++ Status = NorFlashWriteBlocks (Instance, Lba, BlockSize, Instance->ShadowBuffer); ++ if (EFI_ERROR (Status)) { + // Return one of the pre-approved error statuses + return EFI_DEVICE_ERROR; + } +-- +2.41.0 + diff --git a/edk2-OvmfPkg-VirtNorFlashDxe-clarify-block-write-logic-fi.patch b/edk2-OvmfPkg-VirtNorFlashDxe-clarify-block-write-logic-fi.patch new file mode 100644 index 0000000..0ac5c14 --- /dev/null +++ b/edk2-OvmfPkg-VirtNorFlashDxe-clarify-block-write-logic-fi.patch @@ -0,0 +1,110 @@ +From f136d4895b1477a56b916a76448ba76e67b08163 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Tue, 16 Jan 2024 18:11:01 +0100 +Subject: [PATCH 12/18] OvmfPkg/VirtNorFlashDxe: clarify block write logic & + fix shadowbuffer reads + +RH-Author: Gerd Hoffmann +RH-MergeRequest: 43: OvmfPkg/VirtNorFlashDxe backport +RH-Jira: RHEL-17587 +RH-Acked-by: Laszlo Ersek +RH-Commit: [14/20] 38009625e5f189166f7a93e236576140a7ccb393 + +Introduce 'Start' and 'End' variables to make it easier to follow the +logic and code flow. Also add a ascii art diagram (based on a +suggestion by Laszlo). + +This also fixes the 'Size' calculation for the NorFlashRead() call. +Without this patch the code will read only one instead of two +P30_MAX_BUFFER_SIZE_IN_BYTES blocks in case '*NumBytes' is smaller than +P30_MAX_BUFFER_SIZE_IN_BYTES but 'Offset + *NumBytes' is not, i.e. the +update range crosses a P30_MAX_BUFFER_SIZE_IN_BYTES boundary. + +Signed-off-by: Gerd Hoffmann +Reviewed-by: Laszlo Ersek +Message-Id: <20240116171105.37831-3-kraxel@redhat.com> +(cherry picked from commit 35d8ea8097794b522149688b5cfaf8364bc44d54) +--- + OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c | 36 ++++++++++++++++++++------ + 1 file changed, 28 insertions(+), 8 deletions(-) + +diff --git a/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c b/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c +index 7f4743b003..88a4d2c23f 100644 +--- a/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c ++++ b/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c +@@ -520,6 +520,7 @@ NorFlashWriteSingleBlock ( + UINTN BlockSize; + UINTN BlockAddress; + UINT8 *OrigData; ++ UINTN Start, End; + + DEBUG ((DEBUG_BLKIO, "NorFlashWriteSingleBlock(Parameters: Lba=%ld, Offset=0x%x, *NumBytes=0x%x, Buffer @ 0x%08x)\n", Lba, Offset, *NumBytes, Buffer)); + +@@ -555,7 +556,28 @@ NorFlashWriteSingleBlock ( + // To avoid pathological cases were a 2 byte write is disregarded because it + // occurs right at a 128 byte buffered write alignment boundary, permit up to + // twice the max buffer size, and perform two writes if needed. +- if ((*NumBytes + (Offset & BOUNDARY_OF_32_WORDS)) <= (2 * P30_MAX_BUFFER_SIZE_IN_BYTES)) { ++ // ++ // 0 128 256 ++ // [----------------|----------------] ++ // ^ ^ ^ ^ ++ // | | | | ++ // | | | End, the next "word" boundary beyond ++ // | | | the (logical) update ++ // | | | ++ // | | (Offset & BOUNDARY_OF_32_WORDS) + NumBytes; ++ // | | i.e., the relative offset inside (or just past) ++ // | | the *double-word* such that it is the ++ // | | *exclusive* end of the (logical) update. ++ // | | ++ // | Offset & BOUNDARY_OF_32_WORDS; i.e., Offset within the "word"; ++ // | this is where the (logical) update is supposed to start ++ // | ++ // Start = Offset & ~BOUNDARY_OF_32_WORDS; i.e., Offset truncated to "word" boundary ++ ++ Start = Offset & ~BOUNDARY_OF_32_WORDS; ++ End = ALIGN_VALUE (Offset + *NumBytes, P30_MAX_BUFFER_SIZE_IN_BYTES); ++ ++ if ((End - Start) <= (2 * P30_MAX_BUFFER_SIZE_IN_BYTES)) { + // Check to see if we need to erase before programming the data into NOR. + // If the destination bits are only changing from 1s to 0s we can just write. + // After a block is erased all bits in the block is set to 1. +@@ -565,8 +587,8 @@ NorFlashWriteSingleBlock ( + Status = NorFlashRead ( + Instance, + Lba, +- Offset & ~BOUNDARY_OF_32_WORDS, +- (*NumBytes | BOUNDARY_OF_32_WORDS) + 1, ++ Start, ++ End - Start, + Instance->ShadowBuffer + ); + if (EFI_ERROR (Status)) { +@@ -601,7 +623,7 @@ NorFlashWriteSingleBlock ( + + Status = NorFlashWriteBuffer ( + Instance, +- BlockAddress + (Offset & ~BOUNDARY_OF_32_WORDS), ++ BlockAddress + Start, + P30_MAX_BUFFER_SIZE_IN_BYTES, + Instance->ShadowBuffer + ); +@@ -609,12 +631,10 @@ NorFlashWriteSingleBlock ( + goto Exit; + } + +- if ((*NumBytes + (Offset & BOUNDARY_OF_32_WORDS)) > P30_MAX_BUFFER_SIZE_IN_BYTES) { +- BlockAddress += P30_MAX_BUFFER_SIZE_IN_BYTES; +- ++ if ((End - Start) > P30_MAX_BUFFER_SIZE_IN_BYTES) { + Status = NorFlashWriteBuffer ( + Instance, +- BlockAddress + (Offset & ~BOUNDARY_OF_32_WORDS), ++ BlockAddress + Start + P30_MAX_BUFFER_SIZE_IN_BYTES, + P30_MAX_BUFFER_SIZE_IN_BYTES, + Instance->ShadowBuffer + P30_MAX_BUFFER_SIZE_IN_BYTES + ); +-- +2.41.0 + diff --git a/edk2-OvmfPkg-VirtNorFlashDxe-clone-ArmPlatformPkg-s-NOR-f.patch b/edk2-OvmfPkg-VirtNorFlashDxe-clone-ArmPlatformPkg-s-NOR-f.patch new file mode 100644 index 0000000..41975ab --- /dev/null +++ b/edk2-OvmfPkg-VirtNorFlashDxe-clone-ArmPlatformPkg-s-NOR-f.patch @@ -0,0 +1,2973 @@ +From 3d6a0e0d3323c8b98a56e69ed01b6634dd480f25 Mon Sep 17 00:00:00 2001 +From: Ard Biesheuvel +Date: Mon, 24 Oct 2022 16:41:43 +0200 +Subject: [PATCH 01/18] OvmfPkg/VirtNorFlashDxe: clone ArmPlatformPkg's NOR + flash driver + +RH-Author: Gerd Hoffmann +RH-MergeRequest: 43: OvmfPkg/VirtNorFlashDxe backport +RH-Jira: RHEL-17587 +RH-Acked-by: Laszlo Ersek +RH-Commit: [3/20] f78caae529b7f943a5d4838cb906546373de4c54 + +QEMU's mach-virt is loosely based on ARM Versatile Express, and inherits +its NOR flash driver, which is now being used on other QEMU emulated +architectures as well. + +In order to permit ourselves the freedom to optimize this driver for +use under KVM emulation, let's clone it into OvmfPkg, so we have a +version we can hack without the risk of regressing bare metal platforms. + +The cloned version is mostly identical to the original, but it depends +on the newly added VirtNorFlashPlatformLib library class instead of the +original one from ArmPlatformPkg. Beyond that, only cosmetic changes +related to #include order etc were made. + +Signed-off-by: Ard Biesheuvel +Reviewed-by: Sunil V L +(cherry picked from commit c1ff81f7990be88c9e98ca3be65178057d8aae77) +--- + OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c | 991 ++++++++++++++++++ + OvmfPkg/VirtNorFlashDxe/VirtNorFlash.h | 422 ++++++++ + .../VirtNorFlashDxe/VirtNorFlashBlockIoDxe.c | 123 +++ + OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.c | 506 +++++++++ + OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.inf | 72 ++ + OvmfPkg/VirtNorFlashDxe/VirtNorFlashFvb.c | 777 ++++++++++++++ + 6 files changed, 2891 insertions(+) + create mode 100644 OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c + create mode 100644 OvmfPkg/VirtNorFlashDxe/VirtNorFlash.h + create mode 100644 OvmfPkg/VirtNorFlashDxe/VirtNorFlashBlockIoDxe.c + create mode 100644 OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.c + create mode 100644 OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.inf + create mode 100644 OvmfPkg/VirtNorFlashDxe/VirtNorFlashFvb.c + +diff --git a/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c b/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c +new file mode 100644 +index 0000000000..12fa720dad +--- /dev/null ++++ b/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c +@@ -0,0 +1,991 @@ ++/** @file NorFlash.c ++ ++ Copyright (c) 2011 - 2020, Arm Limited. All rights reserved.
++ Copyright (c) 2020, Linaro, Ltd. All rights reserved.
++ ++ SPDX-License-Identifier: BSD-2-Clause-Patent ++ ++**/ ++ ++#include ++ ++#include "VirtNorFlash.h" ++ ++// ++// Global variable declarations ++// ++extern NOR_FLASH_INSTANCE **mNorFlashInstances; ++extern UINT32 mNorFlashDeviceCount; ++ ++UINT32 ++NorFlashReadStatusRegister ( ++ IN NOR_FLASH_INSTANCE *Instance, ++ IN UINTN SR_Address ++ ) ++{ ++ // Prepare to read the status register ++ SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_STATUS_REGISTER); ++ return MmioRead32 (Instance->DeviceBaseAddress); ++} ++ ++STATIC ++BOOLEAN ++NorFlashBlockIsLocked ( ++ IN NOR_FLASH_INSTANCE *Instance, ++ IN UINTN BlockAddress ++ ) ++{ ++ UINT32 LockStatus; ++ ++ // Send command for reading device id ++ SEND_NOR_COMMAND (BlockAddress, 2, P30_CMD_READ_DEVICE_ID); ++ ++ // Read block lock status ++ LockStatus = MmioRead32 (CREATE_NOR_ADDRESS (BlockAddress, 2)); ++ ++ // Decode block lock status ++ LockStatus = FOLD_32BIT_INTO_16BIT (LockStatus); ++ ++ if ((LockStatus & 0x2) != 0) { ++ DEBUG ((DEBUG_ERROR, "NorFlashBlockIsLocked: WARNING: Block LOCKED DOWN\n")); ++ } ++ ++ return ((LockStatus & 0x1) != 0); ++} ++ ++STATIC ++EFI_STATUS ++NorFlashUnlockSingleBlock ( ++ IN NOR_FLASH_INSTANCE *Instance, ++ IN UINTN BlockAddress ++ ) ++{ ++ UINT32 LockStatus; ++ ++ // Raise the Task Priority Level to TPL_NOTIFY to serialise all its operations ++ // and to protect shared data structures. ++ ++ if (FeaturePcdGet (PcdNorFlashCheckBlockLocked) == TRUE) { ++ do { ++ // Request a lock setup ++ SEND_NOR_COMMAND (BlockAddress, 0, P30_CMD_LOCK_BLOCK_SETUP); ++ ++ // Request an unlock ++ SEND_NOR_COMMAND (BlockAddress, 0, P30_CMD_UNLOCK_BLOCK); ++ ++ // Send command for reading device id ++ SEND_NOR_COMMAND (BlockAddress, 2, P30_CMD_READ_DEVICE_ID); ++ ++ // Read block lock status ++ LockStatus = MmioRead32 (CREATE_NOR_ADDRESS (BlockAddress, 2)); ++ ++ // Decode block lock status ++ LockStatus = FOLD_32BIT_INTO_16BIT (LockStatus); ++ } while ((LockStatus & 0x1) == 1); ++ } else { ++ // Request a lock setup ++ SEND_NOR_COMMAND (BlockAddress, 0, P30_CMD_LOCK_BLOCK_SETUP); ++ ++ // Request an unlock ++ SEND_NOR_COMMAND (BlockAddress, 0, P30_CMD_UNLOCK_BLOCK); ++ ++ // Wait until the status register gives us the all clear ++ do { ++ LockStatus = NorFlashReadStatusRegister (Instance, BlockAddress); ++ } while ((LockStatus & P30_SR_BIT_WRITE) != P30_SR_BIT_WRITE); ++ } ++ ++ // Put device back into Read Array mode ++ SEND_NOR_COMMAND (BlockAddress, 0, P30_CMD_READ_ARRAY); ++ ++ DEBUG ((DEBUG_BLKIO, "UnlockSingleBlock: BlockAddress=0x%08x\n", BlockAddress)); ++ ++ return EFI_SUCCESS; ++} ++ ++EFI_STATUS ++NorFlashUnlockSingleBlockIfNecessary ( ++ IN NOR_FLASH_INSTANCE *Instance, ++ IN UINTN BlockAddress ++ ) ++{ ++ EFI_STATUS Status; ++ ++ Status = EFI_SUCCESS; ++ ++ if (NorFlashBlockIsLocked (Instance, BlockAddress)) { ++ Status = NorFlashUnlockSingleBlock (Instance, BlockAddress); ++ } ++ ++ return Status; ++} ++ ++/** ++ * The following function presumes that the block has already been unlocked. ++ **/ ++EFI_STATUS ++NorFlashEraseSingleBlock ( ++ IN NOR_FLASH_INSTANCE *Instance, ++ IN UINTN BlockAddress ++ ) ++{ ++ EFI_STATUS Status; ++ UINT32 StatusRegister; ++ ++ Status = EFI_SUCCESS; ++ ++ // Request a block erase and then confirm it ++ SEND_NOR_COMMAND (BlockAddress, 0, P30_CMD_BLOCK_ERASE_SETUP); ++ SEND_NOR_COMMAND (BlockAddress, 0, P30_CMD_BLOCK_ERASE_CONFIRM); ++ ++ // Wait until the status register gives us the all clear ++ do { ++ StatusRegister = NorFlashReadStatusRegister (Instance, BlockAddress); ++ } while ((StatusRegister & P30_SR_BIT_WRITE) != P30_SR_BIT_WRITE); ++ ++ if (StatusRegister & P30_SR_BIT_VPP) { ++ DEBUG ((DEBUG_ERROR, "EraseSingleBlock(BlockAddress=0x%08x: VPP Range Error\n", BlockAddress)); ++ Status = EFI_DEVICE_ERROR; ++ } ++ ++ if ((StatusRegister & (P30_SR_BIT_ERASE | P30_SR_BIT_PROGRAM)) == (P30_SR_BIT_ERASE | P30_SR_BIT_PROGRAM)) { ++ DEBUG ((DEBUG_ERROR, "EraseSingleBlock(BlockAddress=0x%08x: Command Sequence Error\n", BlockAddress)); ++ Status = EFI_DEVICE_ERROR; ++ } ++ ++ if (StatusRegister & P30_SR_BIT_ERASE) { ++ DEBUG ((DEBUG_ERROR, "EraseSingleBlock(BlockAddress=0x%08x: Block Erase Error StatusRegister:0x%X\n", BlockAddress, StatusRegister)); ++ Status = EFI_DEVICE_ERROR; ++ } ++ ++ if (StatusRegister & P30_SR_BIT_BLOCK_LOCKED) { ++ // The debug level message has been reduced because a device lock might happen. In this case we just retry it ... ++ DEBUG ((DEBUG_INFO, "EraseSingleBlock(BlockAddress=0x%08x: Block Locked Error\n", BlockAddress)); ++ Status = EFI_WRITE_PROTECTED; ++ } ++ ++ if (EFI_ERROR (Status)) { ++ // Clear the Status Register ++ SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_CLEAR_STATUS_REGISTER); ++ } ++ ++ // Put device back into Read Array mode ++ SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY); ++ ++ return Status; ++} ++ ++EFI_STATUS ++NorFlashWriteSingleWord ( ++ IN NOR_FLASH_INSTANCE *Instance, ++ IN UINTN WordAddress, ++ IN UINT32 WriteData ++ ) ++{ ++ EFI_STATUS Status; ++ UINT32 StatusRegister; ++ ++ Status = EFI_SUCCESS; ++ ++ // Request a write single word command ++ SEND_NOR_COMMAND (WordAddress, 0, P30_CMD_WORD_PROGRAM_SETUP); ++ ++ // Store the word into NOR Flash; ++ MmioWrite32 (WordAddress, WriteData); ++ ++ // Wait for the write to complete and then check for any errors; i.e. check the Status Register ++ do { ++ // Prepare to read the status register ++ StatusRegister = NorFlashReadStatusRegister (Instance, WordAddress); ++ // The chip is busy while the WRITE bit is not asserted ++ } while ((StatusRegister & P30_SR_BIT_WRITE) != P30_SR_BIT_WRITE); ++ ++ // Perform a full status check: ++ // Mask the relevant bits of Status Register. ++ // Everything should be zero, if not, we have a problem ++ ++ if (StatusRegister & P30_SR_BIT_VPP) { ++ DEBUG ((DEBUG_ERROR, "NorFlashWriteSingleWord(WordAddress:0x%X): VPP Range Error\n", WordAddress)); ++ Status = EFI_DEVICE_ERROR; ++ } ++ ++ if (StatusRegister & P30_SR_BIT_PROGRAM) { ++ DEBUG ((DEBUG_ERROR, "NorFlashWriteSingleWord(WordAddress:0x%X): Program Error\n", WordAddress)); ++ Status = EFI_DEVICE_ERROR; ++ } ++ ++ if (StatusRegister & P30_SR_BIT_BLOCK_LOCKED) { ++ DEBUG ((DEBUG_ERROR, "NorFlashWriteSingleWord(WordAddress:0x%X): Device Protect Error\n", WordAddress)); ++ Status = EFI_DEVICE_ERROR; ++ } ++ ++ if (!EFI_ERROR (Status)) { ++ // Clear the Status Register ++ SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_CLEAR_STATUS_REGISTER); ++ } ++ ++ // Put device back into Read Array mode ++ SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY); ++ ++ return Status; ++} ++ ++/* ++ * Writes data to the NOR Flash using the Buffered Programming method. ++ * ++ * The maximum size of the on-chip buffer is 32-words, because of hardware restrictions. ++ * Therefore this function will only handle buffers up to 32 words or 128 bytes. ++ * To deal with larger buffers, call this function again. ++ * ++ * This function presumes that both the TargetAddress and the TargetAddress+BufferSize ++ * exist entirely within the NOR Flash. Therefore these conditions will not be checked here. ++ * ++ * In buffered programming, if the target address not at the beginning of a 32-bit word boundary, ++ * then programming time is doubled and power consumption is increased. ++ * Therefore, it is a requirement to align buffer writes to 32-bit word boundaries. ++ * i.e. the last 4 bits of the target start address must be zero: 0x......00 ++ */ ++EFI_STATUS ++NorFlashWriteBuffer ( ++ IN NOR_FLASH_INSTANCE *Instance, ++ IN UINTN TargetAddress, ++ IN UINTN BufferSizeInBytes, ++ IN UINT32 *Buffer ++ ) ++{ ++ EFI_STATUS Status; ++ UINTN BufferSizeInWords; ++ UINTN Count; ++ volatile UINT32 *Data; ++ UINTN WaitForBuffer; ++ BOOLEAN BufferAvailable; ++ UINT32 StatusRegister; ++ ++ WaitForBuffer = MAX_BUFFERED_PROG_ITERATIONS; ++ BufferAvailable = FALSE; ++ ++ // Check that the target address does not cross a 32-word boundary. ++ if ((TargetAddress & BOUNDARY_OF_32_WORDS) != 0) { ++ return EFI_INVALID_PARAMETER; ++ } ++ ++ // Check there are some data to program ++ if (BufferSizeInBytes == 0) { ++ return EFI_BUFFER_TOO_SMALL; ++ } ++ ++ // Check that the buffer size does not exceed the maximum hardware buffer size on chip. ++ if (BufferSizeInBytes > P30_MAX_BUFFER_SIZE_IN_BYTES) { ++ return EFI_BAD_BUFFER_SIZE; ++ } ++ ++ // Check that the buffer size is a multiple of 32-bit words ++ if ((BufferSizeInBytes % 4) != 0) { ++ return EFI_BAD_BUFFER_SIZE; ++ } ++ ++ // Pre-programming conditions checked, now start the algorithm. ++ ++ // Prepare the data destination address ++ Data = (UINT32 *)TargetAddress; ++ ++ // Check the availability of the buffer ++ do { ++ // Issue the Buffered Program Setup command ++ SEND_NOR_COMMAND (TargetAddress, 0, P30_CMD_BUFFERED_PROGRAM_SETUP); ++ ++ // Read back the status register bit#7 from the same address ++ if (((*Data) & P30_SR_BIT_WRITE) == P30_SR_BIT_WRITE) { ++ BufferAvailable = TRUE; ++ } ++ ++ // Update the loop counter ++ WaitForBuffer--; ++ } while ((WaitForBuffer > 0) && (BufferAvailable == FALSE)); ++ ++ // The buffer was not available for writing ++ if (WaitForBuffer == 0) { ++ Status = EFI_DEVICE_ERROR; ++ goto EXIT; ++ } ++ ++ // From now on we work in 32-bit words ++ BufferSizeInWords = BufferSizeInBytes / (UINTN)4; ++ ++ // Write the word count, which is (buffer_size_in_words - 1), ++ // because word count 0 means one word. ++ SEND_NOR_COMMAND (TargetAddress, 0, (BufferSizeInWords - 1)); ++ ++ // Write the data to the NOR Flash, advancing each address by 4 bytes ++ for (Count = 0; Count < BufferSizeInWords; Count++, Data++, Buffer++) { ++ MmioWrite32 ((UINTN)Data, *Buffer); ++ } ++ ++ // Issue the Buffered Program Confirm command, to start the programming operation ++ SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_BUFFERED_PROGRAM_CONFIRM); ++ ++ // Wait for the write to complete and then check for any errors; i.e. check the Status Register ++ do { ++ StatusRegister = NorFlashReadStatusRegister (Instance, TargetAddress); ++ // The chip is busy while the WRITE bit is not asserted ++ } while ((StatusRegister & P30_SR_BIT_WRITE) != P30_SR_BIT_WRITE); ++ ++ // Perform a full status check: ++ // Mask the relevant bits of Status Register. ++ // Everything should be zero, if not, we have a problem ++ ++ Status = EFI_SUCCESS; ++ ++ if (StatusRegister & P30_SR_BIT_VPP) { ++ DEBUG ((DEBUG_ERROR, "NorFlashWriteBuffer(TargetAddress:0x%X): VPP Range Error\n", TargetAddress)); ++ Status = EFI_DEVICE_ERROR; ++ } ++ ++ if (StatusRegister & P30_SR_BIT_PROGRAM) { ++ DEBUG ((DEBUG_ERROR, "NorFlashWriteBuffer(TargetAddress:0x%X): Program Error\n", TargetAddress)); ++ Status = EFI_DEVICE_ERROR; ++ } ++ ++ if (StatusRegister & P30_SR_BIT_BLOCK_LOCKED) { ++ DEBUG ((DEBUG_ERROR, "NorFlashWriteBuffer(TargetAddress:0x%X): Device Protect Error\n", TargetAddress)); ++ Status = EFI_DEVICE_ERROR; ++ } ++ ++ if (!EFI_ERROR (Status)) { ++ // Clear the Status Register ++ SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_CLEAR_STATUS_REGISTER); ++ } ++ ++EXIT: ++ // Put device back into Read Array mode ++ SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY); ++ ++ return Status; ++} ++ ++EFI_STATUS ++NorFlashWriteBlocks ( ++ IN NOR_FLASH_INSTANCE *Instance, ++ IN EFI_LBA Lba, ++ IN UINTN BufferSizeInBytes, ++ IN VOID *Buffer ++ ) ++{ ++ UINT32 *pWriteBuffer; ++ EFI_STATUS Status; ++ EFI_LBA CurrentBlock; ++ UINT32 BlockSizeInWords; ++ UINT32 NumBlocks; ++ UINT32 BlockCount; ++ ++ Status = EFI_SUCCESS; ++ ++ // The buffer must be valid ++ if (Buffer == NULL) { ++ return EFI_INVALID_PARAMETER; ++ } ++ ++ if (Instance->Media.ReadOnly == TRUE) { ++ return EFI_WRITE_PROTECTED; ++ } ++ ++ // We must have some bytes to read ++ DEBUG ((DEBUG_BLKIO, "NorFlashWriteBlocks: BufferSizeInBytes=0x%x\n", BufferSizeInBytes)); ++ if (BufferSizeInBytes == 0) { ++ return EFI_BAD_BUFFER_SIZE; ++ } ++ ++ // The size of the buffer must be a multiple of the block size ++ DEBUG ((DEBUG_BLKIO, "NorFlashWriteBlocks: BlockSize in bytes =0x%x\n", Instance->Media.BlockSize)); ++ if ((BufferSizeInBytes % Instance->Media.BlockSize) != 0) { ++ return EFI_BAD_BUFFER_SIZE; ++ } ++ ++ // All blocks must be within the device ++ NumBlocks = ((UINT32)BufferSizeInBytes) / Instance->Media.BlockSize; ++ ++ DEBUG ((DEBUG_BLKIO, "NorFlashWriteBlocks: NumBlocks=%d, LastBlock=%ld, Lba=%ld.\n", NumBlocks, Instance->Media.LastBlock, Lba)); ++ ++ if ((Lba + NumBlocks) > (Instance->Media.LastBlock + 1)) { ++ DEBUG ((DEBUG_ERROR, "NorFlashWriteBlocks: ERROR - Write will exceed last block.\n")); ++ return EFI_INVALID_PARAMETER; ++ } ++ ++ BlockSizeInWords = Instance->Media.BlockSize / 4; ++ ++ // Because the target *Buffer is a pointer to VOID, we must put all the data into a pointer ++ // to a proper data type, so use *ReadBuffer ++ pWriteBuffer = (UINT32 *)Buffer; ++ ++ CurrentBlock = Lba; ++ for (BlockCount = 0; BlockCount < NumBlocks; BlockCount++, CurrentBlock++, pWriteBuffer = pWriteBuffer + BlockSizeInWords) { ++ DEBUG ((DEBUG_BLKIO, "NorFlashWriteBlocks: Writing block #%d\n", (UINTN)CurrentBlock)); ++ ++ Status = NorFlashWriteFullBlock (Instance, CurrentBlock, pWriteBuffer, BlockSizeInWords); ++ ++ if (EFI_ERROR (Status)) { ++ break; ++ } ++ } ++ ++ DEBUG ((DEBUG_BLKIO, "NorFlashWriteBlocks: Exit Status = \"%r\".\n", Status)); ++ return Status; ++} ++ ++#define BOTH_ALIGNED(a, b, align) ((((UINTN)(a) | (UINTN)(b)) & ((align) - 1)) == 0) ++ ++/** ++ Copy Length bytes from Source to Destination, using aligned accesses only. ++ Note that this implementation uses memcpy() semantics rather then memmove() ++ semantics, i.e., SourceBuffer and DestinationBuffer should not overlap. ++ ++ @param DestinationBuffer The target of the copy request. ++ @param SourceBuffer The place to copy from. ++ @param Length The number of bytes to copy. ++ ++ @return Destination ++ ++**/ ++STATIC ++VOID * ++AlignedCopyMem ( ++ OUT VOID *DestinationBuffer, ++ IN CONST VOID *SourceBuffer, ++ IN UINTN Length ++ ) ++{ ++ UINT8 *Destination8; ++ CONST UINT8 *Source8; ++ UINT32 *Destination32; ++ CONST UINT32 *Source32; ++ UINT64 *Destination64; ++ CONST UINT64 *Source64; ++ ++ if (BOTH_ALIGNED (DestinationBuffer, SourceBuffer, 8) && (Length >= 8)) { ++ Destination64 = DestinationBuffer; ++ Source64 = SourceBuffer; ++ while (Length >= 8) { ++ *Destination64++ = *Source64++; ++ Length -= 8; ++ } ++ ++ Destination8 = (UINT8 *)Destination64; ++ Source8 = (CONST UINT8 *)Source64; ++ } else if (BOTH_ALIGNED (DestinationBuffer, SourceBuffer, 4) && (Length >= 4)) { ++ Destination32 = DestinationBuffer; ++ Source32 = SourceBuffer; ++ while (Length >= 4) { ++ *Destination32++ = *Source32++; ++ Length -= 4; ++ } ++ ++ Destination8 = (UINT8 *)Destination32; ++ Source8 = (CONST UINT8 *)Source32; ++ } else { ++ Destination8 = DestinationBuffer; ++ Source8 = SourceBuffer; ++ } ++ ++ while (Length-- != 0) { ++ *Destination8++ = *Source8++; ++ } ++ ++ return DestinationBuffer; ++} ++ ++EFI_STATUS ++NorFlashReadBlocks ( ++ IN NOR_FLASH_INSTANCE *Instance, ++ IN EFI_LBA Lba, ++ IN UINTN BufferSizeInBytes, ++ OUT VOID *Buffer ++ ) ++{ ++ UINT32 NumBlocks; ++ UINTN StartAddress; ++ ++ DEBUG (( ++ DEBUG_BLKIO, ++ "NorFlashReadBlocks: BufferSize=0x%xB BlockSize=0x%xB LastBlock=%ld, Lba=%ld.\n", ++ BufferSizeInBytes, ++ Instance->Media.BlockSize, ++ Instance->Media.LastBlock, ++ Lba ++ )); ++ ++ // The buffer must be valid ++ if (Buffer == NULL) { ++ return EFI_INVALID_PARAMETER; ++ } ++ ++ // Return if we have not any byte to read ++ if (BufferSizeInBytes == 0) { ++ return EFI_SUCCESS; ++ } ++ ++ // The size of the buffer must be a multiple of the block size ++ if ((BufferSizeInBytes % Instance->Media.BlockSize) != 0) { ++ return EFI_BAD_BUFFER_SIZE; ++ } ++ ++ // All blocks must be within the device ++ NumBlocks = ((UINT32)BufferSizeInBytes) / Instance->Media.BlockSize; ++ ++ if ((Lba + NumBlocks) > (Instance->Media.LastBlock + 1)) { ++ DEBUG ((DEBUG_ERROR, "NorFlashReadBlocks: ERROR - Read will exceed last block\n")); ++ return EFI_INVALID_PARAMETER; ++ } ++ ++ // Get the address to start reading from ++ StartAddress = GET_NOR_BLOCK_ADDRESS ( ++ Instance->RegionBaseAddress, ++ Lba, ++ Instance->Media.BlockSize ++ ); ++ ++ // Put the device into Read Array mode ++ SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY); ++ ++ // Readout the data ++ AlignedCopyMem (Buffer, (VOID *)StartAddress, BufferSizeInBytes); ++ ++ return EFI_SUCCESS; ++} ++ ++EFI_STATUS ++NorFlashRead ( ++ IN NOR_FLASH_INSTANCE *Instance, ++ IN EFI_LBA Lba, ++ IN UINTN Offset, ++ IN UINTN BufferSizeInBytes, ++ OUT VOID *Buffer ++ ) ++{ ++ UINTN StartAddress; ++ ++ // The buffer must be valid ++ if (Buffer == NULL) { ++ return EFI_INVALID_PARAMETER; ++ } ++ ++ // Return if we have not any byte to read ++ if (BufferSizeInBytes == 0) { ++ return EFI_SUCCESS; ++ } ++ ++ if (((Lba * Instance->Media.BlockSize) + Offset + BufferSizeInBytes) > Instance->Size) { ++ DEBUG ((DEBUG_ERROR, "NorFlashRead: ERROR - Read will exceed device size.\n")); ++ return EFI_INVALID_PARAMETER; ++ } ++ ++ // Get the address to start reading from ++ StartAddress = GET_NOR_BLOCK_ADDRESS ( ++ Instance->RegionBaseAddress, ++ Lba, ++ Instance->Media.BlockSize ++ ); ++ ++ // Put the device into Read Array mode ++ SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY); ++ ++ // Readout the data ++ AlignedCopyMem (Buffer, (VOID *)(StartAddress + Offset), BufferSizeInBytes); ++ ++ return EFI_SUCCESS; ++} ++ ++/* ++ Write a full or portion of a block. It must not span block boundaries; that is, ++ Offset + *NumBytes <= Instance->Media.BlockSize. ++*/ ++EFI_STATUS ++NorFlashWriteSingleBlock ( ++ IN NOR_FLASH_INSTANCE *Instance, ++ IN EFI_LBA Lba, ++ IN UINTN Offset, ++ IN OUT UINTN *NumBytes, ++ IN UINT8 *Buffer ++ ) ++{ ++ EFI_STATUS TempStatus; ++ UINT32 Tmp; ++ UINT32 TmpBuf; ++ UINT32 WordToWrite; ++ UINT32 Mask; ++ BOOLEAN DoErase; ++ UINTN BytesToWrite; ++ UINTN CurOffset; ++ UINTN WordAddr; ++ UINTN BlockSize; ++ UINTN BlockAddress; ++ UINTN PrevBlockAddress; ++ ++ PrevBlockAddress = 0; ++ ++ DEBUG ((DEBUG_BLKIO, "NorFlashWriteSingleBlock(Parameters: Lba=%ld, Offset=0x%x, *NumBytes=0x%x, Buffer @ 0x%08x)\n", Lba, Offset, *NumBytes, Buffer)); ++ ++ // Detect WriteDisabled state ++ if (Instance->Media.ReadOnly == TRUE) { ++ DEBUG ((DEBUG_ERROR, "NorFlashWriteSingleBlock: ERROR - Can not write: Device is in WriteDisabled state.\n")); ++ // It is in WriteDisabled state, return an error right away ++ return EFI_ACCESS_DENIED; ++ } ++ ++ // Cache the block size to avoid de-referencing pointers all the time ++ BlockSize = Instance->Media.BlockSize; ++ ++ // The write must not span block boundaries. ++ // We need to check each variable individually because adding two large values together overflows. ++ if ((Offset >= BlockSize) || ++ (*NumBytes > BlockSize) || ++ ((Offset + *NumBytes) > BlockSize)) ++ { ++ DEBUG ((DEBUG_ERROR, "NorFlashWriteSingleBlock: ERROR - EFI_BAD_BUFFER_SIZE: (Offset=0x%x + NumBytes=0x%x) > BlockSize=0x%x\n", Offset, *NumBytes, BlockSize)); ++ return EFI_BAD_BUFFER_SIZE; ++ } ++ ++ // We must have some bytes to write ++ if (*NumBytes == 0) { ++ DEBUG ((DEBUG_ERROR, "NorFlashWriteSingleBlock: ERROR - EFI_BAD_BUFFER_SIZE: (Offset=0x%x + NumBytes=0x%x) > BlockSize=0x%x\n", Offset, *NumBytes, BlockSize)); ++ return EFI_BAD_BUFFER_SIZE; ++ } ++ ++ // Pick 128bytes as a good start for word operations as opposed to erasing the ++ // block and writing the data regardless if an erase is really needed. ++ // It looks like most individual NV variable writes are smaller than 128bytes. ++ if (*NumBytes <= 128) { ++ // Check to see if we need to erase before programming the data into NOR. ++ // If the destination bits are only changing from 1s to 0s we can just write. ++ // After a block is erased all bits in the block is set to 1. ++ // If any byte requires us to erase we just give up and rewrite all of it. ++ DoErase = FALSE; ++ BytesToWrite = *NumBytes; ++ CurOffset = Offset; ++ ++ while (BytesToWrite > 0) { ++ // Read full word from NOR, splice as required. A word is the smallest ++ // unit we can write. ++ TempStatus = NorFlashRead (Instance, Lba, CurOffset & ~(0x3), sizeof (Tmp), &Tmp); ++ if (EFI_ERROR (TempStatus)) { ++ return EFI_DEVICE_ERROR; ++ } ++ ++ // Physical address of word in NOR to write. ++ WordAddr = (CurOffset & ~(0x3)) + GET_NOR_BLOCK_ADDRESS ( ++ Instance->RegionBaseAddress, ++ Lba, ++ BlockSize ++ ); ++ // The word of data that is to be written. ++ TmpBuf = *((UINT32 *)(Buffer + (*NumBytes - BytesToWrite))); ++ ++ // First do word aligned chunks. ++ if ((CurOffset & 0x3) == 0) { ++ if (BytesToWrite >= 4) { ++ // Is the destination still in 'erased' state? ++ if (~Tmp != 0) { ++ // Check to see if we are only changing bits to zero. ++ if ((Tmp ^ TmpBuf) & TmpBuf) { ++ DoErase = TRUE; ++ break; ++ } ++ } ++ ++ // Write this word to NOR ++ WordToWrite = TmpBuf; ++ CurOffset += sizeof (TmpBuf); ++ BytesToWrite -= sizeof (TmpBuf); ++ } else { ++ // BytesToWrite < 4. Do small writes and left-overs ++ Mask = ~((~0) << (BytesToWrite * 8)); ++ // Mask out the bytes we want. ++ TmpBuf &= Mask; ++ // Is the destination still in 'erased' state? ++ if ((Tmp & Mask) != Mask) { ++ // Check to see if we are only changing bits to zero. ++ if ((Tmp ^ TmpBuf) & TmpBuf) { ++ DoErase = TRUE; ++ break; ++ } ++ } ++ ++ // Merge old and new data. Write merged word to NOR ++ WordToWrite = (Tmp & ~Mask) | TmpBuf; ++ CurOffset += BytesToWrite; ++ BytesToWrite = 0; ++ } ++ } else { ++ // Do multiple words, but starting unaligned. ++ if (BytesToWrite > (4 - (CurOffset & 0x3))) { ++ Mask = ((~0) << ((CurOffset & 0x3) * 8)); ++ // Mask out the bytes we want. ++ TmpBuf &= Mask; ++ // Is the destination still in 'erased' state? ++ if ((Tmp & Mask) != Mask) { ++ // Check to see if we are only changing bits to zero. ++ if ((Tmp ^ TmpBuf) & TmpBuf) { ++ DoErase = TRUE; ++ break; ++ } ++ } ++ ++ // Merge old and new data. Write merged word to NOR ++ WordToWrite = (Tmp & ~Mask) | TmpBuf; ++ BytesToWrite -= (4 - (CurOffset & 0x3)); ++ CurOffset += (4 - (CurOffset & 0x3)); ++ } else { ++ // Unaligned and fits in one word. ++ Mask = (~((~0) << (BytesToWrite * 8))) << ((CurOffset & 0x3) * 8); ++ // Mask out the bytes we want. ++ TmpBuf = (TmpBuf << ((CurOffset & 0x3) * 8)) & Mask; ++ // Is the destination still in 'erased' state? ++ if ((Tmp & Mask) != Mask) { ++ // Check to see if we are only changing bits to zero. ++ if ((Tmp ^ TmpBuf) & TmpBuf) { ++ DoErase = TRUE; ++ break; ++ } ++ } ++ ++ // Merge old and new data. Write merged word to NOR ++ WordToWrite = (Tmp & ~Mask) | TmpBuf; ++ CurOffset += BytesToWrite; ++ BytesToWrite = 0; ++ } ++ } ++ ++ // ++ // Write the word to NOR. ++ // ++ ++ BlockAddress = GET_NOR_BLOCK_ADDRESS (Instance->RegionBaseAddress, Lba, BlockSize); ++ if (BlockAddress != PrevBlockAddress) { ++ TempStatus = NorFlashUnlockSingleBlockIfNecessary (Instance, BlockAddress); ++ if (EFI_ERROR (TempStatus)) { ++ return EFI_DEVICE_ERROR; ++ } ++ ++ PrevBlockAddress = BlockAddress; ++ } ++ ++ TempStatus = NorFlashWriteSingleWord (Instance, WordAddr, WordToWrite); ++ if (EFI_ERROR (TempStatus)) { ++ return EFI_DEVICE_ERROR; ++ } ++ } ++ ++ // Exit if we got here and could write all the data. Otherwise do the ++ // Erase-Write cycle. ++ if (!DoErase) { ++ return EFI_SUCCESS; ++ } ++ } ++ ++ // Check we did get some memory. Buffer is BlockSize. ++ if (Instance->ShadowBuffer == NULL) { ++ DEBUG ((DEBUG_ERROR, "FvbWrite: ERROR - Buffer not ready\n")); ++ return EFI_DEVICE_ERROR; ++ } ++ ++ // Read NOR Flash data into shadow buffer ++ TempStatus = NorFlashReadBlocks (Instance, Lba, BlockSize, Instance->ShadowBuffer); ++ if (EFI_ERROR (TempStatus)) { ++ // Return one of the pre-approved error statuses ++ return EFI_DEVICE_ERROR; ++ } ++ ++ // Put the data at the appropriate location inside the buffer area ++ CopyMem ((VOID *)((UINTN)Instance->ShadowBuffer + Offset), Buffer, *NumBytes); ++ ++ // Write the modified buffer back to the NorFlash ++ TempStatus = NorFlashWriteBlocks (Instance, Lba, BlockSize, Instance->ShadowBuffer); ++ if (EFI_ERROR (TempStatus)) { ++ // Return one of the pre-approved error statuses ++ return EFI_DEVICE_ERROR; ++ } ++ ++ return EFI_SUCCESS; ++} ++ ++/* ++ Although DiskIoDxe will automatically install the DiskIO protocol whenever ++ we install the BlockIO protocol, its implementation is sub-optimal as it reads ++ and writes entire blocks using the BlockIO protocol. In fact we can access ++ NOR flash with a finer granularity than that, so we can improve performance ++ by directly producing the DiskIO protocol. ++*/ ++ ++/** ++ Read BufferSize bytes from Offset into Buffer. ++ ++ @param This Protocol instance pointer. ++ @param MediaId Id of the media, changes every time the media is replaced. ++ @param Offset The starting byte offset to read from ++ @param BufferSize Size of Buffer ++ @param Buffer Buffer containing read data ++ ++ @retval EFI_SUCCESS The data was read correctly from the device. ++ @retval EFI_DEVICE_ERROR The device reported an error while performing the read. ++ @retval EFI_NO_MEDIA There is no media in the device. ++ @retval EFI_MEDIA_CHANGED The MediaId does not match the current device. ++ @retval EFI_INVALID_PARAMETER The read request contains device addresses that are not ++ valid for the device. ++ ++**/ ++EFI_STATUS ++EFIAPI ++NorFlashDiskIoReadDisk ( ++ IN EFI_DISK_IO_PROTOCOL *This, ++ IN UINT32 MediaId, ++ IN UINT64 DiskOffset, ++ IN UINTN BufferSize, ++ OUT VOID *Buffer ++ ) ++{ ++ NOR_FLASH_INSTANCE *Instance; ++ UINT32 BlockSize; ++ UINT32 BlockOffset; ++ EFI_LBA Lba; ++ ++ Instance = INSTANCE_FROM_DISKIO_THIS (This); ++ ++ if (MediaId != Instance->Media.MediaId) { ++ return EFI_MEDIA_CHANGED; ++ } ++ ++ BlockSize = Instance->Media.BlockSize; ++ Lba = (EFI_LBA)DivU64x32Remainder (DiskOffset, BlockSize, &BlockOffset); ++ ++ return NorFlashRead (Instance, Lba, BlockOffset, BufferSize, Buffer); ++} ++ ++/** ++ Writes a specified number of bytes to a device. ++ ++ @param This Indicates a pointer to the calling context. ++ @param MediaId ID of the medium to be written. ++ @param Offset The starting byte offset on the logical block I/O device to write. ++ @param BufferSize The size in bytes of Buffer. The number of bytes to write to the device. ++ @param Buffer A pointer to the buffer containing the data to be written. ++ ++ @retval EFI_SUCCESS The data was written correctly to the device. ++ @retval EFI_WRITE_PROTECTED The device can not be written to. ++ @retval EFI_DEVICE_ERROR The device reported an error while performing the write. ++ @retval EFI_NO_MEDIA There is no media in the device. ++ @retval EFI_MEDIA_CHANGED The MediaId does not match the current device. ++ @retval EFI_INVALID_PARAMETER The write request contains device addresses that are not ++ valid for the device. ++ ++**/ ++EFI_STATUS ++EFIAPI ++NorFlashDiskIoWriteDisk ( ++ IN EFI_DISK_IO_PROTOCOL *This, ++ IN UINT32 MediaId, ++ IN UINT64 DiskOffset, ++ IN UINTN BufferSize, ++ IN VOID *Buffer ++ ) ++{ ++ NOR_FLASH_INSTANCE *Instance; ++ UINT32 BlockSize; ++ UINT32 BlockOffset; ++ EFI_LBA Lba; ++ UINTN RemainingBytes; ++ UINTN WriteSize; ++ EFI_STATUS Status; ++ ++ Instance = INSTANCE_FROM_DISKIO_THIS (This); ++ ++ if (MediaId != Instance->Media.MediaId) { ++ return EFI_MEDIA_CHANGED; ++ } ++ ++ BlockSize = Instance->Media.BlockSize; ++ Lba = (EFI_LBA)DivU64x32Remainder (DiskOffset, BlockSize, &BlockOffset); ++ ++ RemainingBytes = BufferSize; ++ ++ // Write either all the remaining bytes, or the number of bytes that bring ++ // us up to a block boundary, whichever is less. ++ // (DiskOffset | (BlockSize - 1)) + 1) rounds DiskOffset up to the next ++ // block boundary (even if it is already on one). ++ WriteSize = MIN (RemainingBytes, ((DiskOffset | (BlockSize - 1)) + 1) - DiskOffset); ++ ++ do { ++ if (WriteSize == BlockSize) { ++ // Write a full block ++ Status = NorFlashWriteFullBlock (Instance, Lba, Buffer, BlockSize / sizeof (UINT32)); ++ } else { ++ // Write a partial block ++ Status = NorFlashWriteSingleBlock (Instance, Lba, BlockOffset, &WriteSize, Buffer); ++ } ++ ++ if (EFI_ERROR (Status)) { ++ return Status; ++ } ++ ++ // Now continue writing either all the remaining bytes or single blocks. ++ RemainingBytes -= WriteSize; ++ Buffer = (UINT8 *)Buffer + WriteSize; ++ Lba++; ++ BlockOffset = 0; ++ WriteSize = MIN (RemainingBytes, BlockSize); ++ } while (RemainingBytes); ++ ++ return Status; ++} ++ ++EFI_STATUS ++NorFlashReset ( ++ IN NOR_FLASH_INSTANCE *Instance ++ ) ++{ ++ // As there is no specific RESET to perform, ensure that the devices is in the default Read Array mode ++ SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY); ++ return EFI_SUCCESS; ++} ++ ++/** ++ Fixup internal data so that EFI can be call in virtual mode. ++ Call the passed in Child Notify event and convert any pointers in ++ lib to virtual mode. ++ ++ @param[in] Event The Event that is being processed ++ @param[in] Context Event Context ++**/ ++VOID ++EFIAPI ++NorFlashVirtualNotifyEvent ( ++ IN EFI_EVENT Event, ++ IN VOID *Context ++ ) ++{ ++ UINTN Index; ++ ++ for (Index = 0; Index < mNorFlashDeviceCount; Index++) { ++ EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->DeviceBaseAddress); ++ EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->RegionBaseAddress); ++ ++ // Convert BlockIo protocol ++ EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->BlockIoProtocol.FlushBlocks); ++ EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->BlockIoProtocol.ReadBlocks); ++ EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->BlockIoProtocol.Reset); ++ EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->BlockIoProtocol.WriteBlocks); ++ ++ // Convert Fvb ++ EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->FvbProtocol.EraseBlocks); ++ EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->FvbProtocol.GetAttributes); ++ EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->FvbProtocol.GetBlockSize); ++ EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->FvbProtocol.GetPhysicalAddress); ++ EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->FvbProtocol.Read); ++ EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->FvbProtocol.SetAttributes); ++ EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->FvbProtocol.Write); ++ ++ if (mNorFlashInstances[Index]->ShadowBuffer != NULL) { ++ EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->ShadowBuffer); ++ } ++ } ++ ++ return; ++} +diff --git a/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.h b/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.h +new file mode 100644 +index 0000000000..e46522a198 +--- /dev/null ++++ b/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.h +@@ -0,0 +1,422 @@ ++/** @file NorFlash.h ++ ++ Copyright (c) 2011 - 2014, ARM Ltd. All rights reserved.
++ ++ SPDX-License-Identifier: BSD-2-Clause-Patent ++ ++**/ ++ ++#ifndef __VIRT_NOR_FLASH__ ++#define __VIRT_NOR_FLASH__ ++ ++#include ++#include ++ ++#include ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#define NOR_FLASH_ERASE_RETRY 10 ++ ++// Device access macros ++// These are necessary because we use 2 x 16bit parts to make up 32bit data ++ ++#define HIGH_16_BITS 0xFFFF0000 ++#define LOW_16_BITS 0x0000FFFF ++#define LOW_8_BITS 0x000000FF ++ ++#define FOLD_32BIT_INTO_16BIT(value) ( ( value >> 16 ) | ( value & LOW_16_BITS ) ) ++ ++#define GET_LOW_BYTE(value) ( value & LOW_8_BITS ) ++#define GET_HIGH_BYTE(value) ( GET_LOW_BYTE( value >> 16 ) ) ++ ++// Each command must be sent simultaneously to both chips, ++// i.e. at the lower 16 bits AND at the higher 16 bits ++#define CREATE_NOR_ADDRESS(BaseAddr, OffsetAddr) ((BaseAddr) + ((OffsetAddr) << 2)) ++#define CREATE_DUAL_CMD(Cmd) ( ( Cmd << 16) | ( Cmd & LOW_16_BITS) ) ++#define SEND_NOR_COMMAND(BaseAddr, Offset, Cmd) MmioWrite32 (CREATE_NOR_ADDRESS(BaseAddr,Offset), CREATE_DUAL_CMD(Cmd)) ++#define GET_NOR_BLOCK_ADDRESS(BaseAddr, Lba, LbaSize) ( BaseAddr + (UINTN)((Lba) * LbaSize) ) ++ ++// Status Register Bits ++#define P30_SR_BIT_WRITE (BIT7 << 16 | BIT7) ++#define P30_SR_BIT_ERASE_SUSPEND (BIT6 << 16 | BIT6) ++#define P30_SR_BIT_ERASE (BIT5 << 16 | BIT5) ++#define P30_SR_BIT_PROGRAM (BIT4 << 16 | BIT4) ++#define P30_SR_BIT_VPP (BIT3 << 16 | BIT3) ++#define P30_SR_BIT_PROGRAM_SUSPEND (BIT2 << 16 | BIT2) ++#define P30_SR_BIT_BLOCK_LOCKED (BIT1 << 16 | BIT1) ++#define P30_SR_BIT_BEFP (BIT0 << 16 | BIT0) ++ ++// Device Commands for Intel StrataFlash(R) Embedded Memory (P30) Family ++ ++// On chip buffer size for buffered programming operations ++// There are 2 chips, each chip can buffer up to 32 (16-bit)words, and each word is 2 bytes. ++// Therefore the total size of the buffer is 2 x 32 x 2 = 128 bytes ++#define P30_MAX_BUFFER_SIZE_IN_BYTES ((UINTN)128) ++#define P30_MAX_BUFFER_SIZE_IN_WORDS (P30_MAX_BUFFER_SIZE_IN_BYTES/((UINTN)4)) ++#define MAX_BUFFERED_PROG_ITERATIONS 10000000 ++#define BOUNDARY_OF_32_WORDS 0x7F ++ ++// CFI Addresses ++#define P30_CFI_ADDR_QUERY_UNIQUE_QRY 0x10 ++#define P30_CFI_ADDR_VENDOR_ID 0x13 ++ ++// CFI Data ++#define CFI_QRY 0x00595251 ++ ++// READ Commands ++#define P30_CMD_READ_DEVICE_ID 0x0090 ++#define P30_CMD_READ_STATUS_REGISTER 0x0070 ++#define P30_CMD_CLEAR_STATUS_REGISTER 0x0050 ++#define P30_CMD_READ_ARRAY 0x00FF ++#define P30_CMD_READ_CFI_QUERY 0x0098 ++ ++// WRITE Commands ++#define P30_CMD_WORD_PROGRAM_SETUP 0x0040 ++#define P30_CMD_ALTERNATE_WORD_PROGRAM_SETUP 0x0010 ++#define P30_CMD_BUFFERED_PROGRAM_SETUP 0x00E8 ++#define P30_CMD_BUFFERED_PROGRAM_CONFIRM 0x00D0 ++#define P30_CMD_BEFP_SETUP 0x0080 ++#define P30_CMD_BEFP_CONFIRM 0x00D0 ++ ++// ERASE Commands ++#define P30_CMD_BLOCK_ERASE_SETUP 0x0020 ++#define P30_CMD_BLOCK_ERASE_CONFIRM 0x00D0 ++ ++// SUSPEND Commands ++#define P30_CMD_PROGRAM_OR_ERASE_SUSPEND 0x00B0 ++#define P30_CMD_SUSPEND_RESUME 0x00D0 ++ ++// BLOCK LOCKING / UNLOCKING Commands ++#define P30_CMD_LOCK_BLOCK_SETUP 0x0060 ++#define P30_CMD_LOCK_BLOCK 0x0001 ++#define P30_CMD_UNLOCK_BLOCK 0x00D0 ++#define P30_CMD_LOCK_DOWN_BLOCK 0x002F ++ ++// PROTECTION Commands ++#define P30_CMD_PROGRAM_PROTECTION_REGISTER_SETUP 0x00C0 ++ ++// CONFIGURATION Commands ++#define P30_CMD_READ_CONFIGURATION_REGISTER_SETUP 0x0060 ++#define P30_CMD_READ_CONFIGURATION_REGISTER 0x0003 ++ ++#define NOR_FLASH_SIGNATURE SIGNATURE_32('n', 'o', 'r', '0') ++#define INSTANCE_FROM_FVB_THIS(a) CR(a, NOR_FLASH_INSTANCE, FvbProtocol, NOR_FLASH_SIGNATURE) ++#define INSTANCE_FROM_BLKIO_THIS(a) CR(a, NOR_FLASH_INSTANCE, BlockIoProtocol, NOR_FLASH_SIGNATURE) ++#define INSTANCE_FROM_DISKIO_THIS(a) CR(a, NOR_FLASH_INSTANCE, DiskIoProtocol, NOR_FLASH_SIGNATURE) ++ ++typedef struct _NOR_FLASH_INSTANCE NOR_FLASH_INSTANCE; ++ ++#pragma pack (1) ++typedef struct { ++ VENDOR_DEVICE_PATH Vendor; ++ UINT8 Index; ++ EFI_DEVICE_PATH_PROTOCOL End; ++} NOR_FLASH_DEVICE_PATH; ++#pragma pack () ++ ++struct _NOR_FLASH_INSTANCE { ++ UINT32 Signature; ++ EFI_HANDLE Handle; ++ ++ UINTN DeviceBaseAddress; ++ UINTN RegionBaseAddress; ++ UINTN Size; ++ EFI_LBA StartLba; ++ ++ EFI_BLOCK_IO_PROTOCOL BlockIoProtocol; ++ EFI_BLOCK_IO_MEDIA Media; ++ EFI_DISK_IO_PROTOCOL DiskIoProtocol; ++ ++ EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL FvbProtocol; ++ VOID *ShadowBuffer; ++ ++ NOR_FLASH_DEVICE_PATH DevicePath; ++}; ++ ++EFI_STATUS ++NorFlashReadCfiData ( ++ IN UINTN DeviceBaseAddress, ++ IN UINTN CFI_Offset, ++ IN UINT32 NumberOfBytes, ++ OUT UINT32 *Data ++ ); ++ ++EFI_STATUS ++NorFlashWriteBuffer ( ++ IN NOR_FLASH_INSTANCE *Instance, ++ IN UINTN TargetAddress, ++ IN UINTN BufferSizeInBytes, ++ IN UINT32 *Buffer ++ ); ++ ++// ++// BlockIO Protocol function EFI_BLOCK_IO_PROTOCOL.Reset ++// ++EFI_STATUS ++EFIAPI ++NorFlashBlockIoReset ( ++ IN EFI_BLOCK_IO_PROTOCOL *This, ++ IN BOOLEAN ExtendedVerification ++ ); ++ ++// ++// BlockIO Protocol function EFI_BLOCK_IO_PROTOCOL.ReadBlocks ++// ++EFI_STATUS ++EFIAPI ++NorFlashBlockIoReadBlocks ( ++ IN EFI_BLOCK_IO_PROTOCOL *This, ++ IN UINT32 MediaId, ++ IN EFI_LBA Lba, ++ IN UINTN BufferSizeInBytes, ++ OUT VOID *Buffer ++ ); ++ ++// ++// BlockIO Protocol function EFI_BLOCK_IO_PROTOCOL.WriteBlocks ++// ++EFI_STATUS ++EFIAPI ++NorFlashBlockIoWriteBlocks ( ++ IN EFI_BLOCK_IO_PROTOCOL *This, ++ IN UINT32 MediaId, ++ IN EFI_LBA Lba, ++ IN UINTN BufferSizeInBytes, ++ IN VOID *Buffer ++ ); ++ ++// ++// BlockIO Protocol function EFI_BLOCK_IO_PROTOCOL.FlushBlocks ++// ++EFI_STATUS ++EFIAPI ++NorFlashBlockIoFlushBlocks ( ++ IN EFI_BLOCK_IO_PROTOCOL *This ++ ); ++ ++// ++// DiskIO Protocol function EFI_DISK_IO_PROTOCOL.ReadDisk ++// ++EFI_STATUS ++EFIAPI ++NorFlashDiskIoReadDisk ( ++ IN EFI_DISK_IO_PROTOCOL *This, ++ IN UINT32 MediaId, ++ IN UINT64 Offset, ++ IN UINTN BufferSize, ++ OUT VOID *Buffer ++ ); ++ ++// ++// DiskIO Protocol function EFI_DISK_IO_PROTOCOL.WriteDisk ++// ++EFI_STATUS ++EFIAPI ++NorFlashDiskIoWriteDisk ( ++ IN EFI_DISK_IO_PROTOCOL *This, ++ IN UINT32 MediaId, ++ IN UINT64 Offset, ++ IN UINTN BufferSize, ++ IN VOID *Buffer ++ ); ++ ++// ++// NorFlashFvbDxe.c ++// ++ ++EFI_STATUS ++EFIAPI ++FvbGetAttributes ( ++ IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This, ++ OUT EFI_FVB_ATTRIBUTES_2 *Attributes ++ ); ++ ++EFI_STATUS ++EFIAPI ++FvbSetAttributes ( ++ IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This, ++ IN OUT EFI_FVB_ATTRIBUTES_2 *Attributes ++ ); ++ ++EFI_STATUS ++EFIAPI ++FvbGetPhysicalAddress ( ++ IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This, ++ OUT EFI_PHYSICAL_ADDRESS *Address ++ ); ++ ++EFI_STATUS ++EFIAPI ++FvbGetBlockSize ( ++ IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This, ++ IN EFI_LBA Lba, ++ OUT UINTN *BlockSize, ++ OUT UINTN *NumberOfBlocks ++ ); ++ ++EFI_STATUS ++EFIAPI ++FvbRead ( ++ IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This, ++ IN EFI_LBA Lba, ++ IN UINTN Offset, ++ IN OUT UINTN *NumBytes, ++ IN OUT UINT8 *Buffer ++ ); ++ ++EFI_STATUS ++EFIAPI ++FvbWrite ( ++ IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This, ++ IN EFI_LBA Lba, ++ IN UINTN Offset, ++ IN OUT UINTN *NumBytes, ++ IN UINT8 *Buffer ++ ); ++ ++EFI_STATUS ++EFIAPI ++FvbEraseBlocks ( ++ IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This, ++ ... ++ ); ++ ++EFI_STATUS ++ValidateFvHeader ( ++ IN NOR_FLASH_INSTANCE *Instance ++ ); ++ ++EFI_STATUS ++InitializeFvAndVariableStoreHeaders ( ++ IN NOR_FLASH_INSTANCE *Instance ++ ); ++ ++VOID ++EFIAPI ++FvbVirtualNotifyEvent ( ++ IN EFI_EVENT Event, ++ IN VOID *Context ++ ); ++ ++// ++// NorFlashDxe.c ++// ++ ++EFI_STATUS ++NorFlashWriteFullBlock ( ++ IN NOR_FLASH_INSTANCE *Instance, ++ IN EFI_LBA Lba, ++ IN UINT32 *DataBuffer, ++ IN UINT32 BlockSizeInWords ++ ); ++ ++EFI_STATUS ++NorFlashUnlockAndEraseSingleBlock ( ++ IN NOR_FLASH_INSTANCE *Instance, ++ IN UINTN BlockAddress ++ ); ++ ++EFI_STATUS ++NorFlashCreateInstance ( ++ IN UINTN NorFlashDeviceBase, ++ IN UINTN NorFlashRegionBase, ++ IN UINTN NorFlashSize, ++ IN UINT32 Index, ++ IN UINT32 BlockSize, ++ IN BOOLEAN SupportFvb, ++ OUT NOR_FLASH_INSTANCE **NorFlashInstance ++ ); ++ ++EFI_STATUS ++EFIAPI ++NorFlashFvbInitialize ( ++ IN NOR_FLASH_INSTANCE *Instance ++ ); ++ ++// ++// NorFlash.c ++// ++EFI_STATUS ++NorFlashWriteSingleBlock ( ++ IN NOR_FLASH_INSTANCE *Instance, ++ IN EFI_LBA Lba, ++ IN UINTN Offset, ++ IN OUT UINTN *NumBytes, ++ IN UINT8 *Buffer ++ ); ++ ++EFI_STATUS ++NorFlashWriteBlocks ( ++ IN NOR_FLASH_INSTANCE *Instance, ++ IN EFI_LBA Lba, ++ IN UINTN BufferSizeInBytes, ++ IN VOID *Buffer ++ ); ++ ++EFI_STATUS ++NorFlashReadBlocks ( ++ IN NOR_FLASH_INSTANCE *Instance, ++ IN EFI_LBA Lba, ++ IN UINTN BufferSizeInBytes, ++ OUT VOID *Buffer ++ ); ++ ++EFI_STATUS ++NorFlashRead ( ++ IN NOR_FLASH_INSTANCE *Instance, ++ IN EFI_LBA Lba, ++ IN UINTN Offset, ++ IN UINTN BufferSizeInBytes, ++ OUT VOID *Buffer ++ ); ++ ++EFI_STATUS ++NorFlashWrite ( ++ IN NOR_FLASH_INSTANCE *Instance, ++ IN EFI_LBA Lba, ++ IN UINTN Offset, ++ IN OUT UINTN *NumBytes, ++ IN UINT8 *Buffer ++ ); ++ ++EFI_STATUS ++NorFlashReset ( ++ IN NOR_FLASH_INSTANCE *Instance ++ ); ++ ++EFI_STATUS ++NorFlashEraseSingleBlock ( ++ IN NOR_FLASH_INSTANCE *Instance, ++ IN UINTN BlockAddress ++ ); ++ ++EFI_STATUS ++NorFlashUnlockSingleBlockIfNecessary ( ++ IN NOR_FLASH_INSTANCE *Instance, ++ IN UINTN BlockAddress ++ ); ++ ++EFI_STATUS ++NorFlashWriteSingleWord ( ++ IN NOR_FLASH_INSTANCE *Instance, ++ IN UINTN WordAddress, ++ IN UINT32 WriteData ++ ); ++ ++VOID ++EFIAPI ++NorFlashVirtualNotifyEvent ( ++ IN EFI_EVENT Event, ++ IN VOID *Context ++ ); ++ ++#endif /* __VIRT_NOR_FLASH__ */ +diff --git a/OvmfPkg/VirtNorFlashDxe/VirtNorFlashBlockIoDxe.c b/OvmfPkg/VirtNorFlashDxe/VirtNorFlashBlockIoDxe.c +new file mode 100644 +index 0000000000..ecf152e355 +--- /dev/null ++++ b/OvmfPkg/VirtNorFlashDxe/VirtNorFlashBlockIoDxe.c +@@ -0,0 +1,123 @@ ++/** @file NorFlashBlockIoDxe.c ++ ++ Copyright (c) 2011-2013, ARM Ltd. All rights reserved.
++ ++ SPDX-License-Identifier: BSD-2-Clause-Patent ++ ++**/ ++ ++#include ++#include ++ ++#include "VirtNorFlash.h" ++ ++// ++// BlockIO Protocol function EFI_BLOCK_IO_PROTOCOL.Reset ++// ++EFI_STATUS ++EFIAPI ++NorFlashBlockIoReset ( ++ IN EFI_BLOCK_IO_PROTOCOL *This, ++ IN BOOLEAN ExtendedVerification ++ ) ++{ ++ NOR_FLASH_INSTANCE *Instance; ++ ++ Instance = INSTANCE_FROM_BLKIO_THIS (This); ++ ++ DEBUG ((DEBUG_BLKIO, "NorFlashBlockIoReset(MediaId=0x%x)\n", This->Media->MediaId)); ++ ++ return NorFlashReset (Instance); ++} ++ ++// ++// BlockIO Protocol function EFI_BLOCK_IO_PROTOCOL.ReadBlocks ++// ++EFI_STATUS ++EFIAPI ++NorFlashBlockIoReadBlocks ( ++ IN EFI_BLOCK_IO_PROTOCOL *This, ++ IN UINT32 MediaId, ++ IN EFI_LBA Lba, ++ IN UINTN BufferSizeInBytes, ++ OUT VOID *Buffer ++ ) ++{ ++ NOR_FLASH_INSTANCE *Instance; ++ EFI_STATUS Status; ++ EFI_BLOCK_IO_MEDIA *Media; ++ ++ if (This == NULL) { ++ return EFI_INVALID_PARAMETER; ++ } ++ ++ Instance = INSTANCE_FROM_BLKIO_THIS (This); ++ Media = This->Media; ++ ++ DEBUG ((DEBUG_BLKIO, "NorFlashBlockIoReadBlocks(MediaId=0x%x, Lba=%ld, BufferSize=0x%x bytes (%d kB), BufferPtr @ 0x%08x)\n", MediaId, Lba, BufferSizeInBytes, BufferSizeInBytes, Buffer)); ++ ++ if (!Media) { ++ Status = EFI_INVALID_PARAMETER; ++ } else if (!Media->MediaPresent) { ++ Status = EFI_NO_MEDIA; ++ } else if (Media->MediaId != MediaId) { ++ Status = EFI_MEDIA_CHANGED; ++ } else if ((Media->IoAlign > 2) && (((UINTN)Buffer & (Media->IoAlign - 1)) != 0)) { ++ Status = EFI_INVALID_PARAMETER; ++ } else { ++ Status = NorFlashReadBlocks (Instance, Lba, BufferSizeInBytes, Buffer); ++ } ++ ++ return Status; ++} ++ ++// ++// BlockIO Protocol function EFI_BLOCK_IO_PROTOCOL.WriteBlocks ++// ++EFI_STATUS ++EFIAPI ++NorFlashBlockIoWriteBlocks ( ++ IN EFI_BLOCK_IO_PROTOCOL *This, ++ IN UINT32 MediaId, ++ IN EFI_LBA Lba, ++ IN UINTN BufferSizeInBytes, ++ IN VOID *Buffer ++ ) ++{ ++ NOR_FLASH_INSTANCE *Instance; ++ EFI_STATUS Status; ++ ++ Instance = INSTANCE_FROM_BLKIO_THIS (This); ++ ++ DEBUG ((DEBUG_BLKIO, "NorFlashBlockIoWriteBlocks(MediaId=0x%x, Lba=%ld, BufferSize=0x%x bytes, BufferPtr @ 0x%08x)\n", MediaId, Lba, BufferSizeInBytes, Buffer)); ++ ++ if ( !This->Media->MediaPresent ) { ++ Status = EFI_NO_MEDIA; ++ } else if ( This->Media->MediaId != MediaId ) { ++ Status = EFI_MEDIA_CHANGED; ++ } else if ( This->Media->ReadOnly ) { ++ Status = EFI_WRITE_PROTECTED; ++ } else { ++ Status = NorFlashWriteBlocks (Instance, Lba, BufferSizeInBytes, Buffer); ++ } ++ ++ return Status; ++} ++ ++// ++// BlockIO Protocol function EFI_BLOCK_IO_PROTOCOL.FlushBlocks ++// ++EFI_STATUS ++EFIAPI ++NorFlashBlockIoFlushBlocks ( ++ IN EFI_BLOCK_IO_PROTOCOL *This ++ ) ++{ ++ // No Flush required for the NOR Flash driver ++ // because cache operations are not permitted. ++ ++ DEBUG ((DEBUG_BLKIO, "NorFlashBlockIoFlushBlocks: Function NOT IMPLEMENTED (not required).\n")); ++ ++ // Nothing to do so just return without error ++ return EFI_SUCCESS; ++} +diff --git a/OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.c b/OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.c +new file mode 100644 +index 0000000000..819425545e +--- /dev/null ++++ b/OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.c +@@ -0,0 +1,506 @@ ++/** @file NorFlashDxe.c ++ ++ Copyright (c) 2011 - 2021, Arm Limited. All rights reserved.
++ ++ SPDX-License-Identifier: BSD-2-Clause-Patent ++ ++**/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "VirtNorFlash.h" ++ ++STATIC EFI_EVENT mNorFlashVirtualAddrChangeEvent; ++ ++// ++// Global variable declarations ++// ++NOR_FLASH_INSTANCE **mNorFlashInstances; ++UINT32 mNorFlashDeviceCount; ++UINTN mFlashNvStorageVariableBase; ++EFI_EVENT mFvbVirtualAddrChangeEvent; ++ ++NOR_FLASH_INSTANCE mNorFlashInstanceTemplate = { ++ NOR_FLASH_SIGNATURE, // Signature ++ NULL, // Handle ... NEED TO BE FILLED ++ ++ 0, // DeviceBaseAddress ... NEED TO BE FILLED ++ 0, // RegionBaseAddress ... NEED TO BE FILLED ++ 0, // Size ... NEED TO BE FILLED ++ 0, // StartLba ++ ++ { ++ EFI_BLOCK_IO_PROTOCOL_REVISION2, // Revision ++ NULL, // Media ... NEED TO BE FILLED ++ NorFlashBlockIoReset, // Reset; ++ NorFlashBlockIoReadBlocks, // ReadBlocks ++ NorFlashBlockIoWriteBlocks, // WriteBlocks ++ NorFlashBlockIoFlushBlocks // FlushBlocks ++ }, // BlockIoProtocol ++ ++ { ++ 0, // MediaId ... NEED TO BE FILLED ++ FALSE, // RemovableMedia ++ TRUE, // MediaPresent ++ FALSE, // LogicalPartition ++ FALSE, // ReadOnly ++ FALSE, // WriteCaching; ++ 0, // BlockSize ... NEED TO BE FILLED ++ 4, // IoAlign ++ 0, // LastBlock ... NEED TO BE FILLED ++ 0, // LowestAlignedLba ++ 1, // LogicalBlocksPerPhysicalBlock ++ }, // Media; ++ ++ { ++ EFI_DISK_IO_PROTOCOL_REVISION, // Revision ++ NorFlashDiskIoReadDisk, // ReadDisk ++ NorFlashDiskIoWriteDisk // WriteDisk ++ }, ++ ++ { ++ FvbGetAttributes, // GetAttributes ++ FvbSetAttributes, // SetAttributes ++ FvbGetPhysicalAddress, // GetPhysicalAddress ++ FvbGetBlockSize, // GetBlockSize ++ FvbRead, // Read ++ FvbWrite, // Write ++ FvbEraseBlocks, // EraseBlocks ++ NULL, // ParentHandle ++ }, // FvbProtoccol; ++ NULL, // ShadowBuffer ++ { ++ { ++ { ++ HARDWARE_DEVICE_PATH, ++ HW_VENDOR_DP, ++ { ++ (UINT8)(OFFSET_OF (NOR_FLASH_DEVICE_PATH, End)), ++ (UINT8)(OFFSET_OF (NOR_FLASH_DEVICE_PATH, End) >> 8) ++ } ++ }, ++ { 0x0, 0x0, 0x0, { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 } ++ }, // GUID ... NEED TO BE FILLED ++ }, ++ 0, // Index ++ { ++ END_DEVICE_PATH_TYPE, ++ END_ENTIRE_DEVICE_PATH_SUBTYPE, ++ { sizeof (EFI_DEVICE_PATH_PROTOCOL), 0 } ++ } ++ } // DevicePath ++}; ++ ++EFI_STATUS ++NorFlashCreateInstance ( ++ IN UINTN NorFlashDeviceBase, ++ IN UINTN NorFlashRegionBase, ++ IN UINTN NorFlashSize, ++ IN UINT32 Index, ++ IN UINT32 BlockSize, ++ IN BOOLEAN SupportFvb, ++ OUT NOR_FLASH_INSTANCE **NorFlashInstance ++ ) ++{ ++ EFI_STATUS Status; ++ NOR_FLASH_INSTANCE *Instance; ++ ++ ASSERT (NorFlashInstance != NULL); ++ ++ Instance = AllocateRuntimeCopyPool (sizeof (NOR_FLASH_INSTANCE), &mNorFlashInstanceTemplate); ++ if (Instance == NULL) { ++ return EFI_OUT_OF_RESOURCES; ++ } ++ ++ Instance->DeviceBaseAddress = NorFlashDeviceBase; ++ Instance->RegionBaseAddress = NorFlashRegionBase; ++ Instance->Size = NorFlashSize; ++ ++ Instance->BlockIoProtocol.Media = &Instance->Media; ++ Instance->Media.MediaId = Index; ++ Instance->Media.BlockSize = BlockSize; ++ Instance->Media.LastBlock = (NorFlashSize / BlockSize)-1; ++ ++ CopyGuid (&Instance->DevicePath.Vendor.Guid, &gEfiCallerIdGuid); ++ Instance->DevicePath.Index = (UINT8)Index; ++ ++ Instance->ShadowBuffer = AllocateRuntimePool (BlockSize); ++ if (Instance->ShadowBuffer == NULL) { ++ return EFI_OUT_OF_RESOURCES; ++ } ++ ++ if (SupportFvb) { ++ NorFlashFvbInitialize (Instance); ++ ++ Status = gBS->InstallMultipleProtocolInterfaces ( ++ &Instance->Handle, ++ &gEfiDevicePathProtocolGuid, ++ &Instance->DevicePath, ++ &gEfiBlockIoProtocolGuid, ++ &Instance->BlockIoProtocol, ++ &gEfiFirmwareVolumeBlockProtocolGuid, ++ &Instance->FvbProtocol, ++ NULL ++ ); ++ if (EFI_ERROR (Status)) { ++ FreePool (Instance); ++ return Status; ++ } ++ } else { ++ Status = gBS->InstallMultipleProtocolInterfaces ( ++ &Instance->Handle, ++ &gEfiDevicePathProtocolGuid, ++ &Instance->DevicePath, ++ &gEfiBlockIoProtocolGuid, ++ &Instance->BlockIoProtocol, ++ &gEfiDiskIoProtocolGuid, ++ &Instance->DiskIoProtocol, ++ NULL ++ ); ++ if (EFI_ERROR (Status)) { ++ FreePool (Instance); ++ return Status; ++ } ++ } ++ ++ *NorFlashInstance = Instance; ++ return Status; ++} ++ ++/** ++ * This function unlock and erase an entire NOR Flash block. ++ **/ ++EFI_STATUS ++NorFlashUnlockAndEraseSingleBlock ( ++ IN NOR_FLASH_INSTANCE *Instance, ++ IN UINTN BlockAddress ++ ) ++{ ++ EFI_STATUS Status; ++ UINTN Index; ++ EFI_TPL OriginalTPL; ++ ++ if (!EfiAtRuntime ()) { ++ // Raise TPL to TPL_HIGH to stop anyone from interrupting us. ++ OriginalTPL = gBS->RaiseTPL (TPL_HIGH_LEVEL); ++ } else { ++ // This initialization is only to prevent the compiler to complain about the ++ // use of uninitialized variables ++ OriginalTPL = TPL_HIGH_LEVEL; ++ } ++ ++ Index = 0; ++ // The block erase might fail a first time (SW bug ?). Retry it ... ++ do { ++ // Unlock the block if we have to ++ Status = NorFlashUnlockSingleBlockIfNecessary (Instance, BlockAddress); ++ if (EFI_ERROR (Status)) { ++ break; ++ } ++ ++ Status = NorFlashEraseSingleBlock (Instance, BlockAddress); ++ Index++; ++ } while ((Index < NOR_FLASH_ERASE_RETRY) && (Status == EFI_WRITE_PROTECTED)); ++ ++ if (Index == NOR_FLASH_ERASE_RETRY) { ++ DEBUG ((DEBUG_ERROR, "EraseSingleBlock(BlockAddress=0x%08x: Block Locked Error (try to erase %d times)\n", BlockAddress, Index)); ++ } ++ ++ if (!EfiAtRuntime ()) { ++ // Interruptions can resume. ++ gBS->RestoreTPL (OriginalTPL); ++ } ++ ++ return Status; ++} ++ ++EFI_STATUS ++NorFlashWriteFullBlock ( ++ IN NOR_FLASH_INSTANCE *Instance, ++ IN EFI_LBA Lba, ++ IN UINT32 *DataBuffer, ++ IN UINT32 BlockSizeInWords ++ ) ++{ ++ EFI_STATUS Status; ++ UINTN WordAddress; ++ UINT32 WordIndex; ++ UINTN BufferIndex; ++ UINTN BlockAddress; ++ UINTN BuffersInBlock; ++ UINTN RemainingWords; ++ EFI_TPL OriginalTPL; ++ UINTN Cnt; ++ ++ Status = EFI_SUCCESS; ++ ++ // Get the physical address of the block ++ BlockAddress = GET_NOR_BLOCK_ADDRESS (Instance->RegionBaseAddress, Lba, BlockSizeInWords * 4); ++ ++ // Start writing from the first address at the start of the block ++ WordAddress = BlockAddress; ++ ++ if (!EfiAtRuntime ()) { ++ // Raise TPL to TPL_HIGH to stop anyone from interrupting us. ++ OriginalTPL = gBS->RaiseTPL (TPL_HIGH_LEVEL); ++ } else { ++ // This initialization is only to prevent the compiler to complain about the ++ // use of uninitialized variables ++ OriginalTPL = TPL_HIGH_LEVEL; ++ } ++ ++ Status = NorFlashUnlockAndEraseSingleBlock (Instance, BlockAddress); ++ if (EFI_ERROR (Status)) { ++ DEBUG ((DEBUG_ERROR, "WriteSingleBlock: ERROR - Failed to Unlock and Erase the single block at 0x%X\n", BlockAddress)); ++ goto EXIT; ++ } ++ ++ // To speed up the programming operation, NOR Flash is programmed using the Buffered Programming method. ++ ++ // Check that the address starts at a 32-word boundary, i.e. last 7 bits must be zero ++ if ((WordAddress & BOUNDARY_OF_32_WORDS) == 0x00) { ++ // First, break the entire block into buffer-sized chunks. ++ BuffersInBlock = (UINTN)(BlockSizeInWords * 4) / P30_MAX_BUFFER_SIZE_IN_BYTES; ++ ++ // Then feed each buffer chunk to the NOR Flash ++ // If a buffer does not contain any data, don't write it. ++ for (BufferIndex = 0; ++ BufferIndex < BuffersInBlock; ++ BufferIndex++, WordAddress += P30_MAX_BUFFER_SIZE_IN_BYTES, DataBuffer += P30_MAX_BUFFER_SIZE_IN_WORDS ++ ) ++ { ++ // Check the buffer to see if it contains any data (not set all 1s). ++ for (Cnt = 0; Cnt < P30_MAX_BUFFER_SIZE_IN_WORDS; Cnt++) { ++ if (~DataBuffer[Cnt] != 0 ) { ++ // Some data found, write the buffer. ++ Status = NorFlashWriteBuffer ( ++ Instance, ++ WordAddress, ++ P30_MAX_BUFFER_SIZE_IN_BYTES, ++ DataBuffer ++ ); ++ if (EFI_ERROR (Status)) { ++ goto EXIT; ++ } ++ ++ break; ++ } ++ } ++ } ++ ++ // Finally, finish off any remaining words that are less than the maximum size of the buffer ++ RemainingWords = BlockSizeInWords % P30_MAX_BUFFER_SIZE_IN_WORDS; ++ ++ if (RemainingWords != 0) { ++ Status = NorFlashWriteBuffer (Instance, WordAddress, (RemainingWords * 4), DataBuffer); ++ if (EFI_ERROR (Status)) { ++ goto EXIT; ++ } ++ } ++ } else { ++ // For now, use the single word programming algorithm ++ // It is unlikely that the NOR Flash will exist in an address which falls within a 32 word boundary range, ++ // i.e. which ends in the range 0x......01 - 0x......7F. ++ for (WordIndex = 0; WordIndex < BlockSizeInWords; WordIndex++, DataBuffer++, WordAddress = WordAddress + 4) { ++ Status = NorFlashWriteSingleWord (Instance, WordAddress, *DataBuffer); ++ if (EFI_ERROR (Status)) { ++ goto EXIT; ++ } ++ } ++ } ++ ++EXIT: ++ if (!EfiAtRuntime ()) { ++ // Interruptions can resume. ++ gBS->RestoreTPL (OriginalTPL); ++ } ++ ++ if (EFI_ERROR (Status)) { ++ DEBUG ((DEBUG_ERROR, "NOR FLASH Programming [WriteSingleBlock] failed at address 0x%08x. Exit Status = \"%r\".\n", WordAddress, Status)); ++ } ++ ++ return Status; ++} ++ ++EFI_STATUS ++EFIAPI ++NorFlashInitialise ( ++ IN EFI_HANDLE ImageHandle, ++ IN EFI_SYSTEM_TABLE *SystemTable ++ ) ++{ ++ EFI_STATUS Status; ++ UINT32 Index; ++ VIRT_NOR_FLASH_DESCRIPTION *NorFlashDevices; ++ BOOLEAN ContainVariableStorage; ++ ++ Status = VirtNorFlashPlatformInitialization (); ++ if (EFI_ERROR (Status)) { ++ DEBUG ((DEBUG_ERROR, "NorFlashInitialise: Fail to initialize Nor Flash devices\n")); ++ return Status; ++ } ++ ++ Status = VirtNorFlashPlatformGetDevices (&NorFlashDevices, &mNorFlashDeviceCount); ++ if (EFI_ERROR (Status)) { ++ DEBUG ((DEBUG_ERROR, "NorFlashInitialise: Fail to get Nor Flash devices\n")); ++ return Status; ++ } ++ ++ mNorFlashInstances = AllocateRuntimePool (sizeof (NOR_FLASH_INSTANCE *) * mNorFlashDeviceCount); ++ ++ for (Index = 0; Index < mNorFlashDeviceCount; Index++) { ++ // Check if this NOR Flash device contain the variable storage region ++ ++ if (PcdGet64 (PcdFlashNvStorageVariableBase64) != 0) { ++ ContainVariableStorage = ++ (NorFlashDevices[Index].RegionBaseAddress <= PcdGet64 (PcdFlashNvStorageVariableBase64)) && ++ (PcdGet64 (PcdFlashNvStorageVariableBase64) + PcdGet32 (PcdFlashNvStorageVariableSize) <= ++ NorFlashDevices[Index].RegionBaseAddress + NorFlashDevices[Index].Size); ++ } else { ++ ContainVariableStorage = ++ (NorFlashDevices[Index].RegionBaseAddress <= PcdGet32 (PcdFlashNvStorageVariableBase)) && ++ (PcdGet32 (PcdFlashNvStorageVariableBase) + PcdGet32 (PcdFlashNvStorageVariableSize) <= ++ NorFlashDevices[Index].RegionBaseAddress + NorFlashDevices[Index].Size); ++ } ++ ++ Status = NorFlashCreateInstance ( ++ NorFlashDevices[Index].DeviceBaseAddress, ++ NorFlashDevices[Index].RegionBaseAddress, ++ NorFlashDevices[Index].Size, ++ Index, ++ NorFlashDevices[Index].BlockSize, ++ ContainVariableStorage, ++ &mNorFlashInstances[Index] ++ ); ++ if (EFI_ERROR (Status)) { ++ DEBUG ((DEBUG_ERROR, "NorFlashInitialise: Fail to create instance for NorFlash[%d]\n", Index)); ++ } ++ } ++ ++ // ++ // Register for the virtual address change event ++ // ++ Status = gBS->CreateEventEx ( ++ EVT_NOTIFY_SIGNAL, ++ TPL_NOTIFY, ++ NorFlashVirtualNotifyEvent, ++ NULL, ++ &gEfiEventVirtualAddressChangeGuid, ++ &mNorFlashVirtualAddrChangeEvent ++ ); ++ ASSERT_EFI_ERROR (Status); ++ ++ return Status; ++} ++ ++EFI_STATUS ++EFIAPI ++NorFlashFvbInitialize ( ++ IN NOR_FLASH_INSTANCE *Instance ++ ) ++{ ++ EFI_STATUS Status; ++ UINT32 FvbNumLba; ++ EFI_BOOT_MODE BootMode; ++ UINTN RuntimeMmioRegionSize; ++ ++ DEBUG ((DEBUG_BLKIO, "NorFlashFvbInitialize\n")); ++ ASSERT ((Instance != NULL)); ++ ++ // ++ // Declare the Non-Volatile storage as EFI_MEMORY_RUNTIME ++ // ++ ++ // Note: all the NOR Flash region needs to be reserved into the UEFI Runtime memory; ++ // even if we only use the small block region at the top of the NOR Flash. ++ // The reason is when the NOR Flash memory is set into program mode, the command ++ // is written as the base of the flash region (ie: Instance->DeviceBaseAddress) ++ RuntimeMmioRegionSize = (Instance->RegionBaseAddress - Instance->DeviceBaseAddress) + Instance->Size; ++ ++ Status = gDS->AddMemorySpace ( ++ EfiGcdMemoryTypeMemoryMappedIo, ++ Instance->DeviceBaseAddress, ++ RuntimeMmioRegionSize, ++ EFI_MEMORY_UC | EFI_MEMORY_RUNTIME ++ ); ++ ASSERT_EFI_ERROR (Status); ++ ++ Status = gDS->SetMemorySpaceAttributes ( ++ Instance->DeviceBaseAddress, ++ RuntimeMmioRegionSize, ++ EFI_MEMORY_UC | EFI_MEMORY_RUNTIME ++ ); ++ ASSERT_EFI_ERROR (Status); ++ ++ mFlashNvStorageVariableBase = (PcdGet64 (PcdFlashNvStorageVariableBase64) != 0) ? ++ PcdGet64 (PcdFlashNvStorageVariableBase64) : PcdGet32 (PcdFlashNvStorageVariableBase); ++ ++ // Set the index of the first LBA for the FVB ++ Instance->StartLba = (mFlashNvStorageVariableBase - Instance->RegionBaseAddress) / Instance->Media.BlockSize; ++ ++ BootMode = GetBootModeHob (); ++ if (BootMode == BOOT_WITH_DEFAULT_SETTINGS) { ++ Status = EFI_INVALID_PARAMETER; ++ } else { ++ // Determine if there is a valid header at the beginning of the NorFlash ++ Status = ValidateFvHeader (Instance); ++ } ++ ++ // Install the Default FVB header if required ++ if (EFI_ERROR (Status)) { ++ // There is no valid header, so time to install one. ++ DEBUG ((DEBUG_INFO, "%a: The FVB Header is not valid.\n", __FUNCTION__)); ++ DEBUG (( ++ DEBUG_INFO, ++ "%a: Installing a correct one for this volume.\n", ++ __FUNCTION__ ++ )); ++ ++ // Erase all the NorFlash that is reserved for variable storage ++ FvbNumLba = (PcdGet32 (PcdFlashNvStorageVariableSize) + PcdGet32 (PcdFlashNvStorageFtwWorkingSize) + PcdGet32 (PcdFlashNvStorageFtwSpareSize)) / Instance->Media.BlockSize; ++ ++ Status = FvbEraseBlocks (&Instance->FvbProtocol, (EFI_LBA)0, FvbNumLba, EFI_LBA_LIST_TERMINATOR); ++ if (EFI_ERROR (Status)) { ++ return Status; ++ } ++ ++ // Install all appropriate headers ++ Status = InitializeFvAndVariableStoreHeaders (Instance); ++ if (EFI_ERROR (Status)) { ++ return Status; ++ } ++ } ++ ++ // ++ // The driver implementing the variable read service can now be dispatched; ++ // the varstore headers are in place. ++ // ++ Status = gBS->InstallProtocolInterface ( ++ &gImageHandle, ++ &gEdkiiNvVarStoreFormattedGuid, ++ EFI_NATIVE_INTERFACE, ++ NULL ++ ); ++ ASSERT_EFI_ERROR (Status); ++ ++ // ++ // Register for the virtual address change event ++ // ++ Status = gBS->CreateEventEx ( ++ EVT_NOTIFY_SIGNAL, ++ TPL_NOTIFY, ++ FvbVirtualNotifyEvent, ++ NULL, ++ &gEfiEventVirtualAddressChangeGuid, ++ &mFvbVirtualAddrChangeEvent ++ ); ++ ASSERT_EFI_ERROR (Status); ++ ++ return Status; ++} +diff --git a/OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.inf b/OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.inf +new file mode 100644 +index 0000000000..1bf50e4823 +--- /dev/null ++++ b/OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.inf +@@ -0,0 +1,72 @@ ++#/** @file ++# ++# Component description file for NorFlashDxe module ++# ++# Copyright (c) 2011 - 2021, Arm Limited. All rights reserved.
++# ++# SPDX-License-Identifier: BSD-2-Clause-Patent ++# ++#**/ ++ ++[Defines] ++ INF_VERSION = 1.29 ++ BASE_NAME = VirtNorFlashDxe ++ FILE_GUID = e452cabd-5fe1-4d97-8161-e80ed6a409a8 ++ MODULE_TYPE = DXE_RUNTIME_DRIVER ++ VERSION_STRING = 1.0 ++ ENTRY_POINT = NorFlashInitialise ++ ++[Sources.common] ++ VirtNorFlash.c ++ VirtNorFlash.h ++ VirtNorFlashBlockIoDxe.c ++ VirtNorFlashDxe.c ++ VirtNorFlashFvb.c ++ ++[Packages] ++ ArmPlatformPkg/ArmPlatformPkg.dec ++ EmbeddedPkg/EmbeddedPkg.dec ++ MdePkg/MdePkg.dec ++ MdeModulePkg/MdeModulePkg.dec ++ OvmfPkg/OvmfPkg.dec ++ ++[LibraryClasses] ++ BaseLib ++ DebugLib ++ DxeServicesTableLib ++ HobLib ++ IoLib ++ UefiBootServicesTableLib ++ UefiDriverEntryPoint ++ UefiLib ++ UefiRuntimeLib ++ VirtNorFlashPlatformLib ++ ++[Guids] ++ gEdkiiNvVarStoreFormattedGuid ## PRODUCES ## PROTOCOL ++ gEfiAuthenticatedVariableGuid ++ gEfiEventVirtualAddressChangeGuid ++ gEfiSystemNvDataFvGuid ++ gEfiVariableGuid ++ ++[Protocols] ++ gEfiBlockIoProtocolGuid ++ gEfiDevicePathProtocolGuid ++ gEfiDiskIoProtocolGuid ++ gEfiFirmwareVolumeBlockProtocolGuid ++ ++[Pcd.common] ++ gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableBase64 ++ gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableBase ++ gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableSize ++ gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingBase64 ++ gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingBase ++ gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingSize ++ gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareBase64 ++ gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareBase ++ gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareSize ++ ++ gArmPlatformTokenSpaceGuid.PcdNorFlashCheckBlockLocked ++ ++[Depex] ++ gEfiCpuArchProtocolGuid +diff --git a/OvmfPkg/VirtNorFlashDxe/VirtNorFlashFvb.c b/OvmfPkg/VirtNorFlashDxe/VirtNorFlashFvb.c +new file mode 100644 +index 0000000000..c824e0a0fb +--- /dev/null ++++ b/OvmfPkg/VirtNorFlashDxe/VirtNorFlashFvb.c +@@ -0,0 +1,777 @@ ++/*++ @file NorFlashFvbDxe.c ++ ++ Copyright (c) 2011 - 2021, Arm Limited. All rights reserved.
++ ++ SPDX-License-Identifier: BSD-2-Clause-Patent ++ ++ --*/ ++ ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include "VirtNorFlash.h" ++ ++extern UINTN mFlashNvStorageVariableBase; ++/// ++/// The Firmware Volume Block Protocol is the low-level interface ++/// to a firmware volume. File-level access to a firmware volume ++/// should not be done using the Firmware Volume Block Protocol. ++/// Normal access to a firmware volume must use the Firmware ++/// Volume Protocol. Typically, only the file system driver that ++/// produces the Firmware Volume Protocol will bind to the ++/// Firmware Volume Block Protocol. ++/// ++ ++/** ++ Initialises the FV Header and Variable Store Header ++ to support variable operations. ++ ++ @param[in] Ptr - Location to initialise the headers ++ ++**/ ++EFI_STATUS ++InitializeFvAndVariableStoreHeaders ( ++ IN NOR_FLASH_INSTANCE *Instance ++ ) ++{ ++ EFI_STATUS Status; ++ VOID *Headers; ++ UINTN HeadersLength; ++ EFI_FIRMWARE_VOLUME_HEADER *FirmwareVolumeHeader; ++ VARIABLE_STORE_HEADER *VariableStoreHeader; ++ UINT32 NvStorageFtwSpareSize; ++ UINT32 NvStorageFtwWorkingSize; ++ UINT32 NvStorageVariableSize; ++ UINT64 NvStorageFtwSpareBase; ++ UINT64 NvStorageFtwWorkingBase; ++ UINT64 NvStorageVariableBase; ++ ++ HeadersLength = sizeof (EFI_FIRMWARE_VOLUME_HEADER) + sizeof (EFI_FV_BLOCK_MAP_ENTRY) + sizeof (VARIABLE_STORE_HEADER); ++ Headers = AllocateZeroPool (HeadersLength); ++ ++ NvStorageFtwWorkingSize = PcdGet32 (PcdFlashNvStorageFtwWorkingSize); ++ NvStorageFtwSpareSize = PcdGet32 (PcdFlashNvStorageFtwSpareSize); ++ NvStorageVariableSize = PcdGet32 (PcdFlashNvStorageVariableSize); ++ ++ NvStorageFtwSpareBase = (PcdGet64 (PcdFlashNvStorageFtwSpareBase64) != 0) ? ++ PcdGet64 (PcdFlashNvStorageFtwSpareBase64) : PcdGet32 (PcdFlashNvStorageFtwSpareBase); ++ NvStorageFtwWorkingBase = (PcdGet64 (PcdFlashNvStorageFtwWorkingBase64) != 0) ? ++ PcdGet64 (PcdFlashNvStorageFtwWorkingBase64) : PcdGet32 (PcdFlashNvStorageFtwWorkingBase); ++ NvStorageVariableBase = (PcdGet64 (PcdFlashNvStorageVariableBase64) != 0) ? ++ PcdGet64 (PcdFlashNvStorageVariableBase64) : PcdGet32 (PcdFlashNvStorageVariableBase); ++ ++ // FirmwareVolumeHeader->FvLength is declared to have the Variable area AND the FTW working area AND the FTW Spare contiguous. ++ if ((NvStorageVariableBase + NvStorageVariableSize) != NvStorageFtwWorkingBase) { ++ DEBUG (( ++ DEBUG_ERROR, ++ "%a: NvStorageFtwWorkingBase is not contiguous with NvStorageVariableBase region\n", ++ __FUNCTION__ ++ )); ++ return EFI_INVALID_PARAMETER; ++ } ++ ++ if ((NvStorageFtwWorkingBase + NvStorageFtwWorkingSize) != NvStorageFtwSpareBase) { ++ DEBUG (( ++ DEBUG_ERROR, ++ "%a: NvStorageFtwSpareBase is not contiguous with NvStorageFtwWorkingBase region\n", ++ __FUNCTION__ ++ )); ++ return EFI_INVALID_PARAMETER; ++ } ++ ++ // Check if the size of the area is at least one block size ++ if ((NvStorageVariableSize <= 0) || (NvStorageVariableSize / Instance->Media.BlockSize <= 0)) { ++ DEBUG (( ++ DEBUG_ERROR, ++ "%a: NvStorageVariableSize is 0x%x, should be atleast one block size\n", ++ __FUNCTION__, ++ NvStorageVariableSize ++ )); ++ return EFI_INVALID_PARAMETER; ++ } ++ ++ if ((NvStorageFtwWorkingSize <= 0) || (NvStorageFtwWorkingSize / Instance->Media.BlockSize <= 0)) { ++ DEBUG (( ++ DEBUG_ERROR, ++ "%a: NvStorageFtwWorkingSize is 0x%x, should be atleast one block size\n", ++ __FUNCTION__, ++ NvStorageFtwWorkingSize ++ )); ++ return EFI_INVALID_PARAMETER; ++ } ++ ++ if ((NvStorageFtwSpareSize <= 0) || (NvStorageFtwSpareSize / Instance->Media.BlockSize <= 0)) { ++ DEBUG (( ++ DEBUG_ERROR, ++ "%a: NvStorageFtwSpareSize is 0x%x, should be atleast one block size\n", ++ __FUNCTION__, ++ NvStorageFtwSpareSize ++ )); ++ return EFI_INVALID_PARAMETER; ++ } ++ ++ // Ensure the Variable area Base Addresses are aligned on a block size boundaries ++ if ((NvStorageVariableBase % Instance->Media.BlockSize != 0) || ++ (NvStorageFtwWorkingBase % Instance->Media.BlockSize != 0) || ++ (NvStorageFtwSpareBase % Instance->Media.BlockSize != 0)) ++ { ++ DEBUG ((DEBUG_ERROR, "%a: NvStorage Base addresses must be aligned to block size boundaries", __FUNCTION__)); ++ return EFI_INVALID_PARAMETER; ++ } ++ ++ // ++ // EFI_FIRMWARE_VOLUME_HEADER ++ // ++ FirmwareVolumeHeader = (EFI_FIRMWARE_VOLUME_HEADER *)Headers; ++ CopyGuid (&FirmwareVolumeHeader->FileSystemGuid, &gEfiSystemNvDataFvGuid); ++ FirmwareVolumeHeader->FvLength = ++ PcdGet32 (PcdFlashNvStorageVariableSize) + ++ PcdGet32 (PcdFlashNvStorageFtwWorkingSize) + ++ PcdGet32 (PcdFlashNvStorageFtwSpareSize); ++ FirmwareVolumeHeader->Signature = EFI_FVH_SIGNATURE; ++ FirmwareVolumeHeader->Attributes = (EFI_FVB_ATTRIBUTES_2)( ++ EFI_FVB2_READ_ENABLED_CAP | // Reads may be enabled ++ EFI_FVB2_READ_STATUS | // Reads are currently enabled ++ EFI_FVB2_STICKY_WRITE | // A block erase is required to flip bits into EFI_FVB2_ERASE_POLARITY ++ EFI_FVB2_MEMORY_MAPPED | // It is memory mapped ++ EFI_FVB2_ERASE_POLARITY | // After erasure all bits take this value (i.e. '1') ++ EFI_FVB2_WRITE_STATUS | // Writes are currently enabled ++ EFI_FVB2_WRITE_ENABLED_CAP // Writes may be enabled ++ ); ++ FirmwareVolumeHeader->HeaderLength = sizeof (EFI_FIRMWARE_VOLUME_HEADER) + sizeof (EFI_FV_BLOCK_MAP_ENTRY); ++ FirmwareVolumeHeader->Revision = EFI_FVH_REVISION; ++ FirmwareVolumeHeader->BlockMap[0].NumBlocks = Instance->Media.LastBlock + 1; ++ FirmwareVolumeHeader->BlockMap[0].Length = Instance->Media.BlockSize; ++ FirmwareVolumeHeader->BlockMap[1].NumBlocks = 0; ++ FirmwareVolumeHeader->BlockMap[1].Length = 0; ++ FirmwareVolumeHeader->Checksum = CalculateCheckSum16 ((UINT16 *)FirmwareVolumeHeader, FirmwareVolumeHeader->HeaderLength); ++ ++ // ++ // VARIABLE_STORE_HEADER ++ // ++ VariableStoreHeader = (VARIABLE_STORE_HEADER *)((UINTN)Headers + FirmwareVolumeHeader->HeaderLength); ++ CopyGuid (&VariableStoreHeader->Signature, &gEfiAuthenticatedVariableGuid); ++ VariableStoreHeader->Size = PcdGet32 (PcdFlashNvStorageVariableSize) - FirmwareVolumeHeader->HeaderLength; ++ VariableStoreHeader->Format = VARIABLE_STORE_FORMATTED; ++ VariableStoreHeader->State = VARIABLE_STORE_HEALTHY; ++ ++ // Install the combined super-header in the NorFlash ++ Status = FvbWrite (&Instance->FvbProtocol, 0, 0, &HeadersLength, Headers); ++ ++ FreePool (Headers); ++ return Status; ++} ++ ++/** ++ Check the integrity of firmware volume header. ++ ++ @param[in] FwVolHeader - A pointer to a firmware volume header ++ ++ @retval EFI_SUCCESS - The firmware volume is consistent ++ @retval EFI_NOT_FOUND - The firmware volume has been corrupted. ++ ++**/ ++EFI_STATUS ++ValidateFvHeader ( ++ IN NOR_FLASH_INSTANCE *Instance ++ ) ++{ ++ UINT16 Checksum; ++ EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader; ++ VARIABLE_STORE_HEADER *VariableStoreHeader; ++ UINTN VariableStoreLength; ++ UINTN FvLength; ++ ++ FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *)Instance->RegionBaseAddress; ++ ++ FvLength = PcdGet32 (PcdFlashNvStorageVariableSize) + PcdGet32 (PcdFlashNvStorageFtwWorkingSize) + ++ PcdGet32 (PcdFlashNvStorageFtwSpareSize); ++ ++ // ++ // Verify the header revision, header signature, length ++ // Length of FvBlock cannot be 2**64-1 ++ // HeaderLength cannot be an odd number ++ // ++ if ( (FwVolHeader->Revision != EFI_FVH_REVISION) ++ || (FwVolHeader->Signature != EFI_FVH_SIGNATURE) ++ || (FwVolHeader->FvLength != FvLength) ++ ) ++ { ++ DEBUG (( ++ DEBUG_INFO, ++ "%a: No Firmware Volume header present\n", ++ __FUNCTION__ ++ )); ++ return EFI_NOT_FOUND; ++ } ++ ++ // Check the Firmware Volume Guid ++ if ( CompareGuid (&FwVolHeader->FileSystemGuid, &gEfiSystemNvDataFvGuid) == FALSE ) { ++ DEBUG (( ++ DEBUG_INFO, ++ "%a: Firmware Volume Guid non-compatible\n", ++ __FUNCTION__ ++ )); ++ return EFI_NOT_FOUND; ++ } ++ ++ // Verify the header checksum ++ Checksum = CalculateSum16 ((UINT16 *)FwVolHeader, FwVolHeader->HeaderLength); ++ if (Checksum != 0) { ++ DEBUG (( ++ DEBUG_INFO, ++ "%a: FV checksum is invalid (Checksum:0x%X)\n", ++ __FUNCTION__, ++ Checksum ++ )); ++ return EFI_NOT_FOUND; ++ } ++ ++ VariableStoreHeader = (VARIABLE_STORE_HEADER *)((UINTN)FwVolHeader + FwVolHeader->HeaderLength); ++ ++ // Check the Variable Store Guid ++ if (!CompareGuid (&VariableStoreHeader->Signature, &gEfiVariableGuid) && ++ !CompareGuid (&VariableStoreHeader->Signature, &gEfiAuthenticatedVariableGuid)) ++ { ++ DEBUG (( ++ DEBUG_INFO, ++ "%a: Variable Store Guid non-compatible\n", ++ __FUNCTION__ ++ )); ++ return EFI_NOT_FOUND; ++ } ++ ++ VariableStoreLength = PcdGet32 (PcdFlashNvStorageVariableSize) - FwVolHeader->HeaderLength; ++ if (VariableStoreHeader->Size != VariableStoreLength) { ++ DEBUG (( ++ DEBUG_INFO, ++ "%a: Variable Store Length does not match\n", ++ __FUNCTION__ ++ )); ++ return EFI_NOT_FOUND; ++ } ++ ++ return EFI_SUCCESS; ++} ++ ++/** ++ The GetAttributes() function retrieves the attributes and ++ current settings of the block. ++ ++ @param This Indicates the EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance. ++ ++ @param Attributes Pointer to EFI_FVB_ATTRIBUTES_2 in which the attributes and ++ current settings are returned. ++ Type EFI_FVB_ATTRIBUTES_2 is defined in EFI_FIRMWARE_VOLUME_HEADER. ++ ++ @retval EFI_SUCCESS The firmware volume attributes were returned. ++ ++ **/ ++EFI_STATUS ++EFIAPI ++FvbGetAttributes ( ++ IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This, ++ OUT EFI_FVB_ATTRIBUTES_2 *Attributes ++ ) ++{ ++ EFI_FVB_ATTRIBUTES_2 FlashFvbAttributes; ++ NOR_FLASH_INSTANCE *Instance; ++ ++ Instance = INSTANCE_FROM_FVB_THIS (This); ++ ++ FlashFvbAttributes = (EFI_FVB_ATTRIBUTES_2)( ++ ++ EFI_FVB2_READ_ENABLED_CAP | // Reads may be enabled ++ EFI_FVB2_READ_STATUS | // Reads are currently enabled ++ EFI_FVB2_STICKY_WRITE | // A block erase is required to flip bits into EFI_FVB2_ERASE_POLARITY ++ EFI_FVB2_MEMORY_MAPPED | // It is memory mapped ++ EFI_FVB2_ERASE_POLARITY // After erasure all bits take this value (i.e. '1') ++ ++ ); ++ ++ // Check if it is write protected ++ if (Instance->Media.ReadOnly != TRUE) { ++ FlashFvbAttributes = FlashFvbAttributes | ++ EFI_FVB2_WRITE_STATUS | // Writes are currently enabled ++ EFI_FVB2_WRITE_ENABLED_CAP; // Writes may be enabled ++ } ++ ++ *Attributes = FlashFvbAttributes; ++ ++ DEBUG ((DEBUG_BLKIO, "FvbGetAttributes(0x%X)\n", *Attributes)); ++ ++ return EFI_SUCCESS; ++} ++ ++/** ++ The SetAttributes() function sets configurable firmware volume attributes ++ and returns the new settings of the firmware volume. ++ ++ ++ @param This Indicates the EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance. ++ ++ @param Attributes On input, Attributes is a pointer to EFI_FVB_ATTRIBUTES_2 ++ that contains the desired firmware volume settings. ++ On successful return, it contains the new settings of ++ the firmware volume. ++ Type EFI_FVB_ATTRIBUTES_2 is defined in EFI_FIRMWARE_VOLUME_HEADER. ++ ++ @retval EFI_SUCCESS The firmware volume attributes were returned. ++ ++ @retval EFI_INVALID_PARAMETER The attributes requested are in conflict with the capabilities ++ as declared in the firmware volume header. ++ ++ **/ ++EFI_STATUS ++EFIAPI ++FvbSetAttributes ( ++ IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This, ++ IN OUT EFI_FVB_ATTRIBUTES_2 *Attributes ++ ) ++{ ++ DEBUG ((DEBUG_BLKIO, "FvbSetAttributes(0x%X) is not supported\n", *Attributes)); ++ return EFI_UNSUPPORTED; ++} ++ ++/** ++ The GetPhysicalAddress() function retrieves the base address of ++ a memory-mapped firmware volume. This function should be called ++ only for memory-mapped firmware volumes. ++ ++ @param This Indicates the EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance. ++ ++ @param Address Pointer to a caller-allocated ++ EFI_PHYSICAL_ADDRESS that, on successful ++ return from GetPhysicalAddress(), contains the ++ base address of the firmware volume. ++ ++ @retval EFI_SUCCESS The firmware volume base address was returned. ++ ++ @retval EFI_NOT_SUPPORTED The firmware volume is not memory mapped. ++ ++ **/ ++EFI_STATUS ++EFIAPI ++FvbGetPhysicalAddress ( ++ IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This, ++ OUT EFI_PHYSICAL_ADDRESS *Address ++ ) ++{ ++ NOR_FLASH_INSTANCE *Instance; ++ ++ Instance = INSTANCE_FROM_FVB_THIS (This); ++ ++ DEBUG ((DEBUG_BLKIO, "FvbGetPhysicalAddress(BaseAddress=0x%08x)\n", Instance->RegionBaseAddress)); ++ ++ ASSERT (Address != NULL); ++ ++ *Address = mFlashNvStorageVariableBase; ++ return EFI_SUCCESS; ++} ++ ++/** ++ The GetBlockSize() function retrieves the size of the requested ++ block. It also returns the number of additional blocks with ++ the identical size. The GetBlockSize() function is used to ++ retrieve the block map (see EFI_FIRMWARE_VOLUME_HEADER). ++ ++ ++ @param This Indicates the EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance. ++ ++ @param Lba Indicates the block for which to return the size. ++ ++ @param BlockSize Pointer to a caller-allocated UINTN in which ++ the size of the block is returned. ++ ++ @param NumberOfBlocks Pointer to a caller-allocated UINTN in ++ which the number of consecutive blocks, ++ starting with Lba, is returned. All ++ blocks in this range have a size of ++ BlockSize. ++ ++ ++ @retval EFI_SUCCESS The firmware volume base address was returned. ++ ++ @retval EFI_INVALID_PARAMETER The requested LBA is out of range. ++ ++ **/ ++EFI_STATUS ++EFIAPI ++FvbGetBlockSize ( ++ IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This, ++ IN EFI_LBA Lba, ++ OUT UINTN *BlockSize, ++ OUT UINTN *NumberOfBlocks ++ ) ++{ ++ EFI_STATUS Status; ++ NOR_FLASH_INSTANCE *Instance; ++ ++ Instance = INSTANCE_FROM_FVB_THIS (This); ++ ++ DEBUG ((DEBUG_BLKIO, "FvbGetBlockSize(Lba=%ld, BlockSize=0x%x, LastBlock=%ld)\n", Lba, Instance->Media.BlockSize, Instance->Media.LastBlock)); ++ ++ if (Lba > Instance->Media.LastBlock) { ++ DEBUG ((DEBUG_ERROR, "FvbGetBlockSize: ERROR - Parameter LBA %ld is beyond the last Lba (%ld).\n", Lba, Instance->Media.LastBlock)); ++ Status = EFI_INVALID_PARAMETER; ++ } else { ++ // This is easy because in this platform each NorFlash device has equal sized blocks. ++ *BlockSize = (UINTN)Instance->Media.BlockSize; ++ *NumberOfBlocks = (UINTN)(Instance->Media.LastBlock - Lba + 1); ++ ++ DEBUG ((DEBUG_BLKIO, "FvbGetBlockSize: *BlockSize=0x%x, *NumberOfBlocks=0x%x.\n", *BlockSize, *NumberOfBlocks)); ++ ++ Status = EFI_SUCCESS; ++ } ++ ++ return Status; ++} ++ ++/** ++ Reads the specified number of bytes into a buffer from the specified block. ++ ++ The Read() function reads the requested number of bytes from the ++ requested block and stores them in the provided buffer. ++ Implementations should be mindful that the firmware volume ++ might be in the ReadDisabled state. If it is in this state, ++ the Read() function must return the status code ++ EFI_ACCESS_DENIED without modifying the contents of the ++ buffer. The Read() function must also prevent spanning block ++ boundaries. If a read is requested that would span a block ++ boundary, the read must read up to the boundary but not ++ beyond. The output parameter NumBytes must be set to correctly ++ indicate the number of bytes actually read. The caller must be ++ aware that a read may be partially completed. ++ ++ @param This Indicates the EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance. ++ ++ @param Lba The starting logical block index from which to read. ++ ++ @param Offset Offset into the block at which to begin reading. ++ ++ @param NumBytes Pointer to a UINTN. ++ At entry, *NumBytes contains the total size of the buffer. ++ At exit, *NumBytes contains the total number of bytes read. ++ ++ @param Buffer Pointer to a caller-allocated buffer that will be used ++ to hold the data that is read. ++ ++ @retval EFI_SUCCESS The firmware volume was read successfully, and contents are ++ in Buffer. ++ ++ @retval EFI_BAD_BUFFER_SIZE Read attempted across an LBA boundary. ++ On output, NumBytes contains the total number of bytes ++ returned in Buffer. ++ ++ @retval EFI_ACCESS_DENIED The firmware volume is in the ReadDisabled state. ++ ++ @retval EFI_DEVICE_ERROR The block device is not functioning correctly and could not be read. ++ ++ **/ ++EFI_STATUS ++EFIAPI ++FvbRead ( ++ IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This, ++ IN EFI_LBA Lba, ++ IN UINTN Offset, ++ IN OUT UINTN *NumBytes, ++ IN OUT UINT8 *Buffer ++ ) ++{ ++ EFI_STATUS TempStatus; ++ UINTN BlockSize; ++ NOR_FLASH_INSTANCE *Instance; ++ ++ Instance = INSTANCE_FROM_FVB_THIS (This); ++ ++ DEBUG ((DEBUG_BLKIO, "FvbRead(Parameters: Lba=%ld, Offset=0x%x, *NumBytes=0x%x, Buffer @ 0x%08x)\n", Instance->StartLba + Lba, Offset, *NumBytes, Buffer)); ++ ++ TempStatus = EFI_SUCCESS; ++ ++ // Cache the block size to avoid de-referencing pointers all the time ++ BlockSize = Instance->Media.BlockSize; ++ ++ DEBUG ((DEBUG_BLKIO, "FvbRead: Check if (Offset=0x%x + NumBytes=0x%x) <= BlockSize=0x%x\n", Offset, *NumBytes, BlockSize)); ++ ++ // The read must not span block boundaries. ++ // We need to check each variable individually because adding two large values together overflows. ++ if ((Offset >= BlockSize) || ++ (*NumBytes > BlockSize) || ++ ((Offset + *NumBytes) > BlockSize)) ++ { ++ DEBUG ((DEBUG_ERROR, "FvbRead: ERROR - EFI_BAD_BUFFER_SIZE: (Offset=0x%x + NumBytes=0x%x) > BlockSize=0x%x\n", Offset, *NumBytes, BlockSize)); ++ return EFI_BAD_BUFFER_SIZE; ++ } ++ ++ // We must have some bytes to read ++ if (*NumBytes == 0) { ++ return EFI_BAD_BUFFER_SIZE; ++ } ++ ++ // Decide if we are doing full block reads or not. ++ if (*NumBytes % BlockSize != 0) { ++ TempStatus = NorFlashRead (Instance, Instance->StartLba + Lba, Offset, *NumBytes, Buffer); ++ if (EFI_ERROR (TempStatus)) { ++ return EFI_DEVICE_ERROR; ++ } ++ } else { ++ // Read NOR Flash data into shadow buffer ++ TempStatus = NorFlashReadBlocks (Instance, Instance->StartLba + Lba, BlockSize, Buffer); ++ if (EFI_ERROR (TempStatus)) { ++ // Return one of the pre-approved error statuses ++ return EFI_DEVICE_ERROR; ++ } ++ } ++ ++ return EFI_SUCCESS; ++} ++ ++/** ++ Writes the specified number of bytes from the input buffer to the block. ++ ++ The Write() function writes the specified number of bytes from ++ the provided buffer to the specified block and offset. If the ++ firmware volume is sticky write, the caller must ensure that ++ all the bits of the specified range to write are in the ++ EFI_FVB_ERASE_POLARITY state before calling the Write() ++ function, or else the result will be unpredictable. This ++ unpredictability arises because, for a sticky-write firmware ++ volume, a write may negate a bit in the EFI_FVB_ERASE_POLARITY ++ state but cannot flip it back again. Before calling the ++ Write() function, it is recommended for the caller to first call ++ the EraseBlocks() function to erase the specified block to ++ write. A block erase cycle will transition bits from the ++ (NOT)EFI_FVB_ERASE_POLARITY state back to the ++ EFI_FVB_ERASE_POLARITY state. Implementations should be ++ mindful that the firmware volume might be in the WriteDisabled ++ state. If it is in this state, the Write() function must ++ return the status code EFI_ACCESS_DENIED without modifying the ++ contents of the firmware volume. The Write() function must ++ also prevent spanning block boundaries. If a write is ++ requested that spans a block boundary, the write must store up ++ to the boundary but not beyond. The output parameter NumBytes ++ must be set to correctly indicate the number of bytes actually ++ written. The caller must be aware that a write may be ++ partially completed. All writes, partial or otherwise, must be ++ fully flushed to the hardware before the Write() service ++ returns. ++ ++ @param This Indicates the EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance. ++ ++ @param Lba The starting logical block index to write to. ++ ++ @param Offset Offset into the block at which to begin writing. ++ ++ @param NumBytes The pointer to a UINTN. ++ At entry, *NumBytes contains the total size of the buffer. ++ At exit, *NumBytes contains the total number of bytes actually written. ++ ++ @param Buffer The pointer to a caller-allocated buffer that contains the source for the write. ++ ++ @retval EFI_SUCCESS The firmware volume was written successfully. ++ ++ @retval EFI_BAD_BUFFER_SIZE The write was attempted across an LBA boundary. ++ On output, NumBytes contains the total number of bytes ++ actually written. ++ ++ @retval EFI_ACCESS_DENIED The firmware volume is in the WriteDisabled state. ++ ++ @retval EFI_DEVICE_ERROR The block device is malfunctioning and could not be written. ++ ++ ++ **/ ++EFI_STATUS ++EFIAPI ++FvbWrite ( ++ IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This, ++ IN EFI_LBA Lba, ++ IN UINTN Offset, ++ IN OUT UINTN *NumBytes, ++ IN UINT8 *Buffer ++ ) ++{ ++ NOR_FLASH_INSTANCE *Instance; ++ ++ Instance = INSTANCE_FROM_FVB_THIS (This); ++ ++ return NorFlashWriteSingleBlock (Instance, Instance->StartLba + Lba, Offset, NumBytes, Buffer); ++} ++ ++/** ++ Erases and initialises a firmware volume block. ++ ++ The EraseBlocks() function erases one or more blocks as denoted ++ by the variable argument list. The entire parameter list of ++ blocks must be verified before erasing any blocks. If a block is ++ requested that does not exist within the associated firmware ++ volume (it has a larger index than the last block of the ++ firmware volume), the EraseBlocks() function must return the ++ status code EFI_INVALID_PARAMETER without modifying the contents ++ of the firmware volume. Implementations should be mindful that ++ the firmware volume might be in the WriteDisabled state. If it ++ is in this state, the EraseBlocks() function must return the ++ status code EFI_ACCESS_DENIED without modifying the contents of ++ the firmware volume. All calls to EraseBlocks() must be fully ++ flushed to the hardware before the EraseBlocks() service ++ returns. ++ ++ @param This Indicates the EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL ++ instance. ++ ++ @param ... The variable argument list is a list of tuples. ++ Each tuple describes a range of LBAs to erase ++ and consists of the following: ++ - An EFI_LBA that indicates the starting LBA ++ - A UINTN that indicates the number of blocks to erase. ++ ++ The list is terminated with an EFI_LBA_LIST_TERMINATOR. ++ For example, the following indicates that two ranges of blocks ++ (5-7 and 10-11) are to be erased: ++ EraseBlocks (This, 5, 3, 10, 2, EFI_LBA_LIST_TERMINATOR); ++ ++ @retval EFI_SUCCESS The erase request successfully completed. ++ ++ @retval EFI_ACCESS_DENIED The firmware volume is in the WriteDisabled state. ++ ++ @retval EFI_DEVICE_ERROR The block device is not functioning correctly and could not be written. ++ The firmware device may have been partially erased. ++ ++ @retval EFI_INVALID_PARAMETER One or more of the LBAs listed in the variable argument list do ++ not exist in the firmware volume. ++ ++ **/ ++EFI_STATUS ++EFIAPI ++FvbEraseBlocks ( ++ IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This, ++ ... ++ ) ++{ ++ EFI_STATUS Status; ++ VA_LIST Args; ++ UINTN BlockAddress; // Physical address of Lba to erase ++ EFI_LBA StartingLba; // Lba from which we start erasing ++ UINTN NumOfLba; // Number of Lba blocks to erase ++ NOR_FLASH_INSTANCE *Instance; ++ ++ Instance = INSTANCE_FROM_FVB_THIS (This); ++ ++ DEBUG ((DEBUG_BLKIO, "FvbEraseBlocks()\n")); ++ ++ Status = EFI_SUCCESS; ++ ++ // Detect WriteDisabled state ++ if (Instance->Media.ReadOnly == TRUE) { ++ // Firmware volume is in WriteDisabled state ++ DEBUG ((DEBUG_ERROR, "FvbEraseBlocks: ERROR - Device is in WriteDisabled state.\n")); ++ return EFI_ACCESS_DENIED; ++ } ++ ++ // Before erasing, check the entire list of parameters to ensure all specified blocks are valid ++ ++ VA_START (Args, This); ++ do { ++ // Get the Lba from which we start erasing ++ StartingLba = VA_ARG (Args, EFI_LBA); ++ ++ // Have we reached the end of the list? ++ if (StartingLba == EFI_LBA_LIST_TERMINATOR) { ++ // Exit the while loop ++ break; ++ } ++ ++ // How many Lba blocks are we requested to erase? ++ NumOfLba = VA_ARG (Args, UINTN); ++ ++ // All blocks must be within range ++ DEBUG (( ++ DEBUG_BLKIO, ++ "FvbEraseBlocks: Check if: ( StartingLba=%ld + NumOfLba=%Lu - 1 ) > LastBlock=%ld.\n", ++ Instance->StartLba + StartingLba, ++ (UINT64)NumOfLba, ++ Instance->Media.LastBlock ++ )); ++ if ((NumOfLba == 0) || ((Instance->StartLba + StartingLba + NumOfLba - 1) > Instance->Media.LastBlock)) { ++ VA_END (Args); ++ DEBUG ((DEBUG_ERROR, "FvbEraseBlocks: ERROR - Lba range goes past the last Lba.\n")); ++ Status = EFI_INVALID_PARAMETER; ++ goto EXIT; ++ } ++ } while (TRUE); ++ ++ VA_END (Args); ++ ++ // ++ // To get here, all must be ok, so start erasing ++ // ++ VA_START (Args, This); ++ do { ++ // Get the Lba from which we start erasing ++ StartingLba = VA_ARG (Args, EFI_LBA); ++ ++ // Have we reached the end of the list? ++ if (StartingLba == EFI_LBA_LIST_TERMINATOR) { ++ // Exit the while loop ++ break; ++ } ++ ++ // How many Lba blocks are we requested to erase? ++ NumOfLba = VA_ARG (Args, UINTN); ++ ++ // Go through each one and erase it ++ while (NumOfLba > 0) { ++ // Get the physical address of Lba to erase ++ BlockAddress = GET_NOR_BLOCK_ADDRESS ( ++ Instance->RegionBaseAddress, ++ Instance->StartLba + StartingLba, ++ Instance->Media.BlockSize ++ ); ++ ++ // Erase it ++ DEBUG ((DEBUG_BLKIO, "FvbEraseBlocks: Erasing Lba=%ld @ 0x%08x.\n", Instance->StartLba + StartingLba, BlockAddress)); ++ Status = NorFlashUnlockAndEraseSingleBlock (Instance, BlockAddress); ++ if (EFI_ERROR (Status)) { ++ VA_END (Args); ++ Status = EFI_DEVICE_ERROR; ++ goto EXIT; ++ } ++ ++ // Move to the next Lba ++ StartingLba++; ++ NumOfLba--; ++ } ++ } while (TRUE); ++ ++ VA_END (Args); ++ ++EXIT: ++ return Status; ++} ++ ++/** ++ Fixup internal data so that EFI can be call in virtual mode. ++ Call the passed in Child Notify event and convert any pointers in ++ lib to virtual mode. ++ ++ @param[in] Event The Event that is being processed ++ @param[in] Context Event Context ++**/ ++VOID ++EFIAPI ++FvbVirtualNotifyEvent ( ++ IN EFI_EVENT Event, ++ IN VOID *Context ++ ) ++{ ++ EfiConvertPointer (0x0, (VOID **)&mFlashNvStorageVariableBase); ++ return; ++} +-- +2.41.0 + diff --git a/edk2-OvmfPkg-VirtNorFlashDxe-drop-block-I-O-protocol-impl.patch b/edk2-OvmfPkg-VirtNorFlashDxe-drop-block-I-O-protocol-impl.patch new file mode 100644 index 0000000..e8c0128 --- /dev/null +++ b/edk2-OvmfPkg-VirtNorFlashDxe-drop-block-I-O-protocol-impl.patch @@ -0,0 +1,504 @@ +From 8cf16599ade30de07c9b51f90d2208046f74fee6 Mon Sep 17 00:00:00 2001 +From: Ard Biesheuvel +Date: Mon, 24 Oct 2022 17:12:08 +0200 +Subject: [PATCH 04/18] OvmfPkg/VirtNorFlashDxe: drop block I/O protocol + implementation + +RH-Author: Gerd Hoffmann +RH-MergeRequest: 43: OvmfPkg/VirtNorFlashDxe backport +RH-Jira: RHEL-17587 +RH-Acked-by: Laszlo Ersek +RH-Commit: [6/20] 6843078997a30c2818e0d53a90fb7f6accb89aaf + +We never boot from NOR flash, and generally rely on the firmware volume +PI protocols to expose the contents. So drop the block I/O protocol +implementation from VirtNorFlashDxe. + +Signed-off-by: Ard Biesheuvel +Reviewed-by: Sunil V L +(cherry picked from commit 83f11f957240ead9b135a778316330762b0a3acb) +--- + OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c | 49 ++++++------------ + OvmfPkg/VirtNorFlashDxe/VirtNorFlash.h | 54 ++------------------ + OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.c | 40 +++------------ + OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.inf | 1 - + OvmfPkg/VirtNorFlashDxe/VirtNorFlashFvb.c | 55 ++++++++------------- + 5 files changed, 45 insertions(+), 154 deletions(-) + +diff --git a/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c b/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c +index 1094d48f7d..f41d9d372f 100644 +--- a/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c ++++ b/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c +@@ -366,10 +366,6 @@ NorFlashWriteBlocks ( + return EFI_INVALID_PARAMETER; + } + +- if (Instance->Media.ReadOnly == TRUE) { +- return EFI_WRITE_PROTECTED; +- } +- + // We must have some bytes to read + DEBUG ((DEBUG_BLKIO, "NorFlashWriteBlocks: BufferSizeInBytes=0x%x\n", BufferSizeInBytes)); + if (BufferSizeInBytes == 0) { +@@ -377,22 +373,22 @@ NorFlashWriteBlocks ( + } + + // The size of the buffer must be a multiple of the block size +- DEBUG ((DEBUG_BLKIO, "NorFlashWriteBlocks: BlockSize in bytes =0x%x\n", Instance->Media.BlockSize)); +- if ((BufferSizeInBytes % Instance->Media.BlockSize) != 0) { ++ DEBUG ((DEBUG_BLKIO, "NorFlashWriteBlocks: BlockSize in bytes =0x%x\n", Instance->BlockSize)); ++ if ((BufferSizeInBytes % Instance->BlockSize) != 0) { + return EFI_BAD_BUFFER_SIZE; + } + + // All blocks must be within the device +- NumBlocks = ((UINT32)BufferSizeInBytes) / Instance->Media.BlockSize; ++ NumBlocks = ((UINT32)BufferSizeInBytes) / Instance->BlockSize; + +- DEBUG ((DEBUG_BLKIO, "NorFlashWriteBlocks: NumBlocks=%d, LastBlock=%ld, Lba=%ld.\n", NumBlocks, Instance->Media.LastBlock, Lba)); ++ DEBUG ((DEBUG_BLKIO, "NorFlashWriteBlocks: NumBlocks=%d, LastBlock=%ld, Lba=%ld.\n", NumBlocks, Instance->LastBlock, Lba)); + +- if ((Lba + NumBlocks) > (Instance->Media.LastBlock + 1)) { ++ if ((Lba + NumBlocks) > (Instance->LastBlock + 1)) { + DEBUG ((DEBUG_ERROR, "NorFlashWriteBlocks: ERROR - Write will exceed last block.\n")); + return EFI_INVALID_PARAMETER; + } + +- BlockSizeInWords = Instance->Media.BlockSize / 4; ++ BlockSizeInWords = Instance->BlockSize / 4; + + // Because the target *Buffer is a pointer to VOID, we must put all the data into a pointer + // to a proper data type, so use *ReadBuffer +@@ -489,8 +485,8 @@ NorFlashReadBlocks ( + DEBUG_BLKIO, + "NorFlashReadBlocks: BufferSize=0x%xB BlockSize=0x%xB LastBlock=%ld, Lba=%ld.\n", + BufferSizeInBytes, +- Instance->Media.BlockSize, +- Instance->Media.LastBlock, ++ Instance->BlockSize, ++ Instance->LastBlock, + Lba + )); + +@@ -505,14 +501,14 @@ NorFlashReadBlocks ( + } + + // The size of the buffer must be a multiple of the block size +- if ((BufferSizeInBytes % Instance->Media.BlockSize) != 0) { ++ if ((BufferSizeInBytes % Instance->BlockSize) != 0) { + return EFI_BAD_BUFFER_SIZE; + } + + // All blocks must be within the device +- NumBlocks = ((UINT32)BufferSizeInBytes) / Instance->Media.BlockSize; ++ NumBlocks = ((UINT32)BufferSizeInBytes) / Instance->BlockSize; + +- if ((Lba + NumBlocks) > (Instance->Media.LastBlock + 1)) { ++ if ((Lba + NumBlocks) > (Instance->LastBlock + 1)) { + DEBUG ((DEBUG_ERROR, "NorFlashReadBlocks: ERROR - Read will exceed last block\n")); + return EFI_INVALID_PARAMETER; + } +@@ -521,7 +517,7 @@ NorFlashReadBlocks ( + StartAddress = GET_NOR_BLOCK_ADDRESS ( + Instance->RegionBaseAddress, + Lba, +- Instance->Media.BlockSize ++ Instance->BlockSize + ); + + // Put the device into Read Array mode +@@ -554,7 +550,7 @@ NorFlashRead ( + return EFI_SUCCESS; + } + +- if (((Lba * Instance->Media.BlockSize) + Offset + BufferSizeInBytes) > Instance->Size) { ++ if (((Lba * Instance->BlockSize) + Offset + BufferSizeInBytes) > Instance->Size) { + DEBUG ((DEBUG_ERROR, "NorFlashRead: ERROR - Read will exceed device size.\n")); + return EFI_INVALID_PARAMETER; + } +@@ -563,7 +559,7 @@ NorFlashRead ( + StartAddress = GET_NOR_BLOCK_ADDRESS ( + Instance->RegionBaseAddress, + Lba, +- Instance->Media.BlockSize ++ Instance->BlockSize + ); + + // Put the device into Read Array mode +@@ -577,7 +573,7 @@ NorFlashRead ( + + /* + Write a full or portion of a block. It must not span block boundaries; that is, +- Offset + *NumBytes <= Instance->Media.BlockSize. ++ Offset + *NumBytes <= Instance->BlockSize. + */ + EFI_STATUS + NorFlashWriteSingleBlock ( +@@ -605,15 +601,8 @@ NorFlashWriteSingleBlock ( + + DEBUG ((DEBUG_BLKIO, "NorFlashWriteSingleBlock(Parameters: Lba=%ld, Offset=0x%x, *NumBytes=0x%x, Buffer @ 0x%08x)\n", Lba, Offset, *NumBytes, Buffer)); + +- // Detect WriteDisabled state +- if (Instance->Media.ReadOnly == TRUE) { +- DEBUG ((DEBUG_ERROR, "NorFlashWriteSingleBlock: ERROR - Can not write: Device is in WriteDisabled state.\n")); +- // It is in WriteDisabled state, return an error right away +- return EFI_ACCESS_DENIED; +- } +- + // Cache the block size to avoid de-referencing pointers all the time +- BlockSize = Instance->Media.BlockSize; ++ BlockSize = Instance->BlockSize; + + // The write must not span block boundaries. + // We need to check each variable individually because adding two large values together overflows. +@@ -819,12 +808,6 @@ NorFlashVirtualNotifyEvent ( + EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->DeviceBaseAddress); + EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->RegionBaseAddress); + +- // Convert BlockIo protocol +- EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->BlockIoProtocol.FlushBlocks); +- EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->BlockIoProtocol.ReadBlocks); +- EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->BlockIoProtocol.Reset); +- EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->BlockIoProtocol.WriteBlocks); +- + // Convert Fvb + EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->FvbProtocol.EraseBlocks); + EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->FvbProtocol.GetAttributes); +diff --git a/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.h b/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.h +index 7733ee02ee..b7f5d208b2 100644 +--- a/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.h ++++ b/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.h +@@ -14,7 +14,6 @@ + + #include + +-#include + #include + + #include +@@ -108,8 +107,7 @@ + #define P30_CMD_READ_CONFIGURATION_REGISTER 0x0003 + + #define NOR_FLASH_SIGNATURE SIGNATURE_32('n', 'o', 'r', '0') +-#define INSTANCE_FROM_FVB_THIS(a) CR(a, NOR_FLASH_INSTANCE, FvbProtocol, NOR_FLASH_SIGNATURE) +-#define INSTANCE_FROM_BLKIO_THIS(a) CR(a, NOR_FLASH_INSTANCE, BlockIoProtocol, NOR_FLASH_SIGNATURE) ++#define INSTANCE_FROM_FVB_THIS(a) CR(a, NOR_FLASH_INSTANCE, FvbProtocol, NOR_FLASH_SIGNATURE) + + typedef struct _NOR_FLASH_INSTANCE NOR_FLASH_INSTANCE; + +@@ -129,9 +127,8 @@ struct _NOR_FLASH_INSTANCE { + UINTN RegionBaseAddress; + UINTN Size; + EFI_LBA StartLba; +- +- EFI_BLOCK_IO_PROTOCOL BlockIoProtocol; +- EFI_BLOCK_IO_MEDIA Media; ++ EFI_LBA LastBlock; ++ UINT32 BlockSize; + + EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL FvbProtocol; + VOID *ShadowBuffer; +@@ -155,51 +152,6 @@ NorFlashWriteBuffer ( + IN UINT32 *Buffer + ); + +-// +-// BlockIO Protocol function EFI_BLOCK_IO_PROTOCOL.Reset +-// +-EFI_STATUS +-EFIAPI +-NorFlashBlockIoReset ( +- IN EFI_BLOCK_IO_PROTOCOL *This, +- IN BOOLEAN ExtendedVerification +- ); +- +-// +-// BlockIO Protocol function EFI_BLOCK_IO_PROTOCOL.ReadBlocks +-// +-EFI_STATUS +-EFIAPI +-NorFlashBlockIoReadBlocks ( +- IN EFI_BLOCK_IO_PROTOCOL *This, +- IN UINT32 MediaId, +- IN EFI_LBA Lba, +- IN UINTN BufferSizeInBytes, +- OUT VOID *Buffer +- ); +- +-// +-// BlockIO Protocol function EFI_BLOCK_IO_PROTOCOL.WriteBlocks +-// +-EFI_STATUS +-EFIAPI +-NorFlashBlockIoWriteBlocks ( +- IN EFI_BLOCK_IO_PROTOCOL *This, +- IN UINT32 MediaId, +- IN EFI_LBA Lba, +- IN UINTN BufferSizeInBytes, +- IN VOID *Buffer +- ); +- +-// +-// BlockIO Protocol function EFI_BLOCK_IO_PROTOCOL.FlushBlocks +-// +-EFI_STATUS +-EFIAPI +-NorFlashBlockIoFlushBlocks ( +- IN EFI_BLOCK_IO_PROTOCOL *This +- ); +- + // + // NorFlashFvbDxe.c + // +diff --git a/OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.c b/OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.c +index 4875b057d5..2ceda22635 100644 +--- a/OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.c ++++ b/OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.c +@@ -34,29 +34,8 @@ NOR_FLASH_INSTANCE mNorFlashInstanceTemplate = { + 0, // RegionBaseAddress ... NEED TO BE FILLED + 0, // Size ... NEED TO BE FILLED + 0, // StartLba +- +- { +- EFI_BLOCK_IO_PROTOCOL_REVISION2, // Revision +- NULL, // Media ... NEED TO BE FILLED +- NorFlashBlockIoReset, // Reset; +- NorFlashBlockIoReadBlocks, // ReadBlocks +- NorFlashBlockIoWriteBlocks, // WriteBlocks +- NorFlashBlockIoFlushBlocks // FlushBlocks +- }, // BlockIoProtocol +- +- { +- 0, // MediaId ... NEED TO BE FILLED +- FALSE, // RemovableMedia +- TRUE, // MediaPresent +- FALSE, // LogicalPartition +- FALSE, // ReadOnly +- FALSE, // WriteCaching; +- 0, // BlockSize ... NEED TO BE FILLED +- 4, // IoAlign +- 0, // LastBlock ... NEED TO BE FILLED +- 0, // LowestAlignedLba +- 1, // LogicalBlocksPerPhysicalBlock +- }, // Media; ++ 0, // LastBlock ++ 0, // BlockSize + + { + FvbGetAttributes, // GetAttributes +@@ -115,11 +94,8 @@ NorFlashCreateInstance ( + Instance->DeviceBaseAddress = NorFlashDeviceBase; + Instance->RegionBaseAddress = NorFlashRegionBase; + Instance->Size = NorFlashSize; +- +- Instance->BlockIoProtocol.Media = &Instance->Media; +- Instance->Media.MediaId = Index; +- Instance->Media.BlockSize = BlockSize; +- Instance->Media.LastBlock = (NorFlashSize / BlockSize)-1; ++ Instance->BlockSize = BlockSize; ++ Instance->LastBlock = (NorFlashSize / BlockSize) - 1; + + CopyGuid (&Instance->DevicePath.Vendor.Guid, &gEfiCallerIdGuid); + Instance->DevicePath.Index = (UINT8)Index; +@@ -136,8 +112,6 @@ NorFlashCreateInstance ( + &Instance->Handle, + &gEfiDevicePathProtocolGuid, + &Instance->DevicePath, +- &gEfiBlockIoProtocolGuid, +- &Instance->BlockIoProtocol, + &gEfiFirmwareVolumeBlockProtocolGuid, + &Instance->FvbProtocol, + NULL +@@ -151,8 +125,6 @@ NorFlashCreateInstance ( + &Instance->Handle, + &gEfiDevicePathProtocolGuid, + &Instance->DevicePath, +- &gEfiBlockIoProtocolGuid, +- &Instance->BlockIoProtocol, + NULL + ); + if (EFI_ERROR (Status)) { +@@ -434,7 +406,7 @@ NorFlashFvbInitialize ( + PcdGet64 (PcdFlashNvStorageVariableBase64) : PcdGet32 (PcdFlashNvStorageVariableBase); + + // Set the index of the first LBA for the FVB +- Instance->StartLba = (mFlashNvStorageVariableBase - Instance->RegionBaseAddress) / Instance->Media.BlockSize; ++ Instance->StartLba = (mFlashNvStorageVariableBase - Instance->RegionBaseAddress) / Instance->BlockSize; + + BootMode = GetBootModeHob (); + if (BootMode == BOOT_WITH_DEFAULT_SETTINGS) { +@@ -455,7 +427,7 @@ NorFlashFvbInitialize ( + )); + + // Erase all the NorFlash that is reserved for variable storage +- FvbNumLba = (PcdGet32 (PcdFlashNvStorageVariableSize) + PcdGet32 (PcdFlashNvStorageFtwWorkingSize) + PcdGet32 (PcdFlashNvStorageFtwSpareSize)) / Instance->Media.BlockSize; ++ FvbNumLba = (PcdGet32 (PcdFlashNvStorageVariableSize) + PcdGet32 (PcdFlashNvStorageFtwWorkingSize) + PcdGet32 (PcdFlashNvStorageFtwSpareSize)) / Instance->BlockSize; + + Status = FvbEraseBlocks (&Instance->FvbProtocol, (EFI_LBA)0, FvbNumLba, EFI_LBA_LIST_TERMINATOR); + if (EFI_ERROR (Status)) { +diff --git a/OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.inf b/OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.inf +index 53e9d58204..2a3d4a218e 100644 +--- a/OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.inf ++++ b/OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.inf +@@ -19,7 +19,6 @@ + [Sources.common] + VirtNorFlash.c + VirtNorFlash.h +- VirtNorFlashBlockIoDxe.c + VirtNorFlashDxe.c + VirtNorFlashFvb.c + +diff --git a/OvmfPkg/VirtNorFlashDxe/VirtNorFlashFvb.c b/OvmfPkg/VirtNorFlashDxe/VirtNorFlashFvb.c +index c824e0a0fb..cc5eefaaf3 100644 +--- a/OvmfPkg/VirtNorFlashDxe/VirtNorFlashFvb.c ++++ b/OvmfPkg/VirtNorFlashDxe/VirtNorFlashFvb.c +@@ -89,7 +89,7 @@ InitializeFvAndVariableStoreHeaders ( + } + + // Check if the size of the area is at least one block size +- if ((NvStorageVariableSize <= 0) || (NvStorageVariableSize / Instance->Media.BlockSize <= 0)) { ++ if ((NvStorageVariableSize <= 0) || (NvStorageVariableSize / Instance->BlockSize <= 0)) { + DEBUG (( + DEBUG_ERROR, + "%a: NvStorageVariableSize is 0x%x, should be atleast one block size\n", +@@ -99,7 +99,7 @@ InitializeFvAndVariableStoreHeaders ( + return EFI_INVALID_PARAMETER; + } + +- if ((NvStorageFtwWorkingSize <= 0) || (NvStorageFtwWorkingSize / Instance->Media.BlockSize <= 0)) { ++ if ((NvStorageFtwWorkingSize <= 0) || (NvStorageFtwWorkingSize / Instance->BlockSize <= 0)) { + DEBUG (( + DEBUG_ERROR, + "%a: NvStorageFtwWorkingSize is 0x%x, should be atleast one block size\n", +@@ -109,7 +109,7 @@ InitializeFvAndVariableStoreHeaders ( + return EFI_INVALID_PARAMETER; + } + +- if ((NvStorageFtwSpareSize <= 0) || (NvStorageFtwSpareSize / Instance->Media.BlockSize <= 0)) { ++ if ((NvStorageFtwSpareSize <= 0) || (NvStorageFtwSpareSize / Instance->BlockSize <= 0)) { + DEBUG (( + DEBUG_ERROR, + "%a: NvStorageFtwSpareSize is 0x%x, should be atleast one block size\n", +@@ -120,9 +120,9 @@ InitializeFvAndVariableStoreHeaders ( + } + + // Ensure the Variable area Base Addresses are aligned on a block size boundaries +- if ((NvStorageVariableBase % Instance->Media.BlockSize != 0) || +- (NvStorageFtwWorkingBase % Instance->Media.BlockSize != 0) || +- (NvStorageFtwSpareBase % Instance->Media.BlockSize != 0)) ++ if ((NvStorageVariableBase % Instance->BlockSize != 0) || ++ (NvStorageFtwWorkingBase % Instance->BlockSize != 0) || ++ (NvStorageFtwSpareBase % Instance->BlockSize != 0)) + { + DEBUG ((DEBUG_ERROR, "%a: NvStorage Base addresses must be aligned to block size boundaries", __FUNCTION__)); + return EFI_INVALID_PARAMETER; +@@ -149,8 +149,8 @@ InitializeFvAndVariableStoreHeaders ( + ); + FirmwareVolumeHeader->HeaderLength = sizeof (EFI_FIRMWARE_VOLUME_HEADER) + sizeof (EFI_FV_BLOCK_MAP_ENTRY); + FirmwareVolumeHeader->Revision = EFI_FVH_REVISION; +- FirmwareVolumeHeader->BlockMap[0].NumBlocks = Instance->Media.LastBlock + 1; +- FirmwareVolumeHeader->BlockMap[0].Length = Instance->Media.BlockSize; ++ FirmwareVolumeHeader->BlockMap[0].NumBlocks = Instance->LastBlock + 1; ++ FirmwareVolumeHeader->BlockMap[0].Length = Instance->BlockSize; + FirmwareVolumeHeader->BlockMap[1].NumBlocks = 0; + FirmwareVolumeHeader->BlockMap[1].Length = 0; + FirmwareVolumeHeader->Checksum = CalculateCheckSum16 ((UINT16 *)FirmwareVolumeHeader, FirmwareVolumeHeader->HeaderLength); +@@ -284,9 +284,6 @@ FvbGetAttributes ( + ) + { + EFI_FVB_ATTRIBUTES_2 FlashFvbAttributes; +- NOR_FLASH_INSTANCE *Instance; +- +- Instance = INSTANCE_FROM_FVB_THIS (This); + + FlashFvbAttributes = (EFI_FVB_ATTRIBUTES_2)( + +@@ -294,17 +291,12 @@ FvbGetAttributes ( + EFI_FVB2_READ_STATUS | // Reads are currently enabled + EFI_FVB2_STICKY_WRITE | // A block erase is required to flip bits into EFI_FVB2_ERASE_POLARITY + EFI_FVB2_MEMORY_MAPPED | // It is memory mapped +- EFI_FVB2_ERASE_POLARITY // After erasure all bits take this value (i.e. '1') ++ EFI_FVB2_ERASE_POLARITY | // After erasure all bits take this value (i.e. '1') ++ EFI_FVB2_WRITE_STATUS | // Writes are currently enabled ++ EFI_FVB2_WRITE_ENABLED_CAP // Writes may be enabled + + ); + +- // Check if it is write protected +- if (Instance->Media.ReadOnly != TRUE) { +- FlashFvbAttributes = FlashFvbAttributes | +- EFI_FVB2_WRITE_STATUS | // Writes are currently enabled +- EFI_FVB2_WRITE_ENABLED_CAP; // Writes may be enabled +- } +- + *Attributes = FlashFvbAttributes; + + DEBUG ((DEBUG_BLKIO, "FvbGetAttributes(0x%X)\n", *Attributes)); +@@ -418,15 +410,15 @@ FvbGetBlockSize ( + + Instance = INSTANCE_FROM_FVB_THIS (This); + +- DEBUG ((DEBUG_BLKIO, "FvbGetBlockSize(Lba=%ld, BlockSize=0x%x, LastBlock=%ld)\n", Lba, Instance->Media.BlockSize, Instance->Media.LastBlock)); ++ DEBUG ((DEBUG_BLKIO, "FvbGetBlockSize(Lba=%ld, BlockSize=0x%x, LastBlock=%ld)\n", Lba, Instance->BlockSize, Instance->LastBlock)); + +- if (Lba > Instance->Media.LastBlock) { +- DEBUG ((DEBUG_ERROR, "FvbGetBlockSize: ERROR - Parameter LBA %ld is beyond the last Lba (%ld).\n", Lba, Instance->Media.LastBlock)); ++ if (Lba > Instance->LastBlock) { ++ DEBUG ((DEBUG_ERROR, "FvbGetBlockSize: ERROR - Parameter LBA %ld is beyond the last Lba (%ld).\n", Lba, Instance->LastBlock)); + Status = EFI_INVALID_PARAMETER; + } else { + // This is easy because in this platform each NorFlash device has equal sized blocks. +- *BlockSize = (UINTN)Instance->Media.BlockSize; +- *NumberOfBlocks = (UINTN)(Instance->Media.LastBlock - Lba + 1); ++ *BlockSize = (UINTN)Instance->BlockSize; ++ *NumberOfBlocks = (UINTN)(Instance->LastBlock - Lba + 1); + + DEBUG ((DEBUG_BLKIO, "FvbGetBlockSize: *BlockSize=0x%x, *NumberOfBlocks=0x%x.\n", *BlockSize, *NumberOfBlocks)); + +@@ -498,7 +490,7 @@ FvbRead ( + TempStatus = EFI_SUCCESS; + + // Cache the block size to avoid de-referencing pointers all the time +- BlockSize = Instance->Media.BlockSize; ++ BlockSize = Instance->BlockSize; + + DEBUG ((DEBUG_BLKIO, "FvbRead: Check if (Offset=0x%x + NumBytes=0x%x) <= BlockSize=0x%x\n", Offset, *NumBytes, BlockSize)); + +@@ -669,13 +661,6 @@ FvbEraseBlocks ( + + Status = EFI_SUCCESS; + +- // Detect WriteDisabled state +- if (Instance->Media.ReadOnly == TRUE) { +- // Firmware volume is in WriteDisabled state +- DEBUG ((DEBUG_ERROR, "FvbEraseBlocks: ERROR - Device is in WriteDisabled state.\n")); +- return EFI_ACCESS_DENIED; +- } +- + // Before erasing, check the entire list of parameters to ensure all specified blocks are valid + + VA_START (Args, This); +@@ -698,9 +683,9 @@ FvbEraseBlocks ( + "FvbEraseBlocks: Check if: ( StartingLba=%ld + NumOfLba=%Lu - 1 ) > LastBlock=%ld.\n", + Instance->StartLba + StartingLba, + (UINT64)NumOfLba, +- Instance->Media.LastBlock ++ Instance->LastBlock + )); +- if ((NumOfLba == 0) || ((Instance->StartLba + StartingLba + NumOfLba - 1) > Instance->Media.LastBlock)) { ++ if ((NumOfLba == 0) || ((Instance->StartLba + StartingLba + NumOfLba - 1) > Instance->LastBlock)) { + VA_END (Args); + DEBUG ((DEBUG_ERROR, "FvbEraseBlocks: ERROR - Lba range goes past the last Lba.\n")); + Status = EFI_INVALID_PARAMETER; +@@ -733,7 +718,7 @@ FvbEraseBlocks ( + BlockAddress = GET_NOR_BLOCK_ADDRESS ( + Instance->RegionBaseAddress, + Instance->StartLba + StartingLba, +- Instance->Media.BlockSize ++ Instance->BlockSize + ); + + // Erase it +-- +2.41.0 + diff --git a/edk2-OvmfPkg-VirtNorFlashDxe-map-flash-memory-as-uncachea.patch b/edk2-OvmfPkg-VirtNorFlashDxe-map-flash-memory-as-uncachea.patch new file mode 100644 index 0000000..8eff08e --- /dev/null +++ b/edk2-OvmfPkg-VirtNorFlashDxe-map-flash-memory-as-uncachea.patch @@ -0,0 +1,67 @@ +From 15415de9a228e74ff1847777a29f1531754b03b0 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Wed, 11 Jan 2023 19:00:23 +0100 +Subject: [PATCH 08/18] OvmfPkg/VirtNorFlashDxe: map flash memory as + uncacheable + +RH-Author: Gerd Hoffmann +RH-MergeRequest: 43: OvmfPkg/VirtNorFlashDxe backport +RH-Jira: RHEL-17587 +RH-Acked-by: Laszlo Ersek +RH-Commit: [10/20] 40ca967bead9ec5c98c528bfe0757f75f3d3352f + +Switching from the ArmPlatformPkg/NorFlashDxe driver to the +OvmfPkg/VirtNorFlashDxe driver had the side effect that flash address +space got registered as EFI_MEMORY_WC instead of EFI_MEMORY_UC. + +That confuses the linux kernel's numa code, seems this makes kernel +consider the flash being node memory. "lsmem" changes from ... + + RANGE SIZE STATE REMOVABLE BLOCK + 0x0000000040000000-0x000000013fffffff 4G online yes 8-39 + +... to ... + + RANGE SIZE STATE REMOVABLE BLOCK + 0x0000000000000000-0x0000000007ffffff 128M online yes 0 + 0x0000000040000000-0x000000013fffffff 4G online yes 8-39 + +... and in the kernel log got new error lines: + + NUMA: Warning: invalid memblk node 512 [mem 0x0000000004000000-0x0000000007ffffff] + NUMA: Faking a node at [mem 0x0000000004000000-0x000000013fffffff] + +Changing the attributes back to EFI_MEMORY_UC fixes this. + +Fixes: b92298af8218 ("ArmVirtPkg/ArmVirtQemu: migrate to OVMF's VirtNorFlashDxe") +Signed-off-by: Gerd Hoffmann +Reviewed-by: Ard Biesheuvel +(cherry picked from commit e5ec3ba409b5baa9cf429cc25fdf3c8d1b8dcef0) +--- + OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.c b/OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.c +index ff3121af2a..f9a41f6aab 100644 +--- a/OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.c ++++ b/OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.c +@@ -394,14 +394,14 @@ NorFlashFvbInitialize ( + EfiGcdMemoryTypeMemoryMappedIo, + Instance->DeviceBaseAddress, + RuntimeMmioRegionSize, +- EFI_MEMORY_WC | EFI_MEMORY_RUNTIME ++ EFI_MEMORY_UC | EFI_MEMORY_RUNTIME + ); + ASSERT_EFI_ERROR (Status); + + Status = gDS->SetMemorySpaceAttributes ( + Instance->DeviceBaseAddress, + RuntimeMmioRegionSize, +- EFI_MEMORY_WC | EFI_MEMORY_RUNTIME ++ EFI_MEMORY_UC | EFI_MEMORY_RUNTIME + ); + ASSERT_EFI_ERROR (Status); + +-- +2.41.0 + diff --git a/edk2-OvmfPkg-VirtNorFlashDxe-move-DoErase-code-block-into.patch b/edk2-OvmfPkg-VirtNorFlashDxe-move-DoErase-code-block-into.patch new file mode 100644 index 0000000..da9eafb --- /dev/null +++ b/edk2-OvmfPkg-VirtNorFlashDxe-move-DoErase-code-block-into.patch @@ -0,0 +1,131 @@ +From 791c26a4a172b4a609a708db8018411ab653de4a Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Tue, 16 Jan 2024 18:11:05 +0100 +Subject: [PATCH 16/18] OvmfPkg/VirtNorFlashDxe: move DoErase code block into + new function + +RH-Author: Gerd Hoffmann +RH-MergeRequest: 43: OvmfPkg/VirtNorFlashDxe backport +RH-Jira: RHEL-17587 +RH-Acked-by: Laszlo Ersek +RH-Commit: [18/20] 10f4685bfcb0c5423e392b4cf0e8633cd25b46b4 + +Move the DoErase code block into a separate function, call the function +instead of jumping around with goto. + +Signed-off-by: Gerd Hoffmann +Message-Id: <20240116171105.37831-7-kraxel@redhat.com> +Reviewed-by: Laszlo Ersek +(cherry picked from commit b481b00f593ef37695ee14271453320ed02a1256) +--- + OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c | 76 ++++++++++++++++++-------- + 1 file changed, 52 insertions(+), 24 deletions(-) + +diff --git a/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c b/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c +index 3d1d20daa1..e6aaed27ce 100644 +--- a/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c ++++ b/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c +@@ -502,6 +502,38 @@ NorFlashRead ( + return EFI_SUCCESS; + } + ++STATIC ++EFI_STATUS ++NorFlashWriteSingleBlockWithErase ( ++ IN NOR_FLASH_INSTANCE *Instance, ++ IN EFI_LBA Lba, ++ IN UINTN Offset, ++ IN OUT UINTN *NumBytes, ++ IN UINT8 *Buffer ++ ) ++{ ++ EFI_STATUS Status; ++ ++ // Read NOR Flash data into shadow buffer ++ Status = NorFlashReadBlocks (Instance, Lba, Instance->BlockSize, Instance->ShadowBuffer); ++ if (EFI_ERROR (Status)) { ++ // Return one of the pre-approved error statuses ++ return EFI_DEVICE_ERROR; ++ } ++ ++ // Put the data at the appropriate location inside the buffer area ++ CopyMem ((VOID *)((UINTN)Instance->ShadowBuffer + Offset), Buffer, *NumBytes); ++ ++ // Write the modified buffer back to the NorFlash ++ Status = NorFlashWriteBlocks (Instance, Lba, Instance->BlockSize, Instance->ShadowBuffer); ++ if (EFI_ERROR (Status)) { ++ // Return one of the pre-approved error statuses ++ return EFI_DEVICE_ERROR; ++ } ++ ++ return EFI_SUCCESS; ++} ++ + /* + Write a full or portion of a block. It must not span block boundaries; that is, + Offset + *NumBytes <= Instance->BlockSize. +@@ -607,7 +639,14 @@ NorFlashWriteSingleBlock ( + // that we want to set. In that case, we will need to erase the block first. + for (CurOffset = 0; CurOffset < *NumBytes; CurOffset++) { + if (~(UINT32)OrigData[CurOffset] & (UINT32)Buffer[CurOffset]) { +- goto DoErase; ++ Status = NorFlashWriteSingleBlockWithErase ( ++ Instance, ++ Lba, ++ Offset, ++ NumBytes, ++ Buffer ++ ); ++ return Status; + } + + OrigData[CurOffset] = Buffer[CurOffset]; +@@ -636,33 +675,22 @@ NorFlashWriteSingleBlock ( + goto Exit; + } + } +- +-Exit: +- // Put device back into Read Array mode +- SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY); +- ++ } else { ++ Status = NorFlashWriteSingleBlockWithErase ( ++ Instance, ++ Lba, ++ Offset, ++ NumBytes, ++ Buffer ++ ); + return Status; + } + +-DoErase: +- // Read NOR Flash data into shadow buffer +- Status = NorFlashReadBlocks (Instance, Lba, BlockSize, Instance->ShadowBuffer); +- if (EFI_ERROR (Status)) { +- // Return one of the pre-approved error statuses +- return EFI_DEVICE_ERROR; +- } +- +- // Put the data at the appropriate location inside the buffer area +- CopyMem ((VOID *)((UINTN)Instance->ShadowBuffer + Offset), Buffer, *NumBytes); +- +- // Write the modified buffer back to the NorFlash +- Status = NorFlashWriteBlocks (Instance, Lba, BlockSize, Instance->ShadowBuffer); +- if (EFI_ERROR (Status)) { +- // Return one of the pre-approved error statuses +- return EFI_DEVICE_ERROR; +- } ++Exit: ++ // Put device back into Read Array mode ++ SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY); + +- return EFI_SUCCESS; ++ return Status; + } + + EFI_STATUS +-- +2.41.0 + diff --git a/edk2-OvmfPkg-VirtNorFlashDxe-remove-CheckBlockLocked-feat.patch b/edk2-OvmfPkg-VirtNorFlashDxe-remove-CheckBlockLocked-feat.patch new file mode 100644 index 0000000..2fdee8e --- /dev/null +++ b/edk2-OvmfPkg-VirtNorFlashDxe-remove-CheckBlockLocked-feat.patch @@ -0,0 +1,94 @@ +From 03e0a729a5c3ebcab8806d136cd8908627bd91c9 Mon Sep 17 00:00:00 2001 +From: Ard Biesheuvel +Date: Mon, 24 Oct 2022 16:45:02 +0200 +Subject: [PATCH 02/18] OvmfPkg/VirtNorFlashDxe: remove CheckBlockLocked + feature + +RH-Author: Gerd Hoffmann +RH-MergeRequest: 43: OvmfPkg/VirtNorFlashDxe backport +RH-Jira: RHEL-17587 +RH-Acked-by: Laszlo Ersek +RH-Commit: [4/20] 990bdf373801df8107d8a6ec4db3fb93e5a6ad68 + +We inherited a feature from the ArmPlatformPkg version of this driver +that never gets enabled. Let's remove it. + +Signed-off-by: Ard Biesheuvel +Reviewed-by: Sunil V L +(cherry picked from commit 0a64106c566273ff8ef951d56ddfa972fe65bd6c) +--- + OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c | 35 +++++---------------- + OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.inf | 3 -- + 2 files changed, 8 insertions(+), 30 deletions(-) + +diff --git a/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c b/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c +index 12fa720dad..59a562efdf 100644 +--- a/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c ++++ b/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c +@@ -65,35 +65,16 @@ NorFlashUnlockSingleBlock ( + // Raise the Task Priority Level to TPL_NOTIFY to serialise all its operations + // and to protect shared data structures. + +- if (FeaturePcdGet (PcdNorFlashCheckBlockLocked) == TRUE) { +- do { +- // Request a lock setup +- SEND_NOR_COMMAND (BlockAddress, 0, P30_CMD_LOCK_BLOCK_SETUP); ++ // Request a lock setup ++ SEND_NOR_COMMAND (BlockAddress, 0, P30_CMD_LOCK_BLOCK_SETUP); + +- // Request an unlock +- SEND_NOR_COMMAND (BlockAddress, 0, P30_CMD_UNLOCK_BLOCK); ++ // Request an unlock ++ SEND_NOR_COMMAND (BlockAddress, 0, P30_CMD_UNLOCK_BLOCK); + +- // Send command for reading device id +- SEND_NOR_COMMAND (BlockAddress, 2, P30_CMD_READ_DEVICE_ID); +- +- // Read block lock status +- LockStatus = MmioRead32 (CREATE_NOR_ADDRESS (BlockAddress, 2)); +- +- // Decode block lock status +- LockStatus = FOLD_32BIT_INTO_16BIT (LockStatus); +- } while ((LockStatus & 0x1) == 1); +- } else { +- // Request a lock setup +- SEND_NOR_COMMAND (BlockAddress, 0, P30_CMD_LOCK_BLOCK_SETUP); +- +- // Request an unlock +- SEND_NOR_COMMAND (BlockAddress, 0, P30_CMD_UNLOCK_BLOCK); +- +- // Wait until the status register gives us the all clear +- do { +- LockStatus = NorFlashReadStatusRegister (Instance, BlockAddress); +- } while ((LockStatus & P30_SR_BIT_WRITE) != P30_SR_BIT_WRITE); +- } ++ // Wait until the status register gives us the all clear ++ do { ++ LockStatus = NorFlashReadStatusRegister (Instance, BlockAddress); ++ } while ((LockStatus & P30_SR_BIT_WRITE) != P30_SR_BIT_WRITE); + + // Put device back into Read Array mode + SEND_NOR_COMMAND (BlockAddress, 0, P30_CMD_READ_ARRAY); +diff --git a/OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.inf b/OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.inf +index 1bf50e4823..53e9d58204 100644 +--- a/OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.inf ++++ b/OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.inf +@@ -24,7 +24,6 @@ + VirtNorFlashFvb.c + + [Packages] +- ArmPlatformPkg/ArmPlatformPkg.dec + EmbeddedPkg/EmbeddedPkg.dec + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec +@@ -66,7 +65,5 @@ + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareBase + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareSize + +- gArmPlatformTokenSpaceGuid.PcdNorFlashCheckBlockLocked +- + [Depex] + gEfiCpuArchProtocolGuid +-- +2.41.0 + diff --git a/edk2-OvmfPkg-VirtNorFlashDxe-remove-disk-I-O-protocol-imp.patch b/edk2-OvmfPkg-VirtNorFlashDxe-remove-disk-I-O-protocol-imp.patch new file mode 100644 index 0000000..9319925 --- /dev/null +++ b/edk2-OvmfPkg-VirtNorFlashDxe-remove-disk-I-O-protocol-imp.patch @@ -0,0 +1,386 @@ +From 56041232238e4e4d3c8d703b27f51b0bc70fd5c8 Mon Sep 17 00:00:00 2001 +From: Ard Biesheuvel +Date: Mon, 24 Oct 2022 16:50:05 +0200 +Subject: [PATCH 03/18] OvmfPkg/VirtNorFlashDxe: remove disk I/O protocol + implementation + +RH-Author: Gerd Hoffmann +RH-MergeRequest: 43: OvmfPkg/VirtNorFlashDxe backport +RH-Jira: RHEL-17587 +RH-Acked-by: Laszlo Ersek +RH-Commit: [5/20] 0551c3f56f43396cfdc380127565e89d69eb29a3 + +We only use NOR flash for firmware volumes, either for executable images +or for the variable store. So we have no need for exposing disk I/O on +top of the NOR flash partitions so let's remove it. + +Signed-off-by: Ard Biesheuvel +Reviewed-by: Sunil V L +(cherry picked from commit 68d234989b2d6bd8f255577e08bf8be0b1d197bb) +--- + OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c | 129 ------------------ + OvmfPkg/VirtNorFlashDxe/VirtNorFlash.h | 29 ---- + .../VirtNorFlashDxe/VirtNorFlashBlockIoDxe.c | 123 ----------------- + OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.c | 8 -- + 4 files changed, 289 deletions(-) + delete mode 100644 OvmfPkg/VirtNorFlashDxe/VirtNorFlashBlockIoDxe.c + +diff --git a/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c b/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c +index 59a562efdf..1094d48f7d 100644 +--- a/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c ++++ b/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c +@@ -788,135 +788,6 @@ NorFlashWriteSingleBlock ( + return EFI_SUCCESS; + } + +-/* +- Although DiskIoDxe will automatically install the DiskIO protocol whenever +- we install the BlockIO protocol, its implementation is sub-optimal as it reads +- and writes entire blocks using the BlockIO protocol. In fact we can access +- NOR flash with a finer granularity than that, so we can improve performance +- by directly producing the DiskIO protocol. +-*/ +- +-/** +- Read BufferSize bytes from Offset into Buffer. +- +- @param This Protocol instance pointer. +- @param MediaId Id of the media, changes every time the media is replaced. +- @param Offset The starting byte offset to read from +- @param BufferSize Size of Buffer +- @param Buffer Buffer containing read data +- +- @retval EFI_SUCCESS The data was read correctly from the device. +- @retval EFI_DEVICE_ERROR The device reported an error while performing the read. +- @retval EFI_NO_MEDIA There is no media in the device. +- @retval EFI_MEDIA_CHANGED The MediaId does not match the current device. +- @retval EFI_INVALID_PARAMETER The read request contains device addresses that are not +- valid for the device. +- +-**/ +-EFI_STATUS +-EFIAPI +-NorFlashDiskIoReadDisk ( +- IN EFI_DISK_IO_PROTOCOL *This, +- IN UINT32 MediaId, +- IN UINT64 DiskOffset, +- IN UINTN BufferSize, +- OUT VOID *Buffer +- ) +-{ +- NOR_FLASH_INSTANCE *Instance; +- UINT32 BlockSize; +- UINT32 BlockOffset; +- EFI_LBA Lba; +- +- Instance = INSTANCE_FROM_DISKIO_THIS (This); +- +- if (MediaId != Instance->Media.MediaId) { +- return EFI_MEDIA_CHANGED; +- } +- +- BlockSize = Instance->Media.BlockSize; +- Lba = (EFI_LBA)DivU64x32Remainder (DiskOffset, BlockSize, &BlockOffset); +- +- return NorFlashRead (Instance, Lba, BlockOffset, BufferSize, Buffer); +-} +- +-/** +- Writes a specified number of bytes to a device. +- +- @param This Indicates a pointer to the calling context. +- @param MediaId ID of the medium to be written. +- @param Offset The starting byte offset on the logical block I/O device to write. +- @param BufferSize The size in bytes of Buffer. The number of bytes to write to the device. +- @param Buffer A pointer to the buffer containing the data to be written. +- +- @retval EFI_SUCCESS The data was written correctly to the device. +- @retval EFI_WRITE_PROTECTED The device can not be written to. +- @retval EFI_DEVICE_ERROR The device reported an error while performing the write. +- @retval EFI_NO_MEDIA There is no media in the device. +- @retval EFI_MEDIA_CHANGED The MediaId does not match the current device. +- @retval EFI_INVALID_PARAMETER The write request contains device addresses that are not +- valid for the device. +- +-**/ +-EFI_STATUS +-EFIAPI +-NorFlashDiskIoWriteDisk ( +- IN EFI_DISK_IO_PROTOCOL *This, +- IN UINT32 MediaId, +- IN UINT64 DiskOffset, +- IN UINTN BufferSize, +- IN VOID *Buffer +- ) +-{ +- NOR_FLASH_INSTANCE *Instance; +- UINT32 BlockSize; +- UINT32 BlockOffset; +- EFI_LBA Lba; +- UINTN RemainingBytes; +- UINTN WriteSize; +- EFI_STATUS Status; +- +- Instance = INSTANCE_FROM_DISKIO_THIS (This); +- +- if (MediaId != Instance->Media.MediaId) { +- return EFI_MEDIA_CHANGED; +- } +- +- BlockSize = Instance->Media.BlockSize; +- Lba = (EFI_LBA)DivU64x32Remainder (DiskOffset, BlockSize, &BlockOffset); +- +- RemainingBytes = BufferSize; +- +- // Write either all the remaining bytes, or the number of bytes that bring +- // us up to a block boundary, whichever is less. +- // (DiskOffset | (BlockSize - 1)) + 1) rounds DiskOffset up to the next +- // block boundary (even if it is already on one). +- WriteSize = MIN (RemainingBytes, ((DiskOffset | (BlockSize - 1)) + 1) - DiskOffset); +- +- do { +- if (WriteSize == BlockSize) { +- // Write a full block +- Status = NorFlashWriteFullBlock (Instance, Lba, Buffer, BlockSize / sizeof (UINT32)); +- } else { +- // Write a partial block +- Status = NorFlashWriteSingleBlock (Instance, Lba, BlockOffset, &WriteSize, Buffer); +- } +- +- if (EFI_ERROR (Status)) { +- return Status; +- } +- +- // Now continue writing either all the remaining bytes or single blocks. +- RemainingBytes -= WriteSize; +- Buffer = (UINT8 *)Buffer + WriteSize; +- Lba++; +- BlockOffset = 0; +- WriteSize = MIN (RemainingBytes, BlockSize); +- } while (RemainingBytes); +- +- return Status; +-} +- + EFI_STATUS + NorFlashReset ( + IN NOR_FLASH_INSTANCE *Instance +diff --git a/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.h b/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.h +index e46522a198..7733ee02ee 100644 +--- a/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.h ++++ b/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.h +@@ -15,7 +15,6 @@ + #include + + #include +-#include + #include + + #include +@@ -111,7 +110,6 @@ + #define NOR_FLASH_SIGNATURE SIGNATURE_32('n', 'o', 'r', '0') + #define INSTANCE_FROM_FVB_THIS(a) CR(a, NOR_FLASH_INSTANCE, FvbProtocol, NOR_FLASH_SIGNATURE) + #define INSTANCE_FROM_BLKIO_THIS(a) CR(a, NOR_FLASH_INSTANCE, BlockIoProtocol, NOR_FLASH_SIGNATURE) +-#define INSTANCE_FROM_DISKIO_THIS(a) CR(a, NOR_FLASH_INSTANCE, DiskIoProtocol, NOR_FLASH_SIGNATURE) + + typedef struct _NOR_FLASH_INSTANCE NOR_FLASH_INSTANCE; + +@@ -134,7 +132,6 @@ struct _NOR_FLASH_INSTANCE { + + EFI_BLOCK_IO_PROTOCOL BlockIoProtocol; + EFI_BLOCK_IO_MEDIA Media; +- EFI_DISK_IO_PROTOCOL DiskIoProtocol; + + EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL FvbProtocol; + VOID *ShadowBuffer; +@@ -203,32 +200,6 @@ NorFlashBlockIoFlushBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This + ); + +-// +-// DiskIO Protocol function EFI_DISK_IO_PROTOCOL.ReadDisk +-// +-EFI_STATUS +-EFIAPI +-NorFlashDiskIoReadDisk ( +- IN EFI_DISK_IO_PROTOCOL *This, +- IN UINT32 MediaId, +- IN UINT64 Offset, +- IN UINTN BufferSize, +- OUT VOID *Buffer +- ); +- +-// +-// DiskIO Protocol function EFI_DISK_IO_PROTOCOL.WriteDisk +-// +-EFI_STATUS +-EFIAPI +-NorFlashDiskIoWriteDisk ( +- IN EFI_DISK_IO_PROTOCOL *This, +- IN UINT32 MediaId, +- IN UINT64 Offset, +- IN UINTN BufferSize, +- IN VOID *Buffer +- ); +- + // + // NorFlashFvbDxe.c + // +diff --git a/OvmfPkg/VirtNorFlashDxe/VirtNorFlashBlockIoDxe.c b/OvmfPkg/VirtNorFlashDxe/VirtNorFlashBlockIoDxe.c +deleted file mode 100644 +index ecf152e355..0000000000 +--- a/OvmfPkg/VirtNorFlashDxe/VirtNorFlashBlockIoDxe.c ++++ /dev/null +@@ -1,123 +0,0 @@ +-/** @file NorFlashBlockIoDxe.c +- +- Copyright (c) 2011-2013, ARM Ltd. All rights reserved.
+- +- SPDX-License-Identifier: BSD-2-Clause-Patent +- +-**/ +- +-#include +-#include +- +-#include "VirtNorFlash.h" +- +-// +-// BlockIO Protocol function EFI_BLOCK_IO_PROTOCOL.Reset +-// +-EFI_STATUS +-EFIAPI +-NorFlashBlockIoReset ( +- IN EFI_BLOCK_IO_PROTOCOL *This, +- IN BOOLEAN ExtendedVerification +- ) +-{ +- NOR_FLASH_INSTANCE *Instance; +- +- Instance = INSTANCE_FROM_BLKIO_THIS (This); +- +- DEBUG ((DEBUG_BLKIO, "NorFlashBlockIoReset(MediaId=0x%x)\n", This->Media->MediaId)); +- +- return NorFlashReset (Instance); +-} +- +-// +-// BlockIO Protocol function EFI_BLOCK_IO_PROTOCOL.ReadBlocks +-// +-EFI_STATUS +-EFIAPI +-NorFlashBlockIoReadBlocks ( +- IN EFI_BLOCK_IO_PROTOCOL *This, +- IN UINT32 MediaId, +- IN EFI_LBA Lba, +- IN UINTN BufferSizeInBytes, +- OUT VOID *Buffer +- ) +-{ +- NOR_FLASH_INSTANCE *Instance; +- EFI_STATUS Status; +- EFI_BLOCK_IO_MEDIA *Media; +- +- if (This == NULL) { +- return EFI_INVALID_PARAMETER; +- } +- +- Instance = INSTANCE_FROM_BLKIO_THIS (This); +- Media = This->Media; +- +- DEBUG ((DEBUG_BLKIO, "NorFlashBlockIoReadBlocks(MediaId=0x%x, Lba=%ld, BufferSize=0x%x bytes (%d kB), BufferPtr @ 0x%08x)\n", MediaId, Lba, BufferSizeInBytes, BufferSizeInBytes, Buffer)); +- +- if (!Media) { +- Status = EFI_INVALID_PARAMETER; +- } else if (!Media->MediaPresent) { +- Status = EFI_NO_MEDIA; +- } else if (Media->MediaId != MediaId) { +- Status = EFI_MEDIA_CHANGED; +- } else if ((Media->IoAlign > 2) && (((UINTN)Buffer & (Media->IoAlign - 1)) != 0)) { +- Status = EFI_INVALID_PARAMETER; +- } else { +- Status = NorFlashReadBlocks (Instance, Lba, BufferSizeInBytes, Buffer); +- } +- +- return Status; +-} +- +-// +-// BlockIO Protocol function EFI_BLOCK_IO_PROTOCOL.WriteBlocks +-// +-EFI_STATUS +-EFIAPI +-NorFlashBlockIoWriteBlocks ( +- IN EFI_BLOCK_IO_PROTOCOL *This, +- IN UINT32 MediaId, +- IN EFI_LBA Lba, +- IN UINTN BufferSizeInBytes, +- IN VOID *Buffer +- ) +-{ +- NOR_FLASH_INSTANCE *Instance; +- EFI_STATUS Status; +- +- Instance = INSTANCE_FROM_BLKIO_THIS (This); +- +- DEBUG ((DEBUG_BLKIO, "NorFlashBlockIoWriteBlocks(MediaId=0x%x, Lba=%ld, BufferSize=0x%x bytes, BufferPtr @ 0x%08x)\n", MediaId, Lba, BufferSizeInBytes, Buffer)); +- +- if ( !This->Media->MediaPresent ) { +- Status = EFI_NO_MEDIA; +- } else if ( This->Media->MediaId != MediaId ) { +- Status = EFI_MEDIA_CHANGED; +- } else if ( This->Media->ReadOnly ) { +- Status = EFI_WRITE_PROTECTED; +- } else { +- Status = NorFlashWriteBlocks (Instance, Lba, BufferSizeInBytes, Buffer); +- } +- +- return Status; +-} +- +-// +-// BlockIO Protocol function EFI_BLOCK_IO_PROTOCOL.FlushBlocks +-// +-EFI_STATUS +-EFIAPI +-NorFlashBlockIoFlushBlocks ( +- IN EFI_BLOCK_IO_PROTOCOL *This +- ) +-{ +- // No Flush required for the NOR Flash driver +- // because cache operations are not permitted. +- +- DEBUG ((DEBUG_BLKIO, "NorFlashBlockIoFlushBlocks: Function NOT IMPLEMENTED (not required).\n")); +- +- // Nothing to do so just return without error +- return EFI_SUCCESS; +-} +diff --git a/OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.c b/OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.c +index 819425545e..4875b057d5 100644 +--- a/OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.c ++++ b/OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.c +@@ -58,12 +58,6 @@ NOR_FLASH_INSTANCE mNorFlashInstanceTemplate = { + 1, // LogicalBlocksPerPhysicalBlock + }, // Media; + +- { +- EFI_DISK_IO_PROTOCOL_REVISION, // Revision +- NorFlashDiskIoReadDisk, // ReadDisk +- NorFlashDiskIoWriteDisk // WriteDisk +- }, +- + { + FvbGetAttributes, // GetAttributes + FvbSetAttributes, // SetAttributes +@@ -159,8 +153,6 @@ NorFlashCreateInstance ( + &Instance->DevicePath, + &gEfiBlockIoProtocolGuid, + &Instance->BlockIoProtocol, +- &gEfiDiskIoProtocolGuid, +- &Instance->DiskIoProtocol, + NULL + ); + if (EFI_ERROR (Status)) { +-- +2.41.0 + diff --git a/edk2-OvmfPkg-VirtNorFlashDxe-sanity-check-variable2.patch b/edk2-OvmfPkg-VirtNorFlashDxe-sanity-check-variable2.patch new file mode 100644 index 0000000..106b9db --- /dev/null +++ b/edk2-OvmfPkg-VirtNorFlashDxe-sanity-check-variable2.patch @@ -0,0 +1,216 @@ +From c4d2144caff4eddb7021752fce6c2dec6d5e1632 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Tue, 9 Jan 2024 12:29:02 +0100 +Subject: [PATCH 10/18] OvmfPkg/VirtNorFlashDxe: sanity-check variables + +RH-Author: Gerd Hoffmann +RH-MergeRequest: 43: OvmfPkg/VirtNorFlashDxe backport +RH-Jira: RHEL-17587 +RH-Acked-by: Laszlo Ersek +RH-Commit: [12/20] 2ad3957478b82a4ca29249ceb9620f97c591a1fe + +Extend the ValidateFvHeader function, additionally to the header checks +walk over the list of variables and sanity check them. + +In case we find inconsistencies indicating variable store corruption +return EFI_NOT_FOUND so the variable store will be re-initialized. + +Signed-off-by: Gerd Hoffmann +Message-Id: <20240109112902.30002-4-kraxel@redhat.com> +Reviewed-by: Laszlo Ersek +[lersek@redhat.com: fix StartId initialization/assignment coding style] +(cherry picked from commit 4a443f73fd67ca8caaf0a3e1a01f8231b330d2e0) +--- + OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.inf | 1 + + OvmfPkg/VirtNorFlashDxe/VirtNorFlashFvb.c | 149 +++++++++++++++++++- + 2 files changed, 145 insertions(+), 5 deletions(-) + +diff --git a/OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.inf b/OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.inf +index 2a3d4a218e..f549400280 100644 +--- a/OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.inf ++++ b/OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.inf +@@ -34,6 +34,7 @@ + DxeServicesTableLib + HobLib + IoLib ++ SafeIntLib + UefiBootServicesTableLib + UefiDriverEntryPoint + UefiLib +diff --git a/OvmfPkg/VirtNorFlashDxe/VirtNorFlashFvb.c b/OvmfPkg/VirtNorFlashDxe/VirtNorFlashFvb.c +index c503272a2b..acc4a413ee 100644 +--- a/OvmfPkg/VirtNorFlashDxe/VirtNorFlashFvb.c ++++ b/OvmfPkg/VirtNorFlashDxe/VirtNorFlashFvb.c +@@ -12,6 +12,7 @@ + #include + #include + #include ++#include + #include + + #include +@@ -185,11 +186,12 @@ ValidateFvHeader ( + IN NOR_FLASH_INSTANCE *Instance + ) + { +- UINT16 Checksum; +- EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader; +- VARIABLE_STORE_HEADER *VariableStoreHeader; +- UINTN VariableStoreLength; +- UINTN FvLength; ++ UINT16 Checksum; ++ CONST EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader; ++ CONST VARIABLE_STORE_HEADER *VariableStoreHeader; ++ UINTN VarOffset; ++ UINTN VariableStoreLength; ++ UINTN FvLength; + + FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *)Instance->RegionBaseAddress; + +@@ -258,6 +260,143 @@ ValidateFvHeader ( + return EFI_NOT_FOUND; + } + ++ // ++ // check variables ++ // ++ DEBUG ((DEBUG_INFO, "%a: checking variables\n", __func__)); ++ VarOffset = sizeof (*VariableStoreHeader); ++ for ( ; ;) { ++ UINTN VarHeaderEnd; ++ UINTN VarNameEnd; ++ UINTN VarEnd; ++ UINTN VarPadding; ++ CONST AUTHENTICATED_VARIABLE_HEADER *VarHeader; ++ CONST CHAR16 *VarName; ++ CONST CHAR8 *VarState; ++ RETURN_STATUS Status; ++ ++ Status = SafeUintnAdd (VarOffset, sizeof (*VarHeader), &VarHeaderEnd); ++ if (RETURN_ERROR (Status)) { ++ DEBUG ((DEBUG_ERROR, "%a: integer overflow\n", __func__)); ++ return EFI_NOT_FOUND; ++ } ++ ++ if (VarHeaderEnd >= VariableStoreHeader->Size) { ++ if (VarOffset <= VariableStoreHeader->Size - sizeof (UINT16)) { ++ CONST UINT16 *StartId; ++ ++ StartId = (VOID *)((UINTN)VariableStoreHeader + VarOffset); ++ if (*StartId == 0x55aa) { ++ DEBUG ((DEBUG_ERROR, "%a: startid at invalid location\n", __func__)); ++ return EFI_NOT_FOUND; ++ } ++ } ++ ++ DEBUG ((DEBUG_INFO, "%a: end of var list (no space left)\n", __func__)); ++ break; ++ } ++ ++ VarHeader = (VOID *)((UINTN)VariableStoreHeader + VarOffset); ++ if (VarHeader->StartId != 0x55aa) { ++ DEBUG ((DEBUG_INFO, "%a: end of var list (no startid)\n", __func__)); ++ break; ++ } ++ ++ VarName = NULL; ++ switch (VarHeader->State) { ++ // usage: State = VAR_HEADER_VALID_ONLY ++ case VAR_HEADER_VALID_ONLY: ++ VarState = "header-ok"; ++ VarName = L""; ++ break; ++ ++ // usage: State = VAR_ADDED ++ case VAR_ADDED: ++ VarState = "ok"; ++ break; ++ ++ // usage: State &= VAR_IN_DELETED_TRANSITION ++ case VAR_ADDED &VAR_IN_DELETED_TRANSITION: ++ VarState = "del-in-transition"; ++ break; ++ ++ // usage: State &= VAR_DELETED ++ case VAR_ADDED &VAR_DELETED: ++ case VAR_ADDED &VAR_DELETED &VAR_IN_DELETED_TRANSITION: ++ VarState = "deleted"; ++ break; ++ ++ default: ++ DEBUG (( ++ DEBUG_ERROR, ++ "%a: invalid variable state: 0x%x\n", ++ __func__, ++ VarHeader->State ++ )); ++ return EFI_NOT_FOUND; ++ } ++ ++ Status = SafeUintnAdd (VarHeaderEnd, VarHeader->NameSize, &VarNameEnd); ++ if (RETURN_ERROR (Status)) { ++ DEBUG ((DEBUG_ERROR, "%a: integer overflow\n", __func__)); ++ return EFI_NOT_FOUND; ++ } ++ ++ Status = SafeUintnAdd (VarNameEnd, VarHeader->DataSize, &VarEnd); ++ if (RETURN_ERROR (Status)) { ++ DEBUG ((DEBUG_ERROR, "%a: integer overflow\n", __func__)); ++ return EFI_NOT_FOUND; ++ } ++ ++ if (VarEnd > VariableStoreHeader->Size) { ++ DEBUG (( ++ DEBUG_ERROR, ++ "%a: invalid variable size: 0x%Lx + 0x%Lx + 0x%x + 0x%x > 0x%x\n", ++ __func__, ++ (UINT64)VarOffset, ++ (UINT64)(sizeof (*VarHeader)), ++ VarHeader->NameSize, ++ VarHeader->DataSize, ++ VariableStoreHeader->Size ++ )); ++ return EFI_NOT_FOUND; ++ } ++ ++ if (((VarHeader->NameSize & 1) != 0) || ++ (VarHeader->NameSize < 4)) ++ { ++ DEBUG ((DEBUG_ERROR, "%a: invalid name size\n", __func__)); ++ return EFI_NOT_FOUND; ++ } ++ ++ if (VarName == NULL) { ++ VarName = (VOID *)((UINTN)VariableStoreHeader + VarHeaderEnd); ++ if (VarName[VarHeader->NameSize / 2 - 1] != L'\0') { ++ DEBUG ((DEBUG_ERROR, "%a: name is not null terminated\n", __func__)); ++ return EFI_NOT_FOUND; ++ } ++ } ++ ++ DEBUG (( ++ DEBUG_VERBOSE, ++ "%a: +0x%04Lx: name=0x%x data=0x%x guid=%g '%s' (%a)\n", ++ __func__, ++ (UINT64)VarOffset, ++ VarHeader->NameSize, ++ VarHeader->DataSize, ++ &VarHeader->VendorGuid, ++ VarName, ++ VarState ++ )); ++ ++ VarPadding = (4 - (VarEnd & 3)) & 3; ++ Status = SafeUintnAdd (VarEnd, VarPadding, &VarOffset); ++ if (RETURN_ERROR (Status)) { ++ DEBUG ((DEBUG_ERROR, "%a: integer overflow\n", __func__)); ++ return EFI_NOT_FOUND; ++ } ++ } ++ + return EFI_SUCCESS; + } + +-- +2.41.0 + diff --git a/edk2-OvmfPkg-VirtNorFlashDxe-stop-accepting-gEfiVariable2.patch b/edk2-OvmfPkg-VirtNorFlashDxe-stop-accepting-gEfiVariable2.patch new file mode 100644 index 0000000..a735619 --- /dev/null +++ b/edk2-OvmfPkg-VirtNorFlashDxe-stop-accepting-gEfiVariable2.patch @@ -0,0 +1,49 @@ +From 1444157aad1b98ce9c1193ef109011b084113890 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Tue, 9 Jan 2024 12:29:01 +0100 +Subject: [PATCH 09/18] OvmfPkg/VirtNorFlashDxe: stop accepting + gEfiVariableGuid +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Gerd Hoffmann +RH-MergeRequest: 43: OvmfPkg/VirtNorFlashDxe backport +RH-Jira: RHEL-17587 +RH-Acked-by: Laszlo Ersek +RH-Commit: [11/20] c7b9cd1b716e1b8163b8094fbea8117241901815 + +Only accept gEfiAuthenticatedVariableGuid when checking the variable +store header in ValidateFvHeader(). + +The edk2 code base has been switched to use the authenticated varstore +format unconditionally (even in case secure boot is not used or +supported) a few years ago. + +Suggested-by: László Érsek +Signed-off-by: Gerd Hoffmann +Reviewed-by: Laszlo Ersek +Message-Id: <20240109112902.30002-3-kraxel@redhat.com> +(cherry picked from commit ae22b2f136bcbd27135a5f4dd76d3a68a172d00e) +--- + OvmfPkg/VirtNorFlashDxe/VirtNorFlashFvb.c | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +diff --git a/OvmfPkg/VirtNorFlashDxe/VirtNorFlashFvb.c b/OvmfPkg/VirtNorFlashDxe/VirtNorFlashFvb.c +index cc5eefaaf3..c503272a2b 100644 +--- a/OvmfPkg/VirtNorFlashDxe/VirtNorFlashFvb.c ++++ b/OvmfPkg/VirtNorFlashDxe/VirtNorFlashFvb.c +@@ -239,9 +239,7 @@ ValidateFvHeader ( + VariableStoreHeader = (VARIABLE_STORE_HEADER *)((UINTN)FwVolHeader + FwVolHeader->HeaderLength); + + // Check the Variable Store Guid +- if (!CompareGuid (&VariableStoreHeader->Signature, &gEfiVariableGuid) && +- !CompareGuid (&VariableStoreHeader->Signature, &gEfiAuthenticatedVariableGuid)) +- { ++ if (!CompareGuid (&VariableStoreHeader->Signature, &gEfiAuthenticatedVariableGuid)) { + DEBUG (( + DEBUG_INFO, + "%a: Variable Store Guid non-compatible\n", +-- +2.41.0 + diff --git a/edk2-OvmfPkg-VirtNorFlashDxe-use-EFI_MEMORY_WC-and-drop-A.patch b/edk2-OvmfPkg-VirtNorFlashDxe-use-EFI_MEMORY_WC-and-drop-A.patch new file mode 100644 index 0000000..a61f223 --- /dev/null +++ b/edk2-OvmfPkg-VirtNorFlashDxe-use-EFI_MEMORY_WC-and-drop-A.patch @@ -0,0 +1,150 @@ +From e65da48afdabc9a5cba1c212b4323898b91ef2a4 Mon Sep 17 00:00:00 2001 +From: Ard Biesheuvel +Date: Mon, 24 Oct 2022 18:16:18 +0200 +Subject: [PATCH 07/18] OvmfPkg/VirtNorFlashDxe: use EFI_MEMORY_WC and drop + AlignedCopyMem() + +RH-Author: Gerd Hoffmann +RH-MergeRequest: 43: OvmfPkg/VirtNorFlashDxe backport +RH-Jira: RHEL-17587 +RH-Acked-by: Laszlo Ersek +RH-Commit: [9/20] 0c01619eff8282d08e05fae8c37175b944449f59 + +NOR flash emulation under KVM involves switching between two modes, +where array mode is backed by a read-only memslot, and programming mode +is fully emulated, i.e., the memory region is not backed by anything, +and the faulting accesses are forwarded to the VMM by the hypervisor, +which translates them into NOR flash programming commands. + +Normally, we are limited to the use of device attributes when mapping +such regions, given that the programming mode has MMIO semantics. +However, when running under KVM, the chosen memory attributes only take +effect when in array mode, since no memory mapping exists otherwise. + +This means we can tune the memory mapping so it behaves a bit more like +a ROM, by switching to EFI_MEMORY_WC attributes. This means we no longer +need a special CopyMem() implementation that avoids unaligned accesses +at all cost. + +Signed-off-by: Ard Biesheuvel +Reviewed-by: Sunil V L +(cherry picked from commit 789a723285533f35652ebd6029976e2ddc955655) +--- + OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c | 65 +---------------------- + OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.c | 4 +- + 2 files changed, 4 insertions(+), 65 deletions(-) + +diff --git a/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c b/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c +index 0343131a54..1afd60ce66 100644 +--- a/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c ++++ b/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c +@@ -401,67 +401,6 @@ NorFlashWriteBlocks ( + return Status; + } + +-#define BOTH_ALIGNED(a, b, align) ((((UINTN)(a) | (UINTN)(b)) & ((align) - 1)) == 0) +- +-/** +- Copy Length bytes from Source to Destination, using aligned accesses only. +- Note that this implementation uses memcpy() semantics rather then memmove() +- semantics, i.e., SourceBuffer and DestinationBuffer should not overlap. +- +- @param DestinationBuffer The target of the copy request. +- @param SourceBuffer The place to copy from. +- @param Length The number of bytes to copy. +- +- @return Destination +- +-**/ +-STATIC +-VOID * +-AlignedCopyMem ( +- OUT VOID *DestinationBuffer, +- IN CONST VOID *SourceBuffer, +- IN UINTN Length +- ) +-{ +- UINT8 *Destination8; +- CONST UINT8 *Source8; +- UINT32 *Destination32; +- CONST UINT32 *Source32; +- UINT64 *Destination64; +- CONST UINT64 *Source64; +- +- if (BOTH_ALIGNED (DestinationBuffer, SourceBuffer, 8) && (Length >= 8)) { +- Destination64 = DestinationBuffer; +- Source64 = SourceBuffer; +- while (Length >= 8) { +- *Destination64++ = *Source64++; +- Length -= 8; +- } +- +- Destination8 = (UINT8 *)Destination64; +- Source8 = (CONST UINT8 *)Source64; +- } else if (BOTH_ALIGNED (DestinationBuffer, SourceBuffer, 4) && (Length >= 4)) { +- Destination32 = DestinationBuffer; +- Source32 = SourceBuffer; +- while (Length >= 4) { +- *Destination32++ = *Source32++; +- Length -= 4; +- } +- +- Destination8 = (UINT8 *)Destination32; +- Source8 = (CONST UINT8 *)Source32; +- } else { +- Destination8 = DestinationBuffer; +- Source8 = SourceBuffer; +- } +- +- while (Length-- != 0) { +- *Destination8++ = *Source8++; +- } +- +- return DestinationBuffer; +-} +- + EFI_STATUS + NorFlashReadBlocks ( + IN NOR_FLASH_INSTANCE *Instance, +@@ -516,7 +455,7 @@ NorFlashReadBlocks ( + SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY); + + // Readout the data +- AlignedCopyMem (Buffer, (VOID *)StartAddress, BufferSizeInBytes); ++ CopyMem (Buffer, (VOID *)StartAddress, BufferSizeInBytes); + + return EFI_SUCCESS; + } +@@ -558,7 +497,7 @@ NorFlashRead ( + SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY); + + // Readout the data +- AlignedCopyMem (Buffer, (VOID *)(StartAddress + Offset), BufferSizeInBytes); ++ CopyMem (Buffer, (VOID *)(StartAddress + Offset), BufferSizeInBytes); + + return EFI_SUCCESS; + } +diff --git a/OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.c b/OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.c +index f9a41f6aab..ff3121af2a 100644 +--- a/OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.c ++++ b/OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.c +@@ -394,14 +394,14 @@ NorFlashFvbInitialize ( + EfiGcdMemoryTypeMemoryMappedIo, + Instance->DeviceBaseAddress, + RuntimeMmioRegionSize, +- EFI_MEMORY_UC | EFI_MEMORY_RUNTIME ++ EFI_MEMORY_WC | EFI_MEMORY_RUNTIME + ); + ASSERT_EFI_ERROR (Status); + + Status = gDS->SetMemorySpaceAttributes ( + Instance->DeviceBaseAddress, + RuntimeMmioRegionSize, +- EFI_MEMORY_UC | EFI_MEMORY_RUNTIME ++ EFI_MEMORY_WC | EFI_MEMORY_RUNTIME + ); + ASSERT_EFI_ERROR (Status); + +-- +2.41.0 + diff --git a/edk2-OvmfPkg-clone-NorFlashPlatformLib-into-VirtNorFlashP.patch b/edk2-OvmfPkg-clone-NorFlashPlatformLib-into-VirtNorFlashP.patch new file mode 100644 index 0000000..178b1e7 --- /dev/null +++ b/edk2-OvmfPkg-clone-NorFlashPlatformLib-into-VirtNorFlashP.patch @@ -0,0 +1,80 @@ +From 59fb955aa77b75345f7828bf9f83764adf4bed46 Mon Sep 17 00:00:00 2001 +From: Ard Biesheuvel +Date: Mon, 24 Oct 2022 18:35:10 +0200 +Subject: [PATCH 18/18] OvmfPkg: clone NorFlashPlatformLib into + VirtNorFlashPlatformLib + +RH-Author: Gerd Hoffmann +RH-MergeRequest: 43: OvmfPkg/VirtNorFlashDxe backport +RH-Jira: RHEL-17587 +RH-Acked-by: Laszlo Ersek +RH-Commit: [20/20] 50ea104b99a997d7d08c1fdef617df1d930ffae6 + +Create a new library class in Ovmf that duplicates the existing +NorFlashPlatformLib, but which will be tied to the VirtNorFlashDxe +driver that will be introduced in a subsequent patch. This allows us to +retire the original from ArmPlatformPkg. + +Signed-off-by: Ard Biesheuvel +Reviewed-by: Sunil V L +(cherry picked from commit 16bf588b604a9f190accb71ada715b81756c94e2) +--- + .../Include/Library/VirtNorFlashPlatformLib.h | 30 +++++++++++++++++++ + OvmfPkg/OvmfPkg.dec | 4 +++ + 2 files changed, 34 insertions(+) + create mode 100644 OvmfPkg/Include/Library/VirtNorFlashPlatformLib.h + +diff --git a/OvmfPkg/Include/Library/VirtNorFlashPlatformLib.h b/OvmfPkg/Include/Library/VirtNorFlashPlatformLib.h +new file mode 100644 +index 0000000000..8f5b5e972d +--- /dev/null ++++ b/OvmfPkg/Include/Library/VirtNorFlashPlatformLib.h +@@ -0,0 +1,30 @@ ++/** @file ++ ++ Copyright (c) 2011-2012, ARM Ltd. All rights reserved.
++ ++ SPDX-License-Identifier: BSD-2-Clause-Patent ++ ++ **/ ++ ++#ifndef __VIRT_NOR_FLASH_PLATFORM_LIB__ ++#define __VIRT_NOR_FLASH_PLATFORM_LIB__ ++ ++typedef struct { ++ UINTN DeviceBaseAddress; // Start address of the Device Base Address (DBA) ++ UINTN RegionBaseAddress; // Start address of one single region ++ UINTN Size; ++ UINTN BlockSize; ++} VIRT_NOR_FLASH_DESCRIPTION; ++ ++EFI_STATUS ++VirtNorFlashPlatformInitialization ( ++ VOID ++ ); ++ ++EFI_STATUS ++VirtNorFlashPlatformGetDevices ( ++ OUT VIRT_NOR_FLASH_DESCRIPTION **NorFlashDescriptions, ++ OUT UINT32 *Count ++ ); ++ ++#endif /* __VIRT_NOR_FLASH_PLATFORM_LIB__ */ +diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec +index 340d83f794..e65ebd81c8 100644 +--- a/OvmfPkg/OvmfPkg.dec ++++ b/OvmfPkg/OvmfPkg.dec +@@ -97,6 +97,10 @@ + # transports. + VirtioMmioDeviceLib|Include/Library/VirtioMmioDeviceLib.h + ++ ## @libraryclass Provides a Nor flash interface. ++ # ++ VirtNorFlashPlatformLib|Include/Library/VirtNorFlashPlatformLib.h ++ + ## @libraryclass Invoke Xen hypercalls + # + XenHypercallLib|Include/Library/XenHypercallLib.h +-- +2.41.0 + diff --git a/edk2.spec b/edk2.spec index e8ff8b9..0aaa791 100644 --- a/edk2.spec +++ b/edk2.spec @@ -7,7 +7,7 @@ ExclusiveArch: x86_64 aarch64 Name: edk2 Version: %{GITDATE}git%{GITCOMMIT} -Release: 9%{?dist} +Release: 10%{?dist} Summary: UEFI firmware for 64-bit virtual machines Group: Applications/Emulators License: BSD-2-Clause-Patent and OpenSSL and MIT @@ -64,6 +64,42 @@ Patch30: edk2-UefiCpuPkg-MpInitLib-fix-apic-mode-for-cpu-hotplug.patch Patch31: edk2-OvmfPkg-VirtNorFlashDxe-stop-accepting-gEfiVariableG.patch # For RHEL-17587 - [rhel8] guest fails to boot due to ASSERT error Patch32: edk2-OvmfPkg-VirtNorFlashDxe-sanity-check-variables.patch +# For RHEL-17587 - [rhel8] guest fails to boot due to ASSERT error +Patch33: edk2-OvmfPkg-VirtNorFlashDxe-clone-ArmPlatformPkg-s-NOR-f.patch +# For RHEL-17587 - [rhel8] guest fails to boot due to ASSERT error +Patch34: edk2-OvmfPkg-VirtNorFlashDxe-remove-CheckBlockLocked-feat.patch +# For RHEL-17587 - [rhel8] guest fails to boot due to ASSERT error +Patch35: edk2-OvmfPkg-VirtNorFlashDxe-remove-disk-I-O-protocol-imp.patch +# For RHEL-17587 - [rhel8] guest fails to boot due to ASSERT error +Patch36: edk2-OvmfPkg-VirtNorFlashDxe-drop-block-I-O-protocol-impl.patch +# For RHEL-17587 - [rhel8] guest fails to boot due to ASSERT error +Patch37: edk2-OvmfPkg-VirtNorFlashDxe-avoid-array-mode-switch-afte.patch +# For RHEL-17587 - [rhel8] guest fails to boot due to ASSERT error +Patch38: edk2-OvmfPkg-VirtNorFlashDxe-avoid-switching-between-mode.patch +# For RHEL-17587 - [rhel8] guest fails to boot due to ASSERT error +Patch39: edk2-OvmfPkg-VirtNorFlashDxe-use-EFI_MEMORY_WC-and-drop-A.patch +# For RHEL-17587 - [rhel8] guest fails to boot due to ASSERT error +Patch40: edk2-OvmfPkg-VirtNorFlashDxe-map-flash-memory-as-uncachea.patch +# For RHEL-17587 - [rhel8] guest fails to boot due to ASSERT error +Patch41: edk2-OvmfPkg-VirtNorFlashDxe-stop-accepting-gEfiVariable2.patch +# For RHEL-17587 - [rhel8] guest fails to boot due to ASSERT error +Patch42: edk2-OvmfPkg-VirtNorFlashDxe-sanity-check-variable2.patch +# For RHEL-17587 - [rhel8] guest fails to boot due to ASSERT error +Patch43: edk2-OvmfPkg-VirtNorFlashDxe-add-casts-to-UINTN-and-UINT3.patch +# For RHEL-17587 - [rhel8] guest fails to boot due to ASSERT error +Patch44: edk2-OvmfPkg-VirtNorFlashDxe-clarify-block-write-logic-fi.patch +# For RHEL-17587 - [rhel8] guest fails to boot due to ASSERT error +Patch45: edk2-OvmfPkg-VirtNorFlashDxe-add-a-loop-for-NorFlashWrite.patch +# For RHEL-17587 - [rhel8] guest fails to boot due to ASSERT error +Patch46: edk2-OvmfPkg-VirtNorFlashDxe-allow-larger-writes-without-.patch +# For RHEL-17587 - [rhel8] guest fails to boot due to ASSERT error +Patch47: edk2-OvmfPkg-VirtNorFlashDxe-ValidateFvHeader-unwritten-s.patch +# For RHEL-17587 - [rhel8] guest fails to boot due to ASSERT error +Patch48: edk2-OvmfPkg-VirtNorFlashDxe-move-DoErase-code-block-into.patch +# For RHEL-17587 - [rhel8] guest fails to boot due to ASSERT error +Patch49: edk2-ArmVirtPkg-ArmVirtQemu-migrate-to-OVMF-s-VirtNorFlas.patch +# For RHEL-17587 - [rhel8] guest fails to boot due to ASSERT error +Patch50: edk2-OvmfPkg-clone-NorFlashPlatformLib-into-VirtNorFlashP.patch # python3-devel and libuuid-devel are required for building tools. @@ -508,6 +544,28 @@ true %endif %changelog +* Sat Feb 03 2024 Jon Maloy - 20220126gitbb1bba3d77-10 +- edk2-OvmfPkg-VirtNorFlashDxe-clone-ArmPlatformPkg-s-NOR-f.patch [RHEL-17587] +- edk2-OvmfPkg-VirtNorFlashDxe-remove-CheckBlockLocked-feat.patch [RHEL-17587] +- edk2-OvmfPkg-VirtNorFlashDxe-remove-disk-I-O-protocol-imp.patch [RHEL-17587] +- edk2-OvmfPkg-VirtNorFlashDxe-drop-block-I-O-protocol-impl.patch [RHEL-17587] +- edk2-OvmfPkg-VirtNorFlashDxe-avoid-array-mode-switch-afte.patch [RHEL-17587] +- edk2-OvmfPkg-VirtNorFlashDxe-avoid-switching-between-mode.patch [RHEL-17587] +- edk2-OvmfPkg-VirtNorFlashDxe-use-EFI_MEMORY_WC-and-drop-A.patch [RHEL-17587] +- edk2-OvmfPkg-VirtNorFlashDxe-map-flash-memory-as-uncachea.patch [RHEL-17587] +- edk2-OvmfPkg-VirtNorFlashDxe-stop-accepting-gEfiVariable2.patch [RHEL-17587] +- edk2-OvmfPkg-VirtNorFlashDxe-sanity-check-variable2.patch [RHEL-17587] +- edk2-OvmfPkg-VirtNorFlashDxe-add-casts-to-UINTN-and-UINT3.patch [RHEL-17587] +- edk2-OvmfPkg-VirtNorFlashDxe-clarify-block-write-logic-fi.patch [RHEL-17587] +- edk2-OvmfPkg-VirtNorFlashDxe-add-a-loop-for-NorFlashWrite.patch [RHEL-17587] +- edk2-OvmfPkg-VirtNorFlashDxe-allow-larger-writes-without-.patch [RHEL-17587] +- edk2-OvmfPkg-VirtNorFlashDxe-ValidateFvHeader-unwritten-s.patch [RHEL-17587] +- edk2-OvmfPkg-VirtNorFlashDxe-move-DoErase-code-block-into.patch [RHEL-17587] +- edk2-ArmVirtPkg-ArmVirtQemu-migrate-to-OVMF-s-VirtNorFlas.patch [RHEL-17587] +- edk2-OvmfPkg-clone-NorFlashPlatformLib-into-VirtNorFlashP.patch [RHEL-17587] +- Resolves: RHEL-17587 + ([rhel8] guest fails to boot due to ASSERT error) + * Wed Jan 24 2024 Jon Maloy - 20220126gitbb1bba3d77-9 - edk2-OvmfPkg-VirtNorFlashDxe-stop-accepting-gEfiVariableG.patch [RHEL-17587] - edk2-OvmfPkg-VirtNorFlashDxe-sanity-check-variables.patch [RHEL-17587]