blob: 8c8d632fce86033f4b230285dc834a4c2af16989 [file] [log] [blame]
Mathieu Poirierecadac72024-10-17 16:39:51 -06001/*
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
Mathieu Poirier17af9592024-10-10 15:07:49 -060014#include "qemu_private.h"
15
Mathieu Poirierecadac72024-10-17 16:39:51 -060016/* default platform version is 0.0 */
17static int platform_version_major;
18static int platform_version_minor;
19
20static uint64_t gic_its_addr;
21static struct qemu_platform_info dynamic_platform_info;
22
23void 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
39static 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
74static 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
139static 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
208static 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
266static 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
Mathieu Poirier99bc6cf2024-10-10 16:03:47 -0600279#if !ENABLE_RME
280static int set_system_memory_base(void *dtb, uintptr_t new_base)
281{
282 (void)dtb;
283 (void)new_base;
284
285 return 0;
286}
287#else /* !ENABLE_RME */
288static int set_system_memory_base(void *dtb, uintptr_t new_base)
289{
290 uint64_t cur_base, cur_size, new_size, delta;
291 int len, prev, node, ret;
292 const fdt32_t *prop;
293 uint32_t node_id;
294 const char *type;
295 fdt64_t new[2];
296
297 /*
298 * QEMU gives us this DeviceTree node:
299 *
300 * memory@100c0000000 {
301 * numa-node-id = <0x01>;
302 * reg = <0x100 0xc0000000 0x00 0x40000000>;
303 * device_type = "memory";
304 * };
305 *
306 * memory@10000000000 {
307 * numa-node-id = <0x00>;
308 * reg = <0x100 0x00 0x00 0xc0000000>;
309 * device_type = "memory";
310 * }
311 */
312
313 for (prev = 0;; prev = node) {
314 node = fdt_next_node(dtb, prev, NULL);
315 if (node < 0) {
316 return node;
317 }
318
319 type = fdt_getprop(dtb, node, "device_type", &len);
320 if (type && strncmp(type, "memory", len) == 0) {
321
322 /*
323 * We are looking for numa node 0, i.e the start of the
324 * system memory. If a "numa-node-id" doesn't exists we
325 * take the first one.
326 */
327 node_id = fdt_read_uint32_default(dtb, node,
328 "numa-node-id", 0);
329
330 if (node_id == 0) {
331 break;
332 }
333 }
334 }
335
336 /*
337 * Get the 'reg' property of this node and
338 * assume two 8 bytes for base and size.
339 */
340 prop = fdt_getprop(dtb, node, "reg", &len);
341 if (!prop || len < 0) {
342 return len;
343 }
344
345 if (len != (2 * sizeof(uint64_t))) {
346 return -FDT_ERR_BADVALUE;
347 }
348
349 ret = fdt_get_reg_props_by_index(dtb, node, 0, &cur_base, &cur_size);
350 if (ret < 0)
351 return ret;
352
353 /*
354 * @cur_base is the base of the NS RAM given to us by QEMU, we can't
355 * go lower than that.
356 */
357 if (new_base < cur_base) {
358 return -FDT_ERR_BADVALUE;
359 }
360
361 if (new_base == cur_base) {
362 return 0;
363 }
364
365 /*
366 * The new base is higher than the base set by QEMU, i.e we are moving
367 * the base memory up and shrinking the size.
368 */
369 delta = (size_t)(new_base - cur_base);
370
371 /*
372 * Make sure the new base is still within the base memory node, i.e
373 * the base memory node is big enough for the RMM.
374 */
375 if (delta >= cur_size) {
376 ERROR("Not enough space in base memory node for RMM\n");
377 return -FDT_ERR_BADVALUE;
378 }
379
380 new_size = cur_size - delta;
381
382 new[0] = cpu_to_fdt64(new_base);
383 new[1] = cpu_to_fdt64(new_size);
384
385 ret = fdt_setprop(dtb, node, "reg", new, len);
386 if (ret < 0) {
387 return ret;
388 }
389
390 return fdt_pack(dtb);
391}
392#endif /* !ENABLE_RME */
393
Mathieu Poirierecadac72024-10-17 16:39:51 -0600394void sbsa_platform_init(void)
395{
396 /* Read DeviceTree data before MMU is enabled */
397
Mathieu Poirier17af9592024-10-10 15:07:49 -0600398 void *dtb = plat_qemu_dt_runtime_address();
Mathieu Poirierecadac72024-10-17 16:39:51 -0600399 int err;
400
401 err = fdt_open_into(dtb, dtb, PLAT_QEMU_DT_MAX_SIZE);
402 if (err < 0) {
403 ERROR("Invalid Device Tree at %p: error %d\n", dtb, err);
404 return;
405 }
406
407 err = fdt_check_header(dtb);
408 if (err < 0) {
409 ERROR("Invalid DTB file passed\n");
410 return;
411 }
412
413 read_platform_version(dtb);
414 INFO("Platform version: %d.%d\n", platform_version_major, platform_version_minor);
415
Mathieu Poirier99bc6cf2024-10-10 16:03:47 -0600416 if (set_system_memory_base(dtb, NS_DRAM0_BASE)) {
417 ERROR("Failed to set system memory in Device Tree\n");
418 return;
419 }
420
Mathieu Poirierecadac72024-10-17 16:39:51 -0600421 read_platform_config_from_dt(dtb);
422 read_cpuinfo_from_dt(dtb);
423 read_meminfo_from_dt(dtb);
424}
425
426int sbsa_platform_version_major(void)
427{
428 return platform_version_major;
429}
430
431int sbsa_platform_version_minor(void)
432{
433 return platform_version_minor;
434}
435
436uint32_t sbsa_platform_num_cpus(void)
437{
438 return dynamic_platform_info.num_cpus;
439}
440
441uint32_t sbsa_platform_num_memnodes(void)
442{
443 return dynamic_platform_info.num_memnodes;
444}
445
446uint64_t sbsa_platform_gic_its_addr(void)
447{
448 return gic_its_addr;
449}
450
451struct platform_cpu_data sbsa_platform_cpu_node(uint64_t index)
452{
453 return dynamic_platform_info.cpu[index];
454}
455
456struct platform_memory_data sbsa_platform_memory_node(uint64_t index)
457{
458 return dynamic_platform_info.memory[index];
459}
460
461struct platform_cpu_topology sbsa_platform_cpu_topology(void)
462{
463 return dynamic_platform_info.cpu_topo;
464}