diff --git a/usr.sbin/bhyve/aarch64/Makefile.inc b/usr.sbin/bhyve/aarch64/Makefile.inc index af458e719d44..9f187a6e4db7 100644 --- a/usr.sbin/bhyve/aarch64/Makefile.inc +++ b/usr.sbin/bhyve/aarch64/Makefile.inc @@ -1,10 +1,11 @@ SRCS+= \ fdt.c \ + mem_md.c \ rtc_pl031.c \ uart_pl011.c .PATH: ${BHYVE_SYSDIR}/sys/arm64/vmm SRCS+= vmm_instruction_emul.c BHYVE_FDT_SUPPORT= BHYVE_GDB_SUPPORT= diff --git a/usr.sbin/bhyve/amd64/Makefile.inc b/usr.sbin/bhyve/amd64/Makefile.inc index 53320a70178b..0a9b13901978 100644 --- a/usr.sbin/bhyve/amd64/Makefile.inc +++ b/usr.sbin/bhyve/amd64/Makefile.inc @@ -1,32 +1,33 @@ BHYVE_GDB_SUPPORT= SRCS+= \ atkbdc.c \ e820.c \ fwctl.c \ inout.c \ ioapic.c \ kernemu_dev.c \ + mem_md.c \ mptbl.c \ pci_fbuf.c \ pci_gvt-d.c \ pci_lpc.c \ pci_passthru.c \ pctestdev.c \ pm.c \ post.c \ ps2kbd.c \ ps2mouse.c \ rfb.c \ rtc.c \ spinup_ap.c \ task_switch.c \ vga.c \ xmsr.c .PATH: ${BHYVE_SYSDIR}/sys/amd64/vmm SRCS+= vmm_instruction_emul.c CFLAGS.kernemu_dev.c+= -I${SRCTOP}/sys/amd64 SUBDIR+= kbdlayout diff --git a/usr.sbin/bhyve/mem.c b/usr.sbin/bhyve/mem.c index b0c09dbf5ddf..2d7d012d084b 100644 --- a/usr.sbin/bhyve/mem.c +++ b/usr.sbin/bhyve/mem.c @@ -1,372 +1,384 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2012 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. */ /* * Memory ranges are represented with an RB tree. On insertion, the range * is checked for overlaps. On lookup, the key has the same base and limit * so it can be searched within the range. */ #include +#define _WANT_KERNEL_ERRNO 1 #include #include #include #include #include #include #include #include #include #include #include "mem.h" struct mmio_rb_range { RB_ENTRY(mmio_rb_range) mr_link; /* RB tree links */ struct mem_range mr_param; uint64_t mr_base; uint64_t mr_end; }; struct mmio_rb_tree; RB_PROTOTYPE(mmio_rb_tree, mmio_rb_range, mr_link, mmio_rb_range_compare); static RB_HEAD(mmio_rb_tree, mmio_rb_range) mmio_rb_root, mmio_rb_fallback; /* * Per-vCPU cache. Since most accesses from a vCPU will be to * consecutive addresses in a range, it makes sense to cache the * result of a lookup. */ static struct mmio_rb_range **mmio_hint; static int mmio_ncpu; static pthread_rwlock_t mmio_rwlock; static int mmio_rb_range_compare(struct mmio_rb_range *a, struct mmio_rb_range *b) { if (a->mr_end < b->mr_base) return (-1); else if (a->mr_base > b->mr_end) return (1); return (0); } static int mmio_rb_lookup(struct mmio_rb_tree *rbt, uint64_t addr, struct mmio_rb_range **entry) { struct mmio_rb_range find, *res; find.mr_base = find.mr_end = addr; res = RB_FIND(mmio_rb_tree, rbt, &find); if (res != NULL) { *entry = res; return (0); } return (ENOENT); } static int mmio_rb_add(struct mmio_rb_tree *rbt, struct mmio_rb_range *new) { struct mmio_rb_range *overlap; overlap = RB_INSERT(mmio_rb_tree, rbt, new); if (overlap != NULL) { #ifdef RB_DEBUG printf("overlap detected: new %lx:%lx, tree %lx:%lx, '%s' " "claims region already claimed for '%s'\n", new->mr_base, new->mr_end, overlap->mr_base, overlap->mr_end, new->mr_param.name, overlap->mr_param.name); #endif return (EEXIST); } return (0); } #if 0 static void mmio_rb_dump(struct mmio_rb_tree *rbt) { int perror; struct mmio_rb_range *np; pthread_rwlock_rdlock(&mmio_rwlock); RB_FOREACH(np, mmio_rb_tree, rbt) { printf(" %lx:%lx, %s\n", np->mr_base, np->mr_end, np->mr_param.name); } perror = pthread_rwlock_unlock(&mmio_rwlock); assert(perror == 0); } #endif RB_GENERATE(mmio_rb_tree, mmio_rb_range, mr_link, mmio_rb_range_compare); typedef int (mem_cb_t)(struct vcpu *vcpu, uint64_t gpa, struct mem_range *mr, void *arg); static int mem_read(struct vcpu *vcpu, uint64_t gpa, uint64_t *rval, int size, void *arg) { int error; struct mem_range *mr = arg; error = (*mr->handler)(vcpu, MEM_F_READ, gpa, size, rval, mr->arg1, mr->arg2); return (error); } static int mem_write(struct vcpu *vcpu, uint64_t gpa, uint64_t wval, int size, void *arg) { int error; struct mem_range *mr = arg; error = (*mr->handler)(vcpu, MEM_F_WRITE, gpa, size, &wval, mr->arg1, mr->arg2); return (error); } static int access_memory(struct vcpu *vcpu, uint64_t paddr, mem_cb_t *cb, void *arg) { struct mmio_rb_range *entry; + struct mem_range *mr; int err, perror, immutable, vcpuid; vcpuid = vcpu_id(vcpu); + mr = NULL; pthread_rwlock_rdlock(&mmio_rwlock); + /* * First check the per-vCPU cache */ if (mmio_hint[vcpuid] && paddr >= mmio_hint[vcpuid]->mr_base && paddr <= mmio_hint[vcpuid]->mr_end) { entry = mmio_hint[vcpuid]; } else entry = NULL; if (entry == NULL) { if (mmio_rb_lookup(&mmio_rb_root, paddr, &entry) == 0) { /* Update the per-vCPU cache */ mmio_hint[vcpuid] = entry; - } else if (mmio_rb_lookup(&mmio_rb_fallback, paddr, &entry)) { - perror = pthread_rwlock_unlock(&mmio_rwlock); - assert(perror == 0); - return (ESRCH); + } else if (mmio_rb_lookup(&mmio_rb_fallback, paddr, + &entry) == 0) { + } else { + err = mmio_handle_non_backed_mem(vcpu, paddr, &mr); + if (err != 0) { + perror = pthread_rwlock_unlock(&mmio_rwlock); + assert(perror == 0); + return (err == EJUSTRETURN ? 0 : err); + } } } - assert(entry != NULL); + if (mr == NULL) { + assert(entry != NULL); + mr = &entry->mr_param; + } /* * An 'immutable' memory range is guaranteed to be never removed * so there is no need to hold 'mmio_rwlock' while calling the * handler. * * XXX writes to the PCIR_COMMAND register can cause register_mem() * to be called. If the guest is using PCI extended config space * to modify the PCIR_COMMAND register then register_mem() can * deadlock on 'mmio_rwlock'. However by registering the extended * config space window as 'immutable' the deadlock can be avoided. */ - immutable = (entry->mr_param.flags & MEM_F_IMMUTABLE); + immutable = (mr->flags & MEM_F_IMMUTABLE) != 0; if (immutable) { perror = pthread_rwlock_unlock(&mmio_rwlock); assert(perror == 0); } - err = cb(vcpu, paddr, &entry->mr_param, arg); + err = cb(vcpu, paddr, mr, arg); if (!immutable) { perror = pthread_rwlock_unlock(&mmio_rwlock); assert(perror == 0); } return (err); } struct emulate_mem_args { struct vie *vie; struct vm_guest_paging *paging; }; static int emulate_mem_cb(struct vcpu *vcpu, uint64_t paddr, struct mem_range *mr, void *arg) { struct emulate_mem_args *ema; ema = arg; return (vmm_emulate_instruction(vcpu, paddr, ema->vie, ema->paging, mem_read, mem_write, mr)); } int emulate_mem(struct vcpu *vcpu, uint64_t paddr, struct vie *vie, struct vm_guest_paging *paging) { struct emulate_mem_args ema; ema.vie = vie; ema.paging = paging; return (access_memory(vcpu, paddr, emulate_mem_cb, &ema)); } struct rw_mem_args { uint64_t *val; int size; int operation; }; static int rw_mem_cb(struct vcpu *vcpu, uint64_t paddr, struct mem_range *mr, void *arg) { struct rw_mem_args *rma; rma = arg; return (mr->handler(vcpu, rma->operation, paddr, rma->size, rma->val, mr->arg1, mr->arg2)); } int read_mem(struct vcpu *vcpu, uint64_t gpa, uint64_t *rval, int size) { struct rw_mem_args rma; rma.val = rval; rma.size = size; rma.operation = MEM_F_READ; return (access_memory(vcpu, gpa, rw_mem_cb, &rma)); } int write_mem(struct vcpu *vcpu, uint64_t gpa, uint64_t wval, int size) { struct rw_mem_args rma; rma.val = &wval; rma.size = size; rma.operation = MEM_F_WRITE; return (access_memory(vcpu, gpa, rw_mem_cb, &rma)); } static int register_mem_int(struct mmio_rb_tree *rbt, struct mem_range *memp) { struct mmio_rb_range *entry, *mrp; int err, perror; err = 0; mrp = malloc(sizeof(struct mmio_rb_range)); if (mrp == NULL) { warn("%s: couldn't allocate memory for mrp\n", __func__); err = ENOMEM; } else { mrp->mr_param = *memp; mrp->mr_base = memp->base; mrp->mr_end = memp->base + memp->size - 1; pthread_rwlock_wrlock(&mmio_rwlock); if (mmio_rb_lookup(rbt, memp->base, &entry) != 0) err = mmio_rb_add(rbt, mrp); perror = pthread_rwlock_unlock(&mmio_rwlock); assert(perror == 0); if (err) free(mrp); } return (err); } int register_mem(struct mem_range *memp) { return (register_mem_int(&mmio_rb_root, memp)); } int register_mem_fallback(struct mem_range *memp) { return (register_mem_int(&mmio_rb_fallback, memp)); } int unregister_mem(struct mem_range *memp) { struct mem_range *mr; struct mmio_rb_range *entry = NULL; int err, perror, i; pthread_rwlock_wrlock(&mmio_rwlock); err = mmio_rb_lookup(&mmio_rb_root, memp->base, &entry); if (err == 0) { mr = &entry->mr_param; assert(mr->name == memp->name); assert(mr->base == memp->base && mr->size == memp->size); assert((mr->flags & MEM_F_IMMUTABLE) == 0); RB_REMOVE(mmio_rb_tree, &mmio_rb_root, entry); /* flush Per-vCPU cache */ for (i = 0; i < mmio_ncpu; i++) { if (mmio_hint[i] == entry) mmio_hint[i] = NULL; } } perror = pthread_rwlock_unlock(&mmio_rwlock); assert(perror == 0); if (entry) free(entry); return (err); } void init_mem(int ncpu) { mmio_ncpu = ncpu; mmio_hint = calloc(ncpu, sizeof(*mmio_hint)); RB_INIT(&mmio_rb_root); RB_INIT(&mmio_rb_fallback); pthread_rwlock_init(&mmio_rwlock, NULL); } diff --git a/usr.sbin/bhyve/mem.h b/usr.sbin/bhyve/mem.h index 90172d1c9124..d7544a6bce30 100644 --- a/usr.sbin/bhyve/mem.h +++ b/usr.sbin/bhyve/mem.h @@ -1,63 +1,65 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2012 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. */ #ifndef _MEM_H_ #define _MEM_H_ #include struct vcpu; typedef int (*mem_func_t)(struct vcpu *vcpu, int dir, uint64_t addr, int size, uint64_t *val, void *arg1, long arg2); struct mem_range { const char *name; int flags; mem_func_t handler; void *arg1; long arg2; uint64_t base; uint64_t size; }; #define MEM_F_READ 0x1 #define MEM_F_WRITE 0x2 #define MEM_F_RW 0x3 #define MEM_F_IMMUTABLE 0x4 /* mem_range cannot be unregistered */ void init_mem(int ncpu); int emulate_mem(struct vcpu *vcpu, uint64_t paddr, struct vie *vie, struct vm_guest_paging *paging); +int mmio_handle_non_backed_mem(struct vcpu *vcpu __unused, uint64_t paddr, + struct mem_range **mr_paramp); int read_mem(struct vcpu *vpu, uint64_t gpa, uint64_t *rval, int size); int register_mem(struct mem_range *memp); int register_mem_fallback(struct mem_range *memp); int unregister_mem(struct mem_range *memp); int write_mem(struct vcpu *vcpu, uint64_t gpa, uint64_t wval, int size); #endif /* _MEM_H_ */ diff --git a/usr.sbin/bhyve/mem.h b/usr.sbin/bhyve/mem_md.c similarity index 51% copy from usr.sbin/bhyve/mem.h copy to usr.sbin/bhyve/mem_md.c index 90172d1c9124..0e29ec0a9de9 100644 --- a/usr.sbin/bhyve/mem.h +++ b/usr.sbin/bhyve/mem_md.c @@ -1,63 +1,44 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (c) 2012 NetApp, Inc. - * All rights reserved. + * Copyright (c) 2025 The FreeBSD Foundation + * + * This software was developed by Konstantin Belousov + * 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 NETAPP, INC ``AS IS'' AND + * 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 NETAPP, INC OR CONTRIBUTORS BE LIABLE + * 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 _MEM_H_ -#define _MEM_H_ - -#include - -struct vcpu; - -typedef int (*mem_func_t)(struct vcpu *vcpu, int dir, uint64_t addr, - int size, uint64_t *val, void *arg1, long arg2); - -struct mem_range { - const char *name; - int flags; - mem_func_t handler; - void *arg1; - long arg2; - uint64_t base; - uint64_t size; -}; -#define MEM_F_READ 0x1 -#define MEM_F_WRITE 0x2 -#define MEM_F_RW 0x3 -#define MEM_F_IMMUTABLE 0x4 /* mem_range cannot be unregistered */ - -void init_mem(int ncpu); -int emulate_mem(struct vcpu *vcpu, uint64_t paddr, struct vie *vie, - struct vm_guest_paging *paging); +#include +#include +#include +#include +#include -int read_mem(struct vcpu *vpu, uint64_t gpa, uint64_t *rval, int size); -int register_mem(struct mem_range *memp); -int register_mem_fallback(struct mem_range *memp); -int unregister_mem(struct mem_range *memp); -int write_mem(struct vcpu *vcpu, uint64_t gpa, uint64_t wval, int size); +#include "mem.h" -#endif /* _MEM_H_ */ +int +mmio_handle_non_backed_mem(struct vcpu *vcpu __unused, uint64_t paddr __unused, + struct mem_range **mr_paramp __unused) +{ + return (ESRCH); +} diff --git a/usr.sbin/bhyve/riscv/Makefile.inc b/usr.sbin/bhyve/riscv/Makefile.inc index 2b57201c6259..591bd119d5d1 100644 --- a/usr.sbin/bhyve/riscv/Makefile.inc +++ b/usr.sbin/bhyve/riscv/Makefile.inc @@ -1,7 +1,8 @@ SRCS+= \ - fdt.c + fdt.c \ + mem_md.c .PATH: ${BHYVE_SYSDIR}/sys/riscv/vmm SRCS+= vmm_instruction_emul.c BHYVE_FDT_SUPPORT=