* [edk2-devel] [PATCH 1/6] AmdPlatformPkg: Adds LogoDxe driver
2024-05-14 8:15 [edk2-devel] [PATCH 0/6] AmdPlatformPkg: Adds board independent modules Abdul Lateef Attar via groups.io
@ 2024-05-14 8:15 ` Abdul Lateef Attar via groups.io
2024-05-14 8:15 ` [edk2-devel] [PATCH 2/6] AmdPlatformPkg: Adds BaseAlwaysFalseDepexLib Library Abdul Lateef Attar via groups.io
` (4 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Abdul Lateef Attar via groups.io @ 2024-05-14 8:15 UTC (permalink / raw)
To: devel; +Cc: Abdul Lateef Attar, Abner Chang, Paul Grimes
Adds LogoDxe driver to display AMD logo.
Cc: Abner Chang <abner.chang@amd.com>
Cc: Paul Grimes <paul.grimes@amd.com>
Signed-off-by: Abdul Lateef Attar <AbdulLateef.Attar@amd.com>
---
.../AMD/AmdPlatformPkg/AmdPlatformPkg.dec | 10 +-
.../AMD/AmdPlatformPkg/AmdPlatformPkg.dsc | 31 ++-
.../Universal/LogoDxe/LogoDxe/JpegLogo.idf | 10 +
.../Universal/LogoDxe/LogoDxe/JpegLogoDxe.inf | 57 +++++
.../Universal/LogoDxe/LogoDxe/Logo.bmp | Bin 0 -> 522054 bytes
.../Universal/LogoDxe/LogoDxe/Logo.c | 194 ++++++++++++++++++
.../Universal/LogoDxe/LogoDxe/Logo.h | 23 +++
.../Universal/LogoDxe/LogoDxe/Logo.idf | 10 +
.../Universal/LogoDxe/LogoDxe/Logo.jpg | Bin 0 -> 75403 bytes
.../Universal/LogoDxe/LogoDxe/LogoDxe.inf | 58 ++++++
.../Universal/LogoDxe/LogoDxe/S3Logo.bmp | Bin 0 -> 964114 bytes
.../Universal/LogoDxe/LogoDxe/S3Logo.idf | 10 +
.../Universal/LogoDxe/LogoDxe/S3LogoDxe.inf | 57 +++++
13 files changed, 458 insertions(+), 2 deletions(-)
create mode 100644 Platform/AMD/AmdPlatformPkg/Universal/LogoDxe/LogoDxe/JpegLogo.idf
create mode 100644 Platform/AMD/AmdPlatformPkg/Universal/LogoDxe/LogoDxe/JpegLogoDxe.inf
create mode 100755 Platform/AMD/AmdPlatformPkg/Universal/LogoDxe/LogoDxe/Logo.bmp
create mode 100644 Platform/AMD/AmdPlatformPkg/Universal/LogoDxe/LogoDxe/Logo.c
create mode 100644 Platform/AMD/AmdPlatformPkg/Universal/LogoDxe/LogoDxe/Logo.h
create mode 100644 Platform/AMD/AmdPlatformPkg/Universal/LogoDxe/LogoDxe/Logo.idf
create mode 100644 Platform/AMD/AmdPlatformPkg/Universal/LogoDxe/LogoDxe/Logo.jpg
create mode 100644 Platform/AMD/AmdPlatformPkg/Universal/LogoDxe/LogoDxe/LogoDxe.inf
create mode 100644 Platform/AMD/AmdPlatformPkg/Universal/LogoDxe/LogoDxe/S3Logo.bmp
create mode 100644 Platform/AMD/AmdPlatformPkg/Universal/LogoDxe/LogoDxe/S3Logo.idf
create mode 100644 Platform/AMD/AmdPlatformPkg/Universal/LogoDxe/LogoDxe/S3LogoDxe.inf
diff --git a/Platform/AMD/AmdPlatformPkg/AmdPlatformPkg.dec b/Platform/AMD/AmdPlatformPkg/AmdPlatformPkg.dec
index 1fe7f94dc7..4d811d1135 100644
--- a/Platform/AMD/AmdPlatformPkg/AmdPlatformPkg.dec
+++ b/Platform/AMD/AmdPlatformPkg/AmdPlatformPkg.dec
@@ -3,7 +3,7 @@
# This is the package provides the AMD edk2 common platform drivers
# and libraries for AMD Server, Clinet and Gaming console platforms.
#
-# Copyright (C) 2023 Advanced Micro Devices, Inc. All rights reserved.<BR>
+# Copyright (C) 2023 - 2024 Advanced Micro Devices, Inc. All rights reserved.<BR>
# SPDX-License-Identifier: BSD-2-Clause-Patent
#
##
@@ -16,3 +16,11 @@
[Guids]
gAmdPlatformPkgTokenSpaceGuid = { 0x663DE733, 0x70E0, 0x4D37, { 0xBB, 0x30, 0x7D, 0x9E, 0xAF, 0x9B, 0xDA, 0xE9 }}
+
+[PcdsDynamic]
+ ## Event GUID to trigger logo displaying
+ # Default set to gMinPlatformPkgTokenSpaceGuid.gBdsEventAfterConsoleReadyBeforeBootOptionGuid
+ # {0x8eb3d5dc, 0xf4e7, 0x4b57, { 0xa9, 0xe7, 0x27, 0x39, 0x10, 0xf2, 0x18, 0x9f}}
+ # Platform DSC can set this value to another event GUID.
+ #
+ gAmdPlatformPkgTokenSpaceGuid.PcdAmdDisplayLogoEventGuid|{0xdc, 0xd5, 0xb3, 0x8e, 0xe7, 0xf4, 0x57, 0x4b, 0xa9, 0xe7, 0x27, 0x39, 0x10, 0xf2, 0x18, 0x9f}|VOID*|0x00010001
diff --git a/Platform/AMD/AmdPlatformPkg/AmdPlatformPkg.dsc b/Platform/AMD/AmdPlatformPkg/AmdPlatformPkg.dsc
index d3368c87ee..151235b791 100644
--- a/Platform/AMD/AmdPlatformPkg/AmdPlatformPkg.dsc
+++ b/Platform/AMD/AmdPlatformPkg/AmdPlatformPkg.dsc
@@ -3,7 +3,7 @@
# This is the package provides the AMD edk2 common platform drivers
# and libraries for AMD Server, Clinet and Gaming console platforms.
#
-# Copyright (C) 2023 Advanced Micro Devices, Inc. All rights reserved.<BR>
+# Copyright (C) 2023 - 2024 Advanced Micro Devices, Inc. All rights reserved.<BR>
# SPDX-License-Identifier: BSD-2-Clause-Patent
#
##
@@ -20,3 +20,32 @@
[Packages]
AmdPlatformPkg/AmdPlatformPkg.dec
+
+!include MdePkg/MdeLibs.dsc.inc
+
+[LibraryClasses.Common]
+ BaseLib|MdePkg/Library/BaseLib/BaseLib.inf
+ BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf
+ DebugPrintErrorLevelLib|MdePkg/Library/BaseDebugPrintErrorLevelLib/BaseDebugPrintErrorLevelLib.inf
+ DevicePathLib|MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.inf
+ PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf
+ PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf
+ SerialPortLib|MdePkg/Library/BaseSerialPortLibNull/BaseSerialPortLibNull.inf
+ UefiBootServicesTableLib|MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.inf
+ UefiDriverEntryPoint|MdePkg/Library/UefiDriverEntryPoint/UefiDriverEntryPoint.inf
+ UefiLib|MdePkg/Library/UefiLib/UefiLib.inf
+ UefiRuntimeServicesTableLib|MdePkg/Library/UefiRuntimeServicesTableLib/UefiRuntimeServicesTableLib.inf
+ !if $(TARGET) == RELEASE
+ DebugLib|MdePkg/Library/BaseDebugLibNull/BaseDebugLibNull.inf
+ !else
+ DebugLib|MdePkg/Library/BaseDebugLibSerialPort/BaseDebugLibSerialPort.inf
+ !endif
+
+[LibraryClasses.common.DXE_DRIVER]
+ BootLogoLib|MdeModulePkg/Library/BootLogoLib/BootLogoLib.inf
+ MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf
+
+[Components]
+ AmdPlatformPkg/Universal/LogoDxe/JpegLogoDxe.inf # Server platform JPEG logo driver
+ AmdPlatformPkg/Universal/LogoDxe/LogoDxe.inf # Server platfrom Bitmap logo driver
+ AmdPlatformPkg/Universal/LogoDxe/S3LogoDxe.inf
diff --git a/Platform/AMD/AmdPlatformPkg/Universal/LogoDxe/LogoDxe/JpegLogo.idf b/Platform/AMD/AmdPlatformPkg/Universal/LogoDxe/LogoDxe/JpegLogo.idf
new file mode 100644
index 0000000000..2d12b78c5c
--- /dev/null
+++ b/Platform/AMD/AmdPlatformPkg/Universal/LogoDxe/LogoDxe/JpegLogo.idf
@@ -0,0 +1,10 @@
+// /** @file
+// Platform Logo image definition file.
+//
+// Copyright (C) 2023 - 2024 Advanced Micro Devices, Inc. All rights reserved.
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#image IMG_LOGO Logo.jpg
diff --git a/Platform/AMD/AmdPlatformPkg/Universal/LogoDxe/LogoDxe/JpegLogoDxe.inf b/Platform/AMD/AmdPlatformPkg/Universal/LogoDxe/LogoDxe/JpegLogoDxe.inf
new file mode 100644
index 0000000000..6a3bbb8d5b
--- /dev/null
+++ b/Platform/AMD/AmdPlatformPkg/Universal/LogoDxe/LogoDxe/JpegLogoDxe.inf
@@ -0,0 +1,57 @@
+## @file
+# The default logo JPEG picture shown on setup screen.
+#
+# Copyright (C) 2023 - 2024 Advanced Micro Devices, Inc. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = JpegLogoDxe
+ FILE_GUID = 319CFE1D-8F15-4A7A-BF40-EECA953D87EF
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+
+ ENTRY_POINT = InitializeLogo
+#
+# This flag specifies whether HII resource section is generated into PE image.
+#
+ UEFI_HII_RESOURCE_SECTION = TRUE
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64
+#
+
+[Sources]
+ JpegLogo.idf
+ Logo.c
+ Logo.jpg
+
+[Packages]
+ AmdPlatformPkg/AmdPlatformPkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ MdePkg/MdePkg.dec
+
+[LibraryClasses]
+ BootLogoLib
+ DebugLib
+ PcdLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+
+[Protocols]
+ gEdkiiPlatformLogoProtocolGuid ## PRODUCES
+ gEfiHiiDatabaseProtocolGuid ## CONSUMES
+ gEfiHiiImageExProtocolGuid ## CONSUMES
+ gEfiHiiPackageListProtocolGuid ## PRODUCES CONSUMES
+
+[Pcd]
+ gAmdPlatformPkgTokenSpaceGuid.PcdAmdDisplayLogoEventGuid
+
+[Depex]
+ gEfiHiiDatabaseProtocolGuid AND
+ gEfiHiiImageExProtocolGuid
diff --git a/Platform/AMD/AmdPlatformPkg/Universal/LogoDxe/LogoDxe/Logo.bmp b/Platform/AMD/AmdPlatformPkg/Universal/LogoDxe/LogoDxe/Logo.bmp
new file mode 100755
index 0000000000000000000000000000000000000000..9bc467b3484c970064b92f55a220e4c50ccd5a13
GIT binary patch
literal 522054
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
YfdB$ShYl5==TS450CX@Q(+r&Yf5}9TWB>pF
literal 0
HcmV?d00001
diff --git a/Platform/AMD/AmdPlatformPkg/Universal/LogoDxe/LogoDxe/Logo.c b/Platform/AMD/AmdPlatformPkg/Universal/LogoDxe/LogoDxe/Logo.c
new file mode 100644
index 0000000000..69fa1dc0e5
--- /dev/null
+++ b/Platform/AMD/AmdPlatformPkg/Universal/LogoDxe/LogoDxe/Logo.c
@@ -0,0 +1,194 @@
+/** @file
+ Logo DXE Driver, install Edk2 Platform Logo protocol.
+
+ Copyright (c) 2016 - 2020, Intel Corporation. All rights reserved.<BR>
+ Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+#include <Library/BootLogoLib.h>
+#include <Library/DebugLib.h>
+#include <Library/PcdLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Protocol/GraphicsOutput.h>
+#include <Protocol/HiiDatabase.h>
+#include <Protocol/HiiImageEx.h>
+#include <Protocol/HiiPackageList.h>
+#include <Protocol/PlatformLogo.h>
+#include <Uefi.h>
+#include "Logo.h"
+
+EFI_HII_IMAGE_EX_PROTOCOL *mHiiImageEx;
+EFI_HII_HANDLE mHiiHandle;
+LOGO_ENTRY mLogos[] = {
+ {
+ IMAGE_TOKEN (IMG_LOGO),
+ EdkiiPlatformLogoDisplayAttributeCenter,
+ 0,
+ 0
+ }
+};
+
+/**
+ Load a platform logo image and return its data and attributes.
+
+ @param[in] This The pointer to this protocol instance.
+ @param[in, out] Instance The visible image instance is found.
+ @param[out] Image Points to the image.
+ @param[out] Attribute The display attributes of the image returned.
+ @param[out] OffsetX The X offset of the image regarding the Attribute.
+ @param[out] OffsetY The Y offset of the image regarding the Attribute.
+
+ @retval EFI_SUCCESS The image was fetched successfully.
+ @retval EFI_NOT_FOUND The specified image could not be found.
+ @retval EFI_INVALID_PARAMETER One of the given input parameters are incorrect
+**/
+EFI_STATUS
+EFIAPI
+GetImage (
+ IN EDKII_PLATFORM_LOGO_PROTOCOL *This,
+ IN OUT UINT32 *Instance,
+ OUT EFI_IMAGE_INPUT *Image,
+ OUT EDKII_PLATFORM_LOGO_DISPLAY_ATTRIBUTE *Attribute,
+ OUT INTN *OffsetX,
+ OUT INTN *OffsetY
+ )
+{
+ UINT32 Current;
+
+ if ((Instance == NULL) || (Image == NULL) ||
+ (Attribute == NULL) || (OffsetX == NULL) || (OffsetY == NULL))
+ {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Current = *Instance;
+ if (Current >= ARRAY_SIZE (mLogos)) {
+ return EFI_NOT_FOUND;
+ }
+
+ (*Instance)++; // Advance to next logo.
+ *Attribute = mLogos[Current].Attribute;
+ *OffsetX = mLogos[Current].OffsetX;
+ *OffsetY = mLogos[Current].OffsetY;
+ return mHiiImageEx->GetImageEx (mHiiImageEx, mHiiHandle, mLogos[Current].ImageId, Image);
+}
+
+EDKII_PLATFORM_LOGO_PROTOCOL mPlatformLogo = {
+ GetImage
+};
+
+// AMD_EDKII_OVERRIDE START
+
+/**
+ After console ready before boot option event callback
+
+ @param[in] Event The Event this notify function registered to.
+ @param[in] Context Pointer to the context data registered to the Event.
+**/
+VOID
+EFIAPI
+LogoDxeDisplayEventCallback (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ DEBUG ((DEBUG_INFO, "AMD logo is displaying.\n"));
+
+ BootLogoEnableLogo ();
+ gBS->CloseEvent (Event);
+}
+
+/**
+ Entrypoint of this module.
+
+ This function is the entrypoint of this module. It installs the Edkii
+ Platform Logo protocol.
+
+ @param ImageHandle The firmware allocated handle for the EFI image.
+ @param SystemTable A pointer to the EFI System Table.
+
+ @retval EFI_SUCCESS The entry point is executed successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+InitializeLogo (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+ EFI_HII_PACKAGE_LIST_HEADER *PackageList;
+ EFI_HII_DATABASE_PROTOCOL *HiiDatabase;
+ EFI_HANDLE Handle;
+ EFI_EVENT AfterConsoleReadyBeforeBootOptionEvent;
+
+ Status = gBS->LocateProtocol (
+ &gEfiHiiDatabaseProtocolGuid,
+ NULL,
+ (VOID **)&HiiDatabase
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ Status = gBS->LocateProtocol (
+ &gEfiHiiImageExProtocolGuid,
+ NULL,
+ (VOID **)&mHiiImageEx
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ //
+ // Retrieve HII package list from ImageHandle
+ //
+ Status = gBS->OpenProtocol (
+ ImageHandle,
+ &gEfiHiiPackageListProtocolGuid,
+ (VOID **)&PackageList,
+ ImageHandle,
+ NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "HII Image Package with logo not found in PE/COFF resource section\n"));
+ return Status;
+ }
+
+ //
+ // Publish HII package list to HII Database.
+ //
+ Status = HiiDatabase->NewPackageList (
+ HiiDatabase,
+ PackageList,
+ NULL,
+ &mHiiHandle
+ );
+ if (!EFI_ERROR (Status)) {
+ Handle = NULL;
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &Handle,
+ &gEdkiiPlatformLogoProtocolGuid,
+ &mPlatformLogo,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Install protocol failed.\n"));
+ return Status;
+ }
+ }
+
+ //
+ // Create AfterConsoleReadyBeforeBootOption event callback
+ //
+ Status = gBS->CreateEventEx (
+ EVT_NOTIFY_SIGNAL,
+ TPL_CALLBACK,
+ LogoDxeDisplayEventCallback,
+ NULL,
+ (EFI_GUID *)PcdGetPtr (PcdAmdDisplayLogoEventGuid),
+ &AfterConsoleReadyBeforeBootOptionEvent
+ );
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+}
diff --git a/Platform/AMD/AmdPlatformPkg/Universal/LogoDxe/LogoDxe/Logo.h b/Platform/AMD/AmdPlatformPkg/Universal/LogoDxe/LogoDxe/Logo.h
new file mode 100644
index 0000000000..7f72f5f57b
--- /dev/null
+++ b/Platform/AMD/AmdPlatformPkg/Universal/LogoDxe/LogoDxe/Logo.h
@@ -0,0 +1,23 @@
+/** @file
+ LogoDxe header file
+
+ Copyright (C) 2023 - 2024 Advanced Micro Devices, Inc. All rights reserved.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef AMD_LOGO_H_
+#define AMD_LOGO_H_
+
+///
+/// Logo display attributes structure
+///
+typedef struct {
+ EFI_IMAGE_ID ImageId; ///< Image ID
+ EDKII_PLATFORM_LOGO_DISPLAY_ATTRIBUTE Attribute; ///< Logo display location
+ INTN OffsetX; ///< Logo display X coordination
+ INTN OffsetY; ///< Logo display Y coordination
+} LOGO_ENTRY;
+
+#endif //AMD_LOGO_H_
diff --git a/Platform/AMD/AmdPlatformPkg/Universal/LogoDxe/LogoDxe/Logo.idf b/Platform/AMD/AmdPlatformPkg/Universal/LogoDxe/LogoDxe/Logo.idf
new file mode 100644
index 0000000000..3ef07a229f
--- /dev/null
+++ b/Platform/AMD/AmdPlatformPkg/Universal/LogoDxe/LogoDxe/Logo.idf
@@ -0,0 +1,10 @@
+// /** @file
+// Platform Logo image definition file.
+//
+// Copyright (C) 2023 - 2024 Advanced Micro Devices, Inc. All rights reserved.
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#image IMG_LOGO Logo.bmp
diff --git a/Platform/AMD/AmdPlatformPkg/Universal/LogoDxe/LogoDxe/Logo.jpg b/Platform/AMD/AmdPlatformPkg/Universal/LogoDxe/LogoDxe/Logo.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..10d40b400fcf57cdb348f92b10bdb1eb9c695e3c
GIT binary patch
literal 75403
zcmeEv2|Sc--}VUEo9xjtvZO32k%|mS$SqpzF_ASPl~A~f5M?bCWs<eTl(mq_UL@H=
z%%DiN8Os=E=JK7R`+nZG=Xsy!zMtRwzTfwLZdYcmduCkAdH(;$|2U5SY3cnE5wZH<
z0fPew78Vx73Gg3csTZMxSi!=={Qmumm7SIOwUUjEm7QZH2M2TG<l^Dt<mBe$;Na%t
z=H}rAUmRSkkbJzWn2(ts!hAdPUErISlY^7_ihu2E=_P`nlT~b`11pOpVg)}7D?iIp
z4FZKgu&{%pWghVFUo0zF+1OWtL*nKEFDP0K4xg2E1vprCHa76;0Pz12Z2as38@Fq(
z6g+IlA?YQgd?hxOQ)=JSO5r0dB<UUYXM?!7MMT$#iOXz~-MmFkMRljzuHAcd_Uj(d
z(>FMH^q8rcxrL?GNe4$KXBXE~=gxay@bUG#7<@G(H0)Y<MBL3=@d=5y?<74)OV7y6
z%6|ALzo4+Fxa8UM(yHp3+PeBzuNzw1+B-VC-goy53=Vz#G(7To^b2`<W_FG;PhD7K
zo)-&(_3LT<?aY2SFMe=dE5JQq<6xc_%L*T`vGTLAZ`{68K>IL<otL1b@)b^@eX*%e
zE4ie093ctYpKak5kyaUyAu~^H`ON;g6ASuNXZE)f`;YVLLGZG&fSbq4k3b_}=<|aJ
z#OD8HoBW(Q%s0G*2oCkeP&Y+KJcfno$I9DPahLaWbZ<W>Jn2!+;U%ANDaS#~PoeL9
zIpq}>Tj9IljS2GDM)A$8v~d#lx(a>+)!eA-yrQ9TXQ9!lR(S^6<jFI`WNVtAiN;Y~
zIf1wXpBoy!=%3JL3$_xbXD=Zt%L$}2Z*!IqY%OF{Y!yBtF)6f	>voY0JJBO<k8K
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
J4vPK%HvzN5X1M?W
literal 0
HcmV?d00001
diff --git a/Platform/AMD/AmdPlatformPkg/Universal/LogoDxe/LogoDxe/LogoDxe.inf b/Platform/AMD/AmdPlatformPkg/Universal/LogoDxe/LogoDxe/LogoDxe.inf
new file mode 100644
index 0000000000..ea07e15d1a
--- /dev/null
+++ b/Platform/AMD/AmdPlatformPkg/Universal/LogoDxe/LogoDxe/LogoDxe.inf
@@ -0,0 +1,58 @@
+## @file
+# The default logo bitmap picture shown on setup screen.
+#
+# Copyright (C) 2023 - 2024 Advanced Micro Devices, Inc. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = LogoDxe
+ FILE_GUID = 76ED6631-44FE-4ED2-8B5D-1B5355BB25E8
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+
+ ENTRY_POINT = InitializeLogo
+#
+# This flag specifies whether HII resource section is generated into PE image.
+#
+ UEFI_HII_RESOURCE_SECTION = TRUE
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64
+#
+
+[Sources]
+ Logo.bmp
+ Logo.c
+ Logo.h
+ Logo.idf
+
+[Packages]
+ AmdPlatformPkg/AmdPlatformPkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ MdePkg/MdePkg.dec
+
+[LibraryClasses]
+ BootLogoLib
+ DebugLib
+ PcdLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+
+[Protocols]
+ gEdkiiPlatformLogoProtocolGuid ## PRODUCES
+ gEfiHiiDatabaseProtocolGuid ## CONSUMES
+ gEfiHiiImageExProtocolGuid ## CONSUMES
+ gEfiHiiPackageListProtocolGuid ## PRODUCES CONSUMES
+
+[Pcd]
+ gAmdPlatformPkgTokenSpaceGuid.PcdAmdDisplayLogoEventGuid
+
+[Depex]
+ gEfiHiiDatabaseProtocolGuid AND
+ gEfiHiiImageExProtocolGuid
diff --git a/Platform/AMD/AmdPlatformPkg/Universal/LogoDxe/LogoDxe/S3Logo.bmp b/Platform/AMD/AmdPlatformPkg/Universal/LogoDxe/LogoDxe/S3Logo.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..137f4dcebe4ebd77d50f35ed3b31bf76c0f5f55d
GIT binary patch
literal 964114
zcmeI*zjE`um)LP-CmE~SVz0nu`pl&EZ0BMdw`zPTz7I7nJn7S<c9AM=+F#*I*h((8
z@E(#P2?7V_fFLD{^4IUnM<gW@;0M4V_(#;g{@cI&pMTypfB*Bp?sorOUjN5`+wK1U
zKkat^Ww&qV|KorE^X~u3N19>!_qN-~|GT%h?nVBU(cS0g=kD_#pSyqmfB$Q@dwi1N
zRR($E@m+>b8NPPAr=1LsGRVl&s|;^4yvy(@!`E*2x|88ih9?<h;`L32cNsoq_&TTi
zD6da4yvpz<!@CThGJNfJGP-+zl;KH+R~g=9c$eW*hA+wFPKHMro@98H;Z25j89rtB
z+U?~3-PfZGPcpp9@Fv5%44*Q5?Vfg$&E2C6&3~^lyvgt`!>0^i-KIT0%IlL1uQI&J
z@Girr3}4-5KRwFplMJsiyvgt`!>0^i9m(sXygteBD#M!$?=pPK@YQj>J<98o46ic0
z$?z`2rwm^m<@=+&KFRPZ!<!86GJMML)iHlQ%IlL1uQI&J@Girr3}0;#JbgXN>yr$x
zGQ7#~F2kn`U%NNi{<qzu49%9m%Fz7xF2kn`U%R)*oeYmMJjw7X!<!86GJMML)fL;*
zqr5)J@G8Tb4DT|0%J9_{>FcAsKFRPZ!<!86GJMML)fKlS^7i&5!>bH$GQ7+1DZ^J+
z72Y4^^+|?T8Qx@gm*G=}udbSWKFaHp46ic0$?z`2rwm_h6?*%6l-DO2US)Wb;a!GL
z8NPO(vi+aCM;V@Ec$MKzhIbj7|Gv7a_4p{SPcpp9@Fv5%44*Q5b=B_aQC^>9c$MKz
zhIbi0W%%l<<m;onKFRPZ!<!86GJMML)m7KGM|pjc;Z=q=8Qx|1l;NwZ%I}Zz`Xs}v
z3~w^L%kU|~S69tHALaE)hF2NhWO(2G`+xlGKIC(6vcBE!r~QBT^V5vUApbS9nt%91
z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{
z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL
zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~
z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**
z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0
z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{
z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL
zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~
z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**
z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0
z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{
z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL
zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~
z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**
z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0
z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{
z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL
zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~
z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**
z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0
z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{
z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL
zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~
z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**
z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0
z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{
z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL
zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~
z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**
z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0
z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{
z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL
zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~
z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**
z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0
z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{
z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL
zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~
z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**
z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0
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
z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL
zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~
z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**
z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0
z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{
z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL
zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~
z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**
z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0
z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{
z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL
zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~
z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**
z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0
z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{
z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL
zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~
z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**
z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0
z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{
z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL
zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~
z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**
z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0
z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{
z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL
zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~
z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILC>Quo
DNG4Z*
literal 0
HcmV?d00001
diff --git a/Platform/AMD/AmdPlatformPkg/Universal/LogoDxe/LogoDxe/S3Logo.idf b/Platform/AMD/AmdPlatformPkg/Universal/LogoDxe/LogoDxe/S3Logo.idf
new file mode 100644
index 0000000000..3735ad0b66
--- /dev/null
+++ b/Platform/AMD/AmdPlatformPkg/Universal/LogoDxe/LogoDxe/S3Logo.idf
@@ -0,0 +1,10 @@
+// /** @file
+// Platform Logo image definition file.
+//
+// Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#image IMG_LOGO S3Logo.bmp
diff --git a/Platform/AMD/AmdPlatformPkg/Universal/LogoDxe/LogoDxe/S3LogoDxe.inf b/Platform/AMD/AmdPlatformPkg/Universal/LogoDxe/LogoDxe/S3LogoDxe.inf
new file mode 100644
index 0000000000..be4c5ad59d
--- /dev/null
+++ b/Platform/AMD/AmdPlatformPkg/Universal/LogoDxe/LogoDxe/S3LogoDxe.inf
@@ -0,0 +1,57 @@
+## @file
+# The default logo bitmap picture shown on setup screen.
+#
+# Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = S3LogoDxe
+ FILE_GUID = A3193794-FCBC-E9A4-1AE0-DAEA9A499808
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+
+ ENTRY_POINT = InitializeLogo
+#
+# This flag specifies whether HII resource section is generated into PE image.
+#
+ UEFI_HII_RESOURCE_SECTION = TRUE
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64
+#
+
+[Sources]
+ Logo.c
+ S3Logo.bmp
+ S3Logo.idf
+
+[Packages]
+ AmdPlatformPkg/AmdPlatformPkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ MdePkg/MdePkg.dec
+
+[LibraryClasses]
+ BootLogoLib
+ DebugLib
+ PcdLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+
+[Protocols]
+ gEdkiiPlatformLogoProtocolGuid ## PRODUCES
+ gEfiHiiDatabaseProtocolGuid ## CONSUMES
+ gEfiHiiImageExProtocolGuid ## CONSUMES
+ gEfiHiiPackageListProtocolGuid ## PRODUCES CONSUMES
+
+[Pcd]
+ gAmdPlatformPkgTokenSpaceGuid.PcdAmdDisplayLogoEventGuid
+
+[Depex]
+ gEfiHiiDatabaseProtocolGuid AND
+ gEfiHiiImageExProtocolGuid
--
2.34.1
-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#118884): https://edk2.groups.io/g/devel/message/118884
Mute This Topic: https://groups.io/mt/106090922/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [edk2-devel] [PATCH 2/6] AmdPlatformPkg: Adds BaseAlwaysFalseDepexLib Library
2024-05-14 8:15 [edk2-devel] [PATCH 0/6] AmdPlatformPkg: Adds board independent modules Abdul Lateef Attar via groups.io
2024-05-14 8:15 ` [edk2-devel] [PATCH 1/6] AmdPlatformPkg: Adds LogoDxe driver Abdul Lateef Attar via groups.io
@ 2024-05-14 8:15 ` Abdul Lateef Attar via groups.io
2024-05-14 8:15 ` [edk2-devel] [PATCH 3/6] AmdPlatformPkg: Implements SerialPortLib for simulator Abdul Lateef Attar via groups.io
` (3 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Abdul Lateef Attar via groups.io @ 2024-05-14 8:15 UTC (permalink / raw)
To: devel; +Cc: Abdul Lateef Attar, Abner Chang, Paul Grimes
Adds BaseAlwaysFalseDepexLib Library which always
adds DEPEX to FALSE.
Using this library will prevent module/driver being dispatched.
Usage:
MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmm.inf {
<LibraryClasses>
NULL|AmdPlatformPkg/Library/BaseAlwaysFalseDepexLib/BaseAlwaysFalseDepexLib.inf
}
Cc: Abner Chang <abner.chang@amd.com>
Cc: Paul Grimes <paul.grimes@amd.com>
Signed-off-by: Abdul Lateef Attar <AbdulLateef.Attar@amd.com>
---
.../AMD/AmdPlatformPkg/AmdPlatformPkg.dsc | 2 ++
.../BaseAlwaysFalseDepexLib.c | 20 +++++++++++
.../BaseAlwaysFalseDepexLib.inf | 35 +++++++++++++++++++
.../BaseAlwaysFalseDepexLib.uni | 12 +++++++
4 files changed, 69 insertions(+)
create mode 100644 Platform/AMD/AmdPlatformPkg/Library/BaseAlwaysFalseDepexLib/BaseAlwaysFalseDepexLib.c
create mode 100644 Platform/AMD/AmdPlatformPkg/Library/BaseAlwaysFalseDepexLib/BaseAlwaysFalseDepexLib.inf
create mode 100644 Platform/AMD/AmdPlatformPkg/Library/BaseAlwaysFalseDepexLib/BaseAlwaysFalseDepexLib.uni
diff --git a/Platform/AMD/AmdPlatformPkg/AmdPlatformPkg.dsc b/Platform/AMD/AmdPlatformPkg/AmdPlatformPkg.dsc
index 151235b791..e39ad93c83 100644
--- a/Platform/AMD/AmdPlatformPkg/AmdPlatformPkg.dsc
+++ b/Platform/AMD/AmdPlatformPkg/AmdPlatformPkg.dsc
@@ -24,6 +24,7 @@
!include MdePkg/MdeLibs.dsc.inc
[LibraryClasses.Common]
+ AlwaysFalseDepexLib|AmdPlatformPkg/Library/BaseAlwaysFalseDepexLib/BaseAlwaysFalseDepexLib.inf
BaseLib|MdePkg/Library/BaseLib/BaseLib.inf
BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf
DebugPrintErrorLevelLib|MdePkg/Library/BaseDebugPrintErrorLevelLib/BaseDebugPrintErrorLevelLib.inf
@@ -46,6 +47,7 @@
MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf
[Components]
+ AmdPlatformPkg/Library/BaseAlwaysFalseDepexLib/BaseAlwaysFalseDepexLib.inf
AmdPlatformPkg/Universal/LogoDxe/JpegLogoDxe.inf # Server platform JPEG logo driver
AmdPlatformPkg/Universal/LogoDxe/LogoDxe.inf # Server platfrom Bitmap logo driver
AmdPlatformPkg/Universal/LogoDxe/S3LogoDxe.inf
diff --git a/Platform/AMD/AmdPlatformPkg/Library/BaseAlwaysFalseDepexLib/BaseAlwaysFalseDepexLib.c b/Platform/AMD/AmdPlatformPkg/Library/BaseAlwaysFalseDepexLib/BaseAlwaysFalseDepexLib.c
new file mode 100644
index 0000000000..e9f176223d
--- /dev/null
+++ b/Platform/AMD/AmdPlatformPkg/Library/BaseAlwaysFalseDepexLib/BaseAlwaysFalseDepexLib.c
@@ -0,0 +1,20 @@
+/** @file
+ No functionality of this file.
+
+ Copyright (C) 2023 - 2024 Advanced Micro Devices, Inc. All rights reserved.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Base.h>
+
+/**
+ Empty function to allow Library Class to be valid.
+**/
+VOID
+AlwaysFalsePlaceHolderFunction (
+ VOID
+ )
+{
+}
diff --git a/Platform/AMD/AmdPlatformPkg/Library/BaseAlwaysFalseDepexLib/BaseAlwaysFalseDepexLib.inf b/Platform/AMD/AmdPlatformPkg/Library/BaseAlwaysFalseDepexLib/BaseAlwaysFalseDepexLib.inf
new file mode 100644
index 0000000000..4e86256497
--- /dev/null
+++ b/Platform/AMD/AmdPlatformPkg/Library/BaseAlwaysFalseDepexLib/BaseAlwaysFalseDepexLib.inf
@@ -0,0 +1,35 @@
+## @file
+# This is the module used to consume the always false dependency.
+# Used to not dispatching the specific module included by using
+# external DSC/FDF include file.
+# For example: MinPlatformPkg/Include/Dsc/CoreCommonLib.dsc
+#
+# The module linked with the NULL class BaseAlwaysFalseDepexLib is still
+# put in the FV however it won't be executed.
+#
+# Copyright (C) 2023 - 2024 Advanced Micro Devices, Inc. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = BaseAlwaysFalseDepexLib
+ MODULE_UNI_FILE = BaseAlwaysFalseDepexLib.uni
+ FILE_GUID = 74DC464F-BC11-4E7D-8829-DD2F911988A8
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = AlwaysFalseDepexLib
+
+#
+# VALID_ARCHITECTURES = X64
+#
+
+[Sources]
+ BaseAlwaysFalseDepexLib.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+
+[Depex]
+ FALSE
diff --git a/Platform/AMD/AmdPlatformPkg/Library/BaseAlwaysFalseDepexLib/BaseAlwaysFalseDepexLib.uni b/Platform/AMD/AmdPlatformPkg/Library/BaseAlwaysFalseDepexLib/BaseAlwaysFalseDepexLib.uni
new file mode 100644
index 0000000000..5c7ccf1840
--- /dev/null
+++ b/Platform/AMD/AmdPlatformPkg/Library/BaseAlwaysFalseDepexLib/BaseAlwaysFalseDepexLib.uni
@@ -0,0 +1,12 @@
+## @file
+#
+# Copyright (C) 2023 - 2024 Advanced Micro Devices, Inc. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+#string STR_MODULE_ABSTRACT #language en-US "Library instance to provide FALSE Depex to prevent running of driver."
+
+#string STR_MODULE_DESCRIPTION #language en-US "Library instance to provide FALSE Depex to prevent running of driver."
+
--
2.34.1
-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#118883): https://edk2.groups.io/g/devel/message/118883
Mute This Topic: https://groups.io/mt/106090921/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [edk2-devel] [PATCH 3/6] AmdPlatformPkg: Implements SerialPortLib for simulator
2024-05-14 8:15 [edk2-devel] [PATCH 0/6] AmdPlatformPkg: Adds board independent modules Abdul Lateef Attar via groups.io
2024-05-14 8:15 ` [edk2-devel] [PATCH 1/6] AmdPlatformPkg: Adds LogoDxe driver Abdul Lateef Attar via groups.io
2024-05-14 8:15 ` [edk2-devel] [PATCH 2/6] AmdPlatformPkg: Adds BaseAlwaysFalseDepexLib Library Abdul Lateef Attar via groups.io
@ 2024-05-14 8:15 ` Abdul Lateef Attar via groups.io
2024-05-14 8:15 ` [edk2-devel] [PATCH 4/6] AmdPlatformPkg: Adds PlatformSocLib library class Abdul Lateef Attar via groups.io
` (2 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Abdul Lateef Attar via groups.io @ 2024-05-14 8:15 UTC (permalink / raw)
To: devel; +Cc: Abdul Lateef Attar, Abner Chang, Paul Grimes
Implements SerialPortLib library class for simulator.
It redirects the output to the port 80.
Cc: Abner Chang <abner.chang@amd.com>
Cc: Paul Grimes <paul.grimes@amd.com>
Signed-off-by: Abdul Lateef Attar <AbdulLateef.Attar@amd.com>
---
.../AMD/AmdPlatformPkg/AmdPlatformPkg.dsc | 3 +-
.../SimulatorSerialPortLibPort80.c | 208 ++++++++++++++++++
.../SimulatorSerialPortLibPort80.inf | 31 +++
3 files changed, 241 insertions(+), 1 deletion(-)
create mode 100644 Platform/AMD/AmdPlatformPkg/Library/SimulatorSerialPortLibPort80/SimulatorSerialPortLibPort80.c
create mode 100644 Platform/AMD/AmdPlatformPkg/Library/SimulatorSerialPortLibPort80/SimulatorSerialPortLibPort80.inf
diff --git a/Platform/AMD/AmdPlatformPkg/AmdPlatformPkg.dsc b/Platform/AMD/AmdPlatformPkg/AmdPlatformPkg.dsc
index e39ad93c83..2c959fb614 100644
--- a/Platform/AMD/AmdPlatformPkg/AmdPlatformPkg.dsc
+++ b/Platform/AMD/AmdPlatformPkg/AmdPlatformPkg.dsc
@@ -48,6 +48,7 @@
[Components]
AmdPlatformPkg/Library/BaseAlwaysFalseDepexLib/BaseAlwaysFalseDepexLib.inf
+ AmdPlatformPkg/Library/SimulatorSerialPortLibPort80/SimulatorSerialPortLibPort80.inf
AmdPlatformPkg/Universal/LogoDxe/JpegLogoDxe.inf # Server platform JPEG logo driver
AmdPlatformPkg/Universal/LogoDxe/LogoDxe.inf # Server platfrom Bitmap logo driver
- AmdPlatformPkg/Universal/LogoDxe/S3LogoDxe.inf
+ AmdPlatformPkg/Universal/LogoDxe/S3LogoDxe.inf
\ No newline at end of file
diff --git a/Platform/AMD/AmdPlatformPkg/Library/SimulatorSerialPortLibPort80/SimulatorSerialPortLibPort80.c b/Platform/AMD/AmdPlatformPkg/Library/SimulatorSerialPortLibPort80/SimulatorSerialPortLibPort80.c
new file mode 100644
index 0000000000..35842ecddc
--- /dev/null
+++ b/Platform/AMD/AmdPlatformPkg/Library/SimulatorSerialPortLibPort80/SimulatorSerialPortLibPort80.c
@@ -0,0 +1,208 @@
+/** @file
+ AMD simulator port80 serial port library functions.
+
+ Copyright (C) 2023 - 2024 Advanced Micro Devices, Inc. All rights reserved.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Base.h>
+#include <Library/BaseLib.h>
+#include <Library/IoLib.h>
+#include <Library/SerialPortLib.h>
+#include <Uefi.h>
+
+/**
+ Initialize the serial device hardware.
+
+ If no initialization is required, then return RETURN_SUCCESS.
+ If the serial device was successfully initialized, then return RETURN_SUCCESS.
+ If the serial device could not be initialized, then return RETURN_DEVICE_ERROR.
+
+ @retval RETURN_SUCCESS The serial device was initialized.
+ @retval RETURN_DEVICE_ERROR The serial device could not be initialized.
+
+**/
+RETURN_STATUS
+EFIAPI
+SerialPortInitialize (
+ VOID
+ )
+{
+ // Chipset defaults properly decoding port 80 to eSPI
+ return EFI_SUCCESS;
+}
+
+/**
+ Write data from buffer to serial device.
+
+ Writes NumberOfBytes data bytes from Buffer to the serial device.
+ The number of bytes actually written to the serial device is returned.
+ If the return value is less than NumberOfBytes, then the write operation failed.
+ If Buffer is NULL, then ASSERT().
+ If NumberOfBytes is zero, then return 0.
+
+ @param[in] Buffer The pointer to the data buffer to be written.
+ @param[in] NumberOfBytes The number of bytes to written to the serial device.
+
+ @retval 0 NumberOfBytes is 0.
+ @retval >0 The number of bytes written to the serial device.
+ If this value is less than NumberOfBytes, then the read operation failed.
+
+**/
+UINTN
+EFIAPI
+SerialPortWrite (
+ IN UINT8 *Buffer,
+ IN UINTN NumberOfBytes
+ )
+{
+ UINTN ByteCount;
+
+ if ((Buffer == NULL) || (NumberOfBytes == 0)) {
+ return 0;
+ }
+
+ IoWrite32 (0x80, SIGNATURE_32 ('R', 'T', 'S', '_'));
+
+ ByteCount = NumberOfBytes;
+ for ( ; ByteCount != 0; ByteCount--, Buffer++) {
+ IoWrite8 (0x80, *Buffer);
+ }
+
+ IoWrite32 (0x80, SIGNATURE_32 ('D', 'N', 'E', '_'));
+
+ return NumberOfBytes;
+}
+
+/**
+ Read data from serial device and save the data in buffer.
+
+ Reads NumberOfBytes data bytes from a serial device into the buffer
+ specified by Buffer. The number of bytes actually read is returned.
+ If the return value is less than NumberOfBytes, then the rest operation failed.
+ If Buffer is NULL, then ASSERT().
+ If NumberOfBytes is zero, then return 0.
+
+ @param[out] Buffer The pointer to the data buffer to store the data read from the serial device.
+ @param[in] NumberOfBytes The number of bytes which will be read.
+
+ @retval 0 Read data failed; No data is to be read.
+ @retval >0 The actual number of bytes read from serial device.
+
+**/
+UINTN
+EFIAPI
+SerialPortRead (
+ OUT UINT8 *Buffer,
+ IN UINTN NumberOfBytes
+ )
+{
+ return 0;
+}
+
+/**
+ Polls a serial device to see if there is any data waiting to be read.
+
+ Polls a serial device to see if there is any data waiting to be read.
+ If there is data waiting to be read from the serial device, then TRUE is returned.
+ If there is no data waiting to be read from the serial device, then FALSE is returned.
+
+ @retval TRUE Data is waiting to be read from the serial device.
+ @retval FALSE There is no data waiting to be read from the serial device.
+
+**/
+BOOLEAN
+EFIAPI
+SerialPortPoll (
+ VOID
+ )
+{
+ return FALSE;
+}
+
+/**
+ Sets the control bits on a serial device.
+
+ @param[in] Control Sets the bits of Control that are settable.
+
+ @retval RETURN_SUCCESS The new control bits were set on the serial device.
+ @retval RETURN_UNSUPPORTED The serial device does not support this operation.
+ @retval RETURN_DEVICE_ERROR The serial device is not functioning correctly.
+
+**/
+RETURN_STATUS
+EFIAPI
+SerialPortSetControl (
+ IN UINT32 Control
+ )
+{
+ return RETURN_UNSUPPORTED;
+}
+
+/**
+ Retrieve the status of the control bits on a serial device.
+
+ @param[out] Control A pointer to return the current control signals from the serial device.
+
+ @retval RETURN_SUCCESS The control bits were read from the serial device.
+ @retval RETURN_UNSUPPORTED The serial device does not support this operation.
+ @retval RETURN_DEVICE_ERROR The serial device is not functioning correctly.
+
+**/
+RETURN_STATUS
+EFIAPI
+SerialPortGetControl (
+ OUT UINT32 *Control
+ )
+{
+ return RETURN_UNSUPPORTED;
+}
+
+/**
+ Sets the baud rate, receive FIFO depth, transmit/receive time out, parity,
+ data bits, and stop bits on a serial device.
+
+ @param[in,out] BaudRate The requested baud rate. A BaudRate value of 0 will use the
+ device's default interface speed.
+ On output, the value actually set.
+ @param[in,out] ReceiveFifoDepth The requested depth of the FIFO on the receive side of the
+ serial interface. A ReceiveFifoDepth value of 0 will use
+ the device's default FIFO depth.
+ On output, the value actually set.
+ @param[in,out] Timeout The requested time out for a single character in microseconds.
+ This timeout applies to both the transmit and receive side of the
+ interface. A Timeout value of 0 will use the device's default time
+ out value.
+ On output, the value actually set.
+ @param[in,out] Parity The type of parity to use on this serial device. A Parity value of
+ DefaultParity will use the device's default parity value.
+ On output, the value actually set.
+ @param[in,out] DataBits The number of data bits to use on the serial device. A DataBits
+ value of 0 will use the device's default data bit setting.
+ On output, the value actually set.
+ @param[in,out] StopBits The number of stop bits to use on this serial device. A StopBits
+ value of DefaultStopBits will use the device's default number of
+ stop bits.
+ On output, the value actually set.
+
+ @retval RETURN_SUCCESS The new attributes were set on the serial device.
+ @retval RETURN_UNSUPPORTED The serial device does not support this operation.
+ @retval RETURN_INVALID_PARAMETER One or more of the attributes has an unsupported value.
+ @retval RETURN_DEVICE_ERROR The serial device is not functioning correctly.
+
+**/
+RETURN_STATUS
+EFIAPI
+SerialPortSetAttributes (
+ IN OUT UINT64 *BaudRate,
+ IN OUT UINT32 *ReceiveFifoDepth,
+ IN OUT UINT32 *Timeout,
+ IN OUT EFI_PARITY_TYPE *Parity,
+ IN OUT UINT8 *DataBits,
+ IN OUT EFI_STOP_BITS_TYPE *StopBits
+ )
+{
+ return RETURN_UNSUPPORTED;
+}
diff --git a/Platform/AMD/AmdPlatformPkg/Library/SimulatorSerialPortLibPort80/SimulatorSerialPortLibPort80.inf b/Platform/AMD/AmdPlatformPkg/Library/SimulatorSerialPortLibPort80/SimulatorSerialPortLibPort80.inf
new file mode 100644
index 0000000000..61e2d77f46
--- /dev/null
+++ b/Platform/AMD/AmdPlatformPkg/Library/SimulatorSerialPortLibPort80/SimulatorSerialPortLibPort80.inf
@@ -0,0 +1,31 @@
+## @file
+# Simlator port80 instance of serial port library functions.
+#
+# Copyright (C) 2023 - 2024 Advanced Micro Devices, Inc. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = SimulatorSerialPortLibPort80
+ FILE_GUID = 35217E20-489E-64F9-4E42-1EAFABE0A86F
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = SerialPortLib
+
+#
+# VALID_ARCHITECTURES = IA32 X64
+#
+
+[Sources]
+ SimulatorSerialPortLibPort80.c
+
+[Packages]
+ MdeModulePkg/MdeModulePkg.dec
+ MdePkg/MdePkg.dec
+
+[LibraryClasses]
+ BaseLib
+ IoLib
--
2.34.1
-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#118885): https://edk2.groups.io/g/devel/message/118885
Mute This Topic: https://groups.io/mt/106090923/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [edk2-devel] [PATCH 4/6] AmdPlatformPkg: Adds PlatformSocLib library class
2024-05-14 8:15 [edk2-devel] [PATCH 0/6] AmdPlatformPkg: Adds board independent modules Abdul Lateef Attar via groups.io
` (2 preceding siblings ...)
2024-05-14 8:15 ` [edk2-devel] [PATCH 3/6] AmdPlatformPkg: Implements SerialPortLib for simulator Abdul Lateef Attar via groups.io
@ 2024-05-14 8:15 ` Abdul Lateef Attar via groups.io
2024-05-14 8:15 ` [edk2-devel] [PATCH 5/6] AmdPlatformPkg: Adds AmdConfigRouting driver Abdul Lateef Attar via groups.io
2024-05-14 8:15 ` [edk2-devel] [PATCH 6/6] AmdPlatformPkg: Adds SecureBootDefaultKeysInit driver Abdul Lateef Attar via groups.io
5 siblings, 0 replies; 7+ messages in thread
From: Abdul Lateef Attar via groups.io @ 2024-05-14 8:15 UTC (permalink / raw)
To: devel; +Cc: Abdul Lateef Attar, Abner Chang, Paul Grimes
Adds PlatformSocLib library class.
Implements null instance of PlatformSocLib.
PlatformSocLib provides interface to the SoC
specific functionality.
Cc: Abner Chang <abner.chang@amd.com>
Cc: Paul Grimes <paul.grimes@amd.com>
Signed-off-by: Abdul Lateef Attar <AbdulLateef.Attar@amd.com>
---
.../AMD/AmdPlatformPkg/AmdPlatformPkg.dec | 7 +
.../AMD/AmdPlatformPkg/AmdPlatformPkg.dsc | 2 +
.../Include/Library/AmdPlatformSocLib.h | 134 ++++++++++++++++++
.../DxePlatformSocLib/DxePlatformSocLibNull.c | 75 ++++++++++
.../DxePlatformSocLibNull.inf | 26 ++++
.../DxePlatformSocLibNull.uni | 13 ++
6 files changed, 257 insertions(+)
create mode 100644 Platform/AMD/AmdPlatformPkg/Include/Library/AmdPlatformSocLib.h
create mode 100644 Platform/AMD/AmdPlatformPkg/Library/DxePlatformSocLib/DxePlatformSocLibNull.c
create mode 100644 Platform/AMD/AmdPlatformPkg/Library/DxePlatformSocLib/DxePlatformSocLibNull.inf
create mode 100644 Platform/AMD/AmdPlatformPkg/Library/DxePlatformSocLib/DxePlatformSocLibNull.uni
diff --git a/Platform/AMD/AmdPlatformPkg/AmdPlatformPkg.dec b/Platform/AMD/AmdPlatformPkg/AmdPlatformPkg.dec
index 4d811d1135..4cb66d2a36 100644
--- a/Platform/AMD/AmdPlatformPkg/AmdPlatformPkg.dec
+++ b/Platform/AMD/AmdPlatformPkg/AmdPlatformPkg.dec
@@ -14,6 +14,13 @@
PACKAGE_GUID = 2CB1238B-18E2-4837-B714-9DAB2B30A3C2
PACKAGE_VERSION = 1.0
+[Includes]
+ Include
+
+[LibraryClasses]
+ ## @libraryclass Defines a get/set interface for platform specific data.
+ PlatformSocLib|Include/Library/AmdPlatformSocLib.h
+
[Guids]
gAmdPlatformPkgTokenSpaceGuid = { 0x663DE733, 0x70E0, 0x4D37, { 0xBB, 0x30, 0x7D, 0x9E, 0xAF, 0x9B, 0xDA, 0xE9 }}
diff --git a/Platform/AMD/AmdPlatformPkg/AmdPlatformPkg.dsc b/Platform/AMD/AmdPlatformPkg/AmdPlatformPkg.dsc
index 2c959fb614..a717263c58 100644
--- a/Platform/AMD/AmdPlatformPkg/AmdPlatformPkg.dsc
+++ b/Platform/AMD/AmdPlatformPkg/AmdPlatformPkg.dsc
@@ -45,9 +45,11 @@
[LibraryClasses.common.DXE_DRIVER]
BootLogoLib|MdeModulePkg/Library/BootLogoLib/BootLogoLib.inf
MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf
+ PlatformSocLib|AmdPlatformPkg/Library/DxePlatformSocLib/DxePlatformSocLibNull.inf
[Components]
AmdPlatformPkg/Library/BaseAlwaysFalseDepexLib/BaseAlwaysFalseDepexLib.inf
+ AmdPlatformPkg/Library/DxePlatformSocLib/DxePlatformSocLibNull.inf
AmdPlatformPkg/Library/SimulatorSerialPortLibPort80/SimulatorSerialPortLibPort80.inf
AmdPlatformPkg/Universal/LogoDxe/JpegLogoDxe.inf # Server platform JPEG logo driver
AmdPlatformPkg/Universal/LogoDxe/LogoDxe.inf # Server platfrom Bitmap logo driver
diff --git a/Platform/AMD/AmdPlatformPkg/Include/Library/AmdPlatformSocLib.h b/Platform/AMD/AmdPlatformPkg/Include/Library/AmdPlatformSocLib.h
new file mode 100644
index 0000000000..f57e5d4989
--- /dev/null
+++ b/Platform/AMD/AmdPlatformPkg/Include/Library/AmdPlatformSocLib.h
@@ -0,0 +1,134 @@
+/** @file
+ AMD Platform SoC Library.
+ Provides interface to Get/Set platform specific data.
+
+ Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef AMD_PLATFORM_SOC_LIB_H_
+#define AMD_PLATFORM_SOC_LIB_H_
+
+#include <IndustryStandard/Acpi65.h>
+#include <IndustryStandard/SmBios.h>
+#include <Uefi/UefiBaseType.h>
+
+#define PCIE_MAX_FUNCTIONS 8
+#define PCIE_MAX_DEVICES 32
+#define PCIE_MAX_ROOTPORT (PCIE_MAX_DEVICES * PCIE_MAX_FUNCTIONS)
+
+typedef struct {
+ UINTN Index;
+ BOOLEAN Enabled;
+ UINT8 PortPresent;
+ UINTN Device;
+ UINTN Function;
+ UINTN SlotNum;
+ // Interrupts are relative to IOAPIC 0->n
+ UINTN BridgeInterrupt; // Redirection table entry for mapped bridge interrupt
+ UINTN EndpointInterruptArray[4]; // Redirection table entries for mapped INT A/B/C/D
+} AMD_PCI_ROOT_PORT_OBJECT;
+
+typedef struct {
+ UINTN Index;
+ UINT8 SocketId;
+ UINTN Segment;
+ UINTN BaseBusNumber;
+} AMD_PCI_ROOT_BRIDGE_OBJECT;
+
+/// Extended PCI address format
+typedef struct {
+ IN OUT UINT32 Register : 12; ///< Register offset
+ IN OUT UINT32 Function : 3; ///< Function number
+ IN OUT UINT32 Device : 5; ///< Device number
+ IN OUT UINT32 Bus : 8; ///< Bus number
+ IN OUT UINT32 Segment : 4; ///< Segment
+} AMD_EXT_PCI_ADDR;
+
+/// Union type for PCI address
+typedef union {
+ IN UINT32 AddressValue; ///< Formal address
+ IN AMD_EXT_PCI_ADDR Address; ///< Extended address
+} AMD_PCI_ADDR;
+
+/// Port Information Structure
+typedef struct {
+ AMD_PCI_ADDR EndPointBDF; ///< Bus/Device/Function of Root Port in PCI_ADDR format
+ BOOLEAN IsCxl2;
+} AMD_CXL_PORT_INFO;
+
+typedef struct {
+ EFI_HANDLE Handle;
+ UINTN Uid;
+ UINTN GlobalInterruptStart;
+ VOID *Configuration; // Never free this buffer
+ AMD_PCI_ROOT_BRIDGE_OBJECT *Object; // Never free this object
+ UINTN RootPortCount;
+ AMD_PCI_ROOT_PORT_OBJECT *RootPort[PCIE_MAX_ROOTPORT]; // Never free this object
+ UINTN CxlCount;
+ AMD_CXL_PORT_INFO CxlPortInfo;
+ UINTN PxmDomain; // Proximity domain
+} AMD_PCI_ROOT_BRIDGE_OBJECT_INSTANCE;
+
+/**
+ Get the platform specific IOAPIC information.
+
+ NOTE: Caller will need to free structure once finished.
+
+ @param IoApicInfo The IOAPIC information
+ @param IoApicCount Number of IOAPIC present
+
+ @retval EFI_SUCCESS Successfully retrieve the IOAPIC information.
+ EFI_INVALID_PARAMETERS Incorrect parameters provided.
+ EFI_UNSUPPORTED Platform do not support this function.
+ Other value Returns other EFI_STATUS in case of failure.
+
+**/
+EFI_STATUS
+EFIAPI
+GetIoApicInfo (
+ IN OUT EFI_ACPI_6_5_IO_APIC_STRUCTURE **IoApicInfo,
+ IN OUT UINT8 *IoApicCount
+ );
+
+/**
+ Get the platform PCIe configuration information.
+
+ NOTE: Caller will need to free structure once finished.
+
+ @param RootBridge The root bridge information
+ @param RootBridgeCount Number of root bridges present
+
+ @retval EFI_SUCCESS Successfully retrieve the root bridge information.
+ EFI_INVALID_PARAMETERS Incorrect parameters provided.
+ EFI_UNSUPPORTED Platform do not support this function.
+ Other value Returns other EFI_STATUS in case of failure.
+
+**/
+EFI_STATUS
+EFIAPI
+GetPcieInfo (
+ IN OUT AMD_PCI_ROOT_BRIDGE_OBJECT_INSTANCE **RootBridge,
+ IN OUT UINTN *RootBridgeCount
+ );
+
+/**
+ Get the platform specific System Slot information.
+
+ NOTE: Caller will need to free structure once finished.
+
+ @param[in, out] SystemSlotInfo The System Slot information
+ @param[in, out] SystemSlotCount Number of System Slot present
+
+ @retval EFI_UNSUPPORTED Platform do not support this function.
+**/
+EFI_STATUS
+EFIAPI
+GetSystemSlotInfo (
+ IN OUT SMBIOS_TABLE_TYPE9 **SystemSlotInfo,
+ IN OUT UINTN *SystemSlotCount
+ );
+
+#endif
diff --git a/Platform/AMD/AmdPlatformPkg/Library/DxePlatformSocLib/DxePlatformSocLibNull.c b/Platform/AMD/AmdPlatformPkg/Library/DxePlatformSocLib/DxePlatformSocLibNull.c
new file mode 100644
index 0000000000..142c3b66a7
--- /dev/null
+++ b/Platform/AMD/AmdPlatformPkg/Library/DxePlatformSocLib/DxePlatformSocLibNull.c
@@ -0,0 +1,75 @@
+/** @file
+ Implements AMD Platform SoC Library.
+ Provides interface to Get/Set platform specific data.
+
+ Copyright (C) 2023 - 2024 Advanced Micro Devices, Inc. All rights reserved.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+#include <Uefi/UefiBaseType.h>
+#include <IndustryStandard/Acpi65.h>
+#include <Library/AmdPlatformSocLib.h>
+#include <IndustryStandard/SmBios.h>
+
+/**
+ Get the platform specific IOAPIC information.
+
+ NOTE: Caller will need to free structure once finished.
+
+ @param IoApicInfo The IOAPIC information
+ @param IoApicCount Number of IOAPIC present
+
+ @retval EFI_UNSUPPORTED Platform do not support this function.
+
+**/
+EFI_STATUS
+EFIAPI
+GetIoApicInfo (
+ IN OUT EFI_ACPI_6_5_IO_APIC_STRUCTURE **IoApicInfo,
+ IN OUT UINT8 *IoApicCount
+ )
+{
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ Get the platform PCIe configuration information.
+
+ NOTE: Caller will need to free structure once finished.
+
+ @param RootBridge The root bridge information
+ @param RootBridgeCount Number of root bridges present
+
+ @retval EFI_UNSUPPORTED Platform do not support this function.
+
+**/
+EFI_STATUS
+EFIAPI
+GetPcieInfo (
+ IN OUT AMD_PCI_ROOT_BRIDGE_OBJECT_INSTANCE **RootBridge,
+ IN OUT UINTN *RootBridgeCount
+ )
+{
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ Get the platform specific System Slot information.
+
+ NOTE: Caller will need to free structure once finished.
+
+ @param[in, out] SystemSlotInfo The System Slot information
+ @param[in, out] SystemSlotCount Number of System Slot present
+
+ @retval EFI_UNSUPPORTED Platform do not support this function.
+**/
+EFI_STATUS
+EFIAPI
+GetSystemSlotInfo (
+ IN OUT SMBIOS_TABLE_TYPE9 **SystemSlotInfo,
+ IN OUT UINTN *SystemSlotCount
+ )
+{
+ return EFI_UNSUPPORTED;
+}
diff --git a/Platform/AMD/AmdPlatformPkg/Library/DxePlatformSocLib/DxePlatformSocLibNull.inf b/Platform/AMD/AmdPlatformPkg/Library/DxePlatformSocLib/DxePlatformSocLibNull.inf
new file mode 100644
index 0000000000..df8eb6b604
--- /dev/null
+++ b/Platform/AMD/AmdPlatformPkg/Library/DxePlatformSocLib/DxePlatformSocLibNull.inf
@@ -0,0 +1,26 @@
+## @file
+# INF file of AMD Platform SoC library
+#
+# Copyright (C) 2023 - 2024 Advanced Micro Devices, Inc. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+ INF_VERSION = 1.29
+ BASE_NAME = DxePlatformSocLibNull
+ MODULE_UNI_FILE = DxePlatformSocLibNull.uni
+ FILE_GUID = AFF6B33C-B084-4B35-BC1F-D077CDB3B464
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = PlatformSocLib
+
+[Sources]
+ DxePlatformSocLibNull.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+
+[LibraryClasses]
+ BaseLib
+
diff --git a/Platform/AMD/AmdPlatformPkg/Library/DxePlatformSocLib/DxePlatformSocLibNull.uni b/Platform/AMD/AmdPlatformPkg/Library/DxePlatformSocLib/DxePlatformSocLibNull.uni
new file mode 100644
index 0000000000..aa2ce2bc2f
--- /dev/null
+++ b/Platform/AMD/AmdPlatformPkg/Library/DxePlatformSocLib/DxePlatformSocLibNull.uni
@@ -0,0 +1,13 @@
+## @file
+# UNI file of AMD Platform SoC library
+#
+# Copyright (C) 2023 - 2024 Advanced Micro Devices, Inc. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+#string STR_MODULE_ABSTRACT #language en-US "AMD DXE Platform SoC null library instance."
+
+#string STR_MODULE_DESCRIPTION #language en-US "AMD DXE Platform SoC null library instance."
+
--
2.34.1
-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#118887): https://edk2.groups.io/g/devel/message/118887
Mute This Topic: https://groups.io/mt/106090925/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [edk2-devel] [PATCH 5/6] AmdPlatformPkg: Adds AmdConfigRouting driver
2024-05-14 8:15 [edk2-devel] [PATCH 0/6] AmdPlatformPkg: Adds board independent modules Abdul Lateef Attar via groups.io
` (3 preceding siblings ...)
2024-05-14 8:15 ` [edk2-devel] [PATCH 4/6] AmdPlatformPkg: Adds PlatformSocLib library class Abdul Lateef Attar via groups.io
@ 2024-05-14 8:15 ` Abdul Lateef Attar via groups.io
2024-05-14 8:15 ` [edk2-devel] [PATCH 6/6] AmdPlatformPkg: Adds SecureBootDefaultKeysInit driver Abdul Lateef Attar via groups.io
5 siblings, 0 replies; 7+ messages in thread
From: Abdul Lateef Attar via groups.io @ 2024-05-14 8:15 UTC (permalink / raw)
To: devel; +Cc: Abdul Lateef Attar, Abner Chang, Paul Grimes
Adds AmdConfigRouting driver to improve HII performance.
Cc: Abner Chang <abner.chang@amd.com>
Cc: Paul Grimes <paul.grimes@amd.com>
Signed-off-by: Abdul Lateef Attar <AbdulLateef.Attar@amd.com>
---
.../AMD/AmdPlatformPkg/AmdPlatformPkg.dsc | 1 +
.../HiiConfigRouting/AmdConfigRouting.inf | 45 +
.../HiiConfigRouting/AmdConfigRoutingEntry.c | 57 +
.../HiiConfigRouting/AmdHiiConfigRouting.c | 1101 +++++++++++++++++
.../HiiConfigRouting/AmdHiiConfigRouting.h | 189 +++
5 files changed, 1393 insertions(+)
create mode 100644 Platform/AMD/AmdPlatformPkg/Universal/HiiConfigRouting/AmdConfigRouting.inf
create mode 100644 Platform/AMD/AmdPlatformPkg/Universal/HiiConfigRouting/AmdConfigRoutingEntry.c
create mode 100644 Platform/AMD/AmdPlatformPkg/Universal/HiiConfigRouting/AmdHiiConfigRouting.c
create mode 100644 Platform/AMD/AmdPlatformPkg/Universal/HiiConfigRouting/AmdHiiConfigRouting.h
diff --git a/Platform/AMD/AmdPlatformPkg/AmdPlatformPkg.dsc b/Platform/AMD/AmdPlatformPkg/AmdPlatformPkg.dsc
index a717263c58..3d13c9e41d 100644
--- a/Platform/AMD/AmdPlatformPkg/AmdPlatformPkg.dsc
+++ b/Platform/AMD/AmdPlatformPkg/AmdPlatformPkg.dsc
@@ -51,6 +51,7 @@
AmdPlatformPkg/Library/BaseAlwaysFalseDepexLib/BaseAlwaysFalseDepexLib.inf
AmdPlatformPkg/Library/DxePlatformSocLib/DxePlatformSocLibNull.inf
AmdPlatformPkg/Library/SimulatorSerialPortLibPort80/SimulatorSerialPortLibPort80.inf
+ AmdPlatformPkg/Universal/HiiConfigRouting/AmdConfigRouting.inf
AmdPlatformPkg/Universal/LogoDxe/JpegLogoDxe.inf # Server platform JPEG logo driver
AmdPlatformPkg/Universal/LogoDxe/LogoDxe.inf # Server platfrom Bitmap logo driver
AmdPlatformPkg/Universal/LogoDxe/S3LogoDxe.inf
\ No newline at end of file
diff --git a/Platform/AMD/AmdPlatformPkg/Universal/HiiConfigRouting/AmdConfigRouting.inf b/Platform/AMD/AmdPlatformPkg/Universal/HiiConfigRouting/AmdConfigRouting.inf
new file mode 100644
index 0000000000..6cfb1dcceb
--- /dev/null
+++ b/Platform/AMD/AmdPlatformPkg/Universal/HiiConfigRouting/AmdConfigRouting.inf
@@ -0,0 +1,45 @@
+## @file
+# AMD HII Config routing driver INF file.
+# This module provides better performance of BlockToConfig and ConfigToBlock
+# functions.
+#
+# Copyright (C) 2021 - 2024 Advanced Micro Devices, Inc. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = AmdConfigRouting
+ FILE_GUID = 64302048-7006-49C4-AF0A-5ACE61257437
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ ENTRY_POINT = AmdConfigRoutingEntry
+
+[Sources]
+ AmdConfigRoutingEntry.c
+ AmdHiiConfigRouting.c
+ AmdHiiConfigRouting.h
+
+[Packages]
+ MdeModulePkg/MdeModulePkg.dec
+ MdePkg/MdePkg.dec
+
+[LibraryClasses]
+ BaseLib
+ BaseMemoryLib
+ DebugLib
+ MemoryAllocationLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+ UefiLib
+
+[Protocols]
+ gEfiHiiConfigRoutingProtocolGuid
+
+[Pcd]
+ gEfiMdePkgTokenSpaceGuid.PcdMaximumUnicodeStringLength
+
+[Depex]
+ gEfiHiiConfigRoutingProtocolGuid
diff --git a/Platform/AMD/AmdPlatformPkg/Universal/HiiConfigRouting/AmdConfigRoutingEntry.c b/Platform/AMD/AmdPlatformPkg/Universal/HiiConfigRouting/AmdConfigRoutingEntry.c
new file mode 100644
index 0000000000..29246ac1b2
--- /dev/null
+++ b/Platform/AMD/AmdPlatformPkg/Universal/HiiConfigRouting/AmdConfigRoutingEntry.c
@@ -0,0 +1,57 @@
+/** @file
+ AMD implementation of interface functions for EFI_HII_CONFIG_ROUTING_PROTOCOL.
+ This module overrides BlockToConfig and ConfigToBlock for the better performance.
+
+ Copyright (C) 2023 - 2024 Advanced Micro Devices, Inc. All rights reserved.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "AmdHiiConfigRouting.h"
+
+/**
+ Entry point for the AMD HII Config Routing driver.
+
+ @param[in] ImageHandle The image handle.
+ @param[in] SystemTable The system table.
+
+ @retval EFI_SUCCESS The entry point is executed successfully.
+ @retval Others Some error occurs when executing this entry point.
+**/
+EFI_STATUS
+EFIAPI
+AmdConfigRoutingEntry (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+ EFI_HII_CONFIG_ROUTING_PROTOCOL *HiiConfigRouting;
+
+ HiiConfigRouting = NULL;
+
+ Status = gBS->LocateProtocol (
+ &gEfiHiiConfigRoutingProtocolGuid,
+ NULL,
+ (VOID **)&HiiConfigRouting
+ );
+ if (!EFI_ERROR (Status)) {
+ ASSERT (HiiConfigRouting != NULL);
+ DEBUG ((
+ DEBUG_INFO,
+ "HiiConfigRouting->BlockToConfig: 0x%lX\n",
+ (UINTN)HiiBlockToConfig
+ ));
+ DEBUG ((
+ DEBUG_INFO,
+ "HiiConfigRouting->ConfigToBlock: 0x%lX\n",
+ (UINTN)HiiConfigToBlock
+ ));
+
+ HiiConfigRouting->BlockToConfig = HiiBlockToConfig;
+ HiiConfigRouting->ConfigToBlock = HiiConfigToBlock;
+ }
+
+ return Status;
+}
diff --git a/Platform/AMD/AmdPlatformPkg/Universal/HiiConfigRouting/AmdHiiConfigRouting.c b/Platform/AMD/AmdPlatformPkg/Universal/HiiConfigRouting/AmdHiiConfigRouting.c
new file mode 100644
index 0000000000..0a28d887e9
--- /dev/null
+++ b/Platform/AMD/AmdPlatformPkg/Universal/HiiConfigRouting/AmdHiiConfigRouting.c
@@ -0,0 +1,1101 @@
+/** @file
+ AMD implementation of interfaces function for EFI_HII_CONFIG_ROUTING_PROTOCOL.
+ This file provides better performance of BlockToConfig and ConfigToBlock
+ functions.
+
+ Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
+ Copyright (C) 2023 - 2024 Advanced Micro Devices, Inc. All rights reserved.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "AmdHiiConfigRouting.h"
+
+HII_ELEMENT gElementInfo[] = {
+ { L"GUID=", FIXED_STR_LEN (L"GUID=") },
+ { L"NAME=", FIXED_STR_LEN (L"NAME=") },
+ { L"PATH=", FIXED_STR_LEN (L"PATH=") },
+ { L"OFFSET=", FIXED_STR_LEN (L"OFFSET=") },
+ { L"WIDTH=", FIXED_STR_LEN (L"WIDTH=") },
+ { L"VALUE=", FIXED_STR_LEN (L"VALUE=") }
+};
+
+/**
+ Converts the unicode character of the string from uppercase to lowercase.
+ This is a internal function.
+
+ @param ConfigString String to be converted
+
+**/
+VOID
+EFIAPI
+HiiToLower (
+ IN EFI_STRING ConfigString
+ )
+{
+ EFI_STRING String;
+ BOOLEAN Lower;
+
+ ASSERT (ConfigString != NULL);
+
+ //
+ // Convert all hex digits in range [A-F] in the configuration header to [a-f]
+ //
+ for (String = ConfigString, Lower = FALSE; *String != L'\0'; String++) {
+ if (*String == L'=') {
+ Lower = TRUE;
+ } else if (*String == L'&') {
+ Lower = FALSE;
+ } else if (Lower && (*String >= L'A') && (*String <= L'F')) {
+ *String = (CHAR16)(*String - L'A' + L'a');
+ }
+ }
+
+ return;
+}
+
+//
+// Updated EDK2 functions to improve performance.
+//
+
+/**
+ Returns the length of a Null-terminated Unicode string.
+
+ This function returns the number of Unicode characters in the Null-terminated
+ Unicode string specified by String.
+
+ @param String A pointer to a Null-terminated Unicode string.
+
+ @retval The length of String.
+
+**/
+UINTN
+EFIAPI
+HiiStrLen (
+ IN EFI_STRING String
+ )
+{
+ UINTN Length;
+
+ ASSERT (String != NULL);
+
+ for (Length = 0; String[Length] != L'\0'; Length++) {
+ }
+
+ //
+ // If PcdMaximumUnicodeStringLength is not zero,
+ // length should not more than PcdMaximumUnicodeStringLength
+ //
+ if (PcdGet32 (PcdMaximumUnicodeStringLength) != 0) {
+ ASSERT (Length < PcdGet32 (PcdMaximumUnicodeStringLength));
+ }
+
+ return Length;
+}
+
+/**
+ Compares up to a specified length the contents of two Null-terminated Unicode
+ strings, and returns the difference between the first mismatched Unicode
+ characters.
+
+ This function compares the Null-terminated Unicode string FirstString to the
+ Null-terminated Unicode string SecondString. At most, Length Unicode
+ characters will be compared. If Length is 0, then 0 is returned. If
+ FirstString is identical to SecondString, then 0 is returned. Otherwise, the
+ value returned is the first mismatched Unicode character in SecondString
+ subtracted from the first mismatched Unicode character in FirstString.
+
+ @param[in] FirstString A pointer to a Null-terminated Unicode string.
+ @param[in] SecondString A pointer to a Null-terminated Unicode string.
+ @param[in] Length The maximum number of Unicode characters to compare.
+
+ @retval 0 FirstString is identical to SecondString.
+ @retval others FirstString is not identical to SecondString.
+
+**/
+INTN
+EFIAPI
+HiiStrnCmp (
+ IN EFI_STRING FirstString,
+ IN EFI_STRING SecondString,
+ IN UINTN Length
+ )
+{
+ if (Length == 0) {
+ return 0;
+ }
+
+ ASSERT (FirstString != NULL);
+ ASSERT (SecondString != NULL);
+ if (PcdGet32 (PcdMaximumUnicodeStringLength) != 0) {
+ ASSERT (Length <= PcdGet32 (PcdMaximumUnicodeStringLength));
+ }
+
+ while ((*FirstString != L'\0') &&
+ (*SecondString != L'\0') &&
+ (*FirstString == *SecondString) &&
+ (Length > 1))
+ {
+ FirstString++;
+ SecondString++;
+ Length--;
+ }
+
+ return *FirstString - *SecondString;
+}
+
+/**
+ Initializes HII_NUMBER instance to 0.
+
+ @param[in, out] This Pointer to HII_NUMBER instances.
+
+**/
+VOID
+HiiNumberInit (
+ IN OUT HII_NUMBER *This
+ )
+{
+ ASSERT (This != NULL);
+
+ This->NumberPtr = 0;
+ This->NumberPtrLength = 0;
+ This->Value = 0;
+ This->PrivateBufferSize = 0;
+}
+
+/**
+ Frees buffer in HII_NUMBER instance.
+
+ @param[in, out] This Pointer to HII_NUMBER instance.
+
+**/
+VOID
+HiiNumberFree (
+ IN OUT HII_NUMBER *This
+ )
+{
+ ASSERT (This != NULL);
+
+ if (This->NumberPtr != NULL) {
+ FreePool (This->NumberPtr);
+ This->NumberPtr = NULL;
+ This->PrivateBufferSize = 0;
+ }
+}
+
+/**
+ If buffer doesn't exist, allocate it. If the existing buffer is less than
+ requested, allocate a larger one.
+
+ @param[in, out] This Pointer to HII_NUMBER instance.
+ @param[in] Size Requested buffer size.
+
+ @retval EFI_SUCCESS Buffer allocated.
+ @retval EFI_OUT_OF_RESOURCES OUt of memory.
+
+**/
+EFI_STATUS
+HiiNumberSetMinBufferSize (
+ IN OUT HII_NUMBER *This,
+ IN UINTN Size
+ )
+{
+ ASSERT (This != NULL);
+
+ if (This->PrivateBufferSize < Size) {
+ Size += MAX_STRING_LENGTH;
+ This->NumberPtr = ReallocatePool (This->PrivateBufferSize, Size, This->NumberPtr);
+ if (This->NumberPtr == NULL) {
+ This->PrivateBufferSize = 0;
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ This->PrivateBufferSize = Size;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Get value of number from string and update HII_NUMBER instance.
+
+ @param[in, out] This Pointer to HII_NUMBER instance.
+ @param[in] String String to get value from. String may end in \0 or &.
+
+ @retval EFI_SUCCESS Buffer allocated.
+ @retval EFI_OUT_OF_RESOURCES OUt of memory.
+
+**/
+EFI_STATUS
+GetValueOfNumber (
+ IN OUT HII_NUMBER *This,
+ IN EFI_STRING String
+ )
+{
+ EFI_STATUS Status;
+ UINTN Index;
+ UINTN StringLength;
+ UINTN NumLen;
+ CHAR16 Digit;
+ UINT8 DigitUint8;
+ EFI_STRING EndOfString;
+
+ if ((This == NULL) || (String == NULL) || (*String == L'\0')) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ EndOfString = String;
+ StringLength = 0;
+
+ while (*EndOfString != L'\0' && *EndOfString != L'&') {
+ EndOfString++;
+ StringLength++;
+ }
+
+ This->StringLength = StringLength;
+
+ NumLen = (StringLength + 1) / 2;
+
+ Status = HiiNumberSetMinBufferSize (This, NumLen);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ for (Index = 0; Index < StringLength; Index++) {
+ Digit = String[StringLength - Index - 1];
+ if (Digit < L'0') {
+ DigitUint8 = 0;
+ } else if (Digit <= L'9') {
+ DigitUint8 = (UINT8)(Digit - L'0');
+ } else if (Digit < L'A') {
+ DigitUint8 = 0;
+ } else if (Digit <= L'F') {
+ DigitUint8 = (UINT8)(Digit - L'A' + 0xa);
+ } else if (Digit < L'a') {
+ DigitUint8 = 0;
+ } else if (Digit <= L'f') {
+ DigitUint8 = (UINT8)(Digit - L'a' + 0xa);
+ } else {
+ DigitUint8 = 0;
+ }
+
+ if ((Index & 1) == 0) {
+ This->NumberPtr[Index / 2] = DigitUint8;
+ } else {
+ This->NumberPtr[Index / 2] = (UINT8)((DigitUint8 << 4) + This->NumberPtr[Index / 2]);
+ }
+ }
+
+ This->NumberPtrLength = StringLength;
+ This->Value = 0;
+
+ if (StringLength <= sizeof (UINTN) * sizeof (CHAR16)) {
+ CopyMem (
+ &This->Value,
+ This->NumberPtr,
+ NumLen < sizeof (UINTN) ? NumLen : sizeof (UINTN)
+ );
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Initializes HII_STRING instance allocating buffer.
+
+ @param[in, out] This Pointer to HII_STRING instance.
+ @param[in] Size Size of initial allocation.
+
+ @retval EFI_SUCCESS Allocated buffer successfully.
+ @retval EFI_OUT_OF_RESOURCES Out of memory.
+**/
+EFI_STATUS
+HiiStringInit (
+ IN OUT HII_STRING *This,
+ IN UINTN Size
+ )
+{
+ ASSERT (This != NULL);
+
+ This->StringLength = 0;
+
+ if (Size == 0) {
+ This->String = NULL;
+ This->PrivateBufferSize = 0;
+ return EFI_SUCCESS;
+ }
+
+ This->String = (EFI_STRING)AllocatePool (Size);
+
+ if (This->String != NULL) {
+ This->String[0] = L'\0';
+ This->PrivateBufferSize = Size;
+ } else {
+ This->PrivateBufferSize = 0;
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Frees HiiString Buffer
+
+ @param[in, out] This Pointer to HII_STRING instance.
+
+**/
+VOID
+HiiStringFree (
+ IN OUT HII_STRING *This
+ )
+{
+ ASSERT (This != NULL);
+
+ if (This->String != NULL) {
+ FreePool (This->String);
+ This->String = NULL;
+ This->PrivateBufferSize = 0;
+ }
+}
+
+/**
+ If buffer doesn't exist, allocate it. If the existing buffer is less than
+ requested, allocate a larger one.
+
+ @param[in, out] This Pointer to HII_STRING instance.
+ @param[in] Size Requested buffer size.
+
+ @retval EFI_SUCCESS Buffer allocated.
+ @retval EFI_OUT_OF_RESOURCES OUt of memory.
+
+**/
+EFI_STATUS
+HiiStringSetMinBufferSize (
+ IN OUT HII_STRING *This,
+ IN UINTN Size
+ )
+{
+ UINTN ThisStringSize;
+ EFI_STRING NewAlloc;
+
+ ThisStringSize = (This->StringLength + 1) * sizeof (CHAR16);
+
+ if (Size > This->PrivateBufferSize) {
+ NewAlloc = (EFI_STRING)AllocatePool (Size);
+ if (NewAlloc == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ CopyMem (NewAlloc, This->String, ThisStringSize);
+ FreePool (This->String);
+ This->String = NewAlloc;
+ This->PrivateBufferSize = Size;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Append a string to the string in HII_STRING instance.
+
+ @param[in, out] This Pointer to HII_STRING instance.
+ @param[in] String String to append.
+
+ @retval EFI_SUCCESS String is appended.
+ @retval EFI_OUT_OF_RESOURCES OUt of memory.
+
+**/
+EFI_STATUS
+HiiStringAppend (
+ IN OUT HII_STRING *This,
+ IN EFI_STRING String
+ )
+{
+ EFI_STATUS Status;
+ UINTN ThisStringSize;
+ UINTN StringSize;
+ UINTN MaxLen;
+
+ if ((This == NULL) || (String == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ThisStringSize = (This->StringLength + 1) * sizeof (CHAR16);
+ StringSize = HII_STR_SIZE (String);
+
+ if (ThisStringSize + StringSize > This->PrivateBufferSize) {
+ MaxLen = (ThisStringSize + StringSize) * 2;
+ Status = HiiStringSetMinBufferSize (This, MaxLen);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ //
+ // Append the incoming string
+ //
+ CopyMem (&This->String[This->StringLength], String, StringSize);
+ This->StringLength += StringSize / sizeof (CHAR16) - 1;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Append a number to the string in HII_STRING instance.
+
+ @param[in, out] This Pointer to HII_STRING instance.
+ @param[in] Number Number to append.
+ @param[in] Length Length of Number.
+
+ @retval EFI_SUCCESS Number is appended.
+ @retval EFI_OUT_OF_RESOURCES OUt of memory.
+**/
+EFI_STATUS
+HiiStringAppendValue (
+ IN OUT HII_STRING *This,
+ IN UINT8 *Number,
+ IN UINTN Length
+ )
+{
+ EFI_STATUS Status;
+ UINTN ThisStringSize;
+ UINTN Index;
+ UINTN Index2;
+ UINT8 Nibble;
+ UINTN MaxLen;
+
+ CHAR16 *String;
+
+ if (Length == 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ThisStringSize = (This->StringLength + 1) * sizeof (CHAR16);
+
+ if (ThisStringSize + Length * 2 * sizeof (CHAR16) > This->PrivateBufferSize) {
+ MaxLen = (ThisStringSize + Length * 2 * sizeof (CHAR16)) * 2; // Double requested string length.
+ Status = HiiStringSetMinBufferSize (This, MaxLen);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ String = This->String + This->StringLength;
+ This->StringLength += Length * 2;
+
+ Index = Length;
+
+ do {
+ Index--;
+ Nibble = Number[Index] >> 4;
+
+ for (Index2 = 0; Index2 < 2; Index2++) {
+ if (Nibble < 0xa) {
+ *String = '0' + Nibble;
+ } else {
+ *String = 'a' + Nibble - 0xa;
+ }
+
+ Nibble = Number[Index] & 0xf;
+ String++;
+ }
+ } while (Index > 0);
+
+ *String = '\0';
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Find an element header in the input string, and return pointer it is value.
+
+ This is a internal function.
+
+ @param[in] Hdr Element Header to search for.
+ @param[in] String Search for element header in this string.
+
+ @retval Pointer to value in element header.
+ @retval NULL if element header not found or end of string.
+
+**/
+EFI_STRING
+FindElmentValue (
+ IN ELEMENT_HDR Hdr,
+ IN EFI_STRING String
+ )
+{
+ ASSERT (String != NULL);
+
+ if (HiiStrnCmp (String, gElementInfo[Hdr].ElementString, gElementInfo[Hdr].ElementLength) != 0) {
+ return NULL;
+ }
+
+ return String + gElementInfo[Hdr].ElementLength;
+}
+
+/**
+ Find pointer after value for element header in string.
+
+ This is a internal function.
+
+ @param[in] String String to search.
+
+ @retval Pointer after value in element header.
+
+**/
+EFI_STRING
+SkipElementValue (
+ IN EFI_STRING String
+ )
+{
+ ASSERT (String != NULL);
+
+ while (*String != 0 && *String != L'&') {
+ String++;
+ }
+
+ if (*String == L'&') {
+ String++; // Skip '&'
+ }
+
+ return String;
+}
+
+/**
+ Return pointer after ConfigHdr.
+
+ This is a internal function.
+
+ @param[in] String String to search.
+
+ @retval Pointer after ConfigHdr.
+ @retval NULL if Config header not formed correctly.
+
+**/
+EFI_STRING
+GetEndOfConfigHdr (
+ IN EFI_STRING String
+ )
+{
+ ASSERT (String != NULL);
+
+ String = FindElmentValue (ElementGuidHdr, String);
+ if (String == NULL) {
+ return NULL;
+ }
+
+ String = SkipElementValue (String);
+ if (*String == 0) {
+ return NULL;
+ }
+
+ while (*String != 0 &&
+ HiiStrnCmp (
+ String,
+ gElementInfo[ElementPathHdr].ElementString,
+ gElementInfo[ElementPathHdr].ElementLength
+ )
+ != 0)
+ {
+ String++;
+ }
+
+ if (*String != 0) {
+ String = String + gElementInfo[ElementPathHdr].ElementLength;
+ }
+
+ String = SkipElementValue (String);
+ return String;
+}
+
+/**
+ This helper function is to be called by drivers to map configuration data
+ stored in byte array ("block") formats such as UEFI Variables into current
+ configuration strings.
+
+ @param[in] This A pointer to the EFI_HII_CONFIG_ROUTING_PROTOCOL
+ instance.
+ @param[in] ConfigRequest A null-terminated Unicode string in
+ <ConfigRequest> format.
+ @param[in] Block Array of bytes defining the block's configuration.
+ @param[in] BlockSize Length in bytes of Block.
+ @param[out] Config Filled-in configuration string. String allocated
+ by the function. Returned only if call is
+ successful. It is <ConfigResp> string format.
+ @param[out] Progress A pointer to a string filled in with the offset of
+ the most recent & before the first failing
+ name/value pair (or the beginning of the string if
+ the failure is in the first name/value pair) or
+ the terminating NULL if all was successful.
+
+ @retval EFI_SUCCESS The request succeeded. Progress points to the null
+ terminator at the end of the ConfigRequest
+ string.
+ @retval EFI_OUT_OF_RESOURCES Not enough memory to allocate Config. Progress
+ points to the first character of ConfigRequest.
+ @retval EFI_INVALID_PARAMETER Passing in a NULL for the ConfigRequest or
+ Block parameter would result in this type of
+ error. Progress points to the first character of
+ ConfigRequest.
+ @retval EFI_DEVICE_ERROR Block not large enough. Progress undefined.
+ @retval EFI_INVALID_PARAMETER Encountered non <BlockName> formatted string.
+ Block is left updated and Progress points at
+ the "&" preceding the first non-<BlockName>.
+
+**/
+EFI_STATUS
+EFIAPI
+HiiBlockToConfig (
+ IN CONST EFI_HII_CONFIG_ROUTING_PROTOCOL *This,
+ IN CONST EFI_STRING ConfigRequest,
+ IN CONST UINT8 *Block,
+ IN CONST UINTN BlockSize,
+ OUT EFI_STRING *Config,
+ OUT EFI_STRING *Progress
+ )
+{
+ EFI_STATUS Status;
+ EFI_STRING StringPtr;
+ EFI_STRING OrigPtr;
+ CHAR16 CharBackup;
+ UINTN Offset;
+ UINTN Width;
+ UINT8 *Value;
+ HII_STRING HiiString;
+ HII_NUMBER HiiNumber;
+
+ if ((This == NULL) || (Progress == NULL) || (Config == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((Block == NULL) || (ConfigRequest == NULL)) {
+ *Progress = ConfigRequest;
+ return EFI_INVALID_PARAMETER;
+ }
+
+ StringPtr = ConfigRequest;
+
+ Status = HiiStringInit (&HiiString, MAX_STRING_LENGTH);
+ if (EFI_ERROR (Status)) {
+ *Progress = ConfigRequest;
+ return Status;
+ }
+
+ HiiNumberInit (&HiiNumber);
+
+ //
+ // Jump <ConfigHdr>
+ //
+ StringPtr = GetEndOfConfigHdr (StringPtr);
+ if (StringPtr == NULL) {
+ //
+ // Invalid header.
+ //
+ *Progress = ConfigRequest;
+ Status = EFI_INVALID_PARAMETER;
+ goto Exit;
+ }
+
+ if (*StringPtr == L'\0') {
+ *Progress = StringPtr;
+ HiiStringAppend (&HiiString, ConfigRequest);
+ HiiToLower (HiiString.String);
+
+ //
+ // Do not free HiiString.String with HiiStringFree;
+ //
+ *Config = HiiString.String;
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Copy <ConfigHdr> and an additional '&' to <ConfigResp>
+ //
+ CharBackup = StringPtr[0];
+ StringPtr[0] = L'\0'; // Temporarily change & to L'\0'
+ Status = HiiStringAppend (&HiiString, ConfigRequest);
+ if (EFI_ERROR (Status)) {
+ *Progress = ConfigRequest;
+ goto Exit;
+ }
+
+ StringPtr[0] = CharBackup;
+
+ //
+ // Parse each <RequestElement> if exists
+ // Only <BlockName> format is supported by this help function.
+ // <BlockName> ::= 'OFFSET='<Number>&'WIDTH='<Number>
+ //
+
+ //
+ // while search for "OFFSET="
+ // When "OFFSET=" is found, OrigPtr starts at "OFFSET=", and StringPtr points to value.
+ //
+ while (*StringPtr != 0 &&
+ (OrigPtr = StringPtr, (StringPtr = FindElmentValue (ElementOffsetHdr, StringPtr)) != NULL)
+ )
+ {
+ //
+ // Get Offset
+ //
+ Status = GetValueOfNumber (&HiiNumber, StringPtr);
+ if (EFI_ERROR (Status)) {
+ if (Status == EFI_OUT_OF_RESOURCES) {
+ *Progress = ConfigRequest; // Out of memory
+ } else {
+ *Progress = OrigPtr - 1;
+ }
+
+ goto Exit;
+ }
+
+ Offset = HiiNumber.Value;
+ StringPtr += HiiNumber.StringLength + 1;
+
+ //
+ // Get Width
+ //
+ StringPtr = FindElmentValue (ElementWidthHdr, StringPtr);
+ if (StringPtr == NULL) {
+ *Progress = OrigPtr - 1;
+ Status = EFI_INVALID_PARAMETER;
+ goto Exit;
+ }
+
+ Status = GetValueOfNumber (&HiiNumber, StringPtr);
+ if (EFI_ERROR (Status)) {
+ if (Status == EFI_OUT_OF_RESOURCES) {
+ *Progress = ConfigRequest; // Out of memory
+ } else {
+ *Progress = OrigPtr - 1;
+ }
+
+ goto Exit;
+ }
+
+ Width = HiiNumber.Value;
+ StringPtr += HiiNumber.StringLength;
+
+ if ((*StringPtr != 0) && (*StringPtr != L'&')) {
+ *Progress = OrigPtr - 1;
+ Status = EFI_INVALID_PARAMETER;
+ goto Exit;
+ }
+
+ //
+ // Calculate Value and convert it to hex string.
+ //
+ if (Offset + Width > BlockSize) {
+ *Progress = StringPtr;
+ Status = EFI_DEVICE_ERROR;
+ goto Exit;
+ }
+
+ Value = (UINT8 *)Block + Offset;
+
+ CharBackup = *StringPtr;
+ *StringPtr = L'\0';
+
+ Status = HiiStringAppend (&HiiString, OrigPtr);
+ if (EFI_ERROR (Status)) {
+ *Progress = ConfigRequest; // Out of memory
+ goto Exit;
+ }
+
+ *StringPtr = CharBackup; // End of section of string OrigPtr
+
+ Status = HiiStringAppend (&HiiString, L"&VALUE=");
+ if (EFI_ERROR (Status)) {
+ *Progress = ConfigRequest; // Out of memory
+ goto Exit;
+ }
+
+ Status = HiiStringAppendValue (&HiiString, Value, Width);
+ if (EFI_ERROR (Status)) {
+ if (Status == EFI_OUT_OF_RESOURCES) {
+ *Progress = ConfigRequest; // Out of memory
+ } else {
+ *Progress = OrigPtr - 1;
+ }
+
+ goto Exit;
+ }
+
+ //
+ // If L'\0', parsing is finished. Otherwise skip L'&' to continue
+ //
+ if (*StringPtr == L'\0') {
+ break;
+ }
+
+ Status = HiiStringAppend (&HiiString, L"&");
+ if (EFI_ERROR (Status)) {
+ *Progress = ConfigRequest; // Out of memory
+ goto Exit;
+ }
+
+ StringPtr++; // Skip L'&'
+ }
+
+ if (*StringPtr != L'\0') {
+ *Progress = StringPtr - 1;
+ Status = EFI_INVALID_PARAMETER;
+ goto Exit;
+ }
+
+ HiiToLower (HiiString.String);
+ *Progress = StringPtr;
+
+ HiiNumberFree (&HiiNumber);
+
+ //
+ // Do not free HiiString.String with HiiStringFree. The caller will
+ // consume it when EFI_SUCCESS.
+ //
+ *Config = HiiString.String;
+
+ return EFI_SUCCESS;
+
+Exit:
+ HiiStringFree (&HiiString);
+ HiiNumberFree (&HiiNumber);
+
+ *Config = NULL;
+
+ return Status;
+}
+
+/**
+ This helper function is to be called by drivers to map configuration strings
+ to configurations stored in byte array ("block") formats such as UEFI Variables.
+
+ @param[in] This A pointer to the EFI_HII_CONFIG_ROUTING_PROTOCOL
+ instance.
+ @param[in] ConfigResp A null-terminated Unicode string in <ConfigResp>
+ format.
+ @param[in, out] Block A possibly null array of bytes representing the
+ current block. Only bytes referenced in the
+ ConfigResp string in the block are modified. If
+ this parameter is null or if the *BlockSize
+ parameter is (on input) shorter than required by
+ the Configuration string, only the BlockSize
+ parameter is updated and an appropriate status
+ (see below) is returned.
+ @param[in, out] BlockSize The length of the Block in units of UINT8. On
+ input, this is the size of the Block. On output,
+ if successful, contains the largest index of the
+ modified byte in the Block, or the required buffer
+ size if the Block is not large enough.
+ @param[out] Progress On return, points to an element of the ConfigResp
+ string filled in with the offset of the most
+ recent '&' before the first failing name / value
+ pair (or the beginning of the string if the
+ failure is in the first name / value pair) or the
+ terminating NULL if all was successful.
+
+ @retval EFI_SUCCESS The request succeeded. Progress points to the null
+ terminator at the end of the ConfigResp string.
+ @retval EFI_OUT_OF_RESOURCES Not enough memory to allocate Config. Progress
+ points to the first character of ConfigResp.
+ @retval EFI_INVALID_PARAMETER Passing in a NULL for the ConfigResp or
+ Block parameter would result in this type of
+ error. Progress points to the first character of
+ ConfigResp.
+ @retval EFI_INVALID_PARAMETER Encountered non <BlockName> formatted name /
+ value pair. Block is left updated and
+ Progress points at the '&' preceding the first
+ non-<BlockName>.
+ @retval EFI_BUFFER_TOO_SMALL Block not large enough. Progress undefined.
+ BlockSize is updated with the required buffer size.
+ @retval EFI_NOT_FOUND Target for the specified routing data was not found.
+ Progress points to the "G" in "GUID" of the errant
+ routing data.
+
+**/
+EFI_STATUS
+EFIAPI
+HiiConfigToBlock (
+ IN CONST EFI_HII_CONFIG_ROUTING_PROTOCOL *This,
+ IN CONST EFI_STRING ConfigResp,
+ IN OUT UINT8 *Block,
+ IN OUT UINTN *BlockSize,
+ OUT EFI_STRING *Progress
+ )
+{
+ EFI_STATUS Status;
+ EFI_STRING StringPtr;
+ EFI_STRING OrigPtr;
+ UINTN Offset;
+ UINTN Width;
+ UINTN BufferSize;
+ UINTN MaxBlockSize;
+ HII_NUMBER HiiNumber;
+
+ if ((This == NULL) || (BlockSize == NULL) || (Progress == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *Progress = ConfigResp;
+ if (ConfigResp == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ StringPtr = ConfigResp;
+ BufferSize = *BlockSize;
+ MaxBlockSize = 0;
+
+ HiiNumberInit (&HiiNumber);
+ //
+ // Jump <ConfigHdr>
+ //
+ StringPtr = GetEndOfConfigHdr (StringPtr);
+ if (StringPtr == NULL) {
+ //
+ // Invalid header.
+ //
+ *Progress = ConfigResp;
+ Status = EFI_INVALID_PARAMETER;
+ goto Exit;
+ }
+
+ if (*StringPtr == L'\0') {
+ *Progress = StringPtr;
+ Status = EFI_INVALID_PARAMETER;
+ goto Exit;
+ }
+
+ //
+ // Parse each <ConfigElement> if exists
+ // Only '&'<BlockConfig> format is supported by this help function.
+ // <BlockConfig> ::= 'OFFSET='<Number>&'WIDTH='<Number>&'VALUE='<Number>
+ //
+ while (*StringPtr != L'\0' &&
+ (OrigPtr = StringPtr, (StringPtr = FindElmentValue (ElementOffsetHdr, StringPtr)) != NULL)
+ )
+ {
+ //
+ // Get Offset
+ //
+ Status = GetValueOfNumber (&HiiNumber, StringPtr);
+ if (EFI_ERROR (Status)) {
+ if (Status == EFI_OUT_OF_RESOURCES) {
+ *Progress = ConfigResp; // Out of memory
+ } else {
+ *Progress = OrigPtr - 1;
+ }
+
+ goto Exit;
+ }
+
+ Offset = HiiNumber.Value;
+ StringPtr += HiiNumber.StringLength + 1;
+
+ //
+ // Get Width
+ //
+ StringPtr = FindElmentValue (ElementWidthHdr, StringPtr);
+ if (StringPtr == NULL) {
+ *Progress = OrigPtr - 1;
+ Status = EFI_INVALID_PARAMETER;
+ goto Exit;
+ }
+
+ Status = GetValueOfNumber (&HiiNumber, StringPtr);
+ if (EFI_ERROR (Status)) {
+ if (Status == EFI_OUT_OF_RESOURCES) {
+ *Progress = ConfigResp; // Out of memory
+ } else {
+ *Progress = OrigPtr - 1;
+ }
+
+ goto Exit;
+ }
+
+ Width = HiiNumber.Value;
+ StringPtr += HiiNumber.StringLength + 1;
+
+ //
+ // Get Value
+ //
+ StringPtr = FindElmentValue (ElementValueHdr, StringPtr);
+ if (StringPtr == NULL) {
+ *Progress = OrigPtr - 1;
+ Status = EFI_INVALID_PARAMETER;
+ goto Exit;
+ }
+
+ Status = GetValueOfNumber (&HiiNumber, StringPtr);
+ if (EFI_ERROR (Status)) {
+ if (Status == EFI_OUT_OF_RESOURCES) {
+ *Progress = ConfigResp; // Out of memory
+ } else {
+ *Progress = OrigPtr - 1;
+ }
+
+ goto Exit;
+ }
+
+ //
+ // Update the Block with configuration info
+ //
+ if ((Block != NULL) && (Offset + Width <= BufferSize)) {
+ CopyMem (Block + Offset, HiiNumber.NumberPtr, Width);
+ }
+
+ if (Offset + Width > MaxBlockSize) {
+ MaxBlockSize = Offset + Width;
+ }
+
+ StringPtr += HiiNumber.StringLength;
+
+ if ((*StringPtr != L'\0') && (*StringPtr != L'&')) {
+ *Progress = OrigPtr - 1;
+ Status = EFI_INVALID_PARAMETER;
+ goto Exit;
+ }
+
+ //
+ // If L'\0', parsing is finished.
+ //
+ if (*StringPtr == L'\0') {
+ break;
+ }
+
+ StringPtr++; // Skip L'&'
+ }
+
+ //
+ // The input string is not ConfigResp format, return error.
+ //
+ if (*StringPtr != L'\0') {
+ *Progress = StringPtr;
+ Status = EFI_INVALID_PARAMETER;
+ goto Exit;
+ }
+
+ *Progress = StringPtr + HiiStrLen (StringPtr);
+ *BlockSize = MaxBlockSize - 1;
+
+ if (MaxBlockSize > BufferSize) {
+ *BlockSize = MaxBlockSize;
+ if (Block != NULL) {
+ Status = EFI_BUFFER_TOO_SMALL;
+ goto Exit;
+ }
+ }
+
+ if (Block == NULL) {
+ *Progress = ConfigResp;
+ Status = EFI_INVALID_PARAMETER;
+ goto Exit;
+ }
+
+ Status = EFI_SUCCESS;
+
+Exit:
+
+ HiiNumberFree (&HiiNumber);
+
+ return Status;
+}
diff --git a/Platform/AMD/AmdPlatformPkg/Universal/HiiConfigRouting/AmdHiiConfigRouting.h b/Platform/AMD/AmdPlatformPkg/Universal/HiiConfigRouting/AmdHiiConfigRouting.h
new file mode 100644
index 0000000000..bce606f7dd
--- /dev/null
+++ b/Platform/AMD/AmdPlatformPkg/Universal/HiiConfigRouting/AmdHiiConfigRouting.h
@@ -0,0 +1,189 @@
+/** @file
+ Provide optimized implementation of HII_CONFIG_ROUTING Protocol
+ functions HiiBlockToConfig and HiiConfigToBlock.
+
+ Copyright (C) 2023 - 2024 Advanced Micro Devices, Inc. All rights reserved.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef AMD_HII_CONFIG_ROUTING_H_
+#define AMD_HII_CONFIG_ROUTING_H_
+
+#include <Library/DebugLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Protocol/HiiConfigRouting.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/UefiBootServicesTableLib.h>
+
+#define MAX_STRING_LENGTH 1024
+
+///
+/// Returns the size of a Null-terminated Unicode string in bytes, including the
+/// Null terminator.
+///
+#define HII_STR_SIZE(str) ((HiiStrLen (str) + 1) * sizeof (*str))
+
+///
+/// HII_NUMBER definitions
+///
+typedef struct {
+ // Public variables
+ UINT8 *NumberPtr; ///< Pointer to a number in array of bytes.
+ UINTN NumberPtrLength; ///< 2 * number of bytes. Note: changing this to
+ ///< number of bytes will impact existing code and
+ ///< hard to test.
+ UINTN Value; ///< If Value is less than or equal to 64-bits,
+ ///< store value here as an unsigned integer.
+ UINTN StringLength; ///< Input string length.
+
+ // Private variables
+ UINTN PrivateBufferSize; ///< Size of allocated NumberPtr. This reduces
+ ///< reallocations as this can be used for
+ ///< multiple numbers.
+} HII_NUMBER;
+
+///
+/// HII_STRING definitions
+///
+typedef struct {
+ // Public variables
+ EFI_STRING String; ///< String that is maintained here, and futures
+ ///< calls will append to it.
+ UINTN StringLength; ///< Length of String.
+
+ // Private variables
+ UINTN PrivateBufferSize; ///< Length of allocated String. This reduces
+ ///< reallocations as strings are appended.
+} HII_STRING;
+
+#define FIXED_STR_LEN(String) (sizeof (String) / sizeof (CHAR16) - 1)
+
+typedef enum {
+ ElementGuidHdr = 0,
+ ElementNameHdr = 1,
+ ElementPathHdr = 2,
+ ElementOffsetHdr = 3,
+ ElementWidthHdr = 4,
+ ElementValueHdr = 5
+} ELEMENT_HDR;
+
+typedef struct {
+ EFI_STRING ElementString;
+ UINTN ElementLength;
+} HII_ELEMENT;
+
+/**
+ This helper function is to be called by drivers to map configuration data
+ stored in byte array ("block") formats such as UEFI Variables into current
+ configuration strings.
+
+ @param This A pointer to the EFI_HII_CONFIG_ROUTING_PROTOCOL
+ instance.
+ @param ConfigRequest A null-terminated Unicode string in
+ <ConfigRequest> format.
+ @param Block Array of bytes defining the block's
+ configuration.
+ @param BlockSize Length in bytes of Block.
+ @param Config Filled-in configuration string. String allocated
+ by the function. Returned only if call is
+ successful.
+ @param Progress A pointer to a string filled in with the offset
+ of the most recent & before the first failing
+ name/value pair (or the beginning of the string
+ if the failure is in the first name / value pair)
+ or the terminating NULL if all was successful.
+
+ @retval EFI_SUCCESS The request succeeded. Progress points to the
+ null terminator at the end of the ConfigRequest
+ string.
+ @retval EFI_OUT_OF_RESOURCES Not enough memory to allocate Config.
+ Progress points to the first character of
+ ConfigRequest.
+ @retval EFI_INVALID_PARAMETER Passing in a NULL for the ConfigRequest or
+ Block parameter would result in this type of
+ error. Progress points to the first character
+ of ConfigRequest.
+ @retval EFI_NOT_FOUND Target for the specified routing data was not
+ found. Progress points to the "G" in "GUID" of
+ the errant routing data.
+ @retval EFI_DEVICE_ERROR Block not large enough. Progress undefined.
+ @retval EFI_INVALID_PARAMETER Encountered non <BlockName> formatted string.
+ Block is left updated and Progress points at
+ the '&' preceding the first non-<BlockName>.
+
+**/
+EFI_STATUS
+EFIAPI
+HiiBlockToConfig (
+ IN CONST EFI_HII_CONFIG_ROUTING_PROTOCOL *This,
+ IN CONST EFI_STRING ConfigRequest,
+ IN CONST UINT8 *Block,
+ IN CONST UINTN BlockSize,
+ OUT EFI_STRING *Config,
+ OUT EFI_STRING *Progress
+ );
+
+/**
+ This helper function is to be called by drivers to map configuration strings
+ to configurations stored in byte array ("block") formats such as UEFI Variables.
+
+ @param This A pointer to the EFI_HII_CONFIG_ROUTING_PROTOCOL
+ instance.
+ @param ConfigResp A null-terminated Unicode string in <ConfigResp>
+ format.
+ @param Block A possibly null array of bytes representing the
+ current block. Only bytes referenced in the
+ ConfigResp string in the block are modified. If
+ this parameter is null or if the *BlockSize
+ parameter is (on input) shorter than required by
+ the Configuration string, only the BlockSize
+ parameter is updated and an appropriate status
+ (see below) is returned.
+ @param BlockSize The length of the Block in units of UINT8. On
+ input, this is the size of the Block. On output,
+ if successful, contains the largest index of
+ the modified byte in the Block, or the required
+ buffer.
+ size if the Block is not large enough.
+ @param Progress On return, points to an element of the ConfigResp
+ string filled in with the offset of the most
+ recent '&' before the first failing name/value
+ pair (or the beginning of the string if the
+ failure is in the first name/value pair) or
+ the terminating NULL if all was successful.
+
+ @retval EFI_SUCCESS The request succeeded. Progress points to the
+ null terminator at the end of the ConfigResp
+ string.
+ @retval EFI_OUT_OF_RESOURCES Not enough memory to allocate Config. Progress
+ points to the first character of ConfigResp.
+ @retval EFI_INVALID_PARAMETER Passing in a NULL for the ConfigResp or Block
+ parameter would result in this type of error.
+ Progress points to the first character of
+ ConfigResp.
+ @retval EFI_NOT_FOUND Target for the specified routing data was not
+ found. Progress points to the "G" in "GUID" of
+ the errant routing data.
+ @retval EFI_INVALID_PARAMETER Encountered non <BlockName> formatted name/
+ value pair. Block is left updated and Progress
+ points at the '&' preceding the first
+ non-<BlockName>.
+ @retval EFI_BUFFER_TOO_SMALL Block not large enough. Progress undefined.
+ BlockSize is updated with the required buffer
+ size.
+
+**/
+EFI_STATUS
+EFIAPI
+HiiConfigToBlock (
+ IN CONST EFI_HII_CONFIG_ROUTING_PROTOCOL *This,
+ IN CONST EFI_STRING ConfigResp,
+ IN OUT UINT8 *Block,
+ IN OUT UINTN *BlockSize,
+ OUT EFI_STRING *Progress
+ );
+
+#endif // AMD_HII_CONFIG_ROUTING_H_
--
2.34.1
-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#118886): https://edk2.groups.io/g/devel/message/118886
Mute This Topic: https://groups.io/mt/106090924/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [edk2-devel] [PATCH 6/6] AmdPlatformPkg: Adds SecureBootDefaultKeysInit driver
2024-05-14 8:15 [edk2-devel] [PATCH 0/6] AmdPlatformPkg: Adds board independent modules Abdul Lateef Attar via groups.io
` (4 preceding siblings ...)
2024-05-14 8:15 ` [edk2-devel] [PATCH 5/6] AmdPlatformPkg: Adds AmdConfigRouting driver Abdul Lateef Attar via groups.io
@ 2024-05-14 8:15 ` Abdul Lateef Attar via groups.io
5 siblings, 0 replies; 7+ messages in thread
From: Abdul Lateef Attar via groups.io @ 2024-05-14 8:15 UTC (permalink / raw)
To: devel; +Cc: Abdul Lateef Attar, Abner Chang, Paul Grimes
Adds SecureBootDefaultKeysInit driver to enroll
secure boot default keys.
Cc: Abner Chang <abner.chang@amd.com>
Cc: Paul Grimes <paul.grimes@amd.com>
Signed-off-by: Abdul Lateef Attar <AbdulLateef.Attar@amd.com>
---
.../AMD/AmdPlatformPkg/AmdPlatformPkg.dsc | 12 +
.../SecureBootDefaultKeysInit.c | 645 ++++++++++++++++++
.../SecureBootDefaultKeysInit.inf | 49 ++
3 files changed, 706 insertions(+)
create mode 100644 Platform/AMD/AmdPlatformPkg/Universal/SecureBoot/SecureBootDefaultKeysInit/SecureBootDefaultKeysInit.c
create mode 100644 Platform/AMD/AmdPlatformPkg/Universal/SecureBoot/SecureBootDefaultKeysInit/SecureBootDefaultKeysInit.inf
diff --git a/Platform/AMD/AmdPlatformPkg/AmdPlatformPkg.dsc b/Platform/AMD/AmdPlatformPkg/AmdPlatformPkg.dsc
index 3d13c9e41d..40ed5ea07c 100644
--- a/Platform/AMD/AmdPlatformPkg/AmdPlatformPkg.dsc
+++ b/Platform/AMD/AmdPlatformPkg/AmdPlatformPkg.dsc
@@ -25,17 +25,28 @@
[LibraryClasses.Common]
AlwaysFalseDepexLib|AmdPlatformPkg/Library/BaseAlwaysFalseDepexLib/BaseAlwaysFalseDepexLib.inf
+ BaseCryptLib|CryptoPkg/Library/BaseCryptLib/BaseCryptLib.inf
BaseLib|MdePkg/Library/BaseLib/BaseLib.inf
BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf
DebugPrintErrorLevelLib|MdePkg/Library/BaseDebugPrintErrorLevelLib/BaseDebugPrintErrorLevelLib.inf
DevicePathLib|MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.inf
+ DxeServicesLib|MdePkg/Library/DxeServicesLib/DxeServicesLib.inf
+ HobLib|MdePkg/Library/DxeHobLib/DxeHobLib.inf
+ IntrinsicLib|CryptoPkg/Library/IntrinsicLib/IntrinsicLib.inf
+ OpensslLib|CryptoPkg/Library/OpensslLib/OpensslLib.inf
PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf
+ PlatformPKProtectionLib|SecurityPkg/Library/PlatformPKProtectionLibVarPolicy/PlatformPKProtectionLibVarPolicy.inf
PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf
+ RngLib|MdePkg/Library/BaseRngLib/BaseRngLib.inf
+ SecureBootVariableLib|SecurityPkg/Library/SecureBootVariableLib/SecureBootVariableLib.inf
+ SecureBootVariableProvisionLib|SecurityPkg/Library/SecureBootVariableProvisionLib/SecureBootVariableProvisionLib.inf
SerialPortLib|MdePkg/Library/BaseSerialPortLibNull/BaseSerialPortLibNull.inf
+ TimerLib|MdePkg/Library/BaseTimerLibNullTemplate/BaseTimerLibNullTemplate.inf
UefiBootServicesTableLib|MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.inf
UefiDriverEntryPoint|MdePkg/Library/UefiDriverEntryPoint/UefiDriverEntryPoint.inf
UefiLib|MdePkg/Library/UefiLib/UefiLib.inf
UefiRuntimeServicesTableLib|MdePkg/Library/UefiRuntimeServicesTableLib/UefiRuntimeServicesTableLib.inf
+
!if $(TARGET) == RELEASE
DebugLib|MdePkg/Library/BaseDebugLibNull/BaseDebugLibNull.inf
!else
@@ -51,6 +62,7 @@
AmdPlatformPkg/Library/BaseAlwaysFalseDepexLib/BaseAlwaysFalseDepexLib.inf
AmdPlatformPkg/Library/DxePlatformSocLib/DxePlatformSocLibNull.inf
AmdPlatformPkg/Library/SimulatorSerialPortLibPort80/SimulatorSerialPortLibPort80.inf
+ AmdPlatformPkg/Universal/SecureBoot/SecureBootDefaultKeysInit/SecureBootDefaultKeysInit.inf
AmdPlatformPkg/Universal/HiiConfigRouting/AmdConfigRouting.inf
AmdPlatformPkg/Universal/LogoDxe/JpegLogoDxe.inf # Server platform JPEG logo driver
AmdPlatformPkg/Universal/LogoDxe/LogoDxe.inf # Server platfrom Bitmap logo driver
diff --git a/Platform/AMD/AmdPlatformPkg/Universal/SecureBoot/SecureBootDefaultKeysInit/SecureBootDefaultKeysInit.c b/Platform/AMD/AmdPlatformPkg/Universal/SecureBoot/SecureBootDefaultKeysInit/SecureBootDefaultKeysInit.c
new file mode 100644
index 0000000000..071bfe5b68
--- /dev/null
+++ b/Platform/AMD/AmdPlatformPkg/Universal/SecureBoot/SecureBootDefaultKeysInit/SecureBootDefaultKeysInit.c
@@ -0,0 +1,645 @@
+/** @file
+ This driver init default Secure Boot variables
+
+ Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR>
+ (C) Copyright 2018 Hewlett Packard Enterprise Development LP<BR>
+ Copyright (c) 2021, ARM Ltd. All rights reserved.<BR>
+ Copyright (c) 2021, Semihalf All rights reserved.<BR>
+ Copyright (c) 2021, Ampere Computing LLC. All rights reserved.<BR>
+ Copyright (C) 2023 - 2024 Advanced Micro Devices, Inc. All rights reserved.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Uefi.h>
+#include <UefiSecureBoot.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/DxeServicesLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Guid/AuthenticatedVariableFormat.h>
+#include <Guid/ImageAuthentication.h>
+#include <Library/SecureBootVariableLib.h>
+#include <Library/SecureBootVariableProvisionLib.h>
+
+/**
+ Set PKDefault Variable.
+
+ @param[in] X509Data X509 Certificate data.
+ @param[in] X509DataSize X509 Certificate data size.
+
+ @retval EFI_SUCCESS PKDefault is set successfully.
+
+**/
+EFI_STATUS
+SetPkDefault (
+ IN UINT8 *X509Data,
+ IN UINTN X509DataSize
+ )
+{
+ EFI_STATUS Status;
+ UINT32 Attr;
+ UINTN DataSize;
+ EFI_SIGNATURE_LIST *PkCert;
+ EFI_SIGNATURE_DATA *PkCertData;
+
+ PkCert = NULL;
+
+ //
+ // Allocate space for PK certificate list and initialize it.
+ // Create PK database entry with SignatureHeaderSize equals 0.
+ //
+ PkCert = (EFI_SIGNATURE_LIST *)AllocateZeroPool (
+ sizeof (EFI_SIGNATURE_LIST) + sizeof (EFI_SIGNATURE_DATA) - 1
+ + X509DataSize
+ );
+ if (PkCert == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ DEBUG ((DEBUG_ERROR, "%a: Cannot initialize PKDefault: %r\n", __func__, Status));
+ goto ON_EXIT;
+ }
+
+ PkCert->SignatureListSize = (UINT32)(sizeof (EFI_SIGNATURE_LIST)
+ + sizeof (EFI_SIGNATURE_DATA) - 1
+ + X509DataSize);
+ PkCert->SignatureSize = (UINT32)(sizeof (EFI_SIGNATURE_DATA) - 1 + X509DataSize);
+ PkCert->SignatureHeaderSize = 0;
+ CopyGuid (&PkCert->SignatureType, &gEfiCertX509Guid);
+ PkCertData = (EFI_SIGNATURE_DATA *)((UINTN)PkCert
+ + sizeof (EFI_SIGNATURE_LIST)
+ + PkCert->SignatureHeaderSize);
+ CopyGuid (&PkCertData->SignatureOwner, &gEfiGlobalVariableGuid);
+ //
+ // Fill the PK database with PKpub data from X509 certificate file.
+ //
+ CopyMem (&(PkCertData->SignatureData[0]), X509Data, X509DataSize);
+
+ Attr = EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS;
+ DataSize = PkCert->SignatureListSize;
+
+ Status = gRT->SetVariable (
+ EFI_PK_DEFAULT_VARIABLE_NAME,
+ &gEfiGlobalVariableGuid,
+ Attr,
+ DataSize,
+ PkCert
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: Cannot initialize PKDefault: %r\n", __func__, Status));
+ goto ON_EXIT;
+ }
+
+ON_EXIT:
+
+ if (PkCert != NULL) {
+ FreePool (PkCert);
+ }
+
+ return Status;
+}
+
+/**
+ Set KDKDefault Variable.
+
+ @param[in] X509Data X509 Certificate data.
+ @param[in] X509DataSize X509 Certificate data size.
+
+ @retval EFI_SUCCESS KEKDefault is set successfully.
+
+**/
+EFI_STATUS
+SetKekDefault (
+ IN UINT8 *X509Data,
+ IN UINTN X509DataSize
+ )
+{
+ EFI_STATUS Status;
+ EFI_SIGNATURE_DATA *KEKSigData;
+ EFI_SIGNATURE_LIST *KekSigList;
+ UINTN DataSize;
+ UINTN KekSigListSize;
+ UINT32 Attr;
+
+ KekSigList = NULL;
+ KekSigListSize = 0;
+ DataSize = 0;
+ KEKSigData = NULL;
+
+ KekSigListSize = sizeof (EFI_SIGNATURE_LIST) + sizeof (EFI_SIGNATURE_DATA) - 1 + X509DataSize;
+ KekSigList = (EFI_SIGNATURE_LIST *)AllocateZeroPool (KekSigListSize);
+ if (KekSigList == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ DEBUG ((DEBUG_ERROR, "%a: Cannot initialize KEKDefault: %r\n", __func__, Status));
+ goto ON_EXIT;
+ }
+
+ //
+ // Fill Certificate Database parameters.
+ //
+ KekSigList->SignatureListSize = (UINT32)KekSigListSize;
+ KekSigList->SignatureHeaderSize = 0;
+ KekSigList->SignatureSize = (UINT32)(sizeof (EFI_SIGNATURE_DATA) - 1 + X509DataSize);
+ CopyGuid (&KekSigList->SignatureType, &gEfiCertX509Guid);
+
+ KEKSigData = (EFI_SIGNATURE_DATA *)((UINT8 *)KekSigList + sizeof (EFI_SIGNATURE_LIST));
+ CopyGuid (&KEKSigData->SignatureOwner, &gEfiGlobalVariableGuid);
+ CopyMem (KEKSigData->SignatureData, X509Data, X509DataSize);
+
+ //
+ // Check if KEK been already existed.
+ // If true, use EFI_VARIABLE_APPEND_WRITE attribute to append the
+ // new kek to original variable
+ //
+ Attr = EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS;
+
+ Status = gRT->GetVariable (
+ EFI_KEK_DEFAULT_VARIABLE_NAME,
+ &gEfiGlobalVariableGuid,
+ NULL,
+ &DataSize,
+ NULL
+ );
+ if (Status == EFI_BUFFER_TOO_SMALL) {
+ Attr |= EFI_VARIABLE_APPEND_WRITE;
+ } else if (Status != EFI_NOT_FOUND) {
+ DEBUG ((DEBUG_ERROR, "%a: Cannot get the value of KEK: %r\n", __func__, Status));
+ goto ON_EXIT;
+ }
+
+ Status = gRT->SetVariable (
+ EFI_KEK_DEFAULT_VARIABLE_NAME,
+ &gEfiGlobalVariableGuid,
+ Attr,
+ KekSigListSize,
+ KekSigList
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: Cannot initialize KEKDefault: %r\n", __func__, Status));
+ goto ON_EXIT;
+ }
+
+ON_EXIT:
+
+ if (KekSigList != NULL) {
+ FreePool (KekSigList);
+ }
+
+ return Status;
+}
+
+/**
+ Checks if the file content complies with EFI_VARIABLE_AUTHENTICATION_2 format
+
+ @param[in] Data Data.
+ @param[in] DataSize Data size.
+
+ @retval TRUE The content is EFI_VARIABLE_AUTHENTICATION_2 format.
+ @retval FALSE The content is NOT a EFI_VARIABLE_AUTHENTICATION_2 format.
+
+**/
+BOOLEAN
+IsAuthentication2Format (
+ IN UINT8 *Data,
+ IN UINTN DataSize
+ )
+{
+ EFI_VARIABLE_AUTHENTICATION_2 *Auth2;
+ BOOLEAN IsAuth2Format;
+
+ IsAuth2Format = FALSE;
+
+ Auth2 = (EFI_VARIABLE_AUTHENTICATION_2 *)Data;
+ if (Auth2->AuthInfo.Hdr.wCertificateType != WIN_CERT_TYPE_EFI_GUID) {
+ goto ON_EXIT;
+ }
+
+ if (CompareGuid (&gEfiCertPkcs7Guid, &Auth2->AuthInfo.CertType)) {
+ IsAuth2Format = TRUE;
+ }
+
+ON_EXIT:
+
+ return IsAuth2Format;
+}
+
+/**
+ Set signature database with the data of EFI_VARIABLE_AUTHENTICATION_2 format.
+
+ @param[in] AuthData AUTHENTICATION_2 data.
+ @param[in] AuthDataSize AUTHENTICATION_2 data size.
+ @param[in] VariableName Variable name of signature database, must be
+ EFI_DB_DEFAULT_VARIABLE_NAME or EFI_DBX_DEFAULT_VARIABLE_NAME or EFI_DBT_DEFAULT_VARIABLE_NAME.
+
+ @retval EFI_SUCCESS New signature is set successfully.
+ @retval EFI_INVALID_PARAMETER The parameter is invalid.
+ @retval EFI_UNSUPPORTED Unsupported command.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.
+
+**/
+EFI_STATUS
+SetAuthentication2ToSigDb (
+ IN UINT8 *AuthData,
+ IN UINTN AuthDataSize,
+ IN CHAR16 *VariableName
+ )
+{
+ EFI_STATUS Status;
+ UINTN DataSize;
+ UINT32 Attr;
+ UINT8 *Data;
+
+ Attr = EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS;
+
+ //
+ // Check if SigDB variable has been already existed.
+ // If true, use EFI_VARIABLE_APPEND_WRITE attribute to append the
+ // new signature data to original variable
+ //
+ DataSize = 0;
+ Status = gRT->GetVariable (
+ VariableName,
+ &gEfiGlobalVariableGuid,
+ NULL,
+ &DataSize,
+ NULL
+ );
+ if (Status == EFI_BUFFER_TOO_SMALL) {
+ Attr |= EFI_VARIABLE_APPEND_WRITE;
+ } else if (Status != EFI_NOT_FOUND) {
+ DEBUG ((DEBUG_ERROR, "%a: Cannot get the value of signature database: %r\n", __func__, Status));
+ return Status;
+ }
+
+ //
+ // Ignore AUTHENTICATION_2 region. Only the actual certificate is needed.
+ //
+ DataSize = AuthDataSize - ((EFI_VARIABLE_AUTHENTICATION_2 *)AuthData)->AuthInfo.Hdr.dwLength - sizeof (EFI_TIME);
+ Data = AuthData + (AuthDataSize - DataSize);
+
+ Status = gRT->SetVariable (
+ VariableName,
+ &gEfiGlobalVariableGuid,
+ Attr,
+ DataSize,
+ Data
+ );
+
+ DEBUG ((DEBUG_INFO, "Set AUTH_2 data to Var:%s Status: %x\n", VariableName, Status));
+ return Status;
+}
+
+/**
+
+ Set signature database with the data of X509 format.
+
+ @param[in] X509Data X509 Certificate data.
+ @param[in] X509DataSize X509 Certificate data size.
+ @param[in] VariableName Variable name of signature database, must be
+ EFI_DB_DEFAULT_VARIABLE_NAME or EFI_DBX_DEFAULT_VARIABLE_NAME or EFI_DBT_DEFAULT_VARIABLE_NAME.
+ @param[in] SignatureOwnerGuid Guid of the signature owner.
+
+ @retval EFI_SUCCESS New X509 is enrolled successfully.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.
+
+**/
+EFI_STATUS
+SetX509ToSigDb (
+ IN UINT8 *X509Data,
+ IN UINTN X509DataSize,
+ IN CHAR16 *VariableName,
+ IN EFI_GUID *SignatureOwnerGuid
+ )
+{
+ EFI_STATUS Status;
+ EFI_SIGNATURE_LIST *SigDBCert;
+ EFI_SIGNATURE_DATA *SigDBCertData;
+ VOID *Data;
+ UINTN DataSize;
+ UINTN SigDBSize;
+ UINT32 Attr;
+
+ SigDBSize = 0;
+ DataSize = 0;
+ SigDBCert = NULL;
+ SigDBCertData = NULL;
+ Data = NULL;
+
+ SigDBSize = sizeof (EFI_SIGNATURE_LIST) + sizeof (EFI_SIGNATURE_DATA) - 1 + X509DataSize;
+
+ Data = AllocateZeroPool (SigDBSize);
+ if (Data == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ DEBUG ((DEBUG_ERROR, "%a: Cannot allocate memory: %r\n", __func__, Status));
+ goto ON_EXIT;
+ }
+
+ //
+ // Fill Certificate Database parameters.
+ //
+ SigDBCert = (EFI_SIGNATURE_LIST *)Data;
+ SigDBCert->SignatureListSize = (UINT32)SigDBSize;
+ SigDBCert->SignatureHeaderSize = 0;
+ SigDBCert->SignatureSize = (UINT32)(sizeof (EFI_SIGNATURE_DATA) - 1 + X509DataSize);
+ CopyGuid (&SigDBCert->SignatureType, &gEfiCertX509Guid);
+
+ SigDBCertData = (EFI_SIGNATURE_DATA *)((UINT8 *)SigDBCert + sizeof (EFI_SIGNATURE_LIST));
+ CopyGuid (&SigDBCertData->SignatureOwner, SignatureOwnerGuid);
+ CopyMem ((UINT8 *)(SigDBCertData->SignatureData), X509Data, X509DataSize);
+
+ //
+ // Check if signature database entry has been already existed.
+ // If true, use EFI_VARIABLE_APPEND_WRITE attribute to append the
+ // new signature data to original variable
+ //
+ Attr = EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS;
+
+ Status = gRT->GetVariable (
+ VariableName,
+ &gEfiGlobalVariableGuid,
+ NULL,
+ &DataSize,
+ NULL
+ );
+ if (Status == EFI_BUFFER_TOO_SMALL) {
+ Attr |= EFI_VARIABLE_APPEND_WRITE;
+ } else if (Status != EFI_NOT_FOUND) {
+ goto ON_EXIT;
+ }
+
+ Status = gRT->SetVariable (
+ VariableName,
+ &gEfiGlobalVariableGuid,
+ Attr,
+ SigDBSize,
+ Data
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: Cannot set signature database: %r\n", __func__, Status));
+ goto ON_EXIT;
+ }
+
+ON_EXIT:
+
+ if (Data != NULL) {
+ FreePool (Data);
+ }
+
+ return Status;
+}
+
+/**
+
+ Set signature database.
+
+ @param[in] Data Data.
+ @param[in] DataSize Data size.
+ @param[in] VariableName Variable name of signature database, must be
+ EFI_DB_DEFAULT_VARIABLE_NAME or EFI_DBX_DEFAULT_VARIABLE_NAME or EFI_DBT_DEFAULT_VARIABLE_NAME.
+ @param[in] SignatureOwnerGuid Guid of the signature owner.
+
+ @retval EFI_SUCCESS Signature is set successfully.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.
+
+**/
+EFI_STATUS
+SetSignatureDatabase (
+ IN UINT8 *Data,
+ IN UINTN DataSize,
+ IN CHAR16 *VariableName,
+ IN EFI_GUID *SignatureOwnerGuid
+ )
+{
+ if (IsAuthentication2Format (Data, DataSize)) {
+ return SetAuthentication2ToSigDb (Data, DataSize, VariableName);
+ } else {
+ return SetX509ToSigDb (Data, DataSize, VariableName, SignatureOwnerGuid);
+ }
+}
+
+/** Initializes PKDefault variable with data from FFS section.
+
+ @retval EFI_SUCCESS Variable was initialized successfully.
+ @retval EFI_UNSUPPORTED Variable already exists.
+**/
+EFI_STATUS
+InitPkDefault (
+ IN VOID
+ )
+{
+ EFI_STATUS Status;
+ UINT8 *Data;
+ UINTN DataSize;
+
+ //
+ // Check if variable exists, if so do not change it
+ //
+ Status = GetVariable2 (EFI_PK_DEFAULT_VARIABLE_NAME, &gEfiGlobalVariableGuid, (VOID **)&Data, &DataSize);
+ if (Status == EFI_SUCCESS) {
+ DEBUG ((DEBUG_INFO, "Variable %s exists. Old value is preserved\n", EFI_PK_DEFAULT_VARIABLE_NAME));
+ FreePool (Data);
+ return EFI_UNSUPPORTED;
+ }
+
+ //
+ // Variable does not exist, can be initialized
+ //
+ DEBUG ((DEBUG_INFO, "Variable %s does not exist.\n", EFI_PK_DEFAULT_VARIABLE_NAME));
+
+ //
+ // Enroll default PK.
+ //
+ Status = GetSectionFromFv (
+ &gDefaultPKFileGuid,
+ EFI_SECTION_RAW,
+ 0,
+ (VOID **)&Data,
+ &DataSize
+ );
+ if (!EFI_ERROR (Status)) {
+ SetPkDefault (Data, DataSize);
+ }
+
+ return EFI_SUCCESS;
+}
+
+/** Initializes KEKDefault variable with data from FFS section.
+
+ @retval EFI_SUCCESS Variable was initialized successfully.
+ @retval EFI_UNSUPPORTED Variable already exists.
+**/
+EFI_STATUS
+InitKekDefault (
+ IN VOID
+ )
+{
+ EFI_STATUS Status;
+ UINTN Index;
+ UINT8 *Data;
+ UINTN DataSize;
+
+ //
+ // Check if variable exists, if so do not change it
+ //
+ Status = GetVariable2 (EFI_KEK_DEFAULT_VARIABLE_NAME, &gEfiGlobalVariableGuid, (VOID **)&Data, &DataSize);
+ if (Status == EFI_SUCCESS) {
+ DEBUG ((DEBUG_INFO, "Variable %s exists. Old value is preserved\n", EFI_KEK_DEFAULT_VARIABLE_NAME));
+ FreePool (Data);
+ return EFI_UNSUPPORTED;
+ }
+
+ Index = 0;
+ do {
+ Status = GetSectionFromFv (
+ &gDefaultKEKFileGuid,
+ EFI_SECTION_RAW,
+ Index,
+ (VOID **)&Data,
+ &DataSize
+ );
+ if (!EFI_ERROR (Status)) {
+ SetKekDefault (Data, DataSize);
+ Index++;
+ }
+ } while (Status == EFI_SUCCESS);
+
+ return EFI_SUCCESS;
+}
+
+/** Initializes dbDefault variable with data from FFS section.
+
+ @retval EFI_SUCCESS Variable was initialized successfully.
+ @retval EFI_UNSUPPORTED Variable already exists.
+**/
+EFI_STATUS
+InitDbDefault (
+ IN VOID
+ )
+{
+ EFI_STATUS Status;
+ UINTN Index;
+ UINT8 *Data;
+ UINTN DataSize;
+
+ Status = GetVariable2 (EFI_DB_DEFAULT_VARIABLE_NAME, &gEfiGlobalVariableGuid, (VOID **)&Data, &DataSize);
+ if (Status == EFI_SUCCESS) {
+ DEBUG ((DEBUG_INFO, "Variable %s exists. Old value is preserved\n", EFI_DB_DEFAULT_VARIABLE_NAME));
+ FreePool (Data);
+ return EFI_UNSUPPORTED;
+ }
+
+ DEBUG ((DEBUG_INFO, "Variable %s does not exist.\n", EFI_DB_DEFAULT_VARIABLE_NAME));
+
+ Index = 0;
+ do {
+ Status = GetSectionFromFv (
+ &gDefaultdbFileGuid,
+ EFI_SECTION_RAW,
+ Index,
+ (VOID **)&Data,
+ &DataSize
+ );
+ if (!EFI_ERROR (Status)) {
+ SetSignatureDatabase (Data, DataSize, EFI_DB_DEFAULT_VARIABLE_NAME, &gEfiGlobalVariableGuid);
+ Index++;
+ }
+ } while (Status == EFI_SUCCESS);
+
+ return EFI_SUCCESS;
+}
+
+/** Initializes dbxDefault variable with data from FFS section.
+
+ @retval EFI_SUCCESS Variable was initialized successfully.
+ @retval EFI_UNSUPPORTED Variable already exists.
+**/
+EFI_STATUS
+InitDbxDefault (
+ IN VOID
+ )
+{
+ EFI_STATUS Status;
+ UINTN Index;
+ UINT8 *Data;
+ UINTN DataSize;
+
+ //
+ // Check if variable exists, if so do not change it
+ //
+ Status = GetVariable2 (EFI_DBX_DEFAULT_VARIABLE_NAME, &gEfiGlobalVariableGuid, (VOID **)&Data, &DataSize);
+ if (Status == EFI_SUCCESS) {
+ DEBUG ((DEBUG_INFO, "Variable %s exists. Old value is preserved\n", EFI_DBX_DEFAULT_VARIABLE_NAME));
+ FreePool (Data);
+ return EFI_UNSUPPORTED;
+ }
+
+ //
+ // Variable does not exist, can be initialized
+ //
+ DEBUG ((DEBUG_INFO, "Variable %s does not exist.\n", EFI_DBX_DEFAULT_VARIABLE_NAME));
+
+ Index = 0;
+ do {
+ Status = GetSectionFromFv (
+ &gDefaultdbxFileGuid,
+ EFI_SECTION_RAW,
+ Index,
+ (VOID **)&Data,
+ &DataSize
+ );
+ if (!EFI_ERROR (Status)) {
+ SetSignatureDatabase (Data, DataSize, EFI_DBX_DEFAULT_VARIABLE_NAME, &gEfiGlobalVariableGuid);
+ Index++;
+ }
+ } while (Status == EFI_SUCCESS);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Initializes default SecureBoot certificates with data from FFS section.
+
+ @param[in] ImageHandle The firmware allocated handle for the EFI image.
+ @param[in] SystemTable A pointer to the EFI System Table.
+
+ @retval EFI_SUCCESS Variable was initialized successfully.
+**/
+EFI_STATUS
+EFIAPI
+SecureBootDefaultKeysInitEntry (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+
+ Status = InitPkDefault ();
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: Cannot initialize PKDefault: %r\n", __func__, Status));
+ return Status;
+ }
+
+ Status = InitKekDefault ();
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: Cannot initialize KEKDefault: %r\n", __func__, Status));
+ return Status;
+ }
+
+ Status = InitDbDefault ();
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: Cannot initialize dbDefault: %r\n", __func__, Status));
+ return Status;
+ }
+
+ Status = InitDbxDefault ();
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: Cannot initialize dbxDefault: %r\n", __func__, Status));
+ return Status;
+ }
+
+ return EFI_SUCCESS;
+}
diff --git a/Platform/AMD/AmdPlatformPkg/Universal/SecureBoot/SecureBootDefaultKeysInit/SecureBootDefaultKeysInit.inf b/Platform/AMD/AmdPlatformPkg/Universal/SecureBoot/SecureBootDefaultKeysInit/SecureBootDefaultKeysInit.inf
new file mode 100644
index 0000000000..345fbdc6ae
--- /dev/null
+++ b/Platform/AMD/AmdPlatformPkg/Universal/SecureBoot/SecureBootDefaultKeysInit/SecureBootDefaultKeysInit.inf
@@ -0,0 +1,49 @@
+## @file
+# Initializes Secure Boot default keys
+#
+# Copyright (c) 2021, ARM Ltd. All rights reserved.<BR>
+# Copyright (c) 2021, Semihalf All rights reserved.<BR>
+# Copyright (C) 2023 - 2024 Advanced Micro Devices, Inc. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 1.29
+ BASE_NAME = SecureBootDefaultKeysInit
+ FILE_GUID = ADB0EEA2-8945-4ADF-94A0-3B0B935B4268
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ ENTRY_POINT = SecureBootDefaultKeysInitEntry
+
+[Sources]
+ SecureBootDefaultKeysInit.c
+
+[Packages]
+ MdeModulePkg/MdeModulePkg.dec
+ MdePkg/MdePkg.dec
+ SecurityPkg/SecurityPkg.dec
+
+[LibraryClasses]
+ DebugLib
+ DxeServicesLib
+ SecureBootVariableLib
+ SecureBootVariableProvisionLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+
+[Guids]
+ gDefaultdbFileGuid
+ gDefaultdbxFileGuid
+ gDefaultKEKFileGuid
+ gDefaultPKFileGuid
+ gEfiCertPkcs7Guid
+ gEfiCertX509Guid
+ gEfiCustomModeEnableGuid
+ gEfiImageSecurityDatabaseGuid
+ gEfiSecureBootEnableDisableGuid
+
+[Depex]
+ gEfiVariableArchProtocolGuid AND
+ gEfiVariableWriteArchProtocolGuid
--
2.34.1
-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#118888): https://edk2.groups.io/g/devel/message/118888
Mute This Topic: https://groups.io/mt/106090926/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-
^ permalink raw reply related [flat|nested] 7+ messages in thread