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: monitor mode\n" 105 " -m: memory size\n" 106 " -o: set config 'var' to 'value'\n" 107 " -p: pin 'vcpu' to 'hostcpu'\n" 108 " -S: guest memory cannot be swapped\n" 109 " -s: <slot,driver,configinfo> PCI slot config\n" 110 " -U: UUID\n" 111 " -W: force virtio to use single-vector MSI\n", 112 progname, (int)strlen(progname), "", (int)strlen(progname), "", 113 (int)strlen(progname), ""); 114 exit(code); 115 } 116 117 void 118 bhyve_optparse(int argc, char **argv) 119 { 120 const char *optstr; 121 int c; 122 123 optstr = "hCDMSWk:f:o:p:c:s:m:U:"; 124 while ((c = getopt(argc, argv, optstr)) != -1) { 125 switch (c) { 126 case 'c': 127 if (bhyve_topology_parse(optarg) != 0) { 128 errx(EX_USAGE, "invalid cpu topology '%s'", 129 optarg); 130 } 131 break; 132 case 'C': 133 set_config_bool("memory.guest_in_core", true); 134 break; 135 case 'D': 136 set_config_bool("destroy_on_poweroff", true); 137 break; 138 case 'k': 139 bhyve_parse_simple_config_file(optarg); 140 break; 141 case 'M': 142 set_config_bool("monitor", true); 143 break; 144 case 'm': 145 set_config_value("memory.size", optarg); 146 break; 147 case 'o': 148 if (!bhyve_parse_config_option(optarg)) { 149 errx(EX_USAGE, 150 "invalid configuration option '%s'", 151 optarg); 152 } 153 break; 154 case 'p': 155 if (bhyve_pincpu_parse(optarg) != 0) { 156 errx(EX_USAGE, 157 "invalid vcpu pinning configuration '%s'", 158 optarg); 159 } 160 break; 161 case 's': 162 if (strncmp(optarg, "help", strlen(optarg)) == 0) { 163 pci_print_supported_devices(); 164 exit(0); 165 } else if (pci_parse_slot(optarg) != 0) 166 exit(BHYVE_EXIT_ERROR); 167 else 168 break; 169 case 'S': 170 set_config_bool("memory.wired", true); 171 break; 172 case 'U': 173 set_config_value("uuid", optarg); 174 break; 175 case 'W': 176 set_config_bool("virtio_msix", false); 177 break; 178 case 'h': 179 bhyve_usage(0); 180 default: 181 bhyve_usage(1); 182 } 183 } 184 } 185 186 void 187 bhyve_init_vcpu(struct vcpu *vcpu __unused) 188 { 189 } 190 191 void 192 bhyve_start_vcpu(struct vcpu *vcpu, bool bsp __unused) 193 { 194 int error; 195 196 /* Set hart ID. */ 197 error = vm_set_register(vcpu, VM_REG_GUEST_A0, vcpu_id(vcpu)); 198 assert(error == 0); 199 200 fbsdrun_addcpu(vcpu_id(vcpu)); 201 } 202 203 /* 204 * Load the specified boot code at the beginning of high memory. 205 */ 206 static void 207 load_bootrom(struct vmctx *ctx, const char *path, uint64_t *elrp, 208 uint64_t *lenp) 209 { 210 struct stat sb; 211 void *data, *gptr; 212 vm_paddr_t loadaddr; 213 off_t size; 214 int fd; 215 216 fd = open(path, O_RDONLY); 217 if (fd < 0) 218 err(1, "open(%s)", path); 219 if (fstat(fd, &sb) != 0) 220 err(1, "fstat(%s)", path); 221 222 size = sb.st_size; 223 224 loadaddr = vm_get_highmem_base(ctx); 225 gptr = vm_map_gpa(ctx, loadaddr, round_page(size)); 226 227 data = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); 228 if (data == MAP_FAILED) 229 err(1, "mmap(%s)", path); 230 (void)close(fd); 231 memcpy(gptr, data, size); 232 233 if (munmap(data, size) != 0) 234 err(1, "munmap(%s)", path); 235 236 *elrp = loadaddr; 237 *lenp = size; 238 } 239 240 static void 241 mmio_uart_intr_assert(void *arg) 242 { 243 struct vmctx *ctx = arg; 244 245 vm_assert_irq(ctx, UART_INTR); 246 } 247 248 static void 249 mmio_uart_intr_deassert(void *arg) 250 { 251 struct vmctx *ctx = arg; 252 253 vm_deassert_irq(ctx, UART_INTR); 254 } 255 256 static int 257 mmio_uart_mem_handler(struct vcpu *vcpu __unused, int dir, uint64_t addr, 258 int size __unused, uint64_t *val, void *arg1, long arg2) 259 { 260 struct uart_ns16550_softc *sc = arg1; 261 long reg; 262 263 reg = addr - arg2; 264 if (dir == MEM_F_WRITE) 265 uart_ns16550_write(sc, reg, *val); 266 else 267 *val = uart_ns16550_read(sc, reg); 268 269 return (0); 270 } 271 272 static bool 273 init_mmio_uart(struct vmctx *ctx) 274 { 275 struct uart_ns16550_softc *sc; 276 struct mem_range mr; 277 const char *path; 278 int error; 279 280 path = get_config_value("console"); 281 if (path == NULL) 282 return (false); 283 284 sc = uart_ns16550_init(mmio_uart_intr_assert, mmio_uart_intr_deassert, 285 ctx); 286 if (uart_ns16550_tty_open(sc, path) != 0) { 287 EPRINTLN("Unable to initialize backend '%s' for mmio uart", 288 path); 289 assert(0); 290 } 291 292 bzero(&mr, sizeof(struct mem_range)); 293 mr.name = "uart"; 294 mr.base = UART_MMIO_BASE; 295 mr.size = UART_MMIO_SIZE; 296 mr.flags = MEM_F_RW; 297 mr.handler = mmio_uart_mem_handler; 298 mr.arg1 = sc; 299 mr.arg2 = mr.base; 300 error = register_mem(&mr); 301 assert(error == 0); 302 303 return (true); 304 } 305 306 int 307 bhyve_init_platform(struct vmctx *ctx, struct vcpu *bsp) 308 { 309 const char *bootrom; 310 uint64_t elr; 311 uint64_t len; 312 int error; 313 int pcie_intrs[4] = {PCIE_INTA, PCIE_INTB, PCIE_INTC, PCIE_INTD}; 314 vm_paddr_t fdt_gpa; 315 char isa[32]; 316 int retval; 317 318 bootrom = get_config_value("bootrom"); 319 if (bootrom == NULL) { 320 warnx("no bootrom specified"); 321 return (ENOENT); 322 } 323 load_bootrom(ctx, bootrom, &elr, &len); 324 error = vm_set_register(bsp, VM_REG_GUEST_SEPC, elr); 325 if (error != 0) { 326 warn("vm_set_register(GUEST_SEPC)"); 327 return (error); 328 } 329 330 error = vm_get_capability(bsp, VM_CAP_SSTC, &retval); 331 assert(error == 0); 332 snprintf(isa, sizeof(isa), "%s%s", "rv64imafdc", 333 retval == 1 ? "_sstc" : ""); 334 335 fdt_gpa = vm_get_highmem_base(ctx) + roundup2(len, FDT_DTB_ALIGN); 336 error = fdt_init(ctx, guest_ncpus, fdt_gpa, FDT_SIZE, isa); 337 if (error != 0) 338 return (error); 339 340 /* Set FDT base address to the bootable hart. */ 341 error = vm_set_register(bsp, VM_REG_GUEST_A1, fdt_gpa); 342 assert(error == 0); 343 344 fdt_add_aplic(APLIC_MEM_BASE, APLIC_MEM_SIZE, guest_ncpus); 345 error = vm_attach_aplic(ctx, APLIC_MEM_BASE, APLIC_MEM_SIZE); 346 if (error != 0) { 347 warn("vm_attach_aplic()"); 348 return (error); 349 } 350 351 if (init_mmio_uart(ctx)) 352 fdt_add_uart(UART_MMIO_BASE, UART_MMIO_SIZE, UART_INTR); 353 354 pci_irq_init(pcie_intrs); 355 fdt_add_pcie(pcie_intrs); 356 vmexit_set_bsp(vcpu_id(bsp)); 357 358 return (0); 359 } 360 361 int 362 bhyve_init_platform_late(struct vmctx *ctx __unused, struct vcpu *bsp __unused) 363 { 364 365 fdt_finalize(); 366 367 return (0); 368 } 369