Index | Thread | Search

From:
Dave Voutila <dv@sisu.io>
Subject:
vmd: set i8253 output latch to 0 in INTTC mode
To:
tech@openbsd.org
Date:
Fri, 13 Feb 2026 09:55:17 -0500

Download raw body.

Thread
  • Dave Voutila:

    vmd: set i8253 output latch to 0 in INTTC mode

So found this while building a toy firmware for vmd.

We don't fully emulate all the nuances of a i8253 PIT, which is
generally fine. However, I needed a way to measure a fixed passage of
time without interrupts, so was trying to use the "interrupt on terminal
count" mode of the PIT and polling for it to output zero.

In INTTC mode, the output latch value should not wrap around when
hitting zero, but vmd isn't distinguishing this mode from the others and
will never precisely hit or stop at zero.

Our PIT isn't super accurate, but the way it works in this case is when
the timer event we set with libevent fires, .state toggles from 0
(counting) to 1 (fired) IFF we're in INTTC mode on that channel.

I'm no PIT expert...so other eyeballs welcome!

Otherwise, ok?

-dv

diff refs/heads/master refs/heads/codex-vmd-i8253-oneshot
commit - 7d8bc7d2b639aaf8b2bb352eaf8db563407cf7d0
commit + dc390c186d477b8b873ab66acb0855cb4fd56f23
blob - 9e32d9382b6aa5b340402e487c577e0702f22ca9
blob + 2bcd52417ec4e3d20d6435647cfe474ab6ea20b6
--- usr.sbin/vmd/i8253.c
+++ usr.sbin/vmd/i8253.c
@@ -135,6 +135,13 @@ i8253_do_readback(uint32_t data)
 		clock_gettime(CLOCK_MONOTONIC, &now);
 		for (i = 0; i < 3; i++) {
 			if (data & readback_channel[i]) {
+				if (i8253_channel[i].mode ==
+				    TIMER_INTTC &&
+				    i8253_channel[i].state) {
+					/* mode 0: latch 0 after fire */
+					i8253_channel[i].olatch = 0;
+					continue;
+				}
 				timespecsub(&now, &i8253_channel[i].ts, &delta);
 				ns = delta.tv_sec * 1000000000 + delta.tv_nsec;
 				ticks = ns / NS_PER_TICK;
@@ -251,6 +258,13 @@ vcpu_exit_i8253(struct vm_run_params *vrp)
 			 * rate.
 			 */
 			if (rw == TIMER_LATCH) {
+				if (i8253_channel[sel].mode ==
+				    TIMER_INTTC &&
+				    i8253_channel[sel].state) {
+					/* mode 0: latch 0 after fire */
+					i8253_channel[sel].olatch = 0;
+					goto ret;
+				}
 				clock_gettime(CLOCK_MONOTONIC, &now);
 				timespecsub(&now, &i8253_channel[sel].ts,
 				    &delta);