xref: /openbsd-src/usr.sbin/vmd/i8253.c (revision 65bbee46cad7861cd5a570f338df9e976422e3ab)
1 /* $OpenBSD: i8253.c,v 1.42 2024/09/26 01:45:13 jsg 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 #include <dev/vmm/vmm.h>
23 
24 #include <event.h>
25 #include <string.h>
26 #include <time.h>
27 #include <unistd.h>
28 
29 #include "i8253.h"
30 #include "vmd.h"
31 #include "atomicio.h"
32 
33 extern char *__progname;
34 
35 /*
36  * Channel 0 is used to generate the legacy hardclock interrupt (HZ).
37  * Channels 1 and 2 can be used by the guest OS as regular timers,
38  * but channel 2 is not connected to any pcppi(4)-like device. Like
39  * a regular PC, channel 2 status can also be read from port 0x61.
40  */
41 struct i8253_channel i8253_channel[3];
42 
43 static struct vm_dev_pipe dev_pipe;
44 
45 /*
46  * i8253_pipe_dispatch
47  *
48  * Reads a message off the pipe, expecting one that corresponds to a
49  * reset request for a specific channel.
50  */
51 static void
52 i8253_pipe_dispatch(int fd, short event, void *arg)
53 {
54 	enum pipe_msg_type msg;
55 
56 	msg = vm_pipe_recv(&dev_pipe);
57 	switch (msg) {
58 	case I8253_RESET_CHAN_0:
59 		i8253_reset(0);
60 		break;
61 	case I8253_RESET_CHAN_1:
62 		i8253_reset(1);
63 		break;
64 	case I8253_RESET_CHAN_2:
65 		i8253_reset(2);
66 		break;
67 	default:
68 		fatalx("%s: unexpected pipe message %d", __func__, msg);
69 	}
70 }
71 
72 /*
73  * i8253_init
74  *
75  * Initialize the emulated i8253 PIT.
76  *
77  * Parameters:
78  *  vm_id: vmm(4)-assigned ID of the VM
79  */
80 void
81 i8253_init(uint32_t vm_id)
82 {
83 	memset(&i8253_channel, 0, sizeof(struct i8253_channel));
84 	clock_gettime(CLOCK_MONOTONIC, &i8253_channel[0].ts);
85 	i8253_channel[0].start = 0xFFFF;
86 	i8253_channel[0].mode = TIMER_INTTC;
87 	i8253_channel[0].last_r = 1;
88 	i8253_channel[0].vm_id = vm_id;
89 	i8253_channel[0].state = 0;
90 
91 	i8253_channel[1].start = 0xFFFF;
92 	i8253_channel[1].mode = TIMER_INTTC;
93 	i8253_channel[1].last_r = 1;
94 	i8253_channel[1].vm_id = vm_id;
95 	i8253_channel[1].state = 0;
96 
97 	i8253_channel[2].start = 0xFFFF;
98 	i8253_channel[2].mode = TIMER_INTTC;
99 	i8253_channel[2].last_r = 1;
100 	i8253_channel[2].vm_id = vm_id;
101 	i8253_channel[2].state = 0;
102 
103 	evtimer_set(&i8253_channel[0].timer, i8253_fire, &i8253_channel[0]);
104 	evtimer_set(&i8253_channel[1].timer, i8253_fire, &i8253_channel[1]);
105 	evtimer_set(&i8253_channel[2].timer, i8253_fire, &i8253_channel[2]);
106 
107 	vm_pipe_init(&dev_pipe, i8253_pipe_dispatch);
108 	event_add(&dev_pipe.read_ev, NULL);
109 }
110 
111 /*
112  * i8253_do_readback
113  *
114  * Handles the readback status command. The readback status command latches
115  * the current counter value plus various status bits.
116  *
117  * Parameters:
118  *  data: The command word written by the guest VM
119  */
120 void
121 i8253_do_readback(uint32_t data)
122 {
123 	struct timespec now, delta;
124 	uint64_t ns, ticks;
125 	int readback_channel[3] = { TIMER_RB_C0, TIMER_RB_C1, TIMER_RB_C2 };
126 	int i;
127 
128 	/* bits are inverted here - !TIMER_RB_STATUS == enable chan readback */
129 	if (data & ~TIMER_RB_STATUS) {
130 		i8253_channel[0].rbs = (data & TIMER_RB_C0) ? 1 : 0;
131 		i8253_channel[1].rbs = (data & TIMER_RB_C1) ? 1 : 0;
132 		i8253_channel[2].rbs = (data & TIMER_RB_C2) ? 1 : 0;
133 	}
134 
135 	/* !TIMER_RB_COUNT == enable counter readback */
136 	if (data & ~TIMER_RB_COUNT) {
137 		clock_gettime(CLOCK_MONOTONIC, &now);
138 		for (i = 0; i < 3; i++) {
139 			if (data & readback_channel[i]) {
140 				timespecsub(&now, &i8253_channel[i].ts, &delta);
141 				ns = delta.tv_sec * 1000000000 + delta.tv_nsec;
142 				ticks = ns / NS_PER_TICK;
143 				if (i8253_channel[i].start)
144 					i8253_channel[i].olatch =
145 					    i8253_channel[i].start -
146 					    ticks % i8253_channel[i].start;
147 				else
148 					i8253_channel[i].olatch = 0;
149 			}
150 		}
151 	}
152 }
153 
154 /*
155  * vcpu_exit_i8253_misc
156  *
157  * Handles the 0x61 misc i8253 PIT register in/out exits.
158  *
159  * Parameters:
160  *  vrp: vm run parameters containing exit information for the I/O
161  *      instruction being performed
162  *
163  * Return value:
164  *  Always 0xFF (no interrupt should be injected)
165  */
166 uint8_t
167 vcpu_exit_i8253_misc(struct vm_run_params *vrp)
168 {
169 	struct vm_exit *vei = vrp->vrp_exit;
170 	uint16_t cur;
171 	uint64_t ns, ticks;
172 	struct timespec now, delta;
173 
174 	if (vei->vei.vei_dir == VEI_DIR_IN) {
175 		/* Port 0x61[5] = counter channel 2 state */
176 		if (i8253_channel[2].mode == TIMER_INTTC) {
177 			if (i8253_channel[2].state) {
178 				set_return_data(vei, (1 << 5));
179 				log_debug("%s: counter 2 fired, returning "
180 				    "0x20", __func__);
181 			} else {
182 				set_return_data(vei, 0);
183 				log_debug("%s: counter 2 clear, returning 0x0",
184 				    __func__);
185 			}
186 		} else if (i8253_channel[2].mode == TIMER_SQWAVE) {
187 			clock_gettime(CLOCK_MONOTONIC, &now);
188 			timespecsub(&now, &i8253_channel[2].ts, &delta);
189 			ns = delta.tv_sec * 1000000000 + delta.tv_nsec;
190 			ticks = ns / NS_PER_TICK;
191 			if (i8253_channel[2].start) {
192 				cur = i8253_channel[2].start -
193 				    ticks % i8253_channel[2].start;
194 
195 				if (cur > i8253_channel[2].start / 2)
196 					set_return_data(vei, 1);
197 				else
198 					set_return_data(vei, 0);
199 			}
200 		}
201 	} else {
202 		log_debug("%s: discarding data written to PIT misc port",
203 		    __func__);
204 	}
205 
206 	return 0xFF;
207 }
208 
209 /*
210  * vcpu_exit_i8253
211  *
212  * Handles emulated i8253 PIT access (in/out instruction to PIT ports).
213  *
214  * Parameters:
215  *  vrp: vm run parameters containing exit information for the I/O
216  *      instruction being performed
217  *
218  * Return value:
219  *  Interrupt to inject to the guest VM, or 0xFF if no interrupt should
220  *      be injected.
221  */
222 uint8_t
223 vcpu_exit_i8253(struct vm_run_params *vrp)
224 {
225 	uint32_t out_data = 0;
226 	uint8_t sel, rw, data;
227 	uint64_t ns, ticks;
228 	struct timespec now, delta;
229 	struct vm_exit *vei = vrp->vrp_exit;
230 
231 	get_input_data(vei, &out_data);
232 
233 	if (vei->vei.vei_port == TIMER_CTRL) {
234 		if (vei->vei.vei_dir == VEI_DIR_OUT) { /* OUT instruction */
235 			sel = out_data &
236 			    (TIMER_SEL0 | TIMER_SEL1 | TIMER_SEL2);
237 			sel = sel >> 6;
238 
239 			if (sel == 3) {
240 				i8253_do_readback(out_data);
241 				return (0xFF);
242 			}
243 
244 			rw = out_data & (TIMER_LATCH | TIMER_16BIT);
245 
246 			/*
247 			 * Since we don't truly emulate each tick of the PIT
248 			 * counter, when the guest asks for the timer to be
249 			 * latched, simulate what the counter would have been
250 			 * had we performed full emulation. We do this by
251 			 * calculating when the counter was reset vs how much
252 			 * time has elapsed, then bias by the counter tick
253 			 * rate.
254 			 */
255 			if (rw == TIMER_LATCH) {
256 				clock_gettime(CLOCK_MONOTONIC, &now);
257 				timespecsub(&now, &i8253_channel[sel].ts,
258 				    &delta);
259 				ns = delta.tv_sec * 1000000000 + delta.tv_nsec;
260 				ticks = ns / NS_PER_TICK;
261 				if (i8253_channel[sel].start) {
262 					i8253_channel[sel].olatch =
263 					    i8253_channel[sel].start -
264 					    ticks % i8253_channel[sel].start;
265 				} else
266 					i8253_channel[sel].olatch = 0;
267 				goto ret;
268 			} else if (rw != TIMER_16BIT) {
269 				log_warnx("%s: i8253 PIT: unsupported counter "
270 				    "%d rw mode 0x%x selected", __func__,
271 				    sel, (rw & TIMER_16BIT));
272 			}
273 			i8253_channel[sel].mode = (out_data & 0xe) >> 1;
274 
275 			goto ret;
276 		} else {
277 			log_warnx("%s: i8253 PIT: read from control port "
278 			    "unsupported", __progname);
279 			set_return_data(vei, 0);
280 		}
281 	} else {
282 		sel = vei->vei.vei_port - (TIMER_CNTR0 + TIMER_BASE);
283 
284 		if (vei->vei.vei_dir == VEI_DIR_OUT) { /* OUT instruction */
285 			if (i8253_channel[sel].last_w == 0) {
286 				i8253_channel[sel].ilatch |= (out_data & 0xff);
287 				i8253_channel[sel].last_w = 1;
288 			} else {
289 				i8253_channel[sel].ilatch |=
290 				    ((out_data & 0xff) << 8);
291 				i8253_channel[sel].start =
292 				    i8253_channel[sel].ilatch;
293 				i8253_channel[sel].last_w = 0;
294 
295 				if (i8253_channel[sel].start == 0)
296 					i8253_channel[sel].start = 0xffff;
297 
298 				DPRINTF("%s: channel %d reset, mode=%d, "
299 				    "start=%d\n", __func__,
300 				    sel, i8253_channel[sel].mode,
301 				    i8253_channel[sel].start);
302 
303 				vm_pipe_send(&dev_pipe, sel);
304 			}
305 		} else {
306 			if (i8253_channel[sel].rbs) {
307 				i8253_channel[sel].rbs = 0;
308 				data = i8253_channel[sel].mode << 1;
309 				data |= TIMER_16BIT;
310 				set_return_data(vei, data);
311 				goto ret;
312 			}
313 
314 			if (i8253_channel[sel].last_r == 0) {
315 				data = i8253_channel[sel].olatch >> 8;
316 				set_return_data(vei, data);
317 				i8253_channel[sel].last_r = 1;
318 			} else {
319 				data = i8253_channel[sel].olatch & 0xFF;
320 				set_return_data(vei, data);
321 				i8253_channel[sel].last_r = 0;
322 			}
323 		}
324 	}
325 
326 ret:
327 	return (0xFF);
328 }
329 
330 /*
331  * i8253_reset
332  *
333  * Resets the i8253's counter timer
334  *
335  * Parameters:
336  *  chn: counter ID. Only channel ID 0 is presently emulated.
337  */
338 void
339 i8253_reset(uint8_t chn)
340 {
341 	struct timeval tv;
342 
343 	evtimer_del(&i8253_channel[chn].timer);
344 	timerclear(&tv);
345 
346 	i8253_channel[chn].in_use = 1;
347 	i8253_channel[chn].state = 0;
348 	tv.tv_usec = (i8253_channel[chn].start * NS_PER_TICK) / 1000;
349 	clock_gettime(CLOCK_MONOTONIC, &i8253_channel[chn].ts);
350 	evtimer_add(&i8253_channel[chn].timer, &tv);
351 }
352 
353 /*
354  * i8253_fire
355  *
356  * Callback invoked when the 8253 PIT timer fires. This will assert
357  * IRQ0 on the legacy PIC attached to VCPU0.
358  *
359  * Parameters:
360  *  fd: unused
361  *  type: unused
362  *  arg: VM ID
363  */
364 void
365 i8253_fire(int fd, short type, void *arg)
366 {
367 	struct timeval tv;
368 	struct i8253_channel *ctr = (struct i8253_channel *)arg;
369 
370 	vcpu_assert_irq(ctr->vm_id, 0, 0);
371 
372 	if (ctr->mode != TIMER_INTTC) {
373 		timerclear(&tv);
374 		tv.tv_usec = (ctr->start * NS_PER_TICK) / 1000;
375 		evtimer_add(&ctr->timer, &tv);
376 	} else
377 		ctr->state = 1;
378 }
379 
380 int
381 i8253_dump(int fd)
382 {
383 	log_debug("%s: sending PIT", __func__);
384 	if (atomicio(vwrite, fd, &i8253_channel, sizeof(i8253_channel)) !=
385 	    sizeof(i8253_channel)) {
386 		log_warnx("%s: error writing PIT to fd", __func__);
387 		return (-1);
388 	}
389 	return (0);
390 }
391 
392 int
393 i8253_restore(int fd, uint32_t vm_id)
394 {
395 	int i;
396 	log_debug("%s: restoring PIT", __func__);
397 	if (atomicio(read, fd, &i8253_channel, sizeof(i8253_channel)) !=
398 	    sizeof(i8253_channel)) {
399 		log_warnx("%s: error reading PIT from fd", __func__);
400 		return (-1);
401 	}
402 
403 	for (i = 0; i < 3; i++) {
404 		memset(&i8253_channel[i].timer, 0, sizeof(struct event));
405 		i8253_channel[i].vm_id = vm_id;
406 		evtimer_set(&i8253_channel[i].timer, i8253_fire,
407 		    &i8253_channel[i]);
408 		i8253_reset(i);
409 	}
410 
411 	vm_pipe_init(&dev_pipe, i8253_pipe_dispatch);
412 
413 	return (0);
414 }
415 
416 void
417 i8253_stop(void)
418 {
419 	int i;
420 	for (i = 0; i < 3; i++)
421 		evtimer_del(&i8253_channel[i].timer);
422 	event_del(&dev_pipe.read_ev);
423 }
424 
425 void
426 i8253_start(void)
427 {
428 	int i;
429 	for (i = 0; i < 3; i++)
430 		if (i8253_channel[i].in_use)
431 			i8253_reset(i);
432 	event_add(&dev_pipe.read_ev, NULL);
433 }
434