1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2011 NetApp, Inc. 5 * All rights reserved. 6 * Copyright (c) 2024 Ruslan Bukin <br@bsdpad.com> 7 * 8 * This software was developed by the University of Cambridge Computer 9 * Laboratory (Department of Computer Science and Technology) under Innovate 10 * UK project 105694, "Digital Security by Design (DSbD) Technology Platform 11 * Prototype". 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 1. Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 2. Redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution. 21 * 22 * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #include <sys/param.h> 36 #include <sys/mman.h> 37 #include <sys/stat.h> 38 39 #include <assert.h> 40 #include <err.h> 41 #include <errno.h> 42 #include <fcntl.h> 43 #include <stdlib.h> 44 #include <string.h> 45 #include <sysexits.h> 46 #include <unistd.h> 47 48 #include <vmmapi.h> 49 50 #include "bhyverun.h" 51 #include "config.h" 52 #include "debug.h" 53 #include "fdt.h" 54 #include "mem.h" 55 #include "pci_emul.h" 56 #include "pci_irq.h" 57 #include "uart_emul.h" 58 #include "riscv.h" 59 60 #define FDT_SIZE (64 * 1024) 61 #define FDT_DTB_ALIGN 8 62 63 /* Start of lowmem + 64K */ 64 #define UART_MMIO_BASE 0x10000 65 #define UART_MMIO_SIZE 0x1000 66 #define UART_INTR 1 67 68 #define APLIC_MEM_BASE 0x2f000000 69 #define APLIC_MEM_SIZE 0x10000 70 71 #define PCIE_INTA 2 72 #define PCIE_INTB 3 73 #define PCIE_INTC 4 74 #define PCIE_INTD 5 75 76 void 77 bhyve_init_config(void) 78 { 79 init_config(); 80 81 /* Set default values prior to option parsing. */ 82 set_config_bool("acpi_tables", false); 83 set_config_bool("acpi_tables_in_memory", false); 84 set_config_value("memory.size", "256M"); 85 } 86 87 void 88 bhyve_usage(int code) 89 { 90 const char *progname; 91 92 progname = getprogname(); 93 94 fprintf(stderr, 95 "Usage: %s [-CDHhSW]\n" 96 " %*s [-c [[cpus=]numcpus][,sockets=n][,cores=n][,threads=n]]\n" 97 " %*s [-k config_file] [-m mem] [-o var=value]\n" 98 " %*s [-p vcpu:hostcpu] [-r file] [-s pci] [-U uuid] vmname\n" 99 " -C: include guest memory in core file\n" 100 " -c: number of CPUs and/or topology specification\n" 101 " -D: destroy on power-off\n" 102 " -h: help\n" 103 " -k: key=value flat config file\n" 104 " -m: memory size\n" 105 " -o: set config 'var' to 'value'\n" 106 " -p: pin 'vcpu' to 'hostcpu'\n" 107 " -S: guest memory cannot be swapped\n" 108 " -s: <slot,driver,configinfo> PCI slot config\n" 109 " -U: UUID\n" 110 " -W: force virtio to use single-vector MSI\n", 111 progname, (int)strlen(progname), "", (int)strlen(progname), "", 112 (int)strlen(progname), ""); 113 exit(code); 114 } 115 116 void 117 bhyve_optparse(int argc, char **argv) 118 { 119 const char *optstr; 120 int c; 121 122 optstr = "hCDSWk:f:o:p:c:s:m:U:"; 123 while ((c = getopt(argc, argv, optstr)) != -1) { 124 switch (c) { 125 case 'c': 126 if (bhyve_topology_parse(optarg) != 0) { 127 errx(EX_USAGE, "invalid cpu topology '%s'", 128 optarg); 129 } 130 break; 131 case 'C': 132 set_config_bool("memory.guest_in_core", true); 133 break; 134 case 'D': 135 set_config_bool("destroy_on_poweroff", true); 136 break; 137 case 'k': 138 bhyve_parse_simple_config_file(optarg); 139 break; 140 case 'm': 141 set_config_value("memory.size", optarg); 142 break; 143 case 'o': 144 if (!bhyve_parse_config_option(optarg)) { 145 errx(EX_USAGE, 146 "invalid configuration option '%s'", 147 optarg); 148 } 149 break; 150 case 'p': 151 if (bhyve_pincpu_parse(optarg) != 0) { 152 errx(EX_USAGE, 153 "invalid vcpu pinning configuration '%s'", 154 optarg); 155 } 156 break; 157 case 's': 158 if (strncmp(optarg, "help", strlen(optarg)) == 0) { 159 pci_print_supported_devices(); 160 exit(0); 161 } else if (pci_parse_slot(optarg) != 0) 162 exit(4); 163 else 164 break; 165 case 'S': 166 set_config_bool("memory.wired", true); 167 break; 168 case 'U': 169 set_config_value("uuid", optarg); 170 break; 171 case 'W': 172 set_config_bool("virtio_msix", false); 173 break; 174 case 'h': 175 bhyve_usage(0); 176 default: 177 bhyve_usage(1); 178 } 179 } 180 } 181 182 void 183 bhyve_init_vcpu(struct vcpu *vcpu __unused) 184 { 185 } 186 187 void 188 bhyve_start_vcpu(struct vcpu *vcpu, bool bsp __unused) 189 { 190 int error; 191 192 /* Set hart ID. */ 193 error = vm_set_register(vcpu, VM_REG_GUEST_A0, vcpu_id(vcpu)); 194 assert(error == 0); 195 196 fbsdrun_addcpu(vcpu_id(vcpu)); 197 } 198 199 /* 200 * Load the specified boot code at the beginning of high memory. 201 */ 202 static void 203 load_bootrom(struct vmctx *ctx, const char *path, uint64_t *elrp, 204 uint64_t *lenp) 205 { 206 struct stat sb; 207 void *data, *gptr; 208 vm_paddr_t loadaddr; 209 off_t size; 210 int fd; 211 212 fd = open(path, O_RDONLY); 213 if (fd < 0) 214 err(1, "open(%s)", path); 215 if (fstat(fd, &sb) != 0) 216 err(1, "fstat(%s)", path); 217 218 size = sb.st_size; 219 220 loadaddr = vm_get_highmem_base(ctx); 221 gptr = vm_map_gpa(ctx, loadaddr, round_page(size)); 222 223 data = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); 224 if (data == MAP_FAILED) 225 err(1, "mmap(%s)", path); 226 (void)close(fd); 227 memcpy(gptr, data, size); 228 229 if (munmap(data, size) != 0) 230 err(1, "munmap(%s)", path); 231 232 *elrp = loadaddr; 233 *lenp = size; 234 } 235 236 static void 237 mmio_uart_intr_assert(void *arg) 238 { 239 struct vmctx *ctx = arg; 240 241 vm_assert_irq(ctx, UART_INTR); 242 } 243 244 static void 245 mmio_uart_intr_deassert(void *arg) 246 { 247 struct vmctx *ctx = arg; 248 249 vm_deassert_irq(ctx, UART_INTR); 250 } 251 252 static int 253 mmio_uart_mem_handler(struct vcpu *vcpu __unused, int dir, uint64_t addr, 254 int size __unused, uint64_t *val, void *arg1, long arg2) 255 { 256 struct uart_ns16550_softc *sc = arg1; 257 long reg; 258 259 reg = addr - arg2; 260 if (dir == MEM_F_WRITE) 261 uart_ns16550_write(sc, reg, *val); 262 else 263 *val = uart_ns16550_read(sc, reg); 264 265 return (0); 266 } 267 268 static bool 269 init_mmio_uart(struct vmctx *ctx) 270 { 271 struct uart_ns16550_softc *sc; 272 struct mem_range mr; 273 const char *path; 274 int error; 275 276 path = get_config_value("console"); 277 if (path == NULL) 278 return (false); 279 280 sc = uart_ns16550_init(mmio_uart_intr_assert, mmio_uart_intr_deassert, 281 ctx); 282 if (uart_ns16550_tty_open(sc, path) != 0) { 283 EPRINTLN("Unable to initialize backend '%s' for mmio uart", 284 path); 285 assert(0); 286 } 287 288 bzero(&mr, sizeof(struct mem_range)); 289 mr.name = "uart"; 290 mr.base = UART_MMIO_BASE; 291 mr.size = UART_MMIO_SIZE; 292 mr.flags = MEM_F_RW; 293 mr.handler = mmio_uart_mem_handler; 294 mr.arg1 = sc; 295 mr.arg2 = mr.base; 296 error = register_mem(&mr); 297 assert(error == 0); 298 299 return (true); 300 } 301 302 int 303 bhyve_init_platform(struct vmctx *ctx, struct vcpu *bsp) 304 { 305 const char *bootrom; 306 uint64_t elr; 307 uint64_t len; 308 int error; 309 int pcie_intrs[4] = {PCIE_INTA, PCIE_INTB, PCIE_INTC, PCIE_INTD}; 310 vm_paddr_t fdt_gpa; 311 312 bootrom = get_config_value("bootrom"); 313 if (bootrom == NULL) { 314 warnx("no bootrom specified"); 315 return (ENOENT); 316 } 317 load_bootrom(ctx, bootrom, &elr, &len); 318 error = vm_set_register(bsp, VM_REG_GUEST_SEPC, elr); 319 if (error != 0) { 320 warn("vm_set_register(GUEST_SEPC)"); 321 return (error); 322 } 323 324 fdt_gpa = vm_get_highmem_base(ctx) + roundup2(len, FDT_DTB_ALIGN); 325 error = fdt_init(ctx, guest_ncpus, fdt_gpa, FDT_SIZE); 326 if (error != 0) 327 return (error); 328 329 /* Set FDT base address to the bootable hart. */ 330 error = vm_set_register(bsp, VM_REG_GUEST_A1, fdt_gpa); 331 assert(error == 0); 332 333 fdt_add_aplic(APLIC_MEM_BASE, APLIC_MEM_SIZE); 334 error = vm_attach_aplic(ctx, APLIC_MEM_BASE, APLIC_MEM_SIZE); 335 if (error != 0) { 336 warn("vm_attach_aplic()"); 337 return (error); 338 } 339 340 if (init_mmio_uart(ctx)) 341 fdt_add_uart(UART_MMIO_BASE, UART_MMIO_SIZE, UART_INTR); 342 343 pci_irq_init(pcie_intrs); 344 fdt_add_pcie(pcie_intrs); 345 vmexit_set_bsp(vcpu_id(bsp)); 346 347 return (0); 348 } 349 350 int 351 bhyve_init_platform_late(struct vmctx *ctx __unused, struct vcpu *bsp __unused) 352 { 353 354 fdt_finalize(); 355 356 return (0); 357 } 358