xref: /freebsd/usr.sbin/bhyve/riscv/bhyverun_machdep.c (revision b64c5a0ace59af62eff52bfe110a521dc73c937b)
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: 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 	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
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
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
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
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
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
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 
312 	bootrom = get_config_value("bootrom");
313 	if (bootrom == NULL) {
314 		warnx("no bootrom specified");
315 		return (ENOENT);
316 	}
317 	load_bootrom(ctx, bootrom, &elr, &len);
318 	error = vm_set_register(bsp, VM_REG_GUEST_SEPC, elr);
319 	if (error != 0) {
320 		warn("vm_set_register(GUEST_SEPC)");
321 		return (error);
322 	}
323 
324 	fdt_gpa = vm_get_highmem_base(ctx) + roundup2(len, FDT_DTB_ALIGN);
325 	error = fdt_init(ctx, guest_ncpus, fdt_gpa, FDT_SIZE);
326 	if (error != 0)
327 		return (error);
328 
329 	/* Set FDT base address to the bootable hart. */
330 	error = vm_set_register(bsp, VM_REG_GUEST_A1, fdt_gpa);
331 	assert(error == 0);
332 
333 	fdt_add_aplic(APLIC_MEM_BASE, APLIC_MEM_SIZE);
334 	error = vm_attach_aplic(ctx, APLIC_MEM_BASE, APLIC_MEM_SIZE);
335 	if (error != 0) {
336 		warn("vm_attach_aplic()");
337 		return (error);
338 	}
339 
340 	if (init_mmio_uart(ctx))
341 		fdt_add_uart(UART_MMIO_BASE, UART_MMIO_SIZE, UART_INTR);
342 
343 	pci_irq_init(pcie_intrs);
344 	fdt_add_pcie(pcie_intrs);
345 	vmexit_set_bsp(vcpu_id(bsp));
346 
347 	return (0);
348 }
349 
350 int
351 bhyve_init_platform_late(struct vmctx *ctx __unused, struct vcpu *bsp __unused)
352 {
353 
354 	fdt_finalize();
355 
356 	return (0);
357 }
358