diff --git a/usr.sbin/bhyve/aarch64/bhyve_machdep.h b/usr.sbin/bhyve/aarch64/bhyve_machdep.h new file mode 100644 index 000000000000..9e01d906ad4d --- /dev/null +++ b/usr.sbin/bhyve/aarch64/bhyve_machdep.h @@ -0,0 +1,12 @@ +/*- + * Copyright (c) 2025 Arm Ltd + * + * SPDX-License-Identifier: BSD-2-Clause +*/ + +#ifndef _BHYVE_MACHDEP_H_ +#define _BHYVE_MACHDEP_H_ + +extern cpuset_t running_cpumask; + +#endif /* _BHYVE_MACHDEP_H_ */ diff --git a/usr.sbin/bhyve/aarch64/bhyverun_machdep.c b/usr.sbin/bhyve/aarch64/bhyverun_machdep.c index ffca4aa8c5a6..098454169272 100644 --- a/usr.sbin/bhyve/aarch64/bhyverun_machdep.c +++ b/usr.sbin/bhyve/aarch64/bhyverun_machdep.c @@ -1,416 +1,417 @@ /*- * 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 #include #include #include #include #include #include #include #include #include #include #include +#include "bhyve_machdep.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 "rtc_pl031.h" #include "uart_emul.h" /* Start of mem + 1M */ #define FDT_BASE 0x100000 #define FDT_SIZE (64 * 1024) /* Start of lowmem + 64K */ #define UART_MMIO_BASE 0x10000 #define UART_MMIO_SIZE 0x1000 #define UART_INTR 32 #define RTC_MMIO_BASE 0x11000 #define RTC_MMIO_SIZE 0x1000 #define RTC_INTR 33 #define GIC_DIST_BASE 0x2f000000 #define GIC_DIST_SIZE 0x10000 #define GIC_REDIST_BASE 0x2f100000 #define GIC_REDIST_SIZE(ncpu) ((ncpu) * 2 * PAGE_SIZE_64K) #define PCIE_INTA 34 #define PCIE_INTB 35 #define PCIE_INTC 36 #define PCIE_INTD 37 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" " -G: start a debug server\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: 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:G: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 'G': bhyve_parse_gdb_options(optarg); 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) { 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) { 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; } 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_pl011_softc *sc = arg1; long reg; reg = (addr - arg2) >> 2; if (dir == MEM_F_WRITE) uart_pl011_write(sc, reg, *val); else *val = uart_pl011_read(sc, reg); return (0); } static bool init_mmio_uart(struct vmctx *ctx) { struct uart_pl011_softc *sc; struct mem_range mr; const char *path; int error; path = get_config_value("console"); if (path == NULL) return (false); sc = uart_pl011_init(mmio_uart_intr_assert, mmio_uart_intr_deassert, ctx); if (uart_pl011_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); } static void mmio_rtc_intr_assert(void *arg) { struct vmctx *ctx = arg; vm_assert_irq(ctx, RTC_INTR); } static void mmio_rtc_intr_deassert(void *arg) { struct vmctx *ctx = arg; vm_deassert_irq(ctx, RTC_INTR); } static int mmio_rtc_mem_handler(struct vcpu *vcpu __unused, int dir, uint64_t addr, int size __unused, uint64_t *val, void *arg1, long arg2) { struct rtc_pl031_softc *sc = arg1; long reg; reg = addr - arg2; if (dir == MEM_F_WRITE) rtc_pl031_write(sc, reg, *val); else *val = rtc_pl031_read(sc, reg); return (0); } static void init_mmio_rtc(struct vmctx *ctx) { struct rtc_pl031_softc *sc; struct mem_range mr; int error; sc = rtc_pl031_init(mmio_rtc_intr_assert, mmio_rtc_intr_deassert, ctx); bzero(&mr, sizeof(struct mem_range)); mr.name = "rtc"; mr.base = RTC_MMIO_BASE; mr.size = RTC_MMIO_SIZE; mr.flags = MEM_F_RW; mr.handler = mmio_rtc_mem_handler; mr.arg1 = sc; mr.arg2 = mr.base; error = register_mem(&mr); assert(error == 0); } static vm_paddr_t fdt_gpa(struct vmctx *ctx) { return (vm_get_highmem_base(ctx) + FDT_BASE); } int bhyve_init_platform(struct vmctx *ctx, struct vcpu *bsp) { const char *bootrom; uint64_t elr; int error; int pcie_intrs[4] = {PCIE_INTA, PCIE_INTB, PCIE_INTC, PCIE_INTD}; bootrom = get_config_value("bootrom"); if (bootrom == NULL) { warnx("no bootrom specified"); return (ENOENT); } load_bootrom(ctx, bootrom, &elr); error = vm_set_register(bsp, VM_REG_GUEST_PC, elr); if (error != 0) { warn("vm_set_register(GUEST_PC)"); return (error); } error = fdt_init(ctx, guest_ncpus, fdt_gpa(ctx), FDT_SIZE); if (error != 0) return (error); fdt_add_gic(GIC_DIST_BASE, GIC_DIST_SIZE, GIC_REDIST_BASE, GIC_REDIST_SIZE(guest_ncpus)); error = vm_attach_vgic(ctx, GIC_DIST_BASE, GIC_DIST_SIZE, GIC_REDIST_BASE, GIC_REDIST_SIZE(guest_ncpus)); if (error != 0) { warn("vm_attach_vgic()"); return (error); } if (init_mmio_uart(ctx)) fdt_add_uart(UART_MMIO_BASE, UART_MMIO_SIZE, UART_INTR); init_mmio_rtc(ctx); fdt_add_rtc(RTC_MMIO_BASE, RTC_MMIO_SIZE, RTC_INTR); fdt_add_timer(); pci_irq_init(pcie_intrs); fdt_add_pcie(pcie_intrs); /* Mark CPU0 as running */ CPU_SET(0, &running_cpumask); return (0); } int bhyve_init_platform_late(struct vmctx *ctx, struct vcpu *bsp __unused) { int error; fdt_finalize(); error = vm_set_register(bsp, VM_REG_GUEST_X0, fdt_gpa(ctx)); assert(error == 0); return (0); } diff --git a/usr.sbin/bhyve/aarch64/vmexit.c b/usr.sbin/bhyve/aarch64/vmexit.c index 2d350566675a..6b7f80e67a92 100644 --- a/usr.sbin/bhyve/aarch64/vmexit.c +++ b/usr.sbin/bhyve/aarch64/vmexit.c @@ -1,300 +1,301 @@ /*- * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include "bhyve_machdep.h" #include "bhyverun.h" #include "config.h" #include "debug.h" #include "gdb.h" #include "mem.h" #include "vmexit.h" cpuset_t running_cpumask; 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_reg_emul(struct vmctx *ctx __unused, struct vcpu *vcpu __unused, struct vm_run *vmrun) { struct vm_exit *vme; struct vre *vre; vme = vmrun->vm_exit; vre = &vme->u.reg_emul.vre; EPRINTLN("Unhandled register access: pc %#lx syndrome %#x reg %d\n", vme->pc, vre->inst_syndrome, vre->reg); 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); } return (0); /* NOTREACHED */ } static int vmexit_debug(struct vmctx *ctx __unused, struct vcpu *vcpu, struct vm_run *vmrun __unused) { gdb_cpu_suspend(vcpu); /* * XXX-MJ sleep for a short period to avoid chewing up the CPU in the * window between activation of the vCPU thread and the STARTUP IPI. */ usleep(1000); return (VMEXIT_CONTINUE); } static int vmexit_bogus(struct vmctx *ctx __unused, struct vcpu *vcpu __unused, struct vm_run *vmrun __unused) { return (VMEXIT_CONTINUE); } static uint64_t smccc_affinity_info(uint64_t target_affinity, uint32_t lowest_affinity_level) { uint64_t cpu_aff, mask = 0; switch (lowest_affinity_level) { case 0: mask |= CPU_AFF0_MASK; /* FALLTHROUGH */ case 1: mask |= CPU_AFF1_MASK; /* FALLTHROUGH */ case 2: mask |= CPU_AFF2_MASK; /* FALLTHROUGH */ case 3: mask |= CPU_AFF3_MASK; break; default: return (PSCI_RETVAL_INVALID_PARAMS); } for (int vcpu = 0; vcpu < guest_ncpus; vcpu++) { /* TODO: We should get this from the kernel */ cpu_aff = (vcpu & 0xf) << MPIDR_AFF0_SHIFT | ((vcpu >> 4) & 0xff) << MPIDR_AFF1_SHIFT | ((vcpu >> 12) & 0xff) << MPIDR_AFF2_SHIFT | (uint64_t)((vcpu >> 20) & 0xff) << MPIDR_AFF3_SHIFT; if ((cpu_aff & mask) == (target_affinity & mask) && CPU_ISSET(vcpu, &running_cpumask)) { /* Return ON if any CPUs are on */ return (PSCI_AFFINITY_INFO_ON); } } /* No CPUs in the affinity mask are on, return OFF */ return (PSCI_AFFINITY_INFO_OFF); } static int vmexit_smccc(struct vmctx *ctx, struct vcpu *vcpu, struct vm_run *vmrun) { struct vcpu *newvcpu; struct vm_exit *vme; uint64_t newcpu, smccc_rv; enum vm_suspend_how how; int error; /* Return the Unknown Function Identifier by default */ smccc_rv = SMCCC_RET_NOT_SUPPORTED; vme = vmrun->vm_exit; switch (vme->u.smccc_call.func_id) { case PSCI_FNID_VERSION: /* We implement PSCI 1.0 */ smccc_rv = PSCI_VER(1, 0); break; case PSCI_FNID_CPU_SUSPEND: case PSCI_FNID_CPU_OFF: break; case PSCI_FNID_CPU_ON: newcpu = vme->u.smccc_call.args[0]; if (newcpu > (uint64_t)guest_ncpus) { smccc_rv = PSCI_RETVAL_INVALID_PARAMS; break; } if (CPU_TEST_SET_ATOMIC(newcpu, &running_cpumask)) { smccc_rv = PSCI_RETVAL_ALREADY_ON; break; } newvcpu = fbsdrun_vcpu(newcpu); assert(newvcpu != NULL); /* Set the context ID */ error = vm_set_register(newvcpu, VM_REG_GUEST_X0, vme->u.smccc_call.args[2]); assert(error == 0); /* Set the start program counter */ error = vm_set_register(newvcpu, VM_REG_GUEST_PC, vme->u.smccc_call.args[1]); assert(error == 0); vm_resume_cpu(newvcpu); smccc_rv = PSCI_RETVAL_SUCCESS; break; case PSCI_FNID_AFFINITY_INFO: smccc_rv = smccc_affinity_info(vme->u.smccc_call.args[0], vme->u.smccc_call.args[1]); break; case PSCI_FNID_SYSTEM_OFF: case PSCI_FNID_SYSTEM_RESET: if (vme->u.smccc_call.func_id == PSCI_FNID_SYSTEM_OFF) how = VM_SUSPEND_POWEROFF; else how = VM_SUSPEND_RESET; error = vm_suspend(ctx, how); assert(error == 0 || errno == EALREADY); break; default: break; } error = vm_set_register(vcpu, VM_REG_GUEST_X0, smccc_rv); assert(error == 0); return (VMEXIT_CONTINUE); } static int vmexit_hyp(struct vmctx *ctx __unused, struct vcpu *vcpu, struct vm_run *vmrun) { /* Raise an unknown reason exception */ if (vm_inject_exception(vcpu, (EXCP_UNKNOWN << ESR_ELx_EC_SHIFT) | ESR_ELx_IL, vmrun->vm_exit->u.hyp.far_el2) != 0) return (VMEXIT_ABORT); return (VMEXIT_CONTINUE); } static int vmexit_brk(struct vmctx *ctx __unused, struct vcpu *vcpu, struct vm_run *vmrun) { gdb_cpu_breakpoint(vcpu, vmrun->vm_exit); return (VMEXIT_CONTINUE); } static int vmexit_ss(struct vmctx *ctx __unused, struct vcpu *vcpu, struct vm_run *vmrun) { gdb_cpu_debug(vcpu, vmrun->vm_exit); return (VMEXIT_CONTINUE); } const vmexit_handler_t vmexit_handlers[VM_EXITCODE_MAX] = { [VM_EXITCODE_BOGUS] = vmexit_bogus, [VM_EXITCODE_INST_EMUL] = vmexit_inst_emul, [VM_EXITCODE_REG_EMUL] = vmexit_reg_emul, [VM_EXITCODE_SUSPENDED] = vmexit_suspend, [VM_EXITCODE_DEBUG] = vmexit_debug, [VM_EXITCODE_SMCCC] = vmexit_smccc, [VM_EXITCODE_HYP] = vmexit_hyp, [VM_EXITCODE_BRK] = vmexit_brk, [VM_EXITCODE_SS] = vmexit_ss, };