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 76void 77bhyve_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 87void 88bhyve_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 116void 117bhyve_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 182void 183bhyve_init_vcpu(struct vcpu *vcpu __unused) 184{ 185} 186 187void 188bhyve_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 */ 196static void 197load_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 228static void 229mmio_uart_intr_assert(void *arg) 230{ 231 struct vmctx *ctx = arg; 232 233 vm_assert_irq(ctx, UART_INTR); 234} 235 236static void 237mmio_uart_intr_deassert(void *arg) 238{ 239 struct vmctx *ctx = arg; 240 241 vm_deassert_irq(ctx, UART_INTR); 242} 243 244static int 245mmio_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 260static bool 261init_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 294static void 295mmio_rtc_intr_assert(void *arg) 296{ 297 struct vmctx *ctx = arg; 298 299 vm_assert_irq(ctx, RTC_INTR); 300} 301 302static void 303mmio_rtc_intr_deassert(void *arg) 304{ 305 struct vmctx *ctx = arg; 306 307 vm_deassert_irq(ctx, RTC_INTR); 308} 309 310static int 311mmio_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 326static void 327init_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 348static vm_paddr_t 349fdt_gpa(struct vmctx *ctx) 350{ 351 return (vm_get_highmem_base(ctx) + FDT_BASE); 352} 353 354int 355bhyve_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 398int 399bhyve_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