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