Parsing the DTB early on using a handcoded assembly routine is a pointless waste of brain cycles, since the UEFI firmware always executes from RAM under Xen. So instead, set up a temporary stack in the memory region at the beginning of the image, and use the libfdt C library.
Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Ard Biesheuvel <ard.biesheu...@linaro.org> Acked-by: Laszlo Ersek <ler...@redhat.com> --- ArmVirtPkg/Library/ArmXenRelocatablePlatformLib/AARCH64/MemnodeParser.S | 237 -------------------- ArmVirtPkg/Library/ArmXenRelocatablePlatformLib/AARCH64/RelocatableVirtHelper.S | 51 ++--- ArmVirtPkg/Library/ArmXenRelocatablePlatformLib/ArmXenRelocatablePlatformLib.inf | 3 +- ArmVirtPkg/Library/ArmXenRelocatablePlatformLib/FdtParser.c | 91 ++++++++ 4 files changed, 113 insertions(+), 269 deletions(-) diff --git a/ArmVirtPkg/Library/ArmXenRelocatablePlatformLib/AARCH64/MemnodeParser.S b/ArmVirtPkg/Library/ArmXenRelocatablePlatformLib/AARCH64/MemnodeParser.S deleted file mode 100644 index 6eef9d7667e3..000000000000 --- a/ArmVirtPkg/Library/ArmXenRelocatablePlatformLib/AARCH64/MemnodeParser.S +++ /dev/null @@ -1,237 +0,0 @@ -/* - * Copyright (c) 2014, Linaro Ltd. All rights reserved. - * - * This program and the accompanying materials - * are licensed and made available under the terms and conditions of the BSD License - * which accompanies this distribution. The full text of the license may be found at - * http://opensource.org/licenses/bsd-license.php - * - * THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, - * WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. - */ - -/* - * Theory of operation - * ------------------- - * - * This code parses a Flattened Device Tree binary (DTB) to find the base of - * system RAM. It is written in assembly so that it can be executed before a - * stack has been set up. - * - * To find the base of system RAM, we have to traverse the FDT to find a memory - * node. In the context of this implementation, the first node that has a - * device_type property with the value 'memory' and a 'reg' property is - * acceptable, and the name of the node (memory[@xxx]) is ignored, as are any - * other nodes that match the above constraints. - * - * In pseudo code, this implementation does the following: - * - * for each node { - * have_device_type = false - * have_reg = false - * - * for each property { - * if property value == 'memory' { - * if property name == 'device_type' { - * have_device_type = true - * } - * } else { - * if property name == 'reg' { - * have_reg = true - * membase = property value[0] - * memsize = property value[1] - * } - * } - * } - * if have_device_type and have_reg { - * return membase and memsize - * } - * } - * return NOT_FOUND - */ - -#define FDT_MAGIC 0xedfe0dd0 - -#define FDT_BEGIN_NODE 0x1 -#define FDT_END_NODE 0x2 -#define FDT_PROP 0x3 -#define FDT_END 0x9 - - xMEMSIZE .req x0 // recorded system RAM size - xMEMBASE .req x1 // recorded system RAM base - - xLR .req x8 // our preserved link register - xDTP .req x9 // pointer to traverse the DT structure - xSTRTAB .req x10 // pointer to the DTB string table - xMEMNODE .req x11 // bit field to record found properties - -#define HAVE_REG 0x1 -#define HAVE_DEVICE_TYPE 0x2 - - .text - .align 3 -_memory: - .asciz "memory" -_reg: - .asciz "reg" -_device_type: - .asciz "device_type" - - /* - * Compare strings in x4 and x5, return in w7 - */ - .align 3 -strcmp: - ldrb w2, [x4], #1 - ldrb w3, [x5], #1 - subs w7, w2, w3 - cbz w2, 0f - cbz w3, 0f - beq strcmp -0: ret - - .globl find_memnode -find_memnode: - // preserve link register - mov xLR, x30 - mov xDTP, x0 - - /* - * Check the DTB magic at offset 0 - */ - movz w4, #(FDT_MAGIC & 0xffff) - movk w4, #(FDT_MAGIC >> 16), lsl #16 - ldr w5, [xDTP] - cmp w4, w5 - bne err_invalid_magic - - /* - * Read the string offset and store it for later use - */ - ldr w4, [xDTP, #12] - rev w4, w4 - add xSTRTAB, xDTP, x4 - - /* - * Read the struct offset and add it to the DT pointer - */ - ldr w5, [xDTP, #8] - rev w5, w5 - add xDTP, xDTP, x5 - - /* - * Check current tag for FDT_BEGIN_NODE - */ - ldr w5, [xDTP] - rev w5, w5 - cmp w5, #FDT_BEGIN_NODE - bne err_unexpected_begin_tag - -begin_node: - mov xMEMNODE, #0 - add xDTP, xDTP, #4 - - /* - * Advance xDTP past NULL terminated string - */ -0: ldrb w4, [xDTP], #1 - cbnz w4, 0b - -next_tag: - /* - * Align the DT pointer xDTP to the next 32-bit boundary - */ - add xDTP, xDTP, #3 - and xDTP, xDTP, #~3 - - /* - * Read the next tag, could be BEGIN_NODE, END_NODE, PROP, END - */ - ldr w5, [xDTP] - rev w5, w5 - cmp w5, #FDT_BEGIN_NODE - beq begin_node - cmp w5, #FDT_END_NODE - beq end_node - cmp w5, #FDT_PROP - beq prop_node - cmp w5, #FDT_END - beq err_end_of_fdt - b err_unexpected_tag - -prop_node: - /* - * If propname == 'reg', record as membase and memsize - * If propname == 'device_type' and value == 'memory', - * set the 'is_memnode' flag for this node - */ - ldr w6, [xDTP, #4] - add xDTP, xDTP, #12 - rev w6, w6 - mov x5, xDTP - adr x4, _memory - bl strcmp - - /* - * Get handle to property name - */ - ldr w5, [xDTP, #-4] - rev w5, w5 - add x5, xSTRTAB, x5 - - cbz w7, check_device_type - - /* - * Check for 'reg' property - */ - adr x4, _reg - bl strcmp - cbnz w7, inc_and_next_tag - - /* - * Extract two 64-bit quantities from the 'reg' property. These values - * will only be used if the node also turns out to have a device_type - * property with a value of 'memory'. - * - * NOTE: xDTP is only guaranteed to be 32 bit aligned, and we are most - * likely executing with the MMU off, so we cannot use 64 bit - * wide accesses here. - */ - ldp w4, w5, [xDTP] - orr xMEMBASE, x4, x5, lsl #32 - ldp w4, w5, [xDTP, #8] - orr xMEMSIZE, x4, x5, lsl #32 - rev xMEMBASE, xMEMBASE - rev xMEMSIZE, xMEMSIZE - orr xMEMNODE, xMEMNODE, #HAVE_REG - b inc_and_next_tag - -check_device_type: - /* - * Check whether the current property's name is 'device_type' - */ - adr x4, _device_type - bl strcmp - cbnz w7, inc_and_next_tag - orr xMEMNODE, xMEMNODE, #HAVE_DEVICE_TYPE - -inc_and_next_tag: - add xDTP, xDTP, x6 - b next_tag - -end_node: - /* - * Check for device_type = memory and reg = xxxx - * If we have both, we are done - */ - add xDTP, xDTP, #4 - cmp xMEMNODE, #(HAVE_REG | HAVE_DEVICE_TYPE) - bne next_tag - - ret xLR - -err_invalid_magic: -err_unexpected_begin_tag: -err_unexpected_tag: -err_end_of_fdt: - wfi diff --git a/ArmVirtPkg/Library/ArmXenRelocatablePlatformLib/AARCH64/RelocatableVirtHelper.S b/ArmVirtPkg/Library/ArmXenRelocatablePlatformLib/AARCH64/RelocatableVirtHelper.S index d6edc62efc0d..ae77492bf363 100644 --- a/ArmVirtPkg/Library/ArmXenRelocatablePlatformLib/AARCH64/RelocatableVirtHelper.S +++ b/ArmVirtPkg/Library/ArmXenRelocatablePlatformLib/AARCH64/RelocatableVirtHelper.S @@ -30,9 +30,6 @@ GCC_ASM_IMPORT(_gPcd_FixedAtBuild_PcdArmPrimaryCore) GCC_ASM_IMPORT(_gPcd_FixedAtBuild_PcdArmPrimaryCoreMask) GCC_ASM_IMPORT(_gPcd_FixedAtBuild_PcdCoreCount) -.LFdtMagic: - .byte 0xd0, 0x0d, 0xfe, 0xed - .LArm64LinuxMagic: .byte 0x41, 0x52, 0x4d, 0x64 @@ -43,17 +40,15 @@ GCC_ASM_IMPORT(_gPcd_FixedAtBuild_PcdCoreCount) // ); ASM_PFX(ArmPlatformPeiBootAction): mov x29, x30 // preserve LR + mov x28, x0 // preserve DTB pointer + mov x27, x1 // preserve base of image pointer // // If we are booting from RAM using the Linux kernel boot protocol, x0 will // point to the DTB image in memory. Otherwise, we are just coming out of - // reset, and x0 will be 0. Check also the FDT magic. + // reset, and x0 will be 0. // cbz x0, .Lout - ldr w8, .LFdtMagic - ldr w9, [x0] - cmp w8, w9 - bne .Lout // // The base of the runtime image has been preserved in x1. Check whether @@ -81,35 +76,29 @@ ASM_PFX(ArmPlatformPeiBootAction): str x7, [x9] // + // Discover the memory size and offset from the DTB, and record in the + // respective PCDs. This will also return false if a corrupt DTB is + // encountered. Since we are calling a C function, use the window at the + // beginning of the FD image as a temp stack. + // + adr x1, PcdGet64 (PcdSystemMemorySize) + adr x2, PcdGet64 (PcdSystemMemoryBase) + mov sp, x7 + bl FindMemnode + cbz x0, .Lout + + // // Copy the DTB to the slack space right after the 64 byte arm64/Linux style // image header at the base of this image (defined in the FDF), and record the // pointer in PcdDeviceTreeInitialBaseAddress. // adr x8, PcdGet64 (PcdDeviceTreeInitialBaseAddress) - add x1, x1, #0x40 - str x1, [x8] - - ldr w8, [x0, #4] // get DTB size (BE) - mov x9, x1 - rev w8, w8 - add x8, x8, x0 -0:ldp x6, x7, [x0], #16 - stp x6, x7, [x9], #16 - cmp x0, x8 - blt 0b - - // - // Discover the memory size and offset from the DTB, and record in the - // respective PCDs - // - mov x0, x1 - bl find_memnode // returns (size, base) size in (x0, x1) - cbz x0, .Lout + add x27, x27, #0x40 + str x27, [x8] - adr x8, PcdGet64 (PcdSystemMemorySize) - adr x9, PcdGet64 (PcdSystemMemoryBase) - str x0, [x8] - str x1, [x9] + mov x0, x27 + mov x1, x28 + bl CopyFdt .Lout: ret x29 diff --git a/ArmVirtPkg/Library/ArmXenRelocatablePlatformLib/ArmXenRelocatablePlatformLib.inf b/ArmVirtPkg/Library/ArmXenRelocatablePlatformLib/ArmXenRelocatablePlatformLib.inf index 1afab21576ac..fce3e11922bc 100644 --- a/ArmVirtPkg/Library/ArmXenRelocatablePlatformLib/ArmXenRelocatablePlatformLib.inf +++ b/ArmVirtPkg/Library/ArmXenRelocatablePlatformLib/ArmXenRelocatablePlatformLib.inf @@ -32,14 +32,15 @@ [LibraryClasses] IoLib ArmLib PrintLib + FdtLib [Sources.common] RelocatableVirt.c XenVirtMem.c + FdtParser.c [Sources.AARCH64] AARCH64/RelocatableVirtHelper.S - AARCH64/MemnodeParser.S [FeaturePcd] gEmbeddedTokenSpaceGuid.PcdCacheEnable diff --git a/ArmVirtPkg/Library/ArmXenRelocatablePlatformLib/FdtParser.c b/ArmVirtPkg/Library/ArmXenRelocatablePlatformLib/FdtParser.c new file mode 100644 index 000000000000..992932ee9754 --- /dev/null +++ b/ArmVirtPkg/Library/ArmXenRelocatablePlatformLib/FdtParser.c @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2015, Linaro Ltd. All rights reserved. + * + * This program and the accompanying materials + * are licensed and made available under the terms and conditions of the BSD License + * which accompanies this distribution. The full text of the license may be found at + * http://opensource.org/licenses/bsd-license.php + * + * THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + * WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + */ + +#include <Uefi.h> +#include <Include/libfdt.h> + +BOOLEAN +FindMemnode ( + IN VOID *DeviceTreeBlob, + OUT UINT64 *SystemMemoryBase, + OUT UINT64 *SystemMemorySize + ) +{ + INT32 MemoryNode; + INT32 AddressCells; + INT32 SizeCells; + INT32 Length; + CONST INT32 *Prop; + + if (fdt_check_header (DeviceTreeBlob) != 0) { + return FALSE; + } + + // + // Look for a node called "memory" at the lowest level of the tree + // + MemoryNode = fdt_path_offset (DeviceTreeBlob, "/memory"); + if (MemoryNode <= 0) { + return FALSE; + } + + // + // Retrieve the #address-cells and #size-cells properties + // from the root node, or use the default if not provided. + // + AddressCells = 1; + SizeCells = 1; + + Prop = fdt_getprop (DeviceTreeBlob, 0, "#address-cells", &Length); + if (Length == 4) { + AddressCells = fdt32_to_cpu (*Prop); + } + + Prop = fdt_getprop (DeviceTreeBlob, 0, "#size-cells", &Length); + if (Length == 4) { + SizeCells = fdt32_to_cpu (*Prop); + } + + // + // Now find the 'reg' property of the /memory node, and read the first + // range listed. + // + Prop = fdt_getprop (DeviceTreeBlob, MemoryNode, "reg", &Length); + + if (Length < (AddressCells + SizeCells) * sizeof (INT32)) { + return FALSE; + } + + if (AddressCells == 1) { + *SystemMemoryBase = fdt32_to_cpu (*Prop); + } else { + *SystemMemoryBase = fdt64_to_cpu (*(UINT64 *)Prop); + } + Prop += AddressCells; + + if (SizeCells == 1) { + *SystemMemorySize = fdt32_to_cpu (*Prop); + } else { + *SystemMemorySize = fdt64_to_cpu (*(UINT64 *)Prop); + } + + return TRUE; +} + +VOID +CopyFdt ( + IN VOID *FdtDest, + IN VOID *FdtSource + ) +{ + CopyMem (FdtDest, FdtSource, fdt_totalsize (FdtSource)); +} -- 2.5.0 _______________________________________________ edk2-devel mailing list edk2-devel@lists.01.org https://lists.01.org/mailman/listinfo/edk2-devel