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