blob: 1e1c6299d0063178e3cab83096c14a26b9649b15 [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
279void sbsa_platform_init(void)
280{
281 /* Read DeviceTree data before MMU is enabled */
282
Mathieu Poirier17af9592024-10-10 15:07:49 -0600283 void *dtb = plat_qemu_dt_runtime_address();
Mathieu Poirierecadac72024-10-17 16:39:51 -0600284 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
306int sbsa_platform_version_major(void)
307{
308 return platform_version_major;
309}
310
311int sbsa_platform_version_minor(void)
312{
313 return platform_version_minor;
314}
315
316uint32_t sbsa_platform_num_cpus(void)
317{
318 return dynamic_platform_info.num_cpus;
319}
320
321uint32_t sbsa_platform_num_memnodes(void)
322{
323 return dynamic_platform_info.num_memnodes;
324}
325
326uint64_t sbsa_platform_gic_its_addr(void)
327{
328 return gic_its_addr;
329}
330
331struct platform_cpu_data sbsa_platform_cpu_node(uint64_t index)
332{
333 return dynamic_platform_info.cpu[index];
334}
335
336struct platform_memory_data sbsa_platform_memory_node(uint64_t index)
337{
338 return dynamic_platform_info.memory[index];
339}
340
341struct platform_cpu_topology sbsa_platform_cpu_topology(void)
342{
343 return dynamic_platform_info.cpu_topo;
344}