xref: /src/usr.sbin/bhyve/riscv/bhyverun_machdep.c (revision 1876de606eb876b7a97beccfe6fcc89e60a72a25)
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