diff --git a/Kconfig b/Kconfig index e5de815d8cae..c8cfbf5d5de2 100644 --- a/Kconfig +++ b/Kconfig @@ -572,6 +572,7 @@ config BUILD_TARGET default "u-boot.itb" if !BINMAN && SPL_LOAD_FIT && (ARCH_ROCKCHIP || \ RISCV || ARCH_ZYNQMP) default "u-boot.kwb" if (ARCH_KIRKWOOD || ARMADA_32BIT) && !SPL + default "u-boot.mbn" if ARCH_SNAPDRAGON && QCOM_GENERATE_MBN help Some SoCs need special image types (e.g. U-Boot binary with a special header) as build targets. By defining diff --git a/Makefile b/Makefile index c6253670a94e..0239203290d4 100644 --- a/Makefile +++ b/Makefile @@ -2012,12 +2012,17 @@ u-boot.elf: u-boot.bin u-boot-elf.lds FORCE quiet_cmd_u-boot-spl-elf ?= LD $@ cmd_u-boot-spl-elf ?= $(LD) spl/u-boot-spl-elf.o -o $@ \ $(if $(CONFIG_SYS_BIG_ENDIAN),-EB,-EL) \ - -T u-boot-elf.lds --defsym=$(CONFIG_PLATFORM_ELFENTRY)=$(CONFIG_SPL_TEXT_BASE) \ + -T spl/u-boot-spl-elf.lds --defsym=$(CONFIG_PLATFORM_ELFENTRY)=$(CONFIG_SPL_TEXT_BASE) \ -Ttext=$(CONFIG_SPL_TEXT_BASE) -spl/u-boot-spl.elf: spl/u-boot-spl.bin u-boot-elf.lds +spl/u-boot-spl.elf: spl/u-boot-spl.bin spl/u-boot-spl-elf.lds FORCE $(Q)$(OBJCOPY) -I binary $(PLATFORM_ELFFLAGS) $< spl/u-boot-spl-elf.o $(call if_changed,u-boot-spl-elf) +SPL_REMAKE_ELF_LDSCRIPT := $(addprefix $(srctree)/,$(CONFIG_SPL_REMAKE_ELF_LDSCRIPT:"%"=%)) + +spl/u-boot-spl-elf.lds: $(SPL_REMAKE_ELF_LDSCRIPT) prepare FORCE + $(call if_changed_dep,cpp_lds) + u-boot-elf.lds: arch/u-boot-elf.lds prepare FORCE $(call if_changed_dep,cpp_lds) diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 8304c2ebb7f9..736a83acf13a 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1139,6 +1139,7 @@ config ARCH_SNAPDRAGON select DM_GPIO select DM_SERIAL select DM_RESET + select EVENT select POWER_DOMAIN select GPIO_EXTRA_HEADER select OF_CONTROL @@ -1149,10 +1150,39 @@ config ARCH_SNAPDRAGON select SAVE_PREV_BL_FDT_ADDR if !ENABLE_ARM_SOC_BOOT0_HOOK && !SPL select LINUX_KERNEL_IMAGE_HEADER if !ENABLE_ARM_SOC_BOOT0_HOOK && !SPL select SYSRESET - select SYSRESET_PSCI if !QCOM_SNAGBOOT_MODE + select SYSRESET_PSCI if !QCOM_SNAGBOOT_MODE || !SPL select ANDROID_BOOT_IMAGE_IGNORE_BLOB_ADDR select MMU_PGPROT select SUPPORT_SPL + + select OF_LIVE + select SPL_OF_LIVE if SPL + select ARMV8_SPL_EXCEPTION_VECTORS if SPL + select ENABLE_ARM_SOC_BOOT0_HOOK if SPL + select SPL_DM if SPL + select SPL_DM_GPIO if SPL + select SPL_DM_PMIC if SPL && DM_PMIC + select SPL_DM_USB_GADGET if SPL && USB + select SPL_ENV_SUPPORT if SPL + select SPL_EVENT if SPL + select SPL_GPIO if SPL + select SPL_HAS_BSS_LINKER_SECTION if SPL && SPL_SEPARATE_BSS + select SPL_LIBCOMMON_SUPPORT if SPL + select SPL_LIBDISK_SUPPORT if SPL + select SPL_LIBGENERIC_SUPPORT if SPL + select SPL_OF_CONTROL if SPL + select SPL_PINCONF if SPL + select SPL_PINCTRL if SPL + select SPL_PINCTRL_FULL if SPL + select SPL_PINCTRL_GENERIC if SPL + select SPL_PINCONF_RECURSIVE if SPL + select SPL_PINMUX if SPL + select SPL_SPMI if SPL + select SPL_SPMI_MSM if SPL + select SPL_SPRINTF if SPL + select SPL_STRTO if SPL + select SPL_USB_GADGET if SPL && USB + imply SPL_MMC if SPL imply OF_UPSTREAM imply CMD_DM imply DM_USB_GADGET diff --git a/arch/arm/dts/ipq5210-rdp504-u-boot.dtsi b/arch/arm/dts/ipq5210-rdp504-u-boot.dtsi index 5f19915047b5..5582de9bebb8 100644 --- a/arch/arm/dts/ipq5210-rdp504-u-boot.dtsi +++ b/arch/arm/dts/ipq5210-rdp504-u-boot.dtsi @@ -6,123 +6,7 @@ */ / { - /* Will be removed when SMEM parsing is updated */ - memory@80000000 { - bootph-all; - device_type = "memory"; - reg = <0x0 0x80000000 0x0 0x20000000>; - }; - - binman: binman { - description = "IPQ5210 boot loader"; - fit { - description = "IPQ5210 Boot Loader FIT"; - fit,fdt-list = "of-list"; - fit,external-offset = <0x0>; - - images { - @qcom-config-SEQ { - fit,operation = "split-elf"; - description = "QC Config"; - type = "qcom-config"; - arch = "arm64"; - os = "elf"; - compression = "none"; - fit,load; - fit,entry; - fit,data; - - qcom-config { - }; - }; - - @qcom-lib-SEQ { - fit,operation = "split-elf"; - description = "QC Lib"; - arch = "arm64"; - os = "elf"; - type = "qcom-lib"; - compression = "none"; - fit,load; - fit,entry; - fit,data; - - qcom-lib { - }; - }; - - @atf-SEQ { - fit,operation = "split-elf"; - description = "ARM Trusted Firmware"; - type = "atf_bl31"; - arch = "arm64"; - os = "arm-trusted-firmware"; - compression = "none"; - fit,load; - fit,entry; - fit,data; - - atf-bl31 { - }; - }; - - @qcom-appsbl-SEQ { - fit,operation = "split-elf"; - type = "qcom-appsbl"; - os = "U-Boot"; - arch = "arm64"; - compression = "none"; - fit,load; - fit,entry; - fit,data; - - qcom-appsbl { - }; - }; - - @tee-SEQ { - fit,operation = "split-elf"; - description = "TEE"; - type = "tee_os"; - arch = "arm64"; - os = "tee"; - compression = "none"; - fit,load; - fit,entry; - fit,data; - - tee-os { - }; - }; - }; - - configurations { - default = "pre-ddr"; - - pre-ddr { - description = "pre-ddr"; - loadables = "qcom-config-1", - "qcom-config-2", - "qcom-config-3", - "qcom-config-4", - "qcom-lib-1", - "qcom-lib-2", - "qcom-lib-3", - "qcom-lib-4"; - }; - - post-ddr { - description = "post-ddr"; - loadables = "atf-1", - "atf-2", - "atf-3", - "atf-4", - "atf-5", - "atf-6", - "qcom-appsbl-1", - "tee-1"; - }; - }; - }; + reserved-memory { + /delete-node/ bootloader@87800000; }; }; diff --git a/arch/arm/dts/lemans-evk-u-boot.dtsi b/arch/arm/dts/lemans-evk-u-boot.dtsi index fd4eaa2fec81..e07f43110cba 100644 --- a/arch/arm/dts/lemans-evk-u-boot.dtsi +++ b/arch/arm/dts/lemans-evk-u-boot.dtsi @@ -1,18 +1,20 @@ // SPDX-License-Identifier: BSD-3-Clause /* - * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * Copyright (c) 2026, Qualcomm Innovation Center, Inc. All rights reserved. */ / { - clocks { - /* Fixed clocks needed by GCC */ - xo_board_clk { - bootph-all; - }; - - sleep_clk { - bootph-all; - }; + /* Will be removed when bootloader updates later */ + memory@80000000 { + device_type = "memory"; + reg = <0x0 0x80000000 0x0 0x3ee00000>, + <0x0 0xc0000000 0x0 0x0fd00000>, + <0xD 0x00000000 0x2 0x54100000>, + <0xA 0x80000000 0x1 0x80000000>, + <0x9 0x00000000 0x1 0x80000000>, + <0x1 0x00000000 0x3 0x00000000>, + <0x0 0xd0000000 0x0 0x01900000>, + <0x0 0xd3500000 0x0 0x2cb00000>; }; /* IMEM boot cookie + TCSR HW version offsets */ @@ -27,40 +29,10 @@ }; }; -&ufs_mem_hc { - bootph-all; /* Enable UFS for SPL */ -}; - -&ufs_mem_phy { - bootph-all; -}; - -&gcc { - bootph-all; /* Enable GCC for SPL */ -}; - -/* RPMH clock controller - required by GCC */ -&rpmhcc { - bootph-all; -}; - -/* Apps RSC - parent of rpmhcc and rpmhpd */ -&apps_rsc { - bootph-all; -}; - -/* UART10 - console serial port */ -&uart10 { - bootph-all; -}; - -&tlmm { - bootph-all; /* Make TLMM GPIO controller available in SPL */ -}; - /* Enter Qualcomm EDL/download mode via "reset -edl". */ &{/psci} { reboot-mode { mode-edl = <0x80000000 0x00000001>; }; }; + diff --git a/arch/arm/dts/sdm845-shift-axolotl-u-boot.dtsi b/arch/arm/dts/sdm845-shift-axolotl-u-boot.dtsi new file mode 100644 index 000000000000..d41210ab23a2 --- /dev/null +++ b/arch/arm/dts/sdm845-shift-axolotl-u-boot.dtsi @@ -0,0 +1,2 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "sdm845-u-boot.dtsi" diff --git a/arch/arm/dts/sdm845-u-boot.dtsi b/arch/arm/dts/sdm845-u-boot.dtsi new file mode 100644 index 000000000000..b029264555b9 --- /dev/null +++ b/arch/arm/dts/sdm845-u-boot.dtsi @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0 +&gcc { + bootph-all; +}; + +&usb_1_hsphy { + bootph-all; +}; + +&usb_1_dwc3 { + bootph-all; +}; + +&rpmhcc { + bootph-all; +}; diff --git a/arch/arm/mach-snapdragon/Kconfig b/arch/arm/mach-snapdragon/Kconfig index 29567b66ff9a..b9c0362117a5 100644 --- a/arch/arm/mach-snapdragon/Kconfig +++ b/arch/arm/mach-snapdragon/Kconfig @@ -11,12 +11,12 @@ config SYS_VENDOR Based on this option board// will be used as the custom board directory. -config SYS_MALLOC_LEN - default 0x10000000 - config SYS_MALLOC_F_LEN default 0x2000 +config SPL_SYS_MALLOC_F + default y + config SPL_SYS_MALLOC_F_LEN default 0x2000 @@ -26,6 +26,13 @@ config SYS_MALLOC_LEN config LNX_KRNL_IMG_TEXT_OFFSET_BASE default 0x80000000 +config SPL_SHARES_INIT_SP_ADDR + # override the default from common/spl/Kconfig + default n + +config SPL_HAVE_INIT_STACK + default y + config SYS_BOARD string "Snapdragon SoCs based board" help @@ -42,20 +49,6 @@ config SYS_CONFIG_NAME Based on this option include/configs/.h header will be used for board configuration. -config SPL_QCOM_BOOT_FROM_PBL - bool "U-Boot SPL is loaded directly by PBL" - depends on SPL - default y - help - Enable this option when U-Boot SPL is loaded directly by PBL (Primary - Boot Loader). In this case, PBL passes boot parameters via r0 register - containing a pointer to pbl_shared_data structure. - - Disable this option when U-Boot SPL is loaded by XBL or another - intermediate bootloader (e.g., PBL->XBL->U-Boot SPL). In such cases, - the r0 register won't contain valid PBL shared data, and boot device - detection will use fallback mechanisms. - config QCOM_FIT_MULTIDTB bool "Enable FIT multi-DTB selection for Qualcomm platforms" depends on FIT @@ -97,14 +90,6 @@ config QCOM_EL2_GUNYAH_EXIT_SUPPORT endchoice -config SPL_WRAPPER_ELF - bool "Create wrapper ELF for applicable platforms" - depends on SPL - help - Some platforms embed the U-Boot SPL binary within an ELF as a segment. - Additional tools are used to convert this ELF into an image that is - usable for the boot ROM. - config QCOM_BOOT0_SNAGBOOT_MODE bool "boot0.h initialization for Snagboot mode" help @@ -133,5 +118,44 @@ config QCOM_SNAGBOOT_MODE Platform-specific requirements: - Set CONFIG_COUNTER_FREQUENCY to match your platform's timer - Configure CONFIG_TEXT_BASE for U-Boot load address +config QCOM_GENERATE_MBN + bool "Generate an MBN-compatible ELF binary" + help + Enable this if you intend to flash U-Boot as a first-stage bootloader. + The build system will generate a board-specific ELF file with the appropriate + MBN hash segments and test keys. + + New platforms can be added to tools/qcom/mkmbn/mkmbn.py if they aren't already + supported. + +config QCOM_TMEL_ELF + string "TME Elf to concatenate with U-Boot SPL MBN image" + depends on SPL && QCOM_GENERATE_MBN + help + Path of the TME Elf file to be concatenated to u-boot.mbn to create + boot rom expected multi-elf image + +choice + prompt "Qualcomm boot0.h workaround" + optional + help + While U-Boot on Qualcomm platforms doesn't generally need compile-time + adjustments based on the target SoC, workarounds in boot0.h can't + practically detect the SoC at runtime. Enable one of these workarounds + if you know you need it. + +config BOOT0_MSM8916_PSCI_WORKAROUND + bool "boot0.h workaround for buggy PSCI on the msm8916 SoC" + help + Select this if you are building U-Boot proper for an msm8916 board that + uses the buggy PSCI implementation. + +config BOOT0_SDM845_WORKAROUND + bool "boot0.h workaround for SPL on the sdm845 SoC" + depends on SPL + help + Select this if you are building U-Boot SPL for sdm845. + +endchoice endif diff --git a/arch/arm/mach-snapdragon/Makefile b/arch/arm/mach-snapdragon/Makefile index c80f63e33f65..20a3e40d0992 100644 --- a/arch/arm/mach-snapdragon/Makefile +++ b/arch/arm/mach-snapdragon/Makefile @@ -2,11 +2,19 @@ # # (C) Copyright 2015 Mateusz Kulikowski -ifndef CONFIG_XPL_BUILD -obj-y += board.o dram.o +obj-y += dram.o +obj-y += mem_map.o + +ifeq ($(CONFIG_SPL_BUILD),y) +obj-y += board_spl.o +obj-y += fit-handler.o +obj-y += pbl-shared-data.o +obj-y += qclib.o +else +obj-y += board.o obj-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += capsule_update.o +endif + obj-$(CONFIG_QCOM_FIT_MULTIDTB) += qcom_fit_multidtb.o obj-$(CONFIG_QCOM_HWDETECT) += qcom_hwdetect.o obj-$(CONFIG_OF_LIVE) += of_fixup.o -endif -obj-$(CONFIG_SPL_BUILD) += spl.o diff --git a/arch/arm/mach-snapdragon/board.c b/arch/arm/mach-snapdragon/board.c index fcfa367902ae..92c23b77b334 100644 --- a/arch/arm/mach-snapdragon/board.c +++ b/arch/arm/mach-snapdragon/board.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0+ /* * Common initialisation for Qualcomm Snapdragon boards. + * U-Boot proper only, see mem_map.c and dram.c for parts shared with SPL * * Copyright (c) 2024 Linaro Ltd. * Author: Casey Connolly @@ -9,40 +10,28 @@ #define LOG_CATEGORY LOGC_BOARD #define pr_fmt(fmt) "QCOM: " fmt -#include -#include -#include #include -#include #include -#include -#include -#include -#include -#include +#include #include #include #include #include #include -#include #include +#include #include #include #include #include -#include #include -#include #include #include -#include #include #include "qcom-priv.h" #include "qcom_fit_multidtb.h" -DECLARE_GLOBAL_DATA_PTR; enum qcom_boot_source qcom_boot_source __section(".data") = 0; enum qcom_memmap_source qcom_memmap_source __section(".data") = 0; @@ -53,10 +42,7 @@ enum qcom_memmap_source qcom_memmap_source __section(".data") = 0; DIV_ROUND_UP(CONFIG_EFI_PARTITION_ENTRIES_NUMBERS * GPT_ENTRY_SIZE, \ GPT_PTE_SECTOR_SIZE) -static struct mm_region rbx_mem_map[CONFIG_NR_DRAM_BANKS + 2] = { { 0 } }; - -struct mm_region *mem_map = rbx_mem_map; - +#if CONFIG_IS_ENABLED(SYSRESET_PSCI) static void show_psci_version(void) { struct arm_smccc_res res; @@ -104,6 +90,7 @@ static void qcom_psci_fixup(void *fdt) if (ret) log_err("Failed to delete /psci node: %d\n", ret); } +#endif /* We support booting U-Boot with an internal DT when running as a first-stage bootloader * or for supporting quirky devices where it's easier to leave the downstream DT in place @@ -163,49 +150,13 @@ int board_fdt_blob_setup(void **fdtp) ret = 0; } +#if CONFIG_IS_ENABLED(SYSRESET_PSCI) qcom_psci_fixup(*fdtp); +#endif return ret; } -/* - * Some Qualcomm boards require GPIO configuration when switching USB modes. - * Support setting this configuration via pinctrl state. - */ -int board_usb_init(int index, enum usb_init_type init) -{ - struct udevice *usb; - int ret = 0; - - /* USB device */ - ret = uclass_find_device_by_seq(UCLASS_USB, index, &usb); - if (ret) { - printf("Cannot find USB device\n"); - return ret; - } - - ret = dev_read_stringlist_search(usb, "pinctrl-names", - "device"); - /* No "device" pinctrl state, so just bail */ - if (ret < 0) - return 0; - - /* Select "default" or "device" pinctrl */ - switch (init) { - case USB_INIT_HOST: - pinctrl_select_state(usb, "default"); - break; - case USB_INIT_DEVICE: - pinctrl_select_state(usb, "device"); - break; - default: - debug("Unknown usb_init_type %d\n", init); - break; - } - - return 0; -} - /* * Some boards still need board specific init code, they can implement that by * overriding this function. @@ -218,7 +169,9 @@ void __weak qcom_board_init(void) int board_init(void) { +#if CONFIG_IS_ENABLED(SYSRESET_PSCI) show_psci_version(); +#endif /* * Default cache only covers 8 blocks, too small for the GPT * partition-entry array, so it's never cached and gets re-read @@ -527,200 +480,3 @@ int board_late_init(void) return 0; } - -static void build_mem_map(void) -{ - int i, j; - - /* - * Ensure the peripheral block is sized to correctly cover the address range - * up to the first memory bank. - * Don't map the first page to ensure that we actually trigger an abort on a - * null pointer access rather than just hanging. - * FIXME: we should probably split this into more precise regions - */ - mem_map[0].phys = 0x1000; - mem_map[0].virt = mem_map[0].phys; - mem_map[0].size = gd->dram[0].start - mem_map[0].phys; - mem_map[0].attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | - PTE_BLOCK_NON_SHARE | - PTE_BLOCK_PXN | PTE_BLOCK_UXN; - - for (i = 1, j = 0; i < ARRAY_SIZE(rbx_mem_map) - 1 && gd->dram[j].size; i++, j++) { - mem_map[i].phys = gd->dram[j].start; - mem_map[i].virt = mem_map[i].phys; - mem_map[i].size = gd->dram[j].size; - mem_map[i].attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) | \ - PTE_BLOCK_INNER_SHARE; - } - - mem_map[i].phys = UINT64_MAX; - mem_map[i].size = 0; - -#ifdef DEBUG - debug("Configured memory map:\n"); - for (i = 0; mem_map[i].size; i++) - debug(" 0x%016llx - 0x%016llx: entry %d\n", - mem_map[i].phys, mem_map[i].phys + mem_map[i].size, i); -#endif -} - -u64 get_page_table_size(void) -{ - return SZ_1M; -} - -struct mem_resource_attrs { - fdt_addr_t start; - fdt_addr_t size; - u64 attrs; -}; - -static int fdt_cmp_res(const void *v1, const void *v2) -{ - const struct mem_resource_attrs *res1 = v1, *res2 = v2; - - return res1->start - res2->start; -} - -#define N_RESERVED_REGIONS 64 - -/* Map and unmap reserved memory regions as appropriate. - * Mark all no-map regions as PTE_TYPE_FAULT to prevent speculative access. - * On some platforms this is enough to trigger a security violation and trap - * to EL3. - * Regions that may be accessed by drivers get mapped explicitly. - */ -static void configure_reserved_memory(void) -{ - static struct mem_resource_attrs res[N_RESERVED_REGIONS] = { 0 }; - int parent, rmem, count, i = 0; - phys_addr_t start; - size_t size; - u64 attrs; - - /* Some reserved nodes must be carved out, as the cache-prefetcher may otherwise - * attempt to access them, causing a security exception. - */ - parent = fdt_path_offset(gd->fdt_blob, "/reserved-memory"); - if (parent <= 0) { - log_err("No reserved memory regions found\n"); - return; - } - - /* Collect the reserved memory regions and appropriate attrs */ - fdt_for_each_subnode(rmem, gd->fdt_blob, parent) { - const fdt32_t *ptr; - attrs = PTE_TYPE_FAULT; - /* If the no-map property isn't set then the region is valid */ - if (!fdt_getprop(gd->fdt_blob, rmem, "no-map", NULL)) - attrs = PTE_TYPE_VALID | PTE_BLOCK_MEMTYPE(MT_NORMAL); - /* If the compatible property is set then this region may be accessed by drivers and should - * be marked valid too. */ - if (fdt_getprop(gd->fdt_blob, rmem, "compatible", NULL)) - attrs = PTE_TYPE_VALID | PTE_BLOCK_MEMTYPE(MT_NORMAL); - - if (i == N_RESERVED_REGIONS) { - log_err("Too many reserved regions!\n"); - break; - } - - /* Read the address and size out from the reg property. Doing this "properly" with - * fdt_get_resource() takes ~70ms on SDM845, but open-coding the happy path here - * takes <1ms... Oh the woes of no dcache. - */ - ptr = fdt_getprop(gd->fdt_blob, rmem, "reg", NULL); - if (ptr) { - /* Qualcomm devices use #address/size-cells = <2> but all reserved regions are within - * the 32-bit address space. So we can cheat here for speed. - */ - res[i].start = fdt32_to_cpu(ptr[1]); - res[i].size = fdt32_to_cpu(ptr[3]); - res[i].attrs = attrs; - i++; - } - } - - /* Sort the reserved memory regions by address */ - count = i; - qsort(res, count, sizeof(res[0]), fdt_cmp_res); - debug("Mapping %d regions!\n", count); - - /* Now set the right attributes for them. Often a lot of the regions are tightly packed together - * so we can optimise the number of calls to mmu_change_region_attr_nobreak() by combining adjacent - * regions. - */ - start = res[0].start; - size = res[0].size; - attrs = res[0].attrs; - /* For each region after the first one, either increase the `size` to eventually be mapped or - * map the region we have and start a new one, this allows us to reduce the number of calls to - * mmu_map_region(). The loop is therefore "lagging" behind by one iteration. */ - for (i = 1; i <= count; i++) { - /* If i == count we are done, just map the last region. If the last region is - * too far away or the attrs don't match then map the meta-region we have and - * start a new one. */ - if (i == count || start + size < res[i].start - SZ_8K || attrs != res[i].attrs) { - debug(" 0x%016llx - 0x%016llx: %s\n", - start, start + size, attrs == PTE_TYPE_FAULT ? "FAULT" : "VALID"); - /* No need to break-before-make since dcache is disabled */ - mmu_change_region_attr_nobreak(start, size, attrs); - /* We have now mapped all the regions */ - if (i == count) - break; - /* Start a new meta-region */ - start = res[i].start; - size = res[i].size; - attrs = res[i].attrs; - } else { - /* This region is next to (<8K) the previous one so combine them. - * Accounting for any small (<8K) gap. */ - size = (res[i].start - start) + res[i].size; - } - } -} - -/* This function open-codes setup_all_pgtables() so that we can - * insert additional mappings *before* turning on the MMU. - */ -void enable_caches(void) -{ - u64 tlb_addr = gd->arch.tlb_addr; - u64 tlb_size = gd->arch.tlb_size; - u64 pt_size; - ulong carveout_start; - - gd->arch.tlb_fillptr = tlb_addr; - - build_mem_map(); - - icache_enable(); - - /* Create normal system page tables */ - setup_pgtables(); - - pt_size = (uintptr_t)gd->arch.tlb_fillptr - - (uintptr_t)gd->arch.tlb_addr; - debug("Primary pagetable size: %lluKiB\n", pt_size / 1024); - - /* Create emergency page tables */ - gd->arch.tlb_size -= pt_size; - gd->arch.tlb_addr = gd->arch.tlb_fillptr; - setup_pgtables(); - gd->arch.tlb_emerg = gd->arch.tlb_addr; - gd->arch.tlb_addr = tlb_addr; - gd->arch.tlb_size = tlb_size; - - /* - * On some boards speculative access may trigger a NOC or XPU violation so explicitly mark - * reserved regions as inacessible (PTE_TYPE_FAULT) - */ - if (qcom_memmap_source == QCOM_MEMMAP_SOURCE_SMEM || - fdt_node_check_compatible(gd->fdt_blob, 0, "qcom,qcs404") == 0) { - carveout_start = get_timer(0); - /* Takes ~20-50ms on SDM845 */ - configure_reserved_memory(); - debug("carveout time: %lums\n", get_timer(carveout_start)); - } - dcache_enable(); -} diff --git a/arch/arm/mach-snapdragon/board_spl.c b/arch/arm/mach-snapdragon/board_spl.c new file mode 100644 index 000000000000..3293eab8f81d --- /dev/null +++ b/arch/arm/mach-snapdragon/board_spl.c @@ -0,0 +1,275 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Common SPL code for Qualcomm Snapdragon boards. + * + * Copyright (c) 2026 Michael Srba + */ + +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +/* in SPL, we always use internal DT */ +int __weak board_fdt_blob_setup(void **fdtp) +{ + return -EEXIST; +} + +__weak void reset_cpu(void) +{ + /* This should currently not get called in non-error paths, so just hang */ + printf("reset_cpu called, going to hang()\n"); + hang(); +} + +#if IS_ENABLED(CONFIG_SPL_SMEM) +/** + * qcom_spl_populate_smem() - Populate shared memory (SMEM) information. + * @ctx: Pointer to the global SPL context. + * + * This function initializes and populates various SMEM items with boot-related + * information, such as flash type. + * Return: 0 on success, or a negative error code on failure. + */ +static int qcom_spl_populate_smem(void *ctx) +{ + int ret; + size_t size; + struct udevice *smem; + u32 *fltype; + + ret = qcom_smem_init(); + if (ret) { + pr_err("Failed init SMEM (%d)\n", ret); + return ret; + } + + size = sizeof(u32); + + fltype = (u32 *)smem_get(-1, SMEM_BOOT_FLASH_TYPE, &size); + if (!fltype) { + pr_err("Failed to get item: SMEM_BOOT_FLASH_TYPE\n"); + return -ENOENT; + } + + if (IS_ENABLED(CONFIG_SPL_MMC)) { + *fltype = SMEM_BOOT_MMC_FLASH; + wmb(); + return 0; + } + + pr_err("Boot medium not specified\n"); + + return -ENOENT; +} +#endif /* IS_ENABLED(CONFIG_SPL_SMEM) */ + +#if CONFIG_IS_ENABLED(MMC) + +#define QCOM_SPL_FIT_IMG_PARTITION "0:BOOTLDR" + +/** + * spl_mmc_boot_mode() - Determine the boot mode for MMC + * @mmc: Pointer to the MMC device + * @boot_device: Boot device ID + * + * Return: MMCSD_MODE_RAW to use raw partition access + */ +u32 spl_mmc_boot_mode(struct mmc *mmc, const u32 boot_device) +{ + return MMCSD_MODE_RAW; +} + +/** + * spl_mmc_boot_partition() - Determine which partition to boot from + * @boot_device: Boot device ID + * + * Return: Partition number to boot from, or default partition on error + */ +int __weak spl_mmc_boot_partition(const u32 boot_device) +{ + int p_no; + struct blk_desc *desc; + struct disk_partition info; + + desc = blk_get_devnum_by_uclass_id(UCLASS_MMC, 0); + if (!desc) { + pr_err("%s: Block device not found\n", __func__); + return -ENODEV; + } + + p_no = part_get_info_by_name(desc, QCOM_SPL_FIT_IMG_PARTITION, &info); + if (p_no < 0) { + pr_err("Partition " QCOM_SPL_FIT_IMG_PARTITION " not found\n"); + return -ENOENT; + } + + pr_debug("Found " QCOM_SPL_FIT_IMG_PARTITION " at %d\n", p_no); + + if (p_no < 0) { + printf("Using default MMC partition %d\n", + CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_PARTITION); + return CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_PARTITION; + } + + return p_no; +} + +unsigned long spl_mmc_get_uboot_raw_sector(struct mmc *mmc, ulong raw_sect) +{ + return 0; +} +#endif /* CONFIG_IS_ENABLED(MMC) */ + +void qcom_spl_malloc_init_f(void) +{ + if (!CONFIG_IS_ENABLED(SYS_MALLOC_F)) + return; + /* + * Set up by crt0.S + */ + assert(gd->malloc_base); + gd->malloc_limit = CONFIG_VAL(SYS_MALLOC_F_LEN); + gd->malloc_ptr = 0; + + mem_malloc_init(gd->malloc_base, gd->malloc_limit); + gd->flags |= GD_FLG_FULL_MALLOC_INIT; +} + +/** + * spl_get_load_buffer() - Allocate a cache-aligned buffer for image loading. + * @offset: Offset (unused, typically 0 for SPL). + * @size: Size of the buffer to allocate. + * + * Return: Pointer to the allocated buffer, or NULL on failure. + */ +struct legacy_img_hdr *spl_get_load_buffer(ssize_t offset, size_t size) +{ +#ifdef CONFIG_SPL_LOAD_FIT_ADDRESS + return (void *)CONFIG_SPL_LOAD_FIT_ADDRESS; +#else + return NULL; +#endif +} + +/** + * board_spl_fit_buffer_addr() - Get the address of the FIT image buffer. + * @fit_size: Size of the FIT image. + * @sectors: Number of sectors. + * @bl_len: Block length. + * + * Return: Address of the FIT image buffer. + */ +void *board_spl_fit_buffer_addr(ulong fit_size, int sectors, int bl_len) +{ + return spl_get_load_buffer(0, sectors * bl_len); +} + +/** + * qcom_spl_loader_pre_ddr() - SPL loader for pre-DDR stage. + * @boot_device: Type of boot device. + * + * Return: 0 on success, or a negative error code on failure. + */ +int qcom_spl_loader_pre_ddr(u8 boot_device) +{ + struct spl_image_loader *loader, *drv; + struct spl_image_info spl_image = { 0 }; + struct spl_boot_device boot_dev = { .boot_device = boot_device, }; + int ret = -ENODEV, n_ents; + + drv = ll_entry_start(struct spl_image_loader, spl_image_loader); + n_ents = ll_entry_count(struct spl_image_loader, spl_image_loader); + + for (loader = drv; loader && (loader != drv + n_ents); loader++) { + if (boot_device != loader->boot_device) + continue; + + ret = loader->load_image(&spl_image, &boot_dev); + if (!ret) + break; + + printf("%s: Error: %d\n", __func__, ret); + } + + return ret; +} + +/** + * board_fit_config_name_match() - Select the FIT config for the current + * boot stage. + * @name: Candidate FIT config name. + * + * Generic default: match "pre-ddr" before DRAM is up, "post-ddr" after. + * A SoC with additional/different FIT configs should provide a strong + * override. + * + * Return: 0 on match, -EINVAL otherwise. + */ +int __weak board_fit_config_name_match(const char *name) +{ + if (!(gd->flags & GD_FLG_SPL_INIT)) { + if (!strcmp(name, "pre-ddr")) { + printf("Selected FIT Config: %s\n", name); + return 0; + } + } else { + if (!strcmp(name, "post-ddr")) { + printf("Selected FIT Config: %s\n", name); + return 0; + } + } + + return -EINVAL; +} + +/** + * board_init_f() - Generic SPL entry point. + * @dummy: Unused. + * + * Generic default: clear BSS, set up malloc, run early init, load the + * pre-DDR image and invoke QCLIB when booting from PBL, then hand off to + * board_init_r(). A SoC with additional bring-up steps should provide a + * strong override. + */ +void __weak board_init_f(ulong dummy) +{ + int ret = 0; + + memset(__bss_start, 0, __bss_end - __bss_start); /* Clear BSS */ + + qcom_spl_malloc_init_f(); + + ret = spl_early_init(); + if (ret) { + pr_debug("spl_early_init() failed (%d)\n", ret); + goto fail; + } + + event_notify_null(EVT_LAST_STAGE_INIT); + + preloader_console_init(); + + ret = qcom_spl_loader_pre_ddr(spl_boot_device()); + if (ret) { + pr_debug("qcom_spl_loader_pre_ddr() failed (%d)\n", ret); + goto fail; + } + + ret = qcom_spl_invoke_qclib(); + if (ret) { + pr_debug("qcom_spl_invoke_qclib() failed (%d)\n", ret); + goto fail; + } + + board_init_r(NULL, 0); + +fail: + if (ret) + reset_cpu(); +} diff --git a/arch/arm/mach-snapdragon/fit-handler.c b/arch/arm/mach-snapdragon/fit-handler.c new file mode 100644 index 000000000000..fc419bd3f008 --- /dev/null +++ b/arch/arm/mach-snapdragon/fit-handler.c @@ -0,0 +1,131 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ +#include +#include +#include +#include +#include +#include +#include + +/** + * qcom_spl_get_fit_img_entry_point() - Get entry point from FIT image node. + * @fit: Pointer to the FIT image blob. + * @node: Node ID within the FIT image. + * @entry_point: Pointer to store the retrieved entry point. + * + * Return: 0 on success, or a negative error code on failure. + */ +int qcom_spl_get_fit_img_entry_point(void *fit, int node, + u64 *entry_point) +{ + int ret; + + if (!fit) { + pr_err("FIT image blob is NULL\n"); + return -EINVAL; + } + if (node <= 0) { + pr_err("Invalid FIT node ID %d\n", node); + return -EINVAL; + } + if (!entry_point) { + pr_err("Entry point pointer is NULL\n"); + return -EINVAL; + } + + ret = fit_image_get_entry(fit, node, (ulong *)entry_point); + if (ret) { + pr_debug("No entry point for node %d, trying load address\n", + node); + ret = fit_image_get_load(fit, node, (ulong *)entry_point); + if (ret) + pr_err("No load address for node %d (%d)\n", node, ret); + } + + return ret; +} + +/** + * qcom_spl_get_iftbl_entry_by_name() - Get an interface table entry by name. + * @if_tbl: Pointer to the QCLIB interface table. + * @name: Name of the entry to find. + * @entry: Pointer to a buffer where the found entry will be copied. + * + * Return: 0 on success, or a negative error code on failure. + */ +int qcom_spl_get_iftbl_entry_by_name(struct interface_table *if_tbl, + char *name, + struct interface_table_entry *entry) +{ + uint uc_index; + + if (!if_tbl) { + pr_err("Invalid interface table\n"); + return -EINVAL; + } + if (!name) { + pr_err("Invalid name\n"); + return -EINVAL; + } + if (!entry) { + pr_err("Invalid entry pointer\n"); + return -EINVAL; + } + + for (uc_index = 0; uc_index < if_tbl->num_entries; uc_index++) { + if (!strcmp(if_tbl->if_table_entries[uc_index].entry_name, name)) { + memcpy(entry, + &if_tbl->if_table_entries[uc_index], + sizeof(struct interface_table_entry)); + return 0; + } + } + pr_err("Interface table entry '%s' not found\n", name); + + return -ENOENT; +} + +/** + * bl2_plat_get_bl31_params_v2() - Retrieve and fixup BL31 parameters. + * @bl32_entry: Entry point for BL32 (OP-TEE). + * @bl33_entry: Entry point for BL33 (U-Boot/kernel). + * @fdt_addr: Address of the Device Tree Blob (FDT). + * + * Return: Pointer to the populated BL31 parameters structure. + */ +struct bl_params *bl2_plat_get_bl31_params_v2(uintptr_t bl32_entry, + uintptr_t bl33_entry, + uintptr_t fdt_addr) +{ + struct bl_params *bl_params; + struct bl_params_node *node; + u64 qcsdi_address = qclib_get_qcsdi_address(); + + /* + * Populate the bl31 params with default values. + */ + bl_params = bl2_plat_get_bl31_params_v2_default(bl32_entry, bl33_entry, + fdt_addr); + + /* + * Fixup the bl31 params based on platform requirements. + */ + for_each_bl_params_node(bl_params, node) { + if (node->image_id == ATF_BL31_IMAGE_ID) { + /* + * Pass QCSDI address to BL31 via arg0 + * This address was populated by qcom_spl_invoke_qclib() + */ + if (qcsdi_address == 0) + pr_warn("QCSDI address not set, BL31 may not function correctly\n"); + + node->ep_info->args.arg0 = qcsdi_address; + pr_debug("Setting BL31 arg0 to QCSDI address: 0x%llx\n", qcsdi_address); + } + } + + return bl_params; +} diff --git a/arch/arm/mach-snapdragon/include/mach/boot0.h b/arch/arm/mach-snapdragon/include/mach/boot0.h index 99ab97881558..8fd9d3ea5d6b 100644 --- a/arch/arm/mach-snapdragon/include/mach/boot0.h +++ b/arch/arm/mach-snapdragon/include/mach/boot0.h @@ -1,7 +1,11 @@ /* SPDX-License-Identifier: GPL-2.0+ */ #if defined(CONFIG_SPL_BUILD) +#if defined(CONFIG_BOOT0_SDM845_WORKAROUND) +#include "sdm845_spl_boot0.h" +#else b reset +#endif #else #if defined(CONFIG_BOOT0_MSM8916_PSCI_WORKAROUND) #include "msm8916_boot0.h" diff --git a/arch/arm/mach-snapdragon/include/mach/qclib.h b/arch/arm/mach-snapdragon/include/mach/qclib.h new file mode 100644 index 000000000000..b44874c514e8 --- /dev/null +++ b/arch/arm/mach-snapdragon/include/mach/qclib.h @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __QCLIB_H__ +#define __QCLIB_H__ + +#include +#include + +#define MAGIC_KEY "QCLIB_CB" +#define MAX_ENTRIES 0xF +#define IF_TABLE_VERSION 0x1 +#define QCSDI "qcsdi" +#define QCLIB_LOG_BUFFER "qclib_log_buffer" + +/* interface_table.global_attributes bits */ +#define QCLIB_GA_ENABLE_UART_LOGGING BIT(0) + +/** + * struct interface_table_entry - Meta data for blobs in QCLIB interface + * @entry_name: Name of the data blob (e.g., "dcb_settings"). + * @address: Address of the data blob. + * @size: Size of the data blob. + * @attributes: Attributes for the blob (e.g., save to storage). + */ +struct interface_table_entry { + char entry_name[24]; + u64 address; + u32 size; + u32 attributes; +}; + +/** + * struct interface_table - QCLIB Interface table header + * @magic_key: Magic key for validation ("QCLIB_CB"). + * @version: Interface table version. + * @num_entries: Number of valid entries. + * @max_entries: Maximum allowable entries. + * @global_attributes: Flags for global attributes (e.g., SDI path). + * @reserved1: Reserved for future use. + * @reserved2: Reserved for future use. + * @if_table_entries: Array of interface table entries. + */ +struct interface_table { + char magic_key[8]; + u32 version; + u32 num_entries; + u32 max_entries; + u32 global_attributes; + u32 reserved1; + u32 reserved2; + struct interface_table_entry if_table_entries[MAX_ENTRIES]; +}; + +/* Exported by fit-handler.c */ +int qcom_spl_get_fit_img_entry_point(void *fit, int node, u64 *entry_point); +int qcom_spl_get_iftbl_entry_by_name(struct interface_table *if_tbl, + char *name, + struct interface_table_entry *entry); + +/* QCSDI address accessors, populated post-QCLIB, consumed by fit-handler.c */ +u64 qclib_get_qcsdi_address(void); +void qclib_set_qcsdi_address(u64 address); + +/* qclib_log_buffer address accessors, populated post-QCLIB */ +u64 qclib_get_log_buffer_entry(void); +void qclib_set_log_buffer_entry(u64 address); + +/* + * __weak SoC extension points. A board's spl-.c may provide a strong + * override for any of these; the defaults in qclib.c are all no-ops. + */ +bool qcom_spl_soc_check_dload_mode(void); +int qcom_spl_soc_pre_qclib_routine(void); +int qcom_spl_soc_qclib_override(struct interface_table *table, const void *fit, + int images_node); +int qcom_spl_soc_post_qclib_routine(struct interface_table *if_tbl); + +int qclib_populate_interface_table(struct interface_table *if_tbl, + const void *fit, int images_node); +int qcom_spl_invoke_qclib(void); + +#endif /* __QCLIB_H__ */ diff --git a/arch/arm/mach-snapdragon/include/mach/sdm845_spl_boot0.h b/arch/arm/mach-snapdragon/include/mach/sdm845_spl_boot0.h new file mode 100644 index 000000000000..11fe229ef56b --- /dev/null +++ b/arch/arm/mach-snapdragon/include/mach/sdm845_spl_boot0.h @@ -0,0 +1,121 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Workaround for non-qcom-signed code being entered in EL1 on sdm845 + * Copyright (C) 2026 Michael Srba + * + * This code uses an unintentional ownership enhancing feature in older builds of XBL_SEC + * in order to elevate our privileges to EL3 as soon as possible after a system reset. + * This allows for a very close approximation of a clean state. + * + * Do note that you still need to own the device in the sense that you control the code that + * XBL_SEC jumps to in EL1, which is sadly not a level of ownership commonly afforded to you + * by the device manufacturer. On such devices, CVE-2021-30327 could help, but it's not documented + * and there is no PoC available utilizing it + * + */ +#include + +#define SCM_SMC_FNID(s, c) ((((s) & 0xFF) << 8) | ((c) & 0xFF)) + +#define ARM_SMCCC_SIP32_FAST_CALL \ + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_32, ARM_SMCCC_OWNER_SIP, 0) + +/* same as with qcom's TZ */ +#define QCOM_SCM_SVC_MEM_DUMP 0x03 +/* unlike the TZ counterpart, in XBL_SEC this simply unlocks the XPUs */ +#define QCOM_SCM_MEM_DUMP_UNLOCK_SECURE_REGIONS 0x10 + +/* + * We put our payload in place of some SCM call, the important thing is that it's hopefully + * in a memory region that is not in cache. + * + * It would be cleaner to just put our code at the scm entry point in the vector table, + * however it seems that we can't force cache coherency from EL1 if EL3 doesn't have + * any reason to care about that. + */ +#define QCOM_SCM_SVC_DONOR 0x01 +#define QCOM_SCM_DONOR 0x16 +/* we replace the instructions at this address with a jump to the start of u-boot */ +/* NOTE: this address is specific to a particular XBL_SEC elf */ +#define XBL_SEC_DONOR_SCM_ADDR 0x146a0ce0 + +/* gnu as doesn't implement these useful pseudoinstructions */ +.macro movq Xn, imm + movz \Xn, \imm & 0xFFFF + movk \Xn, (\imm >> 16) & 0xFFFF, lsl 16 + movk \Xn, (\imm >> 32) & 0xFFFF, lsl 32 + movk \Xn, (\imm >> 48) & 0xFFFF, lsl 48 +.endm + +.macro movl Wn, imm + movz \Wn, \imm & 0xFFFF + movk \Wn, (\imm >> 16) & 0xFFFF, lsl 16 +.endm + +/* copy 32 bits to an address from a label */ +.macro copy32 addr, text_base, addrofval, offset + movl x0, \addr + add x0, x0, \offset + movq x1, \text_base + add x1, x1, \addrofval + add x1, x1, \offset + ldr w2, [x1] + str w2, [x0] + dc cvau, x0 // flush cache to RAM straight away, we need to do it by address anyway +.endm + +.macro copy_instructions addr, text_base, start_addr, num_bytes // num_bytes must be a multiple of 4 + mov x3, #0x0 // x0, x1 and w2 used by copy32 +1: + copy32 \addr, \text_base, \start_addr, x3 + add x3, x3, #0x4 // i+=4 + cmp x3, \num_bytes + blo 1b +.endm + + /* If we're already in EL3 for some reason, skip this whole thing */ + mrs x0, CurrentEL + cmp x0, #(3 << 2) /* EL3 */ + beq reset + + /* disable the mmu */ + mrs x0, sctlr_el1 + and x0, x0, #~(1 << 0) // CTRL_M + msr sctlr_el1, x0 + + mov x0, #ARM_SMCCC_SIP32_FAST_CALL + movk x0, #SCM_SMC_FNID(QCOM_SCM_SVC_MEM_DUMP, QCOM_SCM_MEM_DUMP_UNLOCK_SECURE_REGIONS) + mov x1, #0x0 /* no params */ + mov x6, #0x0 + + smc #0 /* unlock XBL_SEC code area for writing (assuming old enough XBL_SEC build) */ + + /* this will also flush the writes from cache */ + copy_instructions XBL_SEC_DONOR_SCM_ADDR, CONFIG_SPL_TEXT_BASE, el3_payload, #((el3_payload_end - el3_payload)) + + /* this probably doesn't affect EL3, but it doesn't hurt */ + dsb ish /* block until cache is flushed */ + ic iallu /* force re-fetch of our shiny new instructions */ + dsb ish /* block until invalidation is finished */ + isb sy /* unify here ? */ + + mov x0, #ARM_SMCCC_SIP32_FAST_CALL + movk x0, #SCM_SMC_FNID(QCOM_SCM_SVC_DONOR, QCOM_SCM_DONOR) + mov x1, #0x0 /* no params */ + smc #0 /* call the payload */ + +el3_ret_point: + b reset + +el3_payload: + /* disable the mmu for EL3 too */ + mrs x0, sctlr_el3 + and x0, x0, #~(1 << 0) // CTRL_M + msr sctlr_el3, x0 + isb + + /* jump back to our code, but now in EL3 */ + movl x0, CONFIG_SPL_TEXT_BASE + add x0, x0, (el3_ret_point - _start) + br x0 +el3_payload_end: diff --git a/arch/arm/mach-snapdragon/include/mach/spl.h b/arch/arm/mach-snapdragon/include/mach/spl.h new file mode 100644 index 000000000000..50cc8dc4e8f8 --- /dev/null +++ b/arch/arm/mach-snapdragon/include/mach/spl.h @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __SPL_H__ +#define __SPL_H__ + +void qcom_spl_malloc_init_f(void); +int qcom_spl_loader_pre_ddr(u8 boot_device); +void qcom_spl_error_handler(void *arg); + +#endif /* __SPL_H__ */ diff --git a/arch/arm/mach-snapdragon/mem_map.c b/arch/arm/mach-snapdragon/mem_map.c new file mode 100644 index 000000000000..17c18e5096a5 --- /dev/null +++ b/arch/arm/mach-snapdragon/mem_map.c @@ -0,0 +1,226 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Common initialisation for Qualcomm Snapdragon boards. + * + * Copyright (c) 2024 Linaro Ltd. + * Author: Casey Connolly + */ + +#define LOG_CATEGORY LOGC_BOARD +#define pr_fmt(fmt) "QCOM: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "qcom-priv.h" + +DECLARE_GLOBAL_DATA_PTR; + +static struct mm_region rbx_mem_map[CONFIG_NR_DRAM_BANKS + 2] = { { 0 } }; + +struct mm_region *mem_map = rbx_mem_map; + +static void build_mem_map(void) +{ + int i, j; + + /* + * Ensure the peripheral block is sized to correctly cover the address range + * up to the first memory bank. + * Don't map the first page to ensure that we actually trigger an abort on a + * null pointer access rather than just hanging. + * FIXME: we should probably split this into more precise regions + */ + mem_map[0].phys = 0x1000; + mem_map[0].virt = mem_map[0].phys; + mem_map[0].size = gd->dram[0].start - mem_map[0].phys; + mem_map[0].attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | + PTE_BLOCK_NON_SHARE | + PTE_BLOCK_PXN | PTE_BLOCK_UXN; + + for (i = 1, j = 0; i < ARRAY_SIZE(rbx_mem_map) - 1 && gd->dram[j].size; i++, j++) { + mem_map[i].phys = gd->dram[j].start; + mem_map[i].virt = mem_map[i].phys; + mem_map[i].size = gd->dram[j].size; + mem_map[i].attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) | \ + PTE_BLOCK_INNER_SHARE; + } + + mem_map[i].phys = UINT64_MAX; + mem_map[i].size = 0; + +#ifdef DEBUG + debug("Configured memory map:\n"); + for (i = 0; mem_map[i].size; i++) + debug(" 0x%016llx - 0x%016llx: entry %d\n", + mem_map[i].phys, mem_map[i].phys + mem_map[i].size, i); +#endif +} + +u64 get_page_table_size(void) +{ + return SZ_1M; +} + +struct mem_resource_attrs { + fdt_addr_t start; + fdt_addr_t size; + u64 attrs; +}; + +static int fdt_cmp_res(const void *v1, const void *v2) +{ + const struct mem_resource_attrs *res1 = v1, *res2 = v2; + + return res1->start - res2->start; +} + +#define N_RESERVED_REGIONS 64 + +/* Map and unmap reserved memory regions as appropriate. + * Mark all no-map regions as PTE_TYPE_FAULT to prevent speculative access. + * On some platforms this is enough to trigger a security violation and trap + * to EL3. + * Regions that may be accessed by drivers get mapped explicitly. + */ +static void configure_reserved_memory(void) +{ + static struct mem_resource_attrs res[N_RESERVED_REGIONS] = { 0 }; + int parent, rmem, count, i = 0; + phys_addr_t start; + size_t size; + u64 attrs; + + /* Some reserved nodes must be carved out, as the cache-prefetcher may otherwise + * attempt to access them, causing a security exception. + */ + parent = fdt_path_offset(gd->fdt_blob, "/reserved-memory"); + if (parent <= 0) { + log_err("No reserved memory regions found\n"); + return; + } + + /* Collect the reserved memory regions and appropriate attrs */ + fdt_for_each_subnode(rmem, gd->fdt_blob, parent) { + const fdt32_t *ptr; + attrs = PTE_TYPE_FAULT; + /* If the no-map property isn't set then the region is valid */ + if (!fdt_getprop(gd->fdt_blob, rmem, "no-map", NULL)) + attrs = PTE_TYPE_VALID | PTE_BLOCK_MEMTYPE(MT_NORMAL); + /* If the compatible property is set then this region may be accessed by drivers and should + * be marked valid too. */ + if (fdt_getprop(gd->fdt_blob, rmem, "compatible", NULL)) + attrs = PTE_TYPE_VALID | PTE_BLOCK_MEMTYPE(MT_NORMAL); + + if (i == N_RESERVED_REGIONS) { + log_err("Too many reserved regions!\n"); + break; + } + + /* Read the address and size out from the reg property. Doing this "properly" with + * fdt_get_resource() takes ~70ms on SDM845, but open-coding the happy path here + * takes <1ms... Oh the woes of no dcache. + */ + ptr = fdt_getprop(gd->fdt_blob, rmem, "reg", NULL); + if (ptr) { + /* Qualcomm devices use #address/size-cells = <2> but all reserved regions are within + * the 32-bit address space. So we can cheat here for speed. + */ + res[i].start = fdt32_to_cpu(ptr[1]); + res[i].size = fdt32_to_cpu(ptr[3]); + res[i].attrs = attrs; + i++; + } + } + + /* Sort the reserved memory regions by address */ + count = i; + qsort(res, count, sizeof(res[0]), fdt_cmp_res); + debug("Mapping %d regions!\n", count); + + /* Now set the right attributes for them. Often a lot of the regions are tightly packed together + * so we can optimise the number of calls to mmu_change_region_attr_nobreak() by combining adjacent + * regions. + */ + start = res[0].start; + size = res[0].size; + attrs = res[0].attrs; + /* For each region after the first one, either increase the `size` to eventually be mapped or + * map the region we have and start a new one, this allows us to reduce the number of calls to + * mmu_map_region(). The loop is therefore "lagging" behind by one iteration. */ + for (i = 1; i <= count; i++) { + /* If i == count we are done, just map the last region. If the last region is + * too far away or the attrs don't match then map the meta-region we have and + * start a new one. */ + if (i == count || start + size < res[i].start - SZ_8K || attrs != res[i].attrs) { + debug(" 0x%016llx - 0x%016llx: %s\n", + start, start + size, attrs == PTE_TYPE_FAULT ? "FAULT" : "VALID"); + /* No need to break-before-make since dcache is disabled */ + mmu_change_region_attr_nobreak(start, size, attrs); + /* We have now mapped all the regions */ + if (i == count) + break; + /* Start a new meta-region */ + start = res[i].start; + size = res[i].size; + attrs = res[i].attrs; + } else { + /* This region is next to (<8K) the previous one so combine them. + * Accounting for any small (<8K) gap. */ + size = (res[i].start - start) + res[i].size; + } + } +} + +/* This function open-codes setup_all_pgtables() so that we can + * insert additional mappings *before* turning on the MMU. + */ +void enable_caches(void) +{ + u64 tlb_addr = gd->arch.tlb_addr; + u64 tlb_size = gd->arch.tlb_size; + u64 pt_size; + ulong carveout_start; + + gd->arch.tlb_fillptr = tlb_addr; + + build_mem_map(); + + icache_enable(); + + /* Create normal system page tables */ + setup_pgtables(); + + pt_size = (uintptr_t)gd->arch.tlb_fillptr - + (uintptr_t)gd->arch.tlb_addr; + debug("Primary pagetable size: %lluKiB\n", pt_size / 1024); + + /* Create emergency page tables */ + gd->arch.tlb_size -= pt_size; + gd->arch.tlb_addr = gd->arch.tlb_fillptr; + setup_pgtables(); + gd->arch.tlb_emerg = gd->arch.tlb_addr; + gd->arch.tlb_addr = tlb_addr; + gd->arch.tlb_size = tlb_size; + + /* + * On some boards speculative access may trigger a NOC or XPU violation so explicitly mark + * reserved regions as inacessible (PTE_TYPE_FAULT) + */ + if (qcom_memmap_source == QCOM_MEMMAP_SOURCE_SMEM || + fdt_node_check_compatible(gd->fdt_blob, 0, "qcom,qcs404") == 0) { + carveout_start = get_timer(0); + /* Takes ~20-50ms on SDM845 */ + configure_reserved_memory(); + debug("carveout time: %lums\n", get_timer(carveout_start)); + } + dcache_enable(); +} \ No newline at end of file diff --git a/arch/arm/mach-snapdragon/of_fixup.c b/arch/arm/mach-snapdragon/of_fixup.c index 57f10d2f4428..4420b8fa8af6 100644 --- a/arch/arm/mach-snapdragon/of_fixup.c +++ b/arch/arm/mach-snapdragon/of_fixup.c @@ -281,7 +281,7 @@ static int qcom_of_fixup_nodes(void * __maybe_unused ctx, struct event *event) EVENT_SPY_FULL(EVT_OF_LIVE_BUILT, qcom_of_fixup_nodes); -int ft_board_setup(void *blob, struct bd_info __maybe_unused *bd) +int __weak ft_board_setup(void __maybe_unused *blob, struct bd_info __maybe_unused *bd) { struct device_node *uboot_parent_np, *uboot_node_np; int kernel_parent, ret; diff --git a/arch/arm/mach-snapdragon/pbl-shared-data.c b/arch/arm/mach-snapdragon/pbl-shared-data.c new file mode 100644 index 000000000000..b9dce22c4171 --- /dev/null +++ b/arch/arm/mach-snapdragon/pbl-shared-data.c @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include + +enum pbl_shared_data_param_id { + PSD_ID_PBL_FW_VERSION = 0x0, /* PBL firmware version */ + PSD_ID_PBL_PATCH_VERSION = 0x1, /* Patch version */ + PSD_ID_RMB_MBOX_BASE_ADDR = 0x2, /* Not used */ + PSD_ID_CPU_BOOT_SPEED_HZ = 0x3, /* CPU boot speed (Hz) */ + PSD_ID_BOOT_MEDIA_TYPE = 0x4, /* Boot media type */ + PSD_ID_IS_EDL_MODE = 0x5, /* Emergency Download mode */ + PSD_ID_DEV_PROG_ELF_ENTRY_ADDR = 0x6, /* Not used */ + PSD_ID_XBL_CONFIG_ELF_ENTRY_ADDR = 0x7, /* Not used */ + PSD_ID_XBL_SC_EXT_ELF_ENTRY_ADDR = 0x8, /* Not used */ + PSD_ID_PBL_TIMESTAMPS_BUFFER_ADDR = 0x9, /* PBL logs address */ + PSD_ID_PBL_TIMESTAMPS_BUFFER_SIZE = 0xa, /* PBL log size */ + PSD_ID_PBL_DEBUG_SHARED_INFO_ADDR = 0xb, /* Debug info address */ + PSD_ID_PBL_DEBUG_SHARED_INFO_SIZE = 0xc, /* Debug info size */ + PSD_ID_TME_CPU_PBL_ROM_BYPASS_FUSE = 0xd, /* Secure boot status */ + PSD_ID_XBL_SC_DEBUG_LOG_ADDR = 0xe, /* XBL SC debug log address */ + PSD_ID_XBL_SC_DEBUG_LOG_SIZE = 0xf, /* XBL SC debug log size */ + PSD_ID_CURRENT_IMAGE_SET = 0x10, /* Booted image set */ + PSD_ID_MEDIA_DATA_INFO_ADDR = 0x11, /* Media info pointer */ + PSD_ID_MEDIA_DATA_INFO_SIZE = 0x12, /* Media info size */ + PBL_SHARED_DATA_PARAM_MAX, +}; + +enum pbl_boot_flash_type { + PSD_NO_FLASH = 0, + PSD_NOR_FLASH = 1, + PSD_NAND_FLASH = 2, + PSD_ONENAND_FLASH = 3, + PSD_SDC_FLASH = 4, + PSD_MMC_FLASH = 5, + PSD_SPI_FLASH = 6, + PSD_PCIE_FLASH = 7, + PSD_UFS_FLASH = 8, + PSD_RSVD_1_FLASH = 9, + PSD_USB_FLASH = 10, + PSD_SPI_NAND_FLASH = 11, + PSD_SPI_FLASH_GPT = 12, +}; + +enum pbl_shared_data_version { + PSD_VERSION_1 = 0x00010000, // IPQ 5332, 9574 + PSD_VERSION_2 = 0x00020000, // IPQ 5210, 5424, 5610, 9650 +}; + +struct pbl_shared_data_entry { + u32 param_id; + ulong value; + bool valid; +}; + +struct pbl_shared_data { + u32 version; + u32 num_of_entries; + struct pbl_shared_data_entry entry[PBL_SHARED_DATA_PARAM_MAX]; +}; + +static struct pbl_shared_data g_psd __section(".data"); + +void save_boot_params(ulong r0, ulong r1, ulong r2, ulong r3) +{ + unsigned long sctlr; + struct pbl_shared_data *psd; + + sctlr = get_sctlr(); + set_sctlr(sctlr & ~(CR_M)); /* Disable MMU */ + + psd = (struct pbl_shared_data *)r0; + + if (!psd || psd->num_of_entries < PBL_SHARED_DATA_PARAM_MAX || + psd->version != PSD_VERSION_2) + goto out; + + memcpy(&g_psd, psd, sizeof(g_psd)); + +out: + save_boot_params_ret(); +} + +u32 __weak spl_boot_device(void) +{ + struct pbl_shared_data *psd = &g_psd; + +#ifdef DEBUG + for (int i = 0; psd && i < psd->num_of_entries; i++) { + printf("entry[0x%x] = %d 0x%08x %d\n", i, + psd->entry[i].param_id, psd->entry[i].value, + psd->entry[i].valid); + } +#endif + if (psd->version != PSD_VERSION_2) { + pr_err("Unknown PBL shared data version\n"); + goto out; + } + + if (psd->entry[PSD_ID_IS_EDL_MODE].valid && + psd->entry[PSD_ID_IS_EDL_MODE].value) { + printf("Selected boot device: DFU\n"); + return BOOT_DEVICE_DFU; + } + + if (psd->entry[PSD_ID_BOOT_MEDIA_TYPE].valid) { + switch (psd->entry[PSD_ID_BOOT_MEDIA_TYPE].value) { + case PSD_MMC_FLASH: + printf("Selected boot device: MMC\n"); + return BOOT_DEVICE_MMC1; + case PSD_NOR_FLASH: + printf("Selected boot device: NOR\n"); + return BOOT_DEVICE_NOR; + case PSD_NAND_FLASH: + printf("Selected boot device: NAND\n"); + return BOOT_DEVICE_NAND; + case PSD_UFS_FLASH: + printf("Selected boot device: UFS\n"); + return BOOT_DEVICE_UFS; + } + } + +out: + pr_err("No boot device configured\n"); + return BOOT_DEVICE_NONE; +} diff --git a/arch/arm/mach-snapdragon/qclib.c b/arch/arm/mach-snapdragon/qclib.c new file mode 100644 index 000000000000..7f25f37b5d92 --- /dev/null +++ b/arch/arm/mach-snapdragon/qclib.c @@ -0,0 +1,160 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * QCLIB interface-table protocol: generic, SoC-agnostic orchestration of the + * hand-off to the QCLIB firmware image, plus __weak extension points a + * per-board spl-.c may override to populate SoC-specific table entries. + */ + +#include +#include +#include +#include +#include +#include +#include + +static u64 g_qcsdi_address __section(".data"); +static u64 g_log_buffer_entry __section(".data"); + +u64 qclib_get_qcsdi_address(void) +{ + return g_qcsdi_address; +} + +void qclib_set_qcsdi_address(u64 addr) +{ + g_qcsdi_address = addr; +} + +u64 qclib_get_log_buffer_entry(void) +{ + return g_log_buffer_entry; +} + +void qclib_set_log_buffer_entry(u64 address) +{ + g_log_buffer_entry = address; +} + +bool __weak qcom_spl_soc_check_dload_mode(void) +{ + return false; +} + +int __weak qcom_spl_soc_pre_qclib_routine(void) +{ + return 0; +} + +int __weak qcom_spl_soc_qclib_override(struct interface_table *table, + const void *fit, int images_node) +{ + return 0; +} + +int __weak qcom_spl_soc_post_qclib_routine(struct interface_table *if_tbl) +{ + return 0; +} + +int __weak qclib_populate_interface_table(struct interface_table *if_tbl, + const void *fit, int images_node) +{ + struct interface_table_entry *log_buffer_entry; + + memset(if_tbl, 0, sizeof(struct interface_table)); + memcpy(if_tbl->magic_key, MAGIC_KEY, strlen(MAGIC_KEY)); + if_tbl->version = IF_TABLE_VERSION; + if_tbl->num_entries = 0; + if_tbl->max_entries = MAX_ENTRIES; + + /* + * Reserve the "qclib_log_buffer" slot — common to every SoC. + * QCLIB fills in the address itself; SPL just allocates the entry. + */ + log_buffer_entry = &if_tbl->if_table_entries[if_tbl->num_entries]; + memcpy(log_buffer_entry->entry_name, QCLIB_LOG_BUFFER, + strlen(QCLIB_LOG_BUFFER)); + log_buffer_entry->address = 0; + log_buffer_entry->size = 0; + log_buffer_entry->attributes = 0; + if_tbl->num_entries++; + + return qcom_spl_soc_qclib_override(if_tbl, fit, images_node); +} + +static int qclib_find_fit_nodes(const void *fit, int *images_node, + int *qclib_node) +{ + *images_node = fdt_subnode_offset(fit, 0, "images"); + if (*images_node < 0) { + pr_err("Failed to find images node in FIT\n"); + return -ENOENT; + } + + *qclib_node = fdt_subnode_offset(fit, *images_node, "qclib_1"); + if (*qclib_node < 0) { + pr_err("Failed to find qclib_1 node in FIT\n"); + return -ENOENT; + } + + return 0; +} + +int qcom_spl_invoke_qclib(void) +{ + int ret; + int images_node; + int qclib_node; + const void *fit; + struct interface_table if_tbl; + u64 entry_point; + + /* Get FIT image from SPL load address */ + fit = (const void *)CONFIG_SPL_LOAD_FIT_ADDRESS; + + pr_debug("QCLIB invoke: fit=%p\n", fit); + + ret = qclib_find_fit_nodes(fit, &images_node, &qclib_node); + if (ret) + return ret; + + ret = qcom_spl_soc_pre_qclib_routine(); + if (ret) + return ret; + + ret = qclib_populate_interface_table(&if_tbl, fit, images_node); + if (ret) + return ret; + + ret = qcom_spl_get_fit_img_entry_point((void *)fit, qclib_node, &entry_point); + if (ret) { + pr_err("Failed to get qcom-lib-1 entry point (%d)\n", ret); + return ret; + } + + pr_info("Jumping to qcom-lib-1 at 0x%llx\n", entry_point); + + /* + * QCLIB is a separately-linked firmware image, not an AAPCS64 + * callee - a plain C function-pointer call risks the compiler + * caching a live value in a callee-saved register across the jump. + * Pin the args/target to fixed registers and clobber everything the + * ABI doesn't guarantee QCLIB will preserve. + */ + { + register void *x0 asm("x0") = &if_tbl; + register void *x1 asm("x1") = NULL; + register u64 x8 asm("x8") = entry_point; + + asm volatile ("blr x8" + : "+r" (x0) + : "r" (x1), "r" (x8) + : "x2", "x3", "x4", "x5", "x6", "x7", "x9", "x10", + "x11", "x12", "x13", "x14", "x15", "x16", "x17", + "x19", "x20", "x21", "x22", "x23", "x24", "x25", + "x26", "x27", "x28", "x30", "cc", "memory"); + } + + return qcom_spl_soc_post_qclib_routine(&if_tbl); +} diff --git a/arch/arm/mach-snapdragon/qcom-priv.h b/arch/arm/mach-snapdragon/qcom-priv.h index 327e378e4fd3..27e5c54e5128 100644 --- a/arch/arm/mach-snapdragon/qcom-priv.h +++ b/arch/arm/mach-snapdragon/qcom-priv.h @@ -35,60 +35,9 @@ extern enum qcom_memmap_source qcom_memmap_source; #if IS_ENABLED(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) void qcom_configure_capsule_updates(void); #else -void qcom_configure_capsule_updates(void) {} +static inline void qcom_configure_capsule_updates(void) {} #endif /* EFI_HAVE_CAPSULE_SUPPORT */ int qcom_parse_memory(const void *fdt, bool fdt_is_internal); -enum pbl_shared_data_param_id { - PSD_ID_PBL_FW_VERSION = 0x0, /* PBL firmware version */ - PSD_ID_PBL_PATCH_VERSION = 0x1, /* Patch version */ - PSD_ID_RMB_MBOX_BASE_ADDR = 0x2, /* Not used */ - PSD_ID_CPU_BOOT_SPEED_HZ = 0x3, /* CPU boot speed (Hz) */ - PSD_ID_BOOT_MEDIA_TYPE = 0x4, /* Boot media type */ - PSD_ID_IS_EDL_MODE = 0x5, /* Emergency Download mode */ - PSD_ID_DEV_PROG_ELF_ENTRY_ADDR = 0x6, /* Not used */ - PSD_ID_XBL_CONFIG_ELF_ENTRY_ADDR = 0x7, /* Not used */ - PSD_ID_XBL_SC_EXT_ELF_ENTRY_ADDR = 0x8, /* Not used */ - PSD_ID_PBL_TIMESTAMPS_BUFFER_ADDR = 0x9, /* PBL logs address */ - PSD_ID_PBL_TIMESTAMPS_BUFFER_SIZE = 0xa, /* PBL log size */ - PSD_ID_PBL_DEBUG_SHARED_INFO_ADDR = 0xb, /* Debug info address */ - PSD_ID_PBL_DEBUG_SHARED_INFO_SIZE = 0xc, /* Debug info size */ - PSD_ID_TME_CPU_PBL_ROM_BYPASS_FUSE = 0xd, /* Secure boot status */ - PSD_ID_XBL_SC_DEBUG_LOG_ADDR = 0xe, /* XBL SC debug log address */ - PSD_ID_XBL_SC_DEBUG_LOG_SIZE = 0xf, /* XBL SC debug log size */ - PSD_ID_CURRENT_IMAGE_SET = 0x10, /* Booted image set */ - PSD_ID_MEDIA_DATA_INFO_ADDR = 0x11, /* Media info pointer */ - PSD_ID_MEDIA_DATA_INFO_SIZE = 0x12, /* Media info size */ - PBL_SHARED_DATA_PARAM_MAX, - PBL_SHARED_DATA_PARAM_SIZE = 0xffffffffu, /* to force 32 bits */ -}; - -enum pbl_boot_flash_type { - PSD_NO_FLASH = 0, - PSD_NOR_FLASH = 1, - PSD_NAND_FLASH = 2, - PSD_ONENAND_FLASH = 3, - PSD_SDC_FLASH = 4, - PSD_MMC_FLASH = 5, - PSD_SPI_FLASH = 6, - PSD_PCIE_FLASH = 7, - PSD_UFS_FLASH = 8, - PSD_RSVD_1_FLASH = 9, - PSD_USB_FLASH = 10, - PSD_SPI_NAND_FLASH = 11, - PSD_SPI_FLASH_GPT = 12, -}; - -struct pbl_shared_data_entry { - u32 param_id; - ulong value; - bool valid; -}; - -struct pbl_shared_data { - u32 version; - u32 num_of_entries; - struct pbl_shared_data_entry entry[PBL_SHARED_DATA_PARAM_MAX]; -}; #endif /* __QCOM_PRIV_H__ */ diff --git a/arch/arm/mach-snapdragon/spl.c b/arch/arm/mach-snapdragon/spl.c deleted file mode 100644 index bfe4dc8dd011..000000000000 --- a/arch/arm/mach-snapdragon/spl.c +++ /dev/null @@ -1,770 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "qcom-priv.h" - -DECLARE_GLOBAL_DATA_PTR; - -#define QCOM_SPL_TCSR_REG_ADDR 0x195c100 -#define QCOM_SPL_DLOAD_MASK BIT(4) -#define QCOM_SPL_DLOAD_SHFT 0x4 - -#define QCOM_SPL_IS_DLOAD_BIT_SET ((readl(QCOM_SPL_TCSR_REG_ADDR) & \ - QCOM_SPL_DLOAD_MASK) >> \ - QCOM_SPL_DLOAD_SHFT) - -#define QCOM_SPL_FIT_IMG_PARTITION "0:BOOTLDR" - -#define MAGIC_KEY "QCLIB_CB" -#define MAX_ENTRIES 0xF -#define IF_TABLE_VERSION 0x1 -#define QCCONFIG "qc_config" -#define QCSDI "qcsdi" - -struct mm_region *mem_map = NULL; - -/** - * struct interface_table_entry - Meta data for blobs in QCLIB interface - * @entry_name: Name of the data blob (e.g., "dcb_settings"). - * @address: Address of the data blob. - * @size: Size of the data blob. - * @attributes: Attributes for the blob (e.g., save to storage). - */ -struct interface_table_entry { - char entry_name[24]; - u64 address; - u32 size; - u32 attributes; -}; - -/** - * struct interface_table - QCLIB Interface table header - * @magic_key: Magic key for validation ("QCLIB_CB"). - * @version: Interface table version. - * @num_entries: Number of valid entries. - * @max_entries: Maximum allowable entries. - * @global_attributes: Flags for global attributes (e.g., SDI path). - * @reserved1: Reserved for future use. - * @reserved2: Reserved for future use. - * @if_table_entries: Array of interface table entries. - */ -struct interface_table { - char magic_key[8]; - u32 version; - u32 num_entries; - u32 max_entries; - u32 global_attributes; - u32 reserved1; - u32 reserved2; - struct interface_table_entry if_table_entries[MAX_ENTRIES]; -}; - -/** - * qcom_spl_jump_img_entry_t - Type definition for image entry point functions. - * @arg1: First argument passed to the entry point. - * @arg2: Second argument passed to the entry point. - */ -typedef void (*qcom_spl_jump_img_entry_t)(void *arg1, void *arg2); - -/* - * Global QCSDI address populated by qclib_post_process_from_spl - * Placed in .data section to ensure it persists - */ -static u64 g_qcsdi_address __section(".data"); - -/** - * lowlevel_init() - Early low-level initialization. - * - * This function performs very early hardware initialization, - * specifically disabling the MMU if enabled by PBL. - */ -void lowlevel_init(void) -{ -} - -/** - * qcom_spl_error_handler() - Centralized SPL error handler. - * @arg: Generic argument (unused). - * - * This function is invoked upon critical errors during the SPL boot process. - */ -void qcom_spl_error_handler(void *arg) -{ - pr_err("Entered the SPL Error Handler\n"); - hang(); -} - -/** - * qcom_spl_malloc_init_f() - Initialize malloc for SPL. - * - * This function initializes the malloc subsystem using the memory region - */ -void qcom_spl_malloc_init_f(void) -{ - if (!CONFIG_IS_ENABLED(SYS_MALLOC_F)) - return; - /* - * Set up by crt0.S - */ - assert(gd->malloc_base); - gd->malloc_limit = CONFIG_VAL(SYS_MALLOC_F_LEN); - gd->malloc_ptr = 0; - - mem_malloc_init(gd->malloc_base, gd->malloc_limit); - gd->flags |= GD_FLG_FULL_MALLOC_INIT; -} - -/** - * qcom_spl_get_fit_img_entry_point() - Get entry point from FIT image node. - * @fit: Pointer to the FIT image blob. - * @node: Node ID within the FIT image. - * @entry_point: Pointer to store the retrieved entry point. - * - * Return: 0 on success, or a negative error code on failure. - */ -static int qcom_spl_get_fit_img_entry_point(void *fit, int node, - u64 *entry_point) -{ - int ret; - - if (!fit) { - pr_err("FIT image blob is NULL\n"); - return -EINVAL; - } - if (node <= 0) { - pr_err("Invalid FIT node ID %d\n", node); - return -EINVAL; - } - if (!entry_point) { - pr_err("Entry point pointer is NULL\n"); - return -EINVAL; - } - - ret = fit_image_get_entry(fit, node, (ulong *)entry_point); - if (ret) { - pr_debug("No entry point for node %d, trying load address\n", - node); - ret = fit_image_get_load(fit, node, (ulong *)entry_point); - if (ret) - pr_err("No load address for node %d (%d)\n", node, ret); - } - - return ret; -} - -#if IS_ENABLED(CONFIG_SPL_SMEM) -/** - * qcom_spl_populate_smem() - Populate shared memory (SMEM) information. - * @ctx: Pointer to the global SPL context. - * - * This function initializes and populates various SMEM items with boot-related - * information, such as flash type, try-mode status, and ATF enable status. - * Return: 0 on success, or a negative error code on failure. - */ -static int qcom_spl_populate_smem(void *ctx) -{ - int ret; - size_t size; - struct udevice *smem; - u32 *fltype; - - ret = uclass_get_device(UCLASS_SMEM, 0, &smem); - if (ret) { - pr_err("Failed to find SMEM node (%d)\n", ret); - return ret; - } - - size = sizeof(u32); - ret = smem_alloc(smem, -1, SMEM_BOOT_FLASH_TYPE, size); - if (ret) { - pr_err("Failed to alloc item: SMEM_BOOT_FLASH_TYPE (%d)\n", ret); - return ret; - } - - fltype = (u32 *)smem_get(smem, -1, SMEM_BOOT_FLASH_TYPE, &size); - if (!fltype) { - pr_err("Failed to get item: SMEM_BOOT_FLASH_TYPE\n"); - return -ENOENT; - } - - if (IS_ENABLED(CONFIG_SPL_MMC)) { - *fltype = SMEM_BOOT_MMC_FLASH; - return 0; - } - - pr_err("Boot medium not specified\n"); - - return -ENOENT; -} -#endif /* IS_ENABLED(CONFIG_SPL_SMEM) */ - -/** - * qcom_spl_get_iftbl_entry_by_name() - Get an interface table entry by name. - * @if_tbl: Pointer to the QCLIB interface table. - * @name: Name of the entry to find. - * @entry: Pointer to a buffer where the found entry will be copied. - * - * Return: 0 on success, or a negative error code on failure. - */ -static int qcom_spl_get_iftbl_entry_by_name(struct interface_table *if_tbl, - char *name, - struct interface_table_entry *entry) -{ - uint uc_index; - - if (!if_tbl) { - pr_err("Invalid interface table\n"); - return -EINVAL; - } - if (!name) { - pr_err("Invalid name\n"); - return -EINVAL; - } - if (!entry) { - pr_err("Invalid entry pointer\n"); - return -EINVAL; - } - - for (uc_index = 0; uc_index < if_tbl->num_entries; uc_index++) { - if (!strcmp(if_tbl->if_table_entries[uc_index].entry_name, name)) { - memcpy(entry, - &if_tbl->if_table_entries[uc_index], - sizeof(struct interface_table_entry)); - return 0; - } - } - pr_err("Interface table entry '%s' not found\n", name); - - return -ENOENT; -} - -/** - * qclib_post_process_from_spl() - Post-process QCLIB image from SPL FIT address - * - * This function performs the same operations as qclib_post_process() but - * takes no arguments. It gets the FIT image from CONFIG_SPL_LOAD_FIT_ADDRESS - * and finds the qcom-lib-1 node automatically. - * - * Return: 0 on success, or a negative error code on failure. - */ -int qclib_post_process_from_spl(void) -{ - int ret; - int entry_idx; - int images_node; - int qcconfig_node; - int qclib_node; - const void *fit; - struct interface_table if_tbl; - struct interface_table_entry qcsdi_entry; - qcom_spl_jump_img_entry_t qclib_entry; - u64 entry_point; - - /* Get FIT image from SPL load address */ - fit = (const void *)CONFIG_SPL_LOAD_FIT_ADDRESS; - - pr_debug("QCLIB post-processing from SPL: fit=%p\n", fit); - - /* - * Find "images" node in FIT (get it once and reuse) - */ - images_node = fdt_subnode_offset(fit, 0, "images"); - if (images_node < 0) { - pr_err("Failed to find images node in FIT\n"); - return -ENOENT; - } - - /* - * Find "qcom-config-1" image node - */ - qcconfig_node = fdt_subnode_offset(fit, images_node, "qcom-config-1"); - if (qcconfig_node < 0) { - pr_err("Failed to find qcom-config-1 node in FIT\n"); - return -ENOENT; - } - - /* - * Find "qcom-lib-1" image node - */ - qclib_node = fdt_subnode_offset(fit, images_node, "qcom-lib-1"); - if (qclib_node < 0) { - pr_err("Failed to find qcom-lib-1 node in FIT\n"); - return -ENOENT; - } - - /* - * Initialize the local interface table - */ - memset(&if_tbl, 0, sizeof(struct interface_table)); - memcpy(if_tbl.magic_key, MAGIC_KEY, strlen(MAGIC_KEY)); - - if_tbl.version = IF_TABLE_VERSION; - if_tbl.num_entries = 0; - if_tbl.max_entries = MAX_ENTRIES; - - /* - * Add QCCONFIG entry to the interface table - */ - entry_idx = 0; - memcpy(if_tbl.if_table_entries[entry_idx].entry_name, - QCCONFIG, strlen(QCCONFIG)); - - ret = qcom_spl_get_fit_img_entry_point((void *)fit, - qcconfig_node, - &if_tbl.if_table_entries[entry_idx].address); - if (ret) { - pr_err("Failed to get qcom-config-1 entry point (%d)\n", ret); - return ret; - } - if_tbl.if_table_entries[entry_idx].attributes = 0; - if_tbl.num_entries = entry_idx + 1; - - /* - * Add QCSDI entry to the interface table - */ - entry_idx++; - memcpy(if_tbl.if_table_entries[entry_idx].entry_name, - QCSDI, strlen(QCSDI)); - - if_tbl.if_table_entries[entry_idx].address = 0; - if_tbl.if_table_entries[entry_idx].attributes = 0; - if_tbl.num_entries = entry_idx + 1; - - /* - * Get qcom-lib-1 entry point - */ - ret = qcom_spl_get_fit_img_entry_point((void *)fit, - qclib_node, - &entry_point); - if (ret) { - pr_err("Failed to get qcom-lib-1 entry point (%d)\n", ret); - return ret; - } - - qclib_entry = (qcom_spl_jump_img_entry_t)entry_point; - - pr_info("Jumping to qcom-lib-1 at 0x%llx\n", entry_point); - qclib_entry(&if_tbl, NULL); - - /* Parse the interface table to extract QCSDI address */ - ret = qcom_spl_get_iftbl_entry_by_name(&if_tbl, QCSDI, &qcsdi_entry); - if (ret) { - pr_err("Failed to get QCSDI entry from interface table (%d)\n", ret); - return ret; - } - - g_qcsdi_address = qcsdi_entry.address; - pr_info("QCSDI address: 0x%llx\n", g_qcsdi_address); - - return 0; -} - -#if IS_ENABLED(CONFIG_SPL_SMEM) -/** - * spl_board_prepare_for_boot() - Prepare board for boot - * - * This function is called by SPL before jumping to the next stage. - * It populates SMEM during coldboot. - */ -void spl_board_prepare_for_boot(void) -{ - int ret; - - /* - * Populate SMEM in coldboot (Dload bit not set) - */ - if (!QCOM_SPL_IS_DLOAD_BIT_SET) { - ret = qcom_spl_populate_smem(NULL); - if (ret) { - pr_err("Failed to populate SMEM (%d)\n", ret); - qcom_spl_error_handler(NULL); - } - } -} -#endif /* IS_ENABLED(CONFIG_SPL_SMEM) */ - -/** - * spl_get_load_buffer() - Allocate a cache-aligned buffer for image loading. - * @offset: Offset (unused, typically 0 for SPL). - * @size: Size of the buffer to allocate. - * - * Return: Pointer to the allocated buffer, or NULL on failure. - */ -struct legacy_img_hdr *spl_get_load_buffer(ssize_t offset, size_t size) -{ - return (void *)(CONFIG_SPL_LOAD_FIT_ADDRESS); -} - -/** - * board_spl_fit_buffer_addr() - Get the address of the FIT image buffer. - * @fit_size: Size of the FIT image. - * @sectors: Number of sectors. - * @bl_len: Block length. - * - * Return: Address of the FIT image buffer. - */ -void *board_spl_fit_buffer_addr(ulong fit_size, int sectors, int bl_len) -{ - return spl_get_load_buffer(0, sectors * bl_len); -} - -/** - * bl2_plat_get_bl31_params_v2() - Retrieve and fixup BL31 parameters. - * @bl32_entry: Entry point for BL32 (OP-TEE). - * @bl33_entry: Entry point for BL33 (U-Boot/kernel). - * @fdt_addr: Address of the Device Tree Blob (FDT). - * - * Return: Pointer to the populated BL31 parameters structure. - */ -struct bl_params *bl2_plat_get_bl31_params_v2(uintptr_t bl32_entry, - uintptr_t bl33_entry, - uintptr_t fdt_addr) -{ - struct bl_params *bl_params; - struct bl_params_node *node; - - /* - * Populate the bl31 params with default values. - */ - bl_params = bl2_plat_get_bl31_params_v2_default(bl32_entry, bl33_entry, - fdt_addr); - - /* - * Fixup the bl31 params based on platform requirements. - */ - for_each_bl_params_node(bl_params, node) { - if (node->image_id == ATF_BL31_IMAGE_ID) { - /* - * Pass QCSDI address to BL31 via arg0 - * This address was populated by qclib_post_process() - */ - if (g_qcsdi_address == 0) - pr_warn("QCSDI address not set, BL31 may not function correctly\n"); - - node->ep_info->args.arg0 = g_qcsdi_address; - pr_debug("Setting BL31 arg0 to QCSDI address: 0x%llx\n", g_qcsdi_address); - } - } - - return bl_params; -} - -/** - * qcom_spl_loader_pre_ddr() - SPL loader for pre-DDR stage. - * @boot_device: Type of boot device. - * - * Return: 0 on success, or a negative error code on failure. - */ -static int qcom_spl_loader_pre_ddr(u8 boot_device) -{ - struct spl_image_loader *loader, *drv; - struct spl_image_info spl_image = { 0 }; - struct spl_boot_device boot_dev = { .boot_device = boot_device, }; - int ret = -ENODEV, n_ents; - - drv = ll_entry_start(struct spl_image_loader, spl_image_loader); - n_ents = ll_entry_count(struct spl_image_loader, spl_image_loader); - - for (loader = drv; loader && (loader != drv + n_ents); loader++) { - if (boot_device != loader->boot_device) - continue; - - ret = loader->load_image(&spl_image, &boot_dev); - if (!ret) - break; - - printf("%s: Error: %d\n", __func__, ret); - } - - return ret; -} - -#if CONFIG_IS_ENABLED(MMC) -/** - * spl_find_partition_info() - Find partition information by name - * @uclass_id: Device class ID (UCLASS_MMC) - * @device_num: Device number within the class - * @part_name: Name of the partition to find - * @info: Pointer to store partition information - * - * This function provides partition lookup logic for MMC. - * Return: Partition number on success, negative error code on failure - */ -static int spl_find_partition_info(enum uclass_id uclass_id, int device_num, - const char *part_name, - struct disk_partition *info) -{ - int ret; - struct blk_desc *desc; - - if (!part_name || !info) { - printf("Invalid parameters for partition lookup\n"); - return -EINVAL; - } - - /* - * Get block device descriptor - */ - desc = blk_get_devnum_by_uclass_id(uclass_id, device_num); - if (!desc) { - printf("Block device not found for class %d, device %d\n", - uclass_id, device_num); - return -ENODEV; - } - - /* - * Initialize partition table if needed - */ - if (desc->part_type == PART_TYPE_UNKNOWN) { - printf("Initializing partition table\n"); - /* - * Prefer EFI/GPT - */ - desc->part_type = PART_TYPE_EFI; - } - - /* - * Find partition by name - */ - ret = part_get_info_by_name(desc, part_name, info); - if (ret < 0) { - printf("Partition '%s' not found\n", part_name); - return -ENOENT; - } - - printf("Found partition '%s' at partition number %d\n", part_name, ret); - return ret; -} - -/** - * spl_mmc_boot_mode() - Determine the boot mode for MMC - * @mmc: Pointer to the MMC device - * @boot_device: Boot device ID - * - * Return: MMCSD_MODE_RAW to use raw partition access - */ -u32 spl_mmc_boot_mode(struct mmc *mmc, const u32 boot_device) -{ - return MMCSD_MODE_RAW; -} - -/** - * spl_mmc_boot_partition() - Determine which partition to boot from - * @boot_device: Boot device ID - * - * Return: Partition number to boot from, or default partition on error - */ -int spl_mmc_boot_partition(const u32 boot_device) -{ - int ret; - struct disk_partition info; - - /* - * Use common partition lookup function - */ - ret = spl_find_partition_info(UCLASS_MMC, 0, QCOM_SPL_FIT_IMG_PARTITION, &info); - if (ret < 0) { - printf("Using default MMC partition %d\n", - CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_PARTITION); - return CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_PARTITION; - } - - return ret; -} - -/** - * spl_mmc_get_uboot_raw_sector() - Find the raw sector offset - * @mmc: Pointer to the MMC device - * @raw_sect: Sector - * - * Return: 0 if the image is at the starting of the partition without any offset. - */ -unsigned long spl_mmc_get_uboot_raw_sector(struct mmc *mmc, ulong raw_sect) -{ - return 0; -} -#endif /* CONFIG_IS_ENABLED(MMC) */ - -static struct pbl_shared_data g_psd __section(".data"); - -void save_boot_params(ulong r0, ulong r1, ulong r2, ulong r3) -{ - unsigned long sctlr; - - sctlr = get_sctlr(); - set_sctlr(sctlr & ~(CR_M)); /* Disable MMU */ - - /* - * When U-Boot SPL is loaded directly by PBL, r0 contains a pointer - * to pbl_shared_data structure. When loaded via XBL or another - * intermediate bootloader, r0 won't contain valid PBL data. - */ - if (CONFIG_IS_ENABLED(QCOM_BOOT_FROM_PBL)) { - struct pbl_shared_data *psd = (struct pbl_shared_data *)r0; - - if (psd && psd->num_of_entries >= PBL_SHARED_DATA_PARAM_MAX) - memcpy(&g_psd, psd, sizeof(g_psd)); - } - - save_boot_params_ret(); -} - -/** - * spl_boot_device() - Determine the boot device. - * - * Return: The mapped boot device type, - * or BOOT_DEVICE_NONE if the device is invalid. - */ -u32 spl_boot_device(void) -{ - struct pbl_shared_data *psd = &g_psd; - - /* - * When booted directly from PBL, use PBL shared data to determine - * boot device. When booted via XBL, fall back to compile-time config. - */ - if (CONFIG_IS_ENABLED(QCOM_BOOT_FROM_PBL)) { -#ifdef DEBUG - for (int i = 0; psd && i < psd->num_of_entries; i++) { - printf("entry[0x%x] = %d 0x%08x %d\n", i, - psd->entry[i].param_id, psd->entry[i].value, - psd->entry[i].valid); - } -#endif - - if (psd->entry[PSD_ID_IS_EDL_MODE].valid && - psd->entry[PSD_ID_IS_EDL_MODE].value) { - printf("Selected boot device: DFU\n"); - return BOOT_DEVICE_DFU; - } - - if (psd->entry[PSD_ID_BOOT_MEDIA_TYPE].valid) { - switch (psd->entry[PSD_ID_BOOT_MEDIA_TYPE].value) { - case PSD_MMC_FLASH: - printf("Selected boot device: MMC\n"); - return BOOT_DEVICE_MMC1; - case PSD_NOR_FLASH: - printf("Selected boot device: NOR\n"); - return BOOT_DEVICE_NOR; - case PSD_NAND_FLASH: - printf("Selected boot device: NAND\n"); - return BOOT_DEVICE_NAND; - case PSD_UFS_FLASH: - printf("Selected boot device: UFS\n"); - return BOOT_DEVICE_UFS; - } - } - } - - /* - * Fallback: Use UFS when PBL shared data is not available - */ - if (IS_ENABLED(CONFIG_SPL_UFS_QCOM)) { - printf("Selected boot device: UFS\n"); - return BOOT_DEVICE_UFS; - } - - pr_err("No boot device configured\n"); - return BOOT_DEVICE_NONE; -} - -#if defined(CONFIG_SPL_BUILD) -/** - * board_init_f() - Main entry point for SPL. - * @dummy: Dummy argument (unused). - */ -void board_init_f(ulong dummy) -{ - int ret; - - memset(__bss_start, 0, __bss_end - __bss_start); /* Clear BSS */ - - qcom_spl_malloc_init_f(); - - ret = spl_early_init(); - if (ret) { - pr_debug("spl_early_init() failed (%d)\n", ret); - goto fail; - } - - preloader_console_init(); - - if (CONFIG_IS_ENABLED(QCOM_BOOT_FROM_PBL)) { - ret = qcom_spl_loader_pre_ddr(spl_boot_device()); - if (ret) { - pr_debug("qcom_spl_loader_pre_ddr() failed (%d)\n", ret); - goto fail; - } - - ret = qclib_post_process_from_spl(); - if (ret) { - pr_debug("qclib_post_process_from_spl() failed (%d)\n", ret); - goto fail; - } - } - - board_init_r(NULL, 0); - -fail: - if (ret) - qcom_spl_error_handler(NULL); -} -#endif /* CONFIG_SPL_BUILD */ - -int board_fit_config_name_match(const char *name) -{ - /* - * SPL loads the pre-HLOS images from bootldr FIT image - * as below - * - * In board_init_f() - Matches "pre-ddr" configuration node and - * load the images mentioned in its - * - * In board_init_r() - Matches "post-ddr" configuration node and - * load the images mentioned in its - * - */ - if (!(gd->flags & GD_FLG_SPL_INIT)) { - if (!strcmp(name, "pre-ddr")) { - printf("Selected FIT Config: %s\n", name); - return 0; - } - } else { - if (!strcmp(name, "post-ddr")) { - printf("Selected FIT Config: %s\n", name); - return 0; - } - } - - return -EINVAL; -} - -int board_fdt_blob_setup(void **fdtp) -{ - return 0; -} - -void reset_cpu(void) -{ - /* - * Empty placeholder for arch/arm/lib/reset.c:do_reset(), - * to avoid "undefined reference to `reset_cpu'" - */ -} diff --git a/board/qualcomm/config.mk b/board/qualcomm/config.mk new file mode 100644 index 000000000000..d6253ebb545a --- /dev/null +++ b/board/qualcomm/config.mk @@ -0,0 +1,37 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright Linaro Ltd. +# +# Qualcomm specific make target for MBN signed ELF files. +# + +# Create Qualcomm signed elf images +CMD_MKMBN = $(srctree)/tools/qcom/mkmbn/mkmbn.py +quiet_cmd_mkmbn = MBN $@ + cmd_mkmbn = $(CMD_MKMBN) -l $(CONFIG_TEXT_BASE) $< + +u-boot.mbn: u-boot.bin FORCE + $(call if_changed,mkmbn) + +ifeq ($(CONFIG_QCOM_GENERATE_MBN),y) + +quiet_cmd_mksplmbn = SPLMBN $@ + cmd_mksplmbn = $(CMD_MKMBN) -o spl/u-boot-spl.mbn -l $(CONFIG_SPL_TEXT_BASE) $< + +INPUTS-$(CONFIG_SPL) += spl/u-boot-spl.mbn + +spl/u-boot-spl.mbn: spl/u-boot-spl.bin FORCE + $(call if_changed,mksplmbn) + +ifneq ($(wildcard $(CONFIG_QCOM_TMEL_ELF:"%"=%)),) + +quiet_cmd_mksplmelf = SPLMELF $@ + cmd_mksplmelf = $(CMD_MKMBN) -m spl/u-boot-spl.mbn $(CONFIG_QCOM_TMEL_ELF:"%"=%) -o spl/u-boot-spl.melf $< + +INPUTS-$(CONFIG_SPL) += spl/u-boot-spl.melf + +spl/u-boot-spl.melf: spl/u-boot-spl.mbn FORCE + $(call if_changed,mksplmelf) +endif # CONFIG_QCOM_TMEL_ELF + +endif # CONFIG_QCOM_GENERATE_MBN diff --git a/board/qualcomm/dragonboard410c/dragonboard410c.c b/board/qualcomm/dragonboard410c/dragonboard410c.c index 36e4d49046e3..3c0d1cbb2d80 100644 --- a/board/qualcomm/dragonboard410c/dragonboard410c.c +++ b/board/qualcomm/dragonboard410c/dragonboard410c.c @@ -8,7 +8,10 @@ #include #include #include +#include #include +#include +#include #include #include #include @@ -19,6 +22,44 @@ #include #include +/* + * db410c requires GPIO configuration when switching USB modes. + * Support setting this configuration via pinctrl state. + */ +int board_usb_init(int index, enum usb_init_type init) +{ + struct udevice *usb; + int ret = 0; + + /* USB device */ + ret = uclass_find_device_by_seq(UCLASS_USB, index, &usb); + if (ret) { + printf("Cannot find USB device\n"); + return ret; + } + + ret = dev_read_stringlist_search(usb, "pinctrl-names", + "device"); + /* No "device" pinctrl state, so just bail */ + if (ret < 0) + return 0; + + /* Select "default" or "device" pinctrl */ + switch (init) { + case USB_INIT_HOST: + pinctrl_select_state(usb, "default"); + break; + case USB_INIT_DEVICE: + pinctrl_select_state(usb, "device"); + break; + default: + debug("Unknown usb_init_type %d\n", init); + break; + } + + return 0; +} + static u32 msm_board_serial(void) { struct mmc *mmc_dev; diff --git a/board/qualcomm/ipq5210/Makefile b/board/qualcomm/ipq5210/Makefile new file mode 100644 index 000000000000..d808c1065248 --- /dev/null +++ b/board/qualcomm/ipq5210/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_SPL) := spl-ipq5210.o diff --git a/arch/arm/mach-snapdragon/ipq5210-spl-wrap-elf.lds b/board/qualcomm/ipq5210/ipq5210-spl-wrap-elf.lds similarity index 88% rename from arch/arm/mach-snapdragon/ipq5210-spl-wrap-elf.lds rename to board/qualcomm/ipq5210/ipq5210-spl-wrap-elf.lds index 5a582e65ca02..f0ced6765da0 100644 --- a/arch/arm/mach-snapdragon/ipq5210-spl-wrap-elf.lds +++ b/board/qualcomm/ipq5210/ipq5210-spl-wrap-elf.lds @@ -9,7 +9,7 @@ PHDRS { ENTRY(_entry) SECTIONS { - . = IMAGE_TEXT_BASE; + . = CONFIG_PLATFORM_ELFENTRY; _entry = . ; data : { *(.data) diff --git a/board/qualcomm/ipq5210/spl-ipq5210.c b/board/qualcomm/ipq5210/spl-ipq5210.c new file mode 100644 index 000000000000..5fc42e64c119 --- /dev/null +++ b/board/qualcomm/ipq5210/spl-ipq5210.c @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +#define QCCONFIG "qc_config" + +/** + * qcom_spl_soc_qclib_override() - Populate ipq5210-specific interface table + * entries. + * @table: Pointer to the QCLIB interface table being built. + * @fit: Pointer to the FIT image blob. + * @images_node: FIT "images" node offset. + * + * Appends the "qc_config" entry and a placeholder "qcsdi" entry, filled in by QCLIB itself. + * + * Return: 0 on success, or a negative error code on failure. + */ +int qcom_spl_soc_qclib_override(struct interface_table *table, + const void *fit, int images_node) +{ + int ret; + int entry_idx; + int qcconfig_node; + + qcconfig_node = fdt_subnode_offset(fit, images_node, "qcconfig_1"); + if (qcconfig_node < 0) { + pr_err("Failed to find qcconfig_1 node in FIT\n"); + return -ENOENT; + } + + entry_idx = table->num_entries; + memcpy(table->if_table_entries[entry_idx].entry_name, + QCCONFIG, strlen(QCCONFIG)); + + ret = qcom_spl_get_fit_img_entry_point((void *)fit, qcconfig_node, + &table->if_table_entries[entry_idx].address); + if (ret) { + pr_err("Failed to get qcconfig_1 entry point (%d)\n", ret); + return ret; + } + table->if_table_entries[entry_idx].attributes = 0; + table->num_entries = entry_idx + 1; + + entry_idx++; + memcpy(table->if_table_entries[entry_idx].entry_name, + QCSDI, strlen(QCSDI)); + table->if_table_entries[entry_idx].address = 0; + table->if_table_entries[entry_idx].attributes = 0; + table->num_entries = entry_idx + 1; + + return 0; +} + +/** + * qcom_spl_soc_post_qclib_routine() - Cache the QCSDI address after QCLIB + * returns. + * @if_tbl: Pointer to the QCLIB interface table, populated by QCLIB. + * + * Return: 0 on success, or a negative error code on failure. + */ +int qcom_spl_soc_post_qclib_routine(struct interface_table *if_tbl) +{ + int ret; + struct interface_table_entry qcsdi_entry; + + ret = qcom_spl_get_iftbl_entry_by_name(if_tbl, QCSDI, &qcsdi_entry); + if (ret) { + pr_err("Failed to get QCSDI entry from interface table (%d)\n", ret); + return ret; + } + + qclib_set_qcsdi_address(qcsdi_entry.address); + pr_info("QCSDI address: 0x%llx\n", qcsdi_entry.address); + + return 0; +} diff --git a/board/qualcomm/sdm845_spl/sdm845_spl.env b/board/qualcomm/sdm845_spl/sdm845_spl.env new file mode 100644 index 000000000000..2396d003b0c0 --- /dev/null +++ b/board/qualcomm/sdm845_spl/sdm845_spl.env @@ -0,0 +1,2 @@ +# U-Boot proper text base - 64 +dfu_alt_info_ram=uboot.bin ram 0x1487FFC0 0x180000 diff --git a/board/qualcomm/sdm845_spl/u-boot-spl-elf-sdm845.lds b/board/qualcomm/sdm845_spl/u-boot-spl-elf-sdm845.lds new file mode 100644 index 000000000000..3740209d4995 --- /dev/null +++ b/board/qualcomm/sdm845_spl/u-boot-spl-elf-sdm845.lds @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ + +/* + * The boot rom uses the segment type to identify the xbl_sec program header. + * See https://github.com/coreboot/coreboot/blob/643efabd2af9f7ac/util/qualcomm/mbn_tools.py#L143. + */ +#define PF_SEGMENT_TYPE_XBL_SEC 5 + +/* + * The boot rom expects this to be equal to a seemingly magic value possibly specific + * to a particular xbl_sec.elf. If you're extracting your xbl_sec.elf from an xbl elf, + * you can just reuse the value; otherwise you can either compute it yourself or use + * coreboot's tool. + * See https://github.com/coreboot/coreboot/blob/643efabd2af9f7ac/util/qualcomm/createxbl.py#L638. + */ +#define XLB_SEC_SEGMENT_ADDR 0x0000000014699000 + + +TARGET("binary") +INPUT("./xbl_sec.elf") + +OUTPUT_FORMAT("default") + +ENTRY(CONFIG_PLATFORM_ELFENTRY) +PHDRS +{ + data PT_LOAD FLAGS(7); + xbl_sec PT_LOAD FLAGS(5 | (PF_SEGMENT_TYPE_XBL_SEC << 24)); +} +SECTIONS +{ + + . = XLB_SEC_SEGMENT_ADDR; + .xbl_sec : { // XBL_SEC nested ELF + . = .; + "./xbl_sec.elf" + } :xbl_sec + + . = CONFIG_PLATFORM_ELFENTRY; + + .data : { + *(.data*) + } :data +} diff --git a/common/spl/Kconfig b/common/spl/Kconfig index f3dfd029ab55..10b0c3d37c32 100644 --- a/common/spl/Kconfig +++ b/common/spl/Kconfig @@ -238,6 +238,18 @@ config SPL_HANDOFF proper. Also SPL can receive information from TPL in the same place if that is enabled. +config SPL_REMAKE_ELF_LDSCRIPT + string "Linker script for SPL ELF" + depends on SPL_REMAKE_ELF + default "arch/u-boot-elf.lds" + help + This allows specifying a linker script that will be used to re-wrap + the SPL binary into an ELF. + + Some platforms (e.g. at least Qualcomm) use the ELF format in creative + ways, including in the bootrom. For such platforms, you can change + the default linker script to a platform-specific one. + config SPL_LDSCRIPT string "Linker script for the SPL stage" default "arch/arm/cpu/arm926ejs/sunxi/u-boot-spl.lds" if MACH_SUNIV @@ -1387,14 +1399,6 @@ config SPL_RAM_DEVICE be already in memory when SPL takes over, e.g. loaded by the boot ROM. -config SPL_SMEM - bool "Support SMEM (Shared Memory manager)" - depends on SMEM - help - Enable support for the Shared Memory Manager. The driver provides an - interface to items in a heap shared among all processors. This enables - the drivers in drivers/smem as part of an SPL build. - config SPL_PCI_DFU bool "PCIe boot support" depends on SPL_PCI_ENDPOINT diff --git a/common/spl/spl.c b/common/spl/spl.c index 722b18c98edc..6c9a1a1cbcc3 100644 --- a/common/spl/spl.c +++ b/common/spl/spl.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -510,6 +511,14 @@ static int spl_common_init(bool setup_malloc) return ret; } } + if (CONFIG_IS_ENABLED(OF_LIVE)) { + bootstage_start(BOOTSTAGE_ID_ACCUM_OF_LIVE, "of_live"); + ret = of_live_build(gd->fdt_blob, + (struct device_node **)gd_of_root_ptr()); + bootstage_accum(BOOTSTAGE_ID_ACCUM_OF_LIVE); + if (ret) + return ret; + } if (CONFIG_IS_ENABLED(DM)) { bootstage_start(BOOTSTAGE_ID_ACCUM_DM_SPL, xpl_phase() == PHASE_TPL ? "dm tpl" : "dm_spl"); diff --git a/config.mk b/config.mk index abed9cb65c6f..22c21c0d3b2d 100644 --- a/config.mk +++ b/config.mk @@ -47,7 +47,7 @@ ifdef SOC sinclude $(srctree)/$(CPUDIR)/$(SOC)/config.mk # include SoC specific rules endif ifneq ($(BOARD),) -ifdef VENDOR +ifneq ($(VENDOR),) BOARDDIR = $(VENDOR)/$(BOARD) ENVDIR=${vendor}/env else @@ -55,7 +55,11 @@ BOARDDIR = $(BOARD) ENVDIR=${board}/env endif endif -ifdef BOARD + +ifneq ($(VENDOR),) +sinclude $(srctree)/board/$(VENDOR)/config.mk # include vendor specific rules +endif +ifdef BOARDDIR sinclude $(srctree)/board/$(BOARDDIR)/config.mk # include board specific rules endif diff --git a/configs/hmibsc_defconfig b/configs/hmibsc_defconfig index c8fad154e315..a0d501e70718 100644 --- a/configs/hmibsc_defconfig +++ b/configs/hmibsc_defconfig @@ -12,6 +12,7 @@ CONFIG_ENV_OFFSET=0x0 CONFIG_DEFAULT_DEVICE_TREE="apq8016-schneider-hmibsc" CONFIG_OF_LIBFDT_OVERLAY=y CONFIG_SYS_LOAD_ADDR=0x80080000 +CONFIG_BOOT0_MSM8916_PSCI_WORKAROUND=y CONFIG_IDENT_STRING="\nSchneider Electric-HMIBSC" CONFIG_REMAKE_ELF=y # CONFIG_ANDROID_BOOT_IMAGE is not set diff --git a/configs/qcm6490_defconfig b/configs/qcm6490_defconfig index 5d0c7e2556e4..391573db5681 100644 --- a/configs/qcm6490_defconfig +++ b/configs/qcm6490_defconfig @@ -11,7 +11,7 @@ CONFIG_ARM=y # Address where U-Boot will be loaded CONFIG_TEXT_BASE=0x9fc00000 -CONFIG_REMAKE_ELF=y +CONFIG_QCOM_GENERATE_MBN=y CONFIG_DEFAULT_DEVICE_TREE="qcom/qcs6490-rb3gen2" diff --git a/configs/dragonboard410c_defconfig b/configs/qcom_dragonboard410c_defconfig similarity index 98% rename from configs/dragonboard410c_defconfig rename to configs/qcom_dragonboard410c_defconfig index 645f08167010..64b8fcc7ea47 100644 --- a/configs/dragonboard410c_defconfig +++ b/configs/qcom_dragonboard410c_defconfig @@ -13,7 +13,7 @@ CONFIG_OF_LIBFDT_OVERLAY=y CONFIG_SYS_LOAD_ADDR=0x80080000 CONFIG_BOOT0_MSM8916_PSCI_WORKAROUND=y CONFIG_IDENT_STRING="\nQualcomm-DragonBoard 410C" -CONFIG_REMAKE_ELF=y +CONFIG_QCOM_GENERATE_MBN=y CONFIG_BUTTON_CMD=y CONFIG_FIT=y CONFIG_BOOTSTD_FULL=y diff --git a/configs/dragonboard820c_defconfig b/configs/qcom_dragonboard820c_defconfig similarity index 100% rename from configs/dragonboard820c_defconfig rename to configs/qcom_dragonboard820c_defconfig diff --git a/configs/qcom_ipq5210_mmc_defconfig b/configs/qcom_ipq5210_mmc_defconfig index e903daa581cc..b4f2ba497285 100644 --- a/configs/qcom_ipq5210_mmc_defconfig +++ b/configs/qcom_ipq5210_mmc_defconfig @@ -3,6 +3,7 @@ CONFIG_SKIP_LOWLEVEL_INIT=y CONFIG_POSITION_INDEPENDENT=y CONFIG_SYS_INIT_SP_BSS_OFFSET=0x180000 CONFIG_ARCH_SNAPDRAGON=y +CONFIG_EVENT=y CONFIG_TEXT_BASE=0x87980000 CONFIG_NR_DRAM_BANKS=2 CONFIG_ENV_SIZE=0x40000 @@ -32,7 +33,6 @@ CONFIG_MSM_GPIO=y CONFIG_MISC=y CONFIG_QCOM_GENI=y CONFIG_QCOM_GENI_MINICORE=y -CONFIG_MMC_HS200_SUPPORT=y CONFIG_MMC_SDHCI=y # CONFIG_MMC_SDHCI_ADMA_HELPERS is not set # CONFIG_MMC_SDHCI_ADMA is not set @@ -67,9 +67,12 @@ CONFIG_SPL_GPIO=y CONFIG_SPL_DM_GPIO=y CONFIG_SPL_DM_RESET=y CONFIG_SPL_PINCTRL=y +CONFIG_SPL_PINCTRL_QCOM_IPQ5210=y CONFIG_SPL_CLK=y CONFIG_SPL_DRIVERS_MISC=y CONFIG_SPL_DRIVERS_MISC_SUPPORT=y +CONFIG_SPL_QCOM_GENI=y +CONFIG_SPL_MSM_GENI_SERIAL=y CONFIG_SPL_SERIAL=y CONFIG_SPL_SMEM=y CONFIG_DM_STATS=y @@ -83,7 +86,7 @@ CONFIG_SPL_SYS_MALLOC_SIZE=0x20000 # CONFIG_SPL_SEPARATE_BSS is not set # CONFIG_SPL_USE_TINY_PRINTF is not set CONFIG_SPL_BSS_MAX_SIZE=0x4000 -CONFIG_SPL_TEXT_BASE=0x08c24000 +CONFIG_SPL_TEXT_BASE=0x08c2e000 CONFIG_SPL_MAX_SIZE=0x3D000 CONFIG_SPL_MMC=y CONFIG_SPL_MMC_SDHCI_ADMA=y @@ -101,6 +104,12 @@ CONFIG_SPL_HAS_LOAD_FIT_ADDRESS=y CONFIG_SPL_LOAD_FIT_ADDRESS=0x08cbe000 # CONFIG_SPL_SHARES_INIT_SP_ADDR is not set CONFIG_SPL_HAVE_INIT_STACK=y -CONFIG_SPL_STACK=0x08c24000 +CONFIG_SPL_STACK=0x08c2e000 # CONFIG_SAVE_PREV_BL_FDT_ADDR is not set -CONFIG_SPL_WRAPPER_ELF=y +CONFIG_SPL_EVENT=y +CONFIG_SYS_BOARD="ipq5210" +CONFIG_SPL_REMAKE_ELF=y +CONFIG_SPL_REMAKE_ELF_LDSCRIPT="board/qualcomm/ipq5210/ipq5210-spl-wrap-elf.lds" +CONFIG_QCOM_TMEL_ELF="../tmel-ipq52xx-patch.elf" +CONFIG_QCOM_GENERATE_MBN=y +CONFIG_SYSRESET_PSCI=y diff --git a/configs/qcom_ipq9574_mmc_defconfig b/configs/qcom_ipq9574_mmc_defconfig index d1da6bc82c8b..51b703433848 100644 --- a/configs/qcom_ipq9574_mmc_defconfig +++ b/configs/qcom_ipq9574_mmc_defconfig @@ -12,7 +12,7 @@ CONFIG_SYS_LOAD_ADDR=0x50000000 CONFIG_DEBUG_UART_BASE=0x78b1000 CONFIG_DEBUG_UART_CLOCK=1843200 CONFIG_DEBUG_UART=y -CONFIG_REMAKE_ELF=y +CONFIG_QCOM_GENERATE_MBN=y # CONFIG_EFI_LOADER is not set CONFIG_FIT=y CONFIG_FIT_VERBOSE=y diff --git a/configs/qcom_lemans_defconfig b/configs/qcom_lemans_defconfig index 7045da8baeca..0d673d1b9a7a 100644 --- a/configs/qcom_lemans_defconfig +++ b/configs/qcom_lemans_defconfig @@ -8,7 +8,7 @@ # Address where U-Boot will be loaded CONFIG_TEXT_BASE=0xaf000000 -CONFIG_REMAKE_ELF=y +CONFIG_QCOM_GENERATE_MBN=y CONFIG_FASTBOOT_BUF_ADDR=0xdb300000 CONFIG_DEFAULT_DEVICE_TREE="qcom/lemans-evk" CONFIG_ENV_IS_IN_SCSI=y diff --git a/configs/qcom_lemans_spl_defconfig b/configs/qcom_lemans_spl_defconfig deleted file mode 100644 index d04397aadfd5..000000000000 --- a/configs/qcom_lemans_spl_defconfig +++ /dev/null @@ -1,103 +0,0 @@ -# Configuration for building U-Boot to be flashed -# to the uefi partition of Lemans-EVK dev boards with -# the "Linux Embedded" partition layout (which have -# a dedicated "uefi" partition for edk2/U-Boot) - -#include "qcom_defconfig" - -CONFIG_FASTBOOT_BUF_ADDR=0xdb300000 -CONFIG_DEFAULT_DEVICE_TREE="qcom/lemans-evk" -CONFIG_ENV_IS_IN_SCSI=y -CONFIG_ENV_SCSI_PART_USE_TYPE_GUID=y -# SCSI partition type GUID for logfs partition -CONFIG_ENV_SCSI_PART_TYPE_GUID="bc0330eb-3410-4951-a617-03898dbe3372" -# CONFIG_ENV_IS_DEFAULT is not set -# CONFIG_ENV_IS_NOWHERE is not set - -# SPL configurations for Lemans-EVK -# Purpose: Load FIT image (containing TFA, OPTEE and U-Boot proper) -# from UFS storage and jump to next image (TFA) - -CONFIG_SPL=y -CONFIG_SPL_BUILD=y -CONFIG_SPL_FRAMEWORK=y - -CONFIG_SPL_TEXT_BASE=0x1c100000 -CONFIG_SPL_MAX_SIZE=0x60000 -CONFIG_SPL_BSS_LIMIT=y -CONFIG_SPL_BSS_MAX_SIZE=0x10000 -# CONFIG_SPL_SEPARATE_BSS is not set - -# CONFIG_SPL_SHARES_INIT_SP_ADDR is not set -CONFIG_SPL_HAVE_INIT_STACK=y -CONFIG_SPL_STACK=0xD7300000 - -CONFIG_SPL_SYS_MALLOC_F_LEN=0x80000 -CONFIG_SPL_SYS_MALLOC=y -CONFIG_SPL_HAS_CUSTOM_MALLOC_START=y -CONFIG_SPL_CUSTOM_SYS_MALLOC_ADDR=0xdb300000 -CONFIG_SPL_SYS_MALLOC_SIZE=0x10000 - -CONFIG_SPL_LIBCOMMON_SUPPORT=y -CONFIG_SPL_LIBGENERIC_SUPPORT=y - -CONFIG_SPL_DM=y -CONFIG_SPL_OF_LIBFDT=y -CONFIG_SPL_OF_CONTROL=y -CONFIG_SPL_OF_REAL=y -CONFIG_SPL_SIMPLE_BUS=y - -CONFIG_SPL_DM_RESET=y - -CONFIG_SPL_CLK=y - -CONFIG_SPL_GPIO=y -CONFIG_SPL_DM_GPIO=y - -CONFIG_SPL_UFS=y -CONFIG_SPL_UFS_QCOM=y - -CONFIG_SPL_UFS_RAW_U_BOOT_DEVNUM=4 -CONFIG_SPL_UFS_RAW_U_BOOT_SECTOR=0x0 -CONFIG_SPL_UFS_RAW_U_BOOT_USE_PARTITION=y -CONFIG_SPL_UFS_RAW_U_BOOT_PARTITION_NAME="uefi_a" -CONFIG_SPL_UFS_RAW_U_BOOT_PARTITION_NUM=1 - -CONFIG_SPL_PARTITIONS=y -CONFIG_SPL_DOS_PARTITION=y -CONFIG_SPL_CHARSET=y - -CONFIG_SPL_PHY=y -CONFIG_SPL_PHY_QCOM_QMP_UFS=y - -CONFIG_SPL_POWER=y -CONFIG_SPL_POWER_DOMAIN=y - -CONFIG_SPL_LOAD_FIT=y - -CONFIG_SPL_ATF=y - -CONFIG_SPL_REMAKE_ELF=y - -CONFIG_COUNTER_FREQUENCY=19200000 - -# CONFIG_SAVE_PREV_BL_FDT_ADDR is not set -# CONFIG_SAVE_PREV_BL_INITRAMFS_START_ADDR is not set -CONFIG_SPL_ATF_LOAD_IMAGE_V2=y -CONFIG_SPL_ATF_NO_PLATFORM_PARAM=y -CONFIG_SPL_HAS_LOAD_FIT_ADDRESS=y -CONFIG_SPL_LOAD_FIT_ADDRESS=0xB0800000 -CONFIG_SPL_FRAMEWORK_BOARD_INIT_F=y - -CONFIG_SPL_DRIVERS_MISC=y -CONFIG_SPL_SERIAL=y -CONFIG_SPL_MISC=y -CONFIG_SPL_QCOM_GENI=y -CONFIG_SPL_MSM_GENI_SERIAL=y - -CONFIG_SPL_BANNER_PRINT=y -CONFIG_SPL_CLK_STUB=y -CONFIG_SPL_PINCTRL=y -CONFIG_SPL_PINCTRL_QCOM_SA8775P=y -CONFIG_SPL_QCOM_SMEM=y -# CONFIG_SPL_QCOM_BOOT_FROM_PBL is not set diff --git a/configs/qcom_qcs615_defconfig b/configs/qcom_qcs615_defconfig index da5ab1faadcd..9447806ce75c 100644 --- a/configs/qcom_qcs615_defconfig +++ b/configs/qcom_qcs615_defconfig @@ -16,7 +16,7 @@ CONFIG_DEBUG_UART_CLOCK=7372800 CONFIG_DEFAULT_DEVICE_TREE="qcom/talos-evk" -CONFIG_REMAKE_ELF=y +CONFIG_QCOM_GENERATE_MBN=y # Address where U-Boot will be loaded CONFIG_TEXT_BASE=0x9fc00000 diff --git a/configs/qcom_qcs8300_defconfig b/configs/qcom_qcs8300_defconfig index cc2fb7879e88..0d042dd4093d 100644 --- a/configs/qcom_qcs8300_defconfig +++ b/configs/qcom_qcs8300_defconfig @@ -16,7 +16,7 @@ CONFIG_DEBUG_UART_CLOCK=14745600 # Address where U-Boot will be loaded CONFIG_TEXT_BASE=0xaf000000 -CONFIG_REMAKE_ELF=y +CONFIG_QCOM_GENERATE_MBN=y CONFIG_DEFAULT_DEVICE_TREE="qcom/monaco-evk" CONFIG_PINCTRL_QCOM_QCS8300=y diff --git a/configs/qcom_sdm845_spl_defconfig b/configs/qcom_sdm845_spl_defconfig new file mode 100644 index 000000000000..375647e71bae --- /dev/null +++ b/configs/qcom_sdm845_spl_defconfig @@ -0,0 +1,137 @@ +CONFIG_ARM=y +CONFIG_SKIP_LOWLEVEL_INIT=y +CONFIG_COUNTER_FREQUENCY=19200000 +CONFIG_POSITION_INDEPENDENT=y +# CONFIG_INIT_SP_RELATIVE is not set +CONFIG_ARCH_SNAPDRAGON=y +CONFIG_TEXT_BASE=0x14880000 +CONFIG_SYS_MALLOC_LEN=0x20000 +CONFIG_HAS_CUSTOM_SYS_INIT_SP_ADDR=y +CONFIG_CUSTOM_SYS_INIT_SP_ADDR=0x146bffff +CONFIG_DEFAULT_DEVICE_TREE="qcom/sdm845-shift-axolotl" +CONFIG_SPL_SYS_MALLOC_F_LEN=0x20000 +CONFIG_SPL_SERIAL=y +CONFIG_SPL_DRIVERS_MISC=y +CONFIG_SPL_STACK=0x146bffff +CONFIG_SPL_TEXT_BASE=0x1483f000 +CONFIG_SPL_BSS_START_ADDR=0x14680000 +CONFIG_SPL_BSS_MAX_SIZE=0x2000 +CONFIG_SYS_BOOTM_LEN=0x4000000 +CONFIG_SYS_LOAD_ADDR=0x0 +CONFIG_WATCHDOG_TIMEOUT_MSECS=60000 +CONFIG_BOOT0_SDM845_WORKAROUND=y +CONFIG_SPL=y +CONFIG_SPL_PAYLOAD="u-boot.img" +CONFIG_SKIP_RELOCATE=y +# CONFIG_EFI_LOADER is not set +CONFIG_OF_BOARD_SETUP=y +CONFIG_USE_PREBOOT=y +CONFIG_CONSOLE_RECORD=y +CONFIG_CONSOLE_RECORD_OUT_SIZE=0xA000 +CONFIG_CONSOLE_RECORD_OUT_SIZE_F=0xA000 +CONFIG_LOGLEVEL=9 +CONFIG_SYS_STDIO_DEREGISTER=y +CONFIG_LOG_MAX_LEVEL=9 +CONFIG_SPL_LOG=y +CONFIG_SPL_LOG_MAX_LEVEL=9 +# CONFIG_DISPLAY_CPUINFO is not set +CONFIG_SPL_MAX_SIZE=0x7ffc0 +CONFIG_SPL_PAD_TO=0x0 +CONFIG_SPL_REMAKE_ELF_LDSCRIPT="board/qualcomm/sdm845_spl/u-boot-spl-elf-sdm845.lds" +CONFIG_SPL_DMA=y +CONFIG_SPL_REMAKE_ELF=y +CONFIG_SPL_DM_RESET=y +CONFIG_SPL_POWER_DOMAIN=y +CONFIG_BOOTM_NETBSD=y +CONFIG_CMD_CLK=y +CONFIG_CMD_DFU=y +CONFIG_CMD_GPIO=y +CONFIG_CMD_I2C=y +CONFIG_CMD_MMC=y +CONFIG_CMD_UFS=y +CONFIG_CMD_CAT=y +CONFIG_CMD_RNG=y +CONFIG_CMD_REGULATOR=y +CONFIG_CMD_LOG=y +CONFIG_OF_UPSTREAM_BUILD_VENDOR=y +CONFIG_ENV_USE_DEFAULT_ENV_TEXT_FILE=y +CONFIG_ENV_DEFAULT_ENV_TEXT_FILE="board/qualcomm/sdm845_spl/sdm845_spl.env" +CONFIG_NET_RANDOM_ETHADDR=y +# CONFIG_OFNODE_MULTI_TREE is not set +CONFIG_BUTTON_QCOM_PMIC=y +CONFIG_CLK=y +CONFIG_SPL_CLK=y +CONFIG_CLK_STUB=y +CONFIG_SPL_CLK_STUB=y +CONFIG_CLK_QCOM_SDM845=y +CONFIG_DFU_MMC=y +CONFIG_DFU_RAM=y +CONFIG_DFU_SCSI=y +CONFIG_SYS_DFU_DATA_BUF_SIZE=0x5000 +CONFIG_DMA=y +CONFIG_DMA_CHANNELS=y +CONFIG_USB_FUNCTION_FASTBOOT=y +CONFIG_FASTBOOT_BUF_ADDR=0xdeadbeef +CONFIG_MSM_GPIO=y +CONFIG_QCOM_PMIC_GPIO=y +CONFIG_DM_I2C=y +CONFIG_SYS_I2C_QUP=y +CONFIG_I2C_MUX=y +CONFIG_IOMMU=y +CONFIG_QCOM_HYP_SMMU=y +CONFIG_MISC=y +CONFIG_NVMEM=y +CONFIG_I2C_EEPROM=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_ADMA=y +CONFIG_MMC_SDHCI_MSM=y +CONFIG_DM_ETH_PHY=y +CONFIG_PHY=y +CONFIG_SPL_PHY=y +CONFIG_PHY_QCOM_QMP_UFS=y +CONFIG_PHY_QCOM_QUSB2=y +CONFIG_PHY_QCOM_USB_SNPS_FEMTO_V2=y +CONFIG_PHY_QCOM_SNPS_EUSB2=y +CONFIG_PHY_QCOM_USB_HS_28NM=y +CONFIG_PHY_QCOM_USB_SS=y +CONFIG_PINCTRL=y +CONFIG_PINCONF=y +CONFIG_PINCTRL_QCOM_APQ8016=y +CONFIG_PINCTRL_QCOM_APQ8096=y +CONFIG_PINCTRL_QCOM_QCM2290=y +CONFIG_PINCTRL_QCOM_QCS404=y +CONFIG_PINCTRL_QCOM_SDM845=y +CONFIG_PINCTRL_QCOM_SM6115=y +CONFIG_PINCTRL_QCOM_SM8250=y +CONFIG_PINCTRL_QCOM_SM8550=y +CONFIG_PINCTRL_QCOM_SM8650=y +CONFIG_PINCTRL_QCOM_X1E80100=y +CONFIG_DM_PMIC=y +CONFIG_PMIC_QCOM=y +CONFIG_DM_REGULATOR=y +CONFIG_DM_REGULATOR_FIXED=y +CONFIG_DM_REGULATOR_QCOM_RPMH=y +CONFIG_DM_RNG=y +CONFIG_RNG_MSM=y +CONFIG_SCSI=y +CONFIG_MSM_SERIAL=y +CONFIG_QCOM_COMMAND_DB=y +CONFIG_QCOM_RPMH=y +CONFIG_SPMI_MSM=y +CONFIG_SYSINFO=y +CONFIG_SYSINFO_SMBIOS=y +CONFIG_SYSRESET_QCOM_PSHOLD=y +CONFIG_USB=y +CONFIG_USB_DWC3=y +CONFIG_USB_DWC3_GENERIC=y +CONFIG_SPL_USB_DWC3_GENERIC=y +CONFIG_USB_GADGET=y +CONFIG_USB_GADGET_VENDOR_NUM=0x0525 +CONFIG_USB_GADGET_PRODUCT_NUM=0xb4a4 +CONFIG_USB_ETHER=y +CONFIG_USB_ETH_CDC=y +CONFIG_SPL_DFU=y +CONFIG_SPL_USB_SDP_SUPPORT=y +CONFIG_UFS=y +# CONFIG_SPL_USE_TINY_PRINTF is not set +CONFIG_CIRCBUF=y diff --git a/doc/board/qualcomm/board.rst b/doc/board/qualcomm/board.rst index eb800f8c535a..18119cec5b13 100644 --- a/doc/board/qualcomm/board.rst +++ b/doc/board/qualcomm/board.rst @@ -104,7 +104,7 @@ Use the following commands:: Or for db410c (and other boards not supported by the generic target):: - make CROSS_COMPILE=aarch64-linux-gnu- O=.output dragonboard410c_defconfig + make CROSS_COMPILE=aarch64-linux-gnu- O=.output qcom_dragonboard410c_defconfig make CROSS_COMPILE=aarch64-linux-gnu- O=.output -j$(nproc) Or for smartphones:: diff --git a/doc/board/qualcomm/dragonboard410c.rst b/doc/board/qualcomm/dragonboard410c.rst index 34629241110c..cc2d28e9dfda 100644 --- a/doc/board/qualcomm/dragonboard410c.rst +++ b/doc/board/qualcomm/dragonboard410c.rst @@ -21,27 +21,20 @@ Installation First, setup ``CROSS_COMPILE`` for aarch64. Then, build U-Boot for ``dragonboard410c``:: $ export CROSS_COMPILE= - $ make dragonboard410c_defconfig + $ make qcom_dragonboard410c_defconfig $ make -This will build ``u-boot.elf`` in the configured output directory. +This will build ``u-boot.mbn`` in the configured output directory. Although the DragonBoard 410c does not have secure boot set up by default, -the firmware still expects firmware ELF images to be "signed". The signature -does not provide any security in this case, but it provides the firmware with -some required metadata. +the firmware still expects firmware ELF images to be "signed". This is +handled automatically with mkmbn (see :doc:`signing` for more details). -To "sign" ``u-boot.elf`` you can use e.g. `qtestsign`_:: - - $ ./qtestsign.py aboot u-boot.elf - -Then install the resulting ``u-boot-test-signed.mbn`` to the ``aboot`` partition -on your device, e.g. with ``fastboot flash aboot u-boot-test-signed.mbn``. +Then install the resulting ``u-boot.mbn`` to the ``aboot`` partition +on your device, e.g. with ``fastboot flash aboot u-boot.mbn``. U-Boot should be running after a reboot (``fastboot reboot``). -.. _qtestsign: https://github.com/msm8916-mainline/qtestsign - Usage ----- Press Volume Down during boot to enter Fastboot mode. diff --git a/doc/board/qualcomm/dragonwing.rst b/doc/board/qualcomm/dragonwing.rst index d48994153095..028ec340d155 100644 --- a/doc/board/qualcomm/dragonwing.rst +++ b/doc/board/qualcomm/dragonwing.rst @@ -20,18 +20,13 @@ First, setup ``CROSS_COMPILE`` for aarch64. Then, build U-Boot for ``QCS615``, ` $ export CROSS_COMPILE= $ make qcom_qcs8300_defconfig - $ make -j8 u-boot.mbn + $ make -j8 Although the board does not have secure boot set up by default, -the firmware still expects firmware ELF images to be "signed". The signature -does not provide any security in this case, but it provides the firmware with -some required metadata. +the firmware still expects firmware ELF images to be "signed" in the MBN format. +This is handled automatically with mkmbn (see :doc:`signing` for more details). -To "sign" ``u-boot.elf`` you can use e.g. `qtestsign`_:: - - $ qtestsign -v6 aboot -o u-boot.mbn u-boot.elf - -Then flash the resulting ``u-boot.mbn`` to the ``uefi_a`` partition +Just flash the resulting ``u-boot.mbn`` to the ``uefi_a`` partition on your device with ``fastboot flash uefi_a u-boot.mbn``. U-Boot should be running after a reboot (``fastboot reboot``). @@ -44,6 +39,5 @@ the firehose loader can be obtained from `dragonwing IQ9 bootbinaries`.) :: $ edl.py --loader /path/to/prog_firehose_ddr.elf w uefi_a u-boot.mbn -.. _qtestsign: https://github.com/msm8916-mainline/qtestsign .. _edl: https://github.com/bkerler/edl .. _dragonwing IQ9 bootbinaries: https://artifacts.codelinaro.org/ui/native/qli-ci/flashable-binaries/qimpsdk/qcs9075-rb8-core-kit diff --git a/doc/board/qualcomm/index.rst b/doc/board/qualcomm/index.rst index 9854896f4ec2..5ef4be8483f7 100644 --- a/doc/board/qualcomm/index.rst +++ b/doc/board/qualcomm/index.rst @@ -14,7 +14,9 @@ Qualcomm iq8 phones rdp + signing snagboot + spl See also -------- diff --git a/doc/board/qualcomm/rb3gen2.rst b/doc/board/qualcomm/rb3gen2.rst index 518d01c4c3a3..329baa8d1f3a 100644 --- a/doc/board/qualcomm/rb3gen2.rst +++ b/doc/board/qualcomm/rb3gen2.rst @@ -18,32 +18,30 @@ First, setup ``CROSS_COMPILE`` for aarch64. Then, build U-Boot for ``qcm6490``:: $ export CROSS_COMPILE= $ make qcm6490_defconfig - $ make -j8 + $ make -j8 DEVICE_TREE=qcom/qcs6490-rb3gen2 -This will build ``u-boot.elf`` in the configured output directory. +This will build ``u-boot.mbn`` in the configured output directory. -Although the RB3 Gen 2 does not have secure boot set up by default, -the firmware still expects firmware ELF images to be "signed". The signature -does not provide any security in this case, but it provides the firmware with -some required metadata. - -To "sign" ``u-boot.elf`` you can use e.g. `qtestsign`_:: - - $ qtestsign -v6 aboot -o u-boot.mbn u-boot.elf +Although the board does not have secure boot set up by default, +the firmware still expects firmware ELF images to be "signed" in the MBN format. +This is handled automatically with mkmbn (see :doc:`signing` for more details). Then install the resulting ``u-boot.mbn`` to the ``uefi_a`` partition on your device with ``fastboot flash uefi_a u-boot.mbn``. U-Boot should be running after a reboot (``fastboot reboot``). -Note that fastboot is not yet supported in U-Boot on this board, as a result, -to flash back the original firmware, or new versoins of the U-Boot, EDL mode -must be used. This can be accessed by pressing the EDL mode button as described -in the Qualcomm Linux documentation. A tool like bkerler's `edl`_ can be used -for flashing with the firehose loader binary appropriate for the board. +Note that fastboot is not yet supported in U-Boot on this board, as a result, to flash +back the original firmware, or new versoins of the U-Boot, EDL mode must be used. This +can be accessed by holding the EDL button while powering on as described in the +Qualcomm Linux documentation. + +A tool like bkerler's `edl`_ can be used for flashing with the firehose loader from the `RB3 Gen 2 bootbinaries`. :: + + $ edl.py --loader /path/to/prog_firehose_ddr.elf w uefi_a u-boot.mbn -.. _qtestsign: https://github.com/msm8916-mainline/qtestsign .. _edl: https://github.com/bkerler/edl +.. _RB3 Gen 2 bootbinaries: https://artifacts.codelinaro.org/artifactory/qli-ci/software/chip/qualcomm_linux-spf-1-0/qualcomm-linux-spf-1-0_test_device_public/r1.0_00039.2/QCM6490.LE.1.0/common/build/ufs/bin/QCM6490_bootbinaries.zip Usage ----- diff --git a/doc/board/qualcomm/rdp.rst b/doc/board/qualcomm/rdp.rst index 354dc9d06e11..27101cc2d0dc 100644 --- a/doc/board/qualcomm/rdp.rst +++ b/doc/board/qualcomm/rdp.rst @@ -17,20 +17,25 @@ First, setup ``CROSS_COMPILE`` for aarch64. Then, build U-Boot for ``IPQ9574``:: $ export CROSS_COMPILE= $ make qcom_ipq9574_mmc_defconfig - $ make -j8 + $ make -j8 u-boot.mbn -This will build ``u-boot.elf`` in the configured output directory. +This will build the signed ``u-boot.mbn`` in the configured output directory. More information +about image signing can be found in :doc:`signing`. The firmware expects the ELF images to be in MBN format. The `elftombn.py` tool can be used to convert the ELF images to MBN format. - IPQ9574: (MBN version 6) +IPQ9574: (MBN version 6) - $ python elftombn.py -f u-boot.elf -o u-boot.mbn -v6 +.. code-block:: bash - IPQ5424: (MBN version 7) + python elftombn.py -f u-boot.elf -o u-boot.mbn -v6 - $ python elftombn.py -f u-boot.elf -o u-boot.mbn -v7 +IPQ5424: (MBN version 7) + +.. code-block:: bash + + python elftombn.py -f u-boot.elf -o u-boot.mbn -v7 Then install the resulting ``u-boot.mbn`` to the ``0:APPSBL`` partition on your device with:: @@ -55,66 +60,67 @@ Please refer to the following URLs for more details about the platforms. both the U-Boot SPL and U-Boot proper images. Assuming ${uboot_dir} is the top of the U-Boot sources and ${out_dir} as the output directory, - $ cd ${uboot_dir} - $ export CROSS_COMPILE= - $ make -j8 O=${out_dir} qcom_ipq5210_mmc_defconfig - $ make -j8 O=${out_dir} +.. code-block:: bash - U-Boot SPL image: ${out_dir}/spl/u-boot-spl.wrap-elf - U-Boot image: ${out_dir}/u-boot.elf + cd ${uboot_dir} + export CROSS_COMPILE= + make -j8 O=${out_dir} qcom_ipq5210_mmc_defconfig + make -j8 O=${out_dir} + +U-Boot SPL image: ${out_dir}/spl/u-boot-spl.wrap-elf +U-Boot image: ${out_dir}/u-boot.elf 2. Convert the SPL image to multi ELF - $ cd ${out_dir}/spl - $ python elftombn.py -f u-boot-spl.wrap-elf -o u-boot-spl.mbn -v7 - $ python `create_multielf.py` -f u-boot-spl.mbn,tmel-ipq52xx-patch.elf \ +.. code-block:: bash + + cd ${out_dir}/spl + python elftombn.py -f u-boot-spl.wrap-elf -o u-boot-spl.mbn -v7 + python `create_multielf.py` -f u-boot-spl.mbn,tmel-ipq52xx-patch.elf \ -o u-boot-spl.melf - This u-boot-spl.melf should be flashed into 0:SPL partition. - Please see below for the location of `tmel-ipq52xx-patch.elf` +This u-boot-spl.melf should be flashed into 0:SPL partition. +Please see below for the location of `tmel-ipq52xx-patch.elf` 3. Convert the U-Boot image to bootloader image - $ cd ${out_dir} - $ python elftombn.py -f u-boot.elf -o u-boot.mbn -v7 - - The u-boot.mbn has to be combined with `qc_config.elf`, `QCLib.elf`, `TFA` - and `OPTEE`. Please see below for the location for these ELFs. TFA and OPTEE - can be built from the sources using the following commands - - TFA: - $ make PLAT=ipq52xx QTISECLIB_PATH=path/to/`libqtisec_dbg.a` SPD=opteed - - OPTEE: - $ make PLATFORM=qcom-ipq52xx -j16 - - These binaries can be combined into a flashable image using binman. - - Assuming all the required binaries are available in ${uboot_dir}/binman - - $ export bm=${uboot_dir}/binman - $ cd ${bm} - $ ${uboot_dir}/tools/binman/binman \ - --toolpath ${uboot_dir}/tools build \ - -u \ - -d ${out_dir}/u-boot.dtb \ - -O ${bm}.out \ - -I ${uboot_dir} \ - -I ${bm} \ - -I ${uboot_dir}/board \ - -I ${out_dir}/dts/upstream/src/arm64 \ - -a of-list="qcom/ipq5210-rdp504" \ - -a atf-bl31-path=${bm}/bl31.mbn \ - -a tee-os-path=${bm}/tee-pager_v2.mbn \ - -a qcom-config-path=${bm}/qc_config.elf \ - -a qcom-lib-path=${bm}/QCLib.elf \ - -a qcom-appsbl-path=${out_dir}/u-boot.mbn \ - -a default-dt="qcom/ipq5210-rdp504" \ - -a spl-bss-pad=1 \ - -a spl-dtb=y \ - -a of-spl-remove-props="interrupt-parent interrupts" - - This should be flashed into 0:BOOTLDR partition. +.. code-block:: bash + + cd ${out_dir} + python elftombn.py -f u-boot.elf -o u-boot.mbn -v7 + +The u-boot.mbn has to be combined with `qc_config.elf`, `QCLib.elf`, `TFA` +and `OPTEE`. Please see below for the location for these ELFs. TFA and OPTEE +can be built from the sources using the following commands + +TFA: + +.. code-block:: bash + + make PLAT=ipq52xx QTISECLIB_PATH=path/to/`libqtisec_dbg.a` SPD=opteed + +OPTEE: + +.. code-block:: bash + + make PLATFORM=qcom-ipq52xx -j16 + +These binaries can be combined into a flashable image using `gen_its.py`. + +.. code-block:: bash + + python gen_its.py --arch ipq5210 \ + --qclib_path `QCLib.elf` \ + --qcconfig_path `qc_config.elf` \ + --tfa_bl31_path bl31.mbn \ + --uboot_path u-boot.mbn \ + --optee_path tee-raw.mbn \ + -p qcconfig qclib \ + -P tfa_bl31 uboot optee \ + -o output/hm_503_test_uboot.img \ + --template `template.its` + +This should be flashed into 0:BOOTLDR partition. .. WARNING Boards with newer software versions would automatically go the emergency @@ -122,7 +128,7 @@ Please refer to the following URLs for more details about the platforms. runtime failure at Uboot, the system will get reset (due to watchdog) and XBL will try to boot from next bank and if Bank B also doesn't have a functional image and is not booting fine, then the system will enter - EDL. A tool like bkerler's `edl` can be used for flashing with the + EDL. A tool like bkerler's `edl`_ can be used for flashing with the firehose loader binary appropriate for the board. Note that the support added is very basic. Restoring the original U-Boot @@ -131,9 +137,11 @@ Please refer to the following URLs for more details about the platforms. .. _create_multielf.py: https://raw.githubusercontent.com/coreboot/coreboot/refs/heads/main/util/qualcomm/create_multielf.py .. _elftombn.py: https://git.codelinaro.org/clo/qsdk/oss/system/tools/meta/-/tree/NHSS.QSDK.13.0.5.r2/scripts?ref_type=heads .. _edl: https://github.com/bkerler/edl +.. _gen_its.py: https://git.codelinaro.org/clo/qsdk/oss/system/tools/meta/-/tree/win.platform_tools.1.0.r34/scripts?ref_type=heads .. _libqtisec_dbg.a: https://softwarecenter.qualcomm.com/nexus/generic/product/chip/software-product/IPQ5210.NLQ.14.0/ipq5210.nlq.14.0-qca-oem-qartifact/r00036.1/WIN.TFA.1.0.R4/apss_proc/out/proprietary/qtiseclib/output/ipq52xx/release/libqtisec_dbg.a .. _OPTEE: https://git.codelinaro.org/clo/trusted-firmware/optee_os/optee_os/-/tree/win.optee.1.0?ref_type=heads .. _qc_config.elf: https://softwarecenter.qualcomm.com/nexus/generic/product/chip/software-product/IPQ5210.NLQ.14.0/ipq5210.nlq.14.0-qca-oem-qartifact/r00036.1/BOOT.MXF.2.3.1.1/boot_images/boot/QcomPkg/SocPkg/Hermosa/Bin/LC/RELEASE/qc_config.elf .. _QCLib.elf: https://softwarecenter.qualcomm.com/nexus/generic/product/chip/software-product/IPQ5210.NLQ.14.0/ipq5210.nlq.14.0-qca-oem-qartifact/r00036.1/BOOT.MXF.2.3.1.1/boot_images/boot/QcomPkg/SocPkg/Hermosa/Bin/LC/RELEASE/QCLib.elf +.. _template.its: https://git.codelinaro.org/clo/qsdk/oss/system/tools/meta/-/tree/win.platform_tools.1.0.r34/scripts?ref_type=heads .. _TFA: https://git.codelinaro.org/clo/trusted-firmware/tf-a/trusted-firmware-a/-/tree/win.tfa.1.0.r4?ref_type=heads .. _tmel-ipq52xx-patch.elf: https://softwarecenter.qualcomm.com/nexus/generic/product/chip/software-product/IPQ5210.NLQ.14.0/ipq5210.nlq.14.0-qca-oem-qartifact/r00036.1/TMEL.WNS.2.4/tmel-ipq52xx-patch.elf diff --git a/doc/board/qualcomm/signing.rst b/doc/board/qualcomm/signing.rst new file mode 100644 index 000000000000..317cd57cefee --- /dev/null +++ b/doc/board/qualcomm/signing.rst @@ -0,0 +1,29 @@ +.. SPDX-License-Identifier: GPL-2.0+ +.. sectionauthor:: Casey Connolly + +Qualcomm Image Signing +====================== + +On some boards like the RB3 Gen 2 where U-Boot runs as the first stage bootloader, +it must be in a Qualcomm specific signed ELF format called ``mbn``. + +For most boards this is handled automatically with the ``mkmbn`` tool in the U-Boot +build system. If you're bringing up a new platform which will run U-Boot as the first +stage bootloader, you may need to add your board and platform compatible string and +the load address used by your board to the ``boards`` table in ``tools/qcom/mkmbn/mkmbn.py``. + +For example: + +.. code-block:: python + + boards: dict[bytes, int] = { + # Exact matches for boards, these are preferred + # Don't forget the null terminator! + b"qcom,qcs6490-rb3gen2\0": MbnData(0x9FC00000, 6, SwId.aboot), + ... + } + + +When you run make to build the ``u-boot.mbn`` target, ``mkmbn`` will inspect the DTB in your +U-Boot image and try to match the compatible to the table, then it will build an ELF image and +hash/sign it per the MBN spec. diff --git a/doc/board/qualcomm/spl.rst b/doc/board/qualcomm/spl.rst new file mode 100644 index 000000000000..7d333194949f --- /dev/null +++ b/doc/board/qualcomm/spl.rst @@ -0,0 +1,91 @@ +.. SPDX-License-Identifier: GPL-2.0+ +.. sectionauthor:: Michael Srba + +=================================== +Booting U-Boot SPL on Qualcomm SoCs +=================================== + +Overview +-------- +The boot process on sdm845 (and some other Qualcomm SoCs) starts with the bootrom +of the Application Processor, which executes XBL_SEC, which jumps to "OEM" code +in EL1. Production devices are typically "fused", with a hash of the OEM's signing +key burnt into one of the "QFUSE" banks on the SoC making it impossible to run +custom bootloader code. As a result U-Boot SPL is only supported on unfused +("secureboot off") devices. XBL_SEC is always signed by Qualcomm, and the fuses +to disable turning off signature verification for it are always burnt at the +factory, so replacing XBL_SEC is impossible without using JTAG. Of course JTAG +is typically disabled on devices that have secure boot enabled, or at minimum +greatly neutered. + +U-Boot SPL for Qualcomm platforms uses a custom linker script (per SoC) to build a bootable ELF. +For sdm845 (and some other platforms) this has two sections, u-boot code and an embedded +xbl_sec elf (signed by Qualcomm). To boot on an unfused SoC, the elf additionally +needs to have hash sections added, which can be accomplished with qtestsign. + +Currently, sdm845 is supported. You need a device with secure boot disabled +(or with secure boot enabled if you enabled it yourself and have the private key, +though for full security you'd also want to disable JTAG which will remove your ability +to mess with the control flow in the bootrom (immutable) and in XBL_SEC (signed)). + +Building +-------- +First, obtain an xbl_sec that includes the EL3 privilege escalation feature +and place it at .output/xbl_sec.elf. You can extract it from an xbl elf. +If you're unable to find one, you can also use JTAG/SWD to break at the SMC +entry and use gdb to jump to the u-boot entry point in EL3. + +To build a bootable image, you need to use a defconfig specific to your SoC. +This is because the ELF has to specify where in the address space to put u-boot SPL, +and this may differ per SoC. There may be other SoC-dependent build time choices, +though in principle those could be made at runtime. + +First run ``make qcom_sdm845_spl_defconfig``:: + + make CROSS_COMPILE=aarch64-suse-linux- O=.output DEVICE_TREE=qcom/sdm845-shift-axolotl qcom_sdm845_spl_defconfig + +Then compile u-boot and specify the dts for your board (technically nothing about the resulting +SPL image should be board-specific, but there are no non-board-specific device trees in Linux):: + + make CROSS_COMPILE=aarch64-suse-linux- O=.output DEVICE_TREE=qcom/sdm845-shift-axolotl + +Finally, use ``qtestsign`` to add the hash segments required by PBL:: + + qtestsign -v 5 -o .output/spl/u-boot-spl_signed.elf prog .output/spl/u-boot-spl.elf + +Running +------- +Currently, U-Boot SPL for Qualcomm platforms expects to be booted via EDL:: + + edl.py --loader=$PWD/.output/spl/u-boot-spl_signed.elf + +SPL will then launch the DFU gadget and wait for you to upload u-boot proper:: + + dfu-util -RD .output/u-boot.img + +u-boot proper will then likely crash, since SPL currently doesn't init DRAM on Qualcomm platforms +and u-boot proper currently doesn't support running from SRAM. The latter should be an easy fix. + +Notes on memory map +------------------- +| There are various banks of SRAM on a Qualcomm SoC that we can use prior to DRAM init. +| For example: +| msm8916 - 512K L2-as-TCM (at ``0x08000000``), 16K OCIMEM (at ``0x08600000``) +| msm8998 - 1M L2-as-TCM (at ``0x14000000``), 256K OCIMEM (at ``0x14680000``) +| sdm845 - 1.5M BOOT_IMEM (at ``0x14800000``), 256K OCIMEM (at ``0x14680000``) + +There's also RPM code/data RAM and hexagon TCMs, but unless we want to boot dram-less Linux +we can probably safely ignore those. On msm8916 they may come in handy though. + +sdm845 can also have 8M LLCC-as-TCM in theory, but this appears to be broken. +L2-as-TCM is no longer present. + +Since a limited amount of not necessarily continuous SRAM is available, we need to manually +specify where .text, .bss, the malloc pool and the stack go. The Kconfig contains reasonable +defaults per SoC. + +On sdm845, we by default put U-Boot SPL in BOOT_IMEM, with .bss, malloc pool and the stack +filling OCIMEM. We can also fit U-Boot proper in BOOT_IMEM, for dram-less DFU or peek/poke +with a shell. To that end, we set ``CONFIG_TEXT_BASE`` at 512K into BOOT_IMEM, and set +``CONFIG_SPL_MAX_SIZE`` to 512K - 64. We also configure dfu to load U-Boot proper +to ``CONFIG_TEXT_BASE`` - 64. (64 bytes is the size of u-boot legacy header) diff --git a/drivers/Makefile b/drivers/Makefile index 6c1bdfefbea7..1cc23adf35f1 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -35,6 +35,7 @@ obj-$(CONFIG_$(PHASE_)RAM) += ram/ obj-$(CONFIG_$(PHASE_)RTC) += rtc/ obj-$(CONFIG_$(PHASE_)SERIAL) += serial/ obj-$(CONFIG_$(PHASE_)SPI) += spi/ +obj-$(CONFIG_$(PHASE_)SPMI) += spmi/ obj-$(CONFIG_$(PHASE_)TIMER) += timer/ obj-$(CONFIG_$(PHASE_)VIRTIO) += virtio/ obj-$(CONFIG_$(PHASE_)DM_MAILBOX) += mailbox/ @@ -74,7 +75,6 @@ obj-$(CONFIG_SPL_SATA) += ata/ scsi/ obj-$(CONFIG_SPL_LEGACY_BLOCK) += block/ obj-$(CONFIG_SPL_THERMAL) += thermal/ obj-$(CONFIG_SPL_UFS) += scsi/ ufs/ -obj-$(CONFIG_SPL_SMEM) += smem/ endif endif @@ -107,7 +107,6 @@ obj-$(CONFIG_DM_REBOOT_MODE) += reboot-mode/ obj-y += rtc/ obj-y += scsi/ obj-y += sound/ -obj-y += spmi/ obj-y += watchdog/ obj-$(CONFIG_QE) += qe/ obj-$(CONFIG_U_QE) += qe/ diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 03f285652fb8..c97083a5252d 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -115,11 +115,11 @@ config CLK_STUB controllers. config SPL_CLK_STUB - bool "Stub clock driver in SPL" + bool "Stub clock driver" depends on SPL_CLK help - Enable this to provide a stub clock driver for non-essential clock - controllers in U-Boot SPL. + Enable this to provide a stub clock driver in SPL for non-essential + clock controllers config CLK_BCM6345 bool "Clock controller driver for BCM6345" diff --git a/drivers/clk/qcom/clock-ipq5210.c b/drivers/clk/qcom/clock-ipq5210.c index 2e6037b64100..cd82b238bea9 100644 --- a/drivers/clk/qcom/clock-ipq5210.c +++ b/drivers/clk/qcom/clock-ipq5210.c @@ -21,10 +21,6 @@ static ulong ipq5210_set_rate(struct clk *clk, ulong rate) { struct msm_clk_priv *priv = dev_get_priv(clk->dev); - /* - * Since GATE_CLK is deprecated and GATE_CLK_POLLED is being used - * use 'cbcr_reg' instead of 'reg'. - */ switch (clk->id) { case GCC_QUPV3_WRAP_SE1_CLK: clk_rcg_set_rate_mnd(priv->base, priv->data->clks[clk->id].cbcr_reg, @@ -47,7 +43,6 @@ static const struct gate_clk ipq5210_clks[] = { GATE_CLK_POLLED(GCC_QUPV3_WRAP_SE1_CLK, 0x05020, BIT(0), 0x05004), GATE_CLK_POLLED(GCC_SDCC1_AHB_CLK, 0x3303c, BIT(0), 0x3303c), GATE_CLK_POLLED(GCC_SDCC1_APPS_CLK, 0x3302c, BIT(0), 0x33004), - GATE_CLK_POLLED(GCC_IM_SLEEP_CLK, 0x34020, BIT(0), 0x34020), }; static int ipq5210_enable(struct clk *clk) diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 09ec40f25b0f..baba41ee2517 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -119,7 +119,6 @@ config SPL_QCOM_GENI Enable support for Qualcomm GENI and it's peripherals in SPL. GENI is responseible for providing a common interface for various peripherals like UART, I2C, SPI, etc. - config QCOM_GENI_MINICORE bool "Support minicores in Qualcomm Generic Interface (GENI) driver" depends on QCOM_GENI diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 791ba1e1d05e..49466d93f53e 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -65,10 +65,11 @@ obj-$(CONFIG_QFW_MMIO) += qfw_mmio.o obj-$(CONFIG_QFW_SMBIOS) += qfw_smbios.o obj-$(CONFIG_SANDBOX) += qfw_sandbox.o endif -obj-$(CONFIG_$(PHASE_)QCOM_SPMI_SDAM) += qcom-spmi-sdam.o +obj-$(CONFIG_QCOM_SPMI_SDAM) += qcom-spmi-sdam.o obj-$(CONFIG_$(PHASE_)QCOM_GENI) += qcom_geni.o -obj-$(CONFIG_QCOM_GENI_MINICORE) += qcom_geni-minicore.o obj-$(CONFIG_$(PHASE_)QCOM_HWINFO) += qcom_hwinfo.o +obj-$(CONFIG_QCOM_GENI) += qcom_geni.o +obj-$(CONFIG_QCOM_GENI_MINICORE) += qcom_geni-minicore.o obj-$(CONFIG_$(PHASE_)ROCKCHIP_EFUSE) += rockchip-efuse.o obj-$(CONFIG_$(PHASE_)ROCKCHIP_OTP) += rockchip-otp.o obj-$(CONFIG_$(PHASE_)ROCKCHIP_IODOMAIN) += rockchip-io-domain.o diff --git a/drivers/misc/qcom_geni-minicore.c b/drivers/misc/qcom_geni-minicore.c index 33bc61ddf350..12c57bdd8b24 100644 --- a/drivers/misc/qcom_geni-minicore.c +++ b/drivers/misc/qcom_geni-minicore.c @@ -5,6 +5,7 @@ #include #include +#include /* * Register configuration for the QUP minicores to setup the corresponding diff --git a/drivers/misc/qcom_geni.c b/drivers/misc/qcom_geni.c index 40044e5d48a6..f3133f858858 100644 --- a/drivers/misc/qcom_geni.c +++ b/drivers/misc/qcom_geni.c @@ -21,6 +21,7 @@ #include #include #include +#include #include struct qup_se_rsc { @@ -34,14 +35,10 @@ struct qup_se_rsc { struct geni_se_plat { bool need_firmware_load; -#if IS_ENABLED(CONFIG_QCOM_GENI_MINICORE) bool is_mini_core; -#endif }; -#if IS_ENABLED(CONFIG_QCOM_GENI_MINICORE) -extern struct qup_mini_core_info qup_mini_cores[]; -#endif +static int qcom_geni_fw_initialise(void); /** * geni_enable_interrupts() Enable interrupts. @@ -172,6 +169,7 @@ static void geni_config_common_control(struct qup_se_rsc *rsc) static int load_se_firmware(struct qup_se_rsc *rsc, bool elf, void *info) { + struct geni_se_plat *plat = dev_get_plat(rsc->dev->parent); struct elf_se_hdr *hdr, tmp_hdr; const u32 *fw_val_arr, *cfg_val_arr; const u8 *cfg_idx_arr; @@ -183,7 +181,7 @@ static int load_se_firmware(struct qup_se_rsc *rsc, bool elf, void *info) fw_val_arr = (const u32 *)((u8 *)hdr + hdr->fw_offset); cfg_idx_arr = (const u8 *)hdr + hdr->cfg_idx_offset; cfg_val_arr = (const u32 *)((u8 *)hdr + hdr->cfg_val_offset); - } else if (IS_ENABLED(CONFIG_QCOM_GENI_MINICORE)) { + } else if (plat->is_mini_core) { /* * Minicore controllers come with pre-configured functionality * and don't need a firmware download and just need the register @@ -433,6 +431,10 @@ int qcom_geni_load_firmware(phys_addr_t qup_base, } else { elf = false; info = fw; + if (!IS_ENABLED(CONFIG_QCOM_GENI_MINICORE)) { + dev_err(dev, "Error: f/w ELF not found and minicore support disabled\n"); + return -EINVAL; + } } dev_info(dev, "Loading QUP firmware...\n"); @@ -467,10 +469,8 @@ static int geni_se_of_to_plat(struct udevice *dev) if (proto == GENI_SE_INVALID_PROTO) plat->need_firmware_load = true; -#if IS_ENABLED(CONFIG_QCOM_GENI_MINICORE) if (readl(res.start + SE_HW_PARAM_2) & GENI_USE_MINICORES) plat->is_mini_core = true; -#endif } return 0; @@ -549,7 +549,7 @@ static int probe_children_load_firmware(struct udevice *dev) * Load firmware for QCOM GENI peripherals from the dedicated partition on storage and bind/probe * all the peripheral devices that need firmware to be loaded. */ -int qcom_geni_fw_initialise(void) +static int qcom_geni_fw_initialise(void) { debug("Loading firmware for QCOM GENI SE\n"); struct udevice *geni_wrapper, *blk_dev; @@ -575,12 +575,11 @@ int qcom_geni_fw_initialise(void) return 0; } -#if IS_ENABLED(CONFIG_QCOM_GENI_MINICORE) if (plat->is_mini_core) { fw_buf = qup_mini_cores; goto mini_core; } -#endif + ret = find_qupfw_part(&blk_dev, &part_info); if (ret) { pr_err("QUP firmware partition not found\n"); @@ -607,9 +606,7 @@ int qcom_geni_fw_initialise(void) return 0; } -#if IS_ENABLED(CONFIG_QCOM_GENI_MINICORE) mini_core: -#endif /* * OK! Firmware is loaded, now bind and probe remaining children. They will attempt to load * firmware during probe. Do this for each GENI SE wrapper that needs firmware loading. @@ -629,14 +626,7 @@ int qcom_geni_fw_initialise(void) return 0; } -#if CONFIG_XPL_BUILD -int qcom_geni_fw_probe(struct udevice *dev) -{ - return qcom_geni_fw_initialise(); -} -#else EVENT_SPY_SIMPLE(EVT_LAST_STAGE_INIT, qcom_geni_fw_initialise); -#endif static const struct udevice_id geni_ids[] = { { .compatible = "qcom,geni-se-qup" }, @@ -650,7 +640,4 @@ U_BOOT_DRIVER(geni_se_qup) = { .of_to_plat = geni_se_of_to_plat, .plat_auto = sizeof(struct geni_se_plat), .flags = DM_FLAG_DEFAULT_PD_CTRL_OFF, -#if CONFIG_XPL_BUILD - .probe = qcom_geni_fw_probe, -#endif }; diff --git a/drivers/mmc/msm_sdhci.c b/drivers/mmc/msm_sdhci.c index 71a5aa159584..155bc2ef3791 100644 --- a/drivers/mmc/msm_sdhci.c +++ b/drivers/mmc/msm_sdhci.c @@ -558,6 +558,7 @@ static int sdhci_msm_hs400_dll_calibration(struct sdhci_host *host) return sdhci_msm_cm_dll_sdc4_calibration(host); } +#if CONFIG_IS_ENABLED(MMC_SUPPORTS_TUNING) static int msm_find_most_appropriate_phase(struct sdhci_host *host, u8 *phase_table, u8 total_phases) @@ -711,6 +712,7 @@ static int sdhci_msm_execute_tuning(struct mmc *mmc, u8 opcode) return rc; } +#endif /* * Configure HC mode selection. Runs from set_control_reg(), which the @@ -907,7 +909,9 @@ static int msm_sdhci_config_dll(struct sdhci_host *host, u32 clock, bool enable) struct sdhci_ops msm_sdhci_ops = { .config_dll = &msm_sdhci_config_dll, .set_control_reg = &sdhci_msm_set_control_reg, +#if CONFIG_IS_ENABLED(MMC_SUPPORTS_TUNING) .platform_execute_tuning = &sdhci_msm_execute_tuning, +#endif }; static int msm_sdc_probe(struct udevice *dev) diff --git a/drivers/pinctrl/qcom/Kconfig b/drivers/pinctrl/qcom/Kconfig index ba866b5b6b2e..fbf91b752161 100644 --- a/drivers/pinctrl/qcom/Kconfig +++ b/drivers/pinctrl/qcom/Kconfig @@ -84,6 +84,14 @@ config PINCTRL_QCOM_IPQ5210 Say Y here to enable support for pinctrl on the IPQ5210 SoC, as well as the associated GPIO driver. +config SPL_PINCTRL_QCOM_IPQ5210 + bool "Qualcomm IPQ5210 Pinctrl in SPL" + depends on SPL_PINCTRL_GENERIC + select SPL_PINCTRL_QCOM + help + SPL variant of PINCTRL_QCOM_IPQ5210. + See the help of PINCTRL_QCOM_IPQ5210 for details. + config PINCTRL_QCOM_IPQ5424 bool "Qualcomm IPQ5424 Pinctrl" default y if PINCTRL_QCOM_GENERIC diff --git a/drivers/spmi/Kconfig b/drivers/spmi/Kconfig index e28fd9af1d02..f89723711501 100644 --- a/drivers/spmi/Kconfig +++ b/drivers/spmi/Kconfig @@ -8,12 +8,25 @@ config SPMI SPMI (System Power Management Interface) bus is used to connect PMIC devices on various SoCs. +config SPL_SPMI + bool "Enable SPMI bus support in SPL" + depends on SPL_DM + help + Select this to enable SPMI bus support in SPL + config SPMI_MSM bool "Support Qualcomm SPMI bus" depends on SPMI help Support SPMI bus implementation found on Qualcomm Snapdragon SoCs. +config SPL_SPMI_MSM + bool "Support Qualcomm SPMI bus" + depends on SPL_SPMI + help + Support SPMI bus implementation found on Qualcomm Snapdragon SoCs + in SPL. + config SPMI_SANDBOX bool "Support for Sandbox SPMI bus" depends on SPMI diff --git a/dts/Kconfig b/dts/Kconfig index dcabe33c1c1a..d56d94679f5d 100644 --- a/dts/Kconfig +++ b/dts/Kconfig @@ -86,6 +86,14 @@ config OF_LIVE enables a live tree which is available after relocation, and can be adjusted as needed. +config SPL_OF_LIVE + bool "Enable use of a live tree in SPL" + depends on SPL_DM && SPL_OF_CONTROL + help + This option enables a live tree in SPL, allowing the sharing + of OF fixup code between U-Boot proper and SPL. + See also OF_LIVE. + config OF_UPSTREAM bool "Enable use of devicetree imported from Linux kernel release" depends on !COMPILE_TEST && !SANDBOX diff --git a/include/configs/ipq5210.h b/include/configs/ipq5210.h new file mode 100644 index 000000000000..5cbf838af824 --- /dev/null +++ b/include/configs/ipq5210.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __CONFIGS_IPQ5210_H +#define __CONFIGS_IPQ5210_H + +#include + +#endif diff --git a/include/soc/qcom/minicore.h b/include/soc/qcom/minicore.h new file mode 100644 index 000000000000..9f2071ed86ba --- /dev/null +++ b/include/soc/qcom/minicore.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) Qualcomm Innovation Center, Inc. All rights reserved. + */ +#ifndef _SOC_QCOM_MINI_CORE +#define _SOC_QCOM_MINI_CORE + +struct qup_mini_core_info { + u16 serial_protocol; + u16 fw_version; + u16 cfg_version; + u16 cfg_count; + u32 *cfg_val; + u8 *cfg_idx; + u32 *cfg_ram; + u32 cfg_ram_count; +}; + +#if IS_ENABLED(CONFIG_QCOM_GENI_MINICORE) +extern struct qup_mini_core_info qup_mini_cores[]; +#else +struct qup_mini_core_info *qup_mini_cores; +#endif + +#endif /* _SOC_QCOM_MINI_CORE */ diff --git a/include/soc/qcom/qup-fw-load.h b/include/soc/qcom/qup-fw-load.h index b329a18ef22c..df8a615b46bf 100644 --- a/include/soc/qcom/qup-fw-load.h +++ b/include/soc/qcom/qup-fw-load.h @@ -174,17 +174,6 @@ struct elf_se_hdr { struct udevice; -struct qup_mini_core_info { - u16 serial_protocol; - u16 fw_version; - u16 cfg_version; - u16 cfg_count; - u32 *cfg_val; - u8 *cfg_idx; - u32 *cfg_ram; - u32 cfg_ram_count; -}; - int qcom_geni_load_firmware(phys_addr_t qup_base, struct udevice *dev); #endif /* _LINUX_QCOM_QUP_FW_LOAD */ diff --git a/include/soc/qcom/smem.h b/include/soc/qcom/smem.h index 9e50f3974a2e..586432412eb8 100644 --- a/include/soc/qcom/smem.h +++ b/include/soc/qcom/smem.h @@ -16,10 +16,6 @@ int qcom_smem_alloc(unsigned host, unsigned item, size_t size); void *qcom_smem_get(unsigned host, unsigned item, size_t *size); int qcom_smem_get_free_space(unsigned host); - -#define SMEM_BOOT_FLASH_TYPE 498 -#define SMEM_BOOT_MMC_FLASH 5 - #else static int qcom_smem_init(void) { return -ENOSYS; } diff --git a/lib/Makefile b/lib/Makefile index bef575e9199c..d25b5b4b8a9c 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -13,7 +13,6 @@ obj-$(CONFIG_FWU_MULTI_BANK_UPDATE) += fwu_updates/ obj-$(CONFIG_LZMA) += lzma/ obj-$(CONFIG_BZIP2) += bzip2/ obj-$(CONFIG_FIT) += libfdt/ -obj-$(CONFIG_OF_LIVE) += of_live.o obj-$(CONFIG_CMD_DHRYSTONE) += dhry/ obj-$(CONFIG_ARCH_AT91) += at91/ obj-$(CONFIG_OPTEE_LIB) += optee/ @@ -56,6 +55,7 @@ obj-y += list_sort.o obj-$(CONFIG_PMBUS) += pmbus.o endif +obj-$(CONFIG_$(PHASE_)OF_LIVE) += of_live.o obj-$(CONFIG_$(PHASE_)TPM) += tpm-common.o ifeq ($(CONFIG_$(PHASE_)TPM),y) obj-$(CONFIG_TPM) += tpm_api.o diff --git a/scripts/Makefile.xpl b/scripts/Makefile.xpl index 6e9f24b748a6..a3fd3e1375f7 100644 --- a/scripts/Makefile.xpl +++ b/scripts/Makefile.xpl @@ -256,26 +256,6 @@ MKIMAGEFLAGS_boot.bin = -T zynqmpimage -R $(srctree)/$(CONFIG_BOOT_INIT_FILE) \ -n "$(shell cd $(srctree); readlink -f $(CONFIG_PMUFW_INIT_FILE))" endif -ifeq ($(CONFIG_SPL_WRAPPER_ELF),y) -# Convert ELF to object file -OBJCOPYFLAGS_$(SPL_BIN).bin.o = -I binary -O elf64-littleaarch64 - -# Wrap the object file inside a ELF -QCOM_SPL_SOC = $(shell echo $(notdir "$(CONFIG_DEFAULT_DEVICE_TREE)") | cut -f1 -d-) -QCOM_SPL_WRAP_LDS = $(srctree)/arch/arm/mach-snapdragon/$(QCOM_SPL_SOC)-spl-wrap-elf.lds -LDFLAGS_$(SPL_BIN).wrap-elf = -T $(obj)/$(SPL_BIN).wrap-elf.lds - -$(obj)/$(SPL_BIN).wrap-elf.lds: $(QCOM_SPL_WRAP_LDS) FORCE - $(call if_changed_dep,cpp_lds) - -$(obj)/$(SPL_BIN).bin.o: $(obj)/$(SPL_BIN).bin $(obj)/$(SPL_BIN).wrap-elf.lds FORCE - $(call if_changed,objcopy) - -$(obj)/$(SPL_BIN).wrap-elf: $(obj)/$(SPL_BIN).bin.o FORCE - $(call if_changed,ld) - -endif - $(obj)/$(SPL_BIN)-align.bin: $(obj)/$(SPL_BIN).bin @dd if=$< of=$@ conv=sync bs=4 2>/dev/null; @@ -322,10 +302,6 @@ INPUTS-$(CONFIG_ARCH_ZYNQMP) += $(obj)/boot.bin INPUTS-$(CONFIG_ARCH_MEDIATEK) += $(obj)/u-boot-spl-mtk.bin -ifeq ($(CONFIG_ARCH_SNAPDRAGON),y) -INPUTS-$(CONFIG_SPL_WRAPPER_ELF) += $(obj)/u-boot-spl.wrap-elf -endif - all: $(INPUTS-y) quiet_cmd_cat = CAT $@ diff --git a/tools/binman/elf.py b/tools/binman/elf.py index b43cf490b377..6ac960e04196 100644 --- a/tools/binman/elf.py +++ b/tools/binman/elf.py @@ -551,7 +551,6 @@ def read_loadable_segments(data): raise ValueError(err) entry = elf.header['e_entry'] segments = [] - n = 0 for i in range(elf.num_segments()): segment = elf.get_segment(i) if segment['p_type'] != 'PT_LOAD' or not segment['p_memsz']: @@ -559,8 +558,7 @@ def read_loadable_segments(data): continue start = segment['p_offset'] rend = start + segment['p_filesz'] - segments.append((n, segment['p_paddr'], data[start:rend])) - n = n + 1 + segments.append((i, segment['p_paddr'], data[start:rend])) return segments, entry def is_valid(data): diff --git a/tools/binman/etype/qcom_appsbl.py b/tools/binman/etype/qcom_appsbl.py deleted file mode 100644 index e308ebd2fca3..000000000000 --- a/tools/binman/etype/qcom_appsbl.py +++ /dev/null @@ -1,18 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. -# -# Entry-type module for U-Boot MBN image -# - -from binman.etype.blob_named_by_arg import Entry_blob_named_by_arg - -class Entry_qcom_appsbl(Entry_blob_named_by_arg): - """U-Boot mbn image - - Properties / Entry arguments: - - filename: Filename of u-boot MBN (default 'u-boot.mbn') - - This is the U-Boot MBN image. - """ - def __init__(self, section, etype, node): - super().__init__(section, etype, node, 'qcom-appsbl') diff --git a/tools/binman/etype/qcom_config.py b/tools/binman/etype/qcom_config.py deleted file mode 100644 index a1787c97b34e..000000000000 --- a/tools/binman/etype/qcom_config.py +++ /dev/null @@ -1,21 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. -# -# Entry-type module for QC Config ELF -# - -from binman.entry import Entry -from binman.etype.blob_named_by_arg import Entry_blob_named_by_arg - -class Entry_qcom_config(Entry_blob_named_by_arg): - """QC Config ELF - - Properties / Entry arguments: - - qcom-config-path: Filename of QC Config ELF (typically 'qcconfig.elf') - - This will be part of the Qualcomm SPL based bootloader image - - """ - def __init__(self, section, etype, node): - super().__init__(section, etype, node, 'qcom-config') - self.external = True diff --git a/tools/binman/etype/qcom_lib.py b/tools/binman/etype/qcom_lib.py deleted file mode 100644 index ca7666fe6e63..000000000000 --- a/tools/binman/etype/qcom_lib.py +++ /dev/null @@ -1,21 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. -# -# Entry-type module for QC LIB ELF -# - -from binman.entry import Entry -from binman.etype.blob_named_by_arg import Entry_blob_named_by_arg - -class Entry_qcom_lib(Entry_blob_named_by_arg): - """QC LIB ELF - - Properties / Entry arguments: - - qcom-lib-path: Filename of QC LIB ELF (typically 'QCLib.elf') - - This will be part of the Qualcomm SPL based bootloader image - - """ - def __init__(self, section, etype, node): - super().__init__(section, etype, node, 'qcom-lib') - self.external = True diff --git a/tools/mkmbn b/tools/mkmbn new file mode 120000 index 000000000000..a7b2096756f7 --- /dev/null +++ b/tools/mkmbn @@ -0,0 +1 @@ +qcom/mkmbn/mkmbn.py \ No newline at end of file diff --git a/tools/qcom/mkmbn/cert.py b/tools/qcom/mkmbn/cert.py new file mode 100644 index 000000000000..e14f88746d53 --- /dev/null +++ b/tools/qcom/mkmbn/cert.py @@ -0,0 +1,127 @@ +# SPDX-License-Identifier: GPL-2.0-only +# Copyright (C) 2021-2022 Stephan Gerhold +# See https://www.qualcomm.com/media/documents/files/secure-boot-and-image-authentication-technical-overview-v1-0.pdf +# Somewhat based on code snippets from https://cryptography.io/en/latest/x509/tutorial.html +from __future__ import annotations + +from datetime import datetime +from typing import List + +from cryptography import x509 +from cryptography.hazmat.primitives import hashes, serialization +from cryptography.x509.oid import NameOID + +# NOTE: The certificate chain generated by qtestsign is NOT meant +# to be secure. The private keys are listed here to make the +# resulting files reproducible. THESE KEYS SHOULD ONLY BE USED +# FOR TESTING AND NOT FOR A PROPER SECURE BOOT SETUP. + +ROOT_KEY = serialization.load_pem_private_key(b""" +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCjZqF/BwggY4Rs +Q1/wSNPLEKQEROZ9i/d+7CXZCukWph+SKHlv652oiAp+TgzIGQQXDlaA+qUoXUjp +g2KTmoulfQjrgc5CSCk6yA01VxNBqR81JorJx8aD9ApOFVoERlmWhZcR3B/LsVyd +vYgwFNNkqUh7fyywyy1Z1ijk4SyJVak1VxfdkTTeb1wr5Awjvh82PrdRQfOvctFH +mVITqdMckdRD3Sx7y8EvypAYpAUiWklNgditetXFjMoV6XyXTPCRkH9zzskrXP6i +neCyS7xUfEYPYNpabzhpdvkx9Is2PlCJA1fZ1ERZsWcag5vDZa3SHslH5Kh9+ssH +ps0Ul1j5AgMBAAECggEAHUEzOdBy+oWGwHFhnF4VmT4t91u0npawJYe3EQBckgMF +FQBtGYYoMHPG2S01KaAc9NnK0AXQCwWEl9Y/kGizhtn3fl67pG9R/mWxw7KGzpMu +dLAlWhIL7zUCoU8+UhScVpAtZ3OvN6NWDyHPX7hizptmUEIJKM//mx12LeBIvn+P +8tSiBXxoDGl0JZ+QMzmshOUXLLnxKITgBGL+G9A1qTZHIs6VV7HWH1ptfObulBZf +yEBK1YBzI6GnBGzLOWnZqGsSbQ717SObQo5rCoRDZB7z4bXNWDEvuH+rqzcs5liu +af4gmBHNOLGh+Ta5HJ0XeoqU5ANOWlUi95/n2dJufwKBgQDNaMlT1937SHPv/eBq +Be3MobllTx4vMYh6CtfP8QozTE+sTcmCyvaWVfXwLnQTl//+siefoWsvzW43LaNU +3A18nCxVFSSbWosBN+0Zo4K9bSpEFGgUrJM5O98zv4+/SzCKFe2562usDzaRiEUW +iSJkzIUnSlcNc+XCY1rhG9HLXwKBgQDLpS6ATtMDSP9p+XYVMEN2CF8M3xvL roOT +6wPYfp9fuagMgzNv9GB9SRyM/dM6mN+fkBqLp3EbDZT0UorHsg+YChoyBmctNqpW +j5/SrVyYe2xoRRgOzUbDstN44/LAhJLQnOXB7S2amo35zZ4FY6sw2w3QfkCildkB +mY3VhvESpwKBgErLtUPKfxJZN55UG7t/nS++U/wH6z3UE5YdDKizZLt5NinPyWjO +7yue8Ycb4zifSKA9zx/Zb2Zgr5l4DNmBp4eQdrQklsfbGHLBIp0LZTgE4DcaFyww +Cwv0OTpmrrlBb9NYWNAyYWqtv3kO3dlu5g8+Sd4cu8YyRZ+a/iSqNKKRAoGBAIPf +QICYCq8a60Lt5xiLe3QIsbx9EdvQ86Wqz3+3Z28uo3MO1xVNc9pNqO5oRAuzCUSj +pXz//g9duTKJ7RKp7M0w5Yu1d8TgnGeXdBCScN7RNf9DlvOm3IdH2wdy3TTr5MKw +h1wQQbLXGM9F5mlpBGeLwqNbznE6hh8yF5XJX30LAoGBAJqaa+yeZskti5ickNTF +vBBIXyYYBymdxfkf9vDSW1XcZEIVqo3+AGV+qHyTjURaty3QuEhSJEXem/obH5uE +y37+bnx8Se1IyJ/phYBLwOmtgZoBJALFhvjkFiGTF6naI8E/i4sbi5j/OEyShfWr +YFZuEKQJhiiMQznfNgthHU6H +-----END PRIVATE KEY----- +""", password=None) + +ATT_KEY = serialization.load_pem_private_key(b""" +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDT449phHltY2aV +QIvaT4PUgNS7wDybnnjVO88NGB5PjfUaWY99oDQgOLJlejyVVqRO2wHxLaUMsbuc +oe0XbgSFJgrnGvG6yPbjSXeIfV5k2dJG60S4Fg2mZ1ieSabuPVKLA03frhbATmIf +Q+VTMlWLgLVxcT04iqph6VpjehnYke0VPMuN7OM6RsIOEhLcje0bvL4YjTYXH5j4 +mPquc/ZEj/n6WJ6VsS27QygOBbaiGqHs54QnQi4gcgIgUmkR/bl2wL5s+729RBzS +v1FZfA5gdM9uEG3ogLHOC2uk+1Nuqcdk/tQxd30/2ulXubqDku/nNY2RSJrwD att +qcyCliANAgMBAAECggEAK3Z7HVbKHZENIsJZrY8v6HAAsv5ssDMicALTpsjytrjU +tPH4B/nLl2xp03zuXmemTnKIBHOrbl4qsKdaXbr4fGNgSyVwvjKoydhxB3NH4IH5 +qwhpUSVc6Ww7dkR/VFEJ1G/6Ek7AZfPuFqGzsYwalgHxtfJXb3iqGGloXA1Yrd5p +W2cTEhtSFZP/PQIEK773wYd3aYMw8OCqG2V5bw9N3xwY6KTC0Px8zyBlmAcUBPAj +QZL/DTGlMdD9+PJ3Ft3Zl2uS7ORn7xfXftvxv5IQdD+JBxV5zUIympKK/7KIVUfH +dfi93R7rqjL9EOP6bVQkg/WzYRLeVf/8km8HRGqtIQKBgQDxFxQ77t4EQqLPFofN +oRV7P3lvFqlJDTzAGBnjIT/ujT3SgoFUjRtfG27nWd1lycxv3tE0GTIw0LjJwvmg +VSFbQbPsmdp+f0jnNIiJayiG591j9Afmw06mnDodaQuSTp7K9idgpnFRGDQzwJHK +0DwSQzlzEXsPhGnXxpv+2Q+ANQKBgQDg/itVFBw3e5wC8boffi1AgnM97Qa/Y+5B +I2J9+cZD9iBkvE7kTwVUOI2Rr+XkQmSf+pT6L0yFXhQjIed004rpKVqzTvGL9VXJ +nBeADS4bxl1jsfkfvq9e6eNUK8vzyLoYQpS5/LK1oG5MPq3+30yzGIHM8JxxaOQ9 +VdKQrUdLeQKBgAh35RAN3eKMbKeVhQOmCtkfa6aJRzz3qBCfSBmAS3yXnXpNdzl/ +E10N26FouKwgoHu1eee4ktjAHB2KKbaGBvvrnORMqy4STn9AiyM4jl3euxoNslFa +vuJ/TlNGI0/qTw2WA+ATOJu+m+bNdtGG6vVBQz1VedsbrZQUt9oFydOZAoGAMlCk +4CHfLYk3GnF0bhaJiCOkIfUfzS1L2sVPAV0aOZiRJfX2rpf9WRhMkIgFoUY3uo8P +QePR+QFQ/4pVeIrWRc45ul+tJN94j92YY8qOxSdXOzRRwgeisFcdv3UL5zi8ZTB+ +khkw3e1CvUpHHvhQ7rxMSsiEM9iBMjY/IJuflgECgYEAqiN3eg8cZjVrYEMcPLGx +wXknCG0KPc8EpDi1moNwS3z/TcUbfP8vnmT2lFHTAbvVBn+4fcLffkQBoGG3AaSH +3kc0HXLdy+rFcsXpX7hk9BM/Uey9dqBOAusLS6XxYhcAJ1xOI0kYWoeOhO8fcjNa +tf26cJGzfbbwf8kfisbv4Uk= +-----END PRIVATE KEY----- +""", password=None) + + +def _begin_cert() -> x509.CertificateBuilder: + return x509.CertificateBuilder() \ + .serial_number(1) \ + .not_valid_before(datetime(2023, 1, 1)) \ + .not_valid_after(datetime(9999, 12, 31, 23, 59, 59)) # no well-defined expiration date, see RFC5280 4.1.2.5. + + +def generate_chain(ou_fields: List[str]) -> bytes: + # First, create the root CA + root_name = x509.Name([ + x509.NameAttribute(NameOID.COMMON_NAME, "qtestsign Root CA - NOT SECURE"), + ]) + # only key_cert_sign=True + root_usage = x509.KeyUsage(False, False, False, False, False, True, False, False, False) + root_ski = x509.SubjectKeyIdentifier.from_public_key(ROOT_KEY.public_key()) + root_cert_der = _begin_cert() \ + .subject_name(root_name) \ + .issuer_name(root_name) \ + .public_key(ROOT_KEY.public_key()) \ + .add_extension(x509.BasicConstraints(ca=True, path_length=0), critical=True) \ + .add_extension(root_usage, critical=True) \ + .add_extension(root_ski, critical=False) \ + .sign(ROOT_KEY, hashes.SHA256()) \ + .public_bytes(serialization.Encoding.DER) + + # Now, create the attestation certificate + att_name = x509.Name([ + x509.NameAttribute(NameOID.COMMON_NAME, "qtestsign Attestation CA - NOT SECURE"), + *[x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, ou) for ou in ou_fields], + ]) + # only digital_signature=True + att_usage = x509.KeyUsage(True, False, False, False, False, False, False, False, False) + att_cert_der = _begin_cert() \ + .subject_name(att_name) \ + .issuer_name(root_name) \ + .public_key(ATT_KEY.public_key()) \ + .add_extension(x509.BasicConstraints(ca=False, path_length=None), critical=True) \ + .add_extension(att_usage, critical=True) \ + .add_extension(x509.SubjectKeyIdentifier.from_public_key(ATT_KEY.public_key()), critical=False) \ + .add_extension(x509.AuthorityKeyIdentifier.from_issuer_subject_key_identifier(root_ski), critical=False) \ + .sign(ROOT_KEY, hashes.SHA256()) \ + .public_bytes(serialization.Encoding.DER) + + # The certificate chain is the attestation and root certificate concatenated + # in DER format. Note: The order (first attestation, then root) is important! + return att_cert_der + root_cert_der diff --git a/tools/qcom/mkmbn/elf.py b/tools/qcom/mkmbn/elf.py new file mode 100644 index 000000000000..ef83d724f855 --- /dev/null +++ b/tools/qcom/mkmbn/elf.py @@ -0,0 +1,241 @@ +# SPDX-License-Identifier: GPL-2.0-only +# Copyright (C) 2021 Stephan Gerhold +# Data classes are based on the header definitions in the ELF(5) man page. +# Also see: https://en.wikipedia.org/wiki/Executable_and_Linkable_Format +from __future__ import annotations + +import dataclasses +from dataclasses import dataclass +from struct import Struct +from typing import List, BinaryIO + + +@dataclass +class Ehdr: + ei_magic: bytes + ei_class: int + ei_data: int + ei_version: int + ei_os_abi: int + ei_abi_version: int + e_type: int + e_machine: int + e_version: int + # Address size specific part + e_entry: int = 0 + e_phoff: int = 0 + e_shoff: int = 0 + # End part + e_flags: int = 0 + e_ehsize: int = 0 + e_phentsize: int = 0 + e_phnum: int = 0 + e_shentsize: int = 0 + e_shnum: int = 0 + e_shstrndx: int = 0 + + START_FORMAT = Struct('<4s5B7xHHL') + START_COUNT = 9 + MEM_FORMAT32 = Struct(' Ehdr: + hdr_unpack = Ehdr.START_FORMAT.unpack_from(b) + hdr = Ehdr(*hdr_unpack) + assert hdr.ei_magic == b'\x7fELF', f"Invalid ELF header magic: {hdr.ei_magic}" + assert hdr.ei_data == 1, "Only little endian supported at the moment" + assert hdr.ei_version == 1, f"Unexpected ei_version: {hdr.ei_version}" + assert hdr.e_version == 1, f"Unexpected e_version: {hdr.e_version}" + + if hdr.ei_class == Ehdr.CLASS32: + mem_format = Ehdr.MEM_FORMAT32 + else: + assert hdr.ei_class == Ehdr.CLASS64, f"Unexpected ei_class: {hdr.ei_class}" + mem_format = Ehdr.MEM_FORMAT64 + + mem_unpack = mem_format.unpack_from(b, Ehdr.START_FORMAT.size) + end_unpack = Ehdr.END_FORMAT.unpack_from(b, Ehdr.START_FORMAT.size + mem_format.size) + return Ehdr(*hdr_unpack, *mem_unpack, *end_unpack) + + def save(self, f: BinaryIO) -> int: + unpack = dataclasses.astuple(self) + written = f.write(Ehdr.START_FORMAT.pack(*unpack[:Ehdr.START_COUNT])) + + if self.ei_class == Ehdr.CLASS32: + mem_format = Ehdr.MEM_FORMAT32 + else: + mem_format = Ehdr.MEM_FORMAT64 + written += f.write( + mem_format.pack(*unpack[Ehdr.START_COUNT:Ehdr.START_COUNT + Ehdr.MEM_COUNT])) + written += f.write(Ehdr.END_FORMAT.pack(*unpack[-Ehdr.END_COUNT:])) + return written + + +@dataclass +class Phdr: + p_type: int + p_offset: int + p_vaddr: int + p_paddr: int + p_filesz: int + p_memsz: int + p_flags: int + p_align: int + + data = None + + FORMAT32 = Struct('<8L') + FORMAT64 = Struct(' Phdr: + if ei_class == Ehdr.CLASS32: + unpack = list(Phdr.FORMAT32.unpack_from(b, offset)) + else: + unpack = list(Phdr.FORMAT64.unpack_from(b, offset)) + + # ELFCLASS64 has flags directly before offset for alignment + flags = unpack.pop(1) + unpack.insert(-1, flags) + + return Phdr(*unpack) + + @staticmethod + def from_bin(b: bytes, loadaddr: int) -> Phdr: + # p_offset is fixed later + phdr = Phdr( + p_type=1, + p_offset=0xFFFFFFFF, + p_vaddr=loadaddr, + p_paddr=loadaddr, + p_filesz=len(b), + p_memsz=len(b), + p_flags=7, + p_align=0x1000, + ) + phdr.data = memoryview(b) + return phdr + + def save(self, f: BinaryIO, ei_class: int) -> int: + unpack: tuple|list = dataclasses.astuple(self) + + if ei_class == Ehdr.CLASS32: + return f.write(Phdr.FORMAT32.pack(*unpack)) + else: + unpack = list(unpack) + + # ELFCLASS64 has flags directly before offset for alignment + flags = unpack.pop(-2) + unpack.insert(1, flags) + + return f.write(Phdr.FORMAT64.pack(*unpack)) + + +def _pad(f: BinaryIO, offset: int, pos: int) -> int: + assert offset >= pos, f"{offset} >= {pos}" + pad = offset - pos + if pad: + assert f.write(b'\0' * pad) == pad + return offset + + +def align(i: int, alignment: int) -> int: + mask = max(alignment - 1, 0) + return (i + mask) & ~mask + + +@dataclass +class Elf: + ehdr: Ehdr + phdrs: List[Phdr] + + def __init__(self, ehdr: Ehdr = Ehdr(), phdrs: List[Phdr] = []): + self.ehdr = ehdr + self.phdrs = phdrs + + def total_header_size(self): + return self.ehdr.e_phoff + len(self.phdrs) * self.ehdr.e_phentsize + + @staticmethod + def parse(b: bytes) -> Elf: + ehdr = Ehdr.parse(b) + view = memoryview(b) + + # Parse program headers + phdrs = [] + offset = ehdr.e_phoff + for i in range(ehdr.e_phnum): + phdr = Phdr.parse(b, offset, ehdr.ei_class) + phdrs.append(phdr) + + # Store data if necessary + if phdr.p_filesz and phdr.p_offset: + phdr.data = view[phdr.p_offset:phdr.p_offset + phdr.p_filesz] + + offset += ehdr.e_phentsize + + return Elf(ehdr, phdrs) + + def update(self): + # Rearrange all segments according to their alignment + pos = self.total_header_size() + for phdr in sorted(self.phdrs, key=lambda phdr: phdr.p_offset): + if phdr.p_offset and phdr.p_filesz: + phdr.p_offset = align(pos, phdr.p_align) + pos = phdr.p_offset + phdr.p_filesz + + # Ensure program header count is correct + self.ehdr.e_phnum = len(self.phdrs) + + # TODO: Clear out sections for now. Those are not read at the moment. + # Also, I don't think the Qualcomm firmware loader has any use for these. + self.ehdr.e_shoff = 0 + self.ehdr.e_shnum = 0 + self.ehdr.e_shstrndx = 0 + + def save_header(self, f: BinaryIO) -> int: + pos = self.ehdr.save(f) + pos = _pad(f, self.ehdr.e_phoff, pos) + + # Write program headers + for phdr in self.phdrs: + pos += phdr.save(f, self.ehdr.ei_class) + + return pos + + def save(self, f: BinaryIO) -> int: + pos = self.save_header(f) + + # Write segment data + for phdr in sorted(self.phdrs, key=lambda phdr: phdr.p_offset): + if phdr.data: + pos = _pad(f, phdr.p_offset, pos) + pos += f.write(phdr.data) + + return pos diff --git a/tools/qcom/mkmbn/hashseg.py b/tools/qcom/mkmbn/hashseg.py new file mode 100644 index 000000000000..db157a23d186 --- /dev/null +++ b/tools/qcom/mkmbn/hashseg.py @@ -0,0 +1,356 @@ +# SPDX-License-Identifier: GPL-2.0-only AND BSD-3-Clause +# Copyright (C) 2021-2023 Stephan Gerhold (GPL-2.0-only) +# MBN header format adapted from: +# - signlk: https://git.linaro.org/landing-teams/working/qualcomm/signlk.git +# - coreboot (util/qualcomm/mbn_tools.py, util/cbfstool/platform_fixups.c) +# Copyright (c) 2016, 2018, The Linux Foundation. All rights reserved. (BSD-3-Clause) +# See also: +# - https://www.qualcomm.com/media/documents/files/secure-boot-and-image-authentication-technical-overview-v1-0.pdf +# - https://www.qualcomm.com/media/documents/files/secure-boot-and-image-authentication-technical-overview-v2-0.pdf +from __future__ import annotations + +import dataclasses +import hashlib +from dataclasses import dataclass +from io import BytesIO +from struct import Struct + +import cert +import elf + +# A typical Qualcomm firmware might have the following program headers: +# LOAD off 0x00000800 vaddr 0x86400000 paddr 0x86400000 align 2**11 +# filesz 0x00001000 memsz 0x00001000 flags rwx +# +# The signed version will then look like: +# NULL off 0x00000000 vaddr 0x00000000 paddr 0x00000000 align 2**0 +# filesz 0x000000e8 memsz 0x00000000 flags --- 7000000 +# NULL off 0x00001000 vaddr 0x86401000 paddr 0x86401000 align 2**12 +# filesz 0x00000988 memsz 0x00001000 flags --- 2200000 +# LOAD off 0x00002000 vaddr 0x86400000 paddr 0x86400000 align 2**11 +# filesz 0x00001000 memsz 0x00001000 flags rwx +# +# The second NULL program header with off 0x1000 and filesz 0x988 is the actual +# "hash table segment" or shortly "hash segment" (see Figure 2 on page 6 in the PDF). +# It contains the MBN header specified below, then a couple of hashes (e.g. SHA256): +# 1. Hash of ELF header and program headers +# 2. Empty hash for hash segment +# 3. Hashes for data of each memory segment (described by program header) +# Finally, it contains an RSA signature and the concatenated certificate chain. +# +# The first NULL program header is never loaded anywhere, because +# vaddr = paddr = memsz = 0. However, the "off" and "filesz" cover exactly +# the ELF header (including all program headers). It is a placeholder so that +# each hash covers the data of exactly one program header. + +# For definitions of the ELF PHDR flags used by Qualcomm, see: +# https://github.com/coreboot/coreboot/blob/812d0e2f626dfea7e7deb960a8dc08ff0e026bc1/util/qualcomm/mbn_tools.py#L108-L189 +PHDR_FLAGS_SEGMENT_TYPE_MASK = 0x07000000 +PHDR_FLAGS_SEGMENT_TYPE_SHIFT = 0x18 +PHDR_FLAGS_SEGMENT_TYPE_HASH = (0x2 << PHDR_FLAGS_SEGMENT_TYPE_SHIFT) +PHDR_FLAGS_SEGMENT_TYPE_HDR = (0x7 << PHDR_FLAGS_SEGMENT_TYPE_SHIFT) + +PDHR_FLAGS_ACCESS_TYPE_MASK = 0x00E00000 +PHDR_FLAGS_ACCESS_TYPE_SHIFT = 0x15 +PHDR_FLAGS_ACCESS_TYPE_RO = (0x1 << PHDR_FLAGS_ACCESS_TYPE_SHIFT) + +# Flags we use for placeholder for hash over ELF header and hash segment +PHDR_FLAGS_HDR_PLACEHOLDER = PHDR_FLAGS_SEGMENT_TYPE_HDR +PHDR_FLAGS_HASH_SEGMENT = (PHDR_FLAGS_SEGMENT_TYPE_HASH | PHDR_FLAGS_ACCESS_TYPE_RO) + +EXTRA_PHDRS = 2 # header placeholder + hash segment + +# Note: None of the alignments seem to be truly required, +# this could probably be reduced to get smaller file sizes. +HASH_SEG_ALIGN = 0x1000 +CERT_CHAIN_ALIGN = 16 + +# According to the v2.0 PDF the metadata is 128 bytes long, but this does not +# seem to work. All official firmware seems to use 120 bytes instead. +MBN_V6_METADATA_SIZE = 120 + +# See OEM Metadata 2.0 definition in coreboot source code: +# https://github.com/coreboot/coreboot/blob/812d0e2f626dfea7e7deb960a8dc08ff0e026bc1/util/qualcomm/mbn_tools.py#L506-L691 +MBN_V7_OEM_2_0_METADATA_SIZE = 224 + + +@dataclass +class _HashSegment: + image_id: int = 0 # Type of image (unused?) + version: int = 0 # Header version number + + hash_size = 0 + signature_size = 0 + cert_chain_size = 0 + total_size = 0 + + hashes = [] + signature = b'' + cert_chain = b'' + + FORMAT = Struct('<10L') + Hash = hashlib.sha256 + + @property + def size_with_header(self): + return self.FORMAT.size + self.total_size + + def update(self, dest_addr: int): + self.hash_size = len(self.hashes) * self.Hash().digest_size + self.signature_size = len(self.signature) + self.cert_chain_size = len(self.cert_chain) + self.total_size = self.hash_size + self.signature_size + self.cert_chain_size + + def check(self): + assert len(self.hashes) * self.Hash().digest_size == self.hash_size + assert len(self.signature) == self.signature_size + assert len(self.cert_chain) == self.cert_chain_size + + def pack_header(self): + self.check() + return self.FORMAT.pack(*dataclasses.astuple(self)) + + def pack(self): + return self.pack_header() \ + + b''.join(self.hashes) \ + + self.signature + self.cert_chain + + +@dataclass +class HashSegmentV3(_HashSegment): + version: int = 3 # Header version number + + flash_addr: int = 0 # Location of image in flash (historical) + dest_addr: int = 0 # Physical address of loaded hash segment data + total_size: int = 0 # = hash_size + signature_size + cert_chain_size + hash_size: int = 0 # Size of hashes for all program segments + signature_addr: int = 0 # Physical address of loaded attestation signature + signature_size: int = 0 # Size of attestation signature + cert_chain_addr: int = 0 # Physical address of loaded certificate chain + cert_chain_size: int = 0 # Size of certificate chain + + def update(self, dest_addr: int): + super().update(dest_addr) + self.dest_addr = dest_addr + self.FORMAT.size + self.signature_addr = self.dest_addr + self.hash_size + self.cert_chain_addr = self.signature_addr + self.signature_size + + +@dataclass +class HashSegmentV5(_HashSegment): + version: int = 5 # Header version number + + signature_size_qcom: int = 0 # Size of signature from Qualcomm + cert_chain_size_qcom: int = 0 # Size of certificate chain from Qualcomm + total_size: int = 0 # = hash_size + signature_size + cert_chain_size + hash_size: int = 0 # Size of hashes for all program segments + signature_addr: int = 0xffffffff # unused? + signature_size: int = 0 # Size of attestation signature + cert_chain_addr: int = 0xffffffff # unused? + cert_chain_size: int = 0 # Size of certificate chain + + signature_qcom = b'' + cert_chain_qcom = b'' + + def update(self, dest_addr: int): + super().update(dest_addr) + self.signature_size_qcom = len(self.signature_qcom) + self.cert_chain_size_qcom = len(self.cert_chain_qcom) + self.total_size += self.signature_size_qcom + self.cert_chain_size_qcom + + def check(self): + super().check() + assert len(self.signature_qcom) == self.signature_size_qcom + assert len(self.cert_chain_qcom) == self.cert_chain_size_qcom + + def pack(self): + return self.pack_header() \ + + b''.join(self.hashes) \ + + self.signature_qcom + self.cert_chain_qcom \ + + self.signature + self.cert_chain + + +@dataclass +class HashSegmentV6(HashSegmentV5): + version: int = 6 # Header version number + + metadata_size_qcom: int = 0 # Size of metadata from Qualcomm + metadata_size: int = 0 # Size of metadata + + metadata_qcom = b'' + metadata = b'' + + FORMAT = Struct('<12L') + Hash = hashlib.sha384 + + def update(self, dest_addr: int): + super().update(dest_addr) + self.metadata_size_qcom = len(self.metadata_qcom) + self.metadata_size = len(self.metadata) + self.total_size += self.metadata_size_qcom + self.metadata_size + + def check(self): + super().check() + assert len(self.metadata_qcom) == self.metadata_size_qcom + assert len(self.metadata) == self.metadata_size + + def pack(self): + return self.pack_header() \ + + self.metadata_qcom + self.metadata \ + + b''.join(self.hashes) \ + + self.signature_qcom + self.cert_chain_qcom \ + + self.signature + self.cert_chain + + +@dataclass +# Information from MBNv7 definition in Coreboot source code: +# https://github.com/coreboot/coreboot/blob/812d0e2f626dfea7e7deb960a8dc08ff0e026bc1/util/qualcomm/mbn_tools.py#L506-L691 +class HashSegmentV7(_HashSegment): + version: int = 7 # Header version number + + common_metadata_size: int = 24 # Size of "common metadata" below + metadata_size_qcom: int = 0 # Size of metadata from Qualcomm + metadata_size: int = 0 # Size of metadata from OEM + hash_size: int = 0 # Size of hashes for all program segments + signature_size_qcom: int = 0 # Size of signature from Qualcomm + cert_chain_size_qcom: int = 0 # Size of certificate chain from Qualcomm + signature_size: int = 0 # Size of attestation signature + cert_chain_size: int = 0 # Size of certificate chain + + # Common metadata, placed directly after MBNv7 header + common_metadata_major_version: int = 0 + common_metadata_minor_version: int = 0 + software_id: int = 0 # Type of software image, mandatory + secondary_software_id: int = 0 + hash_table_algorithm: int = 3 # SHA384 + measurement_register_target: int = 0 + + metadata_qcom = b'' + metadata = b'' + signature_qcom = b'' + cert_chain_qcom = b'' + + FORMAT = Struct('<16L') + Hash = hashlib.sha384 + + def update(self, dest_addr: int): + super().update(dest_addr) + self.metadata_size_qcom = len(self.metadata_qcom) + self.metadata_size = len(self.metadata) + self.signature_size_qcom = len(self.signature_qcom) + self.cert_chain_size_qcom = len(self.cert_chain_qcom) + # self.common_metadata_size is already included as part of the header + self.total_size += self.metadata_size_qcom + self.metadata_size + self.total_size += self.signature_size_qcom + self.cert_chain_size_qcom + + def check(self): + super().check() + assert len(self.metadata_qcom) == self.metadata_size_qcom + assert len(self.metadata) == self.metadata_size + assert len(self.signature_qcom) == self.signature_size_qcom + assert len(self.cert_chain_qcom) == self.cert_chain_size_qcom + + def pack(self): + return self.pack_header() \ + + self.metadata_qcom + self.metadata \ + + b''.join(self.hashes) \ + + self.signature_qcom + self.cert_chain_qcom \ + + self.signature + self.cert_chain + +HashSegment = { + 3: HashSegmentV3, + 5: HashSegmentV5, + 6: HashSegmentV6, + 7: HashSegmentV7, +} + + +def drop(elff: elf.Elf): + # Drop existing hash segments + elff.phdrs = [phdr for phdr in elff.phdrs if phdr.p_type != elf.Phdr.PT_NULL + or (phdr.p_flags & PHDR_FLAGS_SEGMENT_TYPE_MASK) not in + [PHDR_FLAGS_SEGMENT_TYPE_HASH, PHDR_FLAGS_SEGMENT_TYPE_HDR]] + + +def generate(elff: elf.Elf, version: int, sw_id: int): + drop(elff) + assert elff.phdrs, "Need at least one program header" + + hash_seg = HashSegment[version]() + + if version == 6: + # TODO: Figure out metadata format and fill this with useful data + hash_seg.metadata = b'\0' * MBN_V6_METADATA_SIZE + + # Software ID is mandatory for MBN v7 + if version == 7: + hash_seg.software_id = sw_id + # The format is documented in Coreboot util/qualcomm/mbn_tools.py + # (see class Boot_Hdr), but for simplicity we just keep this empty. + hash_seg.metadata = b'\0' * MBN_V7_OEM_2_0_METADATA_SIZE + + # Generate hash for all existing segments with data + digest_size = hash_seg.Hash().digest_size + hash_seg.hashes = [b'\0' * digest_size] * (len(elff.phdrs) + EXTRA_PHDRS) + for i, phdr in enumerate(elff.phdrs, start=EXTRA_PHDRS): + if phdr.data: + hash_seg.hashes[i] = hash_seg.Hash(phdr.data).digest() + total_hashes_size = len(hash_seg.hashes) * digest_size + + # Generate certificate chain with specified OU fields (for < v6) + # on >= v6 this is part of the metadata instead + ou_fields = [] + if version < 6: + ou_fields = [ + # Note: The SW_ID is checked by the firmware on some platforms (even if secure boot + # is disabled), so it must match the firmware type being signed. Everything else seems + # to be mostly ignored when secure boot is off and is just added here to match the + # documentation and better mimic the official firmware. + "01 %016X SW_ID" % sw_id, + "02 %016X HW_ID" % 0, + "03 %016X DEBUG" % 2, # DISABLED + "04 %04X OEM_ID" % 0, + "05 %08X SW_SIZE" % (hash_seg.FORMAT.size + total_hashes_size), + "06 %04X MODEL_ID" % 0, + "07 %04X SHA256" % 1, + ] + hash_seg.cert_chain = cert.generate_chain(ou_fields) + hash_seg.cert_chain = hash_seg.cert_chain.ljust(elf.align(len(hash_seg.cert_chain), CERT_CHAIN_ALIGN), b'\xff') + # hash_seg.cert_chain = b'' # uncomment this to omit the certificate chain in the signed image + + # TODO: Generate actual signature with our generated attestation certificate! + # There are different signature schemes that could be implemented (RSASSA-PKCS#1 v1.5 + # RSASSA-PSS, ECDSA over P-384) but it's not entirely clear yet which chipsets supports/ + # uses which. The signature does not seem to be checked on devices without secure boot, + # so just use a dummy value for now. + hash_seg.signature = b'\xff' * (cert.ATT_KEY.key_size // 8) + # hash_seg.signature = b'' # uncomment this to omit the signature in the signed image + + # Align maximum end address to get address for hash table header, then update header + hash_addr = elf.align(max(phdr.p_paddr + phdr.p_memsz for phdr in elff.phdrs), HASH_SEG_ALIGN) + hash_seg.update(hash_addr) + + # Insert new hash NULL segment + hash_phdr = elf.Phdr(elf.Phdr.PT_NULL, HASH_SEG_ALIGN, hash_addr, hash_addr, hash_seg.size_with_header, + elf.align(hash_seg.size_with_header, HASH_SEG_ALIGN), + PHDR_FLAGS_HASH_SEGMENT, HASH_SEG_ALIGN) + elff.phdrs.insert(0, hash_phdr) + + # Insert new ELF header placeholder program header + hdr_hash_phdr = elf.Phdr(elf.Phdr.PT_NULL, 0, 0, 0, 0, 0, PHDR_FLAGS_HDR_PLACEHOLDER, 0) + elff.phdrs.insert(0, hdr_hash_phdr) + + # Now determine size of ELF header (including program headers) + hdr_hash_phdr.p_filesz = elff.total_header_size() + + # Recompute attributes to match final output (e.g. adjust e_phnum) + elff.update() + + # Compute the hash for the ELF header + with BytesIO() as hdr_io: + elff.save_header(hdr_io) + hash_seg.hashes[0] = hash_seg.Hash(hdr_io.getbuffer()).digest() + + # And finally, assemble the hash segment + hash_phdr.data = hash_seg.pack() + assert len(hash_phdr.data) == hash_phdr.p_filesz \ No newline at end of file diff --git a/tools/qcom/mkmbn/mkmbn.py b/tools/qcom/mkmbn/mkmbn.py new file mode 100755 index 000000000000..6b0ea3a42394 --- /dev/null +++ b/tools/qcom/mkmbn/mkmbn.py @@ -0,0 +1,194 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-only +# Copyright (C) 2024 Stephan Gerhold +# Copyright (C) 2026 Casey Connolly +# +# This is a port of qtestsign designed to integrate with the +# U-Boot build system. See the qtestsign repo for more information. +# https://github.com/msm8916-mainline/qtestsign +# +from __future__ import annotations + +import argparse +from pathlib import Path + +from elf import Elf, Phdr +import hashseg +import sys +from enum import Enum +import struct + +MBN_ALIGN_SIZE = 4096 + +verbose = False + +def log(*args, **kwargs): + if verbose: + print(*args, *kwargs, file=sys.stderr) + +def error(*args, **kwargs): + print("mkmbn: ", file=sys.stderr, end='') + print(*args, *kwargs, file=sys.stderr) + +class SwId(Enum): + sbl1 = 0x00 + mba = 0x01 + modem = 0x02 + prog = 0x03 + adsp = 0x04 + devcfg = 0x05 + tz = 0x07 + aboot = 0x09 + uefi = 0x09 + rpm = 0x0A + tz_app = 0x0C + wcnss = 0x0D + venus = 0x0E + wlanmdsp = 0x12 + gpu = 0x14 + hyp = 0x15 + cdsp = 0x17 + slpi = 0x18 + abl = 0x1C + cmnlib = 0x1F + aop = 0x21 + qup = 0x24 + xbl_config = 0x25 + +class MbnData: + + # sw_id 0x9 is aboot/uefi, the most common + def __init__(self, loadaddr: int, version: int, sw_id: SwId = SwId.aboot): + self.loadaddr = loadaddr + self.version = version + self.sw_id = sw_id + + +""" +This dictionary is used to map a board or platform to the appropriate MBN +metadata. When adding support for a new platform to U-Boot, the appropriate data +should be filled out here. Looking at similar platforms in the table below may +help. +""" +boards: dict[bytes, MbnData] = { + # Exact matches for boards, these are preferred + b"qcom,qcs6490-rb3gen2\0": MbnData(6, SwId.uefi), + b"qcom,qcs9100-ride-r3\0": MbnData(6, SwId.uefi), # Dragonwing IQ9 + b"qcom,qcs8300-ride\0": MbnData(6, SwId.uefi), # Dragonwing IQ8 + b"qcom,qcs615-ride\0": MbnData(6, SwId.uefi), # Dragonwing IQ6 + # Fallback/generic matches since most boards for a platform will + # use the same load address + b"qcom,qcm6490\0": MbnData(0x9FC00000, 6, SwId.uefi), # rb3gen2, rubikpi3 + b"qcom,qcs9100\0": MbnData(0xAF000000, 6, SwId.uefi), # Dragonwing IQ9 + b"qcom,qcs8300\0": MbnData(0xAF000000, 6, SwId.uefi), # Dragonwing IQ8 + b"qcom,qcs8550\0": MbnData(0xA7000000, 7, SwId.uefi), # C8550 + b"qcom,sm8550\0": MbnData(0xA7000000, 7, SwId.uefi), # C8550 + b"qcom,sm8650\0": MbnData(0xA7000000, 7, SwId.uefi), # SM8650 + b"qcom,qcs615\0": MbnData(0x9FC00000, 6, SwId.uefi), # Dragonwing IQ6 + b"qcom,ipq5210\0": MbnData(0x87980000, 7, SwId.aboot), + b"qcom,ipq5424\0": MbnData(0x8a380000, 7, SwId.aboot), + b"qcom,ipq9574\0": MbnData(0x4A240000, 6, SwId.aboot), + + # msm8916/apq8016 has an "aboot" partition but the process is the same + # They use header version 3. + b"qcom,apq8016\0": MbnData(0x8f600000, 3, SwId.aboot), + b"qcom,msm8916\0": MbnData(0x8f600000, 3, SwId.aboot), +} + +parser = argparse.ArgumentParser( + description=""" + Create a signed Qualcomm "uefi" ELF image +""" +) +parser.register("type", "hex", lambda s: int(s, 16)) +parser.add_argument( + "-l", "--load", type=lambda x: int(x, 0), default=0, help="Load address" +) +parser.add_argument( + "-m", "--melfs", nargs='+', default=[], help="List of ELFs to include in multi-elf" +) +parser.add_argument( + "-o", "--output", type=Path, default="u-boot.mbn", help="Output file" +) +parser.add_argument( + "-v", dest="verbose", action="store_true", default=False, help="Verbose" +) +parser.add_argument( + "bin", type=argparse.FileType("rb"), help="Binary to embed (e.g. u-boot.bin)" +) +args = parser.parse_args() +verbose = args.verbose + +if (len(args.melfs)): + try: + with open(args.output, "wb") as f: + for path in args.melfs: + with open(path, 'rb') as tmp: + f.write(tmp.read()) + except Exception as e: + print(f"Error: {e}") + sys.exit(1) + + sys.exit(0) + +elf = Elf() + +data: bytes = args.bin.read() + +# dtb is at the end, so find the last match +dtb_off = 0 +off = 0 +dtb_size = 0 +while True: + off = data.find(b"\xd0\x0d\xfe\xed", dtb_off + dtb_size) + if off == -1: + break + (dtb_size,) = struct.unpack_from("I", data, offset=off) + dtb_off = off + +if not dtb_off: + print("Couldn't find DTB in provided binary!") + exit(1) + +log(f"Found FDT at {dtb_off:#x} size {dtb_size:#x}") + +mbn: MbnData|None = None + +for match, mbndata in boards.items(): + if data.find(match, dtb_off) != -1: + mbn = mbndata + break + +if not mbn: + error( + "CONFIG_QCOM_GENERATE_MBN is enabled but this platform doesn't appear to be supported\n" + "Please see tools/qcom/mkmbn/mkmbn.py for details. If you intend to chainload U-Boot\n" + "then disregard this message and disable CONFIG_QCOM_GENERATE_MBN in your defconfig." + ) + args.output.unlink(missing_ok=True) + exit(1) + +if args.load == 0: + error( + "Cannot proceed without load address.\n" + "Ensure CONFIG_TEXT_BASE/CONFIG_SPL_TEXT_BASE is set\n") + exit(1) +else: + mbn.loadaddr = args.load + +log(f"Detected board {match.decode('UTF-8')} with load address {mbn.loadaddr:#x}") + +data += b'\x00' * (-len(data) % MBN_ALIGN_SIZE) + +elf.phdrs.append(Phdr.from_bin(data, mbn.loadaddr)) +elf.ehdr.e_entry = mbn.loadaddr +elf.update() + +# QLI boards use v6 sw_id is "aboot" +hashseg.generate(elf, mbn.version, mbn.sw_id.value) +# print(f"after: {elf}") + +with open(args.output, "wb") as f: + elf.save(f) + +log(f"Built signed MBN: {args.output.resolve()}")