1*65bbee46Sjsg /* $OpenBSD: i8253.c,v 1.42 2024/09/26 01:45:13 jsg Exp $ */ 2ecc93de1Smlarkin /* 3ecc93de1Smlarkin * Copyright (c) 2016 Mike Larkin <mlarkin@openbsd.org> 4ecc93de1Smlarkin * 5ecc93de1Smlarkin * Permission to use, copy, modify, and distribute this software for any 6ecc93de1Smlarkin * purpose with or without fee is hereby granted, provided that the above 7ecc93de1Smlarkin * copyright notice and this permission notice appear in all copies. 8ecc93de1Smlarkin * 9ecc93de1Smlarkin * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10ecc93de1Smlarkin * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11ecc93de1Smlarkin * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12ecc93de1Smlarkin * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13ecc93de1Smlarkin * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14ecc93de1Smlarkin * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15ecc93de1Smlarkin * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16ecc93de1Smlarkin */ 17ecc93de1Smlarkin 18ecc93de1Smlarkin #include <sys/time.h> 19ecc93de1Smlarkin #include <sys/types.h> 20ecc93de1Smlarkin 21ecc93de1Smlarkin #include <dev/ic/i8253reg.h> 22ba66f564Sdv #include <dev/vmm/vmm.h> 23ecc93de1Smlarkin 24ecc93de1Smlarkin #include <event.h> 25ecc93de1Smlarkin #include <string.h> 2694c97f60Scheloha #include <time.h> 27149417b6Sreyk #include <unistd.h> 28ecc93de1Smlarkin 29ecc93de1Smlarkin #include "i8253.h" 3008fd0ce3Spd #include "vmd.h" 31149417b6Sreyk #include "atomicio.h" 32ecc93de1Smlarkin 33ecc93de1Smlarkin extern char *__progname; 34ecc93de1Smlarkin 35ecc93de1Smlarkin /* 36d23749a0Smlarkin * Channel 0 is used to generate the legacy hardclock interrupt (HZ). 3703fd2d06Smlarkin * Channels 1 and 2 can be used by the guest OS as regular timers, 3803fd2d06Smlarkin * but channel 2 is not connected to any pcppi(4)-like device. Like 3903fd2d06Smlarkin * a regular PC, channel 2 status can also be read from port 0x61. 40ecc93de1Smlarkin */ 41d23749a0Smlarkin struct i8253_channel i8253_channel[3]; 42ecc93de1Smlarkin 4308fd0ce3Spd static struct vm_dev_pipe dev_pipe; 4408fd0ce3Spd 4508fd0ce3Spd /* 4608fd0ce3Spd * i8253_pipe_dispatch 4708fd0ce3Spd * 4808fd0ce3Spd * Reads a message off the pipe, expecting one that corresponds to a 4908fd0ce3Spd * reset request for a specific channel. 5008fd0ce3Spd */ 5108fd0ce3Spd static void 5208fd0ce3Spd i8253_pipe_dispatch(int fd, short event, void *arg) 5308fd0ce3Spd { 5408fd0ce3Spd enum pipe_msg_type msg; 5508fd0ce3Spd 5608fd0ce3Spd msg = vm_pipe_recv(&dev_pipe); 5708fd0ce3Spd switch (msg) { 5808fd0ce3Spd case I8253_RESET_CHAN_0: 5908fd0ce3Spd i8253_reset(0); 6008fd0ce3Spd break; 6108fd0ce3Spd case I8253_RESET_CHAN_1: 6208fd0ce3Spd i8253_reset(1); 6308fd0ce3Spd break; 6408fd0ce3Spd case I8253_RESET_CHAN_2: 6508fd0ce3Spd i8253_reset(2); 6608fd0ce3Spd break; 6708fd0ce3Spd default: 6808fd0ce3Spd fatalx("%s: unexpected pipe message %d", __func__, msg); 6908fd0ce3Spd } 7008fd0ce3Spd } 7108fd0ce3Spd 72ecc93de1Smlarkin /* 73ecc93de1Smlarkin * i8253_init 74ecc93de1Smlarkin * 75ecc93de1Smlarkin * Initialize the emulated i8253 PIT. 76ecc93de1Smlarkin * 77ecc93de1Smlarkin * Parameters: 78ecc93de1Smlarkin * vm_id: vmm(4)-assigned ID of the VM 79ecc93de1Smlarkin */ 80ecc93de1Smlarkin void 81ecc93de1Smlarkin i8253_init(uint32_t vm_id) 82ecc93de1Smlarkin { 83d23749a0Smlarkin memset(&i8253_channel, 0, sizeof(struct i8253_channel)); 8494c97f60Scheloha clock_gettime(CLOCK_MONOTONIC, &i8253_channel[0].ts); 85d23749a0Smlarkin i8253_channel[0].start = 0xFFFF; 86d23749a0Smlarkin i8253_channel[0].mode = TIMER_INTTC; 87d23749a0Smlarkin i8253_channel[0].last_r = 1; 88d23749a0Smlarkin i8253_channel[0].vm_id = vm_id; 8903fd2d06Smlarkin i8253_channel[0].state = 0; 906cc75de0Smlarkin 91d23749a0Smlarkin i8253_channel[1].start = 0xFFFF; 92d23749a0Smlarkin i8253_channel[1].mode = TIMER_INTTC; 93d23749a0Smlarkin i8253_channel[1].last_r = 1; 94d23749a0Smlarkin i8253_channel[1].vm_id = vm_id; 9503fd2d06Smlarkin i8253_channel[1].state = 0; 966cc75de0Smlarkin 97d23749a0Smlarkin i8253_channel[2].start = 0xFFFF; 98d23749a0Smlarkin i8253_channel[2].mode = TIMER_INTTC; 99d23749a0Smlarkin i8253_channel[2].last_r = 1; 100d23749a0Smlarkin i8253_channel[2].vm_id = vm_id; 10103fd2d06Smlarkin i8253_channel[2].state = 0; 1026cc75de0Smlarkin 103d23749a0Smlarkin evtimer_set(&i8253_channel[0].timer, i8253_fire, &i8253_channel[0]); 104d23749a0Smlarkin evtimer_set(&i8253_channel[1].timer, i8253_fire, &i8253_channel[1]); 105d23749a0Smlarkin evtimer_set(&i8253_channel[2].timer, i8253_fire, &i8253_channel[2]); 10608fd0ce3Spd 10708fd0ce3Spd vm_pipe_init(&dev_pipe, i8253_pipe_dispatch); 10808fd0ce3Spd event_add(&dev_pipe.read_ev, NULL); 109ecc93de1Smlarkin } 110ecc93de1Smlarkin 111ecc93de1Smlarkin /* 112764091f3Smlarkin * i8253_do_readback 113764091f3Smlarkin * 114764091f3Smlarkin * Handles the readback status command. The readback status command latches 115764091f3Smlarkin * the current counter value plus various status bits. 116764091f3Smlarkin * 117764091f3Smlarkin * Parameters: 118764091f3Smlarkin * data: The command word written by the guest VM 119764091f3Smlarkin */ 120764091f3Smlarkin void 121764091f3Smlarkin i8253_do_readback(uint32_t data) 122764091f3Smlarkin { 12394c97f60Scheloha struct timespec now, delta; 124764091f3Smlarkin uint64_t ns, ticks; 12594c97f60Scheloha int readback_channel[3] = { TIMER_RB_C0, TIMER_RB_C1, TIMER_RB_C2 }; 12694c97f60Scheloha int i; 127764091f3Smlarkin 128764091f3Smlarkin /* bits are inverted here - !TIMER_RB_STATUS == enable chan readback */ 1296cc75de0Smlarkin if (data & ~TIMER_RB_STATUS) { 130d23749a0Smlarkin i8253_channel[0].rbs = (data & TIMER_RB_C0) ? 1 : 0; 131d23749a0Smlarkin i8253_channel[1].rbs = (data & TIMER_RB_C1) ? 1 : 0; 132d23749a0Smlarkin i8253_channel[2].rbs = (data & TIMER_RB_C2) ? 1 : 0; 1336cc75de0Smlarkin } 134764091f3Smlarkin 135764091f3Smlarkin /* !TIMER_RB_COUNT == enable counter readback */ 136764091f3Smlarkin if (data & ~TIMER_RB_COUNT) { 137f53d796aScheloha clock_gettime(CLOCK_MONOTONIC, &now); 13894c97f60Scheloha for (i = 0; i < 3; i++) { 13994c97f60Scheloha if (data & readback_channel[i]) { 14094c97f60Scheloha timespecsub(&now, &i8253_channel[i].ts, &delta); 14194c97f60Scheloha ns = delta.tv_sec * 1000000000 + delta.tv_nsec; 142764091f3Smlarkin ticks = ns / NS_PER_TICK; 14394c97f60Scheloha if (i8253_channel[i].start) 14494c97f60Scheloha i8253_channel[i].olatch = 14594c97f60Scheloha i8253_channel[i].start - 14694c97f60Scheloha ticks % i8253_channel[i].start; 147764091f3Smlarkin else 14894c97f60Scheloha i8253_channel[i].olatch = 0; 149764091f3Smlarkin } 1506cc75de0Smlarkin } 151764091f3Smlarkin } 152764091f3Smlarkin } 153764091f3Smlarkin 154764091f3Smlarkin /* 15503fd2d06Smlarkin * vcpu_exit_i8253_misc 15603fd2d06Smlarkin * 15703fd2d06Smlarkin * Handles the 0x61 misc i8253 PIT register in/out exits. 15803fd2d06Smlarkin * 15903fd2d06Smlarkin * Parameters: 16003fd2d06Smlarkin * vrp: vm run parameters containing exit information for the I/O 16103fd2d06Smlarkin * instruction being performed 16203fd2d06Smlarkin * 16303fd2d06Smlarkin * Return value: 16403fd2d06Smlarkin * Always 0xFF (no interrupt should be injected) 16503fd2d06Smlarkin */ 16603fd2d06Smlarkin uint8_t 16703fd2d06Smlarkin vcpu_exit_i8253_misc(struct vm_run_params *vrp) 16803fd2d06Smlarkin { 16902ee787fSmlarkin struct vm_exit *vei = vrp->vrp_exit; 1705272a52dSmlarkin uint16_t cur; 1715272a52dSmlarkin uint64_t ns, ticks; 1725272a52dSmlarkin struct timespec now, delta; 17303fd2d06Smlarkin 17403fd2d06Smlarkin if (vei->vei.vei_dir == VEI_DIR_IN) { 17503fd2d06Smlarkin /* Port 0x61[5] = counter channel 2 state */ 1765272a52dSmlarkin if (i8253_channel[2].mode == TIMER_INTTC) { 17703fd2d06Smlarkin if (i8253_channel[2].state) { 17803fd2d06Smlarkin set_return_data(vei, (1 << 5)); 1795272a52dSmlarkin log_debug("%s: counter 2 fired, returning " 1805272a52dSmlarkin "0x20", __func__); 18103fd2d06Smlarkin } else { 18203fd2d06Smlarkin set_return_data(vei, 0); 18388373b6aSmlarkin log_debug("%s: counter 2 clear, returning 0x0", 18488373b6aSmlarkin __func__); 18503fd2d06Smlarkin } 1865272a52dSmlarkin } else if (i8253_channel[2].mode == TIMER_SQWAVE) { 1875272a52dSmlarkin clock_gettime(CLOCK_MONOTONIC, &now); 1885272a52dSmlarkin timespecsub(&now, &i8253_channel[2].ts, &delta); 1895272a52dSmlarkin ns = delta.tv_sec * 1000000000 + delta.tv_nsec; 1905272a52dSmlarkin ticks = ns / NS_PER_TICK; 1915272a52dSmlarkin if (i8253_channel[2].start) { 1925272a52dSmlarkin cur = i8253_channel[2].start - 1935272a52dSmlarkin ticks % i8253_channel[2].start; 1945272a52dSmlarkin 1955272a52dSmlarkin if (cur > i8253_channel[2].start / 2) 1965272a52dSmlarkin set_return_data(vei, 1); 1975272a52dSmlarkin else 1985272a52dSmlarkin set_return_data(vei, 0); 1995272a52dSmlarkin } 2005272a52dSmlarkin } 20103fd2d06Smlarkin } else { 2026dc2ac0bSclaudio log_debug("%s: discarding data written to PIT misc port", 20303fd2d06Smlarkin __func__); 20403fd2d06Smlarkin } 20503fd2d06Smlarkin 20603fd2d06Smlarkin return 0xFF; 20703fd2d06Smlarkin } 20803fd2d06Smlarkin 20903fd2d06Smlarkin /* 210ecc93de1Smlarkin * vcpu_exit_i8253 211ecc93de1Smlarkin * 212ecc93de1Smlarkin * Handles emulated i8253 PIT access (in/out instruction to PIT ports). 213ecc93de1Smlarkin * 214ecc93de1Smlarkin * Parameters: 215ecc93de1Smlarkin * vrp: vm run parameters containing exit information for the I/O 216ecc93de1Smlarkin * instruction being performed 217ecc93de1Smlarkin * 218ecc93de1Smlarkin * Return value: 219ecc93de1Smlarkin * Interrupt to inject to the guest VM, or 0xFF if no interrupt should 220ecc93de1Smlarkin * be injected. 221ecc93de1Smlarkin */ 222ecc93de1Smlarkin uint8_t 223ecc93de1Smlarkin vcpu_exit_i8253(struct vm_run_params *vrp) 224ecc93de1Smlarkin { 225cd5b5ebbSmbuhl uint32_t out_data = 0; 2260ebc07deSmlarkin uint8_t sel, rw, data; 227ecc93de1Smlarkin uint64_t ns, ticks; 22894c97f60Scheloha struct timespec now, delta; 22902ee787fSmlarkin struct vm_exit *vei = vrp->vrp_exit; 230ecc93de1Smlarkin 231b966d91aSmlarkin get_input_data(vei, &out_data); 232764091f3Smlarkin 233ecc93de1Smlarkin if (vei->vei.vei_port == TIMER_CTRL) { 2346ee12970Smlarkin if (vei->vei.vei_dir == VEI_DIR_OUT) { /* OUT instruction */ 235ecc93de1Smlarkin sel = out_data & 236ecc93de1Smlarkin (TIMER_SEL0 | TIMER_SEL1 | TIMER_SEL2); 237ecc93de1Smlarkin sel = sel >> 6; 238764091f3Smlarkin 239764091f3Smlarkin if (sel == 3) { 240764091f3Smlarkin i8253_do_readback(out_data); 241764091f3Smlarkin return (0xFF); 242ecc93de1Smlarkin } 243ecc93de1Smlarkin 244764091f3Smlarkin rw = out_data & (TIMER_LATCH | TIMER_16BIT); 245ecc93de1Smlarkin 246ecc93de1Smlarkin /* 247ecc93de1Smlarkin * Since we don't truly emulate each tick of the PIT 248ecc93de1Smlarkin * counter, when the guest asks for the timer to be 249ecc93de1Smlarkin * latched, simulate what the counter would have been 250ecc93de1Smlarkin * had we performed full emulation. We do this by 251ecc93de1Smlarkin * calculating when the counter was reset vs how much 252ecc93de1Smlarkin * time has elapsed, then bias by the counter tick 253ecc93de1Smlarkin * rate. 254ecc93de1Smlarkin */ 255ecc93de1Smlarkin if (rw == TIMER_LATCH) { 25694c97f60Scheloha clock_gettime(CLOCK_MONOTONIC, &now); 25788373b6aSmlarkin timespecsub(&now, &i8253_channel[sel].ts, 25888373b6aSmlarkin &delta); 25994c97f60Scheloha ns = delta.tv_sec * 1000000000 + delta.tv_nsec; 260ecc93de1Smlarkin ticks = ns / NS_PER_TICK; 261d23749a0Smlarkin if (i8253_channel[sel].start) { 262d23749a0Smlarkin i8253_channel[sel].olatch = 263d23749a0Smlarkin i8253_channel[sel].start - 264d23749a0Smlarkin ticks % i8253_channel[sel].start; 265764091f3Smlarkin } else 266d23749a0Smlarkin i8253_channel[sel].olatch = 0; 267ecc93de1Smlarkin goto ret; 268ecc93de1Smlarkin } else if (rw != TIMER_16BIT) { 269ecc93de1Smlarkin log_warnx("%s: i8253 PIT: unsupported counter " 270ecc93de1Smlarkin "%d rw mode 0x%x selected", __func__, 271ecc93de1Smlarkin sel, (rw & TIMER_16BIT)); 272ecc93de1Smlarkin } 2730ebc07deSmlarkin i8253_channel[sel].mode = (out_data & 0xe) >> 1; 274ecc93de1Smlarkin 275ecc93de1Smlarkin goto ret; 276ecc93de1Smlarkin } else { 277ecc93de1Smlarkin log_warnx("%s: i8253 PIT: read from control port " 278ecc93de1Smlarkin "unsupported", __progname); 279764091f3Smlarkin set_return_data(vei, 0); 280ecc93de1Smlarkin } 281ecc93de1Smlarkin } else { 282ecc93de1Smlarkin sel = vei->vei.vei_port - (TIMER_CNTR0 + TIMER_BASE); 283ecc93de1Smlarkin 2846ee12970Smlarkin if (vei->vei.vei_dir == VEI_DIR_OUT) { /* OUT instruction */ 285d23749a0Smlarkin if (i8253_channel[sel].last_w == 0) { 286d23749a0Smlarkin i8253_channel[sel].ilatch |= (out_data & 0xff); 287d23749a0Smlarkin i8253_channel[sel].last_w = 1; 288ecc93de1Smlarkin } else { 289718d01bdSmlarkin i8253_channel[sel].ilatch |= 290718d01bdSmlarkin ((out_data & 0xff) << 8); 291d23749a0Smlarkin i8253_channel[sel].start = 292d23749a0Smlarkin i8253_channel[sel].ilatch; 293d23749a0Smlarkin i8253_channel[sel].last_w = 0; 294ecc93de1Smlarkin 295d23749a0Smlarkin if (i8253_channel[sel].start == 0) 296d23749a0Smlarkin i8253_channel[sel].start = 0xffff; 2976cc75de0Smlarkin 298445bc9d2Sdv DPRINTF("%s: channel %d reset, mode=%d, " 299445bc9d2Sdv "start=%d\n", __func__, 3000ebc07deSmlarkin sel, i8253_channel[sel].mode, 3010ebc07deSmlarkin i8253_channel[sel].start); 3020ebc07deSmlarkin 30308fd0ce3Spd vm_pipe_send(&dev_pipe, sel); 304ecc93de1Smlarkin } 305ecc93de1Smlarkin } else { 306d23749a0Smlarkin if (i8253_channel[sel].rbs) { 307d23749a0Smlarkin i8253_channel[sel].rbs = 0; 308d23749a0Smlarkin data = i8253_channel[sel].mode << 1; 309764091f3Smlarkin data |= TIMER_16BIT; 310764091f3Smlarkin set_return_data(vei, data); 311764091f3Smlarkin goto ret; 312764091f3Smlarkin } 313764091f3Smlarkin 314d23749a0Smlarkin if (i8253_channel[sel].last_r == 0) { 315e405a10eSmlarkin data = i8253_channel[sel].olatch >> 8; 316764091f3Smlarkin set_return_data(vei, data); 317d23749a0Smlarkin i8253_channel[sel].last_r = 1; 318ecc93de1Smlarkin } else { 319e405a10eSmlarkin data = i8253_channel[sel].olatch & 0xFF; 320764091f3Smlarkin set_return_data(vei, data); 321d23749a0Smlarkin i8253_channel[sel].last_r = 0; 322ecc93de1Smlarkin } 323ecc93de1Smlarkin } 324ecc93de1Smlarkin } 325ecc93de1Smlarkin 326ecc93de1Smlarkin ret: 327ecc93de1Smlarkin return (0xFF); 328ecc93de1Smlarkin } 329ecc93de1Smlarkin 330ecc93de1Smlarkin /* 331ecc93de1Smlarkin * i8253_reset 332ecc93de1Smlarkin * 333ecc93de1Smlarkin * Resets the i8253's counter timer 334ecc93de1Smlarkin * 335ecc93de1Smlarkin * Parameters: 336ecc93de1Smlarkin * chn: counter ID. Only channel ID 0 is presently emulated. 337ecc93de1Smlarkin */ 338ecc93de1Smlarkin void 339ecc93de1Smlarkin i8253_reset(uint8_t chn) 340ecc93de1Smlarkin { 341ecc93de1Smlarkin struct timeval tv; 342ecc93de1Smlarkin 343d23749a0Smlarkin evtimer_del(&i8253_channel[chn].timer); 344ecc93de1Smlarkin timerclear(&tv); 345ecc93de1Smlarkin 34652e954a3Spd i8253_channel[chn].in_use = 1; 34703fd2d06Smlarkin i8253_channel[chn].state = 0; 348d23749a0Smlarkin tv.tv_usec = (i8253_channel[chn].start * NS_PER_TICK) / 1000; 3490ebc07deSmlarkin clock_gettime(CLOCK_MONOTONIC, &i8253_channel[chn].ts); 350d23749a0Smlarkin evtimer_add(&i8253_channel[chn].timer, &tv); 351ecc93de1Smlarkin } 352ecc93de1Smlarkin 353ecc93de1Smlarkin /* 354ecc93de1Smlarkin * i8253_fire 355ecc93de1Smlarkin * 356ecc93de1Smlarkin * Callback invoked when the 8253 PIT timer fires. This will assert 357ecc93de1Smlarkin * IRQ0 on the legacy PIC attached to VCPU0. 358ecc93de1Smlarkin * 359ecc93de1Smlarkin * Parameters: 360ecc93de1Smlarkin * fd: unused 361ecc93de1Smlarkin * type: unused 362ecc93de1Smlarkin * arg: VM ID 363ecc93de1Smlarkin */ 364ecc93de1Smlarkin void 365ecc93de1Smlarkin i8253_fire(int fd, short type, void *arg) 366ecc93de1Smlarkin { 367ecc93de1Smlarkin struct timeval tv; 368d23749a0Smlarkin struct i8253_channel *ctr = (struct i8253_channel *)arg; 369ecc93de1Smlarkin 370c4fd4c5bSdv vcpu_assert_irq(ctr->vm_id, 0, 0); 371ecc93de1Smlarkin 3720ebc07deSmlarkin if (ctr->mode != TIMER_INTTC) { 3730ebc07deSmlarkin timerclear(&tv); 3740ebc07deSmlarkin tv.tv_usec = (ctr->start * NS_PER_TICK) / 1000; 3756cc75de0Smlarkin evtimer_add(&ctr->timer, &tv); 3760ebc07deSmlarkin } else 37703fd2d06Smlarkin ctr->state = 1; 378ecc93de1Smlarkin } 379149417b6Sreyk 380149417b6Sreyk int 381149417b6Sreyk i8253_dump(int fd) 382149417b6Sreyk { 383149417b6Sreyk log_debug("%s: sending PIT", __func__); 384149417b6Sreyk if (atomicio(vwrite, fd, &i8253_channel, sizeof(i8253_channel)) != 385149417b6Sreyk sizeof(i8253_channel)) { 386149417b6Sreyk log_warnx("%s: error writing PIT to fd", __func__); 387149417b6Sreyk return (-1); 388149417b6Sreyk } 389149417b6Sreyk return (0); 390149417b6Sreyk } 391149417b6Sreyk 392149417b6Sreyk int 393149417b6Sreyk i8253_restore(int fd, uint32_t vm_id) 394149417b6Sreyk { 39552e954a3Spd int i; 396149417b6Sreyk log_debug("%s: restoring PIT", __func__); 397149417b6Sreyk if (atomicio(read, fd, &i8253_channel, sizeof(i8253_channel)) != 398149417b6Sreyk sizeof(i8253_channel)) { 399149417b6Sreyk log_warnx("%s: error reading PIT from fd", __func__); 400149417b6Sreyk return (-1); 401149417b6Sreyk } 402149417b6Sreyk 40352e954a3Spd for (i = 0; i < 3; i++) { 40452e954a3Spd memset(&i8253_channel[i].timer, 0, sizeof(struct event)); 40552e954a3Spd i8253_channel[i].vm_id = vm_id; 40652e954a3Spd evtimer_set(&i8253_channel[i].timer, i8253_fire, 40752e954a3Spd &i8253_channel[i]); 40852e954a3Spd i8253_reset(i); 40952e954a3Spd } 410bc11ef5cSdv 411bc11ef5cSdv vm_pipe_init(&dev_pipe, i8253_pipe_dispatch); 412bc11ef5cSdv 413149417b6Sreyk return (0); 414149417b6Sreyk } 415149417b6Sreyk 416149417b6Sreyk void 41731106e66Stb i8253_stop(void) 418149417b6Sreyk { 41952e954a3Spd int i; 42052e954a3Spd for (i = 0; i < 3; i++) 42152e954a3Spd evtimer_del(&i8253_channel[i].timer); 422bc11ef5cSdv event_del(&dev_pipe.read_ev); 42352e954a3Spd } 42452e954a3Spd 42552e954a3Spd void 42631106e66Stb i8253_start(void) 42752e954a3Spd { 42852e954a3Spd int i; 42952e954a3Spd for (i = 0; i < 3; i++) 43052e954a3Spd if (i8253_channel[i].in_use) 43152e954a3Spd i8253_reset(i); 432bc11ef5cSdv event_add(&dev_pipe.read_ev, NULL); 433149417b6Sreyk } 434