xref: /freebsd/usr.sbin/bhyve/aarch64/bhyverun_machdep.c (revision 6580f5c38dd5b01aeeaed16b370f1a12423437f0)
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