summaryrefslogtreecommitdiff
path: root/usr
diff options
context:
space:
mode:
Diffstat (limited to 'usr')
-rw-r--r--usr/src/cmd/bhyve/acpi.h3
-rw-r--r--usr/src/cmd/bhyve/bhyverun.c3
-rw-r--r--usr/src/cmd/bhyve/pm.c12
-rw-r--r--usr/src/cmd/bhyvectl/bhyvectl.c15
-rw-r--r--usr/src/lib/libvmmapi/common/mapfile-vers1
-rw-r--r--usr/src/lib/libvmmapi/common/vmmapi.c6
-rw-r--r--usr/src/lib/libvmmapi/common/vmmapi.h2
-rw-r--r--usr/src/uts/common/io/tem_safe.c46
-rw-r--r--usr/src/uts/i86pc/io/viona/viona_impl.h3
-rw-r--r--usr/src/uts/i86pc/io/viona/viona_main.c31
-rw-r--r--usr/src/uts/i86pc/io/vmm/io/vatpic.c29
-rw-r--r--usr/src/uts/i86pc/io/vmm/io/vatpic.h12
-rw-r--r--usr/src/uts/i86pc/io/vmm/io/vatpit.c15
-rw-r--r--usr/src/uts/i86pc/io/vmm/io/vatpit.h8
-rw-r--r--usr/src/uts/i86pc/io/vmm/io/vpmtmr.c56
-rw-r--r--usr/src/uts/i86pc/io/vmm/io/vpmtmr.h18
-rw-r--r--usr/src/uts/i86pc/io/vmm/io/vrtc.c32
-rw-r--r--usr/src/uts/i86pc/io/vmm/io/vrtc.h8
-rw-r--r--usr/src/uts/i86pc/io/vmm/sys/vmm_kernel.h19
-rw-r--r--usr/src/uts/i86pc/io/vmm/vmm.c151
-rw-r--r--usr/src/uts/i86pc/io/vmm/vmm_instruction_emul.c59
-rw-r--r--usr/src/uts/i86pc/io/vmm/vmm_ioport.c265
-rw-r--r--usr/src/uts/i86pc/io/vmm/vmm_ioport.h52
-rw-r--r--usr/src/uts/i86pc/io/vmm/vmm_sol_dev.c16
-rw-r--r--usr/src/uts/i86pc/sys/vmm_dev.h1
-rw-r--r--usr/src/uts/i86pc/sys/vmm_drv.h13
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 */