Device-trees are used to convey information about hardware to the operating system. Some of the properties are only known at boot time. (One example of such a property is the number of the boot hart on RISC-V systems.) Therefore the firmware applies fix-ups to the original device-tree. Some nodes and properties are added or altered. When using GRUB's device-tree command the same fix-ups have to be applied. The EFI Device Tree Fixup Protocol allows to pass the loaded device tree to the firmware for this purpose. The protocol can * add nodes and update properties * reserve memory according to the /reserved-memory node and the memory reservation block * install the device-tree as configuration table With the patch GRUB checks if the protocol is installed and invokes it if available. Signed-off-by: Heinrich Schuchardt --- grub-core/loader/efi/fdt.c | 35 ++++++++++++++++++++++++++++++++++- include/grub/efi/api.h | 22 ++++++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/grub-core/loader/efi/fdt.c b/grub-core/loader/efi/fdt.c index 57ee81686..58e95eb05 100644 --- a/grub-core/loader/efi/fdt.c +++ b/grub-core/loader/efi/fdt.c @@ -29,6 +29,7 @@ static void *loaded_fdt; static void *fdt; +static grub_efi_guid_t dt_fixup_guid = GRUB_EFI_DT_FIXUP_PROTOCOL_GUID; #define FDT_ADDR_CELLS_STRING "#address-cells" #define FDT_SIZE_CELLS_STRING "#size-cells" @@ -36,6 +37,38 @@ static void *fdt; sizeof (FDT_ADDR_CELLS_STRING) + \ sizeof (FDT_SIZE_CELLS_STRING)) +static void *grub_fdt_fixup (void) +{ + grub_efi_dt_fixup_t *dt_fixup_prot; + grub_efi_uintn_t size = 0; + grub_efi_status_t status; + void *fixup_fdt; + + dt_fixup_prot = grub_efi_locate_protocol (&dt_fixup_guid, 0); + if (! dt_fixup_prot) + return loaded_fdt; + + grub_dprintf ("linux", "EFI_DT_FIXUP_PROTOCOL available\n"); + + status = efi_call_4 (dt_fixup_prot->fixup, dt_fixup_prot, loaded_fdt, &size, + GRUB_EFI_DT_APPLY_FIXUPS | GRUB_EFI_DT_RESERVE_MEMORY); + if (status != GRUB_EFI_BUFFER_TOO_SMALL) + return loaded_fdt; + + fixup_fdt = grub_realloc (loaded_fdt, size); + if (!fixup_fdt) + return loaded_fdt; + loaded_fdt = fixup_fdt; + + status = efi_call_4 (dt_fixup_prot->fixup, dt_fixup_prot, loaded_fdt, &size, + GRUB_EFI_DT_APPLY_FIXUPS | GRUB_EFI_DT_RESERVE_MEMORY); + + if (status == GRUB_EFI_SUCCESS) + grub_dprintf ("linux", "Device tree fixed up via EFI_DT_FIXUP_PROTOCOL\n"); + + return loaded_fdt; +} + void * grub_fdt_load (grub_size_t additional_size) { @@ -49,7 +82,7 @@ grub_fdt_load (grub_size_t additional_size) } if (loaded_fdt) - raw_fdt = loaded_fdt; + raw_fdt = grub_fdt_fixup(); else raw_fdt = grub_efi_get_firmware_fdt(); diff --git a/include/grub/efi/api.h b/include/grub/efi/api.h index 34109861a..8101df0df 100644 --- a/include/grub/efi/api.h +++ b/include/grub/efi/api.h @@ -334,6 +334,11 @@ { 0x83, 0x0b, 0xd9, 0x15, 0x2c, 0x69, 0xaa, 0xe0 } \ } +#define GRUB_EFI_DT_FIXUP_PROTOCOL_GUID \ + { 0xe617d64c, 0xfe08, 0x46da, \ + { 0xf4, 0xdc, 0xbb, 0xd5, 0x87, 0x0c, 0x73, 0x00 } \ + } + #define GRUB_EFI_VENDOR_APPLE_GUID \ { 0x2B0585EB, 0xD8B8, 0x49A9, \ { 0x8B, 0x8C, 0xE2, 0x1B, 0x01, 0xAE, 0xF2, 0xB7 } \ @@ -1641,6 +1646,13 @@ enum GRUB_EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST = 0x10, }; +enum + { + GRUB_EFI_DT_APPLY_FIXUPS = 0x01, + GRUB_EFI_DT_RESERVE_MEMORY = 0x02, + GRUB_EFI_EFI_DT_INSTALL_TABLE = 0x04, + }; + struct grub_efi_simple_network { grub_uint64_t revision; @@ -1704,6 +1716,16 @@ struct grub_efi_block_io }; typedef struct grub_efi_block_io grub_efi_block_io_t; +struct grub_efi_dt_fixup +{ + grub_efi_uint64_t revision; + grub_efi_status_t (*fixup) (struct grub_efi_dt_fixup *this, + void *fdt, + grub_efi_uintn_t *buffer_size, + grub_uint32_t flags); +}; +typedef struct grub_efi_dt_fixup grub_efi_dt_fixup_t; + struct grub_efi_shim_lock_protocol { grub_efi_status_t (*verify) (void *buffer, grub_uint32_t size); -- 2.30.0