xref: /freebsd/usr.sbin/bhyve/aarch64/fdt.c (revision 6227d0f81a5e5e2473ceb8b185098ee9a53c82ff)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2022 The FreeBSD Foundation
5  *
6  * This software was developed by Andrew Turner under sponsorship from
7  * the FreeBSD Foundation.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30 
31 #include <sys/param.h>
32 
33 #include <assert.h>
34 #include <errno.h>
35 #include <stdio.h>
36 #include <unistd.h>
37 
38 #include <libfdt.h>
39 #include <vmmapi.h>
40 
41 #include "config.h"
42 #include "bhyve_machdep.h"
43 #include "bhyverun.h"
44 #include "fdt.h"
45 
46 #define	SET_PROP_U32(prop, idx, val)	\
47     ((uint32_t *)(prop))[(idx)] = cpu_to_fdt32(val)
48 #define	SET_PROP_U64(prop, idx, val)	\
49     ((uint64_t *)(prop))[(idx)] = cpu_to_fdt64(val)
50 
51 #define	GIC_SPI			0
52 #define	GIC_PPI			1
53 #define	IRQ_TYPE_LEVEL_HIGH	4
54 #define	IRQ_TYPE_LEVEL_LOW	8
55 
56 #define	GIC_FIRST_PPI		16
57 #define	GIC_FIRST_SPI		32
58 
59 static void *fdtroot;
60 static uint32_t gic_phandle = 0;
61 static uint32_t apb_pclk_phandle;
62 
63 static uint32_t
assign_phandle(void * fdt)64 assign_phandle(void *fdt)
65 {
66 	static uint32_t next_phandle = 1;
67 	uint32_t phandle;
68 
69 	phandle = next_phandle;
70 	next_phandle++;
71 	fdt_property_u32(fdt, "phandle", phandle);
72 
73 	return (phandle);
74 }
75 
76 static void
set_single_reg(void * fdt,uint64_t start,uint64_t len)77 set_single_reg(void *fdt, uint64_t start, uint64_t len)
78 {
79 	void *reg;
80 
81 	fdt_property_placeholder(fdt, "reg", 2 * sizeof(uint64_t), &reg);
82 	SET_PROP_U64(reg, 0, start);
83 	SET_PROP_U64(reg, 1, len);
84 }
85 
86 static void
add_cpu(void * fdt,int cpuid)87 add_cpu(void *fdt, int cpuid)
88 {
89 	char node_name[16];
90 
91 	snprintf(node_name, sizeof(node_name), "cpu@%d", cpuid);
92 
93 	fdt_begin_node(fdt, node_name);
94 	fdt_property_string(fdt, "device_type", "cpu");
95 	fdt_property_string(fdt, "compatible", "arm,armv8");
96 	fdt_property_u64(fdt, "reg", cpu_to_mpidr[cpuid]);
97 	fdt_property_string(fdt, "enable-method", "psci");
98 	fdt_end_node(fdt);
99 }
100 
101 static void
add_cpus(void * fdt,int ncpu)102 add_cpus(void *fdt, int ncpu)
103 {
104 	int cpuid;
105 
106 	fdt_begin_node(fdt, "cpus");
107 	/* XXX: Needed given the root #address-cells? */
108 	fdt_property_u32(fdt, "#address-cells", 2);
109 	fdt_property_u32(fdt, "#size-cells", 0);
110 
111 	for (cpuid = 0; cpuid < ncpu; cpuid++) {
112 		add_cpu(fdt, cpuid);
113 	}
114 	fdt_end_node(fdt);
115 }
116 
117 int
fdt_init(struct vmctx * ctx,int ncpu,vm_paddr_t fdtaddr,vm_size_t fdtsize)118 fdt_init(struct vmctx *ctx, int ncpu, vm_paddr_t fdtaddr, vm_size_t fdtsize)
119 {
120 	void *fdt;
121 	const char *bootargs;
122 
123 	fdt = paddr_guest2host(ctx, fdtaddr, fdtsize);
124 	if (fdt == NULL)
125 		return (EFAULT);
126 
127 	fdt_create(fdt, (int)fdtsize);
128 
129 	/* Add the memory reserve map (needed even if none is reserved) */
130 	fdt_finish_reservemap(fdt);
131 
132 	/* Create the root node */
133 	fdt_begin_node(fdt, "");
134 
135 	fdt_property_string(fdt, "compatible", "freebsd,bhyve");
136 	fdt_property_u32(fdt, "#address-cells", 2);
137 	fdt_property_u32(fdt, "#size-cells", 2);
138 
139 	fdt_begin_node(fdt, "chosen");
140 	fdt_property_string(fdt, "stdout-path", "serial0:115200n8");
141 	bootargs = get_config_value("fdt.bootargs");
142 	if (bootargs != NULL)
143 		fdt_property_string(fdt, "bootargs", bootargs);
144 	fdt_end_node(fdt);
145 
146 	fdt_begin_node(fdt, "memory");
147 	fdt_property_string(fdt, "device_type", "memory");
148 	/* There is no lowmem on arm64. */
149 	assert(vm_get_lowmem_size(ctx) == 0);
150 	set_single_reg(fdt, vm_get_highmem_base(ctx), vm_get_highmem_size(ctx));
151 	fdt_end_node(fdt);
152 
153 	add_cpus(fdt, ncpu);
154 
155 	fdt_begin_node(fdt, "psci");
156 	fdt_property_string(fdt, "compatible", "arm,psci-1.0");
157 	fdt_property_string(fdt, "method", "hvc");
158 	fdt_end_node(fdt);
159 
160 	fdt_begin_node(fdt, "apb-pclk");
161 	fdt_property_string(fdt, "compatible", "fixed-clock");
162 	fdt_property_string(fdt, "clock-output-names", "clk24mhz");
163 	fdt_property_u32(fdt, "#clock-cells", 0);
164 	fdt_property_u32(fdt, "clock-frequency", 24000000);
165 	apb_pclk_phandle = assign_phandle(fdt);
166 	fdt_end_node(fdt);
167 
168 	/* Finalized by fdt_finalized(). */
169 	fdtroot = fdt;
170 
171 	return (0);
172 }
173 
174 void
fdt_add_gic(uint64_t dist_base,uint64_t dist_size,uint64_t redist_base,uint64_t redist_size)175 fdt_add_gic(uint64_t dist_base, uint64_t dist_size,
176     uint64_t redist_base, uint64_t redist_size)
177 {
178 	char node_name[32];
179 	void *fdt, *prop;
180 
181 	fdt = fdtroot;
182 
183 	snprintf(node_name, sizeof(node_name), "interrupt-controller@%lx",
184 	    (unsigned long)dist_base);
185 	fdt_begin_node(fdt, node_name);
186 
187 	gic_phandle = assign_phandle(fdt);
188 	fdt_property_string(fdt, "compatible", "arm,gic-v3");
189 	fdt_property(fdt, "interrupt-controller", NULL, 0);
190 	fdt_property(fdt, "msi-controller", NULL, 0);
191 	/* XXX: Needed given the root #address-cells? */
192 	fdt_property_u32(fdt, "#address-cells", 2);
193 	fdt_property_u32(fdt, "#interrupt-cells", 3);
194 	fdt_property_placeholder(fdt, "reg", 4 * sizeof(uint64_t), &prop);
195 	/* GICD */
196 	SET_PROP_U64(prop, 0, dist_base);
197 	SET_PROP_U64(prop, 1, dist_size);
198 	/* GICR */
199 	SET_PROP_U64(prop, 2, redist_base);
200 	SET_PROP_U64(prop, 3, redist_size);
201 
202 	fdt_property_placeholder(fdt, "mbi-ranges", 2 * sizeof(uint32_t),
203 	    &prop);
204 	SET_PROP_U32(prop, 0, 256);
205 	SET_PROP_U32(prop, 1, 64);
206 
207 	fdt_end_node(fdt);
208 
209 	fdt_property_u32(fdt, "interrupt-parent", gic_phandle);
210 }
211 
212 void
fdt_add_uart(uint64_t uart_base,uint64_t uart_size,int intr)213 fdt_add_uart(uint64_t uart_base, uint64_t uart_size, int intr)
214 {
215 	void *fdt, *interrupts, *prop;
216 	char node_name[32];
217 
218 	assert(gic_phandle != 0);
219 	assert(apb_pclk_phandle != 0);
220 	assert(intr >= GIC_FIRST_SPI);
221 
222 	fdt = fdtroot;
223 
224 	snprintf(node_name, sizeof(node_name), "serial@%lx", uart_base);
225 	fdt_begin_node(fdt, node_name);
226 #define	UART_COMPAT	"arm,pl011\0arm,primecell"
227 	fdt_property(fdt, "compatible", UART_COMPAT, sizeof(UART_COMPAT));
228 #undef UART_COMPAT
229 	set_single_reg(fdt, uart_base, uart_size);
230 	fdt_property_u32(fdt, "interrupt-parent", gic_phandle);
231 	fdt_property_placeholder(fdt, "interrupts", 3 * sizeof(uint32_t),
232 	    &interrupts);
233 	SET_PROP_U32(interrupts, 0, GIC_SPI);
234 	SET_PROP_U32(interrupts, 1, intr - GIC_FIRST_SPI);
235 	SET_PROP_U32(interrupts, 2, IRQ_TYPE_LEVEL_HIGH);
236 	fdt_property_placeholder(fdt, "clocks", 2 * sizeof(uint32_t), &prop);
237 	SET_PROP_U32(prop, 0, apb_pclk_phandle);
238 	SET_PROP_U32(prop, 1, apb_pclk_phandle);
239 #define	UART_CLK_NAMES	"uartclk\0apb_pclk"
240 	fdt_property(fdt, "clock-names", UART_CLK_NAMES,
241 	    sizeof(UART_CLK_NAMES));
242 #undef UART_CLK_NAMES
243 
244 	fdt_end_node(fdt);
245 
246 	snprintf(node_name, sizeof(node_name), "/serial@%lx", uart_base);
247 	fdt_begin_node(fdt, "aliases");
248 	fdt_property_string(fdt, "serial0", node_name);
249 	fdt_end_node(fdt);
250 }
251 
252 void
fdt_add_rtc(uint64_t rtc_base,uint64_t rtc_size,int intr)253 fdt_add_rtc(uint64_t rtc_base, uint64_t rtc_size, int intr)
254 {
255 	void *fdt, *interrupts, *prop;
256 	char node_name[32];
257 
258 	assert(gic_phandle != 0);
259 	assert(apb_pclk_phandle != 0);
260 	assert(intr >= GIC_FIRST_SPI);
261 
262 	fdt = fdtroot;
263 
264 	snprintf(node_name, sizeof(node_name), "rtc@%lx", rtc_base);
265 	fdt_begin_node(fdt, node_name);
266 #define	RTC_COMPAT	"arm,pl031\0arm,primecell"
267 	fdt_property(fdt, "compatible", RTC_COMPAT, sizeof(RTC_COMPAT));
268 #undef RTC_COMPAT
269 	set_single_reg(fdt, rtc_base, rtc_size);
270 	fdt_property_u32(fdt, "interrupt-parent", gic_phandle);
271 	fdt_property_placeholder(fdt, "interrupts", 3 * sizeof(uint32_t),
272 	    &interrupts);
273 	SET_PROP_U32(interrupts, 0, GIC_SPI);
274 	SET_PROP_U32(interrupts, 1, intr - GIC_FIRST_SPI);
275 	SET_PROP_U32(interrupts, 2, IRQ_TYPE_LEVEL_HIGH);
276 	fdt_property_placeholder(fdt, "clocks", sizeof(uint32_t), &prop);
277 	SET_PROP_U32(prop, 0, apb_pclk_phandle);
278 	fdt_property_string(fdt, "clock-names", "apb_pclk");
279 
280 	fdt_end_node(fdt);
281 }
282 
283 void
fdt_add_timer(void)284 fdt_add_timer(void)
285 {
286 	void *fdt, *interrupts;
287 	uint32_t irqs[] = { 13, 14, 11 };
288 
289 	assert(gic_phandle != 0);
290 
291 	fdt = fdtroot;
292 
293 	fdt_begin_node(fdt, "timer");
294 	fdt_property_string(fdt, "compatible", "arm,armv8-timer");
295 	fdt_property_u32(fdt, "interrupt-parent", gic_phandle);
296 	fdt_property_placeholder(fdt, "interrupts", 9 * sizeof(uint32_t),
297 	    &interrupts);
298 	for (u_int i = 0; i < nitems(irqs); i++) {
299 		SET_PROP_U32(interrupts, i * 3 + 0, GIC_PPI);
300 		SET_PROP_U32(interrupts, i * 3 + 1, irqs[i]);
301 		SET_PROP_U32(interrupts, i * 3 + 2, IRQ_TYPE_LEVEL_LOW);
302 	}
303 	fdt_end_node(fdt);
304 }
305 
306 void
fdt_add_pcie(int intrs[static4])307 fdt_add_pcie(int intrs[static 4])
308 {
309 	void *fdt, *prop;
310 	int slot, pin, intr, i;
311 
312 	assert(gic_phandle != 0);
313 
314 	fdt = fdtroot;
315 
316 	fdt_begin_node(fdt, "pcie@1f0000000");
317 	fdt_property_string(fdt, "compatible", "pci-host-ecam-generic");
318 	fdt_property_u32(fdt, "#address-cells", 3);
319 	fdt_property_u32(fdt, "#size-cells", 2);
320 	fdt_property_string(fdt, "device_type", "pci");
321 	fdt_property_u64(fdt, "bus-range", (0ul << 32) | 1);
322 	set_single_reg(fdt, 0xe0000000, 0x10000000);
323 	fdt_property_placeholder(fdt, "ranges",
324 	    2 * 7 * sizeof(uint32_t), &prop);
325 	SET_PROP_U32(prop, 0, 0x01000000);
326 
327 	SET_PROP_U32(prop, 1, 0);
328 	SET_PROP_U32(prop, 2, 0xdf000000);
329 
330 	SET_PROP_U32(prop, 3, 0);
331 	SET_PROP_U32(prop, 4, 0xdf000000);
332 
333 	SET_PROP_U32(prop, 5, 0);
334 	SET_PROP_U32(prop, 6, 0x01000000);
335 
336 	SET_PROP_U32(prop, 7, 0x02000000);
337 
338 	SET_PROP_U32(prop, 8, 0);
339 	SET_PROP_U32(prop, 9, 0xa0000000);
340 
341 	SET_PROP_U32(prop, 10, 0);
342 	SET_PROP_U32(prop, 11, 0xa0000000);
343 
344 	SET_PROP_U32(prop, 12, 0);
345 	SET_PROP_U32(prop, 13, 0x3f000000);
346 
347 	fdt_property_placeholder(fdt, "msi-map", 4 * sizeof(uint32_t), &prop);
348 	SET_PROP_U32(prop, 0, 0);		/* RID base */
349 	SET_PROP_U32(prop, 1, gic_phandle);	/* MSI parent */
350 	SET_PROP_U32(prop, 2, 0);		/* MSI base */
351 	SET_PROP_U32(prop, 3, 0x10000);		/* RID length */
352 	fdt_property_u32(fdt, "msi-parent", gic_phandle);
353 
354 	fdt_property_u32(fdt, "#interrupt-cells", 1);
355 	fdt_property_u32(fdt, "interrupt-parent", gic_phandle);
356 
357 	/*
358 	 * Describe standard swizzled interrupts routing (pins rotated by one
359 	 * for each consecutive slot). Must match pci_irq_route().
360 	 */
361 	fdt_property_placeholder(fdt, "interrupt-map-mask",
362 	    4 * sizeof(uint32_t), &prop);
363 	SET_PROP_U32(prop, 0, 3 << 11);
364 	SET_PROP_U32(prop, 1, 0);
365 	SET_PROP_U32(prop, 2, 0);
366 	SET_PROP_U32(prop, 3, 7);
367 	fdt_property_placeholder(fdt, "interrupt-map",
368 	    160 * sizeof(uint32_t), &prop);
369 	for (i = 0; i < 16; ++i) {
370 		pin = i % 4;
371 		slot = i / 4;
372 		intr = intrs[(pin + slot) % 4];
373 		assert(intr >= GIC_FIRST_SPI);
374 		SET_PROP_U32(prop, 10 * i + 0, slot << 11);
375 		SET_PROP_U32(prop, 10 * i + 1, 0);
376 		SET_PROP_U32(prop, 10 * i + 2, 0);
377 		SET_PROP_U32(prop, 10 * i + 3, pin + 1);
378 		SET_PROP_U32(prop, 10 * i + 4, gic_phandle);
379 		SET_PROP_U32(prop, 10 * i + 5, 0);
380 		SET_PROP_U32(prop, 10 * i + 6, 0);
381 		SET_PROP_U32(prop, 10 * i + 7, GIC_SPI);
382 		SET_PROP_U32(prop, 10 * i + 8, intr - GIC_FIRST_SPI);
383 		SET_PROP_U32(prop, 10 * i + 9, IRQ_TYPE_LEVEL_HIGH);
384 	}
385 
386 	fdt_end_node(fdt);
387 }
388 
389 void
fdt_finalize(void)390 fdt_finalize(void)
391 {
392 	fdt_end_node(fdtroot);
393 
394 	fdt_finish(fdtroot);
395 }
396