It is possible to derive the memory map for a Qualcomm platform from the SMEM shared memory region. The memory map is populated by the preloader.
Introduce support for parsing this data and using it to populate U-Boot's memory map. Since we aren't yet sure if this will work for every platform, it is not yet used in all cases, if U-Boot is booted with an internal FDT which has the memory map defined, this will be used instead. If the FDT is internal FDT with no memory map defined, then U-Boot will try to use SMEM. This should remove the need to define the memory map statically in a U-Boot overlay for boards that don't chainload. Signed-off-by: Casey Connolly <[email protected]> --- arch/arm/Kconfig | 1 + arch/arm/mach-snapdragon/board.c | 45 +++++--- arch/arm/mach-snapdragon/dram.c | 172 +++++++++++++++++++++++++------ arch/arm/mach-snapdragon/qcom-priv.h | 17 ++- arch/arm/mach-snapdragon/rampart.h | 194 +++++++++++++++++++++++++++++++++++ 5 files changed, 386 insertions(+), 43 deletions(-) diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index a8fcfe76296c..1da0677d85b3 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1140,8 +1140,9 @@ config ARCH_SNAPDRAGON select DM_RESET select POWER_DOMAIN select GPIO_EXTRA_HEADER select OF_CONTROL + select QCOM_SMEM select SPMI select BOARD_LATE_INIT select OF_BOARD select SAVE_PREV_BL_FDT_ADDR if !ENABLE_ARM_SOC_BOOT0_HOOK diff --git a/arch/arm/mach-snapdragon/board.c b/arch/arm/mach-snapdragon/board.c index a2d97ad77910..7610891191fd 100644 --- a/arch/arm/mach-snapdragon/board.c +++ b/arch/arm/mach-snapdragon/board.c @@ -29,16 +29,18 @@ #include <lmb.h> #include <malloc.h> #include <fdt_support.h> #include <usb.h> +#include <soc/qcom/smem.h> #include <sort.h> #include <time.h> #include "qcom-priv.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; static struct mm_region rbx_mem_map[CONFIG_NR_DRAM_BANKS + 2] = { { 0 } }; struct mm_region *mem_map = rbx_mem_map; @@ -109,18 +111,18 @@ int board_fdt_blob_setup(void **fdtp) (phys_addr_t)external_fdt); /* Prefer memory information from internal DT if it's present */ if (internal_valid) - ret = qcom_parse_memory(internal_fdt); + ret = qcom_parse_memory(internal_fdt, true); if (ret < 0 && external_valid) { /* No internal FDT or it lacks a proper /memory node. * The previous bootloader handed us something, let's try that. */ if (internal_valid) debug("No memory info in internal FDT, falling back to external\n"); - ret = qcom_parse_memory(external_fdt); + ret = qcom_parse_memory(external_fdt, false); } if (ret < 0) panic("No valid memory ranges found!\n"); @@ -373,23 +375,39 @@ static void configure_env(void) qcom_set_serialno(); } -void qcom_show_boot_source(void) +static void qcom_show_boot_context(void) { - const char *name = "UNKNOWN"; + const char *boot_source = "UNKNOWN"; + const char *memmap_source = "UNKNOWN"; switch (qcom_boot_source) { case QCOM_BOOT_SOURCE_ANDROID: - name = "ABL"; + boot_source = "ABL"; break; case QCOM_BOOT_SOURCE_XBL: - name = "XBL"; + boot_source = "XBL"; break; } - log_info("U-Boot loaded from %s\n", name); - env_set("boot_source", name); + log_info("U-Boot loaded from %s\n", boot_source); + env_set("boot_source", boot_source); + + switch (qcom_memmap_source) { + case QCOM_MEMMAP_SOURCE_INTERNAL_FDT: + memmap_source = "INTERNAL_FDT"; + break; + case QCOM_MEMMAP_SOURCE_EXTERNAL_FDT: + memmap_source = "EXTERNAL_FDT"; + break; + case QCOM_MEMMAP_SOURCE_SMEM: + memmap_source = "SMEM"; + break; + } + + log_info("Memory map loaded from %s\n", memmap_source); + env_set("memmap_source", memmap_source); } void __weak qcom_late_init(void) { @@ -458,9 +476,9 @@ int board_late_init(void) configure_env(); qcom_late_init(); - qcom_show_boot_source(); + qcom_show_boot_context(); /* Configure the dfu_string for capsule updates */ qcom_configure_capsule_updates(); return 0; @@ -648,11 +666,14 @@ void enable_caches(void) 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 (fdt_node_check_compatible(gd->fdt_blob, 0, "qcom,qcs404") == 0) { + /* + * 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)); diff --git a/arch/arm/mach-snapdragon/dram.c b/arch/arm/mach-snapdragon/dram.c index ad73b685a935..af5f3c84882c 100644 --- a/arch/arm/mach-snapdragon/dram.c +++ b/arch/arm/mach-snapdragon/dram.c @@ -9,8 +9,12 @@ #include <asm-generic/unaligned.h> #include <dm.h> #include <log.h> #include <sort.h> +#include <soc/qcom/smem.h> + +#include "qcom-priv.h" +#include "rampart.h" static struct { phys_addr_t start; phys_size_t size; @@ -47,8 +51,12 @@ static void qcom_configure_bi_dram(void) for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { gd->bd->bi_dram[i].start = prevbl_ddr_banks[i].start; gd->bd->bi_dram[i].size = prevbl_ddr_banks[i].size; + debug("Bank[%d]: start = %#011llx, size = %#011llx\n", + i, gd->bd->bi_dram[i].start, gd->bd->bi_dram[i].size); + if (!prevbl_ddr_banks[i].size) + break; } } int dram_init_banksize(void) @@ -57,8 +65,124 @@ int dram_init_banksize(void) return 0; } +#define entry_field(v, e, field) (v == 0 ? ((struct ram_partition_entry_v0 *)(e))->field : \ + (v == 1 ? ((struct ram_partition_entry_v1 *)(e))->field : \ + ((struct ram_partition_entry_v3 *)(e))->field)) +#define entry_start(v, e) entry_field(v, e, start_address) +#define entry_length(v, e) entry_field(v, e, length) +#define entry_category(v, e) entry_field(v, e, partition_category) +#define entry_domain(v, e) entry_field(v, e, partition_domain) +#define entry_type(v, e) entry_field(v, e, partition_type) + +/* Parse memory map from SMEM, return the number of entries */ +static int qcom_parse_memory_smem(phys_addr_t *ram_end) +{ + size_t size; + int i, j = 0, ret; + struct usable_ram_partition_table_header *header; + u32 ver; + void *entry; + u32 entry_size; // Size of each RAM partition entry (version dependent) + + ret = qcom_smem_init(); + if (ret) { + debug("Failed to initialize SMEM: %d.\n", ret); + return ret; + } + + header = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_USABLE_RAM_PARTITION_TABLE, &size); + if (!header) { + debug("Failed to find SMEM partition.\n"); + return -ENODEV; + } + + ver = header->version; + debug("SMEM RAM partition table version %u. %u entries\n", ver, header->num_partitions); + + switch (ver) { + case 0: + entry_size = sizeof(struct ram_partition_entry_v0); + entry = ((struct usable_ram_partition_table_v0 *)header)->entries; + break; + case 1: + entry_size = sizeof(struct ram_partition_entry_v1); + entry = ((struct usable_ram_partition_table_v1 *)header)->entries; + break; + default: + pr_warn("Unknown SMEM ram partition table version!\n"); + case 2: + case 3: + entry_size = sizeof(struct ram_partition_entry_v3); + entry = ((struct usable_ram_partition_table_v3 *)header)->entries; + break; + } + + debug("SMEM RAM partition entry size: %u bytes\n", entry_size); + + /* Check validy of RAM */ + for (i = 0; i < header->num_partitions && j < CONFIG_NR_DRAM_BANKS; i++, entry += entry_size) { + debug("Entry %d: [%#010llx - %#010llx]\n", i, entry_start(ver, entry), + (u64)entry_start(ver, entry) + entry_length(ver, entry)); + debug(" cat: %#04x type: %#04x domain: %#04x\n", entry_category(ver, entry), + entry_type(ver, entry), entry_domain(ver, entry)); + + + if (entry_category(ver, entry) != RAM_PARTITION_SDRAM || + entry_type(ver, entry) != RAM_PARTITION_SYS_MEMORY) + continue; + if (!entry_length(ver, entry) && !entry_start(ver, entry)) + break; + + prevbl_ddr_banks[j].start = entry_start(ver, entry); + prevbl_ddr_banks[j].size = entry_length(ver, entry); + *ram_end = max(*ram_end, prevbl_ddr_banks[j].start + prevbl_ddr_banks[j].size); + j++; + } + + if (j == CONFIG_NR_DRAM_BANKS) + pr_err("SMEM: More than CONFIG_NR_DRAM_BANKS (%u) entries!", CONFIG_NR_DRAM_BANKS); + + return j; +} + +static void qcom_parse_memory_dt(const fdt64_t *fdt, int *banks, phys_addr_t *ram_end) +{ + int offset; + const fdt64_t *memory; + int memsize; + int i, j; + + *ram_end = 0; + + offset = fdt_path_offset(fdt, "/memory"); + if (offset < 0) + return; + + memory = fdt_getprop(fdt, offset, "reg", &memsize); + if (!memory) + return; + + *banks = min(memsize / (2 * sizeof(u64)), (ulong)CONFIG_NR_DRAM_BANKS); + + if (memsize / sizeof(u64) > CONFIG_NR_DRAM_BANKS * 2) + log_err("Provided more than the max of %d memory banks\n", CONFIG_NR_DRAM_BANKS); + + if (*banks > CONFIG_NR_DRAM_BANKS) + log_err("Provided more memory banks than we can handle\n"); + + for (i = 0, j = 0; i < *banks * 2; i += 2, j++) { + prevbl_ddr_banks[j].start = get_unaligned_be64(&memory[i]); + prevbl_ddr_banks[j].size = get_unaligned_be64(&memory[i + 1]); + if (!prevbl_ddr_banks[j].size) { + j--; + continue; + } + *ram_end = max(*ram_end, prevbl_ddr_banks[j].start + prevbl_ddr_banks[j].size); + } +} + /** * The generic memory parsing code in U-Boot lacks a few things that we * need on Qualcomm: * @@ -76,48 +200,36 @@ int dram_init_banksize(void) * We can't use fdtdec_setup_memory_banksize() since it stores the result in * gd->bd, which is not yet allocated. * * @fdt: FDT blob to parse /memory node from + * @fdt_is_internal: is the FDT one embedded into U-Boot or was it provided by a prior + * bootloader stage? This determined if we should try to rely on SMEM + * (internal FDT) or error. We currently assume that if we are passed + * an external FDT then it already has the memory map populated. * * Return: 0 on success or -ENODATA if /memory node is missing or incomplete */ -int qcom_parse_memory(const void *fdt) +int qcom_parse_memory(const void *fdt, bool fdt_is_internal) { - int offset; - const fdt64_t *memory; - int memsize; phys_addr_t ram_end = 0; - int i, j, banks; + int banks; - offset = fdt_path_offset(fdt, "/memory"); - if (offset < 0) - return -ENODATA; + qcom_memmap_source = fdt_is_internal ? QCOM_MEMMAP_SOURCE_INTERNAL_FDT + : QCOM_MEMMAP_SOURCE_EXTERNAL_FDT; - memory = fdt_getprop(fdt, offset, "reg", &memsize); - if (!memory) - return -ENODATA; + qcom_parse_memory_dt(fdt, &banks, &ram_end); - banks = min(memsize / (2 * sizeof(u64)), (ulong)CONFIG_NR_DRAM_BANKS); - - if (memsize / sizeof(u64) > CONFIG_NR_DRAM_BANKS * 2) - log_err("Provided more than the max of %d memory banks\n", CONFIG_NR_DRAM_BANKS); - - if (banks > CONFIG_NR_DRAM_BANKS) - log_err("Provided more memory banks than we can handle\n"); - - for (i = 0, j = 0; i < banks * 2; i += 2, j++) { - prevbl_ddr_banks[j].start = get_unaligned_be64(&memory[i]); - prevbl_ddr_banks[j].size = get_unaligned_be64(&memory[i + 1]); - if (!prevbl_ddr_banks[j].size) { - j--; - continue; - } - ram_end = max(ram_end, prevbl_ddr_banks[j].start + prevbl_ddr_banks[j].size); + /* + * If using an internal FDT but the memory node is empty + * then fall back to SMEM. + */ + if (!prevbl_ddr_banks[0].size && fdt_is_internal) { + banks = qcom_parse_memory_smem(&ram_end); + if (banks < 0) + panic("Couldn't find a valid memory map!\n"); + qcom_memmap_source = QCOM_MEMMAP_SOURCE_SMEM; } - if (!banks || !prevbl_ddr_banks[0].size) - return -ENODATA; - /* Sort our RAM banks -_- */ qsort(prevbl_ddr_banks, banks, sizeof(prevbl_ddr_banks[0]), ddr_bank_cmp); gd->ram_base = prevbl_ddr_banks[0].start; diff --git a/arch/arm/mach-snapdragon/qcom-priv.h b/arch/arm/mach-snapdragon/qcom-priv.h index ce409314a98b..39dc8fcc76ad 100644 --- a/arch/arm/mach-snapdragon/qcom-priv.h +++ b/arch/arm/mach-snapdragon/qcom-priv.h @@ -2,8 +2,10 @@ #ifndef __QCOM_PRIV_H__ #define __QCOM_PRIV_H__ +#include <stdbool.h> + /** * enum qcom_boot_source - Track where we got loaded from. * Used for capsule update logic. * @@ -16,13 +18,26 @@ enum qcom_boot_source { }; extern enum qcom_boot_source qcom_boot_source; +/* + * enum qcom_memmap_source - Track where we got the memory map from. + * used for debugging and validation. + */ +enum qcom_memmap_source { + QCOM_MEMMAP_SOURCE_INTERNAL_FDT = 1, + QCOM_MEMMAP_SOURCE_EXTERNAL_FDT, + QCOM_MEMMAP_SOURCE_SMEM, +}; + +/* Set by qcom_parse_memory() */ +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) {} #endif /* EFI_HAVE_CAPSULE_SUPPORT */ -int qcom_parse_memory(const void *fdt); +int qcom_parse_memory(const void *fdt, bool fdt_is_internal); #endif /* __QCOM_PRIV_H__ */ diff --git a/arch/arm/mach-snapdragon/rampart.h b/arch/arm/mach-snapdragon/rampart.h new file mode 100644 index 000000000000..57f3dbb7eec0 --- /dev/null +++ b/arch/arm/mach-snapdragon/rampart.h @@ -0,0 +1,194 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * RAM partition table definitions + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + */ + +#ifndef __QCOM_SMEM_RAMPART_H__ +#define __QCOM_SMEM_RAMPART_H__ + +#include <linux/types.h> + +#define SMEM_USABLE_RAM_PARTITION_TABLE 402 + +#define RAM_PARTITION_H_MAJOR 03 +#define RAM_PARTITION_H_MINOR 00 + +/** + * Total length of zero filled name string. This is not a C + * string, as it can occupy the total number of bytes, and if + * it does, it does not require a zero terminator. It cannot + * be manipulated with standard string handling library functions. + */ +#define RAM_PART_NAME_LENGTH 16 + +/** + * Number of RAM partition entries which are usable by APPS. + */ +#define RAM_NUM_PART_ENTRIES 32 + +/** + * @name: Magic numbers + * Used in identifying valid RAM partition table. + */ +#define RAM_PART_MAGIC1 0x9da5e0a8 +#define RAM_PART_MAGIC2 0xaf9ec4e2 + +/** + * RAM partition attributes. + */ +enum ram_partition_attribute_t { + RAM_PARTITION_READ_ONLY = 0, /* Read-only RAM partition */ + RAM_PARTITION_READWRITE, /* Read/write RAM partition */ +}; + +/** + * RAM partition categories. + */ +enum ram_partition_category_t { + RAM_PARTITION_IRAM = 4, /* IRAM RAM partition */ + RAM_PARTITION_IMEM = 5, /* IMEM RAM partition */ + RAM_PARTITION_SDRAM = 14, /* SDRAM type without specific bus information**/ +}; + +/** + * RAM Partition domains. + * @note: For shared RAM partition, domain value would be 0b11:\n + * RAM_PARTITION_APPS_DOMAIN | RAM_PARTITION_MODEM_DOMAIN. + */ +enum ram_partition_domain_t { + RAM_PARTITION_DEFAULT_DOMAIN = 0, /* 0b00: No specific domain definition */ + RAM_PARTITION_APPS_DOMAIN = 1, /* 0b01: APPS RAM partition */ + RAM_PARTITION_MODEM_DOMAIN = 2, /* 0b10: MODEM RAM partition */ +}; + +/** + * RAM Partition types. + * @note: The RAM_PARTITION_SYS_MEMORY type represents DDR rams that are attached + * to the current system. + */ +enum ram_partition_type_t { + RAM_PARTITION_SYS_MEMORY = 1, /* system memory */ + RAM_PARTITION_BOOT_REGION_MEMORY1, /* boot loader memory 1 */ + RAM_PARTITION_BOOT_REGION_MEMORY2, /* boot loader memory 2, reserved */ + RAM_PARTITION_APPSBL_MEMORY, /* apps boot loader memory */ + RAM_PARTITION_APPS_MEMORY, /* apps usage memory */ + RAM_PARTITION_TOOLS_FV_MEMORY, /* tools usage memory */ + RAM_PARTITION_QUANTUM_FV_MEMORY, /* quantum usage memory */ + RAM_PARTITION_QUEST_FV_MEMORY, /* quest usage memory */ +}; + +/* Common table header between versions */ +struct usable_ram_partition_table_header { + u32 magic1; /* Magic number to identify valid RAM partition table */ + u32 magic2; /* Magic number to identify valid RAM partition table */ + u32 version; /* Version number to track structure definition changes */ + u32 reserved1; /* Reserved for future use */ + + u32 num_partitions; /* Number of RAM partition table entries */ +}; + +/* Holds information for an entry in the RAM partition table */ +struct ram_partition_entry_v3 { + char name[RAM_PART_NAME_LENGTH]; /* Partition name, unused for now */ + u64 start_address; /* Partition start address in RAM */ + u64 length; /* Partition length in RAM in Bytes */ + u32 partition_attribute; /* Partition attribute */ + u32 partition_category; /* Partition category */ + u32 partition_domain; /* Partition domain */ + u32 partition_type; /* Partition type */ + u32 num_partitions; /* Number of partitions on device */ + u32 hw_info; /* hw information such as type and frequency */ + u8 highest_bank_bit; /* Highest bit corresponding to a bank */ + u8 reserve0; /* Reserved for future use */ + u8 reserve1; /* Reserved for future use */ + u8 reserve2; /* Reserved for future use */ + u32 min_pasr_size; /* Minimum PASR size in MB */ + u64 available_length; /* Available Partition length in RAM in Bytes */ +}; + +/* + * Defines the RAM partition table structure + * + * Do not change the placement of the first four elements so that future + * compatibility will always be guaranteed at least for the identifiers. + * + * The other portion of the structure may be changed as necessary to accommodate + * new features. Be sure to increment version number if you change it. + */ +struct usable_ram_partition_table_v3 { + struct usable_ram_partition_table_header header; + + u32 reserved2; /* Added for 8 bytes alignment of header */ + + /* RAM partition table entries */ + struct ram_partition_entry_v3 entries[RAM_NUM_PART_ENTRIES]; +}; + +/* Version 1 structure 32 Bit - Holds information for an entry in the RAM partition table */ +struct ram_partition_entry_v1 { + char name[RAM_PART_NAME_LENGTH]; /* Partition name, unused for now */ + u64 start_address; /* Partition start address in RAM */ + u64 length; /* Partition length in RAM in Bytes */ + u32 partition_attribute; /* Partition attribute */ + u32 partition_category; /* Partition category */ + u32 partition_domain; /* Partition domain */ + u32 partition_type; /* Partition type */ + u32 num_partitions; /* Number of partitions on device */ + u32 hw_info; /* hw information such as type and frequency */ + u32 reserved4; /* Reserved for future use */ + u32 reserved5; /* Reserved for future use */ +}; + +/* + * Defines the RAM partition table structure (Version 1) + * + * Do not change the placement of the first four elements so that future + * compatibility will always be guaranteed at least for the identifiers. + * + * The other portion of the structure may be changed as necessary to accommodate + * new features. Be sure to increment version number if you change it. + */ +struct usable_ram_partition_table_v1 { + struct usable_ram_partition_table_header header; + + u32 reserved2; /* Added for 8 bytes alignment of header */ + + /* RAM partition table entries */ + struct ram_partition_entry_v1 entries[RAM_NUM_PART_ENTRIES]; +}; + +/* Version 0 structure 32 Bit - Holds information for an entry in the RAM partition table */ +struct ram_partition_entry_v0 { + char name[RAM_PART_NAME_LENGTH]; /* Partition name, unused for now */ + u32 start_address; /* Partition start address in RAM */ + u32 length; /* Partition length in RAM in Bytes */ + u32 partition_attribute; /* Partition attribute */ + u32 partition_category; /* Partition category */ + u32 partition_domain; /* Partition domain */ + u32 partition_type; /* Partition type */ + u32 num_partitions; /* Number of partitions on device */ + u32 reserved3; /* Reserved for future use */ + u32 reserved4; /* Reserved for future use */ + u32 reserved5; /* Reserved for future use */ +}; + +/* + * Defines the RAM partition table structure (Version 0) + * + * Do not change the placement of the first four elements so that future + * compatibility will always be guaranteed at least for the identifiers. + * + * The other portion of the structure may be changed as necessary to accommodate + * new features. Be sure to increment version number if you change it. + */ +struct usable_ram_partition_table_v0 { + struct usable_ram_partition_table_header header; + + /* RAM partition table entries */ + struct ram_partition_entry_v0 entries[RAM_NUM_PART_ENTRIES]; +}; + +#endif // __QCOM_SMEM_RAMPART_H__ -- 2.53.0

