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
bhyve_init_config(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
bhyve_usage(int code)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
bhyve_optparse(int argc,char ** argv)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
bhyve_init_vcpu(struct vcpu * vcpu __unused)183 bhyve_init_vcpu(struct vcpu *vcpu __unused)
184 {
185 }
186
187 void
bhyve_start_vcpu(struct vcpu * vcpu,bool bsp __unused)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
load_bootrom(struct vmctx * ctx,const char * path,uint64_t * elrp,uint64_t * lenp)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
mmio_uart_intr_assert(void * arg)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
mmio_uart_intr_deassert(void * arg)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
mmio_uart_mem_handler(struct vcpu * vcpu __unused,int dir,uint64_t addr,int size __unused,uint64_t * val,void * arg1,long arg2)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
init_mmio_uart(struct vmctx * ctx)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
bhyve_init_platform(struct vmctx * ctx,struct vcpu * bsp)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 char isa[32];
312 int retval;
313
314 bootrom = get_config_value("bootrom");
315 if (bootrom == NULL) {
316 warnx("no bootrom specified");
317 return (ENOENT);
318 }
319 load_bootrom(ctx, bootrom, &elr, &len);
320 error = vm_set_register(bsp, VM_REG_GUEST_SEPC, elr);
321 if (error != 0) {
322 warn("vm_set_register(GUEST_SEPC)");
323 return (error);
324 }
325
326 error = vm_get_capability(bsp, VM_CAP_SSTC, &retval);
327 assert(error == 0);
328 snprintf(isa, sizeof(isa), "%s%s", "rv64imafdc",
329 retval == 1 ? "_sstc" : "");
330
331 fdt_gpa = vm_get_highmem_base(ctx) + roundup2(len, FDT_DTB_ALIGN);
332 error = fdt_init(ctx, guest_ncpus, fdt_gpa, FDT_SIZE, isa);
333 if (error != 0)
334 return (error);
335
336 /* Set FDT base address to the bootable hart. */
337 error = vm_set_register(bsp, VM_REG_GUEST_A1, fdt_gpa);
338 assert(error == 0);
339
340 fdt_add_aplic(APLIC_MEM_BASE, APLIC_MEM_SIZE, guest_ncpus);
341 error = vm_attach_aplic(ctx, APLIC_MEM_BASE, APLIC_MEM_SIZE);
342 if (error != 0) {
343 warn("vm_attach_aplic()");
344 return (error);
345 }
346
347 if (init_mmio_uart(ctx))
348 fdt_add_uart(UART_MMIO_BASE, UART_MMIO_SIZE, UART_INTR);
349
350 pci_irq_init(pcie_intrs);
351 fdt_add_pcie(pcie_intrs);
352 vmexit_set_bsp(vcpu_id(bsp));
353
354 return (0);
355 }
356
357 int
bhyve_init_platform_late(struct vmctx * ctx __unused,struct vcpu * bsp __unused)358 bhyve_init_platform_late(struct vmctx *ctx __unused, struct vcpu *bsp __unused)
359 {
360
361 fdt_finalize();
362
363 return (0);
364 }
365