1*ecadac7cSMathieu Poirier /* 2*ecadac7cSMathieu Poirier * Copyright (c) 2024-2025, Linaro Limited. All rights reserved. 3*ecadac7cSMathieu Poirier * 4*ecadac7cSMathieu Poirier * SPDX-License-Identifier: BSD-3-Clause 5*ecadac7cSMathieu Poirier */ 6*ecadac7cSMathieu Poirier 7*ecadac7cSMathieu Poirier #include <assert.h> 8*ecadac7cSMathieu Poirier 9*ecadac7cSMathieu Poirier #include <common/fdt_wrappers.h> 10*ecadac7cSMathieu Poirier #include <libfdt.h> 11*ecadac7cSMathieu Poirier 12*ecadac7cSMathieu Poirier #include <sbsa_platform.h> 13*ecadac7cSMathieu Poirier 14*ecadac7cSMathieu Poirier /* default platform version is 0.0 */ 15*ecadac7cSMathieu Poirier static int platform_version_major; 16*ecadac7cSMathieu Poirier static int platform_version_minor; 17*ecadac7cSMathieu Poirier 18*ecadac7cSMathieu Poirier static uint64_t gic_its_addr; 19*ecadac7cSMathieu Poirier static struct qemu_platform_info dynamic_platform_info; 20*ecadac7cSMathieu Poirier 21*ecadac7cSMathieu Poirier void sbsa_set_gic_bases(const uintptr_t gicd_base, const uintptr_t gicr_base); 22*ecadac7cSMathieu Poirier 23*ecadac7cSMathieu Poirier /* 24*ecadac7cSMathieu Poirier * QEMU provides us with minimal information about hardware platform using 25*ecadac7cSMathieu Poirier * minimalistic DeviceTree. This is not a Linux DeviceTree. It is not even 26*ecadac7cSMathieu Poirier * a firmware DeviceTree. 27*ecadac7cSMathieu Poirier * 28*ecadac7cSMathieu Poirier * It is information passed from QEMU to describe the information a hardware 29*ecadac7cSMathieu Poirier * platform would have other mechanisms to discover at runtime, that are 30*ecadac7cSMathieu Poirier * affected by the QEMU command line. 31*ecadac7cSMathieu Poirier * 32*ecadac7cSMathieu Poirier * Ultimately this device tree will be replaced by IPC calls to an emulated SCP. 33*ecadac7cSMathieu Poirier * And when we do that, we won't then have to rewrite Normal world firmware to 34*ecadac7cSMathieu Poirier * cope. 35*ecadac7cSMathieu Poirier */ 36*ecadac7cSMathieu Poirier 37*ecadac7cSMathieu Poirier static void read_cpu_topology_from_dt(void *dtb) 38*ecadac7cSMathieu Poirier { 39*ecadac7cSMathieu Poirier int node; 40*ecadac7cSMathieu Poirier 41*ecadac7cSMathieu Poirier /* 42*ecadac7cSMathieu Poirier * QEMU gives us this DeviceTree node when we config: 43*ecadac7cSMathieu Poirier * -smp 16,sockets=2,clusters=2,cores=2,threads=2 44*ecadac7cSMathieu Poirier * 45*ecadac7cSMathieu Poirier * topology { 46*ecadac7cSMathieu Poirier * threads = <0x02>; 47*ecadac7cSMathieu Poirier * cores = <0x02>; 48*ecadac7cSMathieu Poirier * clusters = <0x02>; 49*ecadac7cSMathieu Poirier * sockets = <0x02>; 50*ecadac7cSMathieu Poirier * }; 51*ecadac7cSMathieu Poirier */ 52*ecadac7cSMathieu Poirier 53*ecadac7cSMathieu Poirier node = fdt_path_offset(dtb, "/cpus/topology"); 54*ecadac7cSMathieu Poirier if (node > 0) { 55*ecadac7cSMathieu Poirier dynamic_platform_info.cpu_topo.sockets = 56*ecadac7cSMathieu Poirier fdt_read_uint32_default(dtb, node, "sockets", 0); 57*ecadac7cSMathieu Poirier dynamic_platform_info.cpu_topo.clusters = 58*ecadac7cSMathieu Poirier fdt_read_uint32_default(dtb, node, "clusters", 0); 59*ecadac7cSMathieu Poirier dynamic_platform_info.cpu_topo.cores = 60*ecadac7cSMathieu Poirier fdt_read_uint32_default(dtb, node, "cores", 0); 61*ecadac7cSMathieu Poirier dynamic_platform_info.cpu_topo.threads = 62*ecadac7cSMathieu Poirier fdt_read_uint32_default(dtb, node, "threads", 0); 63*ecadac7cSMathieu Poirier } 64*ecadac7cSMathieu Poirier 65*ecadac7cSMathieu Poirier INFO("Cpu topology: sockets: %d, clusters: %d, cores: %d, threads: %d\n", 66*ecadac7cSMathieu Poirier dynamic_platform_info.cpu_topo.sockets, 67*ecadac7cSMathieu Poirier dynamic_platform_info.cpu_topo.clusters, 68*ecadac7cSMathieu Poirier dynamic_platform_info.cpu_topo.cores, 69*ecadac7cSMathieu Poirier dynamic_platform_info.cpu_topo.threads); 70*ecadac7cSMathieu Poirier } 71*ecadac7cSMathieu Poirier 72*ecadac7cSMathieu Poirier static void read_cpuinfo_from_dt(void *dtb) 73*ecadac7cSMathieu Poirier { 74*ecadac7cSMathieu Poirier int node; 75*ecadac7cSMathieu Poirier int prev; 76*ecadac7cSMathieu Poirier int cpu = 0; 77*ecadac7cSMathieu Poirier uintptr_t mpidr; 78*ecadac7cSMathieu Poirier 79*ecadac7cSMathieu Poirier /* 80*ecadac7cSMathieu Poirier * QEMU gives us this DeviceTree node: 81*ecadac7cSMathieu Poirier * numa-node-id entries are only when NUMA config is used 82*ecadac7cSMathieu Poirier * 83*ecadac7cSMathieu Poirier * cpus { 84*ecadac7cSMathieu Poirier * #size-cells = <0x00>; 85*ecadac7cSMathieu Poirier * #address-cells = <0x02>; 86*ecadac7cSMathieu Poirier * 87*ecadac7cSMathieu Poirier * cpu@0 { 88*ecadac7cSMathieu Poirier * numa-node-id = <0x00>; 89*ecadac7cSMathieu Poirier * reg = <0x00 0x00>; 90*ecadac7cSMathieu Poirier * }; 91*ecadac7cSMathieu Poirier * 92*ecadac7cSMathieu Poirier * cpu@1 { 93*ecadac7cSMathieu Poirier * numa-node-id = <0x03>; 94*ecadac7cSMathieu Poirier * reg = <0x00 0x01>; 95*ecadac7cSMathieu Poirier * }; 96*ecadac7cSMathieu Poirier * }; 97*ecadac7cSMathieu Poirier */ 98*ecadac7cSMathieu Poirier node = fdt_path_offset(dtb, "/cpus"); 99*ecadac7cSMathieu Poirier if (node < 0) { 100*ecadac7cSMathieu Poirier ERROR("No information about cpus in DeviceTree.\n"); 101*ecadac7cSMathieu Poirier panic(); 102*ecadac7cSMathieu Poirier } 103*ecadac7cSMathieu Poirier 104*ecadac7cSMathieu Poirier /* 105*ecadac7cSMathieu Poirier * QEMU numbers cpus from 0 and there can be /cpus/cpu-map present so we 106*ecadac7cSMathieu Poirier * cannot use fdt_first_subnode() here 107*ecadac7cSMathieu Poirier */ 108*ecadac7cSMathieu Poirier node = fdt_path_offset(dtb, "/cpus/cpu@0"); 109*ecadac7cSMathieu Poirier 110*ecadac7cSMathieu Poirier while (node > 0) { 111*ecadac7cSMathieu Poirier if (fdt_getprop(dtb, node, "reg", NULL)) { 112*ecadac7cSMathieu Poirier fdt_get_reg_props_by_index(dtb, node, 0, &mpidr, NULL); 113*ecadac7cSMathieu Poirier } else { 114*ecadac7cSMathieu Poirier ERROR("Incomplete information for cpu %d in DeviceTree.\n", cpu); 115*ecadac7cSMathieu Poirier panic(); 116*ecadac7cSMathieu Poirier } 117*ecadac7cSMathieu Poirier 118*ecadac7cSMathieu Poirier dynamic_platform_info.cpu[cpu].mpidr = mpidr; 119*ecadac7cSMathieu Poirier dynamic_platform_info.cpu[cpu].nodeid = 120*ecadac7cSMathieu Poirier fdt_read_uint32_default(dtb, node, "numa-node-id", 0); 121*ecadac7cSMathieu Poirier 122*ecadac7cSMathieu Poirier INFO("CPU %d: node-id: %d, mpidr: %ld\n", cpu, 123*ecadac7cSMathieu Poirier dynamic_platform_info.cpu[cpu].nodeid, mpidr); 124*ecadac7cSMathieu Poirier 125*ecadac7cSMathieu Poirier cpu++; 126*ecadac7cSMathieu Poirier 127*ecadac7cSMathieu Poirier prev = node; 128*ecadac7cSMathieu Poirier node = fdt_next_subnode(dtb, prev); 129*ecadac7cSMathieu Poirier } 130*ecadac7cSMathieu Poirier 131*ecadac7cSMathieu Poirier dynamic_platform_info.num_cpus = cpu; 132*ecadac7cSMathieu Poirier INFO("Found %d cpus\n", dynamic_platform_info.num_cpus); 133*ecadac7cSMathieu Poirier 134*ecadac7cSMathieu Poirier read_cpu_topology_from_dt(dtb); 135*ecadac7cSMathieu Poirier } 136*ecadac7cSMathieu Poirier 137*ecadac7cSMathieu Poirier static void read_meminfo_from_dt(void *dtb) 138*ecadac7cSMathieu Poirier { 139*ecadac7cSMathieu Poirier const fdt32_t *prop; 140*ecadac7cSMathieu Poirier const char *type; 141*ecadac7cSMathieu Poirier int prev, node; 142*ecadac7cSMathieu Poirier int len; 143*ecadac7cSMathieu Poirier uint32_t memnode = 0; 144*ecadac7cSMathieu Poirier uint32_t higher_value, lower_value; 145*ecadac7cSMathieu Poirier uint64_t cur_base, cur_size; 146*ecadac7cSMathieu Poirier 147*ecadac7cSMathieu Poirier /* 148*ecadac7cSMathieu Poirier * QEMU gives us this DeviceTree node: 149*ecadac7cSMathieu Poirier * 150*ecadac7cSMathieu Poirier * memory@100c0000000 { 151*ecadac7cSMathieu Poirier * numa-node-id = <0x01>; 152*ecadac7cSMathieu Poirier * reg = <0x100 0xc0000000 0x00 0x40000000>; 153*ecadac7cSMathieu Poirier * device_type = "memory"; 154*ecadac7cSMathieu Poirier * }; 155*ecadac7cSMathieu Poirier * 156*ecadac7cSMathieu Poirier * memory@10000000000 { 157*ecadac7cSMathieu Poirier * numa-node-id = <0x00>; 158*ecadac7cSMathieu Poirier * reg = <0x100 0x00 0x00 0xc0000000>; 159*ecadac7cSMathieu Poirier * device_type = "memory"; 160*ecadac7cSMathieu Poirier * } 161*ecadac7cSMathieu Poirier */ 162*ecadac7cSMathieu Poirier 163*ecadac7cSMathieu Poirier for (prev = 0;; prev = node) { 164*ecadac7cSMathieu Poirier node = fdt_next_node(dtb, prev, NULL); 165*ecadac7cSMathieu Poirier if (node < 0) { 166*ecadac7cSMathieu Poirier break; 167*ecadac7cSMathieu Poirier } 168*ecadac7cSMathieu Poirier 169*ecadac7cSMathieu Poirier type = fdt_getprop(dtb, node, "device_type", &len); 170*ecadac7cSMathieu Poirier if (type && strncmp(type, "memory", len) == 0) { 171*ecadac7cSMathieu Poirier dynamic_platform_info.memory[memnode].nodeid = 172*ecadac7cSMathieu Poirier fdt_read_uint32_default(dtb, node, "numa-node-id", 0); 173*ecadac7cSMathieu Poirier 174*ecadac7cSMathieu Poirier /* 175*ecadac7cSMathieu Poirier * Get the 'reg' property of this node and 176*ecadac7cSMathieu Poirier * assume two 8 bytes for base and size. 177*ecadac7cSMathieu Poirier */ 178*ecadac7cSMathieu Poirier prop = fdt_getprop(dtb, node, "reg", &len); 179*ecadac7cSMathieu Poirier if (prop != 0 && len == (2 * sizeof(int64_t))) { 180*ecadac7cSMathieu Poirier higher_value = fdt32_to_cpu(*prop); 181*ecadac7cSMathieu Poirier lower_value = fdt32_to_cpu(*(prop + 1)); 182*ecadac7cSMathieu Poirier cur_base = (uint64_t)(lower_value | ((uint64_t)higher_value) << 32); 183*ecadac7cSMathieu Poirier 184*ecadac7cSMathieu Poirier higher_value = fdt32_to_cpu(*(prop + 2)); 185*ecadac7cSMathieu Poirier lower_value = fdt32_to_cpu(*(prop + 3)); 186*ecadac7cSMathieu Poirier cur_size = (uint64_t)(lower_value | ((uint64_t)higher_value) << 32); 187*ecadac7cSMathieu Poirier 188*ecadac7cSMathieu Poirier dynamic_platform_info.memory[memnode].addr_base = cur_base; 189*ecadac7cSMathieu Poirier dynamic_platform_info.memory[memnode].addr_size = cur_size; 190*ecadac7cSMathieu Poirier 191*ecadac7cSMathieu Poirier INFO("RAM %d: node-id: %d, address: 0x%lx - 0x%lx\n", 192*ecadac7cSMathieu Poirier memnode, 193*ecadac7cSMathieu Poirier dynamic_platform_info.memory[memnode].nodeid, 194*ecadac7cSMathieu Poirier dynamic_platform_info.memory[memnode].addr_base, 195*ecadac7cSMathieu Poirier dynamic_platform_info.memory[memnode].addr_base + 196*ecadac7cSMathieu Poirier dynamic_platform_info.memory[memnode].addr_size - 1); 197*ecadac7cSMathieu Poirier } 198*ecadac7cSMathieu Poirier 199*ecadac7cSMathieu Poirier memnode++; 200*ecadac7cSMathieu Poirier } 201*ecadac7cSMathieu Poirier } 202*ecadac7cSMathieu Poirier 203*ecadac7cSMathieu Poirier dynamic_platform_info.num_memnodes = memnode; 204*ecadac7cSMathieu Poirier } 205*ecadac7cSMathieu Poirier 206*ecadac7cSMathieu Poirier static void read_platform_config_from_dt(void *dtb) 207*ecadac7cSMathieu Poirier { 208*ecadac7cSMathieu Poirier int node; 209*ecadac7cSMathieu Poirier const fdt64_t *data; 210*ecadac7cSMathieu Poirier int err; 211*ecadac7cSMathieu Poirier uintptr_t gicd_base; 212*ecadac7cSMathieu Poirier uintptr_t gicr_base; 213*ecadac7cSMathieu Poirier 214*ecadac7cSMathieu Poirier /* 215*ecadac7cSMathieu Poirier * QEMU gives us this DeviceTree node: 216*ecadac7cSMathieu Poirier * 217*ecadac7cSMathieu Poirier * intc { 218*ecadac7cSMathieu Poirier * reg = < 0x00 0x40060000 0x00 0x10000 219*ecadac7cSMathieu Poirier * 0x00 0x40080000 0x00 0x4000000>; 220*ecadac7cSMathieu Poirier * its { 221*ecadac7cSMathieu Poirier * reg = <0x00 0x44081000 0x00 0x20000>; 222*ecadac7cSMathieu Poirier * }; 223*ecadac7cSMathieu Poirier * }; 224*ecadac7cSMathieu Poirier */ 225*ecadac7cSMathieu Poirier node = fdt_path_offset(dtb, "/intc"); 226*ecadac7cSMathieu Poirier if (node < 0) { 227*ecadac7cSMathieu Poirier return; 228*ecadac7cSMathieu Poirier } 229*ecadac7cSMathieu Poirier 230*ecadac7cSMathieu Poirier data = fdt_getprop(dtb, node, "reg", NULL); 231*ecadac7cSMathieu Poirier if (data == NULL) { 232*ecadac7cSMathieu Poirier return; 233*ecadac7cSMathieu Poirier } 234*ecadac7cSMathieu Poirier 235*ecadac7cSMathieu Poirier err = fdt_get_reg_props_by_index(dtb, node, 0, &gicd_base, NULL); 236*ecadac7cSMathieu Poirier if (err < 0) { 237*ecadac7cSMathieu Poirier ERROR("Failed to read GICD reg property of GIC node\n"); 238*ecadac7cSMathieu Poirier return; 239*ecadac7cSMathieu Poirier } 240*ecadac7cSMathieu Poirier INFO("GICD base = 0x%lx\n", gicd_base); 241*ecadac7cSMathieu Poirier 242*ecadac7cSMathieu Poirier err = fdt_get_reg_props_by_index(dtb, node, 1, &gicr_base, NULL); 243*ecadac7cSMathieu Poirier if (err < 0) { 244*ecadac7cSMathieu Poirier ERROR("Failed to read GICR reg property of GIC node\n"); 245*ecadac7cSMathieu Poirier return; 246*ecadac7cSMathieu Poirier } 247*ecadac7cSMathieu Poirier INFO("GICR base = 0x%lx\n", gicr_base); 248*ecadac7cSMathieu Poirier 249*ecadac7cSMathieu Poirier sbsa_set_gic_bases(gicd_base, gicr_base); 250*ecadac7cSMathieu Poirier 251*ecadac7cSMathieu Poirier node = fdt_path_offset(dtb, "/intc/its"); 252*ecadac7cSMathieu Poirier if (node < 0) { 253*ecadac7cSMathieu Poirier return; 254*ecadac7cSMathieu Poirier } 255*ecadac7cSMathieu Poirier 256*ecadac7cSMathieu Poirier err = fdt_get_reg_props_by_index(dtb, node, 0, &gic_its_addr, NULL); 257*ecadac7cSMathieu Poirier if (err < 0) { 258*ecadac7cSMathieu Poirier ERROR("Failed to read GICI reg property of GIC node\n"); 259*ecadac7cSMathieu Poirier return; 260*ecadac7cSMathieu Poirier } 261*ecadac7cSMathieu Poirier INFO("GICI base = 0x%lx\n", gic_its_addr); 262*ecadac7cSMathieu Poirier } 263*ecadac7cSMathieu Poirier 264*ecadac7cSMathieu Poirier static void read_platform_version(void *dtb) 265*ecadac7cSMathieu Poirier { 266*ecadac7cSMathieu Poirier int node; 267*ecadac7cSMathieu Poirier 268*ecadac7cSMathieu Poirier node = fdt_path_offset(dtb, "/"); 269*ecadac7cSMathieu Poirier if (node >= 0) { 270*ecadac7cSMathieu Poirier platform_version_major = 271*ecadac7cSMathieu Poirier fdt_read_uint32_default(dtb, node, "machine-version-major", 0); 272*ecadac7cSMathieu Poirier platform_version_minor = 273*ecadac7cSMathieu Poirier fdt_read_uint32_default(dtb, node, "machine-version-minor", 0); 274*ecadac7cSMathieu Poirier } 275*ecadac7cSMathieu Poirier } 276*ecadac7cSMathieu Poirier 277*ecadac7cSMathieu Poirier void sbsa_platform_init(void) 278*ecadac7cSMathieu Poirier { 279*ecadac7cSMathieu Poirier /* Read DeviceTree data before MMU is enabled */ 280*ecadac7cSMathieu Poirier 281*ecadac7cSMathieu Poirier void *dtb = (void *)(uintptr_t)ARM_PRELOADED_DTB_BASE; 282*ecadac7cSMathieu Poirier int err; 283*ecadac7cSMathieu Poirier 284*ecadac7cSMathieu Poirier err = fdt_open_into(dtb, dtb, PLAT_QEMU_DT_MAX_SIZE); 285*ecadac7cSMathieu Poirier if (err < 0) { 286*ecadac7cSMathieu Poirier ERROR("Invalid Device Tree at %p: error %d\n", dtb, err); 287*ecadac7cSMathieu Poirier return; 288*ecadac7cSMathieu Poirier } 289*ecadac7cSMathieu Poirier 290*ecadac7cSMathieu Poirier err = fdt_check_header(dtb); 291*ecadac7cSMathieu Poirier if (err < 0) { 292*ecadac7cSMathieu Poirier ERROR("Invalid DTB file passed\n"); 293*ecadac7cSMathieu Poirier return; 294*ecadac7cSMathieu Poirier } 295*ecadac7cSMathieu Poirier 296*ecadac7cSMathieu Poirier read_platform_version(dtb); 297*ecadac7cSMathieu Poirier INFO("Platform version: %d.%d\n", platform_version_major, platform_version_minor); 298*ecadac7cSMathieu Poirier 299*ecadac7cSMathieu Poirier read_platform_config_from_dt(dtb); 300*ecadac7cSMathieu Poirier read_cpuinfo_from_dt(dtb); 301*ecadac7cSMathieu Poirier read_meminfo_from_dt(dtb); 302*ecadac7cSMathieu Poirier } 303*ecadac7cSMathieu Poirier 304*ecadac7cSMathieu Poirier int sbsa_platform_version_major(void) 305*ecadac7cSMathieu Poirier { 306*ecadac7cSMathieu Poirier return platform_version_major; 307*ecadac7cSMathieu Poirier } 308*ecadac7cSMathieu Poirier 309*ecadac7cSMathieu Poirier int sbsa_platform_version_minor(void) 310*ecadac7cSMathieu Poirier { 311*ecadac7cSMathieu Poirier return platform_version_minor; 312*ecadac7cSMathieu Poirier } 313*ecadac7cSMathieu Poirier 314*ecadac7cSMathieu Poirier uint32_t sbsa_platform_num_cpus(void) 315*ecadac7cSMathieu Poirier { 316*ecadac7cSMathieu Poirier return dynamic_platform_info.num_cpus; 317*ecadac7cSMathieu Poirier } 318*ecadac7cSMathieu Poirier 319*ecadac7cSMathieu Poirier uint32_t sbsa_platform_num_memnodes(void) 320*ecadac7cSMathieu Poirier { 321*ecadac7cSMathieu Poirier return dynamic_platform_info.num_memnodes; 322*ecadac7cSMathieu Poirier } 323*ecadac7cSMathieu Poirier 324*ecadac7cSMathieu Poirier uint64_t sbsa_platform_gic_its_addr(void) 325*ecadac7cSMathieu Poirier { 326*ecadac7cSMathieu Poirier return gic_its_addr; 327*ecadac7cSMathieu Poirier } 328*ecadac7cSMathieu Poirier 329*ecadac7cSMathieu Poirier struct platform_cpu_data sbsa_platform_cpu_node(uint64_t index) 330*ecadac7cSMathieu Poirier { 331*ecadac7cSMathieu Poirier return dynamic_platform_info.cpu[index]; 332*ecadac7cSMathieu Poirier } 333*ecadac7cSMathieu Poirier 334*ecadac7cSMathieu Poirier struct platform_memory_data sbsa_platform_memory_node(uint64_t index) 335*ecadac7cSMathieu Poirier { 336*ecadac7cSMathieu Poirier return dynamic_platform_info.memory[index]; 337*ecadac7cSMathieu Poirier } 338*ecadac7cSMathieu Poirier 339*ecadac7cSMathieu Poirier struct platform_cpu_topology sbsa_platform_cpu_topology(void) 340*ecadac7cSMathieu Poirier { 341*ecadac7cSMathieu Poirier return dynamic_platform_info.cpu_topo; 342*ecadac7cSMathieu Poirier } 343