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