From 6f7988e96e23cc30de9b397cbf6f4dfa9aa24ae1 Mon Sep 17 00:00:00 2001 From: Brandon Maier Date: Thu, 28 Dec 2023 11:32:13 -0600 Subject: [PATCH] patterns: Added Xilinx boot images (#210) * Adds pattern for Xilinx's Zynq UltraScale+ Boot Image format. * Boot images are generated by Xilinx's `bootgen` tool. * Spec: Xilinx UG1283 https://docs.xilinx.com/r/en-US/ug1283-bootgen-user-guide/Zynq-UltraScale-MPSoC-Boot-and-Configuration * Add test file generated as follows - Build bootgen tool from https://github.com/Xilinx/bootgen - Create dummy data with `dd if=/dev/zero of=image.bin count=1` - Create bif with `echo "img: {image.bin}" >image.bif` - Create test bin with `./bootgen -arch zynqmp -image image.bif -o xilinx_bootgen.hexpat.bin` --- README.md | 1 + patterns/xilinx_bootgen.hexpat | 374 +++++++++++++++++++++++ tests/patterns/xilinx_bootgen.hexpat.bin | Bin 0 -> 10752 bytes 3 files changed, 375 insertions(+) create mode 100644 patterns/xilinx_bootgen.hexpat create mode 100644 tests/patterns/xilinx_bootgen.hexpat.bin diff --git a/README.md b/README.md index 8bd39da..141c22c 100644 --- a/README.md +++ b/README.md @@ -115,6 +115,7 @@ Everything will immediately show up in ImHex's Content Store and gets bundled wi | XBEH | `audio/x-xbox-executable` | [`patterns/xbeh.hexpat`](patterns/xbeh.hexpat) | Xbox executable | | XCI | | [`patterns/xci.hexpat`](patterns/xci.hexpat) | Nintendo Switch XCI cardridge ROM | | Xilinx BIT | | [`patterns/xilinx_bit.hexpat`](patterns/xilinx_bit.hexpat) | Xilinx FPGA Bitstreams | +| Xilinx Bootgen | | [`patterns/xilinx_bootgen.hexpat`](patterns/xilinx_bootgen.hexpat) | Xilinx ZynqMP Boot Images | | ZIP | `application/zip` | [`patterns/zip.hexpat`](patterns/zip.hexpat) | End of Central Directory Header, Central Directory File Headers | | ZLIB | `application/zlib` | [`patterns/zlib.hexpat`](patterns/zlib.hexpat) | ZLIB compressed data format | | ZSTD | `application/zstd` | [`patterns/zstd.hexpat`](patterns/zstd.hexpat) | Zstandard compressed data format | diff --git a/patterns/xilinx_bootgen.hexpat b/patterns/xilinx_bootgen.hexpat new file mode 100644 index 0000000..d601f70 --- /dev/null +++ b/patterns/xilinx_bootgen.hexpat @@ -0,0 +1,374 @@ +#pragma endian little +#pragma description Xilinx Zynq UltraScale+ Boot Image +#pragma magic [ 58 4E 4C 58 ] @ 0x24 + +// Spec: Xilinx UG1283 https://docs.xilinx.com/r/en-US/ug1283-bootgen-user-guide/Zynq-UltraScale-MPSoC-Boot-and-Configuration + +#include +#include +#include +#include +#include + +fn format_u32_string(auto arr) { + str result = ""; + for (u32 i = 0, i < sizeof(arr) / 4, i = i + 1) { + result += char((arr[i] >> 24) & 0xff); + result += char((arr[i] >> 16) & 0xff); + result += char((arr[i] >> 8) & 0xff); + result += char((arr[i] ) & 0xff); + } + return result; +}; + +fn pointer_word(auto offset) { + return u128(offset) * 3; +}; + +// Based on std::ptr::NullablePtr +// Pointer addresses 4-byte words +struct NullableWordPtr { + PointerTy pointerValue [[no_unique_address, hidden]]; + if (pointerValue == 0x0) { + padding[sizeof(PointerTy)]; + } else { + PointeeTy *data : PointerTy [[pointer_base("pointer_word"),inline]]; + } +}; + +namespace zynqmp { + enum SpkUserEfuseSelect : u8 { + Spk = 0x01, + User = 0x10, + }; + + enum PpkKeySelect : u8 { + Ppk0 = 0, + Ppk1 = 1, + }; + + enum AcFormat : u8 { + Pkcs1v1_5 = 0, + }; + + enum AcVersion : u8 { + Current = 0, + }; + + enum PpkKeyType : u8 { + Hash = 0, + }; + + enum PpkKeySource : u8 { + Efuse = 0, + }; + + enum SpkEnable : u8 { + Disabled = 0, + Enabled = 1, + }; + + enum PublicStrength : u8 { + b2048 = 0, + b4096 = 1, + }; + + enum HashAlgo : u8 { + Sha3_384 = 1, + }; + + enum PublicAlgo : u8 { + Rsa = 1, + }; + + bitfield AuthenticationCertificationHeader { + PublicAlgo public_algorithm : 2; + HashAlgo hash_algorithm : 2; + PublicStrength public_strength : 4; + SpkEnable spk_enable : 1; + PpkKeySource ppk_key_source : 2; + PpkKeyType ppk_key_type : 1; + AcVersion authentication_certificate_version : 2; + AcFormat authentication_certificate_format : 2; + PpkKeySelect ppk_key_select : 2; + SpkUserEfuseSelect spk_user_efuse_select : 2; + padding : 12; + }; + + struct AuthenticationCertificates { + AuthenticationCertificationHeader authentication_header; + u32 spk_id; + u32 udf[14]; + u32 ppk_mod[128]; + u32 ppk_mod_ext[128]; + u32 ppk_exponent; + padding[60]; + u32 spk_mod[128]; + u32 spk_mod_ext[128]; + u32 spk_exponent; + padding[60]; + u32 spk_signature[128]; + u32 boot_header_signature[128]; + u32 partition_signature[128]; + }; + + enum VectorLocation : u8 { + Low = 0, + High = 1, + }; + + enum EarlyHandoff : u8 { + Disabled = 0, + Enabled = 1, + }; + + enum Endianness : u8 { + Little = 0, + Big = 1, + }; + + enum PartitionOwner : u8 { + Fsbl = 0, + UBoot = 1, + }; + + enum RsaCertPresent : u8 { + No = 0, + Yes = 1, + }; + + enum ChecksumType : u8 { + None = 0, + Md5 = 1, + Sha2 = 2, + Sha3 = 3, + }; + + enum DestCpu : u8 { + None = 0, + A53_0 = 1, + A53_1 = 2, + A53_2 = 3, + A53_3 = 4, + R5_0 = 5, + R5_1 = 6, + R5Lockstep = 7, + Pmu = 8, + }; + + enum EncryptionPresent : u8 { + No = 0, + Yes = 1, + }; + + enum DestDevice : u8 { + None = 0, + Ps = 1, + Pl = 2, + Pmu = 3, + Xip = 4, + }; + + enum A5xExecState : u8 { + Aarch64 = 0, + Aarch32 = 1, + }; + + enum ExceptionLevel : u8 { + El0 = 0, + El1 = 1, + El2 = 2, + El3 = 3, + }; + + enum TrustZone : u8 { + NonSecure = 0, + Secure = 1, + }; + + bitfield PartitionAttributeBits { + TrustZone trustzone : 1; + ExceptionLevel exception_level : 2; + A5xExecState a5x_exec_state : 1; + DestDevice destination_device : 3; + EncryptionPresent encryption_present : 1; + DestCpu destination_cpu : 4; + ChecksumType checksum_type : 3; + RsaCertPresent rsa_authentication_certificate_present : 1; + PartitionOwner partition_owner : 2; + Endianness endianness : 1; + EarlyHandoff early_handoff : 1; + padding : 3; + VectorLocation vector_location : 1; + padding : 8; + }; + + struct PartitionHeader { + u32 encrypted_partition_data_word_length; + u32 unencrypted_data_word_length; + u32 total_partition_word_length; + + NullableWordPtr next_partition_header_offset; + + u64 destination_execution_address; + u64 destination_load_address; + NullableWordPtr, u32> actual_partition_word_offset; + + PartitionAttributeBits attributes; + u32 section_count; + u32 checksum_word_offset; + u32 image_header_word_offset; + + NullableWordPtr ac_offset; + + u32 partition_number; + u32 header_checksum; + }; + + struct ImageHeader { + NullableWordPtr image_header; + + NullableWordPtr corresponding_partition_header; + + padding[4]; + u32 partition_count; + u32 image_name[] [[format("format_u32_string")]]; + }; + + enum BootDevice : u32 { + Same = 0, + Qspi32 = 1, + Qspi24 = 2, + Nand = 3, + Sd0 = 4, + Sd1 = 5, + Sdls = 6, + Mmc = 7, + Usb = 8, + Ethernet = 9, + PciE = 10, + Sata = 11, + }; + + struct ImageHeaderTable { + u32 version; + u32 count_of_image_header; + u32 first_partition_header_offset; + + NullableWordPtr image_header_table_offset; + + NullableWordPtr header_authentication_certificate; + + BootDevice secondary_boot_device; + padding[36]; + u32 checksum; + }; + + struct RegisterInitializationTablePair { + u32 address; + u32 value; + }; + + struct RegisterInitializationTable { + RegisterInitializationTablePair register_initialization_pairs[256]; + }; + + enum CpuSelect : u8 { + R5Single, + A53Single32Bit, + A53Single64Bit, + R5Dual, + }; + + enum HashingSelect : u8 { + NoIntegrityCheck = 0x0 ... 0x1, + Sha3IntegrityCheck = 0x3, + }; + + enum PufHdLocation : u8 { + Efuse = 0x0 ... 0x2, + BootHeader = 0x3, + }; + + enum BootHeaderRsa : u8 { + AuthDecidedByEfuse = 0x0 ... 0x2, + AuthEnabled = 0x3, + }; + + bitfield BootHeaderAttributeBits { + padding : 6; + PufHdLocation puf_hd : 2; + HashingSelect hashing_select : 2; + CpuSelect cpu_select : 2; + padding : 2; + BootHeaderRsa bhdr_rsa : 2; + padding : 16; + }; + + struct PufHelperData { + u32 data[384]; + u32 chash; + u16 aux; + padding[2]; + }; + + enum ArmVectorTable : u32 { + Cortex32Bit = 0xEAFFFFFE, + Cortex64Bit = 0x14000000, + }; + + enum KeySource : u32 { + Unencrypted = 0x00000000, + BlackKeyInEfuse = 0xA5C3C5A5, + ObfuscatedKeyInEfuse = 0xA5C3C5A7, + RedKeyInBbram = 0x3A5C3C5A, + EfuseRedKeyInEfuse = 0xA5C3C5A3, + ObfuscatedKeyInBootHeader = 0xA35C7CA5, + UserKeyInBootHeader = 0xA3A5C3C5, + BlackKeyInBootHeader = 0xA35C7C53, + }; + + struct BootHeader { + ArmVectorTable arm_vector_table[8]; + u32 width_detection_word; + type::Magic<"XNLX"> header_signature; + KeySource key_source; + u32 fsbl_execution_address; + u32 source_offset; + u32 pmu_image_length; + u32 total_pmu_fw_length; + u32 fsbl_image_length; + u32 total_fsbl_length; + BootHeaderAttributeBits fsbl_image_attributes; + u32 boot_header_checksum; + u32 obfuscated_black_key_storage[8]; + u32 shutter_value; + u32 user_defined_fields[10]; + + std::ptr::NullablePtr image_header_table_offset; + + u32 partition_header_table_offset; + u32 secure_header_iv[3]; + u32 obfuscated_black_key_iv[3]; + + if (fsbl_image_attributes.puf_hd == 0x3) { + PufHelperData puf_helper_data @ 0x8b8; + } + + if (source_offset > 0) { + if (pmu_image_length > 0) { + u8 pmu_firmware[total_pmu_fw_length] @ source_offset; + u8 fsbl[total_fsbl_length] @ source_offset + total_pmu_fw_length; + } else { + u8 fsbl[total_fsbl_length] @ source_offset; + } + } + }; + + struct DeviceBootImage { + BootHeader boot_header @ 0x0; + RegisterInitializationTable register_initialization_table @ 0xB8; + }; +} + +zynqmp::DeviceBootImage boot_image @ 0x00; diff --git a/tests/patterns/xilinx_bootgen.hexpat.bin b/tests/patterns/xilinx_bootgen.hexpat.bin new file mode 100644 index 0000000000000000000000000000000000000000..dd96cba32b97216410c26e1857bfee8226471da5 GIT binary patch literal 10752 zcmeI$F$%&k6oBE^;vjB#r`_^)-q&G|gDO~tIECAT*qK2CM>uxuM^Vb|HO4^_39Znb)}HoFIb00Iag zfB*t%37qvC?~7K$=!p!q)&ID7-(yVfZqFSKifc(rlBYoc0R#|0009ILKmY**5I_I{ V1Q0*~0R#~EC4sm<)nuhA@B%I>Ue5pk literal 0 HcmV?d00001