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
i8253_pipe_dispatch(int fd,short event,void * arg)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
i8253_init(uint32_t vm_id)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
i8253_do_readback(uint32_t data)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
vcpu_exit_i8253_misc(struct vm_run_params * vrp)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
vcpu_exit_i8253(struct vm_run_params * vrp)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
i8253_reset(uint8_t chn)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
i8253_fire(int fd,short type,void * arg)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
i8253_dump(int fd)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
i8253_restore(int fd,uint32_t vm_id)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
i8253_stop(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
i8253_start(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