xref: /openbsd/usr.sbin/vmd/i8253.c (revision 6f40fd34)
1 /* $OpenBSD: i8253.c,v 1.15 2017/05/08 09:08:40 reyk Exp $ */
2 /*
3  * Copyright (c) 2016 Mike Larkin <mlarkin@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/time.h>
19 #include <sys/types.h>
20 
21 #include <dev/ic/i8253reg.h>
22 
23 #include <machine/vmmvar.h>
24 
25 #include <event.h>
26 #include <string.h>
27 #include <stddef.h>
28 #include <unistd.h>
29 
30 #include "i8253.h"
31 #include "proc.h"
32 #include "vmm.h"
33 #include "atomicio.h"
34 
35 extern char *__progname;
36 
37 /*
38  * Channel 0 is used to generate the legacy hardclock interrupt (HZ).
39  * Channels 1 and 2 are not connected to any output (although someone
40  * could hook channel 2 up to an emulated pcppi(4) at some point).
41  */
42 struct i8253_channel i8253_channel[3];
43 
44 /*
45  * i8253_init
46  *
47  * Initialize the emulated i8253 PIT.
48  *
49  * Parameters:
50  *  vm_id: vmm(4)-assigned ID of the VM
51  */
52 void
53 i8253_init(uint32_t vm_id)
54 {
55 	memset(&i8253_channel, 0, sizeof(struct i8253_channel));
56 	gettimeofday(&i8253_channel[0].tv, NULL);
57 	i8253_channel[0].start = 0xFFFF;
58 	i8253_channel[0].mode = TIMER_INTTC;
59 	i8253_channel[0].last_r = 1;
60 	i8253_channel[0].vm_id = vm_id;
61 
62 	i8253_channel[1].start = 0xFFFF;
63 	i8253_channel[1].mode = TIMER_INTTC;
64 	i8253_channel[1].last_r = 1;
65 	i8253_channel[1].vm_id = vm_id;
66 
67 	i8253_channel[2].start = 0xFFFF;
68 	i8253_channel[2].mode = TIMER_INTTC;
69 	i8253_channel[2].last_r = 1;
70 	i8253_channel[2].vm_id = vm_id;
71 
72 	evtimer_set(&i8253_channel[0].timer, i8253_fire, &i8253_channel[0]);
73 	evtimer_set(&i8253_channel[1].timer, i8253_fire, &i8253_channel[1]);
74 	evtimer_set(&i8253_channel[2].timer, i8253_fire, &i8253_channel[2]);
75 }
76 
77 /*
78  * i8253_do_readback
79  *
80  * Handles the readback status command. The readback status command latches
81  * the current counter value plus various status bits.
82  *
83  * Parameters:
84  *  data: The command word written by the guest VM
85  */
86 void
87 i8253_do_readback(uint32_t data)
88 {
89 	struct timeval now, delta;
90 	uint64_t ns, ticks;
91 
92 	/* bits are inverted here - !TIMER_RB_STATUS == enable chan readback */
93 	if (data & ~TIMER_RB_STATUS) {
94 		i8253_channel[0].rbs = (data & TIMER_RB_C0) ? 1 : 0;
95 		i8253_channel[1].rbs = (data & TIMER_RB_C1) ? 1 : 0;
96 		i8253_channel[2].rbs = (data & TIMER_RB_C2) ? 1 : 0;
97 	}
98 
99 	/* !TIMER_RB_COUNT == enable counter readback */
100 	if (data & ~TIMER_RB_COUNT) {
101 		if (data & TIMER_RB_C0) {
102 			gettimeofday(&now, NULL);
103 			delta.tv_sec = now.tv_sec - i8253_channel[0].tv.tv_sec;
104 			delta.tv_usec = now.tv_usec -
105 			    i8253_channel[0].tv.tv_usec;
106 			if (delta.tv_usec < 0) {
107 				delta.tv_sec--;
108 				delta.tv_usec += 1000000;
109 			}
110 			if (delta.tv_usec > 1000000) {
111 				delta.tv_sec++;
112 				delta.tv_usec -= 1000000;
113 			}
114 			ns = delta.tv_usec * 1000 + delta.tv_sec * 1000000000;
115 			ticks = ns / NS_PER_TICK;
116 			if (i8253_channel[0].start)
117 				i8253_channel[0].olatch =
118 				    i8253_channel[0].start -
119 				    ticks % i8253_channel[0].start;
120 			else
121 				i8253_channel[0].olatch = 0;
122 		}
123 
124 		if (data & TIMER_RB_C1) {
125 			gettimeofday(&now, NULL);
126 			delta.tv_sec = now.tv_sec - i8253_channel[1].tv.tv_sec;
127 			delta.tv_usec = now.tv_usec -
128 			    i8253_channel[1].tv.tv_usec;
129 			if (delta.tv_usec < 0) {
130 				delta.tv_sec--;
131 				delta.tv_usec += 1000000;
132 			}
133 			if (delta.tv_usec > 1000000) {
134 				delta.tv_sec++;
135 				delta.tv_usec -= 1000000;
136 			}
137 			ns = delta.tv_usec * 1000 + delta.tv_sec * 1000000000;
138 			ticks = ns / NS_PER_TICK;
139 			if (i8253_channel[1].start)
140 				i8253_channel[1].olatch =
141 				    i8253_channel[1].start -
142 				    ticks % i8253_channel[1].start;
143 			else
144 				i8253_channel[1].olatch = 0;
145 		}
146 
147 		if (data & TIMER_RB_C2) {
148 			gettimeofday(&now, NULL);
149 			delta.tv_sec = now.tv_sec - i8253_channel[2].tv.tv_sec;
150 			delta.tv_usec = now.tv_usec -
151 			    i8253_channel[2].tv.tv_usec;
152 			if (delta.tv_usec < 0) {
153 				delta.tv_sec--;
154 				delta.tv_usec += 1000000;
155 			}
156 			if (delta.tv_usec > 1000000) {
157 				delta.tv_sec++;
158 				delta.tv_usec -= 1000000;
159 			}
160 			ns = delta.tv_usec * 1000 + delta.tv_sec * 1000000000;
161 			ticks = ns / NS_PER_TICK;
162 			if (i8253_channel[2].start)
163 				i8253_channel[2].olatch =
164 				    i8253_channel[2].start -
165 				    ticks % i8253_channel[2].start;
166 			else
167 				i8253_channel[2].olatch = 0;
168 		}
169 	}
170 }
171 
172 /*
173  * vcpu_exit_i8253
174  *
175  * Handles emulated i8253 PIT access (in/out instruction to PIT ports).
176  *
177  * Parameters:
178  *  vrp: vm run parameters containing exit information for the I/O
179  *      instruction being performed
180  *
181  * Return value:
182  *  Interrupt to inject to the guest VM, or 0xFF if no interrupt should
183  *      be injected.
184  */
185 uint8_t
186 vcpu_exit_i8253(struct vm_run_params *vrp)
187 {
188 	uint32_t out_data;
189 	uint8_t sel, rw, data, mode;
190 	uint64_t ns, ticks;
191 	struct timeval now, delta;
192 	union vm_exit *vei = vrp->vrp_exit;
193 
194 	get_input_data(vei, &out_data);
195 
196 	if (vei->vei.vei_port == TIMER_CTRL) {
197 		if (vei->vei.vei_dir == VEI_DIR_OUT) { /* OUT instruction */
198 			sel = out_data &
199 			    (TIMER_SEL0 | TIMER_SEL1 | TIMER_SEL2);
200 			sel = sel >> 6;
201 
202 			if (sel == 3) {
203 				i8253_do_readback(out_data);
204 				return (0xFF);
205 			}
206 
207 			rw = out_data & (TIMER_LATCH | TIMER_16BIT);
208 
209 			/*
210 			 * Since we don't truly emulate each tick of the PIT
211 			 * counter, when the guest asks for the timer to be
212 			 * latched, simulate what the counter would have been
213 			 * had we performed full emulation. We do this by
214 			 * calculating when the counter was reset vs how much
215 			 * time has elapsed, then bias by the counter tick
216 			 * rate.
217 			 */
218 			if (rw == TIMER_LATCH) {
219 				gettimeofday(&now, NULL);
220 				delta.tv_sec = now.tv_sec -
221 				    i8253_channel[sel].tv.tv_sec;
222 				delta.tv_usec = now.tv_usec -
223 				    i8253_channel[sel].tv.tv_usec;
224 				if (delta.tv_usec < 0) {
225 					delta.tv_sec--;
226 					delta.tv_usec += 1000000;
227 				}
228 				if (delta.tv_usec > 1000000) {
229 					delta.tv_sec++;
230 					delta.tv_usec -= 1000000;
231 				}
232 				ns = delta.tv_usec * 1000 +
233 				    delta.tv_sec * 1000000000;
234 				ticks = ns / NS_PER_TICK;
235 				if (i8253_channel[sel].start) {
236 					i8253_channel[sel].olatch =
237 					    i8253_channel[sel].start -
238 					    ticks % i8253_channel[sel].start;
239 				} else
240 					i8253_channel[sel].olatch = 0;
241 				goto ret;
242 			} else if (rw != TIMER_16BIT) {
243 				log_warnx("%s: i8253 PIT: unsupported counter "
244 				    "%d rw mode 0x%x selected", __func__,
245 				    sel, (rw & TIMER_16BIT));
246 			}
247 
248 			goto ret;
249 		} else {
250 			log_warnx("%s: i8253 PIT: read from control port "
251 			    "unsupported", __progname);
252 			set_return_data(vei, 0);
253 		}
254 	} else {
255 		sel = vei->vei.vei_port - (TIMER_CNTR0 + TIMER_BASE);
256 
257 		if (vei->vei.vei_dir == VEI_DIR_OUT) { /* OUT instruction */
258 			if (i8253_channel[sel].last_w == 0) {
259 				i8253_channel[sel].ilatch |= (out_data & 0xff);
260 				i8253_channel[sel].last_w = 1;
261 			} else {
262 				i8253_channel[sel].ilatch |= ((out_data & 0xff) << 8);
263 				i8253_channel[sel].start =
264 				    i8253_channel[sel].ilatch;
265 				i8253_channel[sel].last_w = 0;
266 				mode = (out_data & 0xe) >> 1;
267 
268 				if (i8253_channel[sel].start == 0)
269 					i8253_channel[sel].start = 0xffff;
270 
271 				log_debug("%s: channel %d reset, mode=%d, start=%d", __func__,
272 				    sel, mode, i8253_channel[sel].start);
273 				i8253_channel[sel].mode = mode;
274 				i8253_reset(sel);
275 			}
276 		} else {
277 			if (i8253_channel[sel].rbs) {
278 				i8253_channel[sel].rbs = 0;
279 				data = i8253_channel[sel].mode << 1;
280 				data |= TIMER_16BIT;
281 				set_return_data(vei, data);
282 				goto ret;
283 			}
284 
285 			if (i8253_channel[sel].last_r == 0) {
286 				data = i8253_channel[sel].olatch >> 8;
287 				set_return_data(vei, data);
288 				i8253_channel[sel].last_r = 1;
289 			} else {
290 				data = i8253_channel[sel].olatch & 0xFF;
291 				set_return_data(vei, data);
292 				i8253_channel[sel].last_r = 0;
293 			}
294 		}
295 	}
296 
297 ret:
298 	return (0xFF);
299 }
300 
301 /*
302  * i8253_reset
303  *
304  * Resets the i8253's counter timer
305  *
306  * Parameters:
307  *  chn: counter ID. Only channel ID 0 is presently emulated.
308  */
309 void
310 i8253_reset(uint8_t chn)
311 {
312 	struct timeval tv;
313 
314 	evtimer_del(&i8253_channel[chn].timer);
315 	timerclear(&tv);
316 
317 	tv.tv_usec = (i8253_channel[chn].start * NS_PER_TICK) / 1000;
318 	evtimer_add(&i8253_channel[chn].timer, &tv);
319 }
320 
321 /*
322  * i8253_fire
323  *
324  * Callback invoked when the 8253 PIT timer fires. This will assert
325  * IRQ0 on the legacy PIC attached to VCPU0.
326  *
327  * Parameters:
328  *  fd: unused
329  *  type: unused
330  *  arg: VM ID
331  */
332 void
333 i8253_fire(int fd, short type, void *arg)
334 {
335 	struct timeval tv;
336 	struct i8253_channel *ctr = (struct i8253_channel *)arg;
337 
338 	timerclear(&tv);
339 	tv.tv_usec = (ctr->start * NS_PER_TICK) / 1000;
340 
341 	vcpu_assert_pic_irq(ctr->vm_id, 0, 0);
342 
343 	if (ctr->mode != TIMER_INTTC)
344 		evtimer_add(&ctr->timer, &tv);
345 }
346 
347 int
348 i8253_dump(int fd)
349 {
350 	log_debug("%s: sending PIT", __func__);
351 	if (atomicio(vwrite, fd, &i8253_channel, sizeof(i8253_channel)) !=
352 	    sizeof(i8253_channel)) {
353 		log_warnx("%s: error writing PIT to fd", __func__);
354 		return (-1);
355 	}
356 	return (0);
357 }
358 
359 int
360 i8253_restore(int fd, uint32_t vm_id)
361 {
362 	log_debug("%s: restoring PIT", __func__);
363 	if (atomicio(read, fd, &i8253_channel, sizeof(i8253_channel)) !=
364 	    sizeof(i8253_channel)) {
365 		log_warnx("%s: error reading PIT from fd", __func__);
366 		return (-1);
367 	}
368 	memset(&i8253_channel[0].timer, 0, sizeof(struct event));
369 	memset(&i8253_channel[1].timer, 0, sizeof(struct event));
370 	memset(&i8253_channel[2].timer, 0, sizeof(struct event));
371 	i8253_channel[0].vm_id = vm_id;
372 	i8253_channel[1].vm_id = vm_id;
373 	i8253_channel[2].vm_id = vm_id;
374 
375 	evtimer_set(&i8253_channel[0].timer, i8253_fire, &i8253_channel[0]);
376 	evtimer_set(&i8253_channel[1].timer, i8253_fire, &i8253_channel[1]);
377 	evtimer_set(&i8253_channel[2].timer, i8253_fire, &i8253_channel[2]);
378 	i8253_reset(0);
379 	return (0);
380 }
381 
382 void
383 i8253_stop()
384 {
385 	evtimer_del(&i8253_channel[0].timer);
386 	evtimer_del(&i8253_channel[1].timer);
387 	evtimer_del(&i8253_channel[2].timer);
388 }
389