git: 7ab1a32cd43c - main - bhyve/riscv: Initial import.
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Thu, 31 Oct 2024 20:25:34 UTC
The branch main has been updated by br: URL: https://cgit.FreeBSD.org/src/commit/?id=7ab1a32cd43cbae61ad4dd435d6a482bbf61cb52 commit 7ab1a32cd43cbae61ad4dd435d6a482bbf61cb52 Author: Ruslan Bukin <br@FreeBSD.org> AuthorDate: 2024-10-31 16:14:32 +0000 Commit: Ruslan Bukin <br@FreeBSD.org> CommitDate: 2024-10-31 20:24:12 +0000 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 --- lib/Makefile | 3 +- lib/libvmmapi/riscv/Makefile.inc | 1 + lib/libvmmapi/riscv/vmmapi_machdep.c | 117 +++++++++ lib/libvmmapi/vmmapi.h | 9 +- usr.sbin/Makefile.riscv | 2 + usr.sbin/bhyve/pci_emul.c | 2 +- usr.sbin/bhyve/pci_irq.h | 2 + usr.sbin/bhyve/riscv/Makefile.inc | 7 + usr.sbin/bhyve/riscv/bhyverun_machdep.c | 357 ++++++++++++++++++++++++++++ usr.sbin/bhyve/riscv/fdt.c | 326 +++++++++++++++++++++++++ usr.sbin/bhyve/riscv/fdt.h | 45 ++++ usr.sbin/bhyve/riscv/pci_irq.c | 66 ++++++ usr.sbin/bhyve/riscv/pci_irq_machdep.h | 49 ++++ usr.sbin/bhyve/riscv/riscv.h | 41 ++++ usr.sbin/bhyve/riscv/vmexit.c | 366 +++++++++++++++++++++++++++++ usr.sbin/bhyvectl/riscv/Makefile.inc | 1 + 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_ + *** 653 LINES SKIPPED ***