1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2022 The FreeBSD Foundation
5 * Copyright (c) 2024 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 "config.h"
48 #include "bhyverun.h"
49 #include "fdt.h"
50
51 #define SET_PROP_U32(prop, idx, val) \
52 ((uint32_t *)(prop))[(idx)] = cpu_to_fdt32(val)
53 #define SET_PROP_U64(prop, idx, val) \
54 ((uint64_t *)(prop))[(idx)] = cpu_to_fdt64(val)
55
56 #define IRQ_TYPE_LEVEL_HIGH 4
57 #define IRQ_TYPE_LEVEL_LOW 8
58
59 static void *fdtroot;
60 static uint32_t aplic_phandle = 0;
61 static uint32_t intc0_phandle = 0;
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), ®);
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,const char * isa)87 add_cpu(void *fdt, int cpuid, const char *isa)
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", "riscv");
96 fdt_property_u32(fdt, "reg", cpuid);
97 fdt_property_string(fdt, "riscv,isa", isa);
98 fdt_property_string(fdt, "mmu-type", "riscv,sv39");
99 fdt_property_string(fdt, "clock-frequency", "1000000000");
100
101 fdt_begin_node(fdt, "interrupt-controller");
102 intc0_phandle = assign_phandle(fdt);
103 fdt_property_u32(fdt, "#address-cells", 2);
104 fdt_property_u32(fdt, "#interrupt-cells", 1);
105 fdt_property(fdt, "interrupt-controller", NULL, 0);
106 fdt_property_string(fdt, "compatible", "riscv,cpu-intc");
107 fdt_end_node(fdt);
108
109 fdt_end_node(fdt);
110 }
111
112 static void
add_cpus(void * fdt,int ncpu,const char * isa)113 add_cpus(void *fdt, int ncpu, const char *isa)
114 {
115 int cpuid;
116
117 fdt_begin_node(fdt, "cpus");
118 /* XXX: Needed given the root #address-cells? */
119 fdt_property_u32(fdt, "#address-cells", 1);
120 fdt_property_u32(fdt, "#size-cells", 0);
121 /* TODO: take timebase from kernel? */
122 fdt_property_u32(fdt, "timebase-frequency", 1000000);
123
124 for (cpuid = 0; cpuid < ncpu; cpuid++)
125 add_cpu(fdt, cpuid, isa);
126
127 fdt_end_node(fdt);
128 }
129
130 int
fdt_init(struct vmctx * ctx,int ncpu,vm_paddr_t fdtaddr,vm_size_t fdtsize,const char * isa)131 fdt_init(struct vmctx *ctx, int ncpu, vm_paddr_t fdtaddr, vm_size_t fdtsize,
132 const char *isa)
133 {
134 void *fdt;
135 const char *bootargs;
136
137 fdt = paddr_guest2host(ctx, fdtaddr, fdtsize);
138 if (fdt == NULL)
139 return (EFAULT);
140
141 fdt_create(fdt, (int)fdtsize);
142
143 /* Add the memory reserve map (needed even if none is reserved) */
144 fdt_finish_reservemap(fdt);
145
146 /* Create the root node */
147 fdt_begin_node(fdt, "");
148
149 fdt_property_string(fdt, "compatible", "freebsd,bhyve");
150 fdt_property_u32(fdt, "#address-cells", 2);
151 fdt_property_u32(fdt, "#size-cells", 2);
152
153 fdt_begin_node(fdt, "chosen");
154 fdt_property_string(fdt, "stdout-path", "serial0:115200n8");
155 bootargs = get_config_value("fdt.bootargs");
156 if (bootargs != NULL)
157 fdt_property_string(fdt, "bootargs", bootargs);
158 fdt_end_node(fdt);
159
160 fdt_begin_node(fdt, "memory");
161 fdt_property_string(fdt, "device_type", "memory");
162 /* There is no lowmem on riscv. */
163 assert(vm_get_lowmem_size(ctx) == 0);
164 set_single_reg(fdt, vm_get_highmem_base(ctx), vm_get_highmem_size(ctx));
165 fdt_end_node(fdt);
166
167 add_cpus(fdt, ncpu, isa);
168
169 /* Finalized by fdt_finalized(). */
170 fdtroot = fdt;
171
172 return (0);
173 }
174
175 void
fdt_add_aplic(uint64_t mem_base,uint64_t mem_size)176 fdt_add_aplic(uint64_t mem_base, uint64_t mem_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)mem_base);
185 fdt_begin_node(fdt, node_name);
186
187 aplic_phandle = assign_phandle(fdt);
188 fdt_property_string(fdt, "compatible", "riscv,aplic");
189 fdt_property(fdt, "interrupt-controller", NULL, 0);
190 #if notyet
191 fdt_property(fdt, "msi-controller", NULL, 0);
192 #endif
193 /* XXX: Needed given the root #address-cells? */
194 fdt_property_u32(fdt, "#address-cells", 2);
195 fdt_property_u32(fdt, "#interrupt-cells", 2);
196 fdt_property_placeholder(fdt, "reg", 2 * sizeof(uint64_t), &prop);
197 SET_PROP_U64(prop, 0, mem_base);
198 SET_PROP_U64(prop, 1, mem_size);
199
200 fdt_property_placeholder(fdt, "interrupts-extended",
201 2 * sizeof(uint32_t), &prop);
202 SET_PROP_U32(prop, 0, intc0_phandle);
203 SET_PROP_U32(prop, 1, 9);
204 fdt_property_u32(fdt, "riscv,num-sources", 63);
205
206 fdt_end_node(fdt);
207
208 fdt_property_u32(fdt, "interrupt-parent", aplic_phandle);
209 }
210
211 void
fdt_add_uart(uint64_t uart_base,uint64_t uart_size,int intr)212 fdt_add_uart(uint64_t uart_base, uint64_t uart_size, int intr)
213 {
214 void *fdt, *interrupts;
215 char node_name[32];
216
217 assert(aplic_phandle != 0);
218
219 fdt = fdtroot;
220
221 snprintf(node_name, sizeof(node_name), "serial@%lx", uart_base);
222 fdt_begin_node(fdt, node_name);
223 fdt_property_string(fdt, "compatible", "ns16550");
224 set_single_reg(fdt, uart_base, uart_size);
225 fdt_property_u32(fdt, "interrupt-parent", aplic_phandle);
226 fdt_property_placeholder(fdt, "interrupts", 2 * sizeof(uint32_t),
227 &interrupts);
228 SET_PROP_U32(interrupts, 0, intr);
229 SET_PROP_U32(interrupts, 1, IRQ_TYPE_LEVEL_HIGH);
230
231 fdt_end_node(fdt);
232
233 snprintf(node_name, sizeof(node_name), "/serial@%lx", uart_base);
234 fdt_begin_node(fdt, "aliases");
235 fdt_property_string(fdt, "serial0", node_name);
236 fdt_end_node(fdt);
237 }
238
239 void
fdt_add_pcie(int intrs[static4])240 fdt_add_pcie(int intrs[static 4])
241 {
242 void *fdt, *prop;
243 int slot, pin, intr, i;
244
245 assert(aplic_phandle != 0);
246
247 fdt = fdtroot;
248
249 fdt_begin_node(fdt, "pcie@1f0000000");
250 fdt_property_string(fdt, "compatible", "pci-host-ecam-generic");
251 fdt_property_u32(fdt, "#address-cells", 3);
252 fdt_property_u32(fdt, "#size-cells", 2);
253 fdt_property_string(fdt, "device_type", "pci");
254 fdt_property_u64(fdt, "bus-range", (0ul << 32) | 1);
255 set_single_reg(fdt, 0xe0000000, 0x10000000);
256 fdt_property_placeholder(fdt, "ranges",
257 2 * 7 * sizeof(uint32_t), &prop);
258 SET_PROP_U32(prop, 0, 0x01000000);
259
260 SET_PROP_U32(prop, 1, 0);
261 SET_PROP_U32(prop, 2, 0xdf000000);
262
263 SET_PROP_U32(prop, 3, 0);
264 SET_PROP_U32(prop, 4, 0xdf000000);
265
266 SET_PROP_U32(prop, 5, 0);
267 SET_PROP_U32(prop, 6, 0x01000000);
268
269 SET_PROP_U32(prop, 7, 0x02000000);
270
271 SET_PROP_U32(prop, 8, 0);
272 SET_PROP_U32(prop, 9, 0xa0000000);
273
274 SET_PROP_U32(prop, 10, 0);
275 SET_PROP_U32(prop, 11, 0xa0000000);
276
277 SET_PROP_U32(prop, 12, 0);
278 SET_PROP_U32(prop, 13, 0x3f000000);
279
280 #if notyet
281 fdt_property_placeholder(fdt, "msi-map", 4 * sizeof(uint32_t), &prop);
282 SET_PROP_U32(prop, 0, 0); /* RID base */
283 SET_PROP_U32(prop, 1, aplic_phandle); /* MSI parent */
284 SET_PROP_U32(prop, 2, 0); /* MSI base */
285 SET_PROP_U32(prop, 3, 0x10000); /* RID length */
286 fdt_property_u32(fdt, "msi-parent", aplic_phandle);
287 #endif
288
289 fdt_property_u32(fdt, "#interrupt-cells", 1);
290 fdt_property_u32(fdt, "interrupt-parent", aplic_phandle);
291
292 /*
293 * Describe standard swizzled interrupts routing (pins rotated by one
294 * for each consecutive slot). Must match pci_irq_route().
295 */
296 fdt_property_placeholder(fdt, "interrupt-map-mask",
297 4 * sizeof(uint32_t), &prop);
298 SET_PROP_U32(prop, 0, 3 << 11);
299 SET_PROP_U32(prop, 1, 0);
300 SET_PROP_U32(prop, 2, 0);
301 SET_PROP_U32(prop, 3, 7);
302 fdt_property_placeholder(fdt, "interrupt-map",
303 16 * 9 * sizeof(uint32_t), &prop);
304 for (i = 0; i < 16; ++i) {
305 pin = i % 4;
306 slot = i / 4;
307 intr = intrs[(pin + slot) % 4];
308 SET_PROP_U32(prop, 10 * i + 0, slot << 11);
309 SET_PROP_U32(prop, 10 * i + 1, 0);
310 SET_PROP_U32(prop, 10 * i + 2, 0);
311 SET_PROP_U32(prop, 10 * i + 3, pin + 1);
312 SET_PROP_U32(prop, 10 * i + 4, aplic_phandle);
313 SET_PROP_U32(prop, 10 * i + 5, 0);
314 SET_PROP_U32(prop, 10 * i + 6, 0);
315 SET_PROP_U32(prop, 10 * i + 7, intr);
316 SET_PROP_U32(prop, 10 * i + 8, IRQ_TYPE_LEVEL_HIGH);
317 }
318
319 fdt_end_node(fdt);
320 }
321
322 void
fdt_finalize(void)323 fdt_finalize(void)
324 {
325 fdt_end_node(fdtroot);
326
327 fdt_finish(fdtroot);
328 }
329