From 63b64acd19f332f65d36d0e4454819948ec42bf8 Mon Sep 17 00:00:00 2001 From: Fabio Pagani Date: Wed, 18 Feb 2026 15:01:06 +0100 Subject: [PATCH 1/2] Add support for AMD Microcode Co-authored-by: yeggor --- UEFIExtract/CMakeLists.txt | 1 + UEFIFind/CMakeLists.txt | 1 + UEFITool/CMakeLists.txt | 1 + UEFITool/uefitool.pro | 2 + common/amd_microcode.cpp | 77 +++++++++++++++ common/amd_microcode.h | 41 ++++++++ common/ffs.cpp | 4 + common/ffs.h | 3 + common/ffsparser.cpp | 185 ++++++++++++++++++++++++++++++++++++- common/ffsparser.h | 5 + fuzzing/CMakeLists.txt | 1 + 11 files changed, 319 insertions(+), 2 deletions(-) create mode 100644 common/amd_microcode.cpp create mode 100644 common/amd_microcode.h diff --git a/UEFIExtract/CMakeLists.txt b/UEFIExtract/CMakeLists.txt index 4f9438170..a81bcd496 100644 --- a/UEFIExtract/CMakeLists.txt +++ b/UEFIExtract/CMakeLists.txt @@ -19,6 +19,7 @@ SET(PROJECT_SOURCES ../common/nvramparser.cpp ../common/meparser.cpp ../common/ffsparser.cpp + ../common/amd_microcode.cpp ../common/fitparser.cpp ../common/ffsreport.cpp ../common/peimage.cpp diff --git a/UEFIFind/CMakeLists.txt b/UEFIFind/CMakeLists.txt index f0242032e..7c3c0d30e 100644 --- a/UEFIFind/CMakeLists.txt +++ b/UEFIFind/CMakeLists.txt @@ -17,6 +17,7 @@ SET(PROJECT_SOURCES ../common/nvram.cpp ../common/nvramparser.cpp ../common/ffsparser.cpp + ../common/amd_microcode.cpp ../common/fitparser.cpp ../common/peimage.cpp ../common/treeitem.cpp diff --git a/UEFITool/CMakeLists.txt b/UEFITool/CMakeLists.txt index 20ec04bb2..25b913791 100644 --- a/UEFITool/CMakeLists.txt +++ b/UEFITool/CMakeLists.txt @@ -48,6 +48,7 @@ SET(PROJECT_SOURCES ../common/utility.cpp ../common/ffsbuilder.cpp ../common/ffsparser.cpp + ../common/amd_microcode.cpp ../common/ffsreport.cpp ../common/treeitem.cpp ../common/treemodel.cpp diff --git a/UEFITool/uefitool.pro b/UEFITool/uefitool.pro index b90b76f4a..484a15b0e 100644 --- a/UEFITool/uefitool.pro +++ b/UEFITool/uefitool.pro @@ -39,6 +39,7 @@ HEADERS += uefitool.h \ ../common/treeitem.h \ ../common/intel_fit.h \ ../common/intel_microcode.h \ + ../common/amd_microcode.h \ ../common/treemodel.h \ ../common/LZMA/LzmaCompress.h \ ../common/LZMA/LzmaDecompress.h \ @@ -109,6 +110,7 @@ SOURCES += uefitool_main.cpp \ ../common/utility.cpp \ ../common/ffsbuilder.cpp \ ../common/ffsparser.cpp \ + ../common/amd_microcode.cpp \ ../common/ffsreport.cpp \ ../common/treeitem.cpp \ ../common/treemodel.cpp \ diff --git a/common/amd_microcode.cpp b/common/amd_microcode.cpp new file mode 100644 index 000000000..c9f44ac6d --- /dev/null +++ b/common/amd_microcode.cpp @@ -0,0 +1,77 @@ +#include "amd_microcode.h" +#include "basetypes.h" + +UINT32 getDataSizeMicrocodeAmd(const AMD_MICROCODE_HEADER *ucodeHeader) { + if (ucodeHeader->LoaderID >= 0x8005) { + return ((ucodeHeader->InitializationFlag << 8) | ucodeHeader->DataSize) * 0x10; + } + + return ucodeHeader->DataSize; +} + +UINT32 getSizeMicrocodeAmd(const AMD_MICROCODE_HEADER *ucodeHeader) { + UINT32 microcodeDataLen = getDataSizeMicrocodeAmd(ucodeHeader); + UINT32 cpuIdByte = (getCpuIdMicrocodeAmd(ucodeHeader) >> 16) & 0xFF; + UINT32 microcodeLen = 0; + + if (microcodeDataLen == 0x20) { + microcodeLen = 0x3C0; + } else if (microcodeDataLen == 0x10) { + microcodeLen = 0x200; + } else if (microcodeDataLen) { + microcodeLen = microcodeDataLen; + } else if (cpuIdByte == 0x50) { + microcodeLen = 0x620; + } else if (cpuIdByte == 0x58) { + microcodeLen = 0x567; + } else if (cpuIdByte == 0x60 || cpuIdByte == 0x61 || cpuIdByte == 0x63 || cpuIdByte == 0x66 || cpuIdByte <= 0x67) { + microcodeLen = 0xA20; + } else if (cpuIdByte == 0x68 || cpuIdByte == 0x69) { + microcodeLen = 0x980; + } else if (cpuIdByte == 0x70 || cpuIdByte == 0x73) { + microcodeLen = 0xD60; + } else if (cpuIdByte >= 0x80 && cpuIdByte <= 0x83 || cpuIdByte >= 0x85 && cpuIdByte <= 0x8A) { + microcodeLen = 0xC80; + } else if (cpuIdByte >= 0xA0 && cpuIdByte <= 0xA7 || cpuIdByte == 0xAA) { + microcodeLen = 0x15C0; + } else if (cpuIdByte == 0xB4) { + microcodeLen = 0x3820; + } + + return microcodeLen; +} + +UINT32 getCpuIdMicrocodeAmd(const AMD_MICROCODE_HEADER *ucodeHeader) { + return (((ucodeHeader->ProcessorSignature >> 8) & 0xFF) << 16) | (0x0f << 8) | + (ucodeHeader->ProcessorSignature & 0xff); +} + +UINT16 getYearMicrocodeAmd(const AMD_MICROCODE_HEADER *ucodeHeader) { + if (getCpuIdMicrocodeAmd(ucodeHeader) == 0x00800F11 && ucodeHeader->UpdateRevision == 0x8001105 && + ucodeHeader->DateYear == 0x2016) { + return 0x2017; + } + + return ucodeHeader->DateYear; +} + +UINT8 getMonthMicrocodeAmd(const AMD_MICROCODE_HEADER *ucodeHeader) { + if (getCpuIdMicrocodeAmd(ucodeHeader) == 0x00300F10 && ucodeHeader->UpdateRevision == 0x3000027 && + ucodeHeader->DateMonth == 0x13) { + return 0x12; + } else if (getCpuIdMicrocodeAmd(ucodeHeader) == 0x00730F01 && ucodeHeader->UpdateRevision == 0x7030106 && + ucodeHeader->DateMonth == 0x09) { + return 0x02; + } + + return ucodeHeader->DateMonth; +} + +UINT8 getDayMicrocodeAmd(const AMD_MICROCODE_HEADER *ucodeHeader) { + if (getCpuIdMicrocodeAmd(ucodeHeader) == 0x00730F01 && ucodeHeader->UpdateRevision == 0x7030106 && + ucodeHeader->DateDay == 0x02) { + return 0x09; + } + + return ucodeHeader->DateDay; +} diff --git a/common/amd_microcode.h b/common/amd_microcode.h new file mode 100644 index 000000000..83a239b8a --- /dev/null +++ b/common/amd_microcode.h @@ -0,0 +1,41 @@ +#ifndef AMD_MICROCODE_H +#define AMD_MICROCODE_H + +#include "basetypes.h" +#include "ubytearray.h" + +// Make sure we use right packing rules +#pragma pack(push, 1) + +typedef struct AMD_MICROCODE_HEADER_ { + UINT16 DateYear; + UINT8 DateDay; + UINT8 DateMonth; + UINT32 UpdateRevision; + UINT16 LoaderID; + UINT8 DataSize; + UINT8 InitializationFlag; + UINT32 DataChecksum; + UINT16 NorthBridgeVEN_ID; + UINT16 NorthBridgeDEV_ID; + UINT16 SouthBridgeVEN_ID; + UINT16 SouthBridgeDEV_ID; + UINT16 ProcessorSignature; + UINT8 NorthBridgeREV_ID; + UINT8 SouthBridgeREV_ID; + UINT8 BiosApiRevision; + UINT8 LoadControl; + UINT8 Reserved_1E; + UINT8 Reserved_1F; +} AMD_MICROCODE_HEADER; + +UINT32 getDataSizeMicrocodeAmd(const AMD_MICROCODE_HEADER *ucodeHeader); +UINT32 getSizeMicrocodeAmd(const AMD_MICROCODE_HEADER *ucodeHeader); +UINT32 getCpuIdMicrocodeAmd(const AMD_MICROCODE_HEADER *ucodeHeader); +UINT16 getYearMicrocodeAmd(const AMD_MICROCODE_HEADER *ucodeHeader); +UINT8 getMonthMicrocodeAmd(const AMD_MICROCODE_HEADER *ucodeHeader); +UINT8 getDayMicrocodeAmd(const AMD_MICROCODE_HEADER *ucodeHeader); + +#pragma pack(pop) + +#endif // AMD_MICROCODE_H diff --git a/common/ffs.cpp b/common/ffs.cpp index b70564be6..a46beda48 100644 --- a/common/ffs.cpp +++ b/common/ffs.cpp @@ -85,6 +85,10 @@ extern const UByteArray EFI_DXE_CORE_GUID // D6A2CB7F-6A18-4E2F-B43B-9920A733700 extern const UByteArray AMD_COMPRESSED_RAW_FILE_GUID //20BC8AC9-94D1-4208-AB28-5D673FD73487 ("\xC9\x8A\xBC\x20\xD1\x94\x08\x42\xAB\x28\x5D\x67\x3F\xD7\x34\x87", 16); +// TE/PE files containing AMD microcode +extern const UByteArray AMD_MICROCODE_FILE_GUID // DE3E049C-A218-4891-8658-5FC0FA84C788 +("\x9C\x04\x3E\xDE\x18\xA2\x91\x48\x86\x58\x5F\xC0\xFA\x84\xC7\x88", 16); + // Insyde Flash Device Map GUIDs extern const UByteArray INSYDE_FLASH_MAP_REGION_BOOT_FV_GUID ("\x56\x6d\xd7\xe3\x8a\x98\x6b\x4d\x89\x13\x64\xf2\xdf\x1d\xf6\xa6", 16); diff --git a/common/ffs.h b/common/ffs.h index 8eb6a69c2..5647b65b0 100644 --- a/common/ffs.h +++ b/common/ffs.h @@ -370,6 +370,9 @@ extern const UByteArray EFI_DXE_CORE_GUID; // D6A2CB7F-6A18-4E2F-B43B-9920A73370 // AMD compressed raw file extern const UByteArray AMD_COMPRESSED_RAW_FILE_GUID; //20BC8AC9-94D1-4208-AB28-5D673FD73487 +// TE/PE files containing AMD microcode +extern const UByteArray AMD_MICROCODE_FILE_GUID; // DE3E049C-A218-4891-8658-5FC0FA84C788 + // FFS size conversion routines extern VOID uint32ToUint24(UINT32 size, UINT8* ffsSize); extern UINT32 uint24ToUint32(const UINT8* ffsSize); diff --git a/common/ffsparser.cpp b/common/ffsparser.cpp index 758af4857..1981cbbb9 100644 --- a/common/ffsparser.cpp +++ b/common/ffsparser.cpp @@ -980,7 +980,15 @@ USTATUS FfsParser::parseRawArea(const UModelIndex & index) UByteArray microcode = data.mid(itemOffset, itemSize); result = parseIntelMicrocodeHeader(microcode, headerSize + itemOffset, index, microcodeIndex); if (result) { - msg(usprintf("%s: microcode header parsing failed with error ", __FUNCTION__) + errorCodeToUString(result), index); + msg(usprintf("%s: Intel microcode header parsing failed with error ", __FUNCTION__) + errorCodeToUString(result), index); + } + } + else if (itemType == Subtypes::AmdMicrocode) { + UModelIndex microcodeIndex; + UByteArray microcode = data.mid(itemOffset, itemSize); + result = parseAmdMicrocodeHeader(microcode, headerSize + itemOffset, index, microcodeIndex); + if (result) { + msg(usprintf("%s: AMD microcode header parsing failed with error ", __FUNCTION__) + errorCodeToUString(result), index); } } else if (itemType == Types::BpdtStore) { @@ -1676,6 +1684,66 @@ bool FfsParser::microcodeHeaderValid(const INTEL_MICROCODE_HEADER* ucodeHeader) return TRUE; } +bool FfsParser::microcodeHeaderValidAmd(const AMD_MICROCODE_HEADER* ucodeHeader) +{ + UINT8 DateDay = getDayMicrocodeAmd(ucodeHeader); + UINT8 DateMonth = getMonthMicrocodeAmd(ucodeHeader); + UINT16 DateYear = getYearMicrocodeAmd(ucodeHeader); + + // Check day to be in 0x01-0x09, 0x10-0x19, 0x20-0x29, 0x30-0x31 + if (DateDay < 0x01 || + (DateDay > 0x09 && DateDay < 0x10) || + (DateDay > 0x19 && DateDay < 0x20) || + (DateDay > 0x29 && DateDay < 0x30) || + DateDay > 0x31) { + return FALSE; + } + + // Check month to be in 0x01-0x09, 0x10-0x12 + if (DateMonth < 0x01 || + (DateMonth > 0x09 && DateMonth < 0x10) || + DateMonth > 0x12) { + return FALSE; + } + + // Check year to be in 0x2001-0x2009, 0x2010-0x2019, 0x2020-0x2029 + if (DateYear < 0x2001 || + (DateYear > 0x2009 && DateYear < 0x2010) || + (DateYear > 0x2019 && DateYear < 0x2020) || + (DateYear > 0x2029)) { + return FALSE; + } + + UINT32 microcodeDataLen = getDataSizeMicrocodeAmd(ucodeHeader); + UINT32 cpuId = getCpuIdMicrocodeAmd(ucodeHeader); + + if (cpuId == 0x00000F00 && microcodeDataLen != 0x10 && microcodeDataLen != 0x20) { + return FALSE; + } + + if (((ucodeHeader->LoaderID >> 8) & 0xFF) != 0x80) { + return FALSE; + } + + if (ucodeHeader->NorthBridgeVEN_ID != 0x0000 && ucodeHeader->NorthBridgeVEN_ID != 0x1022) { + return FALSE; + } + + if (ucodeHeader->SouthBridgeVEN_ID != 0x0000 && ucodeHeader->SouthBridgeVEN_ID != 0x1022) { + return FALSE; + } + + if (ucodeHeader->BiosApiRevision != 0x00 && ucodeHeader->BiosApiRevision != 0x01) { + return FALSE; + } + + if (ucodeHeader->LoadControl > 0x0F && ucodeHeader->LoadControl != 0xAA) { + return FALSE; + } + + return TRUE; +} + USTATUS FfsParser::findNextRawAreaItem(const UModelIndex & index, const UINT32 localOffset, UINT8 & nextItemType, UINT32 & nextItemOffset, UINT32 & nextItemSize, UINT32 & nextItemAlternativeSize) { UByteArray data = model->body(index); @@ -1851,6 +1919,29 @@ continue_searching: {} break; } #endif + + // Since the AMD microcode header lacks a clear signature, we attempt to parse it as a last resort + else if (restSize >= sizeof(AMD_MICROCODE_HEADER) + 0x44) { + const AMD_MICROCODE_HEADER* ucodeHeader = (const AMD_MICROCODE_HEADER*)currentPos; + if (FALSE == microcodeHeaderValidAmd(ucodeHeader)) { + continue; + } + + if (readUnaligned(currentPos + 0x40) == 0x00000000) { + continue; + } + + UINT32 microcodeLen = getSizeMicrocodeAmd(ucodeHeader); + if (microcodeLen == 0 || restSize < microcodeLen) { + continue; + } + + nextItemType = Subtypes::AmdMicrocode; + nextItemSize = microcodeLen; + nextItemAlternativeSize = microcodeLen; + nextItemOffset = offset; + break; + } } // No more stores found @@ -3748,6 +3839,10 @@ USTATUS FfsParser::parsePeImageSectionBody(const UModelIndex & index, const bool } model->addInfo(index, info); + + // Search for AMD Microcode in certain PE images + searchForAmdMicrocode(index); + return U_SUCCESS; } @@ -3798,7 +3893,10 @@ USTATUS FfsParser::parseTeImageSectionBody(const UModelIndex & index, const bool // Add TE info model->addInfo(index, info); - + + // Search for AMD Microcode in certain TE images + searchForAmdMicrocode(index); + return U_SUCCESS; } @@ -4686,6 +4784,89 @@ USTATUS FfsParser::parseIntelMicrocodeHeader(const UByteArray & microcode, const return U_SUCCESS; } +USTATUS FfsParser::searchForAmdMicrocode(const UModelIndex &index) { + UModelIndex fileIndex = model->parent(index); + if (!fileIndex.isValid()) { + return U_INVALID_PARAMETER; + } + + if (model->type(fileIndex) != Types::File) { + return U_INVALID_PARAMETER; + } + + UByteArray fileHeader = model->header(fileIndex); + if (fileHeader.size() < sizeof(EFI_GUID)) { + return U_INVALID_PARAMETER; + } + + USTATUS status = U_ITEM_NOT_FOUND; + + UByteArray fileGuid = UByteArray(fileHeader.constData(), sizeof(EFI_GUID)); + if (fileGuid != AMD_MICROCODE_FILE_GUID) { + return status; + } + + UByteArray body = model->body(index); + UINT32 dataSize = body.size(); + UINT32 minSize = sizeof(AMD_MICROCODE_HEADER) + 0x44; + if (dataSize < minSize) { + return U_INVALID_PARAMETER; + } + + for (UINT32 offset = 0; offset < dataSize - minSize; offset++) { + const AMD_MICROCODE_HEADER* ucodeHeader = (const AMD_MICROCODE_HEADER*)(body.constData() + offset); + if (!microcodeHeaderValidAmd(ucodeHeader)) { + continue; + } + + UModelIndex microcodeIndex; + if (parseAmdMicrocodeHeader(body.mid(offset), offset, index.parent(), microcodeIndex) == U_SUCCESS) { + // we might find several microcode items in body, so keep scanning + msg(usprintf("%s: found microcode in PE/TE file", __FUNCTION__), microcodeIndex); + status = U_SUCCESS; + } + } + + return status; +} + +USTATUS FfsParser::parseAmdMicrocodeHeader(const UByteArray & microcode, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index) +{ + // We have enough data to fit the header + if ((UINT32)microcode.size() < sizeof(AMD_MICROCODE_HEADER)) { + return U_INVALID_MICROCODE; + } + + const AMD_MICROCODE_HEADER* ucodeHeader = (const AMD_MICROCODE_HEADER*)microcode.constData(); + + if (!microcodeHeaderValidAmd(ucodeHeader)) { + return U_INVALID_MICROCODE; + } + + // We have enough data to fit the whole TotalSize + UINT32 microcodeSize = getSizeMicrocodeAmd(ucodeHeader); + if ((UINT32)microcode.size() < microcodeSize) { + return U_INVALID_MICROCODE; + } + + // Add info + UString name("AMD microcode"); + UString info = usprintf("Full size: %Xh (%u)\n" + "Date: %02X.%02X.%04x\nCPU signature: %08Xh\nRevision: %08Xh\n", + (UINT32)microcodeSize, (UINT32)microcodeSize, + getDayMicrocodeAmd(ucodeHeader), + getMonthMicrocodeAmd(ucodeHeader), + getYearMicrocodeAmd(ucodeHeader), + ucodeHeader->ProcessorSignature, + ucodeHeader->UpdateRevision); + + // Add tree item + index = model->addItem(localOffset, Types::Microcode, Subtypes::AmdMicrocode, name, UString(), info, UByteArray(), microcode, UByteArray(), Fixed, parent); + + // No need to parse the body further for now + return U_SUCCESS; +} + USTATUS FfsParser::parseBpdtRegion(const UByteArray & region, const UINT32 localOffset, const UINT32 sbpdtOffsetFixup, const UModelIndex & parent, UModelIndex & index) { UINT32 regionSize = (UINT32)region.size(); diff --git a/common/ffsparser.h b/common/ffsparser.h index 30d606495..b84c61950 100644 --- a/common/ffsparser.h +++ b/common/ffsparser.h @@ -20,6 +20,7 @@ WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include "ubytearray.h" #include "treemodel.h" #include "intel_microcode.h" +#include "amd_microcode.h" #include "descriptor.h" #include "ffs.h" #include "fitparser.h" @@ -205,6 +206,10 @@ class FfsParser USTATUS parseIntelMicrocodeHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index); bool microcodeHeaderValid(const INTEL_MICROCODE_HEADER* ucodeHeader); + USTATUS searchForAmdMicrocode(const UModelIndex & index); + USTATUS parseAmdMicrocodeHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index); + bool microcodeHeaderValidAmd(const AMD_MICROCODE_HEADER* ucodeHeader); + USTATUS parseVendorHashFile(const UByteArray & fileGuid, const UModelIndex & index); // AMD specific diff --git a/fuzzing/CMakeLists.txt b/fuzzing/CMakeLists.txt index fec08f265..2d361a1d4 100644 --- a/fuzzing/CMakeLists.txt +++ b/fuzzing/CMakeLists.txt @@ -19,6 +19,7 @@ SET(PROJECT_SOURCES ../common/nvramparser.cpp ../common/meparser.cpp ../common/ffsparser.cpp + ../common/amd_microcode.cpp ../common/fitparser.cpp ../common/peimage.cpp ../common/treeitem.cpp From 134a93be6a7bd739a355a0e6535861d724bbcd15 Mon Sep 17 00:00:00 2001 From: Fabio Pagani Date: Wed, 25 Feb 2026 14:11:41 +0100 Subject: [PATCH 2/2] add amd_microcode.cpp to meson.build --- common/meson.build | 1 + 1 file changed, 1 insertion(+) diff --git a/common/meson.build b/common/meson.build index 9bb76c699..3db37afdc 100644 --- a/common/meson.build +++ b/common/meson.build @@ -43,6 +43,7 @@ uefitoolcommon = static_library('uefitoolcommon', 'meparser.cpp', 'fitparser.cpp', 'ffsparser.cpp', + 'amd_microcode.cpp', 'ffsreport.cpp', 'peimage.cpp', 'treeitem.cpp',