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