#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/queue.h>
#include <sys/kernel.h>
#include <sys/kmem.h>
#include <sys/systm.h>
#include <machine/vmm.h>
#include "vpmtmr.h"
#define PMTMR_FREQ 3579545
struct vpmtmr {
struct vm *vm;
void *io_cookie;
uint16_t io_port;
hrtime_t base_time;
};
struct vpmtmr *
vpmtmr_init(struct vm *vm)
{
struct vpmtmr *vpmtmr;
vpmtmr = kmem_zalloc(sizeof (struct vpmtmr), KM_SLEEP);
vpmtmr->vm = vm;
vpmtmr->base_time = gethrtime();
return (vpmtmr);
}
static int
vpmtmr_detach_ioport(struct vpmtmr *vpmtmr)
{
if (vpmtmr->io_cookie != NULL) {
ioport_handler_t old_func;
void *old_arg;
int err;
err = vm_ioport_detach(vpmtmr->vm, &vpmtmr->io_cookie,
&old_func, &old_arg);
if (err != 0) {
return (err);
}
ASSERT3P(old_func, ==, vpmtmr_handler);
ASSERT3P(old_arg, ==, vpmtmr);
ASSERT3P(vpmtmr->io_cookie, ==, NULL);
vpmtmr->io_port = 0;
}
return (0);
}
void
vpmtmr_cleanup(struct vpmtmr *vpmtmr)
{
int err;
err = vpmtmr_detach_ioport(vpmtmr);
VERIFY3P(err, ==, 0);
kmem_free(vpmtmr, sizeof (*vpmtmr));
}
int
vpmtmr_set_location(struct vm *vm, uint16_t ioport)
{
struct vpmtmr *vpmtmr = vm_pmtmr(vm);
int err;
if (vpmtmr->io_cookie != NULL) {
if (vpmtmr->io_port == ioport) {
return (0);
}
err = vpmtmr_detach_ioport(vpmtmr);
VERIFY3P(err, ==, 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;
if (!in || bytes != 4)
return (-1);
const hrtime_t delta = gethrtime() - vpmtmr->base_time;
ASSERT3S(delta, >=, 0);
*val = hrt_freq_count(delta, PMTMR_FREQ);
return (0);
}
static int
vpmtmr_data_read(void *datap, const vmm_data_req_t *req)
{
VERIFY3U(req->vdr_class, ==, VDC_PM_TIMER);
VERIFY3U(req->vdr_version, ==, 1);
VERIFY3U(req->vdr_len, >=, sizeof (struct vdi_pm_timer_v1));
struct vpmtmr *vpmtmr = datap;
struct vdi_pm_timer_v1 *out = req->vdr_data;
out->vpt_time_base = vm_normalize_hrtime(vpmtmr->vm, vpmtmr->base_time);
out->vpt_ioport = vpmtmr->io_port;
return (0);
}
static int
vpmtmr_data_write(void *datap, const vmm_data_req_t *req)
{
VERIFY3U(req->vdr_class, ==, VDC_PM_TIMER);
VERIFY3U(req->vdr_version, ==, 1);
VERIFY3U(req->vdr_len, >=, sizeof (struct vdi_pm_timer_v1));
struct vpmtmr *vpmtmr = datap;
const struct vdi_pm_timer_v1 *src = req->vdr_data;
vpmtmr->base_time =
vm_denormalize_hrtime(vpmtmr->vm, src->vpt_time_base);
return (0);
}
static const vmm_data_version_entry_t pm_timer_v1 = {
.vdve_class = VDC_PM_TIMER,
.vdve_version = 1,
.vdve_len_expect = sizeof (struct vdi_pm_timer_v1),
.vdve_readf = vpmtmr_data_read,
.vdve_writef = vpmtmr_data_write,
};
VMM_DATA_VERSION(pm_timer_v1);