xref: /openbsd-src/usr.sbin/vmd/i8253.c (revision 65bbee46cad7861cd5a570f338df9e976422e3ab)
1*65bbee46Sjsg /* $OpenBSD: i8253.c,v 1.42 2024/09/26 01:45:13 jsg Exp $ */
2ecc93de1Smlarkin /*
3ecc93de1Smlarkin  * Copyright (c) 2016 Mike Larkin <mlarkin@openbsd.org>
4ecc93de1Smlarkin  *
5ecc93de1Smlarkin  * Permission to use, copy, modify, and distribute this software for any
6ecc93de1Smlarkin  * purpose with or without fee is hereby granted, provided that the above
7ecc93de1Smlarkin  * copyright notice and this permission notice appear in all copies.
8ecc93de1Smlarkin  *
9ecc93de1Smlarkin  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10ecc93de1Smlarkin  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11ecc93de1Smlarkin  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12ecc93de1Smlarkin  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13ecc93de1Smlarkin  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14ecc93de1Smlarkin  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15ecc93de1Smlarkin  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16ecc93de1Smlarkin  */
17ecc93de1Smlarkin 
18ecc93de1Smlarkin #include <sys/time.h>
19ecc93de1Smlarkin #include <sys/types.h>
20ecc93de1Smlarkin 
21ecc93de1Smlarkin #include <dev/ic/i8253reg.h>
22ba66f564Sdv #include <dev/vmm/vmm.h>
23ecc93de1Smlarkin 
24ecc93de1Smlarkin #include <event.h>
25ecc93de1Smlarkin #include <string.h>
2694c97f60Scheloha #include <time.h>
27149417b6Sreyk #include <unistd.h>
28ecc93de1Smlarkin 
29ecc93de1Smlarkin #include "i8253.h"
3008fd0ce3Spd #include "vmd.h"
31149417b6Sreyk #include "atomicio.h"
32ecc93de1Smlarkin 
33ecc93de1Smlarkin extern char *__progname;
34ecc93de1Smlarkin 
35ecc93de1Smlarkin /*
36d23749a0Smlarkin  * Channel 0 is used to generate the legacy hardclock interrupt (HZ).
3703fd2d06Smlarkin  * Channels 1 and 2 can be used by the guest OS as regular timers,
3803fd2d06Smlarkin  * but channel 2 is not connected to any pcppi(4)-like device. Like
3903fd2d06Smlarkin  * a regular PC, channel 2 status can also be read from port 0x61.
40ecc93de1Smlarkin  */
41d23749a0Smlarkin struct i8253_channel i8253_channel[3];
42ecc93de1Smlarkin 
4308fd0ce3Spd static struct vm_dev_pipe dev_pipe;
4408fd0ce3Spd 
4508fd0ce3Spd /*
4608fd0ce3Spd  * i8253_pipe_dispatch
4708fd0ce3Spd  *
4808fd0ce3Spd  * Reads a message off the pipe, expecting one that corresponds to a
4908fd0ce3Spd  * reset request for a specific channel.
5008fd0ce3Spd  */
5108fd0ce3Spd static void
5208fd0ce3Spd i8253_pipe_dispatch(int fd, short event, void *arg)
5308fd0ce3Spd {
5408fd0ce3Spd 	enum pipe_msg_type msg;
5508fd0ce3Spd 
5608fd0ce3Spd 	msg = vm_pipe_recv(&dev_pipe);
5708fd0ce3Spd 	switch (msg) {
5808fd0ce3Spd 	case I8253_RESET_CHAN_0:
5908fd0ce3Spd 		i8253_reset(0);
6008fd0ce3Spd 		break;
6108fd0ce3Spd 	case I8253_RESET_CHAN_1:
6208fd0ce3Spd 		i8253_reset(1);
6308fd0ce3Spd 		break;
6408fd0ce3Spd 	case I8253_RESET_CHAN_2:
6508fd0ce3Spd 		i8253_reset(2);
6608fd0ce3Spd 		break;
6708fd0ce3Spd 	default:
6808fd0ce3Spd 		fatalx("%s: unexpected pipe message %d", __func__, msg);
6908fd0ce3Spd 	}
7008fd0ce3Spd }
7108fd0ce3Spd 
72ecc93de1Smlarkin /*
73ecc93de1Smlarkin  * i8253_init
74ecc93de1Smlarkin  *
75ecc93de1Smlarkin  * Initialize the emulated i8253 PIT.
76ecc93de1Smlarkin  *
77ecc93de1Smlarkin  * Parameters:
78ecc93de1Smlarkin  *  vm_id: vmm(4)-assigned ID of the VM
79ecc93de1Smlarkin  */
80ecc93de1Smlarkin void
81ecc93de1Smlarkin i8253_init(uint32_t vm_id)
82ecc93de1Smlarkin {
83d23749a0Smlarkin 	memset(&i8253_channel, 0, sizeof(struct i8253_channel));
8494c97f60Scheloha 	clock_gettime(CLOCK_MONOTONIC, &i8253_channel[0].ts);
85d23749a0Smlarkin 	i8253_channel[0].start = 0xFFFF;
86d23749a0Smlarkin 	i8253_channel[0].mode = TIMER_INTTC;
87d23749a0Smlarkin 	i8253_channel[0].last_r = 1;
88d23749a0Smlarkin 	i8253_channel[0].vm_id = vm_id;
8903fd2d06Smlarkin 	i8253_channel[0].state = 0;
906cc75de0Smlarkin 
91d23749a0Smlarkin 	i8253_channel[1].start = 0xFFFF;
92d23749a0Smlarkin 	i8253_channel[1].mode = TIMER_INTTC;
93d23749a0Smlarkin 	i8253_channel[1].last_r = 1;
94d23749a0Smlarkin 	i8253_channel[1].vm_id = vm_id;
9503fd2d06Smlarkin 	i8253_channel[1].state = 0;
966cc75de0Smlarkin 
97d23749a0Smlarkin 	i8253_channel[2].start = 0xFFFF;
98d23749a0Smlarkin 	i8253_channel[2].mode = TIMER_INTTC;
99d23749a0Smlarkin 	i8253_channel[2].last_r = 1;
100d23749a0Smlarkin 	i8253_channel[2].vm_id = vm_id;
10103fd2d06Smlarkin 	i8253_channel[2].state = 0;
1026cc75de0Smlarkin 
103d23749a0Smlarkin 	evtimer_set(&i8253_channel[0].timer, i8253_fire, &i8253_channel[0]);
104d23749a0Smlarkin 	evtimer_set(&i8253_channel[1].timer, i8253_fire, &i8253_channel[1]);
105d23749a0Smlarkin 	evtimer_set(&i8253_channel[2].timer, i8253_fire, &i8253_channel[2]);
10608fd0ce3Spd 
10708fd0ce3Spd 	vm_pipe_init(&dev_pipe, i8253_pipe_dispatch);
10808fd0ce3Spd 	event_add(&dev_pipe.read_ev, NULL);
109ecc93de1Smlarkin }
110ecc93de1Smlarkin 
111ecc93de1Smlarkin /*
112764091f3Smlarkin  * i8253_do_readback
113764091f3Smlarkin  *
114764091f3Smlarkin  * Handles the readback status command. The readback status command latches
115764091f3Smlarkin  * the current counter value plus various status bits.
116764091f3Smlarkin  *
117764091f3Smlarkin  * Parameters:
118764091f3Smlarkin  *  data: The command word written by the guest VM
119764091f3Smlarkin  */
120764091f3Smlarkin void
121764091f3Smlarkin i8253_do_readback(uint32_t data)
122764091f3Smlarkin {
12394c97f60Scheloha 	struct timespec now, delta;
124764091f3Smlarkin 	uint64_t ns, ticks;
12594c97f60Scheloha 	int readback_channel[3] = { TIMER_RB_C0, TIMER_RB_C1, TIMER_RB_C2 };
12694c97f60Scheloha 	int i;
127764091f3Smlarkin 
128764091f3Smlarkin 	/* bits are inverted here - !TIMER_RB_STATUS == enable chan readback */
1296cc75de0Smlarkin 	if (data & ~TIMER_RB_STATUS) {
130d23749a0Smlarkin 		i8253_channel[0].rbs = (data & TIMER_RB_C0) ? 1 : 0;
131d23749a0Smlarkin 		i8253_channel[1].rbs = (data & TIMER_RB_C1) ? 1 : 0;
132d23749a0Smlarkin 		i8253_channel[2].rbs = (data & TIMER_RB_C2) ? 1 : 0;
1336cc75de0Smlarkin 	}
134764091f3Smlarkin 
135764091f3Smlarkin 	/* !TIMER_RB_COUNT == enable counter readback */
136764091f3Smlarkin 	if (data & ~TIMER_RB_COUNT) {
137f53d796aScheloha 		clock_gettime(CLOCK_MONOTONIC, &now);
13894c97f60Scheloha 		for (i = 0; i < 3; i++) {
13994c97f60Scheloha 			if (data & readback_channel[i]) {
14094c97f60Scheloha 				timespecsub(&now, &i8253_channel[i].ts, &delta);
14194c97f60Scheloha 				ns = delta.tv_sec * 1000000000 + delta.tv_nsec;
142764091f3Smlarkin 				ticks = ns / NS_PER_TICK;
14394c97f60Scheloha 				if (i8253_channel[i].start)
14494c97f60Scheloha 					i8253_channel[i].olatch =
14594c97f60Scheloha 					    i8253_channel[i].start -
14694c97f60Scheloha 					    ticks % i8253_channel[i].start;
147764091f3Smlarkin 				else
14894c97f60Scheloha 					i8253_channel[i].olatch = 0;
149764091f3Smlarkin 			}
1506cc75de0Smlarkin 		}
151764091f3Smlarkin 	}
152764091f3Smlarkin }
153764091f3Smlarkin 
154764091f3Smlarkin /*
15503fd2d06Smlarkin  * vcpu_exit_i8253_misc
15603fd2d06Smlarkin  *
15703fd2d06Smlarkin  * Handles the 0x61 misc i8253 PIT register in/out exits.
15803fd2d06Smlarkin  *
15903fd2d06Smlarkin  * Parameters:
16003fd2d06Smlarkin  *  vrp: vm run parameters containing exit information for the I/O
16103fd2d06Smlarkin  *      instruction being performed
16203fd2d06Smlarkin  *
16303fd2d06Smlarkin  * Return value:
16403fd2d06Smlarkin  *  Always 0xFF (no interrupt should be injected)
16503fd2d06Smlarkin  */
16603fd2d06Smlarkin uint8_t
16703fd2d06Smlarkin vcpu_exit_i8253_misc(struct vm_run_params *vrp)
16803fd2d06Smlarkin {
16902ee787fSmlarkin 	struct vm_exit *vei = vrp->vrp_exit;
1705272a52dSmlarkin 	uint16_t cur;
1715272a52dSmlarkin 	uint64_t ns, ticks;
1725272a52dSmlarkin 	struct timespec now, delta;
17303fd2d06Smlarkin 
17403fd2d06Smlarkin 	if (vei->vei.vei_dir == VEI_DIR_IN) {
17503fd2d06Smlarkin 		/* Port 0x61[5] = counter channel 2 state */
1765272a52dSmlarkin 		if (i8253_channel[2].mode == TIMER_INTTC) {
17703fd2d06Smlarkin 			if (i8253_channel[2].state) {
17803fd2d06Smlarkin 				set_return_data(vei, (1 << 5));
1795272a52dSmlarkin 				log_debug("%s: counter 2 fired, returning "
1805272a52dSmlarkin 				    "0x20", __func__);
18103fd2d06Smlarkin 			} else {
18203fd2d06Smlarkin 				set_return_data(vei, 0);
18388373b6aSmlarkin 				log_debug("%s: counter 2 clear, returning 0x0",
18488373b6aSmlarkin 				    __func__);
18503fd2d06Smlarkin 			}
1865272a52dSmlarkin 		} else if (i8253_channel[2].mode == TIMER_SQWAVE) {
1875272a52dSmlarkin 			clock_gettime(CLOCK_MONOTONIC, &now);
1885272a52dSmlarkin 			timespecsub(&now, &i8253_channel[2].ts, &delta);
1895272a52dSmlarkin 			ns = delta.tv_sec * 1000000000 + delta.tv_nsec;
1905272a52dSmlarkin 			ticks = ns / NS_PER_TICK;
1915272a52dSmlarkin 			if (i8253_channel[2].start) {
1925272a52dSmlarkin 				cur = i8253_channel[2].start -
1935272a52dSmlarkin 				    ticks % i8253_channel[2].start;
1945272a52dSmlarkin 
1955272a52dSmlarkin 				if (cur > i8253_channel[2].start / 2)
1965272a52dSmlarkin 					set_return_data(vei, 1);
1975272a52dSmlarkin 				else
1985272a52dSmlarkin 					set_return_data(vei, 0);
1995272a52dSmlarkin 			}
2005272a52dSmlarkin 		}
20103fd2d06Smlarkin 	} else {
2026dc2ac0bSclaudio 		log_debug("%s: discarding data written to PIT misc port",
20303fd2d06Smlarkin 		    __func__);
20403fd2d06Smlarkin 	}
20503fd2d06Smlarkin 
20603fd2d06Smlarkin 	return 0xFF;
20703fd2d06Smlarkin }
20803fd2d06Smlarkin 
20903fd2d06Smlarkin /*
210ecc93de1Smlarkin  * vcpu_exit_i8253
211ecc93de1Smlarkin  *
212ecc93de1Smlarkin  * Handles emulated i8253 PIT access (in/out instruction to PIT ports).
213ecc93de1Smlarkin  *
214ecc93de1Smlarkin  * Parameters:
215ecc93de1Smlarkin  *  vrp: vm run parameters containing exit information for the I/O
216ecc93de1Smlarkin  *      instruction being performed
217ecc93de1Smlarkin  *
218ecc93de1Smlarkin  * Return value:
219ecc93de1Smlarkin  *  Interrupt to inject to the guest VM, or 0xFF if no interrupt should
220ecc93de1Smlarkin  *      be injected.
221ecc93de1Smlarkin  */
222ecc93de1Smlarkin uint8_t
223ecc93de1Smlarkin vcpu_exit_i8253(struct vm_run_params *vrp)
224ecc93de1Smlarkin {
225cd5b5ebbSmbuhl 	uint32_t out_data = 0;
2260ebc07deSmlarkin 	uint8_t sel, rw, data;
227ecc93de1Smlarkin 	uint64_t ns, ticks;
22894c97f60Scheloha 	struct timespec now, delta;
22902ee787fSmlarkin 	struct vm_exit *vei = vrp->vrp_exit;
230ecc93de1Smlarkin 
231b966d91aSmlarkin 	get_input_data(vei, &out_data);
232764091f3Smlarkin 
233ecc93de1Smlarkin 	if (vei->vei.vei_port == TIMER_CTRL) {
2346ee12970Smlarkin 		if (vei->vei.vei_dir == VEI_DIR_OUT) { /* OUT instruction */
235ecc93de1Smlarkin 			sel = out_data &
236ecc93de1Smlarkin 			    (TIMER_SEL0 | TIMER_SEL1 | TIMER_SEL2);
237ecc93de1Smlarkin 			sel = sel >> 6;
238764091f3Smlarkin 
239764091f3Smlarkin 			if (sel == 3) {
240764091f3Smlarkin 				i8253_do_readback(out_data);
241764091f3Smlarkin 				return (0xFF);
242ecc93de1Smlarkin 			}
243ecc93de1Smlarkin 
244764091f3Smlarkin 			rw = out_data & (TIMER_LATCH | TIMER_16BIT);
245ecc93de1Smlarkin 
246ecc93de1Smlarkin 			/*
247ecc93de1Smlarkin 			 * Since we don't truly emulate each tick of the PIT
248ecc93de1Smlarkin 			 * counter, when the guest asks for the timer to be
249ecc93de1Smlarkin 			 * latched, simulate what the counter would have been
250ecc93de1Smlarkin 			 * had we performed full emulation. We do this by
251ecc93de1Smlarkin 			 * calculating when the counter was reset vs how much
252ecc93de1Smlarkin 			 * time has elapsed, then bias by the counter tick
253ecc93de1Smlarkin 			 * rate.
254ecc93de1Smlarkin 			 */
255ecc93de1Smlarkin 			if (rw == TIMER_LATCH) {
25694c97f60Scheloha 				clock_gettime(CLOCK_MONOTONIC, &now);
25788373b6aSmlarkin 				timespecsub(&now, &i8253_channel[sel].ts,
25888373b6aSmlarkin 				    &delta);
25994c97f60Scheloha 				ns = delta.tv_sec * 1000000000 + delta.tv_nsec;
260ecc93de1Smlarkin 				ticks = ns / NS_PER_TICK;
261d23749a0Smlarkin 				if (i8253_channel[sel].start) {
262d23749a0Smlarkin 					i8253_channel[sel].olatch =
263d23749a0Smlarkin 					    i8253_channel[sel].start -
264d23749a0Smlarkin 					    ticks % i8253_channel[sel].start;
265764091f3Smlarkin 				} else
266d23749a0Smlarkin 					i8253_channel[sel].olatch = 0;
267ecc93de1Smlarkin 				goto ret;
268ecc93de1Smlarkin 			} else if (rw != TIMER_16BIT) {
269ecc93de1Smlarkin 				log_warnx("%s: i8253 PIT: unsupported counter "
270ecc93de1Smlarkin 				    "%d rw mode 0x%x selected", __func__,
271ecc93de1Smlarkin 				    sel, (rw & TIMER_16BIT));
272ecc93de1Smlarkin 			}
2730ebc07deSmlarkin 			i8253_channel[sel].mode = (out_data & 0xe) >> 1;
274ecc93de1Smlarkin 
275ecc93de1Smlarkin 			goto ret;
276ecc93de1Smlarkin 		} else {
277ecc93de1Smlarkin 			log_warnx("%s: i8253 PIT: read from control port "
278ecc93de1Smlarkin 			    "unsupported", __progname);
279764091f3Smlarkin 			set_return_data(vei, 0);
280ecc93de1Smlarkin 		}
281ecc93de1Smlarkin 	} else {
282ecc93de1Smlarkin 		sel = vei->vei.vei_port - (TIMER_CNTR0 + TIMER_BASE);
283ecc93de1Smlarkin 
2846ee12970Smlarkin 		if (vei->vei.vei_dir == VEI_DIR_OUT) { /* OUT instruction */
285d23749a0Smlarkin 			if (i8253_channel[sel].last_w == 0) {
286d23749a0Smlarkin 				i8253_channel[sel].ilatch |= (out_data & 0xff);
287d23749a0Smlarkin 				i8253_channel[sel].last_w = 1;
288ecc93de1Smlarkin 			} else {
289718d01bdSmlarkin 				i8253_channel[sel].ilatch |=
290718d01bdSmlarkin 				    ((out_data & 0xff) << 8);
291d23749a0Smlarkin 				i8253_channel[sel].start =
292d23749a0Smlarkin 				    i8253_channel[sel].ilatch;
293d23749a0Smlarkin 				i8253_channel[sel].last_w = 0;
294ecc93de1Smlarkin 
295d23749a0Smlarkin 				if (i8253_channel[sel].start == 0)
296d23749a0Smlarkin 					i8253_channel[sel].start = 0xffff;
2976cc75de0Smlarkin 
298445bc9d2Sdv 				DPRINTF("%s: channel %d reset, mode=%d, "
299445bc9d2Sdv 				    "start=%d\n", __func__,
3000ebc07deSmlarkin 				    sel, i8253_channel[sel].mode,
3010ebc07deSmlarkin 				    i8253_channel[sel].start);
3020ebc07deSmlarkin 
30308fd0ce3Spd 				vm_pipe_send(&dev_pipe, sel);
304ecc93de1Smlarkin 			}
305ecc93de1Smlarkin 		} else {
306d23749a0Smlarkin 			if (i8253_channel[sel].rbs) {
307d23749a0Smlarkin 				i8253_channel[sel].rbs = 0;
308d23749a0Smlarkin 				data = i8253_channel[sel].mode << 1;
309764091f3Smlarkin 				data |= TIMER_16BIT;
310764091f3Smlarkin 				set_return_data(vei, data);
311764091f3Smlarkin 				goto ret;
312764091f3Smlarkin 			}
313764091f3Smlarkin 
314d23749a0Smlarkin 			if (i8253_channel[sel].last_r == 0) {
315e405a10eSmlarkin 				data = i8253_channel[sel].olatch >> 8;
316764091f3Smlarkin 				set_return_data(vei, data);
317d23749a0Smlarkin 				i8253_channel[sel].last_r = 1;
318ecc93de1Smlarkin 			} else {
319e405a10eSmlarkin 				data = i8253_channel[sel].olatch & 0xFF;
320764091f3Smlarkin 				set_return_data(vei, data);
321d23749a0Smlarkin 				i8253_channel[sel].last_r = 0;
322ecc93de1Smlarkin 			}
323ecc93de1Smlarkin 		}
324ecc93de1Smlarkin 	}
325ecc93de1Smlarkin 
326ecc93de1Smlarkin ret:
327ecc93de1Smlarkin 	return (0xFF);
328ecc93de1Smlarkin }
329ecc93de1Smlarkin 
330ecc93de1Smlarkin /*
331ecc93de1Smlarkin  * i8253_reset
332ecc93de1Smlarkin  *
333ecc93de1Smlarkin  * Resets the i8253's counter timer
334ecc93de1Smlarkin  *
335ecc93de1Smlarkin  * Parameters:
336ecc93de1Smlarkin  *  chn: counter ID. Only channel ID 0 is presently emulated.
337ecc93de1Smlarkin  */
338ecc93de1Smlarkin void
339ecc93de1Smlarkin i8253_reset(uint8_t chn)
340ecc93de1Smlarkin {
341ecc93de1Smlarkin 	struct timeval tv;
342ecc93de1Smlarkin 
343d23749a0Smlarkin 	evtimer_del(&i8253_channel[chn].timer);
344ecc93de1Smlarkin 	timerclear(&tv);
345ecc93de1Smlarkin 
34652e954a3Spd 	i8253_channel[chn].in_use = 1;
34703fd2d06Smlarkin 	i8253_channel[chn].state = 0;
348d23749a0Smlarkin 	tv.tv_usec = (i8253_channel[chn].start * NS_PER_TICK) / 1000;
3490ebc07deSmlarkin 	clock_gettime(CLOCK_MONOTONIC, &i8253_channel[chn].ts);
350d23749a0Smlarkin 	evtimer_add(&i8253_channel[chn].timer, &tv);
351ecc93de1Smlarkin }
352ecc93de1Smlarkin 
353ecc93de1Smlarkin /*
354ecc93de1Smlarkin  * i8253_fire
355ecc93de1Smlarkin  *
356ecc93de1Smlarkin  * Callback invoked when the 8253 PIT timer fires. This will assert
357ecc93de1Smlarkin  * IRQ0 on the legacy PIC attached to VCPU0.
358ecc93de1Smlarkin  *
359ecc93de1Smlarkin  * Parameters:
360ecc93de1Smlarkin  *  fd: unused
361ecc93de1Smlarkin  *  type: unused
362ecc93de1Smlarkin  *  arg: VM ID
363ecc93de1Smlarkin  */
364ecc93de1Smlarkin void
365ecc93de1Smlarkin i8253_fire(int fd, short type, void *arg)
366ecc93de1Smlarkin {
367ecc93de1Smlarkin 	struct timeval tv;
368d23749a0Smlarkin 	struct i8253_channel *ctr = (struct i8253_channel *)arg;
369ecc93de1Smlarkin 
370c4fd4c5bSdv 	vcpu_assert_irq(ctr->vm_id, 0, 0);
371ecc93de1Smlarkin 
3720ebc07deSmlarkin 	if (ctr->mode != TIMER_INTTC) {
3730ebc07deSmlarkin 		timerclear(&tv);
3740ebc07deSmlarkin 		tv.tv_usec = (ctr->start * NS_PER_TICK) / 1000;
3756cc75de0Smlarkin 		evtimer_add(&ctr->timer, &tv);
3760ebc07deSmlarkin 	} else
37703fd2d06Smlarkin 		ctr->state = 1;
378ecc93de1Smlarkin }
379149417b6Sreyk 
380149417b6Sreyk int
381149417b6Sreyk i8253_dump(int fd)
382149417b6Sreyk {
383149417b6Sreyk 	log_debug("%s: sending PIT", __func__);
384149417b6Sreyk 	if (atomicio(vwrite, fd, &i8253_channel, sizeof(i8253_channel)) !=
385149417b6Sreyk 	    sizeof(i8253_channel)) {
386149417b6Sreyk 		log_warnx("%s: error writing PIT to fd", __func__);
387149417b6Sreyk 		return (-1);
388149417b6Sreyk 	}
389149417b6Sreyk 	return (0);
390149417b6Sreyk }
391149417b6Sreyk 
392149417b6Sreyk int
393149417b6Sreyk i8253_restore(int fd, uint32_t vm_id)
394149417b6Sreyk {
39552e954a3Spd 	int i;
396149417b6Sreyk 	log_debug("%s: restoring PIT", __func__);
397149417b6Sreyk 	if (atomicio(read, fd, &i8253_channel, sizeof(i8253_channel)) !=
398149417b6Sreyk 	    sizeof(i8253_channel)) {
399149417b6Sreyk 		log_warnx("%s: error reading PIT from fd", __func__);
400149417b6Sreyk 		return (-1);
401149417b6Sreyk 	}
402149417b6Sreyk 
40352e954a3Spd 	for (i = 0; i < 3; i++) {
40452e954a3Spd 		memset(&i8253_channel[i].timer, 0, sizeof(struct event));
40552e954a3Spd 		i8253_channel[i].vm_id = vm_id;
40652e954a3Spd 		evtimer_set(&i8253_channel[i].timer, i8253_fire,
40752e954a3Spd 		    &i8253_channel[i]);
40852e954a3Spd 		i8253_reset(i);
40952e954a3Spd 	}
410bc11ef5cSdv 
411bc11ef5cSdv 	vm_pipe_init(&dev_pipe, i8253_pipe_dispatch);
412bc11ef5cSdv 
413149417b6Sreyk 	return (0);
414149417b6Sreyk }
415149417b6Sreyk 
416149417b6Sreyk void
41731106e66Stb i8253_stop(void)
418149417b6Sreyk {
41952e954a3Spd 	int i;
42052e954a3Spd 	for (i = 0; i < 3; i++)
42152e954a3Spd 		evtimer_del(&i8253_channel[i].timer);
422bc11ef5cSdv 	event_del(&dev_pipe.read_ev);
42352e954a3Spd }
42452e954a3Spd 
42552e954a3Spd void
42631106e66Stb i8253_start(void)
42752e954a3Spd {
42852e954a3Spd 	int i;
42952e954a3Spd 	for (i = 0; i < 3; i++)
43052e954a3Spd 		if (i8253_channel[i].in_use)
43152e954a3Spd 			i8253_reset(i);
432bc11ef5cSdv 	event_add(&dev_pipe.read_ev, NULL);
433149417b6Sreyk }
434