xref: /freebsd/sys/amd64/vmm/io/vatpit.c (revision 79d6ca331ec02d76c58383803148333576e6ddd8)
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$");
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 
48 static 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	TMR2_OUT_STS		0x20
60 
61 #define	PIT_8254_FREQ		1193182
62 #define	TIMER_DIV(freq, hz)	(((freq) + (hz) / 2) / (hz))
63 
64 struct vatpit_callout_arg {
65 	struct vatpit	*vatpit;
66 	int		channel_num;
67 };
68 
69 
70 struct channel {
71 	int		mode;
72 	uint16_t	initial;	/* initial counter value */
73 	sbintime_t	now_sbt;	/* uptime when counter was loaded */
74 	uint8_t		cr[2];
75 	uint8_t		ol[2];
76 	int		crbyte;
77 	int		olbyte;
78 	int		frbyte;
79 	struct callout	callout;
80 	sbintime_t	callout_sbt;	/* target time */
81 	struct vatpit_callout_arg callout_arg;
82 };
83 
84 struct vatpit {
85 	struct vm	*vm;
86 	struct mtx	mtx;
87 
88 	sbintime_t	freq_sbt;
89 
90 	struct channel	channel[3];
91 };
92 
93 static void pit_timer_start_cntr0(struct vatpit *vatpit);
94 
95 static int
96 vatpit_get_out(struct vatpit *vatpit, int channel)
97 {
98 	struct channel *c;
99 	sbintime_t delta_ticks;
100 	int out;
101 
102 	c = &vatpit->channel[channel];
103 
104 	switch (c->mode) {
105 	case TIMER_INTTC:
106 		delta_ticks = (sbinuptime() - c->now_sbt) / vatpit->freq_sbt;
107 		out = ((c->initial - delta_ticks) <= 0);
108 		break;
109 	default:
110 		out = 0;
111 		break;
112 	}
113 
114 	return (out);
115 }
116 
117 static void
118 vatpit_callout_handler(void *a)
119 {
120 	struct vatpit_callout_arg *arg = a;
121 	struct vatpit *vatpit;
122 	struct callout *callout;
123 	struct channel *c;
124 
125 	vatpit = arg->vatpit;
126 	c = &vatpit->channel[arg->channel_num];
127 	callout = &c->callout;
128 
129 	VM_CTR1(vatpit->vm, "atpit t%d fired", arg->channel_num);
130 
131 	VATPIT_LOCK(vatpit);
132 
133 	if (callout_pending(callout))		/* callout was reset */
134 		goto done;
135 
136 	if (!callout_active(callout))		/* callout was stopped */
137 		goto done;
138 
139 	callout_deactivate(callout);
140 
141 	if (c->mode == TIMER_RATEGEN) {
142 		pit_timer_start_cntr0(vatpit);
143 	}
144 
145 	vatpic_pulse_irq(vatpit->vm, 0);
146 	vioapic_pulse_irq(vatpit->vm, 2);
147 
148 done:
149 	VATPIT_UNLOCK(vatpit);
150 	return;
151 }
152 
153 static void
154 pit_timer_start_cntr0(struct vatpit *vatpit)
155 {
156 	struct channel *c;
157 	sbintime_t now, delta, precision;
158 
159 	c = &vatpit->channel[0];
160 	if (c->initial != 0) {
161 		delta = c->initial * vatpit->freq_sbt;
162 		precision = delta >> tc_precexp;
163 		c->callout_sbt = c->callout_sbt + delta;
164 
165 		/*
166 		 * Reset 'callout_sbt' if the time that the callout
167 		 * was supposed to fire is more than 'c->initial'
168 		 * ticks in the past.
169 		 */
170 		now = sbinuptime();
171 		if (c->callout_sbt < now)
172 			c->callout_sbt = now + delta;
173 
174 		callout_reset_sbt(&c->callout, c->callout_sbt,
175 		    precision, vatpit_callout_handler, &c->callout_arg,
176 		    C_ABSOLUTE);
177 	}
178 }
179 
180 static uint16_t
181 pit_update_counter(struct vatpit *vatpit, struct channel *c, bool latch)
182 {
183 	uint16_t lval;
184 	sbintime_t delta_ticks;
185 
186 	/* cannot latch a new value until the old one has been consumed */
187 	if (latch && c->olbyte != 0)
188 		return (0);
189 
190 	if (c->initial == 0) {
191 		/*
192 		 * This is possibly an o/s bug - reading the value of
193 		 * the timer without having set up the initial value.
194 		 *
195 		 * The original user-space version of this code set
196 		 * the timer to 100hz in this condition; do the same
197 		 * here.
198 		 */
199 		c->initial = TIMER_DIV(PIT_8254_FREQ, 100);
200 		c->now_sbt = sbinuptime();
201 	}
202 
203 	delta_ticks = (sbinuptime() - c->now_sbt) / vatpit->freq_sbt;
204 
205 	lval = c->initial - delta_ticks % c->initial;
206 
207 	if (latch) {
208 		c->olbyte = 2;
209 		c->ol[1] = lval;		/* LSB */
210 		c->ol[0] = lval >> 8;		/* MSB */
211 	}
212 
213 	return (lval);
214 }
215 
216 static int
217 vatpit_update_mode(struct vatpit *vatpit, uint8_t val)
218 {
219 	struct channel *c;
220 	int sel, rw, mode;
221 
222 	sel = val & TIMER_SEL_MASK;
223 	rw = val & TIMER_RW_MASK;
224 	mode = val & TIMER_MODE_MASK;
225 
226 	if (sel == TIMER_SEL_READBACK)
227 		return (-1);
228 
229 	if (rw != TIMER_LATCH && rw != TIMER_16BIT)
230 		return (-1);
231 
232 	if (rw != TIMER_LATCH) {
233 		/*
234 		 * Counter mode is not affected when issuing a
235 		 * latch command.
236 		 */
237 		if (mode != TIMER_INTTC &&
238 		    mode != TIMER_RATEGEN &&
239 		    mode != TIMER_SQWAVE &&
240 		    mode != TIMER_SWSTROBE)
241 			return (-1);
242 	}
243 
244 	c = &vatpit->channel[sel >> 6];
245 	if (rw == TIMER_LATCH)
246 		pit_update_counter(vatpit, c, true);
247 	else {
248 		c->mode = mode;
249 		c->olbyte = 0;	/* reset latch after reprogramming */
250 	}
251 
252 	return (0);
253 }
254 
255 int
256 vatpit_handler(void *vm, int vcpuid, struct vm_exit *vmexit)
257 {
258 	struct vatpit *vatpit;
259 	struct channel *c;
260 	int port;
261 	uint8_t val;
262 	int error;
263 
264 	vatpit = vm_atpit(vm);
265 
266 	if (vmexit->u.inout.bytes != 1)
267 		return (-1);
268 
269 	val = vmexit->u.inout.eax;
270 	port = vmexit->u.inout.port;
271 
272 	if (port == TIMER_MODE) {
273 		if (vmexit->u.inout.in) {
274 			VM_CTR0(vatpit->vm, "vatpit attempt to read mode");
275 			return (-1);
276 		}
277 
278 		VATPIT_LOCK(vatpit);
279 		error = vatpit_update_mode(vatpit, val);
280 		VATPIT_UNLOCK(vatpit);
281 
282 		return (error);
283 	}
284 
285 	/* counter ports */
286 	KASSERT(port >= TIMER_CNTR0 && vmexit->u.inout.port <= TIMER_CNTR2,
287 	    ("invalid port 0x%x", port));
288 	c = &vatpit->channel[port - TIMER_CNTR0];
289 
290 	VATPIT_LOCK(vatpit);
291 	if (vmexit->u.inout.in) {
292 		/*
293 		 * The spec says that once the output latch is completely
294 		 * read it should revert to "following" the counter. Use
295 		 * the free running counter for this case (i.e. Linux
296 		 * TSC calibration). Assuming the access mode is 16-bit,
297 		 * toggle the MSB/LSB bit on each read.
298 		 */
299 		if (c->olbyte == 0) {
300 			uint16_t tmp;
301 
302 			tmp = pit_update_counter(vatpit, c, false);
303 			if (c->frbyte)
304 				tmp >>= 8;
305 			tmp &= 0xff;
306 			vmexit->u.inout.eax = tmp;
307 			c->frbyte ^= 1;
308 		}  else
309 			vmexit->u.inout.eax = c->ol[--c->olbyte];
310 	} else {
311 		c->cr[c->crbyte++] = vmexit->u.inout.eax;
312 		if (c->crbyte == 2) {
313 			c->frbyte = 0;
314 			c->crbyte = 0;
315 			c->initial = c->cr[0] | (uint16_t)c->cr[1] << 8;
316 			c->now_sbt = sbinuptime();
317 			/* Start an interval timer for channel 0 */
318 			if (port == TIMER_CNTR0) {
319 				c->callout_sbt = c->now_sbt;
320 				pit_timer_start_cntr0(vatpit);
321 			}
322 			if (c->initial == 0)
323 				c->initial = 0xffff;
324 		}
325 	}
326 	VATPIT_UNLOCK(vatpit);
327 
328 	return (0);
329 }
330 
331 int
332 vatpit_nmisc_handler(void *vm, int vcpuid, struct vm_exit *vmexit)
333 {
334 	struct vatpit *vatpit;
335 
336 	vatpit = vm_atpit(vm);
337 
338 	if (vmexit->u.inout.in) {
339 			VATPIT_LOCK(vatpit);
340 			if (vatpit_get_out(vatpit, 2))
341 				vmexit->u.inout.eax = TMR2_OUT_STS;
342 			else
343 				vmexit->u.inout.eax = 0;
344 
345 			VATPIT_UNLOCK(vatpit);
346 	}
347 
348 	return (0);
349 }
350 
351 struct vatpit *
352 vatpit_init(struct vm *vm)
353 {
354 	struct vatpit *vatpit;
355 	struct bintime bt;
356 	struct vatpit_callout_arg *arg;
357 	int i;
358 
359 	vatpit = malloc(sizeof(struct vatpit), M_VATPIT, M_WAITOK | M_ZERO);
360 	vatpit->vm = vm;
361 
362 	mtx_init(&vatpit->mtx, "vatpit lock", NULL, MTX_SPIN);
363 
364 	FREQ2BT(PIT_8254_FREQ, &bt);
365 	vatpit->freq_sbt = bttosbt(bt);
366 
367 	for (i = 0; i < 3; i++) {
368 		callout_init(&vatpit->channel[i].callout, true);
369 		arg = &vatpit->channel[i].callout_arg;
370 		arg->vatpit = vatpit;
371 		arg->channel_num = i;
372 	}
373 
374 	return (vatpit);
375 }
376 
377 void
378 vatpit_cleanup(struct vatpit *vatpit)
379 {
380 	int i;
381 
382 	for (i = 0; i < 3; i++)
383 		callout_drain(&vatpit->channel[i].callout);
384 
385 	free(vatpit, M_VATPIT);
386 }
387