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