diff options
Diffstat (limited to 'usr')
26 files changed, 625 insertions, 251 deletions
diff --git a/usr/src/cmd/bhyve/acpi.h b/usr/src/cmd/bhyve/acpi.h index 50e5337f33..50cbda9639 100644 --- a/usr/src/cmd/bhyve/acpi.h +++ b/usr/src/cmd/bhyve/acpi.h @@ -62,5 +62,8 @@ void dsdt_fixed_mem32(uint32_t base, uint32_t length); void dsdt_indent(int levels); void dsdt_unindent(int levels); void sci_init(struct vmctx *ctx); +#ifndef __FreeBSD__ +void pmtmr_init(struct vmctx *ctx); +#endif #endif /* _ACPI_H_ */ diff --git a/usr/src/cmd/bhyve/bhyverun.c b/usr/src/cmd/bhyve/bhyverun.c index c4ebef1b31..4392f8640b 100644 --- a/usr/src/cmd/bhyve/bhyverun.c +++ b/usr/src/cmd/bhyve/bhyverun.c @@ -1451,6 +1451,9 @@ main(int argc, char *argv[]) rtc_init(ctx, rtc_localtime); sci_init(ctx); +#ifndef __FreeBSD__ + pmtmr_init(ctx); +#endif /* * Exit if a device emulation finds an error in its initilization diff --git a/usr/src/cmd/bhyve/pm.c b/usr/src/cmd/bhyve/pm.c index c57da7fd74..fa162faab1 100644 --- a/usr/src/cmd/bhyve/pm.c +++ b/usr/src/cmd/bhyve/pm.c @@ -28,6 +28,7 @@ */ /* * Copyright 2018 Joyent, Inc. + * Copyright 2020 Oxide Computer Company */ #include <sys/cdefs.h> @@ -441,3 +442,14 @@ sci_init(struct vmctx *ctx) } #endif } + +#ifndef __FreeBSD__ +void pmtmr_init(struct vmctx *ctx) +{ + int err; + + /* Attach in-kernel PM timer emulation to correct IO port */ + err = vm_pmtmr_set_location(ctx, IO_PMTMR); + assert(err == 0); +} +#endif diff --git a/usr/src/cmd/bhyvectl/bhyvectl.c b/usr/src/cmd/bhyvectl/bhyvectl.c index 7f8847b184..2948c14ebb 100644 --- a/usr/src/cmd/bhyvectl/bhyvectl.c +++ b/usr/src/cmd/bhyvectl/bhyvectl.c @@ -93,6 +93,7 @@ usage(bool cpu_intel) " [--create]\n" " [--destroy]\n" #ifndef __FreeBSD__ + " [--pmtmr-port=ioport]\n" " [--wrlock-cycle]\n" #endif " [--get-all]\n" @@ -309,6 +310,7 @@ static int unassign_pptdev, bus, slot, func; static int run; static int get_cpu_topology; #ifndef __FreeBSD__ +static int pmtmr_port; static int wrlock_cycle; #endif @@ -655,6 +657,9 @@ enum { SET_RTC_TIME, SET_RTC_NVRAM, RTC_NVRAM_OFFSET, +#ifndef __FreeBSD__ + PMTMR_PORT, +#endif }; static void @@ -1527,6 +1532,7 @@ setup_options(bool cpu_intel) { "get-intinfo", NO_ARG, &get_intinfo, 1 }, { "get-cpu-topology", NO_ARG, &get_cpu_topology, 1 }, #ifndef __FreeBSD__ + { "pmtmr-port", REQ_ARG, 0, PMTMR_PORT }, { "wrlock-cycle", NO_ARG, &wrlock_cycle, 1 }, #endif }; @@ -1930,6 +1936,11 @@ main(int argc, char *argv[]) case ASSERT_LAPIC_LVT: assert_lapic_lvt = atoi(optarg); break; +#ifndef __FreeBSD__ + case PMTMR_PORT: + pmtmr_port = strtoul(optarg, NULL, 16); + break; +#endif default: usage(cpu_intel); } @@ -1954,6 +1965,10 @@ main(int argc, char *argv[]) } #ifndef __FreeBSD__ + if (!error && pmtmr_port) { + error = vm_pmtmr_set_location(ctx, pmtmr_port); + exit(error); + } if (!error && wrlock_cycle) { error = vm_wrlock_cycle(ctx); exit(error); diff --git a/usr/src/lib/libvmmapi/common/mapfile-vers b/usr/src/lib/libvmmapi/common/mapfile-vers index 56eb5a7b95..9e972b8d60 100644 --- a/usr/src/lib/libvmmapi/common/mapfile-vers +++ b/usr/src/lib/libvmmapi/common/mapfile-vers @@ -121,6 +121,7 @@ SYMBOL_VERSION ILLUMOSprivate { vm_suspended_cpus; vm_resume_cpu; vm_unassign_pptdev; + vm_pmtmr_set_location; vm_wrlock_cycle; local: diff --git a/usr/src/lib/libvmmapi/common/vmmapi.c b/usr/src/lib/libvmmapi/common/vmmapi.c index 6d5145431e..5b2cb4c235 100644 --- a/usr/src/lib/libvmmapi/common/vmmapi.c +++ b/usr/src/lib/libvmmapi/common/vmmapi.c @@ -1826,6 +1826,12 @@ vm_get_device_fd(struct vmctx *ctx) #ifndef __FreeBSD__ int +vm_pmtmr_set_location(struct vmctx *ctx, uint16_t ioport) +{ + return (ioctl(ctx->fd, VM_PMTMR_LOCATE, ioport)); +} + +int vm_wrlock_cycle(struct vmctx *ctx) { if (ioctl(ctx->fd, VM_WRLOCK_CYCLE, 0) != 0) { diff --git a/usr/src/lib/libvmmapi/common/vmmapi.h b/usr/src/lib/libvmmapi/common/vmmapi.h index 4656f417b4..96102ec925 100644 --- a/usr/src/lib/libvmmapi/common/vmmapi.h +++ b/usr/src/lib/libvmmapi/common/vmmapi.h @@ -39,6 +39,7 @@ * * Copyright 2015 Pluribus Networks Inc. * Copyright 2019 Joyent, Inc. + * Copyright 2020 Oxide Computer Company */ #ifndef _VMMAPI_H_ @@ -301,6 +302,7 @@ int vm_get_topology(struct vmctx *ctx, uint16_t *sockets, uint16_t *cores, #ifndef __FreeBSD__ /* illumos-specific APIs */ +int vm_pmtmr_set_location(struct vmctx *ctx, uint16_t ioport); int vm_wrlock_cycle(struct vmctx *ctx); #endif /* __FreeBSD__ */ diff --git a/usr/src/uts/common/io/tem_safe.c b/usr/src/uts/common/io/tem_safe.c index 8d47a00d5f..8764c5764e 100644 --- a/usr/src/uts/common/io/tem_safe.c +++ b/usr/src/uts/common/io/tem_safe.c @@ -518,33 +518,41 @@ tem_safe_setparam(struct tem_vt_state *tem, int count, int newparam) } } - +/* + * For colors 0-15 the tem is using color code translation + * from sun colors to vga (dim_xlate and brt_xlate tables, see tem_get_color). + * Colors 16-255 are used without translation. + */ static void tem_select_color(struct tem_vt_state *tem, text_color_t color, boolean_t fg) { - if (tems.ts_pdepth >= 24 || - (color < 8 && tems.ts_pdepth < 24)) { - if (fg == B_TRUE) { - tem->tvs_fg_color = color; + if (fg == B_TRUE) + tem->tvs_fg_color = color; + else + tem->tvs_bg_color = color; + + /* + * For colors 0-7, make sure the BRIGHT attribute is not set. + */ + if (color < 8) { + if (fg == B_TRUE) tem->tvs_flags &= ~TEM_ATTR_BRIGHT_FG; - } else { - tem->tvs_bg_color = color; + else tem->tvs_flags &= ~TEM_ATTR_BRIGHT_BG; - } return; } - if (color > 15) - return; - - /* Bright color and depth < 24 */ - color -= 8; - if (fg == B_TRUE) { - tem->tvs_fg_color = color; - tem->tvs_flags |= TEM_ATTR_BRIGHT_FG; - } else { - tem->tvs_bg_color = color; - tem->tvs_flags |= TEM_ATTR_BRIGHT_BG; + /* + * For colors 8-15, we use color codes 0-7 and set BRIGHT attribute. + */ + if (color < 16) { + if (fg == B_TRUE) { + tem->tvs_fg_color -= 8; + tem->tvs_flags |= TEM_ATTR_BRIGHT_FG; + } else { + tem->tvs_bg_color -= 8; + tem->tvs_flags |= TEM_ATTR_BRIGHT_BG; + } } } diff --git a/usr/src/uts/i86pc/io/viona/viona_impl.h b/usr/src/uts/i86pc/io/viona/viona_impl.h index 5471b611a4..3fa299eafa 100644 --- a/usr/src/uts/i86pc/io/viona/viona_impl.h +++ b/usr/src/uts/i86pc/io/viona/viona_impl.h @@ -35,6 +35,7 @@ * * Copyright 2015 Pluribus Networks Inc. * Copyright 2019 Joyent, Inc. + * Copyright 2020 Oxide Computer Company */ #ifndef _VIONA_IMPL_H @@ -158,7 +159,7 @@ struct viona_link { uint32_t l_features_hw; uint32_t l_cap_csum; - uintptr_t l_notify_ioport; + uint16_t l_notify_ioport; void *l_notify_cookie; datalink_id_t l_linkid; diff --git a/usr/src/uts/i86pc/io/viona/viona_main.c b/usr/src/uts/i86pc/io/viona/viona_main.c index f51a1f9b12..0d16eb1205 100644 --- a/usr/src/uts/i86pc/io/viona/viona_main.c +++ b/usr/src/uts/i86pc/io/viona/viona_main.c @@ -35,6 +35,7 @@ * * Copyright 2015 Pluribus Networks Inc. * Copyright 2019 Joyent, Inc. + * Copyright 2020 Oxide Computer Company */ /* @@ -283,7 +284,7 @@ static int viona_chpoll(dev_t dev, short events, int anyyet, short *reventsp, static int viona_ioc_create(viona_soft_state_t *, void *, int, cred_t *); static int viona_ioc_delete(viona_soft_state_t *, boolean_t); -static int viona_ioc_set_notify_ioport(viona_link_t *, uint_t); +static int viona_ioc_set_notify_ioport(viona_link_t *, uint16_t); static int viona_ioc_ring_init(viona_link_t *, void *, int); static int viona_ioc_ring_reset(viona_link_t *, uint_t); static int viona_ioc_ring_kick(viona_link_t *, uint_t); @@ -581,7 +582,11 @@ viona_ioctl(dev_t dev, int cmd, intptr_t data, int md, cred_t *cr, int *rv) err = viona_ioc_intr_poll(link, dptr, md, rv); break; case VNA_IOC_SET_NOTIFY_IOP: - err = viona_ioc_set_notify_ioport(link, (uint_t)data); + if (data < 0 || data > UINT16_MAX) { + err = EINVAL; + break; + } + err = viona_ioc_set_notify_ioport(link, (uint16_t)data); break; default: err = ENOTTY; @@ -926,19 +931,29 @@ viona_ioc_ring_set_msi(viona_link_t *link, void *data, int md) } static int -viona_notify_wcb(void *arg, uintptr_t ioport, uint_t sz, uint64_t val) +viona_notify_iop(void *arg, bool in, uint16_t port, uint8_t bytes, + uint32_t *val) { viona_link_t *link = (viona_link_t *)arg; - uint16_t vq = (uint16_t)val; + uint16_t vq = *val; + + if (in) { + /* + * Do not service read (in/ins) requests on this ioport. + * Instead, indicate that the handler is not found, causing a + * fallback to userspace processing. + */ + return (ESRCH); + } - if (ioport != link->l_notify_ioport || sz != sizeof (uint16_t)) { + if (port != link->l_notify_ioport) { return (EINVAL); } return (viona_ioc_ring_kick(link, vq)); } static int -viona_ioc_set_notify_ioport(viona_link_t *link, uint_t ioport) +viona_ioc_set_notify_ioport(viona_link_t *link, uint16_t ioport) { int err = 0; @@ -948,8 +963,8 @@ viona_ioc_set_notify_ioport(viona_link_t *link, uint_t ioport) } if (ioport != 0) { - err = vmm_drv_ioport_hook(link->l_vm_hold, ioport, NULL, - viona_notify_wcb, (void *)link, &link->l_notify_cookie); + err = vmm_drv_ioport_hook(link->l_vm_hold, ioport, + viona_notify_iop, (void *)link, &link->l_notify_cookie); if (err == 0) { link->l_notify_ioport = ioport; } diff --git a/usr/src/uts/i86pc/io/vmm/io/vatpic.c b/usr/src/uts/i86pc/io/vmm/io/vatpic.c index b81259647c..627f4ed14e 100644 --- a/usr/src/uts/i86pc/io/vmm/io/vatpic.c +++ b/usr/src/uts/i86pc/io/vmm/io/vatpic.c @@ -709,14 +709,11 @@ vatpic_write(struct vatpic *vatpic, struct atpic *atpic, bool in, int port, } int -vatpic_master_handler(struct vm *vm, int vcpuid, bool in, uint16_t port, - uint8_t bytes, uint32_t *eax) +vatpic_master_handler(void *arg, bool in, uint16_t port, uint8_t bytes, + uint32_t *eax) { - struct vatpic *vatpic; - struct atpic *atpic; - - vatpic = vm_atpic(vm); - atpic = &vatpic->atpic[0]; + struct vatpic *vatpic = arg; + struct atpic *atpic = &vatpic->atpic[0]; if (bytes != 1) return (-1); @@ -729,14 +726,11 @@ vatpic_master_handler(struct vm *vm, int vcpuid, bool in, uint16_t port, } int -vatpic_slave_handler(struct vm *vm, int vcpuid, bool in, uint16_t port, - uint8_t bytes, uint32_t *eax) +vatpic_slave_handler(void *arg, bool in, uint16_t port, uint8_t bytes, + uint32_t *eax) { - struct vatpic *vatpic; - struct atpic *atpic; - - vatpic = vm_atpic(vm); - atpic = &vatpic->atpic[1]; + struct vatpic *vatpic = arg; + struct atpic *atpic = &vatpic->atpic[1]; if (bytes != 1) return (-1); @@ -749,13 +743,12 @@ vatpic_slave_handler(struct vm *vm, int vcpuid, bool in, uint16_t port, } int -vatpic_elc_handler(struct vm *vm, int vcpuid, bool in, uint16_t port, - uint8_t bytes, uint32_t *eax) +vatpic_elc_handler(void *arg, bool in, uint16_t port, uint8_t bytes, + uint32_t *eax) { - struct vatpic *vatpic; + struct vatpic *vatpic = arg; bool is_master; - vatpic = vm_atpic(vm); is_master = (port == IO_ELCR1); if (bytes != 1) diff --git a/usr/src/uts/i86pc/io/vmm/io/vatpic.h b/usr/src/uts/i86pc/io/vmm/io/vatpic.h index dcb8ea6c6f..4518df34a5 100644 --- a/usr/src/uts/i86pc/io/vmm/io/vatpic.h +++ b/usr/src/uts/i86pc/io/vmm/io/vatpic.h @@ -39,12 +39,12 @@ struct vatpic *vatpic_init(struct vm *vm); void vatpic_cleanup(struct vatpic *vatpic); -int vatpic_master_handler(struct vm *vm, int vcpuid, bool in, uint16_t port, - uint8_t bytes, uint32_t *eax); -int vatpic_slave_handler(struct vm *vm, int vcpuid, bool in, uint16_t port, - uint8_t bytes, uint32_t *eax); -int vatpic_elc_handler(struct vm *vm, int vcpuid, bool in, uint16_t port, - uint8_t bytes, uint32_t *eax); +int vatpic_master_handler(void *arg, bool in, uint16_t port, uint8_t bytes, + uint32_t *eax); +int vatpic_slave_handler(void *arg, bool in, uint16_t port, uint8_t bytes, + uint32_t *eax); +int vatpic_elc_handler(void *arg, bool in, uint16_t port, uint8_t bytes, + uint32_t *eax); int vatpic_assert_irq(struct vm *vm, int irq); int vatpic_deassert_irq(struct vm *vm, int irq); diff --git a/usr/src/uts/i86pc/io/vmm/io/vatpit.c b/usr/src/uts/i86pc/io/vmm/io/vatpit.c index 47cb40f9bd..7d68bc659d 100644 --- a/usr/src/uts/i86pc/io/vmm/io/vatpit.c +++ b/usr/src/uts/i86pc/io/vmm/io/vatpit.c @@ -336,16 +336,13 @@ vatpit_update_mode(struct vatpit *vatpit, uint8_t val) } int -vatpit_handler(struct vm *vm, int vcpuid, bool in, uint16_t port, uint8_t bytes, - uint32_t *eax) +vatpit_handler(void *arg, bool in, uint16_t port, uint8_t bytes, uint32_t *eax) { - struct vatpit *vatpit; + struct vatpit *vatpit = arg; struct channel *c; uint8_t val; int error; - vatpit = vm_atpit(vm); - if (bytes != 1) return (-1); @@ -419,12 +416,10 @@ vatpit_handler(struct vm *vm, int vcpuid, bool in, uint16_t port, uint8_t bytes, } int -vatpit_nmisc_handler(struct vm *vm, int vcpuid, bool in, uint16_t port, - uint8_t bytes, uint32_t *eax) +vatpit_nmisc_handler(void *arg, bool in, uint16_t port, uint8_t bytes, + uint32_t *eax) { - struct vatpit *vatpit; - - vatpit = vm_atpit(vm); + struct vatpit *vatpit = arg; if (in) { VATPIT_LOCK(vatpit); diff --git a/usr/src/uts/i86pc/io/vmm/io/vatpit.h b/usr/src/uts/i86pc/io/vmm/io/vatpit.h index 512ce20735..9148326dd3 100644 --- a/usr/src/uts/i86pc/io/vmm/io/vatpit.h +++ b/usr/src/uts/i86pc/io/vmm/io/vatpit.h @@ -39,10 +39,10 @@ struct vatpit *vatpit_init(struct vm *vm); void vatpit_cleanup(struct vatpit *vatpit); -int vatpit_handler(struct vm *vm, int vcpuid, bool in, uint16_t port, - uint8_t bytes, uint32_t *eax); -int vatpit_nmisc_handler(struct vm *vm, int vcpuid, bool in, uint16_t port, - uint8_t bytes, uint32_t *eax); +int vatpit_handler(void *arg, bool in, uint16_t port, uint8_t bytes, + uint32_t *eax); +int vatpit_nmisc_handler(void *arg, bool in, uint16_t port, uint8_t bytes, + uint32_t *eax); #ifndef __FreeBSD__ void vatpit_localize_resources(struct vatpit *); diff --git a/usr/src/uts/i86pc/io/vmm/io/vpmtmr.c b/usr/src/uts/i86pc/io/vmm/io/vpmtmr.c index 6664cb06e7..e49f583772 100644 --- a/usr/src/uts/i86pc/io/vmm/io/vpmtmr.c +++ b/usr/src/uts/i86pc/io/vmm/io/vpmtmr.c @@ -25,6 +25,18 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + * + * Copyright 2020 Oxide Computer Company + */ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); @@ -49,6 +61,9 @@ __FBSDID("$FreeBSD$"); #define PMTMR_FREQ 3579545 /* 3.579545MHz */ struct vpmtmr { + struct vm *vm; + void *io_cookie; + uint16_t io_port; sbintime_t freq_sbt; sbintime_t baseuptime; uint32_t baseval; @@ -80,17 +95,48 @@ vpmtmr_cleanup(struct vpmtmr *vpmtmr) } int -vpmtmr_handler(struct vm *vm, int vcpuid, bool in, uint16_t port, uint8_t bytes, - uint32_t *val) +vpmtmr_set_location(struct vm *vm, uint16_t ioport) { - struct vpmtmr *vpmtmr; + struct vpmtmr *vpmtmr = vm_pmtmr(vm); + int err; + + if (vpmtmr->io_cookie != NULL) { + ioport_handler_t old_func; + void *old_arg; + + if (vpmtmr->io_port == ioport) { + /* already attached in the right place */ + return (0); + } + + err = vm_ioport_detach(vm, &vpmtmr->io_cookie, &old_func, + &old_arg); + if (err != 0) { + return (err); + } + + ASSERT3P(old_func, ==, vpmtmr_handler); + ASSERT3P(old_arg, ==, vpmtmr); + vpmtmr->io_port = 0; + } + err = vm_ioport_attach(vm, ioport, vpmtmr_handler, vpmtmr, + &vpmtmr->io_cookie); + if (err == 0) { + vpmtmr->io_port = ioport; + } + + return (err); +} + +int +vpmtmr_handler(void *arg, bool in, uint16_t port, uint8_t bytes, uint32_t *val) +{ + struct vpmtmr *vpmtmr = arg; sbintime_t now, delta; if (!in || bytes != 4) return (-1); - vpmtmr = vm_pmtmr(vm); - /* * No locking needed because 'baseuptime' and 'baseval' are * written only during initialization. diff --git a/usr/src/uts/i86pc/io/vmm/io/vpmtmr.h b/usr/src/uts/i86pc/io/vmm/io/vpmtmr.h index c06825b970..0451da0350 100644 --- a/usr/src/uts/i86pc/io/vmm/io/vpmtmr.h +++ b/usr/src/uts/i86pc/io/vmm/io/vpmtmr.h @@ -27,6 +27,18 @@ * * $FreeBSD$ */ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + * + * Copyright 2020 Oxide Computer Company + */ #ifndef _VPMTMR_H_ #define _VPMTMR_H_ @@ -38,7 +50,9 @@ struct vpmtmr; struct vpmtmr *vpmtmr_init(struct vm *vm); void vpmtmr_cleanup(struct vpmtmr *pmtmr); -int vpmtmr_handler(struct vm *vm, int vcpuid, bool in, uint16_t port, - uint8_t bytes, uint32_t *val); +int vpmtmr_set_location(struct vm *, uint16_t); + +int vpmtmr_handler(void *arg, bool in, uint16_t port, uint8_t bytes, + uint32_t *val); #endif diff --git a/usr/src/uts/i86pc/io/vmm/io/vrtc.c b/usr/src/uts/i86pc/io/vmm/io/vrtc.c index e560ce9b7f..2f87446bdc 100644 --- a/usr/src/uts/i86pc/io/vmm/io/vrtc.c +++ b/usr/src/uts/i86pc/io/vmm/io/vrtc.c @@ -833,12 +833,10 @@ vrtc_nvram_read(struct vm *vm, int offset, uint8_t *retval) } int -vrtc_addr_handler(struct vm *vm, int vcpuid, bool in, uint16_t port, - uint8_t bytes, uint32_t *val) +vrtc_addr_handler(void *arg, bool in, uint16_t port, uint8_t bytes, + uint32_t *val) { - struct vrtc *vrtc; - - vrtc = vm_rtc(vm); + struct vrtc *vrtc = arg; if (bytes != 1) return (-1); @@ -856,18 +854,15 @@ vrtc_addr_handler(struct vm *vm, int vcpuid, bool in, uint16_t port, } int -vrtc_data_handler(struct vm *vm, int vcpuid, bool in, uint16_t port, - uint8_t bytes, uint32_t *val) +vrtc_data_handler(void *arg, bool in, uint16_t port, uint8_t bytes, + uint32_t *val) { - struct vrtc *vrtc; - struct rtcdev *rtc; + struct vrtc *vrtc = arg; + struct rtcdev *rtc = &vrtc->rtcdev; sbintime_t basetime; time_t curtime; int error, offset; - vrtc = vm_rtc(vm); - rtc = &vrtc->rtcdev; - if (bytes != 1) return (-1); @@ -904,24 +899,24 @@ vrtc_data_handler(struct vm *vm, int vcpuid, bool in, uint16_t port, } else { *val = *((uint8_t *)rtc + offset); } - VCPU_CTR2(vm, vcpuid, "Read value %x from RTC offset %x", + VM_CTR2(vm, "Read value %x from RTC offset %x", *val, offset); } else { switch (offset) { case 10: - VCPU_CTR1(vm, vcpuid, "RTC reg_a set to %x", *val); + VM_CTR1(vm, "RTC reg_a set to %x", *val); vrtc_set_reg_a(vrtc, *val); break; case 11: - VCPU_CTR1(vm, vcpuid, "RTC reg_b set to %x", *val); + VM_CTR1(vm, "RTC reg_b set to %x", *val); error = vrtc_set_reg_b(vrtc, *val); break; case 12: - VCPU_CTR1(vm, vcpuid, "RTC reg_c set to %x (ignored)", + VM_CTR1(vm, "RTC reg_c set to %x (ignored)", *val); break; case 13: - VCPU_CTR1(vm, vcpuid, "RTC reg_d set to %x (ignored)", + VM_CTR1(vm, "RTC reg_d set to %x (ignored)", *val); break; case 0: @@ -931,8 +926,7 @@ vrtc_data_handler(struct vm *vm, int vcpuid, bool in, uint16_t port, *val &= 0x7f; /* FALLTHRU */ default: - VCPU_CTR2(vm, vcpuid, "RTC offset %x set to %x", - offset, *val); + VM_CTR2(vm, "RTC offset %x set to %x", offset, *val); *((uint8_t *)rtc + offset) = *val; break; } diff --git a/usr/src/uts/i86pc/io/vmm/io/vrtc.h b/usr/src/uts/i86pc/io/vmm/io/vrtc.h index 92a060cb8e..3fc4094921 100644 --- a/usr/src/uts/i86pc/io/vmm/io/vrtc.h +++ b/usr/src/uts/i86pc/io/vmm/io/vrtc.h @@ -48,10 +48,10 @@ int vrtc_set_time(struct vm *vm, time_t secs); int vrtc_nvram_write(struct vm *vm, int offset, uint8_t value); int vrtc_nvram_read(struct vm *vm, int offset, uint8_t *retval); -int vrtc_addr_handler(struct vm *vm, int vcpuid, bool in, uint16_t port, - uint8_t bytes, uint32_t *val); -int vrtc_data_handler(struct vm *vm, int vcpuid, bool in, uint16_t port, - uint8_t bytes, uint32_t *val); +int vrtc_addr_handler(void *arg, bool in, uint16_t port, uint8_t bytes, + uint32_t *val); +int vrtc_data_handler(void *arg, bool in, uint16_t port, uint8_t bytes, + uint32_t *val); #ifndef __FreeBSD__ void vrtc_localize_resources(struct vrtc *); diff --git a/usr/src/uts/i86pc/io/vmm/sys/vmm_kernel.h b/usr/src/uts/i86pc/io/vmm/sys/vmm_kernel.h index 68e5e1d51d..acdabf556f 100644 --- a/usr/src/uts/i86pc/io/vmm/sys/vmm_kernel.h +++ b/usr/src/uts/i86pc/io/vmm/sys/vmm_kernel.h @@ -385,16 +385,21 @@ int vmm_mod_unload(void); void vmm_call_trap(uint64_t); /* - * Because of tangled headers, these are mirrored by vmm_drv.h to present the - * interface to driver consumers. + * Because of tangled headers, this is not exposed directly via the vmm_drv + * interface, but rather mirrored as vmm_drv_iop_cb_t in vmm_drv.h. */ -typedef int (*vmm_rmem_cb_t)(void *, uintptr_t, uint_t, uint64_t *); -typedef int (*vmm_wmem_cb_t)(void *, uintptr_t, uint_t, uint64_t); +typedef int (*ioport_handler_t)(void *, bool, uint16_t, uint8_t, uint32_t *); -int vm_ioport_hook(struct vm *, uint_t, vmm_rmem_cb_t, vmm_wmem_cb_t, void *, - void **); +int vm_ioport_access(struct vm *vm, int vcpuid, bool in, uint16_t port, + uint8_t bytes, uint32_t *val); + +int vm_ioport_attach(struct vm *vm, uint16_t port, ioport_handler_t func, + void *arg, void **cookie); +int vm_ioport_detach(struct vm *vm, void **cookie, ioport_handler_t *old_func, + void **old_arg); + +int vm_ioport_hook(struct vm *, uint16_t, ioport_handler_t, void *, void **); void vm_ioport_unhook(struct vm *, void **); -int vm_ioport_handle_hook(struct vm *, int, bool, int, int, uint32_t *); #endif /* __FreeBSD */ diff --git a/usr/src/uts/i86pc/io/vmm/vmm.c b/usr/src/uts/i86pc/io/vmm/vmm.c index 14aa9c552b..23a4fecf7b 100644 --- a/usr/src/uts/i86pc/io/vmm/vmm.c +++ b/usr/src/uts/i86pc/io/vmm/vmm.c @@ -198,9 +198,9 @@ struct vm { uint16_t cores; /* (o) num of cores/socket */ uint16_t threads; /* (o) num of threads/core */ uint16_t maxcpus; /* (o) max pluggable cpus */ -#ifndef __FreeBSD__ - list_t ioport_hooks; -#endif /* __FreeBSD__ */ + + struct ioport_config ioports; /* (o) ioport handling */ + bool sipi_req; /* (i) SIPI requested */ int sipi_req_vcpu; /* (i) SIPI destination */ uint64_t sipi_req_rip; /* (i) SIPI start %rip */ @@ -302,14 +302,6 @@ static void vcpu_notify_event_locked(struct vcpu *vcpu, bool lapic_intr); #ifndef __FreeBSD__ static void vm_clear_memseg(struct vm *, int); -typedef struct vm_ioport_hook { - list_node_t vmih_node; - uint_t vmih_ioport; - void *vmih_arg; - vmm_rmem_cb_t vmih_rmem_cb; - vmm_wmem_cb_t vmih_wmem_cb; -} vm_ioport_hook_t; - /* Flags for vtc_status */ #define VTCS_FPU_RESTORED 1 /* guest FPU restored, host FPU saved */ #define VTCS_FPU_CTX_CRITICAL 2 /* in ctx where FPU restore cannot be lazy */ @@ -508,14 +500,10 @@ vm_init(struct vm *vm, bool create) vm->vpmtmr = vpmtmr_init(vm); if (create) vm->vrtc = vrtc_init(vm); -#ifndef __FreeBSD__ + if (create) { - list_create(&vm->ioport_hooks, sizeof (vm_ioport_hook_t), - offsetof (vm_ioport_hook_t, vmih_node)); - } else { - VERIFY(list_is_empty(&vm->ioport_hooks)); + vm_inout_init(vm, &vm->ioports); } -#endif /* __FreeBSD__ */ CPU_ZERO(&vm->active_cpus); CPU_ZERO(&vm->debug_cpus); @@ -618,6 +606,10 @@ vm_cleanup(struct vm *vm, bool destroy) if (vm->iommu != NULL) iommu_destroy_domain(vm->iommu); + if (destroy) { + vm_inout_cleanup(vm, &vm->ioports); + } + if (destroy) vrtc_cleanup(vm->vrtc); else @@ -3359,94 +3351,75 @@ vm_get_wiredcnt(struct vm *vm, int vcpu, struct vmm_stat_type *stat) VMM_STAT_FUNC(VMM_MEM_RESIDENT, "Resident memory", vm_get_rescnt); VMM_STAT_FUNC(VMM_MEM_WIRED, "Wired memory", vm_get_wiredcnt); -#ifndef __FreeBSD__ int -vm_ioport_hook(struct vm *vm, uint_t ioport, vmm_rmem_cb_t rfunc, - vmm_wmem_cb_t wfunc, void *arg, void **cookie) +vm_ioport_access(struct vm *vm, int vcpuid, bool in, uint16_t port, + uint8_t bytes, uint32_t *val) { - list_t *ih = &vm->ioport_hooks; - vm_ioport_hook_t *hook, *node; - - if (ioport == 0) { - return (EINVAL); - } - - /* - * Find the node position in the list which this region should be - * inserted behind to maintain sorted order. - */ - for (node = list_tail(ih); node != NULL; node = list_prev(ih, node)) { - if (ioport == node->vmih_ioport) { - /* Reject duplicate port hook */ - return (EEXIST); - } else if (ioport > node->vmih_ioport) { - break; - } - } + return (vm_inout_access(&vm->ioports, in, port, bytes, val)); +} - hook = kmem_alloc(sizeof (*hook), KM_SLEEP); - hook->vmih_ioport = ioport; - hook->vmih_arg = arg; - hook->vmih_rmem_cb = rfunc; - hook->vmih_wmem_cb = wfunc; - if (node == NULL) { - list_insert_head(ih, hook); - } else { - list_insert_after(ih, node, hook); +/* + * bhyve-internal interfaces to attach or detach IO port handlers. + * Must be called with VM write lock held for safety. + */ +int +vm_ioport_attach(struct vm *vm, uint16_t port, ioport_handler_t func, void *arg, + void **cookie) +{ + int err; + err = vm_inout_attach(&vm->ioports, port, IOPF_DEFAULT, func, arg); + if (err == 0) { + *cookie = (void *)IOP_GEN_COOKIE(func, arg, port); } - - *cookie = (void *)hook; - return (0); + return (err); } - -void -vm_ioport_unhook(struct vm *vm, void **cookie) +int +vm_ioport_detach(struct vm *vm, void **cookie, ioport_handler_t *old_func, + void **old_arg) { - vm_ioport_hook_t *hook; - list_t *ih = &vm->ioport_hooks; + uint16_t port = IOP_PORT_FROM_COOKIE((uintptr_t)*cookie); + int err; - hook = *cookie; - list_remove(ih, hook); - kmem_free(hook, sizeof (*hook)); - *cookie = NULL; + err = vm_inout_detach(&vm->ioports, port, false, old_func, old_arg); + if (err == 0) { + *cookie = NULL; + } + return (err); } +/* + * External driver interfaces to attach or detach IO port handlers. + * Must be called with VM write lock held for safety. + */ int -vm_ioport_handle_hook(struct vm *vm, int cpuid, bool in, int port, int bytes, - uint32_t *val) +vm_ioport_hook(struct vm *vm, uint16_t port, ioport_handler_t func, + void *arg, void **cookie) { - vm_ioport_hook_t *hook; - list_t *ih = &vm->ioport_hooks; - int err = 0; + int err; - for (hook = list_head(ih); hook != NULL; hook = list_next(ih, hook)) { - if (hook->vmih_ioport == port) { - break; - } - } - if (hook == NULL) { - return (ESRCH); + if (port == 0) { + return (EINVAL); } - if (in) { - uint64_t tval; - - if (hook->vmih_rmem_cb == NULL) { - return (ESRCH); - } - err = hook->vmih_rmem_cb(hook->vmih_arg, (uintptr_t)port, - (uint_t)bytes, &tval); - *val = (uint32_t)tval; - } else { - if (hook->vmih_wmem_cb == NULL) { - return (ESRCH); - } - err = hook->vmih_wmem_cb(hook->vmih_arg, (uintptr_t)port, - (uint_t)bytes, (uint64_t)*val); + err = vm_inout_attach(&vm->ioports, port, IOPF_DRV_HOOK, func, arg); + if (err == 0) { + *cookie = (void *)IOP_GEN_COOKIE(func, arg, port); } - return (err); } +void +vm_ioport_unhook(struct vm *vm, void **cookie) +{ + uint16_t port = IOP_PORT_FROM_COOKIE((uintptr_t)*cookie); + ioport_handler_t old_func; + void *old_arg; + int err; + err = vm_inout_detach(&vm->ioports, port, true, &old_func, &old_arg); -#endif /* __FreeBSD__ */ + /* ioport-hook-using drivers are expected to be well-behaved */ + VERIFY0(err); + VERIFY(IOP_GEN_COOKIE(old_func, old_arg, port) == (uintptr_t)*cookie); + + *cookie = NULL; +} diff --git a/usr/src/uts/i86pc/io/vmm/vmm_instruction_emul.c b/usr/src/uts/i86pc/io/vmm/vmm_instruction_emul.c index 9f5bc59876..0ac5e21fd0 100644 --- a/usr/src/uts/i86pc/io/vmm/vmm_instruction_emul.c +++ b/usr/src/uts/i86pc/io/vmm/vmm_instruction_emul.c @@ -74,7 +74,8 @@ enum vie_status { VIES_PENDING_MMIO = (1U << 5), VIES_PENDING_INOUT = (1U << 6), VIES_REPEAT = (1U << 7), - VIES_COMPLETE = (1U << 8), + VIES_USER_FALLBACK = (1U << 8), + VIES_COMPLETE = (1U << 9), }; /* State of request to perform emulated access (inout or MMIO) */ @@ -1893,6 +1894,12 @@ vie_mmio_read(struct vie *vie, struct vm *vm, int cpuid, uint64_t gpa, vie->mmio_req_read.gpa = gpa; vie->mmio_req_read.state = VR_PENDING; vie->status |= VIES_PENDING_MMIO; + } else if (err < 0) { + /* + * The MMIO read failed in such a way that fallback to handling + * in userspace is required. + */ + vie->status |= VIES_USER_FALLBACK; } return (err); } @@ -1928,6 +1935,12 @@ vie_mmio_write(struct vie *vie, struct vm *vm, int cpuid, uint64_t gpa, vie->mmio_req_write.data = wval; vie->mmio_req_write.state = VR_PENDING; vie->status |= VIES_PENDING_MMIO; + } else if (err < 0) { + /* + * The MMIO write failed in such a way that fallback to handling + * in userspace is required. + */ + vie->status |= VIES_USER_FALLBACK; } return (err); } @@ -2023,7 +2036,7 @@ vie_emulate_inout_port(struct vie *vie, struct vm *vm, int vcpuid) } if (vie->inout_req_state != VR_DONE) { - err = vm_inout_access(vm, vcpuid, in, vie->inout.port, + err = vm_ioport_access(vm, vcpuid, in, vie->inout.port, vie->inout.bytes, &val); } else { /* @@ -2205,21 +2218,21 @@ vie_emulate_inout(struct vie *vie, struct vm *vm, int vcpuid) } err = vie_emulate_inout_port(vie, vm, vcpuid); - - if (err == ESRCH) { - ASSERT(vie->status & VIES_PENDING_INOUT); - /* Return to userspace with the in/out request */ - err = -1; - } } else { vie->status &= ~VIES_REPEAT; err = vie_emulate_inout_str(vie, vm, vcpuid); - if (err == ESRCH) { - ASSERT(vie->status & VIES_PENDING_INOUT); - /* Return to userspace with the in/out request */ - err = -1; - } + } + if (err < 0) { + /* + * Access to an I/O port failed in such a way that fallback to + * handling in userspace is required. + */ + vie->status |= VIES_USER_FALLBACK; + } else if (err == ESRCH) { + ASSERT(vie->status & VIES_PENDING_INOUT); + /* Return to userspace with the in/out request */ + err = -1; } return (err); @@ -2244,7 +2257,14 @@ vie_advance_pc(struct vie *vie, uint64_t *nextrip) void vie_exitinfo(const struct vie *vie, struct vm_exit *vme) { - if (vie->status & VIES_MMIO) { + if (vie->status & VIES_USER_FALLBACK) { + /* + * Despite the fact that the instruction was successfully + * decoded, some aspect of the emulation failed in such a way + * that it is left up to userspace to complete the operation. + */ + vie_fallback_exitinfo(vie, vme); + } else if (vie->status & VIES_MMIO) { vme->exitcode = VM_EXITCODE_MMIO; if (vie->mmio_req_read.state == VR_PENDING) { vme->u.mmio.gpa = vie->mmio_req_read.gpa; @@ -2298,7 +2318,16 @@ vie_fallback_exitinfo(const struct vie *vie, struct vm_exit *vme) bool vie_pending(const struct vie *vie) { - return ((vie->status & (VIES_PENDING_MMIO|VIES_PENDING_INOUT)) != 0); + /* + * These VIE status bits indicate conditions which must be addressed + * through either device IO fulfillment (with corresponding + * vie_fulfill_*()) or complete userspace emulation (followed by a + * vie_reset()). + */ + const enum vie_status of_interest = + VIES_PENDING_MMIO | VIES_PENDING_INOUT | VIES_USER_FALLBACK; + + return ((vie->status & of_interest) != 0); } bool diff --git a/usr/src/uts/i86pc/io/vmm/vmm_ioport.c b/usr/src/uts/i86pc/io/vmm/vmm_ioport.c index 01fae7d584..3826dbe8b5 100644 --- a/usr/src/uts/i86pc/io/vmm/vmm_ioport.c +++ b/usr/src/uts/i86pc/io/vmm/vmm_ioport.c @@ -48,47 +48,250 @@ __FBSDID("$FreeBSD$"); #include "vatpic.h" #include "vatpit.h" -#include "vpmtmr.h" #include "vrtc.h" #include "vmm_ioport.h" -#define MAX_IOPORTS 1280 - -static ioport_handler_func_t ioport_handler[MAX_IOPORTS] = { - [TIMER_MODE] = vatpit_handler, - [TIMER_CNTR0] = vatpit_handler, - [TIMER_CNTR1] = vatpit_handler, - [TIMER_CNTR2] = vatpit_handler, - [NMISC_PORT] = vatpit_nmisc_handler, - [IO_ICU1] = vatpic_master_handler, - [IO_ICU1 + ICU_IMR_OFFSET] = vatpic_master_handler, - [IO_ICU2] = vatpic_slave_handler, - [IO_ICU2 + ICU_IMR_OFFSET] = vatpic_slave_handler, - [IO_ELCR1] = vatpic_elc_handler, - [IO_ELCR2] = vatpic_elc_handler, - [IO_PMTMR] = vpmtmr_handler, - [IO_RTC] = vrtc_addr_handler, - [IO_RTC + 1] = vrtc_data_handler, -}; +/* Arbitrary limit on entries per VM */ +static uint_t ioport_entry_limit = 64; + +static void +vm_inout_def(ioport_entry_t *entries, uint_t i, uint16_t port, + ioport_handler_t func, void *arg, uint16_t flags) +{ + ioport_entry_t *ent = &entries[i]; + + if (i != 0) { + const ioport_entry_t *prev = &entries[i - 1]; + /* ensure that entries are inserted in sorted order */ + VERIFY(prev->iope_port < port); + } + ent->iope_func = func; + ent->iope_arg = arg; + ent->iope_port = port; + ent->iope_flags = flags; +} + +void +vm_inout_init(struct vm *vm, struct ioport_config *cfg) +{ + struct vatpit *pit = vm_atpit(vm); + struct vatpic *pic = vm_atpic(vm); + struct vrtc *rtc = vm_rtc(vm); + const uint_t ndefault = 13; + const uint16_t flag = IOPF_FIXED; + ioport_entry_t *ents; + uint_t i = 0; + + VERIFY0(cfg->iop_entries); + VERIFY0(cfg->iop_count); + + ents = kmem_zalloc(ndefault * sizeof (ioport_entry_t), KM_SLEEP); + + /* PIC (master): 0x20-0x21 */ + vm_inout_def(ents, i++, IO_ICU1, vatpic_master_handler, pic, flag); + vm_inout_def(ents, i++, IO_ICU1 + ICU_IMR_OFFSET, vatpic_master_handler, + pic, flag); + + /* PIT: 0x40-0x43 and 0x61 (ps2 tie-in) */ + vm_inout_def(ents, i++, TIMER_CNTR0, vatpit_handler, pit, flag); + vm_inout_def(ents, i++, TIMER_CNTR1, vatpit_handler, pit, flag); + vm_inout_def(ents, i++, TIMER_CNTR2, vatpit_handler, pit, flag); + vm_inout_def(ents, i++, TIMER_MODE, vatpit_handler, pit, flag); + vm_inout_def(ents, i++, NMISC_PORT, vatpit_nmisc_handler, pit, flag); + + /* RTC: 0x70-0x71 */ + vm_inout_def(ents, i++, IO_RTC, vrtc_addr_handler, rtc, flag); + vm_inout_def(ents, i++, IO_RTC + 1, vrtc_data_handler, rtc, flag); + + /* PIC (slave): 0xa0-0xa1 */ + vm_inout_def(ents, i++, IO_ICU2, vatpic_slave_handler, pic, flag); + vm_inout_def(ents, i++, IO_ICU2 + ICU_IMR_OFFSET, vatpic_slave_handler, + pic, flag); + + /* PIC (ELCR): 0x4d0-0x4d1 */ + vm_inout_def(ents, i++, IO_ELCR1, vatpic_elc_handler, pic, flag); + vm_inout_def(ents, i++, IO_ELCR2, vatpic_elc_handler, pic, flag); + + VERIFY3U(i, ==, ndefault); + cfg->iop_entries = ents; + cfg->iop_count = ndefault; +} + +void +vm_inout_cleanup(struct vm *vm, struct ioport_config *cfg) +{ + VERIFY(cfg->iop_entries); + VERIFY(cfg->iop_count); + + kmem_free(cfg->iop_entries, + sizeof (ioport_entry_t) * cfg->iop_count); + cfg->iop_entries = NULL; + cfg->iop_count = 0; +} + +static void +vm_inout_remove_at(uint_t idx, uint_t old_count, ioport_entry_t *old_ents, + ioport_entry_t *new_ents) +{ + uint_t new_count = old_count - 1; + + VERIFY(old_count != 0); + VERIFY(idx < old_count); + + /* copy entries preceeding to-be-removed index */ + if (idx > 0) { + bcopy(old_ents, new_ents, sizeof (ioport_entry_t) * idx); + } + /* copy entries following to-be-removed index */ + if (idx < new_count) { + bcopy(&old_ents[idx + 1], &new_ents[idx], + sizeof (ioport_entry_t) * (new_count - idx)); + } +} + +static void +vm_inout_insert_space_at(uint_t idx, uint_t old_count, ioport_entry_t *old_ents, + ioport_entry_t *new_ents) +{ + uint_t new_count = old_count + 1; + + VERIFY(idx < new_count); + + /* copy entries preceeding index where space is to be added */ + if (idx > 0) { + bcopy(old_ents, new_ents, sizeof (ioport_entry_t) * idx); + } + /* copy entries to follow added space */ + if (idx < new_count) { + bcopy(&old_ents[idx], &new_ents[idx + 1], + sizeof (ioport_entry_t) * (old_count - idx)); + } +} int -vm_inout_access(struct vm *vm, int vcpuid, bool in, uint16_t port, - uint8_t bytes, uint32_t *val) +vm_inout_attach(struct ioport_config *cfg, uint16_t port, uint16_t flags, + ioport_handler_t func, void *arg) { - ioport_handler_func_t handler; - int error; + uint_t i, old_count, insert_idx; + ioport_entry_t *old_ents; + + if (cfg->iop_count >= ioport_entry_limit) { + return (ENOSPC); + } + + old_count = cfg->iop_count; + old_ents = cfg->iop_entries; + for (insert_idx = i = 0; i < old_count; i++) { + const ioport_entry_t *compare = &old_ents[i]; + if (compare->iope_port == port) { + return (EEXIST); + } else if (compare->iope_port < port) { + insert_idx = i + 1; + } + } + + + ioport_entry_t *new_ents; + uint_t new_count = old_count + 1; + new_ents = kmem_alloc(new_count * sizeof (ioport_entry_t), KM_SLEEP); + vm_inout_insert_space_at(insert_idx, old_count, old_ents, new_ents); + + new_ents[insert_idx].iope_func = func; + new_ents[insert_idx].iope_arg = arg; + new_ents[insert_idx].iope_port = port; + new_ents[insert_idx].iope_flags = flags; + new_ents[insert_idx].iope_pad = 0; - handler = NULL; - if (port < MAX_IOPORTS) { - handler = ioport_handler[port]; + cfg->iop_entries = new_ents; + cfg->iop_count = new_count; + kmem_free(old_ents, old_count * sizeof (ioport_entry_t)); + + return (0); +} + +int +vm_inout_detach(struct ioport_config *cfg, uint16_t port, bool drv_hook, + ioport_handler_t *old_func, void **old_arg) +{ + uint_t i, old_count, remove_idx; + ioport_entry_t *old_ents; + + old_count = cfg->iop_count; + old_ents = cfg->iop_entries; + VERIFY(old_count > 1); + for (i = 0; i < old_count; i++) { + const ioport_entry_t *compare = &old_ents[i]; + if (compare->iope_port != port) { + continue; + } + /* fixed ports are not allowed to be detached at runtime */ + if ((compare->iope_flags & IOPF_FIXED) != 0) { + return (EPERM); + } + + /* + * Driver-attached and bhyve-internal ioport hooks can only be + * removed by the respective party which attached them. + */ + if (drv_hook && (compare->iope_flags & IOPF_DRV_HOOK) == 0) { + return (EPERM); + } else if (!drv_hook && + (compare->iope_flags & IOPF_DRV_HOOK) != 0) { + return (EPERM); + } + break; + } + if (i == old_count) { + return (ENOENT); } + remove_idx = i; + + if (old_func != NULL) { + *old_func = cfg->iop_entries[remove_idx].iope_func; + } + if (old_arg != NULL) { + *old_arg = cfg->iop_entries[remove_idx].iope_arg; + } + + ioport_entry_t *new_ents; + uint_t new_count = old_count - 1; + new_ents = kmem_alloc(new_count * sizeof (ioport_entry_t), KM_SLEEP); + vm_inout_remove_at(remove_idx, old_count, old_ents, new_ents); + + cfg->iop_entries = new_ents; + cfg->iop_count = new_count; + kmem_free(old_ents, old_count * sizeof (ioport_entry_t)); + + return (0); +} + +static ioport_entry_t * +vm_inout_find(const struct ioport_config *cfg, uint16_t port) +{ + const uint_t count = cfg->iop_count; + ioport_entry_t *entries = cfg->iop_entries; + + for (uint_t i = 0; i < count; i++) { + if (entries[i].iope_port == port) { + return (&entries[i]); + } + } + return (NULL); +} + +int +vm_inout_access(struct ioport_config *cfg, bool in, uint16_t port, + uint8_t bytes, uint32_t *val) +{ + const ioport_entry_t *ent; + int err; - if (handler != NULL) { - error = (*handler)(vm, vcpuid, in, port, bytes, val); + ent = vm_inout_find(cfg, port); + if (ent == NULL) { + err = ESRCH; } else { - /* Look for hooks, if a standard handler is not present */ - error = vm_ioport_handle_hook(vm, vcpuid, in, port, bytes, val); + err = ent->iope_func(ent->iope_arg, in, port, bytes, val); } - return (error); + return (err); } diff --git a/usr/src/uts/i86pc/io/vmm/vmm_ioport.h b/usr/src/uts/i86pc/io/vmm/vmm_ioport.h index 7c51906e85..2a448133d9 100644 --- a/usr/src/uts/i86pc/io/vmm/vmm_ioport.h +++ b/usr/src/uts/i86pc/io/vmm/vmm_ioport.h @@ -27,14 +27,60 @@ * * $FreeBSD$ */ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + * + * Copyright 2020 Oxide Computer Company + */ #ifndef _VMM_IOPORT_H_ #define _VMM_IOPORT_H_ -typedef int (*ioport_handler_func_t)(struct vm *vm, int vcpuid, - bool in, uint16_t port, uint8_t bytes, uint32_t *val); +#include <sys/vmm_kernel.h> + +struct ioport_entry { + ioport_handler_t iope_func; + void *iope_arg; + uint16_t iope_port; + uint16_t iope_flags; + uint32_t iope_pad; +}; +typedef struct ioport_entry ioport_entry_t; + +struct ioport_config { + struct ioport_entry *iop_entries; + uint_t iop_count; +}; -int vm_inout_access(struct vm *vm, int vcpuid, bool in, uint16_t port, +#define IOPF_DEFAULT 0 +#define IOPF_FIXED (1 << 0) /* system device fixed in position */ +#define IOPF_DRV_HOOK (1 << 1) /* external driver hook */ + +void vm_inout_init(struct vm *vm, struct ioport_config *ports); +void vm_inout_cleanup(struct vm *vm, struct ioport_config *ports); + +int vm_inout_attach(struct ioport_config *ports, uint16_t port, uint16_t flags, + ioport_handler_t func, void *arg); +int vm_inout_detach(struct ioport_config *ports, uint16_t port, bool drv_hook, + ioport_handler_t *old_func, void **old_arg); + +int vm_inout_access(struct ioport_config *ports, bool in, uint16_t port, uint8_t bytes, uint32_t *val); +/* Arbitrary cookie for io port hook: + * - top 48 bits: func address + arg + * - lower 16 bits: port + */ +#define IOP_GEN_COOKIE(func, arg, port) \ + ((uintptr_t)((((uintptr_t)(func) + (uintptr_t)(arg)) << 16) \ + | (uint16_t)(port))) +#define IOP_PORT_FROM_COOKIE(cookie) (uint16_t)(cookie) + #endif /* _VMM_IOPORT_H_ */ diff --git a/usr/src/uts/i86pc/io/vmm/vmm_sol_dev.c b/usr/src/uts/i86pc/io/vmm/vmm_sol_dev.c index c176f55087..b62c6d1043 100644 --- a/usr/src/uts/i86pc/io/vmm/vmm_sol_dev.c +++ b/usr/src/uts/i86pc/io/vmm/vmm_sol_dev.c @@ -52,6 +52,7 @@ #include "io/vioapic.h" #include "io/vrtc.h" #include "io/vhpet.h" +#include "io/vpmtmr.h" #include "vmm_lapic.h" #include "vmm_stat.h" #include "vmm_util.h" @@ -466,6 +467,7 @@ vmmdev_do_ioctl(vmm_softc_t *sc, int cmd, intptr_t arg, int md, case VM_ALLOC_MEMSEG: case VM_MMAP_MEMSEG: case VM_WRLOCK_CYCLE: + case VM_PMTMR_LOCATE: vmm_write_lock(sc); lock_type = LOCK_WRITE_HOLD; break; @@ -1275,6 +1277,12 @@ vmmdev_do_ioctl(vmm_softc_t *sc, int cmd, intptr_t arg, int md, break; } + case VM_PMTMR_LOCATE: { + uint16_t port = arg; + error = vpmtmr_set_location(sc->vmm_vm, port); + break; + } + case VM_RESTART_INSTRUCTION: error = vm_restart_instruction(sc->vmm_vm, vcpu); break; @@ -1683,8 +1691,8 @@ vmm_drv_msi(vmm_lease_t *lease, uint64_t addr, uint64_t msg) } int -vmm_drv_ioport_hook(vmm_hold_t *hold, uint_t ioport, vmm_drv_rmem_cb_t rfunc, - vmm_drv_wmem_cb_t wfunc, void *arg, void **cookie) +vmm_drv_ioport_hook(vmm_hold_t *hold, uint16_t ioport, vmm_drv_iop_cb_t func, + void *arg, void **cookie) { vmm_softc_t *sc; int err; @@ -1707,8 +1715,8 @@ vmm_drv_ioport_hook(vmm_hold_t *hold, uint_t ioport, vmm_drv_rmem_cb_t rfunc, mutex_exit(&vmm_mtx); vmm_write_lock(sc); - err = vm_ioport_hook(sc->vmm_vm, ioport, (vmm_rmem_cb_t)rfunc, - (vmm_wmem_cb_t)wfunc, arg, cookie); + err = vm_ioport_hook(sc->vmm_vm, ioport, (ioport_handler_t)func, + arg, cookie); vmm_write_unlock(sc); if (err != 0) { diff --git a/usr/src/uts/i86pc/sys/vmm_dev.h b/usr/src/uts/i86pc/sys/vmm_dev.h index 090e82ed29..c894c6aeb0 100644 --- a/usr/src/uts/i86pc/sys/vmm_dev.h +++ b/usr/src/uts/i86pc/sys/vmm_dev.h @@ -299,6 +299,7 @@ _Static_assert(sizeof(struct vm_readwrite_kernemu_device) == 24, "ABI"); #define VM_MAP_PPTDEV_MMIO (VMM_LOCK_IOC_BASE | 0x04) #define VM_ALLOC_MEMSEG (VMM_LOCK_IOC_BASE | 0x05) #define VM_MMAP_MEMSEG (VMM_LOCK_IOC_BASE | 0x06) +#define VM_PMTMR_LOCATE (VMM_LOCK_IOC_BASE | 0x07) #define VM_WRLOCK_CYCLE (VMM_LOCK_IOC_BASE | 0xff) diff --git a/usr/src/uts/i86pc/sys/vmm_drv.h b/usr/src/uts/i86pc/sys/vmm_drv.h index 34f5b58a06..1f2b3d9254 100644 --- a/usr/src/uts/i86pc/sys/vmm_drv.h +++ b/usr/src/uts/i86pc/sys/vmm_drv.h @@ -12,6 +12,7 @@ /* * Copyright 2019 Joyent, Inc. + * Copyright 2020 Oxide Computer Company */ #ifndef _VMM_DRV_H_ @@ -20,6 +21,7 @@ #ifdef _KERNEL #include <sys/file.h> +#include <sys/stdbool.h> struct vmm_hold; typedef struct vmm_hold vmm_hold_t; @@ -28,11 +30,10 @@ struct vmm_lease; typedef struct vmm_lease vmm_lease_t; /* - * Because of tangled headers, these definitions mirror their vmm_[rw]mem_cb_t - * counterparts in vmm.h. + * Because of tangled headers, this definitions mirrors its ioport_handler_t + * counterpart in vmm_kernel.h. */ -typedef int (*vmm_drv_rmem_cb_t)(void *, uintptr_t, uint_t, uint64_t *); -typedef int (*vmm_drv_wmem_cb_t)(void *, uintptr_t, uint_t, uint64_t); +typedef int (*vmm_drv_iop_cb_t)(void *, bool, uint16_t, uint8_t, uint32_t *); extern int vmm_drv_hold(file_t *, cred_t *, vmm_hold_t **); extern void vmm_drv_rele(vmm_hold_t *); @@ -46,8 +47,8 @@ extern boolean_t vmm_drv_lease_expired(vmm_lease_t *); extern void *vmm_drv_gpa2kva(vmm_lease_t *, uintptr_t, size_t); extern int vmm_drv_msi(vmm_lease_t *, uint64_t, uint64_t); -extern int vmm_drv_ioport_hook(vmm_hold_t *, uint_t, vmm_drv_rmem_cb_t, - vmm_drv_wmem_cb_t, void *, void **); +extern int vmm_drv_ioport_hook(vmm_hold_t *, uint16_t, vmm_drv_iop_cb_t, void *, + void **); extern void vmm_drv_ioport_unhook(vmm_hold_t *, void **); #endif /* _KERNEL */ |