diff options
author | tychon <tychon@FreeBSD.org> | 2014-03-25 19:20:34 +0000 |
---|---|---|
committer | tychon <tychon@FreeBSD.org> | 2014-03-25 19:20:34 +0000 |
commit | 58699bc5fc91f93fc2dcfdf5bd3b813c4ad2bb7d (patch) | |
tree | 70436186ec9e187e64b571603fc3dd8faffb1e6c | |
parent | 49eacc91a5f5843b878a707d57fb588c3f3713bb (diff) | |
download | FreeBSD-src-58699bc5fc91f93fc2dcfdf5bd3b813c4ad2bb7d.zip FreeBSD-src-58699bc5fc91f93fc2dcfdf5bd3b813c4ad2bb7d.tar.gz |
Move the atpit device model from userspace into vmm.ko for better
precision and lower latency.
Approved by: grehan (co-mentor)
-rw-r--r-- | sys/amd64/include/vmm.h | 1 | ||||
-rw-r--r-- | sys/amd64/vmm/io/vatpit.c | 370 | ||||
-rw-r--r-- | sys/amd64/vmm/io/vatpit.h (renamed from usr.sbin/bhyve/pit_8254.h) | 21 | ||||
-rw-r--r-- | sys/amd64/vmm/vmm.c | 10 | ||||
-rw-r--r-- | sys/amd64/vmm/vmm_ioport.c | 5 | ||||
-rw-r--r-- | sys/modules/vmm/Makefile | 1 | ||||
-rw-r--r-- | usr.sbin/bhyve/Makefile | 1 | ||||
-rw-r--r-- | usr.sbin/bhyve/pit_8254.c | 292 |
8 files changed, 395 insertions, 306 deletions
diff --git a/sys/amd64/include/vmm.h b/sys/amd64/include/vmm.h index 890f765..d50e7bc 100644 --- a/sys/amd64/include/vmm.h +++ b/sys/amd64/include/vmm.h @@ -191,6 +191,7 @@ struct vmspace *vm_get_vmspace(struct vm *vm); int vm_assign_pptdev(struct vm *vm, int bus, int slot, int func); int vm_unassign_pptdev(struct vm *vm, int bus, int slot, int func); struct vatpic *vm_atpic(struct vm *vm); +struct vatpit *vm_atpit(struct vm *vm); /* * Inject exception 'vme' into the guest vcpu. This function returns 0 on diff --git a/sys/amd64/vmm/io/vatpit.c b/sys/amd64/vmm/io/vatpit.c new file mode 100644 index 0000000..6e6ffb7 --- /dev/null +++ b/sys/amd64/vmm/io/vatpit.c @@ -0,0 +1,370 @@ +/*- + * Copyright (c) 2014 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com> + * Copyright (c) 2011 NetApp, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/queue.h> +#include <sys/cpuset.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/malloc.h> +#include <sys/mutex.h> +#include <sys/systm.h> + +#include <machine/vmm.h> + +#include "vmm_ktr.h" +#include "vatpic.h" +#include "vioapic.h" +#include "vatpit.h" + +static MALLOC_DEFINE(M_VATPIT, "atpit", "bhyve virtual atpit (8254)"); + +#define VATPIT_LOCK(vatpit) mtx_lock_spin(&((vatpit)->mtx)) +#define VATPIT_UNLOCK(vatpit) mtx_unlock_spin(&((vatpit)->mtx)) +#define VATPIT_LOCKED(vatpit) mtx_owned(&((vatpit)->mtx)) + +#define TIMER_SEL_MASK 0xc0 +#define TIMER_RW_MASK 0x30 +#define TIMER_MODE_MASK 0x0f +#define TIMER_SEL_READBACK 0xc0 + +#define PIT_8254_FREQ 1193182 +#define TIMER_DIV(freq, hz) (((freq) + (hz) / 2) / (hz)) + +struct vatpit_callout_arg { + struct vatpit *vatpit; + int channel_num; +}; + + +struct channel { + int mode; + uint16_t initial; /* initial counter value */ + sbintime_t now_sbt; /* uptime when counter was loaded */ + uint8_t cr[2]; + uint8_t ol[2]; + int crbyte; + int olbyte; + int frbyte; + struct callout callout; + sbintime_t callout_sbt; /* target time */ + struct vatpit_callout_arg callout_arg; +}; + +struct vatpit { + struct vm *vm; + struct mtx mtx; + + sbintime_t freq_sbt; + + struct channel channel[3]; +}; + +#define VATPIT_CTR0(vatpit, fmt) \ + VM_CTR0((vatpit)->vm, fmt) + +#define VATPIT_CTR1(vatpit, fmt, a1) \ + VM_CTR1((vatpit)->vm, fmt, a1) + +#define VATPIT_CTR2(vatpit, fmt, a1, a2) \ + VM_CTR2((vatpit)->vm, fmt, a1, a2) + +#define VATPIT_CTR3(vatpit, fmt, a1, a2, a3) \ + VM_CTR3((vatpit)->vm, fmt, a1, a2, a3) + +#define VATPIT_CTR4(vatpit, fmt, a1, a2, a3, a4) \ + VM_CTR4((vatpit)->vm, fmt, a1, a2, a3, a4) + +static void pit_timer_start_cntr0(struct vatpit *vatpit); + +static void +vatpit_callout_handler(void *a) +{ + struct vatpit_callout_arg *arg = a; + struct vatpit *vatpit; + struct callout *callout; + struct channel *c; + + vatpit = arg->vatpit; + c = &vatpit->channel[arg->channel_num]; + callout = &c->callout; + + VATPIT_CTR1(vatpit, "atpit t%d fired", arg->channel_num); + + VATPIT_LOCK(vatpit); + + if (callout_pending(callout)) /* callout was reset */ + goto done; + + if (!callout_active(callout)) /* callout was stopped */ + goto done; + + callout_deactivate(callout); + + if (c->mode == TIMER_RATEGEN) { + pit_timer_start_cntr0(vatpit); + } + + vatpic_pulse_irq(vatpit->vm, 0); + vioapic_pulse_irq(vatpit->vm, 2); + +done: + VATPIT_UNLOCK(vatpit); + return; +} + +static void +pit_timer_start_cntr0(struct vatpit *vatpit) +{ + struct channel *c; + sbintime_t delta, precision; + + c = &vatpit->channel[0]; + if (c->initial != 0) { + delta = c->initial * vatpit->freq_sbt; + precision = delta >> tc_precexp; + c->callout_sbt = c->callout_sbt + delta; + + callout_reset_sbt(&c->callout, c->callout_sbt, + precision, vatpit_callout_handler, &c->callout_arg, + C_ABSOLUTE); + } +} + +static uint16_t +pit_update_counter(struct vatpit *vatpit, struct channel *c, bool latch) +{ + uint16_t lval; + sbintime_t delta_ticks; + + /* cannot latch a new value until the old one has been consumed */ + if (latch && c->olbyte != 0) + return (0); + + if (c->initial == 0) { + /* + * This is possibly an o/s bug - reading the value of + * the timer without having set up the initial value. + * + * The original user-space version of this code set + * the timer to 100hz in this condition; do the same + * here. + */ + c->initial = TIMER_DIV(PIT_8254_FREQ, 100); + c->now_sbt = sbinuptime(); + } + + delta_ticks = (sbinuptime() - c->now_sbt) / vatpit->freq_sbt; + + lval = c->initial - delta_ticks % c->initial; + + if (latch) { + c->olbyte = 2; + c->ol[1] = lval; /* LSB */ + c->ol[0] = lval >> 8; /* MSB */ + } + + return (lval); +} + +static int +vatpit_update_mode(struct vatpit *vatpit, uint8_t val) +{ + struct channel *c; + int sel, rw, mode; + + sel = val & TIMER_SEL_MASK; + rw = val & TIMER_RW_MASK; + mode = val & TIMER_MODE_MASK; + + if (sel == TIMER_SEL_READBACK) + return (-1); + + if (rw != TIMER_LATCH && rw != TIMER_16BIT) + return (-1); + + if (rw != TIMER_LATCH) { + /* + * Counter mode is not affected when issuing a + * latch command. + */ + if (mode != TIMER_INTTC && + mode != TIMER_RATEGEN && + mode != TIMER_SQWAVE && + mode != TIMER_SWSTROBE) + return (-1); + } + + c = &vatpit->channel[sel >> 6]; + if (rw == TIMER_LATCH) + pit_update_counter(vatpit, c, true); + else { + c->mode = mode; + c->olbyte = 0; /* reset latch after reprogramming */ + } + + return (0); +} + +static int +vatpit_get_out(struct vatpit *vatpit, int channel) +{ + struct channel *c; + sbintime_t delta_ticks; + int out; + + c = &vatpit->channel[channel]; + + switch (c->mode) { + case TIMER_INTTC: + delta_ticks = (sbinuptime() - c->now_sbt) / vatpit->freq_sbt; + out = ((c->initial - delta_ticks) <= 0); + break; + default: + out = 0; + break; + } + + return (out); +} + +int +vatpit_handler(void *vm, int vcpuid, struct vm_exit *vmexit) +{ + struct vatpit *vatpit; + struct channel *c; + int port; + uint8_t val; + int error; + + vatpit = vm_atpit(vm); + + if (vmexit->u.inout.bytes != 1) + return (-1); + + val = vmexit->u.inout.eax; + port = vmexit->u.inout.port; + + if (port == TIMER_MODE) { + if (vmexit->u.inout.in != 0) { + VATPIT_CTR0(vatpit, "vatpit attempt to read mode"); + return (-1); + } + + VATPIT_LOCK(vatpit); + error = vatpit_update_mode(vatpit, val); + VATPIT_UNLOCK(vatpit); + + return (error); + } + + /* counter ports */ + KASSERT(port >= TIMER_CNTR0 && vmexit->u.inout.port <= TIMER_CNTR2, + ("invalid port 0x%x", port)); + c = &vatpit->channel[port - TIMER_CNTR0]; + + VATPIT_LOCK(vatpit); + if (vmexit->u.inout.in) { + /* + * The spec says that once the output latch is completely + * read it should revert to "following" the counter. Use + * the free running counter for this case (i.e. Linux + * TSC calibration). Assuming the access mode is 16-bit, + * toggle the MSB/LSB bit on each read. + */ + if (c->olbyte == 0) { + uint16_t tmp; + + tmp = pit_update_counter(vatpit, c, false); + if (c->frbyte) + tmp >>= 8; + tmp &= 0xff; + vmexit->u.inout.eax = tmp; + c->frbyte ^= 1; + } else + vmexit->u.inout.eax = c->ol[--c->olbyte]; + } else { + c->cr[c->crbyte++] = vmexit->u.inout.eax; + if (c->crbyte == 2) { + c->frbyte = 0; + c->crbyte = 0; + c->initial = c->cr[0] | (uint16_t)c->cr[1] << 8; + c->now_sbt = sbinuptime(); + /* Start an interval timer for channel 0 */ + if (port == TIMER_CNTR0) { + c->callout_sbt = c->now_sbt; + pit_timer_start_cntr0(vatpit); + } + if (c->initial == 0) + c->initial = 0xffff; + } + } + VATPIT_UNLOCK(vatpit); + + return (0); +} + +struct vatpit * +vatpit_init(struct vm *vm) +{ + struct vatpit *vatpit; + struct bintime bt; + struct vatpit_callout_arg *arg; + int i; + + vatpit = malloc(sizeof(struct vatpit), M_VATPIT, M_WAITOK | M_ZERO); + vatpit->vm = vm; + + mtx_init(&vatpit->mtx, "vatpit lock", NULL, MTX_SPIN); + + FREQ2BT(PIT_8254_FREQ, &bt); + vatpit->freq_sbt = bttosbt(bt); + + for (i = 0; i < 3; i++) { + callout_init(&vatpit->channel[i].callout, true); + arg = &vatpit->channel[i].callout_arg; + arg->vatpit = vatpit; + arg->channel_num = i; + } + + return (vatpit); +} + +void +vatpit_cleanup(struct vatpit *vatpit) +{ + int i; + + for (i = 0; i < 3; i++) + callout_drain(&vatpit->channel[i].callout); + + free(vatpit, M_VATPIT); +} diff --git a/usr.sbin/bhyve/pit_8254.h b/sys/amd64/vmm/io/vatpit.h index 61bd15d..4f30acf 100644 --- a/usr.sbin/bhyve/pit_8254.h +++ b/sys/amd64/vmm/io/vatpit.h @@ -1,4 +1,5 @@ /*- + * Copyright (c) 2014 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com> * Copyright (c) 2011 NetApp, Inc. * All rights reserved. * @@ -26,20 +27,14 @@ * $FreeBSD$ */ -#ifndef _PIT_8254_H_ -#define _PIT_8254_H_ +#ifndef _VATPIT_H_ +#define _VATPIT_H_ -/* - * Borrowed from amd64/include/timerreg.h because in that file it is - * conditionally compiled for #ifdef _KERNEL only. - */ +#include <machine/timerreg.h> -#include <dev/ic/i8253reg.h> +struct vatpit *vatpit_init(struct vm *vm); +void vatpit_cleanup(struct vatpit *vatpit); -#define IO_TIMER1 0x40 /* 8253 Timer #1 */ -#define TIMER_CNTR0 (IO_TIMER1 + TIMER_REG_CNTR0) -#define TIMER_CNTR1 (IO_TIMER1 + TIMER_REG_CNTR1) -#define TIMER_CNTR2 (IO_TIMER1 + TIMER_REG_CNTR2) -#define TIMER_MODE (IO_TIMER1 + TIMER_REG_MODE) +int vatpit_handler(void *vm, int vcpuid, struct vm_exit *vmexit); -#endif /* _PIT_8254_H_ */ +#endif /* _VATPIT_H_ */ diff --git a/sys/amd64/vmm/vmm.c b/sys/amd64/vmm/vmm.c index 3b78623..9d740d1 100644 --- a/sys/amd64/vmm/vmm.c +++ b/sys/amd64/vmm/vmm.c @@ -68,6 +68,7 @@ __FBSDID("$FreeBSD$"); #include "vmm_mem.h" #include "vmm_util.h" #include "vatpic.h" +#include "vatpit.h" #include "vhpet.h" #include "vioapic.h" #include "vlapic.h" @@ -119,6 +120,7 @@ struct vm { struct vhpet *vhpet; /* virtual HPET */ struct vioapic *vioapic; /* virtual ioapic */ struct vatpic *vatpic; /* virtual atpic */ + struct vatpit *vatpit; /* virtual atpit */ struct vmspace *vmspace; /* guest's address space */ struct vcpu vcpu[VM_MAXCPU]; int num_mem_segs; @@ -349,6 +351,7 @@ vm_create(const char *name, struct vm **retvm) vm->vioapic = vioapic_init(vm); vm->vhpet = vhpet_init(vm); vm->vatpic = vatpic_init(vm); + vm->vatpit = vatpit_init(vm); for (i = 0; i < VM_MAXCPU; i++) { vcpu_init(vm, i); @@ -381,6 +384,7 @@ vm_destroy(struct vm *vm) if (vm->iommu != NULL) iommu_destroy_domain(vm->iommu); + vatpit_cleanup(vm->vatpit); vhpet_cleanup(vm->vhpet); vatpic_cleanup(vm->vatpic); vioapic_cleanup(vm->vioapic); @@ -1740,3 +1744,9 @@ vm_atpic(struct vm *vm) { return (vm->vatpic); } + +struct vatpit * +vm_atpit(struct vm *vm) +{ + return (vm->vatpit); +} diff --git a/sys/amd64/vmm/vmm_ioport.c b/sys/amd64/vmm/vmm_ioport.c index 7783dc2..1df916a 100644 --- a/sys/amd64/vmm/vmm_ioport.c +++ b/sys/amd64/vmm/vmm_ioport.c @@ -36,11 +36,16 @@ __FBSDID("$FreeBSD$"); #include <machine/vmm.h> #include "vatpic.h" +#include "vatpit.h" #include "vmm_ioport.h" #define MAX_IOPORTS 1280 ioport_handler_func_t ioport_handler[MAX_IOPORTS] = { + [TIMER_MODE] = vatpit_handler, + [TIMER_CNTR0] = vatpit_handler, + [TIMER_CNTR1] = vatpit_handler, + [TIMER_CNTR2] = vatpit_handler, [IO_ICU1] = vatpic_master_handler, [IO_ICU1 + ICU_IMR_OFFSET] = vatpic_master_handler, [IO_ICU2] = vatpic_slave_handler, diff --git a/sys/modules/vmm/Makefile b/sys/modules/vmm/Makefile index 7a2b2df..76f9364 100644 --- a/sys/modules/vmm/Makefile +++ b/sys/modules/vmm/Makefile @@ -29,6 +29,7 @@ SRCS+= vmm.c \ SRCS+= iommu.c \ ppt.c \ vatpic.c \ + vatpit.c \ vhpet.c \ vioapic.c \ vlapic.c diff --git a/usr.sbin/bhyve/Makefile b/usr.sbin/bhyve/Makefile index 98ead03..116fb60 100644 --- a/usr.sbin/bhyve/Makefile +++ b/usr.sbin/bhyve/Makefile @@ -27,7 +27,6 @@ SRCS= \ pci_virtio_block.c \ pci_virtio_net.c \ pci_uart.c \ - pit_8254.c \ pm.c \ pmtmr.c \ post.c \ diff --git a/usr.sbin/bhyve/pit_8254.c b/usr.sbin/bhyve/pit_8254.c deleted file mode 100644 index b45b100..0000000 --- a/usr.sbin/bhyve/pit_8254.c +++ /dev/null @@ -1,292 +0,0 @@ -/*- - * Copyright (c) 2011 NetApp, Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * $FreeBSD$ - */ - -#include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - -#include <sys/types.h> -#include <sys/time.h> -#include <machine/vmm.h> - -#include <machine/clock.h> - -#include <assert.h> -#include <stdio.h> -#include <string.h> -#include <unistd.h> - -#include <vmmapi.h> - -#include "acpi.h" -#include "bhyverun.h" -#include "inout.h" -#include "mevent.h" -#include "pci_lpc.h" -#include "pit_8254.h" - -#define TIMER_SEL_MASK 0xc0 -#define TIMER_RW_MASK 0x30 -#define TIMER_MODE_MASK 0x0f -#define TIMER_SEL_READBACK 0xc0 - -#define TIMER_DIV(freq, hz) (((freq) + (hz) / 2) / (hz)) - -#define PIT_8254_FREQ 1193182 -static const int nsecs_per_tick = 1000000000 / PIT_8254_FREQ; - -struct counter { - struct vmctx *ctx; - struct mevent *tevp; - struct timeval tv; /* uptime when counter was loaded */ - int mode; - uint16_t initial; /* initial counter value */ - uint8_t cr[2]; - uint8_t ol[2]; - int crbyte; - int olbyte; - int frbyte; -}; - - -static void -timevalfix(struct timeval *t1) -{ - - if (t1->tv_usec < 0) { - t1->tv_sec--; - t1->tv_usec += 1000000; - } - if (t1->tv_usec >= 1000000) { - t1->tv_sec++; - t1->tv_usec -= 1000000; - } -} - -static void -timevalsub(struct timeval *t1, const struct timeval *t2) -{ - - t1->tv_sec -= t2->tv_sec; - t1->tv_usec -= t2->tv_usec; - timevalfix(t1); -} - -static uint64_t pit_mev_count; - -static void -pit_mevent_cb(int fd, enum ev_type type, void *param) -{ - struct counter *c; - - c = param; - - pit_mev_count++; - - vm_isa_pulse_irq(c->ctx, 0, 2); - - /* - * Delete the timer for one-shots - */ - if (c->mode != TIMER_RATEGEN) { - mevent_delete(c->tevp); - c->tevp = NULL; - } -} - -static void -pit_timer_start(struct vmctx *ctx, struct counter *c) -{ - int msecs; - - if (c->initial != 0) { - msecs = c->initial * nsecs_per_tick / 1000000; - if (msecs == 0) - msecs = 1; - - if (c->tevp == NULL) - c->tevp = mevent_add(msecs, EVF_TIMER, pit_mevent_cb, - c); - } -} - -static uint16_t -pit_update_counter(struct counter *c, int latch) -{ - struct timeval tv2; - uint16_t lval; - uint64_t delta_nsecs, delta_ticks; - - /* cannot latch a new value until the old one has been consumed */ - if (latch && c->olbyte != 0) - return (0); - - if (c->initial == 0 || c->initial == 1) { - /* - * XXX the program that runs the VM can be stopped and - * restarted at any time. This means that state that was - * created by the guest is destroyed between invocations - * of the program. - * - * If the counter's initial value is not programmed we - * assume a value that would be set to generate 100 - * interrupts per second. - */ - c->initial = TIMER_DIV(PIT_8254_FREQ, 100); - gettimeofday(&c->tv, NULL); - } - - (void)gettimeofday(&tv2, NULL); - timevalsub(&tv2, &c->tv); - delta_nsecs = tv2.tv_sec * 1000000000 + tv2.tv_usec * 1000; - delta_ticks = delta_nsecs / nsecs_per_tick; - - lval = c->initial - delta_ticks % c->initial; - - if (latch) { - c->olbyte = 2; - c->ol[1] = lval; /* LSB */ - c->ol[0] = lval >> 8; /* MSB */ - } - - return (lval); -} - -static int -pit_8254_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, - uint32_t *eax, void *arg) -{ - int sel, rw, mode; - uint8_t val; - struct counter *c; - - static struct counter counter[3]; - - if (bytes != 1) - return (-1); - - val = *eax; - - if (port == TIMER_MODE) { - assert(in == 0); - sel = val & TIMER_SEL_MASK; - rw = val & TIMER_RW_MASK; - mode = val & TIMER_MODE_MASK; - - if (sel == TIMER_SEL_READBACK) - return (-1); - if (rw != TIMER_LATCH && rw != TIMER_16BIT) - return (-1); - - if (rw != TIMER_LATCH) { - /* - * Counter mode is not affected when issuing a - * latch command. - */ - if (mode != TIMER_INTTC && - mode != TIMER_RATEGEN && - mode != TIMER_SQWAVE && - mode != TIMER_SWSTROBE) - return (-1); - } - - c = &counter[sel >> 6]; - c->ctx = ctx; - if (rw == TIMER_LATCH) - pit_update_counter(c, 1); - else { - c->mode = mode; - c->olbyte = 0; /* reset latch after reprogramming */ - } - - return (0); - } - - /* counter ports */ - assert(port >= TIMER_CNTR0 && port <= TIMER_CNTR2); - c = &counter[port - TIMER_CNTR0]; - - if (in) { - /* - * The spec says that once the output latch is completely - * read it should revert to "following" the counter. Use - * the free running counter for this case (i.e. Linux - * TSC calibration). Assuming the access mode is 16-bit, - * toggle the MSB/LSB bit on each read. - */ - if (c->olbyte == 0) { - uint16_t tmp; - - tmp = pit_update_counter(c, 0); - if (c->frbyte) - tmp >>= 8; - tmp &= 0xff; - *eax = tmp; - c->frbyte ^= 1; - } else - *eax = c->ol[--c->olbyte]; - } else { - c->cr[c->crbyte++] = *eax; - if (c->crbyte == 2) { - c->frbyte = 0; - c->crbyte = 0; - c->initial = c->cr[0] | (uint16_t)c->cr[1] << 8; - /* Start an interval timer for counter 0 */ - if (port == 0x40) - pit_timer_start(ctx, c); - if (c->initial == 0) - c->initial = 0xffff; - gettimeofday(&c->tv, NULL); - } - } - - return (0); -} - -INOUT_PORT(8254, TIMER_MODE, IOPORT_F_OUT, pit_8254_handler); -INOUT_PORT(8254, TIMER_CNTR0, IOPORT_F_INOUT, pit_8254_handler); -INOUT_PORT(8254, TIMER_CNTR1, IOPORT_F_INOUT, pit_8254_handler); -INOUT_PORT(8254, TIMER_CNTR2, IOPORT_F_INOUT, pit_8254_handler); - -static void -pit_dsdt(void) -{ - - dsdt_line(""); - dsdt_line("Device (TIMR)"); - dsdt_line("{"); - dsdt_line(" Name (_HID, EisaId (\"PNP0100\"))"); - dsdt_line(" Name (_CRS, ResourceTemplate ()"); - dsdt_line(" {"); - dsdt_indent(2); - dsdt_fixed_ioport(IO_TIMER1, 4); - dsdt_fixed_irq(0); - dsdt_unindent(2); - dsdt_line(" })"); - dsdt_line("}"); -} -LPC_DSDT(pit_dsdt); |