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 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 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 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 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 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 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 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 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 330 fdt_finalize(void) 331 { 332 fdt_end_node(fdtroot); 333 334 fdt_finish(fdtroot); 335 336 free(intc_phandles); 337 } 338