1 /*-
2  * Copyright (c) 2018-2022 Intel Corporation.
3  * Copyright (c) 2014 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
4  * Copyright (c) 2011 NetApp, Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <pthread.h>
30 #include <errno.h>
31 #include <stdbool.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <err.h>
35 #include <sysexits.h>
36 #include <string.h>
37 
38 #include "vmmapi.h"
39 #include "timer.h"
40 #include "inout.h"
41 #include "pit.h"
42 #include "log.h"
43 
44 #define	TMR2_OUT_STS		0x20
45 
46 #define	PIT_8254_FREQ		(1193182)
47 #define	PIT_HZ_TO_TICKS(hz)	((PIT_8254_FREQ + (hz) / 2) / (hz))
48 
49 #define PERIODIC_MODE(mode)	\
50 	((mode) == TIMER_RATEGEN || (mode) == TIMER_SQWAVE)
51 
52 #define	VPIT_LOCK()													\
53 	do {															\
54 		int err;													\
55 		err = pthread_mutex_lock(&vpit_mtx);						\
56 		if (err)													\
57 			pr_dbg("pthread_mutex_lock returned %s", strerror(err));\
58 	} while (0)
59 
60 #define	VPIT_UNLOCK()												\
61 	do {															\
62 		int err;													\
63 		err = pthread_mutex_unlock(&vpit_mtx);						\
64 		if (err)													\
65 			pr_dbg("pthread_mutex_unlock returned %s", strerror(err));\
66 	} while (0)
67 
68 #define vpit_ts_to_ticks(ts)	ts_to_ticks(PIT_8254_FREQ, ts)
69 /* won't overflow since the max value of ticks is 65536 */
70 #define vpit_ticks_to_ts(tk, ts)	ticks_to_ts(PIT_8254_FREQ, tk, ts)
71 
72 
73 struct vpit_timer_arg {
74 	struct vpit	*vpit;
75 	int		channel_num;
76 	bool	active;
77 } vpit_timer_arg[3];
78 
79 struct channel {
80 	int		mode;
81 	uint32_t	initial;	/* initial counter value */
82 	struct timespec start_ts;	/* uptime when counter was loaded */
83 	uint8_t		cr[2];
84 	uint8_t		ol[2];
85 	bool		nullcnt;
86 	bool		slatched;	/* status latched */
87 	uint8_t		status;
88 	int		crbyte;
89 	int		olbyte;
90 	int		frbyte;
91 	int		timer_idx;	/* only used by counter 0 */
92 	timer_t	timer_id;	/* only used by counter 0 */
93 };
94 
95 struct vpit {
96 	struct vmctx	*vm;
97 	struct channel	channel[3];
98 };
99 
100 
101 /* one vPIT per VM */
102 static pthread_mutex_t vpit_mtx = PTHREAD_MUTEX_INITIALIZER;
103 
104 
105 static uint64_t
ticks_elapsed_since(const struct timespec * since)106 ticks_elapsed_since(const struct timespec *since)
107 {
108 	struct timespec ts;
109 
110 	if (clock_gettime(CLOCK_MONOTONIC, &ts))
111 		pr_dbg("clock_gettime returned: %s", strerror(errno));
112 
113 	if (timespeccmp(&ts, since, <=))
114 		return 0;
115 
116 	timespecsub(&ts, since);
117 	return vpit_ts_to_ticks(&ts);
118 }
119 
120 static bool
pit_cntr0_timer_running(struct vpit * vpit)121 pit_cntr0_timer_running(struct vpit *vpit)
122 {
123 	struct channel *c;
124 
125 	c = &vpit->channel[0];
126 	return vpit_timer_arg[c->timer_idx].active;
127 }
128 
129 static int
vpit_get_out(struct vpit * vpit,int channel,uint64_t delta_ticks)130 vpit_get_out(struct vpit *vpit, int channel, uint64_t delta_ticks)
131 {
132 	struct channel *c;
133 	bool initval;
134 	int out = 1;
135 
136 	c = &vpit->channel[channel];
137 	initval = c->nullcnt;
138 
139 	/* XXX only channel 0 emulates delayed CE loading */
140 	if (channel == 0 && PERIODIC_MODE(c->mode)) {
141 		initval = initval && !pit_cntr0_timer_running(vpit);
142 	}
143 
144 	switch (c->mode) {
145 	case TIMER_INTTC:
146 		/*
147 		 * For mode 0, see if the elapsed time is greater
148 		 * than the initial value - this results in the
149 		 * output pin being set to 1 in the status byte.
150 		 */
151 		out = (initval) ? 0 : (delta_ticks >= c->initial);
152 		break;
153 	case TIMER_RATEGEN:
154 		out = (initval) ?
155 			1 : (delta_ticks % c->initial != c->initial - 1);
156 		break;
157 	case TIMER_SQWAVE:
158 		out = (initval) ?
159 			1 : (delta_ticks % c->initial < (c->initial + 1) / 2);
160 		break;
161 	case TIMER_SWSTROBE:
162 		out = (initval) ? 1 : (delta_ticks != c->initial);
163 		break;
164 	default:
165 		pr_warn("vpit invalid timer mode: %d", c->mode);
166 	}
167 
168 	return out;
169 }
170 
171 static uint32_t
pit_cr_val(uint8_t cr[2])172 pit_cr_val(uint8_t cr[2])
173 {
174 	uint32_t val;
175 
176 	val = cr[0] | (uint16_t)cr[1] << 8;
177 
178 	/* CR == 0 means 2^16 for binary counting */
179 	if (val == 0) {
180 		val = 0x10000;
181 	}
182 
183 	return val;
184 }
185 
186 static void
pit_load_ce(struct channel * c)187 pit_load_ce(struct channel *c)
188 {
189 	/* no CR update in progress */
190 	if (c->nullcnt && c->crbyte == 2) {
191 		c->initial = pit_cr_val(c->cr);
192 		c->nullcnt = false;
193 		c->crbyte = 0;
194 
195 		if (clock_gettime(CLOCK_MONOTONIC, &c->start_ts))
196 			pr_dbg("clock_gettime returned: %s", strerror(errno));
197 
198 		if (c->initial == 0 || c->initial > 0x10000) {
199 			pr_dbg("vpit invalid initial count: 0x%x - use 0x10000",
200 					c->initial);
201 			c->initial = 0x10000;
202 		}
203 	}
204 }
205 
206 /*
207  * Dangling timer callbacks must have a way to synchronize
208  * with timer deletions and deinit.
209  */
210 static void
vpit_timer_handler(union sigval s)211 vpit_timer_handler(union sigval s)
212 {
213 	struct vpit_timer_arg *arg;
214 	struct vpit *vpit;
215 	struct channel *c;
216 
217 	arg = s.sival_ptr;
218 
219 	VPIT_LOCK();
220 
221 	/* skip if timer is no longer active */
222 	if (!arg->active) {
223 		goto done;
224 	}
225 
226 	/* it's now safe to use the vpit pointer */
227 	vpit = arg->vpit;
228 
229 	if (vpit == NULL) {
230 		pr_dbg("vpit is NULL");
231 		goto done;
232 	}
233 
234 	c = &vpit->channel[arg->channel_num];
235 
236 	/* generate a rising edge on OUT */
237 	vm_set_gsi_irq(vpit->vm, PIT_IOAPIC_IRQ, GSI_RAISING_PULSE);
238 
239 	/* CR -> CE if necessary */
240 	pit_load_ce(c);
241 
242 done:
243 	VPIT_UNLOCK();
244 }
245 
246 static bool
pit_timer_stop_cntr0(struct vpit * vpit,struct itimerspec * rem)247 pit_timer_stop_cntr0(struct vpit *vpit, struct itimerspec *rem)
248 {
249 	struct channel *c;
250 	bool active;
251 
252 	c = &vpit->channel[0];
253 	active = pit_cntr0_timer_running(vpit);
254 
255 	if (active) {
256 		vpit_timer_arg[c->timer_idx].active = false;
257 
258 		if (rem) {
259 			if (timer_gettime(c->timer_id, rem))
260 				pr_dbg("timer_gettime returned: %s", strerror(errno));
261 		}
262 
263 		if (timer_delete(c->timer_id))
264 			pr_dbg("timer_delete returned: %s", strerror(errno));
265 
266 		if (++c->timer_idx == nitems(vpit_timer_arg))
267 			c->timer_idx = 0;
268 
269 		if (pit_cntr0_timer_running(vpit)) {
270 			pr_dbg("vpit timer %d is still active", c->timer_idx);
271 			vpit_timer_arg[c->timer_idx].active = false;
272 		}
273 	}
274 
275 	return active;
276 }
277 
278 static void
pit_timer_start_cntr0(struct vpit * vpit)279 pit_timer_start_cntr0(struct vpit *vpit)
280 {
281 	struct channel *c;
282 	struct itimerspec ts = { 0 };
283 	struct sigevent sigevt = { 0 };
284 
285 	c = &vpit->channel[0];
286 
287 	if (pit_timer_stop_cntr0(vpit, &ts) && PERIODIC_MODE(c->mode)) {
288 		/*
289 		 * Counter is being updated while counting in periodic mode.
290 		 * Update CE at the end of the current counting cycle.
291 		 *
292 		 * XXX
293 		 * pit_update_counter() or vpit_get_out() can be called
294 		 * right before vpit_timer_handler(), causing it to use a
295 		 * wrapped-around value that's inconsistent with the spec.
296 		 *
297 		 * On real hardware, mode 3 requires CE to be updated at the
298 		 * end of its current half-cycle. We operate as if CR is
299 		 * always updated in the second half-cycle (before a rising
300 		 * edge on OUT).
301 		 */
302 		if (!timespecisset(&ts.it_interval))
303 			pr_dbg("vpit is in periodic mode but with a one-shot timer");
304 
305 		/* ts.it_value contains the remaining time until expiration */
306 		vpit_ticks_to_ts(pit_cr_val(c->cr), &ts.it_interval);
307 	} else {
308 		/*
309 		 * Aperiodic mode or no running periodic counter.
310 		 * Update CE immediately.
311 		 */
312 		uint64_t timer_ticks;
313 
314 		pit_load_ce(c);
315 
316 		timer_ticks = (c->mode == TIMER_SWSTROBE) ?
317 		              c->initial + 1 : c->initial;
318 		vpit_ticks_to_ts(timer_ticks, &ts.it_value);
319 
320 		/* make it periodic if required */
321 		if (PERIODIC_MODE(c->mode))
322 			ts.it_interval = ts.it_value;
323 		else if (timespecisset(&ts.it_interval)) {
324 			pr_dbg("vpit is in aperiodic mode but with a periodic timer");
325 			memset(&ts.it_interval, 0, sizeof(ts.it_interval));
326 		}
327 	}
328 
329 	sigevt.sigev_value.sival_ptr = &vpit_timer_arg[c->timer_idx];
330 	sigevt.sigev_notify = SIGEV_THREAD;
331 	sigevt.sigev_notify_function = vpit_timer_handler;
332 
333 	if (timer_create(CLOCK_MONOTONIC, &sigevt, &c->timer_id))
334 		pr_dbg("timer_create returned: %s", strerror(errno));
335 
336 	vpit_timer_arg[c->timer_idx].active = true;
337 
338 	/* arm the timer */
339 	if (timer_settime(c->timer_id, 0, &ts, NULL))
340 		pr_dbg("timer_settime returned: %s", strerror(errno));
341 }
342 
343 static uint16_t
pit_update_counter(struct vpit * vpit,struct channel * c,bool latch,uint64_t * ticks_elapsed)344 pit_update_counter(struct vpit *vpit, struct channel *c, bool latch,
345 		uint64_t *ticks_elapsed)
346 {
347 	uint16_t lval = 0;
348 	uint64_t delta_ticks;
349 
350 	if (c->initial == 0) {
351 		/*
352 		 * This is possibly an o/s bug - reading the value of
353 		 * the timer without having set up the initial value.
354 		 *
355 		 * The original Bhyve user-space version of this code
356 		 * set the timer to 100hz in this condition; do the same
357 		 * here.
358 		 */
359 		pr_warn("vpit reading uninitialized counter value\n");
360 
361 		c->initial = PIT_HZ_TO_TICKS(100);
362 		delta_ticks = 0;
363 		if (clock_gettime(CLOCK_MONOTONIC, &c->start_ts))
364 			pr_dbg("clock_gettime returned: %s", strerror(errno));
365 	} else
366 		delta_ticks = ticks_elapsed_since(&c->start_ts);
367 
368 	switch (c->mode) {
369 	case TIMER_INTTC:
370 	case TIMER_SWSTROBE:
371 		lval = c->initial - delta_ticks;
372 		break;
373 	case TIMER_RATEGEN:
374 		lval = c->initial - delta_ticks % c->initial;
375 		break;
376 	case TIMER_SQWAVE: {
377 		uint64_t t = delta_ticks % c->initial;
378 
379 		if (t >= (c->initial + 1) / 2)
380 			t -= (c->initial + 1) / 2;
381 
382 		lval = (c->initial & ~0x1) - (t * 2);
383 		break;
384 	}
385 	default:
386 		pr_warn("vpit invalid timer mode: %d", c->mode);
387 	}
388 
389 	/* cannot latch a new value until the old one has been consumed */
390 	if (latch && c->olbyte == 0) {
391 		c->olbyte = 2;
392 		c->ol[1] = lval;		/* LSB */
393 		c->ol[0] = lval >> 8;		/* MSB */
394 	}
395 
396 	*ticks_elapsed = delta_ticks;
397 	return lval;
398 }
399 
400 static int
pit_readback1(struct vpit * vpit,int channel,uint8_t cmd)401 pit_readback1(struct vpit *vpit, int channel, uint8_t cmd)
402 {
403 	struct channel *c;
404 	uint64_t delta_ticks;
405 
406 	c = &vpit->channel[channel];
407 
408 	/*
409 	 * Latch the count/status of the timer if not already latched.
410 	 * N.B. that the count/status latch-select bits are active-low.
411 	 */
412 	pit_update_counter(vpit, c, !(cmd & TIMER_RB_LCTR), &delta_ticks);
413 
414 	if (!(cmd & TIMER_RB_LSTATUS) && !c->slatched) {
415 		c->slatched = true;
416 
417 		/* status byte is only updated upon latching */
418 		c->status = TIMER_16BIT | c->mode;
419 
420 		if (c->nullcnt) {
421 			c->status |= TIMER_STS_NULLCNT;
422 		}
423 
424 		/* use the same delta_ticks for both latches */
425 		if (vpit_get_out(vpit, channel, delta_ticks)) {
426 			c->status |= TIMER_STS_OUT;
427 		}
428 	}
429 
430 	return (0);
431 }
432 
433 static int
pit_readback(struct vpit * vpit,uint8_t cmd)434 pit_readback(struct vpit *vpit, uint8_t cmd)
435 {
436 	int error = 0;
437 
438 	/*
439 	 * The readback command can apply to all timers.
440 	 */
441 	if (cmd & TIMER_RB_CTR_0) {
442 		error = pit_readback1(vpit, 0, cmd);
443 	}
444 	if (!error && cmd & TIMER_RB_CTR_1) {
445 		error = pit_readback1(vpit, 1, cmd);
446 	}
447 	if (!error && cmd & TIMER_RB_CTR_2) {
448 		error = pit_readback1(vpit, 2, cmd);
449 	}
450 
451 	return error;
452 }
453 
454 
455 static int
vpit_update_mode(struct vpit * vpit,uint8_t val)456 vpit_update_mode(struct vpit *vpit, uint8_t val)
457 {
458 	struct channel *c;
459 	int sel, rw, mode, channel;
460 	uint64_t delta_ticks;
461 
462 	sel = val & TIMER_SEL_MASK;
463 	rw = val & TIMER_RW_MASK;
464 	mode = val & TIMER_MODE_MASK;
465 
466 	if (sel == TIMER_SEL_READBACK) {
467 		return pit_readback(vpit, val);
468 	}
469 
470 	if (rw != TIMER_LATCH) {
471 		if (rw != TIMER_16BIT) {
472 			pr_err("vpit unsupported rw: 0x%x\n", rw);
473 			return (-1);
474 		}
475 
476 		/*
477 		 * Counter mode is not affected when issuing a
478 		 * latch command.
479 		 */
480 		if (mode != TIMER_INTTC &&
481 		    !PERIODIC_MODE(mode & ~TIMER_MODE_DONT_CARE_MASK) &&
482 		    mode != TIMER_SWSTROBE) {
483 			pr_err("vpit unsupported mode: 0x%x\n", mode);
484 			return (-1);
485 		}
486 	}
487 
488 	channel = sel >> 6;
489 	c = &vpit->channel[channel];
490 
491 	if (rw == TIMER_LATCH) {
492 		pit_update_counter(vpit, c, true, &delta_ticks);
493 	} else {
494 		if (mode == (TIMER_MODE_DONT_CARE_MASK | TIMER_RATEGEN) ||
495 		    mode == (TIMER_MODE_DONT_CARE_MASK | TIMER_SQWAVE)) {
496 			mode &= ~TIMER_MODE_DONT_CARE_MASK;
497 		}
498 
499 		c->mode = mode;
500 		c->nullcnt = true;
501 		c->crbyte = 0;	/* control word must be written first */
502 		c->olbyte = 0;	/* reset latch after reprogramming */
503 		/* XXX reset frbyte? */
504 
505 		if (channel == 0) {
506 			pit_timer_stop_cntr0(vpit, NULL);
507 		}
508 	}
509 
510 	return (0);
511 }
512 
513 static int
vpit_handler(struct vmctx * ctx,int vcpu,int in,int port,int bytes,uint32_t * eax,void * arg)514 vpit_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
515 		uint32_t *eax, void *arg)
516 {
517 	struct vpit *vpit = ctx->vpit;
518 	struct channel *c;
519 	uint8_t val;
520 	int error = 0;
521 
522 	if (bytes != 1) {
523 		pr_err("vpit invalid operation size: %d bytes", bytes);
524 		return -1;
525 	}
526 
527 	val = *eax;
528 
529 	if (port == TIMER_MODE) {
530 		if (in) {
531 			pr_err("invalid in op @ io port 0x%x", port);
532 			return -1;
533 		}
534 
535 		VPIT_LOCK();
536 		error = vpit_update_mode(vpit, val);
537 		VPIT_UNLOCK();
538 
539 		return error;
540 	}
541 
542 	/* counter ports */
543 	if (port < TIMER_CNTR0 || port > TIMER_CNTR2) {
544 		pr_err("invalid %s op @ io port 0x%x", in ? "in" : "out", port);
545 		return -1;
546 	}
547 
548 	c = &vpit->channel[port - TIMER_CNTR0];
549 
550 	VPIT_LOCK();
551 
552 	if (in) {
553 		if (c->slatched) {
554 			/*
555 			 * Return the status byte if latched
556 			 */
557 			*eax = c->status;
558 			c->slatched = false;
559 		} else {
560 			/*
561 			 * The spec says that once the output latch is completely
562 			 * read it should revert to "following" the counter. Use
563 			 * the free running counter for this case (i.e. Linux
564 			 * TSC calibration). Assuming the access mode is 16-bit,
565 			 * toggle the MSB/LSB bit on each read.
566 			 */
567 			if (c->olbyte == 0) {
568 				uint16_t tmp;
569 				uint64_t delta_ticks;
570 
571 				tmp = pit_update_counter(vpit, c, false, &delta_ticks);
572 
573 				if (c->frbyte)
574 					tmp >>= 8;
575 				tmp &= 0xff;
576 				*eax = tmp;
577 				c->frbyte ^= 1;
578 			} else {
579 				*eax = c->ol[--c->olbyte];
580 			}
581 		}
582 	} else {	/* out */
583 		if (c->crbyte == 2) {
584 			/* keep nullcnt */
585 			c->crbyte = 0;
586 		}
587 
588 		c->cr[c->crbyte++] = *eax;
589 
590 		if (c->crbyte == 2) {
591 			if (PERIODIC_MODE(c->mode) && pit_cr_val(c->cr) == 1) {
592 				/* illegal value */
593 				c->cr[0] = 0;
594 				c->crbyte = 0;
595 				error = -1;
596 				goto done;
597 			}
598 
599 			c->frbyte = 0;
600 			c->nullcnt = true;
601 
602 			/* Start an interval timer for channel 0 */
603 			if (port == TIMER_CNTR0) {
604 				pit_timer_start_cntr0(vpit);
605 			} else {
606 				/*
607 				 * For channel 1 & 2, load the value into CE
608 				 * immediately.
609 				 *
610 				 * XXX
611 				 * On real hardware, in periodic mode, CE doesn't
612 				 * get updated until the end of the current cycle
613 				 * or half-cycle.
614 				 */
615 				pit_load_ce(c);
616 			}
617 		}
618 	}
619 
620 done:
621 	VPIT_UNLOCK();
622 
623 	return error;
624 }
625 
626 static int
vpit_nmisc_handler(struct vmctx * ctx,int vcpu,int in,int port,int bytes,uint32_t * eax,void * arg)627 vpit_nmisc_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
628 		uint32_t *eax, void *arg)
629 {
630 	struct vpit *vpit = ctx->vpit;
631 	struct channel *c;
632 
633 	/* XXX GATE2 control is not emulated */
634 
635 	if (in) {
636 		c = &vpit->channel[2];
637 
638 		VPIT_LOCK();
639 
640 		if (vpit_get_out(vpit, 2, ticks_elapsed_since(&c->start_ts))) {
641 			*eax = TMR2_OUT_STS;
642 		} else {
643 			*eax = 0;
644 		}
645 
646 		VPIT_UNLOCK();
647 	} else
648 		pr_err("out instr on NMI port (0x%x) not supported\n", NMISC_PORT);
649 
650 	return (0);
651 }
652 
653 int
vpit_init(struct vmctx * ctx)654 vpit_init(struct vmctx *ctx)
655 {
656 	struct vpit *vpit;
657 	struct vpit_timer_arg *arg;
658 	int error = 0;
659 	int i;
660 
661 	vpit = calloc(1U, sizeof(*vpit));
662 
663 	if (vpit == NULL) {
664 		error = -ENOMEM;
665 		goto done;
666 	}
667 
668 	vpit->vm = ctx;
669 
670 	VPIT_LOCK();
671 
672 	for (i = 0; i < nitems(vpit_timer_arg); i++) {
673 		arg = &vpit_timer_arg[i];
674 		arg->vpit = vpit;
675 		arg->channel_num = 0; /* only counter 0 uses a timer */
676 		arg->active = false;
677 	}
678 
679 	ctx->vpit = vpit;
680 
681 	VPIT_UNLOCK();
682 
683 done:
684 	return error;
685 }
686 
687 /*
688  * Assume this function is called when only the
689  * dangling timer callback can grab the vPIT lock.
690  */
691 void
vpit_deinit(struct vmctx * ctx)692 vpit_deinit(struct vmctx *ctx)
693 {
694 	struct vpit *vpit;
695 
696 	VPIT_LOCK();
697 
698 	vpit = ctx->vpit;
699 
700 	if (vpit == NULL)
701 		goto done;
702 
703 	ctx->vpit = NULL;
704 	pit_timer_stop_cntr0(vpit, NULL);
705 	memset(vpit_timer_arg, 0, sizeof(vpit_timer_arg));
706 
707 done:
708 	VPIT_UNLOCK();
709 
710 	if (vpit)
711 		free(vpit);
712 }
713 
714 INOUT_PORT(vpit_counter0, TIMER_CNTR0, IOPORT_F_INOUT, vpit_handler);
715 INOUT_PORT(vpit_counter1, TIMER_CNTR1, IOPORT_F_INOUT, vpit_handler);
716 INOUT_PORT(vpit_counter2, TIMER_CNTR2, IOPORT_F_INOUT, vpit_handler);
717 INOUT_PORT(vpit_cwr, TIMER_MODE, IOPORT_F_OUT, vpit_handler);
718 INOUT_PORT(nmi, NMISC_PORT, IOPORT_F_INOUT, vpit_nmisc_handler);
719