diff options
author | Ruslan Bukin <br@FreeBSD.org> | 2024-10-31 16:14:32 +0000 |
---|---|---|
committer | Ruslan Bukin <br@FreeBSD.org> | 2024-10-31 20:24:12 +0000 |
commit | 7ab1a32cd43cbae61ad4dd435d6a482bbf61cb52 (patch) | |
tree | f81b95461b40f432ebb1c7f6b372ee488f34215c | |
parent | d3916eace506b8ab23537223f5c92924636a1c41 (diff) | |
download | src-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/Makefile | 3 | ||||
-rw-r--r-- | lib/libvmmapi/riscv/Makefile.inc | 1 | ||||
-rw-r--r-- | lib/libvmmapi/riscv/vmmapi_machdep.c | 117 | ||||
-rw-r--r-- | lib/libvmmapi/vmmapi.h | 9 | ||||
-rw-r--r-- | usr.sbin/Makefile.riscv | 2 | ||||
-rw-r--r-- | usr.sbin/bhyve/pci_emul.c | 2 | ||||
-rw-r--r-- | usr.sbin/bhyve/pci_irq.h | 2 | ||||
-rw-r--r-- | usr.sbin/bhyve/riscv/Makefile.inc | 7 | ||||
-rw-r--r-- | usr.sbin/bhyve/riscv/bhyverun_machdep.c | 357 | ||||
-rw-r--r-- | usr.sbin/bhyve/riscv/fdt.c | 326 | ||||
-rw-r--r-- | usr.sbin/bhyve/riscv/fdt.h | 45 | ||||
-rw-r--r-- | usr.sbin/bhyve/riscv/pci_irq.c | 66 | ||||
-rw-r--r-- | usr.sbin/bhyve/riscv/pci_irq_machdep.h | 49 | ||||
-rw-r--r-- | usr.sbin/bhyve/riscv/riscv.h | 41 | ||||
-rw-r--r-- | usr.sbin/bhyve/riscv/vmexit.c | 366 | ||||
-rw-r--r-- | usr.sbin/bhyvectl/riscv/Makefile.inc | 1 | ||||
-rw-r--r-- | usr.sbin/bhyvectl/riscv/bhyvectl_machdep.c | 82 |
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), ®); + 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) +{ +} |