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 " -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 fbsdrun_addcpu(vcpu_id(vcpu)); 191 } 192 193 /* 194 * Load the specified boot code at the beginning of high memory. 195 */ 196 static void 197 load_bootrom(struct vmctx *ctx, const char *path, uint64_t *elrp) 198 { 199 struct stat sb; 200 void *data, *gptr; 201 vm_paddr_t loadaddr; 202 off_t size; 203 int fd; 204 205 fd = open(path, O_RDONLY); 206 if (fd < 0) 207 err(1, "open(%s)", path); 208 if (fstat(fd, &sb) != 0) 209 err(1, "fstat(%s)", path); 210 211 size = sb.st_size; 212 213 loadaddr = vm_get_highmem_base(ctx); 214 gptr = vm_map_gpa(ctx, loadaddr, round_page(size)); 215 216 data = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); 217 if (data == MAP_FAILED) 218 err(1, "mmap(%s)", path); 219 (void)close(fd); 220 memcpy(gptr, data, size); 221 222 if (munmap(data, size) != 0) 223 err(1, "munmap(%s)", path); 224 225 *elrp = loadaddr; 226 } 227 228 static void 229 mmio_uart_intr_assert(void *arg) 230 { 231 struct vmctx *ctx = arg; 232 233 vm_assert_irq(ctx, UART_INTR); 234 } 235 236 static void 237 mmio_uart_intr_deassert(void *arg) 238 { 239 struct vmctx *ctx = arg; 240 241 vm_deassert_irq(ctx, UART_INTR); 242 } 243 244 static int 245 mmio_uart_mem_handler(struct vcpu *vcpu __unused, int dir, 246 uint64_t addr, int size __unused, uint64_t *val, void *arg1, long arg2) 247 { 248 struct uart_pl011_softc *sc = arg1; 249 long reg; 250 251 reg = (addr - arg2) >> 2; 252 if (dir == MEM_F_WRITE) 253 uart_pl011_write(sc, reg, *val); 254 else 255 *val = uart_pl011_read(sc, reg); 256 257 return (0); 258 } 259 260 static bool 261 init_mmio_uart(struct vmctx *ctx) 262 { 263 struct uart_pl011_softc *sc; 264 struct mem_range mr; 265 const char *path; 266 int error; 267 268 path = get_config_value("console"); 269 if (path == NULL) 270 return (false); 271 272 sc = uart_pl011_init(mmio_uart_intr_assert, mmio_uart_intr_deassert, 273 ctx); 274 if (uart_pl011_tty_open(sc, path) != 0) { 275 EPRINTLN("Unable to initialize backend '%s' for mmio uart", 276 path); 277 assert(0); 278 } 279 280 bzero(&mr, sizeof(struct mem_range)); 281 mr.name = "uart"; 282 mr.base = UART_MMIO_BASE; 283 mr.size = UART_MMIO_SIZE; 284 mr.flags = MEM_F_RW; 285 mr.handler = mmio_uart_mem_handler; 286 mr.arg1 = sc; 287 mr.arg2 = mr.base; 288 error = register_mem(&mr); 289 assert(error == 0); 290 291 return (true); 292 } 293 294 static void 295 mmio_rtc_intr_assert(void *arg) 296 { 297 struct vmctx *ctx = arg; 298 299 vm_assert_irq(ctx, RTC_INTR); 300 } 301 302 static void 303 mmio_rtc_intr_deassert(void *arg) 304 { 305 struct vmctx *ctx = arg; 306 307 vm_deassert_irq(ctx, RTC_INTR); 308 } 309 310 static int 311 mmio_rtc_mem_handler(struct vcpu *vcpu __unused, int dir, 312 uint64_t addr, int size __unused, uint64_t *val, void *arg1, long arg2) 313 { 314 struct rtc_pl031_softc *sc = arg1; 315 long reg; 316 317 reg = addr - arg2; 318 if (dir == MEM_F_WRITE) 319 rtc_pl031_write(sc, reg, *val); 320 else 321 *val = rtc_pl031_read(sc, reg); 322 323 return (0); 324 } 325 326 static void 327 init_mmio_rtc(struct vmctx *ctx) 328 { 329 struct rtc_pl031_softc *sc; 330 struct mem_range mr; 331 int error; 332 333 sc = rtc_pl031_init(mmio_rtc_intr_assert, mmio_rtc_intr_deassert, 334 ctx); 335 336 bzero(&mr, sizeof(struct mem_range)); 337 mr.name = "rtc"; 338 mr.base = RTC_MMIO_BASE; 339 mr.size = RTC_MMIO_SIZE; 340 mr.flags = MEM_F_RW; 341 mr.handler = mmio_rtc_mem_handler; 342 mr.arg1 = sc; 343 mr.arg2 = mr.base; 344 error = register_mem(&mr); 345 assert(error == 0); 346 } 347 348 static vm_paddr_t 349 fdt_gpa(struct vmctx *ctx) 350 { 351 return (vm_get_highmem_base(ctx) + FDT_BASE); 352 } 353 354 int 355 bhyve_init_platform(struct vmctx *ctx, struct vcpu *bsp) 356 { 357 const char *bootrom; 358 uint64_t elr; 359 int error; 360 int pcie_intrs[4] = {PCIE_INTA, PCIE_INTB, PCIE_INTC, PCIE_INTD}; 361 362 bootrom = get_config_value("bootrom"); 363 if (bootrom == NULL) { 364 warnx("no bootrom specified"); 365 return (ENOENT); 366 } 367 load_bootrom(ctx, bootrom, &elr); 368 error = vm_set_register(bsp, VM_REG_GUEST_PC, elr); 369 if (error != 0) { 370 warn("vm_set_register(GUEST_PC)"); 371 return (error); 372 } 373 374 error = fdt_init(ctx, guest_ncpus, fdt_gpa(ctx), FDT_SIZE); 375 if (error != 0) 376 return (error); 377 378 fdt_add_gic(GIC_DIST_BASE, GIC_DIST_SIZE, GIC_REDIST_BASE, 379 GIC_REDIST_SIZE(guest_ncpus)); 380 error = vm_attach_vgic(ctx, GIC_DIST_BASE, GIC_DIST_SIZE, 381 GIC_REDIST_BASE, GIC_REDIST_SIZE(guest_ncpus)); 382 if (error != 0) { 383 warn("vm_attach_vgic()"); 384 return (error); 385 } 386 387 if (init_mmio_uart(ctx)) 388 fdt_add_uart(UART_MMIO_BASE, UART_MMIO_SIZE, UART_INTR); 389 init_mmio_rtc(ctx); 390 fdt_add_rtc(RTC_MMIO_BASE, RTC_MMIO_SIZE, RTC_INTR); 391 fdt_add_timer(); 392 pci_irq_init(pcie_intrs); 393 fdt_add_pcie(pcie_intrs); 394 395 return (0); 396 } 397 398 int 399 bhyve_init_platform_late(struct vmctx *ctx, struct vcpu *bsp __unused) 400 { 401 int error; 402 403 fdt_finalize(); 404 405 error = vm_set_register(bsp, VM_REG_GUEST_X0, fdt_gpa(ctx)); 406 assert(error == 0); 407 408 return (0); 409 } 410