1 /*
2 * Copyright (c) 2024-2025, Linaro Limited. All rights reserved.
3 * Copyright (c) 2025, Arm Limited and Contributors. All rights reserved.
4 *
5 * SPDX-License-Identifier: BSD-3-Clause
6 */
7
8 #include <assert.h>
9
10 #include <common/fdt_wrappers.h>
11 #include <libfdt.h>
12
13 #include <sbsa_platform.h>
14
15 #include "qemu_private.h"
16
17 /* default platform version is 0.0 */
18 static int platform_version_major;
19 static int platform_version_minor;
20
21 static uint64_t gic_its_addr;
22 static struct qemu_platform_info dynamic_platform_info;
23
24 void sbsa_set_gic_bases(const uintptr_t gicd_base, const uintptr_t gicr_base);
25
26 /*
27 * QEMU provides us with minimal information about hardware platform using
28 * minimalistic DeviceTree. This is not a Linux DeviceTree. It is not even
29 * a firmware DeviceTree.
30 *
31 * It is information passed from QEMU to describe the information a hardware
32 * platform would have other mechanisms to discover at runtime, that are
33 * affected by the QEMU command line.
34 *
35 * Ultimately this device tree will be replaced by IPC calls to an emulated SCP.
36 * And when we do that, we won't then have to rewrite Normal world firmware to
37 * cope.
38 */
39
read_cpu_topology_from_dt(void * dtb)40 static void read_cpu_topology_from_dt(void *dtb)
41 {
42 int node;
43
44 /*
45 * QEMU gives us this DeviceTree node when we config:
46 * -smp 16,sockets=2,clusters=2,cores=2,threads=2
47 *
48 * topology {
49 * threads = <0x02>;
50 * cores = <0x02>;
51 * clusters = <0x02>;
52 * sockets = <0x02>;
53 * };
54 */
55
56 node = fdt_path_offset(dtb, "/cpus/topology");
57 if (node > 0) {
58 dynamic_platform_info.cpu_topo.sockets =
59 fdt_read_uint32_default(dtb, node, "sockets", 0);
60 dynamic_platform_info.cpu_topo.clusters =
61 fdt_read_uint32_default(dtb, node, "clusters", 0);
62 dynamic_platform_info.cpu_topo.cores =
63 fdt_read_uint32_default(dtb, node, "cores", 0);
64 dynamic_platform_info.cpu_topo.threads =
65 fdt_read_uint32_default(dtb, node, "threads", 0);
66 }
67
68 INFO("Cpu topology: sockets: %d, clusters: %d, cores: %d, threads: %d\n",
69 dynamic_platform_info.cpu_topo.sockets,
70 dynamic_platform_info.cpu_topo.clusters,
71 dynamic_platform_info.cpu_topo.cores,
72 dynamic_platform_info.cpu_topo.threads);
73 }
74
read_cpuinfo_from_dt(void * dtb)75 static void read_cpuinfo_from_dt(void *dtb)
76 {
77 int node;
78 int prev;
79 int cpu = 0;
80 uintptr_t mpidr;
81
82 /*
83 * QEMU gives us this DeviceTree node:
84 * numa-node-id entries are only when NUMA config is used
85 *
86 * cpus {
87 * #size-cells = <0x00>;
88 * #address-cells = <0x02>;
89 *
90 * cpu@0 {
91 * numa-node-id = <0x00>;
92 * reg = <0x00 0x00>;
93 * };
94 *
95 * cpu@1 {
96 * numa-node-id = <0x03>;
97 * reg = <0x00 0x01>;
98 * };
99 * };
100 */
101 node = fdt_path_offset(dtb, "/cpus");
102 if (node < 0) {
103 ERROR("No information about cpus in DeviceTree.\n");
104 panic();
105 }
106
107 /*
108 * QEMU numbers cpus from 0 and there can be /cpus/cpu-map present so we
109 * cannot use fdt_first_subnode() here
110 */
111 node = fdt_path_offset(dtb, "/cpus/cpu@0");
112
113 while (node > 0) {
114 if (fdt_getprop(dtb, node, "reg", NULL)) {
115 fdt_get_reg_props_by_index(dtb, node, 0, &mpidr, NULL);
116 } else {
117 ERROR("Incomplete information for cpu %d in DeviceTree.\n", cpu);
118 panic();
119 }
120
121 dynamic_platform_info.cpu[cpu].mpidr = mpidr;
122 dynamic_platform_info.cpu[cpu].nodeid =
123 fdt_read_uint32_default(dtb, node, "numa-node-id", 0);
124
125 INFO("CPU %d: node-id: %d, mpidr: %ld\n", cpu,
126 dynamic_platform_info.cpu[cpu].nodeid, mpidr);
127
128 cpu++;
129
130 prev = node;
131 node = fdt_next_subnode(dtb, prev);
132 }
133
134 dynamic_platform_info.num_cpus = cpu;
135 INFO("Found %d cpus\n", dynamic_platform_info.num_cpus);
136
137 read_cpu_topology_from_dt(dtb);
138 }
139
read_meminfo_from_dt(void * dtb)140 static void read_meminfo_from_dt(void *dtb)
141 {
142 const fdt32_t *prop;
143 const char *type;
144 int prev, node;
145 int len;
146 uint32_t memnode = 0;
147 uint32_t higher_value, lower_value;
148 uint64_t cur_base, cur_size;
149
150 /*
151 * QEMU gives us this DeviceTree node:
152 *
153 * memory@100c0000000 {
154 * numa-node-id = <0x01>;
155 * reg = <0x100 0xc0000000 0x00 0x40000000>;
156 * device_type = "memory";
157 * };
158 *
159 * memory@10000000000 {
160 * numa-node-id = <0x00>;
161 * reg = <0x100 0x00 0x00 0xc0000000>;
162 * device_type = "memory";
163 * }
164 */
165
166 for (prev = 0;; prev = node) {
167 node = fdt_next_node(dtb, prev, NULL);
168 if (node < 0) {
169 break;
170 }
171
172 type = fdt_getprop(dtb, node, "device_type", &len);
173 if (type && strncmp(type, "memory", len) == 0) {
174 dynamic_platform_info.memory[memnode].nodeid =
175 fdt_read_uint32_default(dtb, node, "numa-node-id", 0);
176
177 /*
178 * Get the 'reg' property of this node and
179 * assume two 8 bytes for base and size.
180 */
181 prop = fdt_getprop(dtb, node, "reg", &len);
182 if (prop != 0 && len == (2 * sizeof(int64_t))) {
183 higher_value = fdt32_to_cpu(*prop);
184 lower_value = fdt32_to_cpu(*(prop + 1));
185 cur_base = (uint64_t)(lower_value | ((uint64_t)higher_value) << 32);
186
187 higher_value = fdt32_to_cpu(*(prop + 2));
188 lower_value = fdt32_to_cpu(*(prop + 3));
189 cur_size = (uint64_t)(lower_value | ((uint64_t)higher_value) << 32);
190
191 dynamic_platform_info.memory[memnode].addr_base = cur_base;
192 dynamic_platform_info.memory[memnode].addr_size = cur_size;
193
194 INFO("RAM %d: node-id: %d, address: 0x%lx - 0x%lx\n",
195 memnode,
196 dynamic_platform_info.memory[memnode].nodeid,
197 dynamic_platform_info.memory[memnode].addr_base,
198 dynamic_platform_info.memory[memnode].addr_base +
199 dynamic_platform_info.memory[memnode].addr_size - 1);
200 }
201
202 memnode++;
203 }
204 }
205
206 dynamic_platform_info.num_memnodes = memnode;
207 }
208
read_platform_config_from_dt(void * dtb)209 static void read_platform_config_from_dt(void *dtb)
210 {
211 int node;
212 const fdt64_t *data;
213 int err;
214 uintptr_t gicd_base;
215 uintptr_t gicr_base;
216
217 /*
218 * QEMU gives us this DeviceTree node:
219 *
220 * intc {
221 * reg = < 0x00 0x40060000 0x00 0x10000
222 * 0x00 0x40080000 0x00 0x4000000>;
223 * its {
224 * reg = <0x00 0x44081000 0x00 0x20000>;
225 * };
226 * };
227 */
228 node = fdt_path_offset(dtb, "/intc");
229 if (node < 0) {
230 return;
231 }
232
233 data = fdt_getprop(dtb, node, "reg", NULL);
234 if (data == NULL) {
235 return;
236 }
237
238 err = fdt_get_reg_props_by_index(dtb, node, 0, &gicd_base, NULL);
239 if (err < 0) {
240 ERROR("Failed to read GICD reg property of GIC node\n");
241 return;
242 }
243 INFO("GICD base = 0x%lx\n", gicd_base);
244
245 err = fdt_get_reg_props_by_index(dtb, node, 1, &gicr_base, NULL);
246 if (err < 0) {
247 ERROR("Failed to read GICR reg property of GIC node\n");
248 return;
249 }
250 INFO("GICR base = 0x%lx\n", gicr_base);
251
252 sbsa_set_gic_bases(gicd_base, gicr_base);
253
254 node = fdt_path_offset(dtb, "/intc/its");
255 if (node < 0) {
256 return;
257 }
258
259 err = fdt_get_reg_props_by_index(dtb, node, 0, &gic_its_addr, NULL);
260 if (err < 0) {
261 ERROR("Failed to read GICI reg property of GIC node\n");
262 return;
263 }
264 INFO("GICI base = 0x%lx\n", gic_its_addr);
265 }
266
read_platform_version(void * dtb)267 static void read_platform_version(void *dtb)
268 {
269 int node;
270
271 node = fdt_path_offset(dtb, "/");
272 if (node >= 0) {
273 platform_version_major =
274 fdt_read_uint32_default(dtb, node, "machine-version-major", 0);
275 platform_version_minor =
276 fdt_read_uint32_default(dtb, node, "machine-version-minor", 0);
277 }
278 }
279
280 #if !ENABLE_RMM
set_system_memory_base(void * dtb,uintptr_t new_base)281 static int set_system_memory_base(void *dtb, uintptr_t new_base)
282 {
283 (void)dtb;
284 (void)new_base;
285
286 return 0;
287 }
288 #else /* !ENABLE_RMM */
set_system_memory_base(void * dtb,uintptr_t new_base)289 static int set_system_memory_base(void *dtb, uintptr_t new_base)
290 {
291 uint64_t cur_base, cur_size, new_size, delta;
292 int len, prev, node, ret;
293 const fdt32_t *prop;
294 uint32_t node_id;
295 const char *type;
296 fdt64_t new[2];
297
298 /*
299 * QEMU gives us this DeviceTree node:
300 *
301 * memory@100c0000000 {
302 * numa-node-id = <0x01>;
303 * reg = <0x100 0xc0000000 0x00 0x40000000>;
304 * device_type = "memory";
305 * };
306 *
307 * memory@10000000000 {
308 * numa-node-id = <0x00>;
309 * reg = <0x100 0x00 0x00 0xc0000000>;
310 * device_type = "memory";
311 * }
312 */
313
314 for (prev = 0;; prev = node) {
315 node = fdt_next_node(dtb, prev, NULL);
316 if (node < 0) {
317 return node;
318 }
319
320 type = fdt_getprop(dtb, node, "device_type", &len);
321 if (type && strncmp(type, "memory", len) == 0) {
322
323 /*
324 * We are looking for numa node 0, i.e the start of the
325 * system memory. If a "numa-node-id" doesn't exists we
326 * take the first one.
327 */
328 node_id = fdt_read_uint32_default(dtb, node,
329 "numa-node-id", 0);
330
331 if (node_id == 0) {
332 break;
333 }
334 }
335 }
336
337 /*
338 * Get the 'reg' property of this node and
339 * assume two 8 bytes for base and size.
340 */
341 prop = fdt_getprop(dtb, node, "reg", &len);
342 if (!prop || len < 0) {
343 return len;
344 }
345
346 if (len != (2 * sizeof(uint64_t))) {
347 return -FDT_ERR_BADVALUE;
348 }
349
350 ret = fdt_get_reg_props_by_index(dtb, node, 0, &cur_base, &cur_size);
351 if (ret < 0)
352 return ret;
353
354 /*
355 * @cur_base is the base of the NS RAM given to us by QEMU, we can't
356 * go lower than that.
357 */
358 if (new_base < cur_base) {
359 return -FDT_ERR_BADVALUE;
360 }
361
362 if (new_base == cur_base) {
363 return 0;
364 }
365
366 /*
367 * The new base is higher than the base set by QEMU, i.e we are moving
368 * the base memory up and shrinking the size.
369 */
370 delta = (size_t)(new_base - cur_base);
371
372 /*
373 * Make sure the new base is still within the base memory node, i.e
374 * the base memory node is big enough for the RMM.
375 */
376 if (delta >= cur_size) {
377 ERROR("Not enough space in base memory node for RMM\n");
378 return -FDT_ERR_BADVALUE;
379 }
380
381 new_size = cur_size - delta;
382
383 new[0] = cpu_to_fdt64(new_base);
384 new[1] = cpu_to_fdt64(new_size);
385
386 ret = fdt_setprop(dtb, node, "reg", new, len);
387 if (ret < 0) {
388 return ret;
389 }
390
391 return fdt_pack(dtb);
392 }
393 #endif /* !ENABLE_RMM */
394
sbsa_platform_init(void)395 void sbsa_platform_init(void)
396 {
397 /* Read DeviceTree data before MMU is enabled */
398
399 void *dtb = plat_qemu_dt_runtime_address();
400 int err;
401
402 err = fdt_open_into(dtb, dtb, PLAT_QEMU_DT_MAX_SIZE);
403 if (err < 0) {
404 ERROR("Invalid Device Tree at %p: error %d\n", dtb, err);
405 return;
406 }
407
408 err = fdt_check_header(dtb);
409 if (err < 0) {
410 ERROR("Invalid DTB file passed\n");
411 return;
412 }
413
414 read_platform_version(dtb);
415 INFO("Platform version: %d.%d\n", platform_version_major, platform_version_minor);
416
417 if (set_system_memory_base(dtb, NS_DRAM0_BASE)) {
418 ERROR("Failed to set system memory in Device Tree\n");
419 return;
420 }
421
422 read_platform_config_from_dt(dtb);
423 read_cpuinfo_from_dt(dtb);
424 read_meminfo_from_dt(dtb);
425 }
426
sbsa_platform_version_major(void)427 int sbsa_platform_version_major(void)
428 {
429 return platform_version_major;
430 }
431
sbsa_platform_version_minor(void)432 int sbsa_platform_version_minor(void)
433 {
434 return platform_version_minor;
435 }
436
sbsa_platform_num_cpus(void)437 uint32_t sbsa_platform_num_cpus(void)
438 {
439 return dynamic_platform_info.num_cpus;
440 }
441
sbsa_platform_num_memnodes(void)442 uint32_t sbsa_platform_num_memnodes(void)
443 {
444 return dynamic_platform_info.num_memnodes;
445 }
446
sbsa_platform_gic_its_addr(void)447 uint64_t sbsa_platform_gic_its_addr(void)
448 {
449 return gic_its_addr;
450 }
451
sbsa_platform_cpu_node(uint64_t index)452 struct platform_cpu_data sbsa_platform_cpu_node(uint64_t index)
453 {
454 return dynamic_platform_info.cpu[index];
455 }
456
sbsa_platform_memory_node(uint64_t index)457 struct platform_memory_data sbsa_platform_memory_node(uint64_t index)
458 {
459 return dynamic_platform_info.memory[index];
460 }
461
sbsa_platform_cpu_topology(void)462 struct platform_cpu_topology sbsa_platform_cpu_topology(void)
463 {
464 return dynamic_platform_info.cpu_topo;
465 }
466