xref: /freebsd/usr.sbin/bhyve/riscv/bhyverun_machdep.c (revision 7ac65902d8ba0a85e5fe95d097f7fbd52cbda12a)
17ab1a32cSRuslan Bukin /*-
27ab1a32cSRuslan Bukin  * SPDX-License-Identifier: BSD-2-Clause
37ab1a32cSRuslan Bukin  *
47ab1a32cSRuslan Bukin  * Copyright (c) 2011 NetApp, Inc.
57ab1a32cSRuslan Bukin  * All rights reserved.
67ab1a32cSRuslan Bukin  * Copyright (c) 2024 Ruslan Bukin <br@bsdpad.com>
77ab1a32cSRuslan Bukin  *
87ab1a32cSRuslan Bukin  * This software was developed by the University of Cambridge Computer
97ab1a32cSRuslan Bukin  * Laboratory (Department of Computer Science and Technology) under Innovate
107ab1a32cSRuslan Bukin  * UK project 105694, "Digital Security by Design (DSbD) Technology Platform
117ab1a32cSRuslan Bukin  * Prototype".
127ab1a32cSRuslan Bukin  *
137ab1a32cSRuslan Bukin  * Redistribution and use in source and binary forms, with or without
147ab1a32cSRuslan Bukin  * modification, are permitted provided that the following conditions
157ab1a32cSRuslan Bukin  * are met:
167ab1a32cSRuslan Bukin  * 1. Redistributions of source code must retain the above copyright
177ab1a32cSRuslan Bukin  *    notice, this list of conditions and the following disclaimer.
187ab1a32cSRuslan Bukin  * 2. Redistributions in binary form must reproduce the above copyright
197ab1a32cSRuslan Bukin  *    notice, this list of conditions and the following disclaimer in the
207ab1a32cSRuslan Bukin  *    documentation and/or other materials provided with the distribution.
217ab1a32cSRuslan Bukin  *
227ab1a32cSRuslan Bukin  * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
237ab1a32cSRuslan Bukin  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
247ab1a32cSRuslan Bukin  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
257ab1a32cSRuslan Bukin  * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
267ab1a32cSRuslan Bukin  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
277ab1a32cSRuslan Bukin  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
287ab1a32cSRuslan Bukin  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
297ab1a32cSRuslan Bukin  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
307ab1a32cSRuslan Bukin  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
317ab1a32cSRuslan Bukin  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
327ab1a32cSRuslan Bukin  * SUCH DAMAGE.
337ab1a32cSRuslan Bukin  */
347ab1a32cSRuslan Bukin 
357ab1a32cSRuslan Bukin #include <sys/param.h>
367ab1a32cSRuslan Bukin #include <sys/mman.h>
377ab1a32cSRuslan Bukin #include <sys/stat.h>
387ab1a32cSRuslan Bukin 
397ab1a32cSRuslan Bukin #include <assert.h>
407ab1a32cSRuslan Bukin #include <err.h>
417ab1a32cSRuslan Bukin #include <errno.h>
427ab1a32cSRuslan Bukin #include <fcntl.h>
437ab1a32cSRuslan Bukin #include <stdlib.h>
447ab1a32cSRuslan Bukin #include <string.h>
457ab1a32cSRuslan Bukin #include <sysexits.h>
467ab1a32cSRuslan Bukin #include <unistd.h>
477ab1a32cSRuslan Bukin 
487ab1a32cSRuslan Bukin #include <vmmapi.h>
497ab1a32cSRuslan Bukin 
507ab1a32cSRuslan Bukin #include "bhyverun.h"
517ab1a32cSRuslan Bukin #include "config.h"
527ab1a32cSRuslan Bukin #include "debug.h"
537ab1a32cSRuslan Bukin #include "fdt.h"
547ab1a32cSRuslan Bukin #include "mem.h"
557ab1a32cSRuslan Bukin #include "pci_emul.h"
567ab1a32cSRuslan Bukin #include "pci_irq.h"
577ab1a32cSRuslan Bukin #include "uart_emul.h"
587ab1a32cSRuslan Bukin #include "riscv.h"
597ab1a32cSRuslan Bukin 
607ab1a32cSRuslan Bukin #define	FDT_SIZE	(64 * 1024)
617ab1a32cSRuslan Bukin #define	FDT_DTB_ALIGN	8
627ab1a32cSRuslan Bukin 
637ab1a32cSRuslan Bukin /* Start of lowmem + 64K */
647ab1a32cSRuslan Bukin #define	UART_MMIO_BASE	0x10000
657ab1a32cSRuslan Bukin #define	UART_MMIO_SIZE	0x1000
667ab1a32cSRuslan Bukin #define	UART_INTR	1
677ab1a32cSRuslan Bukin 
687ab1a32cSRuslan Bukin #define	APLIC_MEM_BASE		0x2f000000
697ab1a32cSRuslan Bukin #define	APLIC_MEM_SIZE		0x10000
707ab1a32cSRuslan Bukin 
717ab1a32cSRuslan Bukin #define	PCIE_INTA	2
727ab1a32cSRuslan Bukin #define	PCIE_INTB	3
737ab1a32cSRuslan Bukin #define	PCIE_INTC	4
747ab1a32cSRuslan Bukin #define	PCIE_INTD	5
757ab1a32cSRuslan Bukin 
767ab1a32cSRuslan Bukin void
bhyve_init_config(void)777ab1a32cSRuslan Bukin bhyve_init_config(void)
787ab1a32cSRuslan Bukin {
797ab1a32cSRuslan Bukin 	init_config();
807ab1a32cSRuslan Bukin 
817ab1a32cSRuslan Bukin 	/* Set default values prior to option parsing. */
827ab1a32cSRuslan Bukin 	set_config_bool("acpi_tables", false);
837ab1a32cSRuslan Bukin 	set_config_bool("acpi_tables_in_memory", false);
847ab1a32cSRuslan Bukin 	set_config_value("memory.size", "256M");
857ab1a32cSRuslan Bukin }
867ab1a32cSRuslan Bukin 
877ab1a32cSRuslan Bukin void
bhyve_usage(int code)887ab1a32cSRuslan Bukin bhyve_usage(int code)
897ab1a32cSRuslan Bukin {
907ab1a32cSRuslan Bukin 	const char *progname;
917ab1a32cSRuslan Bukin 
927ab1a32cSRuslan Bukin 	progname = getprogname();
937ab1a32cSRuslan Bukin 
947ab1a32cSRuslan Bukin 	fprintf(stderr,
957ab1a32cSRuslan Bukin 	    "Usage: %s [-CDHhSW]\n"
967ab1a32cSRuslan Bukin 	    "       %*s [-c [[cpus=]numcpus][,sockets=n][,cores=n][,threads=n]]\n"
977ab1a32cSRuslan Bukin 	    "       %*s [-k config_file] [-m mem] [-o var=value]\n"
987ab1a32cSRuslan Bukin 	    "       %*s [-p vcpu:hostcpu] [-r file] [-s pci] [-U uuid] vmname\n"
997ab1a32cSRuslan Bukin 	    "       -C: include guest memory in core file\n"
1007ab1a32cSRuslan Bukin 	    "       -c: number of CPUs and/or topology specification\n"
1017ab1a32cSRuslan Bukin 	    "       -D: destroy on power-off\n"
1027ab1a32cSRuslan Bukin 	    "       -h: help\n"
1037ab1a32cSRuslan Bukin 	    "       -k: key=value flat config file\n"
1047ab1a32cSRuslan Bukin 	    "       -m: memory size\n"
1057ab1a32cSRuslan Bukin 	    "       -o: set config 'var' to 'value'\n"
1067ab1a32cSRuslan Bukin 	    "       -p: pin 'vcpu' to 'hostcpu'\n"
1077ab1a32cSRuslan Bukin 	    "       -S: guest memory cannot be swapped\n"
1087ab1a32cSRuslan Bukin 	    "       -s: <slot,driver,configinfo> PCI slot config\n"
1097ab1a32cSRuslan Bukin 	    "       -U: UUID\n"
1107ab1a32cSRuslan Bukin 	    "       -W: force virtio to use single-vector MSI\n",
1117ab1a32cSRuslan Bukin 	    progname, (int)strlen(progname), "", (int)strlen(progname), "",
1127ab1a32cSRuslan Bukin 	    (int)strlen(progname), "");
1137ab1a32cSRuslan Bukin 	exit(code);
1147ab1a32cSRuslan Bukin }
1157ab1a32cSRuslan Bukin 
1167ab1a32cSRuslan Bukin void
bhyve_optparse(int argc,char ** argv)1177ab1a32cSRuslan Bukin bhyve_optparse(int argc, char **argv)
1187ab1a32cSRuslan Bukin {
1197ab1a32cSRuslan Bukin 	const char *optstr;
1207ab1a32cSRuslan Bukin 	int c;
1217ab1a32cSRuslan Bukin 
1227ab1a32cSRuslan Bukin 	optstr = "hCDSWk:f:o:p:c:s:m:U:";
1237ab1a32cSRuslan Bukin 	while ((c = getopt(argc, argv, optstr)) != -1) {
1247ab1a32cSRuslan Bukin 		switch (c) {
1257ab1a32cSRuslan Bukin 		case 'c':
1267ab1a32cSRuslan Bukin 			if (bhyve_topology_parse(optarg) != 0) {
1277ab1a32cSRuslan Bukin 				errx(EX_USAGE, "invalid cpu topology '%s'",
1287ab1a32cSRuslan Bukin 				    optarg);
1297ab1a32cSRuslan Bukin 			}
1307ab1a32cSRuslan Bukin 			break;
1317ab1a32cSRuslan Bukin 		case 'C':
1327ab1a32cSRuslan Bukin 			set_config_bool("memory.guest_in_core", true);
1337ab1a32cSRuslan Bukin 			break;
1347ab1a32cSRuslan Bukin 		case 'D':
1357ab1a32cSRuslan Bukin 			set_config_bool("destroy_on_poweroff", true);
1367ab1a32cSRuslan Bukin 			break;
1377ab1a32cSRuslan Bukin 		case 'k':
1387ab1a32cSRuslan Bukin 			bhyve_parse_simple_config_file(optarg);
1397ab1a32cSRuslan Bukin 			break;
1407ab1a32cSRuslan Bukin 		case 'm':
1417ab1a32cSRuslan Bukin 			set_config_value("memory.size", optarg);
1427ab1a32cSRuslan Bukin 			break;
1437ab1a32cSRuslan Bukin 		case 'o':
1447ab1a32cSRuslan Bukin 			if (!bhyve_parse_config_option(optarg)) {
1457ab1a32cSRuslan Bukin 				errx(EX_USAGE,
1467ab1a32cSRuslan Bukin 				    "invalid configuration option '%s'",
1477ab1a32cSRuslan Bukin 				    optarg);
1487ab1a32cSRuslan Bukin 			}
1497ab1a32cSRuslan Bukin 			break;
1507ab1a32cSRuslan Bukin 		case 'p':
1517ab1a32cSRuslan Bukin 			if (bhyve_pincpu_parse(optarg) != 0) {
1527ab1a32cSRuslan Bukin 				errx(EX_USAGE,
1537ab1a32cSRuslan Bukin 				    "invalid vcpu pinning configuration '%s'",
1547ab1a32cSRuslan Bukin 				    optarg);
1557ab1a32cSRuslan Bukin 			}
1567ab1a32cSRuslan Bukin 			break;
1577ab1a32cSRuslan Bukin 		case 's':
1587ab1a32cSRuslan Bukin 			if (strncmp(optarg, "help", strlen(optarg)) == 0) {
1597ab1a32cSRuslan Bukin 				pci_print_supported_devices();
1607ab1a32cSRuslan Bukin 				exit(0);
1617ab1a32cSRuslan Bukin 			} else if (pci_parse_slot(optarg) != 0)
1627ab1a32cSRuslan Bukin 				exit(4);
1637ab1a32cSRuslan Bukin 			else
1647ab1a32cSRuslan Bukin 				break;
1657ab1a32cSRuslan Bukin 		case 'S':
1667ab1a32cSRuslan Bukin 			set_config_bool("memory.wired", true);
1677ab1a32cSRuslan Bukin 			break;
1687ab1a32cSRuslan Bukin 		case 'U':
1697ab1a32cSRuslan Bukin 			set_config_value("uuid", optarg);
1707ab1a32cSRuslan Bukin 			break;
1717ab1a32cSRuslan Bukin 		case 'W':
1727ab1a32cSRuslan Bukin 			set_config_bool("virtio_msix", false);
1737ab1a32cSRuslan Bukin 			break;
1747ab1a32cSRuslan Bukin 		case 'h':
1757ab1a32cSRuslan Bukin 			bhyve_usage(0);
1767ab1a32cSRuslan Bukin 		default:
1777ab1a32cSRuslan Bukin 			bhyve_usage(1);
1787ab1a32cSRuslan Bukin 		}
1797ab1a32cSRuslan Bukin 	}
1807ab1a32cSRuslan Bukin }
1817ab1a32cSRuslan Bukin 
1827ab1a32cSRuslan Bukin void
bhyve_init_vcpu(struct vcpu * vcpu __unused)1837ab1a32cSRuslan Bukin bhyve_init_vcpu(struct vcpu *vcpu __unused)
1847ab1a32cSRuslan Bukin {
1857ab1a32cSRuslan Bukin }
1867ab1a32cSRuslan Bukin 
1877ab1a32cSRuslan Bukin void
bhyve_start_vcpu(struct vcpu * vcpu,bool bsp __unused)1887ab1a32cSRuslan Bukin bhyve_start_vcpu(struct vcpu *vcpu, bool bsp __unused)
1897ab1a32cSRuslan Bukin {
1907ab1a32cSRuslan Bukin 	int error;
1917ab1a32cSRuslan Bukin 
1927ab1a32cSRuslan Bukin 	/* Set hart ID. */
1937ab1a32cSRuslan Bukin 	error = vm_set_register(vcpu, VM_REG_GUEST_A0, vcpu_id(vcpu));
1947ab1a32cSRuslan Bukin 	assert(error == 0);
1957ab1a32cSRuslan Bukin 
1967ab1a32cSRuslan Bukin 	fbsdrun_addcpu(vcpu_id(vcpu));
1977ab1a32cSRuslan Bukin }
1987ab1a32cSRuslan Bukin 
1997ab1a32cSRuslan Bukin /*
2007ab1a32cSRuslan Bukin  * Load the specified boot code at the beginning of high memory.
2017ab1a32cSRuslan Bukin  */
2027ab1a32cSRuslan Bukin static void
load_bootrom(struct vmctx * ctx,const char * path,uint64_t * elrp,uint64_t * lenp)2037ab1a32cSRuslan Bukin load_bootrom(struct vmctx *ctx, const char *path, uint64_t *elrp,
2047ab1a32cSRuslan Bukin     uint64_t *lenp)
2057ab1a32cSRuslan Bukin {
2067ab1a32cSRuslan Bukin 	struct stat sb;
2077ab1a32cSRuslan Bukin 	void *data, *gptr;
2087ab1a32cSRuslan Bukin 	vm_paddr_t loadaddr;
2097ab1a32cSRuslan Bukin 	off_t size;
2107ab1a32cSRuslan Bukin 	int fd;
2117ab1a32cSRuslan Bukin 
2127ab1a32cSRuslan Bukin 	fd = open(path, O_RDONLY);
2137ab1a32cSRuslan Bukin 	if (fd < 0)
2147ab1a32cSRuslan Bukin 		err(1, "open(%s)", path);
2157ab1a32cSRuslan Bukin 	if (fstat(fd, &sb) != 0)
2167ab1a32cSRuslan Bukin 		err(1, "fstat(%s)", path);
2177ab1a32cSRuslan Bukin 
2187ab1a32cSRuslan Bukin 	size = sb.st_size;
2197ab1a32cSRuslan Bukin 
2207ab1a32cSRuslan Bukin 	loadaddr = vm_get_highmem_base(ctx);
2217ab1a32cSRuslan Bukin 	gptr = vm_map_gpa(ctx, loadaddr, round_page(size));
2227ab1a32cSRuslan Bukin 
2237ab1a32cSRuslan Bukin 	data = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
2247ab1a32cSRuslan Bukin 	if (data == MAP_FAILED)
2257ab1a32cSRuslan Bukin 		err(1, "mmap(%s)", path);
2267ab1a32cSRuslan Bukin 	(void)close(fd);
2277ab1a32cSRuslan Bukin 	memcpy(gptr, data, size);
2287ab1a32cSRuslan Bukin 
2297ab1a32cSRuslan Bukin 	if (munmap(data, size) != 0)
2307ab1a32cSRuslan Bukin 		err(1, "munmap(%s)", path);
2317ab1a32cSRuslan Bukin 
2327ab1a32cSRuslan Bukin 	*elrp = loadaddr;
2337ab1a32cSRuslan Bukin 	*lenp = size;
2347ab1a32cSRuslan Bukin }
2357ab1a32cSRuslan Bukin 
2367ab1a32cSRuslan Bukin static void
mmio_uart_intr_assert(void * arg)2377ab1a32cSRuslan Bukin mmio_uart_intr_assert(void *arg)
2387ab1a32cSRuslan Bukin {
2397ab1a32cSRuslan Bukin 	struct vmctx *ctx = arg;
2407ab1a32cSRuslan Bukin 
2417ab1a32cSRuslan Bukin 	vm_assert_irq(ctx, UART_INTR);
2427ab1a32cSRuslan Bukin }
2437ab1a32cSRuslan Bukin 
2447ab1a32cSRuslan Bukin static void
mmio_uart_intr_deassert(void * arg)2457ab1a32cSRuslan Bukin mmio_uart_intr_deassert(void *arg)
2467ab1a32cSRuslan Bukin {
2477ab1a32cSRuslan Bukin 	struct vmctx *ctx = arg;
2487ab1a32cSRuslan Bukin 
2497ab1a32cSRuslan Bukin 	vm_deassert_irq(ctx, UART_INTR);
2507ab1a32cSRuslan Bukin }
2517ab1a32cSRuslan Bukin 
2527ab1a32cSRuslan Bukin 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)2537ab1a32cSRuslan Bukin mmio_uart_mem_handler(struct vcpu *vcpu __unused, int dir, uint64_t addr,
2547ab1a32cSRuslan Bukin     int size __unused, uint64_t *val, void *arg1, long arg2)
2557ab1a32cSRuslan Bukin {
2567ab1a32cSRuslan Bukin 	struct uart_ns16550_softc *sc = arg1;
2577ab1a32cSRuslan Bukin 	long reg;
2587ab1a32cSRuslan Bukin 
2597ab1a32cSRuslan Bukin 	reg = addr - arg2;
2607ab1a32cSRuslan Bukin 	if (dir == MEM_F_WRITE)
2617ab1a32cSRuslan Bukin 		uart_ns16550_write(sc, reg, *val);
2627ab1a32cSRuslan Bukin 	else
2637ab1a32cSRuslan Bukin 		*val = uart_ns16550_read(sc, reg);
2647ab1a32cSRuslan Bukin 
2657ab1a32cSRuslan Bukin 	return (0);
2667ab1a32cSRuslan Bukin }
2677ab1a32cSRuslan Bukin 
2687ab1a32cSRuslan Bukin static bool
init_mmio_uart(struct vmctx * ctx)2697ab1a32cSRuslan Bukin init_mmio_uart(struct vmctx *ctx)
2707ab1a32cSRuslan Bukin {
2717ab1a32cSRuslan Bukin 	struct uart_ns16550_softc *sc;
2727ab1a32cSRuslan Bukin 	struct mem_range mr;
2737ab1a32cSRuslan Bukin 	const char *path;
2747ab1a32cSRuslan Bukin 	int error;
2757ab1a32cSRuslan Bukin 
2767ab1a32cSRuslan Bukin 	path = get_config_value("console");
2777ab1a32cSRuslan Bukin 	if (path == NULL)
2787ab1a32cSRuslan Bukin 		return (false);
2797ab1a32cSRuslan Bukin 
2807ab1a32cSRuslan Bukin 	sc = uart_ns16550_init(mmio_uart_intr_assert, mmio_uart_intr_deassert,
2817ab1a32cSRuslan Bukin 	    ctx);
2827ab1a32cSRuslan Bukin 	if (uart_ns16550_tty_open(sc, path) != 0) {
2837ab1a32cSRuslan Bukin 		EPRINTLN("Unable to initialize backend '%s' for mmio uart",
2847ab1a32cSRuslan Bukin 		    path);
2857ab1a32cSRuslan Bukin 		assert(0);
2867ab1a32cSRuslan Bukin 	}
2877ab1a32cSRuslan Bukin 
2887ab1a32cSRuslan Bukin 	bzero(&mr, sizeof(struct mem_range));
2897ab1a32cSRuslan Bukin 	mr.name = "uart";
2907ab1a32cSRuslan Bukin 	mr.base = UART_MMIO_BASE;
2917ab1a32cSRuslan Bukin 	mr.size = UART_MMIO_SIZE;
2927ab1a32cSRuslan Bukin 	mr.flags = MEM_F_RW;
2937ab1a32cSRuslan Bukin 	mr.handler = mmio_uart_mem_handler;
2947ab1a32cSRuslan Bukin 	mr.arg1 = sc;
2957ab1a32cSRuslan Bukin 	mr.arg2 = mr.base;
2967ab1a32cSRuslan Bukin 	error = register_mem(&mr);
2977ab1a32cSRuslan Bukin 	assert(error == 0);
2987ab1a32cSRuslan Bukin 
2997ab1a32cSRuslan Bukin 	return (true);
3007ab1a32cSRuslan Bukin }
3017ab1a32cSRuslan Bukin 
3027ab1a32cSRuslan Bukin int
bhyve_init_platform(struct vmctx * ctx,struct vcpu * bsp)3037ab1a32cSRuslan Bukin bhyve_init_platform(struct vmctx *ctx, struct vcpu *bsp)
3047ab1a32cSRuslan Bukin {
3057ab1a32cSRuslan Bukin 	const char *bootrom;
3067ab1a32cSRuslan Bukin 	uint64_t elr;
3077ab1a32cSRuslan Bukin 	uint64_t len;
3087ab1a32cSRuslan Bukin 	int error;
3097ab1a32cSRuslan Bukin 	int pcie_intrs[4] = {PCIE_INTA, PCIE_INTB, PCIE_INTC, PCIE_INTD};
3107ab1a32cSRuslan Bukin 	vm_paddr_t fdt_gpa;
311a7bf553dSRuslan Bukin 	char isa[32];
312a7bf553dSRuslan Bukin 	int retval;
3137ab1a32cSRuslan Bukin 
3147ab1a32cSRuslan Bukin 	bootrom = get_config_value("bootrom");
3157ab1a32cSRuslan Bukin 	if (bootrom == NULL) {
3167ab1a32cSRuslan Bukin 		warnx("no bootrom specified");
3177ab1a32cSRuslan Bukin 		return (ENOENT);
3187ab1a32cSRuslan Bukin 	}
3197ab1a32cSRuslan Bukin 	load_bootrom(ctx, bootrom, &elr, &len);
3207ab1a32cSRuslan Bukin 	error = vm_set_register(bsp, VM_REG_GUEST_SEPC, elr);
3217ab1a32cSRuslan Bukin 	if (error != 0) {
3227ab1a32cSRuslan Bukin 		warn("vm_set_register(GUEST_SEPC)");
3237ab1a32cSRuslan Bukin 		return (error);
3247ab1a32cSRuslan Bukin 	}
3257ab1a32cSRuslan Bukin 
326a7bf553dSRuslan Bukin 	error = vm_get_capability(bsp, VM_CAP_SSTC, &retval);
327a7bf553dSRuslan Bukin 	assert(error == 0);
328a7bf553dSRuslan Bukin 	snprintf(isa, sizeof(isa), "%s%s", "rv64imafdc",
329a7bf553dSRuslan Bukin 	    retval == 1 ? "_sstc" : "");
330a7bf553dSRuslan Bukin 
3317ab1a32cSRuslan Bukin 	fdt_gpa = vm_get_highmem_base(ctx) + roundup2(len, FDT_DTB_ALIGN);
332a7bf553dSRuslan Bukin 	error = fdt_init(ctx, guest_ncpus, fdt_gpa, FDT_SIZE, isa);
3337ab1a32cSRuslan Bukin 	if (error != 0)
3347ab1a32cSRuslan Bukin 		return (error);
3357ab1a32cSRuslan Bukin 
3367ab1a32cSRuslan Bukin 	/* Set FDT base address to the bootable hart. */
3377ab1a32cSRuslan Bukin 	error = vm_set_register(bsp, VM_REG_GUEST_A1, fdt_gpa);
3387ab1a32cSRuslan Bukin 	assert(error == 0);
3397ab1a32cSRuslan Bukin 
340*7ac65902SRuslan Bukin 	fdt_add_aplic(APLIC_MEM_BASE, APLIC_MEM_SIZE, guest_ncpus);
3417ab1a32cSRuslan Bukin 	error = vm_attach_aplic(ctx, APLIC_MEM_BASE, APLIC_MEM_SIZE);
3427ab1a32cSRuslan Bukin 	if (error != 0) {
3437ab1a32cSRuslan Bukin 		warn("vm_attach_aplic()");
3447ab1a32cSRuslan Bukin 		return (error);
3457ab1a32cSRuslan Bukin 	}
3467ab1a32cSRuslan Bukin 
3477ab1a32cSRuslan Bukin 	if (init_mmio_uart(ctx))
3487ab1a32cSRuslan Bukin 		fdt_add_uart(UART_MMIO_BASE, UART_MMIO_SIZE, UART_INTR);
3497ab1a32cSRuslan Bukin 
3507ab1a32cSRuslan Bukin 	pci_irq_init(pcie_intrs);
3517ab1a32cSRuslan Bukin 	fdt_add_pcie(pcie_intrs);
3527ab1a32cSRuslan Bukin 	vmexit_set_bsp(vcpu_id(bsp));
3537ab1a32cSRuslan Bukin 
3547ab1a32cSRuslan Bukin 	return (0);
3557ab1a32cSRuslan Bukin }
3567ab1a32cSRuslan Bukin 
3577ab1a32cSRuslan Bukin int
bhyve_init_platform_late(struct vmctx * ctx __unused,struct vcpu * bsp __unused)3587ab1a32cSRuslan Bukin bhyve_init_platform_late(struct vmctx *ctx __unused, struct vcpu *bsp __unused)
3597ab1a32cSRuslan Bukin {
3607ab1a32cSRuslan Bukin 
3617ab1a32cSRuslan Bukin 	fdt_finalize();
3627ab1a32cSRuslan Bukin 
3637ab1a32cSRuslan Bukin 	return (0);
3647ab1a32cSRuslan Bukin }
365