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), ®);
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