Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 19 Sep 2013 04:59:44 +0000 (UTC)
From:      Peter Grehan <grehan@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r255691 - head/usr.sbin/bhyve
Message-ID:  <201309190459.r8J4xiJN008528@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: grehan
Date: Thu Sep 19 04:59:44 2013
New Revision: 255691
URL: http://svnweb.freebsd.org/changeset/base/255691

Log:
  Implement support for the interrupt-on-terminal-count and
  s/w-strobe timer modes. These are commonly used by non-FreeBSD
  o/s's.
  
  Approved by:	re@ (blanket)

Modified:
  head/usr.sbin/bhyve/pit_8254.c

Modified: head/usr.sbin/bhyve/pit_8254.c
==============================================================================
--- head/usr.sbin/bhyve/pit_8254.c	Thu Sep 19 04:48:26 2013	(r255690)
+++ head/usr.sbin/bhyve/pit_8254.c	Thu Sep 19 04:59:44 2013	(r255691)
@@ -29,15 +29,23 @@
 #include <sys/cdefs.h>
 __FBSDID("$FreeBSD$");
 
+#include <sys/types.h>
 #include <sys/time.h>
+#include <machine/vmm.h>
 
 #include <machine/clock.h>
 
-#include <stdio.h>
 #include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <vmmapi.h>
 
 #include "bhyverun.h"
 #include "inout.h"
+#include "ioapic.h"
+#include "mevent.h"
 #include "pit_8254.h"
 
 #define	TIMER_SEL_MASK		0xc0
@@ -51,14 +59,19 @@ __FBSDID("$FreeBSD$");
 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)
 {
@@ -82,16 +95,55 @@ timevalsub(struct timeval *t1, const str
 	timevalfix(t1);
 }
 
+static uint64_t pit_mev_count;
+
 static void
-latch(struct counter *c)
+pit_mevent_cb(int fd, enum ev_type type, void *param)
+{
+	struct counter *c;
+
+	c = param;
+
+	pit_mev_count++;
+
+	ioapic_assert_pin(c->ctx, 0);
+	ioapic_deassert_pin(c->ctx, 0);
+
+	/*
+	 * 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 (c->olbyte != 0)
-		return;
+	if (latch && c->olbyte != 0)
+		return (0);
 
 	if (c->initial == 0 || c->initial == 1) {
 		/*
@@ -114,9 +166,14 @@ latch(struct counter *c)
 	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
@@ -150,13 +207,18 @@ pit_8254_handler(struct vmctx *ctx, int 
 			 * 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 &&
+			    mode != TIMER_SWSTROBE)
 				return (-1);
 		}
 		
 		c = &counter[sel >> 6];
+		c->ctx = ctx;
+		c->mode = mode;
 		if (rw == TIMER_LATCH)
-			latch(c);
+			pit_update_counter(c, 1);
 		else
 			c->olbyte = 0;	/* reset latch after reprogramming */
 		
@@ -169,20 +231,32 @@ pit_8254_handler(struct vmctx *ctx, int 
 
 	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;
+			/* 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);



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201309190459.r8J4xiJN008528>