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