vatpit.c revision 276429
1/*-
2 * Copyright (c) 2014 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
3 * Copyright (c) 2011 NetApp, Inc.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28#include <sys/cdefs.h>
29__FBSDID("$FreeBSD: stable/10/sys/amd64/vmm/io/vatpit.c 276429 2014-12-30 22:22:46Z neel $");
30
31#include <sys/param.h>
32#include <sys/types.h>
33#include <sys/queue.h>
34#include <sys/cpuset.h>
35#include <sys/kernel.h>
36#include <sys/lock.h>
37#include <sys/malloc.h>
38#include <sys/mutex.h>
39#include <sys/systm.h>
40
41#include <machine/vmm.h>
42
43#include "vmm_ktr.h"
44#include "vatpic.h"
45#include "vioapic.h"
46#include "vatpit.h"
47
48static MALLOC_DEFINE(M_VATPIT, "atpit", "bhyve virtual atpit (8254)");
49
50#define	VATPIT_LOCK(vatpit)		mtx_lock_spin(&((vatpit)->mtx))
51#define	VATPIT_UNLOCK(vatpit)		mtx_unlock_spin(&((vatpit)->mtx))
52#define	VATPIT_LOCKED(vatpit)		mtx_owned(&((vatpit)->mtx))
53
54#define	TIMER_SEL_MASK		0xc0
55#define	TIMER_RW_MASK		0x30
56#define	TIMER_MODE_MASK		0x0f
57#define	TIMER_SEL_READBACK	0xc0
58
59#define	TIMER_STS_OUT		0x80
60#define	TIMER_STS_NULLCNT	0x40
61
62#define	TIMER_RB_LCTR		0x20
63#define	TIMER_RB_LSTATUS	0x10
64#define	TIMER_RB_CTR_2		0x08
65#define	TIMER_RB_CTR_1		0x04
66#define	TIMER_RB_CTR_0		0x02
67
68#define	TMR2_OUT_STS		0x20
69
70#define	PIT_8254_FREQ		1193182
71#define	TIMER_DIV(freq, hz)	(((freq) + (hz) / 2) / (hz))
72
73struct vatpit_callout_arg {
74	struct vatpit	*vatpit;
75	int		channel_num;
76};
77
78
79struct channel {
80	int		mode;
81	uint16_t	initial;	/* initial counter value */
82	sbintime_t	now_sbt;	/* uptime when counter was loaded */
83	uint8_t		cr[2];
84	uint8_t		ol[2];
85	bool		slatched;	/* status latched */
86	uint8_t		status;
87	int		crbyte;
88	int		olbyte;
89	int		frbyte;
90	struct callout	callout;
91	sbintime_t	callout_sbt;	/* target time */
92	struct vatpit_callout_arg callout_arg;
93};
94
95struct vatpit {
96	struct vm	*vm;
97	struct mtx	mtx;
98
99	sbintime_t	freq_sbt;
100
101	struct channel	channel[3];
102};
103
104static void pit_timer_start_cntr0(struct vatpit *vatpit);
105
106static int
107vatpit_get_out(struct vatpit *vatpit, int channel)
108{
109	struct channel *c;
110	sbintime_t delta_ticks;
111	int out;
112
113	c = &vatpit->channel[channel];
114
115	switch (c->mode) {
116	case TIMER_INTTC:
117		delta_ticks = (sbinuptime() - c->now_sbt) / vatpit->freq_sbt;
118		out = ((c->initial - delta_ticks) <= 0);
119		break;
120	default:
121		out = 0;
122		break;
123	}
124
125	return (out);
126}
127
128static void
129vatpit_callout_handler(void *a)
130{
131	struct vatpit_callout_arg *arg = a;
132	struct vatpit *vatpit;
133	struct callout *callout;
134	struct channel *c;
135
136	vatpit = arg->vatpit;
137	c = &vatpit->channel[arg->channel_num];
138	callout = &c->callout;
139
140	VM_CTR1(vatpit->vm, "atpit t%d fired", arg->channel_num);
141
142	VATPIT_LOCK(vatpit);
143
144	if (callout_pending(callout))		/* callout was reset */
145		goto done;
146
147	if (!callout_active(callout))		/* callout was stopped */
148		goto done;
149
150	callout_deactivate(callout);
151
152	if (c->mode == TIMER_RATEGEN) {
153		pit_timer_start_cntr0(vatpit);
154	}
155
156	vatpic_pulse_irq(vatpit->vm, 0);
157	vioapic_pulse_irq(vatpit->vm, 2);
158
159done:
160	VATPIT_UNLOCK(vatpit);
161	return;
162}
163
164static void
165pit_timer_start_cntr0(struct vatpit *vatpit)
166{
167	struct channel *c;
168	sbintime_t now, delta, precision;
169
170	c = &vatpit->channel[0];
171	if (c->initial != 0) {
172		delta = c->initial * vatpit->freq_sbt;
173		precision = delta >> tc_precexp;
174		c->callout_sbt = c->callout_sbt + delta;
175
176		/*
177		 * Reset 'callout_sbt' if the time that the callout
178		 * was supposed to fire is more than 'c->initial'
179		 * ticks in the past.
180		 */
181		now = sbinuptime();
182		if (c->callout_sbt < now)
183			c->callout_sbt = now + delta;
184
185		callout_reset_sbt(&c->callout, c->callout_sbt,
186		    precision, vatpit_callout_handler, &c->callout_arg,
187		    C_ABSOLUTE);
188	}
189}
190
191static uint16_t
192pit_update_counter(struct vatpit *vatpit, struct channel *c, bool latch)
193{
194	uint16_t lval;
195	sbintime_t delta_ticks;
196
197	/* cannot latch a new value until the old one has been consumed */
198	if (latch && c->olbyte != 0)
199		return (0);
200
201	if (c->initial == 0) {
202		/*
203		 * This is possibly an o/s bug - reading the value of
204		 * the timer without having set up the initial value.
205		 *
206		 * The original user-space version of this code set
207		 * the timer to 100hz in this condition; do the same
208		 * here.
209		 */
210		c->initial = TIMER_DIV(PIT_8254_FREQ, 100);
211		c->now_sbt = sbinuptime();
212		c->status &= ~TIMER_STS_NULLCNT;
213	}
214
215	delta_ticks = (sbinuptime() - c->now_sbt) / vatpit->freq_sbt;
216
217	lval = c->initial - delta_ticks % c->initial;
218
219	if (latch) {
220		c->olbyte = 2;
221		c->ol[1] = lval;		/* LSB */
222		c->ol[0] = lval >> 8;		/* MSB */
223	}
224
225	return (lval);
226}
227
228static int
229pit_readback1(struct vatpit *vatpit, int channel, uint8_t cmd)
230{
231	struct channel *c;
232
233	c = &vatpit->channel[channel];
234
235	/*
236	 * Latch the count/status of the timer if not already latched.
237	 * N.B. that the count/status latch-select bits are active-low.
238	 */
239	if (!(cmd & TIMER_RB_LCTR) && !c->olbyte) {
240		(void) pit_update_counter(vatpit, c, true);
241	}
242
243	if (!(cmd & TIMER_RB_LSTATUS) && !c->slatched) {
244		c->slatched = true;
245		/*
246		 * For mode 0, see if the elapsed time is greater
247		 * than the initial value - this results in the
248		 * output pin being set to 1 in the status byte.
249		 */
250		if (c->mode == TIMER_INTTC && vatpit_get_out(vatpit, channel))
251			c->status |= TIMER_STS_OUT;
252		else
253			c->status &= ~TIMER_STS_OUT;
254	}
255
256	return (0);
257}
258
259static int
260pit_readback(struct vatpit *vatpit, uint8_t cmd)
261{
262	int error;
263
264	/*
265	 * The readback command can apply to all timers.
266	 */
267	error = 0;
268	if (cmd & TIMER_RB_CTR_0)
269		error = pit_readback1(vatpit, 0, cmd);
270	if (!error && cmd & TIMER_RB_CTR_1)
271		error = pit_readback1(vatpit, 1, cmd);
272	if (!error && cmd & TIMER_RB_CTR_2)
273		error = pit_readback1(vatpit, 2, cmd);
274
275	return (error);
276}
277
278
279static int
280vatpit_update_mode(struct vatpit *vatpit, uint8_t val)
281{
282	struct channel *c;
283	int sel, rw, mode;
284
285	sel = val & TIMER_SEL_MASK;
286	rw = val & TIMER_RW_MASK;
287	mode = val & TIMER_MODE_MASK;
288
289	if (sel == TIMER_SEL_READBACK)
290		return (pit_readback(vatpit, val));
291
292	if (rw != TIMER_LATCH && rw != TIMER_16BIT)
293		return (-1);
294
295	if (rw != TIMER_LATCH) {
296		/*
297		 * Counter mode is not affected when issuing a
298		 * latch command.
299		 */
300		if (mode != TIMER_INTTC &&
301		    mode != TIMER_RATEGEN &&
302		    mode != TIMER_SQWAVE &&
303		    mode != TIMER_SWSTROBE)
304			return (-1);
305	}
306
307	c = &vatpit->channel[sel >> 6];
308	if (rw == TIMER_LATCH)
309		pit_update_counter(vatpit, c, true);
310	else {
311		c->mode = mode;
312		c->olbyte = 0;	/* reset latch after reprogramming */
313		c->status |= TIMER_STS_NULLCNT;
314	}
315
316	return (0);
317}
318
319int
320vatpit_handler(struct vm *vm, int vcpuid, bool in, int port, int bytes,
321    uint32_t *eax)
322{
323	struct vatpit *vatpit;
324	struct channel *c;
325	uint8_t val;
326	int error;
327
328	vatpit = vm_atpit(vm);
329
330	if (bytes != 1)
331		return (-1);
332
333	val = *eax;
334
335	if (port == TIMER_MODE) {
336		if (in) {
337			VM_CTR0(vatpit->vm, "vatpit attempt to read mode");
338			return (-1);
339		}
340
341		VATPIT_LOCK(vatpit);
342		error = vatpit_update_mode(vatpit, val);
343		VATPIT_UNLOCK(vatpit);
344
345		return (error);
346	}
347
348	/* counter ports */
349	KASSERT(port >= TIMER_CNTR0 && port <= TIMER_CNTR2,
350	    ("invalid port 0x%x", port));
351	c = &vatpit->channel[port - TIMER_CNTR0];
352
353	VATPIT_LOCK(vatpit);
354	if (in && c->slatched) {
355		/*
356		 * Return the status byte if latched
357		 */
358		*eax = c->status;
359		c->slatched = false;
360		c->status = 0;
361	} else if (in) {
362		/*
363		 * The spec says that once the output latch is completely
364		 * read it should revert to "following" the counter. Use
365		 * the free running counter for this case (i.e. Linux
366		 * TSC calibration). Assuming the access mode is 16-bit,
367		 * toggle the MSB/LSB bit on each read.
368		 */
369		if (c->olbyte == 0) {
370			uint16_t tmp;
371
372			tmp = pit_update_counter(vatpit, c, false);
373			if (c->frbyte)
374				tmp >>= 8;
375			tmp &= 0xff;
376			*eax = tmp;
377			c->frbyte ^= 1;
378		}  else
379			*eax = c->ol[--c->olbyte];
380	} else {
381		c->cr[c->crbyte++] = *eax;
382		if (c->crbyte == 2) {
383			c->status &= ~TIMER_STS_NULLCNT;
384			c->frbyte = 0;
385			c->crbyte = 0;
386			c->initial = c->cr[0] | (uint16_t)c->cr[1] << 8;
387			c->now_sbt = sbinuptime();
388			/* Start an interval timer for channel 0 */
389			if (port == TIMER_CNTR0) {
390				c->callout_sbt = c->now_sbt;
391				pit_timer_start_cntr0(vatpit);
392			}
393			if (c->initial == 0)
394				c->initial = 0xffff;
395		}
396	}
397	VATPIT_UNLOCK(vatpit);
398
399	return (0);
400}
401
402int
403vatpit_nmisc_handler(struct vm *vm, int vcpuid, bool in, int port, int bytes,
404    uint32_t *eax)
405{
406	struct vatpit *vatpit;
407
408	vatpit = vm_atpit(vm);
409
410	if (in) {
411			VATPIT_LOCK(vatpit);
412			if (vatpit_get_out(vatpit, 2))
413				*eax = TMR2_OUT_STS;
414			else
415				*eax = 0;
416
417			VATPIT_UNLOCK(vatpit);
418	}
419
420	return (0);
421}
422
423struct vatpit *
424vatpit_init(struct vm *vm)
425{
426	struct vatpit *vatpit;
427	struct bintime bt;
428	struct vatpit_callout_arg *arg;
429	int i;
430
431	vatpit = malloc(sizeof(struct vatpit), M_VATPIT, M_WAITOK | M_ZERO);
432	vatpit->vm = vm;
433
434	mtx_init(&vatpit->mtx, "vatpit lock", NULL, MTX_SPIN);
435
436	FREQ2BT(PIT_8254_FREQ, &bt);
437	vatpit->freq_sbt = bttosbt(bt);
438
439	for (i = 0; i < 3; i++) {
440		callout_init(&vatpit->channel[i].callout, true);
441		arg = &vatpit->channel[i].callout_arg;
442		arg->vatpit = vatpit;
443		arg->channel_num = i;
444	}
445
446	return (vatpit);
447}
448
449void
450vatpit_cleanup(struct vatpit *vatpit)
451{
452	int i;
453
454	for (i = 0; i < 3; i++)
455		callout_drain(&vatpit->channel[i].callout);
456
457	free(vatpit, M_VATPIT);
458}
459