Index: mevent.c =================================================================== --- mevent.c (revision 245934) +++ mevent.c (working copy) @@ -59,12 +59,15 @@ extern char *vmname; static pthread_t mevent_tid; +static int mevent_timid = 43; static int mevent_pipefd[2]; static pthread_mutex_t mevent_lmutex = PTHREAD_MUTEX_INITIALIZER; struct mevent { void (*me_func)(int, enum ev_type, void *); +#define me_msecs me_fd int me_fd; + int me_timid; enum ev_type me_type; void *me_param; int me_cq; @@ -129,6 +132,9 @@ if (mevp->me_type == EVF_WRITE) retval = EVFILT_WRITE; + if (mevp->me_type == EVF_TIMER) + retval = EVFILT_TIMER; + return (retval); } @@ -140,6 +146,8 @@ switch (mevp->me_state) { case MEV_ENABLE: ret = EV_ADD; + if (mevp->me_type == EVF_TIMER) + ret |= EV_ENABLE; break; case MEV_DISABLE: ret = EV_DISABLE; @@ -177,11 +185,16 @@ */ close(mevp->me_fd); } else { - kev[i].ident = mevp->me_fd; + if (mevp->me_type == EVF_TIMER) { + kev[i].ident = mevp->me_timid; + kev[i].data = mevp->me_msecs; + } else { + kev[i].ident = mevp->me_fd; + kev[i].data = 0; + } kev[i].filter = mevent_kq_filter(mevp); kev[i].flags = mevent_kq_flags(mevp); kev[i].fflags = mevent_kq_fflags(mevp); - kev[i].data = 0; kev[i].udata = mevp; i++; } @@ -219,12 +232,12 @@ } struct mevent * -mevent_add(int fd, enum ev_type type, +mevent_add(int tfd, enum ev_type type, void (*func)(int, enum ev_type, void *), void *param) { struct mevent *lp, *mevp; - if (fd < 0 || func == NULL) { + if (tfd < 0 || func == NULL) { return (NULL); } @@ -236,13 +249,15 @@ * Verify that the fd/type tuple is not present in any list */ LIST_FOREACH(lp, &global_head, me_list) { - if (lp->me_fd == fd && lp->me_type == type) { + if (type != EVF_TIMER && lp->me_fd == tfd && + lp->me_type == type) { goto exit; } } LIST_FOREACH(lp, &change_head, me_list) { - if (lp->me_fd == fd && lp->me_type == type) { + if (type != EVF_TIMER && lp->me_fd == tfd && + lp->me_type == type) { goto exit; } } @@ -256,7 +271,11 @@ } memset(mevp, 0, sizeof(struct mevent)); - mevp->me_fd = fd; + if (type == EVF_TIMER) { + mevp->me_msecs = tfd; + mevp->me_timid = mevent_timid++; + } else + mevp->me_fd = tfd; mevp->me_type = type; mevp->me_func = func; mevp->me_param = param; Index: mevent.h =================================================================== --- mevent.h (revision 245934) +++ mevent.h (working copy) @@ -31,7 +31,8 @@ enum ev_type { EVF_READ, - EVF_WRITE + EVF_WRITE, + EVF_TIMER }; struct mevent; Index: pci_emul.c =================================================================== --- pci_emul.c (revision 245934) +++ pci_emul.c (working copy) @@ -787,29 +787,33 @@ } } +static uint32_t cfgval; static int cfgbus, cfgslot, cfgfunc, cfgoff; static int pci_emul_cfgaddr(struct vmctx *ctx, int vcpu, int in, int port, int bytes, uint32_t *eax, void *arg) { - uint32_t x; - assert(!in); + if (bytes != 4) { + *eax = 0xffffffff; + } else { + if (in) { + *eax = cfgval; + } else { + cfgval = *eax; + cfgoff = cfgval & PCI_REGMAX; + cfgfunc = (cfgval >> 8) & PCI_FUNCMAX; + cfgslot = (cfgval >> 11) & PCI_SLOTMAX; + cfgbus = (cfgval >> 16) & PCI_BUSMAX; + } + } - if (bytes != 4) - return (-1); - - x = *eax; - cfgoff = x & PCI_REGMAX; - cfgfunc = (x >> 8) & PCI_FUNCMAX; - cfgslot = (x >> 11) & PCI_SLOTMAX; - cfgbus = (x >> 16) & PCI_BUSMAX; - return (0); } -INOUT_PORT(pci_cfgaddr, CONF1_ADDR_PORT, IOPORT_F_OUT, pci_emul_cfgaddr); +INOUT_PORT(pci_cfgaddr, CONF1_ADDR_PORT, IOPORT_F_INOUT, pci_emul_cfgaddr); + static int pci_emul_cfgdata(struct vmctx *ctx, int vcpu, int in, int port, int bytes, uint32_t *eax, void *arg) Index: atpic.c =================================================================== --- atpic.c (revision 245934) +++ atpic.c (working copy) @@ -56,7 +56,7 @@ return (-1); if (in) - return (-1); + *eax = 1; /* Pretend all writes to the 8259 are alright */ return (0); Index: mevent_test.c =================================================================== --- mevent_test.c (revision 245934) +++ mevent_test.c (working copy) @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -48,8 +49,38 @@ static pthread_mutex_t accept_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t accept_condvar = PTHREAD_COND_INITIALIZER; +static struct mevent *tevp; + +char *vmname = "test vm"; + + #define MEVENT_ECHO +/* Number of timer events to capture */ +#define TEVSZ 4096 +uint64_t tevbuf[TEVSZ]; + +static void +timer_callback(int fd, enum ev_type type, void *param) +{ + static int i; + + if (i >= TEVSZ) + abort(); + + tevbuf[i++] = rdtsc(); + + if (i == TEVSZ) { + int j; + mevent_delete(tevp); + printf("timers done\n"); + for (j = 1; j < TEVSZ; j++) { + printf("%lld\n", (tevbuf[j] - tevbuf[j-1])/3392ULL); + } + } +} + + #ifdef MEVENT_ECHO struct esync { pthread_mutex_t e_mt; @@ -133,6 +164,7 @@ pthread_t tid; int news; int s; + static int first; if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("socket"); @@ -163,6 +195,17 @@ if (news < 0) { perror("accept error"); } else { + static int first = 1; + + if (first) { + /* + * Start a timer + */ + first = 0; + tevp = mevent_add(1, EVF_TIMER, timer_callback, + NULL); + } + printf("incoming connection, spawning thread\n"); pthread_create(&tid, NULL, echoer, (void *)(uintptr_t)news); Index: pit_8254.c =================================================================== --- pit_8254.c (revision 245934) +++ pit_8254.c (working copy) @@ -29,15 +29,23 @@ #include __FBSDID("$FreeBSD$"); +#include #include +#include #include +#include #include -#include +#include +#include +#include + #include "bhyverun.h" #include "inout.h" +#include "ioapic.h" +#include "mevent.h" #include "pit_8254.h" #define TIMER_SEL_MASK 0xc0 @@ -52,14 +60,30 @@ struct counter { 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; }; +#include static void +pg_debug(char *fmt, ...) +{ + char buf[120]; + va_list ap; + + va_start(ap, fmt); + vsprintf(buf, fmt, ap); + va_end(ap); + write(0, buf, strlen(buf)); + write(0, '\r', 1); +} + +static void timevalfix(struct timeval *t1) { @@ -82,16 +106,39 @@ timevalfix(t1); } +static struct mevent *pit_tevp; +static uint64_t pit_mev_count; + static void -latch(struct counter *c) +pit_mev_callback(int fd, enum ev_type type, void *param) { + struct vmctx *ctx; + + ctx = param; + + pit_mev_count++; + + ioapic_assert_pin(ctx, 0); + ioapic_deassert_pin(ctx, 0); +} + +static void +pit_timer_start(struct vmctx *ctx, struct counter *c) +{ + if (pit_tevp == NULL) + pit_tevp = mevent_add(1, EVF_TIMER, pit_mev_callback, ctx); +} + +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 (c->olbyte != 0) - return; + if (latch && c->olbyte != 0) + return (0); if (c->initial == 0 || c->initial == 1) { /* @@ -114,9 +161,14 @@ delta_ticks = delta_nsecs / nsecs_per_tick; lval = c->initial - delta_ticks % c->initial; - c->olbyte = 2; - c->ol[1] = lval; /* LSB */ - c->ol[0] = lval >> 8; /* MSB */ + + if (latch) { + c->olbyte = 2; + c->ol[1] = lval; /* LSB */ + c->ol[0] = lval >> 8; /* MSB */ + } + + return (lval); } static int @@ -136,6 +188,7 @@ if (port == TIMER_MODE) { assert(in == 0); + pg_debug("-> mode %1x\n", val); sel = val & TIMER_SEL_MASK; rw = val & TIMER_RW_MASK; mode = val & TIMER_MODE_MASK; @@ -150,13 +203,15 @@ * Counter mode is not affected when issuing a * latch command. */ - if (mode != TIMER_RATEGEN && mode != TIMER_SQWAVE) + if (mode != TIMER_INTTC && + mode != TIMER_RATEGEN && + mode != TIMER_SQWAVE) return (-1); } c = &counter[sel >> 6]; if (rw == TIMER_LATCH) - latch(c); + pit_update_counter(c, 1); else c->olbyte = 0; /* reset latch after reprogramming */ @@ -169,20 +224,32 @@ if (in) { /* - * XXX * The spec says that once the output latch is completely - * read it should revert to "following" the counter. We don't - * do this because it is hard and any reasonable OS should - * always latch the counter before trying to read it. + * 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) - c->olbyte = 2; - *eax = c->ol[--c->olbyte]; + 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; + pg_debug("-> cntr %x init, %d\n", port, c->initial); + if (port == 0x40) + pit_timer_start(ctx, c); if (c->initial == 0) c->initial = 0xffff; gettimeofday(&c->tv, NULL);