1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2011 NetApp, Inc. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/param.h> 30 #include <sys/mman.h> 31 #include <sys/stat.h> 32 33 #include <assert.h> 34 #include <err.h> 35 #include <errno.h> 36 #include <fcntl.h> 37 #include <stdlib.h> 38 #include <string.h> 39 #include <sysexits.h> 40 #include <unistd.h> 41 42 #include <vmmapi.h> 43 44 #include "bhyverun.h" 45 #include "config.h" 46 #include "debug.h" 47 #include "fdt.h" 48 #include "mem.h" 49 #include "pci_emul.h" 50 #include "pci_irq.h" 51 #include "rtc_pl031.h" 52 #include "uart_emul.h" 53 54 /* Start of mem + 1M */ 55 #define FDT_BASE 0x100000 56 #define FDT_SIZE (64 * 1024) 57 58 /* Start of lowmem + 64K */ 59 #define UART_MMIO_BASE 0x10000 60 #define UART_MMIO_SIZE 0x1000 61 #define UART_INTR 32 62 #define RTC_MMIO_BASE 0x11000 63 #define RTC_MMIO_SIZE 0x1000 64 #define RTC_INTR 33 65 66 #define GIC_DIST_BASE 0x2f000000 67 #define GIC_DIST_SIZE 0x10000 68 #define GIC_REDIST_BASE 0x2f100000 69 #define GIC_REDIST_SIZE(ncpu) ((ncpu) * 2 * PAGE_SIZE_64K) 70 71 #define PCIE_INTA 34 72 #define PCIE_INTB 35 73 #define PCIE_INTC 36 74 #define PCIE_INTD 37 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 " -G: start a debug server\n" 103 " -h: help\n" 104 " -k: key=value flat config file\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 = "hCDSWk:f:o:p:G: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 'G': 139 bhyve_parse_gdb_options(optarg); 140 break; 141 case 'k': 142 bhyve_parse_simple_config_file(optarg); 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(4); 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 fbsdrun_addcpu(vcpu_id(vcpu)); 195 } 196 197 /* 198 * Load the specified boot code at the beginning of high memory. 199 */ 200 static void 201 load_bootrom(struct vmctx *ctx, const char *path, uint64_t *elrp) 202 { 203 struct stat sb; 204 void *data, *gptr; 205 vm_paddr_t loadaddr; 206 off_t size; 207 int fd; 208 209 fd = open(path, O_RDONLY); 210 if (fd < 0) 211 err(1, "open(%s)", path); 212 if (fstat(fd, &sb) != 0) 213 err(1, "fstat(%s)", path); 214 215 size = sb.st_size; 216 217 loadaddr = vm_get_highmem_base(ctx); 218 gptr = vm_map_gpa(ctx, loadaddr, round_page(size)); 219 220 data = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); 221 if (data == MAP_FAILED) 222 err(1, "mmap(%s)", path); 223 (void)close(fd); 224 memcpy(gptr, data, size); 225 226 if (munmap(data, size) != 0) 227 err(1, "munmap(%s)", path); 228 229 *elrp = loadaddr; 230 } 231 232 static void 233 mmio_uart_intr_assert(void *arg) 234 { 235 struct vmctx *ctx = arg; 236 237 vm_assert_irq(ctx, UART_INTR); 238 } 239 240 static void 241 mmio_uart_intr_deassert(void *arg) 242 { 243 struct vmctx *ctx = arg; 244 245 vm_deassert_irq(ctx, UART_INTR); 246 } 247 248 static int 249 mmio_uart_mem_handler(struct vcpu *vcpu __unused, int dir, 250 uint64_t addr, int size __unused, uint64_t *val, void *arg1, long arg2) 251 { 252 struct uart_pl011_softc *sc = arg1; 253 long reg; 254 255 reg = (addr - arg2) >> 2; 256 if (dir == MEM_F_WRITE) 257 uart_pl011_write(sc, reg, *val); 258 else 259 *val = uart_pl011_read(sc, reg); 260 261 return (0); 262 } 263 264 static bool 265 init_mmio_uart(struct vmctx *ctx) 266 { 267 struct uart_pl011_softc *sc; 268 struct mem_range mr; 269 const char *path; 270 int error; 271 272 path = get_config_value("console"); 273 if (path == NULL) 274 return (false); 275 276 sc = uart_pl011_init(mmio_uart_intr_assert, mmio_uart_intr_deassert, 277 ctx); 278 if (uart_pl011_tty_open(sc, path) != 0) { 279 EPRINTLN("Unable to initialize backend '%s' for mmio uart", 280 path); 281 assert(0); 282 } 283 284 bzero(&mr, sizeof(struct mem_range)); 285 mr.name = "uart"; 286 mr.base = UART_MMIO_BASE; 287 mr.size = UART_MMIO_SIZE; 288 mr.flags = MEM_F_RW; 289 mr.handler = mmio_uart_mem_handler; 290 mr.arg1 = sc; 291 mr.arg2 = mr.base; 292 error = register_mem(&mr); 293 assert(error == 0); 294 295 return (true); 296 } 297 298 static void 299 mmio_rtc_intr_assert(void *arg) 300 { 301 struct vmctx *ctx = arg; 302 303 vm_assert_irq(ctx, RTC_INTR); 304 } 305 306 static void 307 mmio_rtc_intr_deassert(void *arg) 308 { 309 struct vmctx *ctx = arg; 310 311 vm_deassert_irq(ctx, RTC_INTR); 312 } 313 314 static int 315 mmio_rtc_mem_handler(struct vcpu *vcpu __unused, int dir, 316 uint64_t addr, int size __unused, uint64_t *val, void *arg1, long arg2) 317 { 318 struct rtc_pl031_softc *sc = arg1; 319 long reg; 320 321 reg = addr - arg2; 322 if (dir == MEM_F_WRITE) 323 rtc_pl031_write(sc, reg, *val); 324 else 325 *val = rtc_pl031_read(sc, reg); 326 327 return (0); 328 } 329 330 static void 331 init_mmio_rtc(struct vmctx *ctx) 332 { 333 struct rtc_pl031_softc *sc; 334 struct mem_range mr; 335 int error; 336 337 sc = rtc_pl031_init(mmio_rtc_intr_assert, mmio_rtc_intr_deassert, 338 ctx); 339 340 bzero(&mr, sizeof(struct mem_range)); 341 mr.name = "rtc"; 342 mr.base = RTC_MMIO_BASE; 343 mr.size = RTC_MMIO_SIZE; 344 mr.flags = MEM_F_RW; 345 mr.handler = mmio_rtc_mem_handler; 346 mr.arg1 = sc; 347 mr.arg2 = mr.base; 348 error = register_mem(&mr); 349 assert(error == 0); 350 } 351 352 static vm_paddr_t 353 fdt_gpa(struct vmctx *ctx) 354 { 355 return (vm_get_highmem_base(ctx) + FDT_BASE); 356 } 357 358 int 359 bhyve_init_platform(struct vmctx *ctx, struct vcpu *bsp) 360 { 361 const char *bootrom; 362 uint64_t elr; 363 int error; 364 int pcie_intrs[4] = {PCIE_INTA, PCIE_INTB, PCIE_INTC, PCIE_INTD}; 365 366 bootrom = get_config_value("bootrom"); 367 if (bootrom == NULL) { 368 warnx("no bootrom specified"); 369 return (ENOENT); 370 } 371 load_bootrom(ctx, bootrom, &elr); 372 error = vm_set_register(bsp, VM_REG_GUEST_PC, elr); 373 if (error != 0) { 374 warn("vm_set_register(GUEST_PC)"); 375 return (error); 376 } 377 378 error = fdt_init(ctx, guest_ncpus, fdt_gpa(ctx), FDT_SIZE); 379 if (error != 0) 380 return (error); 381 382 fdt_add_gic(GIC_DIST_BASE, GIC_DIST_SIZE, GIC_REDIST_BASE, 383 GIC_REDIST_SIZE(guest_ncpus)); 384 error = vm_attach_vgic(ctx, GIC_DIST_BASE, GIC_DIST_SIZE, 385 GIC_REDIST_BASE, GIC_REDIST_SIZE(guest_ncpus)); 386 if (error != 0) { 387 warn("vm_attach_vgic()"); 388 return (error); 389 } 390 391 if (init_mmio_uart(ctx)) 392 fdt_add_uart(UART_MMIO_BASE, UART_MMIO_SIZE, UART_INTR); 393 init_mmio_rtc(ctx); 394 fdt_add_rtc(RTC_MMIO_BASE, RTC_MMIO_SIZE, RTC_INTR); 395 fdt_add_timer(); 396 pci_irq_init(pcie_intrs); 397 fdt_add_pcie(pcie_intrs); 398 399 return (0); 400 } 401 402 int 403 bhyve_init_platform_late(struct vmctx *ctx, struct vcpu *bsp __unused) 404 { 405 int error; 406 407 fdt_finalize(); 408 409 error = vm_set_register(bsp, VM_REG_GUEST_X0, fdt_gpa(ctx)); 410 assert(error == 0); 411 412 return (0); 413 } 414