mirror of
https://github.com/freebsd/freebsd-src.git
synced 2024-11-26 20:12:44 +00:00
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
This commit is contained in:
parent
d3916eace5
commit
7ab1a32cd4
@ -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
|
||||
|
||||
|
1
lib/libvmmapi/riscv/Makefile.inc
Normal file
1
lib/libvmmapi/riscv/Makefile.inc
Normal file
@ -0,0 +1 @@
|
||||
SRCS+= vmmapi_machdep.c
|
117
lib/libvmmapi/riscv/vmmapi_machdep.c
Normal file
117
lib/libvmmapi/riscv/vmmapi_machdep.c
Normal file
@ -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));
|
||||
}
|
@ -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);
|
||||
|
2
usr.sbin/Makefile.riscv
Normal file
2
usr.sbin/Makefile.riscv
Normal file
@ -0,0 +1,2 @@
|
||||
SUBDIR+= bhyve
|
||||
SUBDIR+= bhyvectl
|
@ -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
|
||||
|
@ -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
|
||||
|
7
usr.sbin/bhyve/riscv/Makefile.inc
Normal file
7
usr.sbin/bhyve/riscv/Makefile.inc
Normal file
@ -0,0 +1,7 @@
|
||||
SRCS+= \
|
||||
fdt.c
|
||||
|
||||
.PATH: ${BHYVE_SYSDIR}/sys/riscv/vmm
|
||||
SRCS+= vmm_instruction_emul.c
|
||||
|
||||
BHYVE_FDT_SUPPORT=
|
357
usr.sbin/bhyve/riscv/bhyverun_machdep.c
Normal file
357
usr.sbin/bhyve/riscv/bhyverun_machdep.c
Normal file
@ -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);
|
||||
}
|
326
usr.sbin/bhyve/riscv/fdt.c
Normal file
326
usr.sbin/bhyve/riscv/fdt.c
Normal file
@ -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);
|
||||
}
|
45
usr.sbin/bhyve/riscv/fdt.h
Normal file
45
usr.sbin/bhyve/riscv/fdt.h
Normal file
@ -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_ */
|
66
usr.sbin/bhyve/riscv/pci_irq.c
Normal file
66
usr.sbin/bhyve/riscv/pci_irq.c
Normal file
@ -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];
|
||||
}
|
49
usr.sbin/bhyve/riscv/pci_irq_machdep.h
Normal file
49
usr.sbin/bhyve/riscv/pci_irq_machdep.h
Normal file
@ -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
|
41
usr.sbin/bhyve/riscv/riscv.h
Normal file
41
usr.sbin/bhyve/riscv/riscv.h
Normal file
@ -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_ */
|
366
usr.sbin/bhyve/riscv/vmexit.c
Normal file
366
usr.sbin/bhyve/riscv/vmexit.c
Normal file
@ -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,
|
||||
};
|
1
usr.sbin/bhyvectl/riscv/Makefile.inc
Normal file
1
usr.sbin/bhyvectl/riscv/Makefile.inc
Normal file
@ -0,0 +1 @@
|
||||
SRCS+= bhyvectl_machdep.c
|
82
usr.sbin/bhyvectl/riscv/bhyvectl_machdep.c
Normal file
82
usr.sbin/bhyvectl/riscv/bhyvectl_machdep.c
Normal file
@ -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)
|
||||
{
|
||||
}
|
Loading…
Reference in New Issue
Block a user