aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRuslan Bukin <br@FreeBSD.org>2024-10-31 16:14:32 +0000
committerRuslan Bukin <br@FreeBSD.org>2024-10-31 20:24:12 +0000
commit7ab1a32cd43cbae61ad4dd435d6a482bbf61cb52 (patch)
treef81b95461b40f432ebb1c7f6b372ee488f34215c
parentd3916eace506b8ab23537223f5c92924636a1c41 (diff)
downloadsrc-7ab1a32cd43c.tar.gz
src-7ab1a32cd43c.zip
bhyve/riscv: Initial import.
Add machine-dependent parts for bhyve hypervisor to support virtualization on RISC-V ISA. No objection: markj Sponsored by: UK Research and Innovation Differential Revision: https://reviews.freebsd.org/D45512
-rw-r--r--lib/Makefile3
-rw-r--r--lib/libvmmapi/riscv/Makefile.inc1
-rw-r--r--lib/libvmmapi/riscv/vmmapi_machdep.c117
-rw-r--r--lib/libvmmapi/vmmapi.h9
-rw-r--r--usr.sbin/Makefile.riscv2
-rw-r--r--usr.sbin/bhyve/pci_emul.c2
-rw-r--r--usr.sbin/bhyve/pci_irq.h2
-rw-r--r--usr.sbin/bhyve/riscv/Makefile.inc7
-rw-r--r--usr.sbin/bhyve/riscv/bhyverun_machdep.c357
-rw-r--r--usr.sbin/bhyve/riscv/fdt.c326
-rw-r--r--usr.sbin/bhyve/riscv/fdt.h45
-rw-r--r--usr.sbin/bhyve/riscv/pci_irq.c66
-rw-r--r--usr.sbin/bhyve/riscv/pci_irq_machdep.h49
-rw-r--r--usr.sbin/bhyve/riscv/riscv.h41
-rw-r--r--usr.sbin/bhyve/riscv/vmexit.c366
-rw-r--r--usr.sbin/bhyvectl/riscv/Makefile.inc1
-rw-r--r--usr.sbin/bhyvectl/riscv/bhyvectl_machdep.c82
17 files changed, 1472 insertions, 4 deletions
diff --git a/lib/Makefile b/lib/Makefile
index fdfe198bea10..af0079978075 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -202,7 +202,8 @@ SUBDIR.${MK_PMC}+= libopencsd
SUBDIR.${MK_PMC}+= libipt
.endif
-.if ${MACHINE_CPUARCH} == "amd64" || ${MACHINE_CPUARCH} == "aarch64"
+.if ${MACHINE_CPUARCH} == "amd64" || ${MACHINE_CPUARCH} == "aarch64" || \
+ ${MACHINE_CPUARCH} == "riscv"
SUBDIR.${MK_BHYVE}+= libvmmapi
.endif
diff --git a/lib/libvmmapi/riscv/Makefile.inc b/lib/libvmmapi/riscv/Makefile.inc
new file mode 100644
index 000000000000..663ea0ab90a3
--- /dev/null
+++ b/lib/libvmmapi/riscv/Makefile.inc
@@ -0,0 +1 @@
+SRCS+= vmmapi_machdep.c
diff --git a/lib/libvmmapi/riscv/vmmapi_machdep.c b/lib/libvmmapi/riscv/vmmapi_machdep.c
new file mode 100644
index 000000000000..9c70185942c9
--- /dev/null
+++ b/lib/libvmmapi/riscv/vmmapi_machdep.c
@@ -0,0 +1,117 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2011 NetApp, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include <machine/vmm.h>
+#include <machine/vmm_dev.h>
+#include <machine/vmm_snapshot.h>
+
+#include <assert.h>
+#include <string.h>
+
+#include "vmmapi.h"
+#include "internal.h"
+
+const char *vm_capstrmap[] = {
+ [VM_CAP_MAX] = NULL,
+};
+
+#define VM_MD_IOCTLS \
+ VM_ATTACH_APLIC, \
+ VM_ASSERT_IRQ, \
+ VM_DEASSERT_IRQ, \
+ VM_RAISE_MSI
+
+const cap_ioctl_t vm_ioctl_cmds[] = {
+ VM_COMMON_IOCTLS,
+ VM_MD_IOCTLS,
+};
+size_t vm_ioctl_ncmds = nitems(vm_ioctl_cmds);
+
+int
+vm_attach_aplic(struct vmctx *ctx, uint64_t mem_start, size_t mem_size)
+{
+ struct vm_aplic_descr aplic;
+
+ bzero(&aplic, sizeof(aplic));
+ aplic.mem_start = mem_start;
+ aplic.mem_size = mem_size;
+
+ return (ioctl(ctx->fd, VM_ATTACH_APLIC, &aplic));
+}
+
+int
+vm_assert_irq(struct vmctx *ctx, uint32_t irq)
+{
+ struct vm_irq vi;
+
+ bzero(&vi, sizeof(vi));
+ vi.irq = irq;
+
+ return (ioctl(ctx->fd, VM_ASSERT_IRQ, &vi));
+}
+
+int
+vm_deassert_irq(struct vmctx *ctx, uint32_t irq)
+{
+ struct vm_irq vi;
+
+ bzero(&vi, sizeof(vi));
+ vi.irq = irq;
+
+ return (ioctl(ctx->fd, VM_DEASSERT_IRQ, &vi));
+}
+
+int
+vm_raise_msi(struct vmctx *ctx, uint64_t addr, uint64_t msg,
+ int bus, int slot, int func)
+{
+ struct vm_msi vmsi;
+
+ bzero(&vmsi, sizeof(vmsi));
+ vmsi.addr = addr;
+ vmsi.msg = msg;
+ vmsi.bus = bus;
+ vmsi.slot = slot;
+ vmsi.func = func;
+
+ return (ioctl(ctx->fd, VM_RAISE_MSI, &vmsi));
+}
+
+int
+vm_inject_exception(struct vcpu *vcpu, uint64_t scause)
+{
+ struct vm_exception vmexc;
+
+ bzero(&vmexc, sizeof(vmexc));
+ vmexc.scause = scause;
+
+ return (vcpu_ioctl(vcpu, VM_INJECT_EXCEPTION, &vmexc));
+}
diff --git a/lib/libvmmapi/vmmapi.h b/lib/libvmmapi/vmmapi.h
index d2a217c4d2e9..0ea1d5824271 100644
--- a/lib/libvmmapi/vmmapi.h
+++ b/lib/libvmmapi/vmmapi.h
@@ -161,12 +161,17 @@ int vm_suspend(struct vmctx *ctx, enum vm_suspend_how how);
int vm_reinit(struct vmctx *ctx);
int vm_raise_msi(struct vmctx *ctx, uint64_t addr, uint64_t msg,
int bus, int slot, int func);
-#ifdef __aarch64__
+#if defined(__aarch64__)
int vm_attach_vgic(struct vmctx *ctx, uint64_t dist_start, size_t dist_size,
uint64_t redist_start, size_t redist_size);
+int vm_inject_exception(struct vcpu *vcpu, uint64_t esr, uint64_t far);
+#elif defined(__riscv)
+int vm_attach_aplic(struct vmctx *ctx, uint64_t mem_start, size_t mem_size);
+int vm_inject_exception(struct vcpu *vcpu, uint64_t scause);
+#endif
+#if defined(__aarch64__) || defined(__riscv)
int vm_assert_irq(struct vmctx *ctx, uint32_t irq);
int vm_deassert_irq(struct vmctx *ctx, uint32_t irq);
-int vm_inject_exception(struct vcpu *vcpu, uint64_t esr, uint64_t far);
#endif
#ifdef __amd64__
int vm_apicid2vcpu(struct vmctx *ctx, int apicid);
diff --git a/usr.sbin/Makefile.riscv b/usr.sbin/Makefile.riscv
new file mode 100644
index 000000000000..a8897983059c
--- /dev/null
+++ b/usr.sbin/Makefile.riscv
@@ -0,0 +1,2 @@
+SUBDIR+= bhyve
+SUBDIR+= bhyvectl
diff --git a/usr.sbin/bhyve/pci_emul.c b/usr.sbin/bhyve/pci_emul.c
index b61f29aa6830..2f04a488d9c1 100644
--- a/usr.sbin/bhyve/pci_emul.c
+++ b/usr.sbin/bhyve/pci_emul.c
@@ -136,7 +136,7 @@ static TAILQ_HEAD(boot_list, boot_device) boot_devices = TAILQ_HEAD_INITIALIZER(
* change this address without changing it in OVMF.
*/
#define PCI_EMUL_MEMBASE32 0xc0000000
-#elif defined(__aarch64__)
+#elif defined(__aarch64__) || defined(__riscv)
#define PCI_EMUL_IOBASE 0xdf000000UL
#define PCI_EMUL_IOLIMIT 0xe0000000UL
#define PCI_EMUL_MEMBASE32 0xa0000000UL
diff --git a/usr.sbin/bhyve/pci_irq.h b/usr.sbin/bhyve/pci_irq.h
index 8b556ddc91a2..8078583add52 100644
--- a/usr.sbin/bhyve/pci_irq.h
+++ b/usr.sbin/bhyve/pci_irq.h
@@ -36,6 +36,8 @@ struct pci_devinst;
#include "amd64/pci_irq_machdep.h"
#elif defined(__aarch64__)
#include "aarch64/pci_irq_machdep.h"
+#elif defined(__riscv)
+#include "riscv/pci_irq_machdep.h"
#else
#error Unsupported platform
#endif
diff --git a/usr.sbin/bhyve/riscv/Makefile.inc b/usr.sbin/bhyve/riscv/Makefile.inc
new file mode 100644
index 000000000000..2b57201c6259
--- /dev/null
+++ b/usr.sbin/bhyve/riscv/Makefile.inc
@@ -0,0 +1,7 @@
+SRCS+= \
+ fdt.c
+
+.PATH: ${BHYVE_SYSDIR}/sys/riscv/vmm
+SRCS+= vmm_instruction_emul.c
+
+BHYVE_FDT_SUPPORT=
diff --git a/usr.sbin/bhyve/riscv/bhyverun_machdep.c b/usr.sbin/bhyve/riscv/bhyverun_machdep.c
new file mode 100644
index 000000000000..39d6a7cdf231
--- /dev/null
+++ b/usr.sbin/bhyve/riscv/bhyverun_machdep.c
@@ -0,0 +1,357 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2011 NetApp, Inc.
+ * All rights reserved.
+ * Copyright (c) 2024 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This software was developed by the University of Cambridge Computer
+ * Laboratory (Department of Computer Science and Technology) under Innovate
+ * UK project 105694, "Digital Security by Design (DSbD) Technology Platform
+ * Prototype".
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include <vmmapi.h>
+
+#include "bhyverun.h"
+#include "config.h"
+#include "debug.h"
+#include "fdt.h"
+#include "mem.h"
+#include "pci_emul.h"
+#include "pci_irq.h"
+#include "uart_emul.h"
+#include "riscv.h"
+
+#define FDT_SIZE (64 * 1024)
+#define FDT_DTB_ALIGN 8
+
+/* Start of lowmem + 64K */
+#define UART_MMIO_BASE 0x10000
+#define UART_MMIO_SIZE 0x1000
+#define UART_INTR 1
+
+#define APLIC_MEM_BASE 0x2f000000
+#define APLIC_MEM_SIZE 0x10000
+
+#define PCIE_INTA 2
+#define PCIE_INTB 3
+#define PCIE_INTC 4
+#define PCIE_INTD 5
+
+void
+bhyve_init_config(void)
+{
+ init_config();
+
+ /* Set default values prior to option parsing. */
+ set_config_bool("acpi_tables", false);
+ set_config_bool("acpi_tables_in_memory", false);
+ set_config_value("memory.size", "256M");
+}
+
+void
+bhyve_usage(int code)
+{
+ const char *progname;
+
+ progname = getprogname();
+
+ fprintf(stderr,
+ "Usage: %s [-CDHhSW]\n"
+ " %*s [-c [[cpus=]numcpus][,sockets=n][,cores=n][,threads=n]]\n"
+ " %*s [-k config_file] [-m mem] [-o var=value]\n"
+ " %*s [-p vcpu:hostcpu] [-r file] [-s pci] [-U uuid] vmname\n"
+ " -C: include guest memory in core file\n"
+ " -c: number of CPUs and/or topology specification\n"
+ " -D: destroy on power-off\n"
+ " -h: help\n"
+ " -k: key=value flat config file\n"
+ " -m: memory size\n"
+ " -o: set config 'var' to 'value'\n"
+ " -p: pin 'vcpu' to 'hostcpu'\n"
+ " -S: guest memory cannot be swapped\n"
+ " -s: <slot,driver,configinfo> PCI slot config\n"
+ " -U: UUID\n"
+ " -W: force virtio to use single-vector MSI\n",
+ progname, (int)strlen(progname), "", (int)strlen(progname), "",
+ (int)strlen(progname), "");
+ exit(code);
+}
+
+void
+bhyve_optparse(int argc, char **argv)
+{
+ const char *optstr;
+ int c;
+
+ optstr = "hCDSWk:f:o:p:c:s:m:U:";
+ while ((c = getopt(argc, argv, optstr)) != -1) {
+ switch (c) {
+ case 'c':
+ if (bhyve_topology_parse(optarg) != 0) {
+ errx(EX_USAGE, "invalid cpu topology '%s'",
+ optarg);
+ }
+ break;
+ case 'C':
+ set_config_bool("memory.guest_in_core", true);
+ break;
+ case 'D':
+ set_config_bool("destroy_on_poweroff", true);
+ break;
+ case 'k':
+ bhyve_parse_simple_config_file(optarg);
+ break;
+ case 'm':
+ set_config_value("memory.size", optarg);
+ break;
+ case 'o':
+ if (!bhyve_parse_config_option(optarg)) {
+ errx(EX_USAGE,
+ "invalid configuration option '%s'",
+ optarg);
+ }
+ break;
+ case 'p':
+ if (bhyve_pincpu_parse(optarg) != 0) {
+ errx(EX_USAGE,
+ "invalid vcpu pinning configuration '%s'",
+ optarg);
+ }
+ break;
+ case 's':
+ if (strncmp(optarg, "help", strlen(optarg)) == 0) {
+ pci_print_supported_devices();
+ exit(0);
+ } else if (pci_parse_slot(optarg) != 0)
+ exit(4);
+ else
+ break;
+ case 'S':
+ set_config_bool("memory.wired", true);
+ break;
+ case 'U':
+ set_config_value("uuid", optarg);
+ break;
+ case 'W':
+ set_config_bool("virtio_msix", false);
+ break;
+ case 'h':
+ bhyve_usage(0);
+ default:
+ bhyve_usage(1);
+ }
+ }
+}
+
+void
+bhyve_init_vcpu(struct vcpu *vcpu __unused)
+{
+}
+
+void
+bhyve_start_vcpu(struct vcpu *vcpu, bool bsp __unused)
+{
+ int error;
+
+ /* Set hart ID. */
+ error = vm_set_register(vcpu, VM_REG_GUEST_A0, vcpu_id(vcpu));
+ assert(error == 0);
+
+ fbsdrun_addcpu(vcpu_id(vcpu));
+}
+
+/*
+ * Load the specified boot code at the beginning of high memory.
+ */
+static void
+load_bootrom(struct vmctx *ctx, const char *path, uint64_t *elrp,
+ uint64_t *lenp)
+{
+ struct stat sb;
+ void *data, *gptr;
+ vm_paddr_t loadaddr;
+ off_t size;
+ int fd;
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ err(1, "open(%s)", path);
+ if (fstat(fd, &sb) != 0)
+ err(1, "fstat(%s)", path);
+
+ size = sb.st_size;
+
+ loadaddr = vm_get_highmem_base(ctx);
+ gptr = vm_map_gpa(ctx, loadaddr, round_page(size));
+
+ data = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (data == MAP_FAILED)
+ err(1, "mmap(%s)", path);
+ (void)close(fd);
+ memcpy(gptr, data, size);
+
+ if (munmap(data, size) != 0)
+ err(1, "munmap(%s)", path);
+
+ *elrp = loadaddr;
+ *lenp = size;
+}
+
+static void
+mmio_uart_intr_assert(void *arg)
+{
+ struct vmctx *ctx = arg;
+
+ vm_assert_irq(ctx, UART_INTR);
+}
+
+static void
+mmio_uart_intr_deassert(void *arg)
+{
+ struct vmctx *ctx = arg;
+
+ vm_deassert_irq(ctx, UART_INTR);
+}
+
+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)
+{
+ struct uart_ns16550_softc *sc = arg1;
+ long reg;
+
+ reg = addr - arg2;
+ if (dir == MEM_F_WRITE)
+ uart_ns16550_write(sc, reg, *val);
+ else
+ *val = uart_ns16550_read(sc, reg);
+
+ return (0);
+}
+
+static bool
+init_mmio_uart(struct vmctx *ctx)
+{
+ struct uart_ns16550_softc *sc;
+ struct mem_range mr;
+ const char *path;
+ int error;
+
+ path = get_config_value("console");
+ if (path == NULL)
+ return (false);
+
+ sc = uart_ns16550_init(mmio_uart_intr_assert, mmio_uart_intr_deassert,
+ ctx);
+ if (uart_ns16550_tty_open(sc, path) != 0) {
+ EPRINTLN("Unable to initialize backend '%s' for mmio uart",
+ path);
+ assert(0);
+ }
+
+ bzero(&mr, sizeof(struct mem_range));
+ mr.name = "uart";
+ mr.base = UART_MMIO_BASE;
+ mr.size = UART_MMIO_SIZE;
+ mr.flags = MEM_F_RW;
+ mr.handler = mmio_uart_mem_handler;
+ mr.arg1 = sc;
+ mr.arg2 = mr.base;
+ error = register_mem(&mr);
+ assert(error == 0);
+
+ return (true);
+}
+
+int
+bhyve_init_platform(struct vmctx *ctx, struct vcpu *bsp)
+{
+ const char *bootrom;
+ uint64_t elr;
+ uint64_t len;
+ int error;
+ int pcie_intrs[4] = {PCIE_INTA, PCIE_INTB, PCIE_INTC, PCIE_INTD};
+ vm_paddr_t fdt_gpa;
+
+ bootrom = get_config_value("bootrom");
+ if (bootrom == NULL) {
+ warnx("no bootrom specified");
+ return (ENOENT);
+ }
+ load_bootrom(ctx, bootrom, &elr, &len);
+ error = vm_set_register(bsp, VM_REG_GUEST_SEPC, elr);
+ if (error != 0) {
+ warn("vm_set_register(GUEST_SEPC)");
+ return (error);
+ }
+
+ fdt_gpa = vm_get_highmem_base(ctx) + roundup2(len, FDT_DTB_ALIGN);
+ error = fdt_init(ctx, guest_ncpus, fdt_gpa, FDT_SIZE);
+ if (error != 0)
+ return (error);
+
+ /* Set FDT base address to the bootable hart. */
+ error = vm_set_register(bsp, VM_REG_GUEST_A1, fdt_gpa);
+ assert(error == 0);
+
+ fdt_add_aplic(APLIC_MEM_BASE, APLIC_MEM_SIZE);
+ error = vm_attach_aplic(ctx, APLIC_MEM_BASE, APLIC_MEM_SIZE);
+ if (error != 0) {
+ warn("vm_attach_aplic()");
+ return (error);
+ }
+
+ if (init_mmio_uart(ctx))
+ fdt_add_uart(UART_MMIO_BASE, UART_MMIO_SIZE, UART_INTR);
+
+ pci_irq_init(pcie_intrs);
+ fdt_add_pcie(pcie_intrs);
+ vmexit_set_bsp(vcpu_id(bsp));
+
+ return (0);
+}
+
+int
+bhyve_init_platform_late(struct vmctx *ctx __unused, struct vcpu *bsp __unused)
+{
+
+ fdt_finalize();
+
+ return (0);
+}
diff --git a/usr.sbin/bhyve/riscv/fdt.c b/usr.sbin/bhyve/riscv/fdt.c
new file mode 100644
index 000000000000..54b75c68ea76
--- /dev/null
+++ b/usr.sbin/bhyve/riscv/fdt.c
@@ -0,0 +1,326 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2022 The FreeBSD Foundation
+ * Copyright (c) 2024 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This software was developed by Andrew Turner under sponsorship from
+ * the FreeBSD Foundation.
+ *
+ * This software was developed by the University of Cambridge Computer
+ * Laboratory (Department of Computer Science and Technology) under Innovate
+ * UK project 105694, "Digital Security by Design (DSbD) Technology Platform
+ * Prototype".
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <libfdt.h>
+#include <vmmapi.h>
+
+#include "config.h"
+#include "bhyverun.h"
+#include "fdt.h"
+
+#define SET_PROP_U32(prop, idx, val) \
+ ((uint32_t *)(prop))[(idx)] = cpu_to_fdt32(val)
+#define SET_PROP_U64(prop, idx, val) \
+ ((uint64_t *)(prop))[(idx)] = cpu_to_fdt64(val)
+
+#define IRQ_TYPE_LEVEL_HIGH 4
+#define IRQ_TYPE_LEVEL_LOW 8
+
+static void *fdtroot;
+static uint32_t aplic_phandle = 0;
+static uint32_t intc0_phandle = 0;
+
+static uint32_t
+assign_phandle(void *fdt)
+{
+ static uint32_t next_phandle = 1;
+ uint32_t phandle;
+
+ phandle = next_phandle;
+ next_phandle++;
+ fdt_property_u32(fdt, "phandle", phandle);
+
+ return (phandle);
+}
+
+static void
+set_single_reg(void *fdt, uint64_t start, uint64_t len)
+{
+ void *reg;
+
+ fdt_property_placeholder(fdt, "reg", 2 * sizeof(uint64_t), &reg);
+ SET_PROP_U64(reg, 0, start);
+ SET_PROP_U64(reg, 1, len);
+}
+
+static void
+add_cpu(void *fdt, int cpuid)
+{
+ char node_name[16];
+
+ snprintf(node_name, sizeof(node_name), "cpu@%d", cpuid);
+
+ fdt_begin_node(fdt, node_name);
+ fdt_property_string(fdt, "device_type", "cpu");
+ fdt_property_string(fdt, "compatible", "riscv");
+ fdt_property_u32(fdt, "reg", cpuid);
+ fdt_property_string(fdt, "riscv,isa", "rv64imafdc_sstc");
+ fdt_property_string(fdt, "mmu-type", "riscv,sv39");
+ fdt_property_string(fdt, "clock-frequency", "1000000000");
+
+ fdt_begin_node(fdt, "interrupt-controller");
+ intc0_phandle = assign_phandle(fdt);
+ fdt_property_u32(fdt, "#address-cells", 2);
+ fdt_property_u32(fdt, "#interrupt-cells", 1);
+ fdt_property(fdt, "interrupt-controller", NULL, 0);
+ fdt_property_string(fdt, "compatible", "riscv,cpu-intc");
+ fdt_end_node(fdt);
+
+ fdt_end_node(fdt);
+}
+
+static void
+add_cpus(void *fdt, int ncpu)
+{
+ int cpuid;
+
+ fdt_begin_node(fdt, "cpus");
+ /* XXX: Needed given the root #address-cells? */
+ fdt_property_u32(fdt, "#address-cells", 1);
+ fdt_property_u32(fdt, "#size-cells", 0);
+ fdt_property_u32(fdt, "timebase-frequency", 10000000);
+
+ for (cpuid = 0; cpuid < ncpu; cpuid++) {
+ add_cpu(fdt, cpuid);
+ }
+ fdt_end_node(fdt);
+}
+
+int
+fdt_init(struct vmctx *ctx, int ncpu, vm_paddr_t fdtaddr, vm_size_t fdtsize)
+{
+ void *fdt;
+ const char *bootargs;
+
+ fdt = paddr_guest2host(ctx, fdtaddr, fdtsize);
+ if (fdt == NULL)
+ return (EFAULT);
+
+ fdt_create(fdt, (int)fdtsize);
+
+ /* Add the memory reserve map (needed even if none is reserved) */
+ fdt_finish_reservemap(fdt);
+
+ /* Create the root node */
+ fdt_begin_node(fdt, "");
+
+ fdt_property_string(fdt, "compatible", "freebsd,bhyve");
+ fdt_property_u32(fdt, "#address-cells", 2);
+ fdt_property_u32(fdt, "#size-cells", 2);
+
+ fdt_begin_node(fdt, "chosen");
+ fdt_property_string(fdt, "stdout-path", "serial0:115200n8");
+ bootargs = get_config_value("fdt.bootargs");
+ if (bootargs != NULL)
+ fdt_property_string(fdt, "bootargs", bootargs);
+ fdt_end_node(fdt);
+
+ fdt_begin_node(fdt, "memory");
+ fdt_property_string(fdt, "device_type", "memory");
+ /* There is no lowmem on riscv. */
+ assert(vm_get_lowmem_size(ctx) == 0);
+ set_single_reg(fdt, vm_get_highmem_base(ctx), vm_get_highmem_size(ctx));
+ fdt_end_node(fdt);
+
+ add_cpus(fdt, ncpu);
+
+ /* Finalized by fdt_finalized(). */
+ fdtroot = fdt;
+
+ return (0);
+}
+
+void
+fdt_add_aplic(uint64_t mem_base, uint64_t mem_size)
+{
+ char node_name[32];
+ void *fdt, *prop;
+
+ fdt = fdtroot;
+
+ snprintf(node_name, sizeof(node_name), "interrupt-controller@%lx",
+ (unsigned long)mem_base);
+ fdt_begin_node(fdt, node_name);
+
+ aplic_phandle = assign_phandle(fdt);
+ fdt_property_string(fdt, "compatible", "riscv,aplic");
+ fdt_property(fdt, "interrupt-controller", NULL, 0);
+#if notyet
+ fdt_property(fdt, "msi-controller", NULL, 0);
+#endif
+ /* XXX: Needed given the root #address-cells? */
+ fdt_property_u32(fdt, "#address-cells", 2);
+ fdt_property_u32(fdt, "#interrupt-cells", 2);
+ fdt_property_placeholder(fdt, "reg", 2 * sizeof(uint64_t), &prop);
+ SET_PROP_U64(prop, 0, mem_base);
+ SET_PROP_U64(prop, 1, mem_size);
+
+ fdt_property_placeholder(fdt, "interrupts-extended",
+ 2 * sizeof(uint32_t), &prop);
+ SET_PROP_U32(prop, 0, intc0_phandle);
+ SET_PROP_U32(prop, 1, 9);
+ fdt_property_u32(fdt, "riscv,num-sources", 63);
+
+ fdt_end_node(fdt);
+
+ fdt_property_u32(fdt, "interrupt-parent", aplic_phandle);
+}
+
+void
+fdt_add_uart(uint64_t uart_base, uint64_t uart_size, int intr)
+{
+ void *fdt, *interrupts;
+ char node_name[32];
+
+ assert(aplic_phandle != 0);
+
+ fdt = fdtroot;
+
+ snprintf(node_name, sizeof(node_name), "serial@%lx", uart_base);
+ fdt_begin_node(fdt, node_name);
+ fdt_property_string(fdt, "compatible", "ns16550");
+ set_single_reg(fdt, uart_base, uart_size);
+ fdt_property_u32(fdt, "interrupt-parent", aplic_phandle);
+ fdt_property_placeholder(fdt, "interrupts", 2 * sizeof(uint32_t),
+ &interrupts);
+ SET_PROP_U32(interrupts, 0, intr);
+ SET_PROP_U32(interrupts, 1, IRQ_TYPE_LEVEL_HIGH);
+
+ fdt_end_node(fdt);
+
+ snprintf(node_name, sizeof(node_name), "/serial@%lx", uart_base);
+ fdt_begin_node(fdt, "aliases");
+ fdt_property_string(fdt, "serial0", node_name);
+ fdt_end_node(fdt);
+}
+
+void
+fdt_add_pcie(int intrs[static 4])
+{
+ void *fdt, *prop;
+ int slot, pin, intr, i;
+
+ assert(aplic_phandle != 0);
+
+ fdt = fdtroot;
+
+ fdt_begin_node(fdt, "pcie@1f0000000");
+ fdt_property_string(fdt, "compatible", "pci-host-ecam-generic");
+ fdt_property_u32(fdt, "#address-cells", 3);
+ fdt_property_u32(fdt, "#size-cells", 2);
+ fdt_property_string(fdt, "device_type", "pci");
+ fdt_property_u64(fdt, "bus-range", (0ul << 32) | 1);
+ set_single_reg(fdt, 0xe0000000, 0x10000000);
+ fdt_property_placeholder(fdt, "ranges",
+ 2 * 7 * sizeof(uint32_t), &prop);
+ SET_PROP_U32(prop, 0, 0x01000000);
+
+ SET_PROP_U32(prop, 1, 0);
+ SET_PROP_U32(prop, 2, 0xdf000000);
+
+ SET_PROP_U32(prop, 3, 0);
+ SET_PROP_U32(prop, 4, 0xdf000000);
+
+ SET_PROP_U32(prop, 5, 0);
+ SET_PROP_U32(prop, 6, 0x01000000);
+
+ SET_PROP_U32(prop, 7, 0x02000000);
+
+ SET_PROP_U32(prop, 8, 0);
+ SET_PROP_U32(prop, 9, 0xa0000000);
+
+ SET_PROP_U32(prop, 10, 0);
+ SET_PROP_U32(prop, 11, 0xa0000000);
+
+ SET_PROP_U32(prop, 12, 0);
+ SET_PROP_U32(prop, 13, 0x3f000000);
+
+#if notyet
+ fdt_property_placeholder(fdt, "msi-map", 4 * sizeof(uint32_t), &prop);
+ SET_PROP_U32(prop, 0, 0); /* RID base */
+ SET_PROP_U32(prop, 1, aplic_phandle); /* MSI parent */
+ SET_PROP_U32(prop, 2, 0); /* MSI base */
+ SET_PROP_U32(prop, 3, 0x10000); /* RID length */
+ fdt_property_u32(fdt, "msi-parent", aplic_phandle);
+#endif
+
+ fdt_property_u32(fdt, "#interrupt-cells", 1);
+ fdt_property_u32(fdt, "interrupt-parent", aplic_phandle);
+
+ /*
+ * Describe standard swizzled interrupts routing (pins rotated by one
+ * for each consecutive slot). Must match pci_irq_route().
+ */
+ fdt_property_placeholder(fdt, "interrupt-map-mask",
+ 4 * sizeof(uint32_t), &prop);
+ SET_PROP_U32(prop, 0, 3 << 11);
+ SET_PROP_U32(prop, 1, 0);
+ SET_PROP_U32(prop, 2, 0);
+ SET_PROP_U32(prop, 3, 7);
+ fdt_property_placeholder(fdt, "interrupt-map",
+ 16 * 9 * sizeof(uint32_t), &prop);
+ for (i = 0; i < 16; ++i) {
+ pin = i % 4;
+ slot = i / 4;
+ intr = intrs[(pin + slot) % 4];
+ SET_PROP_U32(prop, 10 * i + 0, slot << 11);
+ SET_PROP_U32(prop, 10 * i + 1, 0);
+ SET_PROP_U32(prop, 10 * i + 2, 0);
+ SET_PROP_U32(prop, 10 * i + 3, pin + 1);
+ SET_PROP_U32(prop, 10 * i + 4, aplic_phandle);
+ SET_PROP_U32(prop, 10 * i + 5, 0);
+ SET_PROP_U32(prop, 10 * i + 6, 0);
+ SET_PROP_U32(prop, 10 * i + 7, intr);
+ SET_PROP_U32(prop, 10 * i + 8, IRQ_TYPE_LEVEL_HIGH);
+ }
+
+ fdt_end_node(fdt);
+}
+
+void
+fdt_finalize(void)
+{
+ fdt_end_node(fdtroot);
+
+ fdt_finish(fdtroot);
+}
diff --git a/usr.sbin/bhyve/riscv/fdt.h b/usr.sbin/bhyve/riscv/fdt.h
new file mode 100644
index 000000000000..9bebe6ffa29d
--- /dev/null
+++ b/usr.sbin/bhyve/riscv/fdt.h
@@ -0,0 +1,45 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2022 The FreeBSD Foundation
+ *
+ * This software was developed by Andrew Turner under sponsorship from
+ * the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _FDT_H_
+#define _FDT_H_
+
+#include <sys/types.h>
+
+struct vmctx;
+
+int fdt_init(struct vmctx *ctx, int ncpu, vm_paddr_t addrp,
+ vm_size_t size);
+void fdt_add_aplic(uint64_t dist_base, uint64_t dist_size);
+void fdt_add_pcie(int intrs[static 4]);
+void fdt_add_uart(uint64_t uart_base, uint64_t uart_size, int intr);
+void fdt_finalize(void);
+
+#endif /* _FDT_H_ */
diff --git a/usr.sbin/bhyve/riscv/pci_irq.c b/usr.sbin/bhyve/riscv/pci_irq.c
new file mode 100644
index 000000000000..dec65ce23196
--- /dev/null
+++ b/usr.sbin/bhyve/riscv/pci_irq.c
@@ -0,0 +1,66 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2024 Jessica Clarke <jrtc27@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <vmmapi.h>
+
+#include "pci_emul.h"
+#include "pci_irq.h"
+
+static int aplic_irqs[4];
+
+void
+pci_irq_init(int intrs[static 4])
+{
+ int i;
+
+ for (i = 0; i < 4; ++i)
+ aplic_irqs[i] = intrs[i];
+}
+
+void
+pci_irq_assert(struct pci_devinst *pi)
+{
+ vm_assert_irq(pi->pi_vmctx, pi->pi_lintr.irq.aplic_irq);
+}
+
+void
+pci_irq_deassert(struct pci_devinst *pi)
+{
+ vm_deassert_irq(pi->pi_vmctx, pi->pi_lintr.irq.aplic_irq);
+}
+
+void
+pci_irq_route(struct pci_devinst *pi, struct pci_irq *irq)
+{
+ /*
+ * Assign swizzled IRQ for this INTx if one is not yet assigned. Must
+ * match fdt_add_pcie().
+ */
+ if (irq->aplic_irq == 0)
+ irq->aplic_irq =
+ aplic_irqs[(pi->pi_slot + pi->pi_lintr.pin - 1) % 4];
+}
diff --git a/usr.sbin/bhyve/riscv/pci_irq_machdep.h b/usr.sbin/bhyve/riscv/pci_irq_machdep.h
new file mode 100644
index 000000000000..3a951d4413ae
--- /dev/null
+++ b/usr.sbin/bhyve/riscv/pci_irq_machdep.h
@@ -0,0 +1,49 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2024 Jessica Clarke <jrtc27@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef __PCI_IRQ_MD_H__
+#define __PCI_IRQ_MD_H__
+
+struct pci_irq {
+ int aplic_irq;
+};
+
+void pci_irq_init(int intrs[static 4]);
+
+static inline void
+pci_irq_init_irq(struct pci_irq *irq)
+{
+ irq->aplic_irq = 0;
+}
+
+static inline uint8_t
+pci_irq_intline(struct pci_irq *irq __unused)
+{
+ return (255);
+}
+
+#endif
diff --git a/usr.sbin/bhyve/riscv/riscv.h b/usr.sbin/bhyve/riscv/riscv.h
new file mode 100644
index 000000000000..7d23c403eb8a
--- /dev/null
+++ b/usr.sbin/bhyve/riscv/riscv.h
@@ -0,0 +1,41 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2024 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This software was developed by the University of Cambridge Computer
+ * Laboratory (Department of Computer Science and Technology) under Innovate
+ * UK project 105694, "Digital Security by Design (DSbD) Technology Platform
+ * Prototype".
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _RISCV_H_
+#define _RISCV_H_
+
+#define CPU_TO_HART(x) (x)
+#define HART_TO_CPU(x) (x)
+
+void vmexit_set_bsp(int hart_id);
+
+#endif /* !_RISCV_H_ */
diff --git a/usr.sbin/bhyve/riscv/vmexit.c b/usr.sbin/bhyve/riscv/vmexit.c
new file mode 100644
index 000000000000..b7d1a272b87e
--- /dev/null
+++ b/usr.sbin/bhyve/riscv/vmexit.c
@@ -0,0 +1,366 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2011 NetApp, Inc.
+ * All rights reserved.
+ * Copyright (c) 2024 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This software was developed by the University of Cambridge Computer
+ * Laboratory (Department of Computer Science and Technology) under Innovate
+ * UK project 105694, "Digital Security by Design (DSbD) Technology Platform
+ * Prototype".
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/cpuset.h>
+
+#include <machine/riscvreg.h>
+#include <machine/cpu.h>
+#include <machine/sbi.h>
+#include <machine/vmm.h>
+#include <machine/vmm_dev.h>
+#include <machine/vmm_instruction_emul.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <vmmapi.h>
+
+#include "bhyverun.h"
+#include "config.h"
+#include "debug.h"
+#include "mem.h"
+#include "vmexit.h"
+#include "riscv.h"
+
+#define BHYVE_VERSION ((uint64_t)__FreeBSD_version)
+#define SBI_VERS_MAJOR 2
+#define SBI_VERS_MINOR 0
+
+static cpuset_t running_hartmask = CPUSET_T_INITIALIZER(0);
+
+void
+vmexit_set_bsp(int hart_id)
+{
+
+ CPU_SET_ATOMIC(hart_id, &running_hartmask);
+}
+
+static int
+vmexit_inst_emul(struct vmctx *ctx __unused, struct vcpu *vcpu,
+ struct vm_run *vmrun)
+{
+ struct vm_exit *vme;
+ struct vie *vie;
+ int err;
+
+ vme = vmrun->vm_exit;
+ vie = &vme->u.inst_emul.vie;
+
+ err = emulate_mem(vcpu, vme->u.inst_emul.gpa, vie,
+ &vme->u.inst_emul.paging);
+ if (err) {
+ if (err == ESRCH) {
+ EPRINTLN("Unhandled memory access to 0x%lx\n",
+ vme->u.inst_emul.gpa);
+ }
+ goto fail;
+ }
+
+ return (VMEXIT_CONTINUE);
+
+fail:
+ fprintf(stderr, "Failed to emulate instruction ");
+ FPRINTLN(stderr, "at 0x%lx", vme->pc);
+ return (VMEXIT_ABORT);
+}
+
+static int
+vmexit_suspend(struct vmctx *ctx, struct vcpu *vcpu, struct vm_run *vmrun)
+{
+ struct vm_exit *vme;
+ enum vm_suspend_how how;
+ int vcpuid = vcpu_id(vcpu);
+
+ vme = vmrun->vm_exit;
+ how = vme->u.suspended.how;
+
+ fbsdrun_deletecpu(vcpuid);
+
+ switch (how) {
+ case VM_SUSPEND_RESET:
+ exit(0);
+ case VM_SUSPEND_POWEROFF:
+ if (get_config_bool_default("destroy_on_poweroff", false))
+ vm_destroy(ctx);
+ exit(1);
+ case VM_SUSPEND_HALT:
+ exit(2);
+ default:
+ fprintf(stderr, "vmexit_suspend: invalid reason %d\n", how);
+ exit(100);
+ }
+
+ /* NOT REACHED. */
+
+ return (0);
+}
+
+static int
+vmexit_debug(struct vmctx *ctx __unused, struct vcpu *vcpu __unused,
+ struct vm_run *vmrun __unused)
+{
+
+ return (VMEXIT_CONTINUE);
+}
+
+static int
+vmexit_bogus(struct vmctx *ctx __unused, struct vcpu *vcpu __unused,
+ struct vm_run *vmrun __unused)
+{
+
+ return (VMEXIT_CONTINUE);
+}
+
+static int
+vmm_sbi_probe_extension(int ext_id)
+{
+
+ switch (ext_id) {
+ case SBI_EXT_ID_HSM:
+ case SBI_EXT_ID_TIME:
+ case SBI_EXT_ID_IPI:
+ case SBI_EXT_ID_RFNC:
+ case SBI_EXT_ID_SRST:
+ case SBI_CONSOLE_PUTCHAR:
+ case SBI_CONSOLE_GETCHAR:
+ break;
+ default:
+ return (0);
+ }
+
+ return (1);
+}
+
+static void
+vmexit_ecall_time(struct vmctx *ctx __unused, struct vm_exit *vme __unused)
+{
+
+}
+
+static void
+vmexit_ecall_hsm(struct vmctx *ctx __unused, struct vcpu *vcpu __unused,
+ struct vm_exit *vme)
+{
+ struct vcpu *newvcpu;
+ uint64_t hart_id;
+ int func_id;
+ int error;
+ int ret;
+
+ hart_id = vme->u.ecall.args[0];
+ func_id = vme->u.ecall.args[6];
+
+ ret = -1;
+
+ if (HART_TO_CPU(hart_id) >= (uint64_t)guest_ncpus)
+ goto done;
+
+ newvcpu = fbsdrun_vcpu(HART_TO_CPU(hart_id));
+ assert(newvcpu != NULL);
+
+ switch (func_id) {
+ case SBI_HSM_HART_START:
+ if (CPU_ISSET(hart_id, &running_hartmask))
+ break;
+
+ /* Set hart ID. */
+ error = vm_set_register(newvcpu, VM_REG_GUEST_A0, hart_id);
+ assert(error == 0);
+
+ /* Set PC. */
+ error = vm_set_register(newvcpu, VM_REG_GUEST_SEPC,
+ vme->u.ecall.args[1]);
+ assert(error == 0);
+
+ vm_resume_cpu(newvcpu);
+ CPU_SET_ATOMIC(hart_id, &running_hartmask);
+
+ ret = 0;
+ break;
+ case SBI_HSM_HART_STOP:
+ if (!CPU_ISSET(hart_id, &running_hartmask))
+ break;
+ CPU_CLR_ATOMIC(hart_id, &running_hartmask);
+ vm_suspend_cpu(newvcpu);
+ ret = 0;
+ break;
+ case SBI_HSM_HART_STATUS:
+ /* TODO. */
+ break;
+ default:
+ break;
+ }
+
+done:
+ error = vm_set_register(vcpu, VM_REG_GUEST_A0, ret);
+ assert(error == 0);
+}
+
+static void
+vmexit_ecall_base(struct vmctx *ctx __unused, struct vcpu *vcpu,
+ struct vm_exit *vme)
+{
+ int sbi_function_id;
+ int ext_id;
+ int error;
+ uint32_t val;
+ int ret;
+
+ sbi_function_id = vme->u.ecall.args[6];
+
+ ret = 0;
+
+ switch (sbi_function_id) {
+ case SBI_BASE_GET_SPEC_VERSION:
+ val = SBI_VERS_MAJOR << SBI_SPEC_VERS_MAJOR_OFFSET;
+ val |= SBI_VERS_MINOR << SBI_SPEC_VERS_MINOR_OFFSET;
+ break;
+ case SBI_BASE_GET_IMPL_ID:
+ val = SBI_IMPL_ID_BHYVE;
+ break;
+ case SBI_BASE_GET_IMPL_VERSION:
+ val = BHYVE_VERSION;
+ break;
+ case SBI_BASE_PROBE_EXTENSION:
+ ext_id = vme->u.ecall.args[0];
+ val = vmm_sbi_probe_extension(ext_id);
+ break;
+ case SBI_BASE_GET_MVENDORID:
+ val = MVENDORID_UNIMPL;
+ break;
+ case SBI_BASE_GET_MARCHID:
+ val = MARCHID_UNIMPL;
+ break;
+ case SBI_BASE_GET_MIMPID:
+ val = 0;
+ break;
+ default:
+ ret = 1;
+ break;
+ }
+
+ error = vm_set_register(vcpu, VM_REG_GUEST_A0, ret);
+ assert(error == 0);
+
+ if (ret == 0) {
+ error = vm_set_register(vcpu, VM_REG_GUEST_A1, val);
+ assert(error == 0);
+ }
+}
+
+static void
+vmexit_ecall_srst(struct vmctx *ctx, struct vm_exit *vme)
+{
+ enum vm_suspend_how how;
+ int func_id;
+ int type;
+
+ func_id = vme->u.ecall.args[6];
+ type = vme->u.ecall.args[0];
+
+ switch (func_id) {
+ case SBI_SRST_SYSTEM_RESET:
+ switch (type) {
+ case SBI_SRST_TYPE_SHUTDOWN:
+ case SBI_SRST_TYPE_COLD_REBOOT:
+ case SBI_SRST_TYPE_WARM_REBOOT:
+ how = VM_SUSPEND_POWEROFF;
+ vm_suspend(ctx, how);
+ break;
+ default:
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+static int
+vmexit_ecall(struct vmctx *ctx, struct vcpu *vcpu, struct vm_run *vmrun)
+{
+ int sbi_extension_id;
+ struct vm_exit *vme;
+
+ vme = vmrun->vm_exit;
+
+ sbi_extension_id = vme->u.ecall.args[7];
+ switch (sbi_extension_id) {
+ case SBI_EXT_ID_SRST:
+ vmexit_ecall_srst(ctx, vme);
+ break;
+ case SBI_EXT_ID_BASE:
+ vmexit_ecall_base(ctx, vcpu, vme);
+ break;
+ case SBI_EXT_ID_TIME:
+ vmexit_ecall_time(ctx, vme);
+ break;
+ case SBI_EXT_ID_HSM:
+ vmexit_ecall_hsm(ctx, vcpu, vme);
+ break;
+ case SBI_CONSOLE_PUTCHAR:
+ case SBI_CONSOLE_GETCHAR:
+ default:
+ /* Unknown SBI extension. */
+ break;
+ }
+
+ return (VMEXIT_CONTINUE);
+}
+
+
+static int
+vmexit_hyp(struct vmctx *ctx __unused, struct vcpu *vcpu __unused,
+ struct vm_run *vmrun)
+{
+ struct vm_exit *vme;
+
+ vme = vmrun->vm_exit;
+
+ printf("unhandled exception: scause %#lx\n", vme->u.hyp.scause);
+
+ return (VMEXIT_ABORT);
+}
+
+const vmexit_handler_t vmexit_handlers[VM_EXITCODE_MAX] = {
+ [VM_EXITCODE_BOGUS] = vmexit_bogus,
+ [VM_EXITCODE_HYP] = vmexit_hyp,
+ [VM_EXITCODE_INST_EMUL] = vmexit_inst_emul,
+ [VM_EXITCODE_SUSPENDED] = vmexit_suspend,
+ [VM_EXITCODE_DEBUG] = vmexit_debug,
+ [VM_EXITCODE_ECALL] = vmexit_ecall,
+};
diff --git a/usr.sbin/bhyvectl/riscv/Makefile.inc b/usr.sbin/bhyvectl/riscv/Makefile.inc
new file mode 100644
index 000000000000..047e8d6c70bc
--- /dev/null
+++ b/usr.sbin/bhyvectl/riscv/Makefile.inc
@@ -0,0 +1 @@
+SRCS+= bhyvectl_machdep.c
diff --git a/usr.sbin/bhyvectl/riscv/bhyvectl_machdep.c b/usr.sbin/bhyvectl/riscv/bhyvectl_machdep.c
new file mode 100644
index 000000000000..3e25f9a260c6
--- /dev/null
+++ b/usr.sbin/bhyvectl/riscv/bhyvectl_machdep.c
@@ -0,0 +1,82 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2024 Mark Johnston <markj@FreeBSD.org>
+ *
+ * This software was developed by the University of Cambridge Computer
+ * Laboratory (Department of Computer Science and Technology) under Innovate
+ * UK project 105694, "Digital Security by Design (DSbD) Technology Platform
+ * Prototype".
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <vmmapi.h>
+
+#include "bhyvectl.h"
+
+void
+bhyvectl_dump_vm_run_exitcode(struct vm_exit *vmexit __unused,
+ int vcpu __unused)
+{
+}
+
+struct option *
+bhyvectl_opts(const struct option *options, size_t count)
+{
+ struct option *all_opts;
+
+ all_opts = calloc(count + 1, sizeof(struct option));
+ if (all_opts == NULL)
+ err(1, "calloc");
+ memcpy(all_opts, options, count * sizeof(struct option));
+ return (all_opts);
+}
+
+void
+bhyvectl_handle_opt(const struct option *opts __unused, int opt __unused)
+{
+}
+
+const char *
+bhyvectl_opt_desc(int opt __unused)
+{
+ /* No riscv-specific options yet. */
+ return ("???");
+}
+
+void
+bhyvectl_md_main(struct vmctx *ctx __unused, struct vcpu *vcpu __unused,
+ int vcpuid __unused, bool get_all __unused)
+{
+}