xref: /rk3399_ARM-atf/plat/qemu/qemu_sbsa/sbsa_platform.c (revision 10ecd58093a34e95e2dfad65b1180610f29397cc)
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 #if !ENABLE_RME
280 static 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 */
288 static 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 
394 void sbsa_platform_init(void)
395 {
396 	/* Read DeviceTree data before MMU is enabled */
397 
398 	void *dtb = plat_qemu_dt_runtime_address();
399 	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 
416 	if (set_system_memory_base(dtb, NS_DRAM0_BASE)) {
417 		ERROR("Failed to set system memory in Device Tree\n");
418 		return;
419 	}
420 
421 	read_platform_config_from_dt(dtb);
422 	read_cpuinfo_from_dt(dtb);
423 	read_meminfo_from_dt(dtb);
424 }
425 
426 int sbsa_platform_version_major(void)
427 {
428 	return platform_version_major;
429 }
430 
431 int sbsa_platform_version_minor(void)
432 {
433 	return platform_version_minor;
434 }
435 
436 uint32_t sbsa_platform_num_cpus(void)
437 {
438 	return dynamic_platform_info.num_cpus;
439 }
440 
441 uint32_t sbsa_platform_num_memnodes(void)
442 {
443 	return dynamic_platform_info.num_memnodes;
444 }
445 
446 uint64_t sbsa_platform_gic_its_addr(void)
447 {
448 	return gic_its_addr;
449 }
450 
451 struct platform_cpu_data sbsa_platform_cpu_node(uint64_t index)
452 {
453 	return dynamic_platform_info.cpu[index];
454 }
455 
456 struct platform_memory_data sbsa_platform_memory_node(uint64_t index)
457 {
458 	return dynamic_platform_info.memory[index];
459 }
460 
461 struct platform_cpu_topology sbsa_platform_cpu_topology(void)
462 {
463 	return dynamic_platform_info.cpu_topo;
464 }
465