Changeset View
Changeset View
Standalone View
Standalone View
head/sys/amd64/vmm/io/vatpit.c
/*- | /*- | ||||
* Copyright (c) 2014 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com> | * Copyright (c) 2014 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com> | ||||
* Copyright (c) 2011 NetApp, Inc. | * Copyright (c) 2011 NetApp, Inc. | ||||
* All rights reserved. | * All rights reserved. | ||||
* Copyright (c) 2018 Joyent, Inc. | |||||
* | * | ||||
* Redistribution and use in source and binary forms, with or without | * Redistribution and use in source and binary forms, with or without | ||||
* modification, are permitted provided that the following conditions | * modification, are permitted provided that the following conditions | ||||
* are met: | * are met: | ||||
* 1. Redistributions of source code must retain the above copyright | * 1. Redistributions of source code must retain the above copyright | ||||
* notice, this list of conditions and the following disclaimer. | * notice, this list of conditions and the following disclaimer. | ||||
* 2. Redistributions in binary form must reproduce the above copyright | * 2. Redistributions in binary form must reproduce the above copyright | ||||
* notice, this list of conditions and the following disclaimer in the | * notice, this list of conditions and the following disclaimer in the | ||||
▲ Show 20 Lines • Show All 60 Lines • ▼ Show 20 Lines | struct vatpit_callout_arg { | ||||
struct vatpit *vatpit; | struct vatpit *vatpit; | ||||
int channel_num; | int channel_num; | ||||
}; | }; | ||||
struct channel { | struct channel { | ||||
int mode; | int mode; | ||||
uint16_t initial; /* initial counter value */ | uint16_t initial; /* initial counter value */ | ||||
sbintime_t now_sbt; /* uptime when counter was loaded */ | struct bintime now_bt; /* uptime when counter was loaded */ | ||||
uint8_t cr[2]; | uint8_t cr[2]; | ||||
uint8_t ol[2]; | uint8_t ol[2]; | ||||
bool slatched; /* status latched */ | bool slatched; /* status latched */ | ||||
uint8_t status; | uint8_t status; | ||||
int crbyte; | int crbyte; | ||||
int olbyte; | int olbyte; | ||||
int frbyte; | int frbyte; | ||||
struct callout callout; | struct callout callout; | ||||
sbintime_t callout_sbt; /* target time */ | struct bintime callout_bt; /* target time */ | ||||
struct vatpit_callout_arg callout_arg; | struct vatpit_callout_arg callout_arg; | ||||
}; | }; | ||||
struct vatpit { | struct vatpit { | ||||
struct vm *vm; | struct vm *vm; | ||||
struct mtx mtx; | struct mtx mtx; | ||||
sbintime_t freq_sbt; | struct bintime freq_bt; | ||||
struct channel channel[3]; | struct channel channel[3]; | ||||
}; | }; | ||||
static void pit_timer_start_cntr0(struct vatpit *vatpit); | static void pit_timer_start_cntr0(struct vatpit *vatpit); | ||||
static uint64_t | |||||
vatpit_delta_ticks(struct vatpit *vatpit, struct channel *c) | |||||
{ | |||||
struct bintime delta; | |||||
uint64_t result; | |||||
binuptime(&delta); | |||||
bintime_sub(&delta, &c->now_bt); | |||||
result = delta.sec * PIT_8254_FREQ; | |||||
result += delta.frac / vatpit->freq_bt.frac; | |||||
return (result); | |||||
} | |||||
static int | static int | ||||
vatpit_get_out(struct vatpit *vatpit, int channel) | vatpit_get_out(struct vatpit *vatpit, int channel) | ||||
{ | { | ||||
struct channel *c; | struct channel *c; | ||||
sbintime_t delta_ticks; | uint64_t delta_ticks; | ||||
int out; | int out; | ||||
c = &vatpit->channel[channel]; | c = &vatpit->channel[channel]; | ||||
switch (c->mode) { | switch (c->mode) { | ||||
case TIMER_INTTC: | case TIMER_INTTC: | ||||
delta_ticks = (sbinuptime() - c->now_sbt) / vatpit->freq_sbt; | delta_ticks = vatpit_delta_ticks(vatpit, c); | ||||
out = ((c->initial - delta_ticks) <= 0); | out = (delta_ticks >= c->initial); | ||||
break; | break; | ||||
default: | default: | ||||
out = 0; | out = 0; | ||||
break; | break; | ||||
} | } | ||||
return (out); | return (out); | ||||
} | } | ||||
Show All 33 Lines | done: | ||||
VATPIT_UNLOCK(vatpit); | VATPIT_UNLOCK(vatpit); | ||||
return; | return; | ||||
} | } | ||||
static void | static void | ||||
pit_timer_start_cntr0(struct vatpit *vatpit) | pit_timer_start_cntr0(struct vatpit *vatpit) | ||||
{ | { | ||||
struct channel *c; | struct channel *c; | ||||
sbintime_t now, delta, precision; | struct bintime now, delta; | ||||
sbintime_t precision; | |||||
c = &vatpit->channel[0]; | c = &vatpit->channel[0]; | ||||
if (c->initial != 0) { | if (c->initial != 0) { | ||||
delta = c->initial * vatpit->freq_sbt; | delta.sec = 0; | ||||
precision = delta >> tc_precexp; | delta.frac = vatpit->freq_bt.frac * c->initial; | ||||
c->callout_sbt = c->callout_sbt + delta; | bintime_add(&c->callout_bt, &delta); | ||||
precision = bttosbt(delta) >> tc_precexp; | |||||
/* | /* | ||||
* Reset 'callout_sbt' if the time that the callout | * Reset 'callout_bt' if the time that the callout | ||||
* was supposed to fire is more than 'c->initial' | * was supposed to fire is more than 'c->initial' | ||||
* ticks in the past. | * ticks in the past. | ||||
*/ | */ | ||||
now = sbinuptime(); | binuptime(&now); | ||||
if (c->callout_sbt < now) | if (bintime_cmp(&c->callout_bt, &now, <)) { | ||||
c->callout_sbt = now + delta; | c->callout_bt = now; | ||||
bintime_add(&c->callout_bt, &delta); | |||||
} | |||||
callout_reset_sbt(&c->callout, c->callout_sbt, | callout_reset_sbt(&c->callout, bttosbt(c->callout_bt), | ||||
precision, vatpit_callout_handler, &c->callout_arg, | precision, vatpit_callout_handler, &c->callout_arg, | ||||
C_ABSOLUTE); | C_ABSOLUTE); | ||||
} | } | ||||
} | } | ||||
static uint16_t | static uint16_t | ||||
pit_update_counter(struct vatpit *vatpit, struct channel *c, bool latch) | pit_update_counter(struct vatpit *vatpit, struct channel *c, bool latch) | ||||
{ | { | ||||
uint16_t lval; | uint16_t lval; | ||||
sbintime_t delta_ticks; | uint64_t delta_ticks; | ||||
/* cannot latch a new value until the old one has been consumed */ | /* cannot latch a new value until the old one has been consumed */ | ||||
if (latch && c->olbyte != 0) | if (latch && c->olbyte != 0) | ||||
return (0); | return (0); | ||||
if (c->initial == 0) { | if (c->initial == 0) { | ||||
/* | /* | ||||
* This is possibly an o/s bug - reading the value of | * This is possibly an o/s bug - reading the value of | ||||
* the timer without having set up the initial value. | * the timer without having set up the initial value. | ||||
* | * | ||||
* The original user-space version of this code set | * The original user-space version of this code set | ||||
* the timer to 100hz in this condition; do the same | * the timer to 100hz in this condition; do the same | ||||
* here. | * here. | ||||
*/ | */ | ||||
c->initial = TIMER_DIV(PIT_8254_FREQ, 100); | c->initial = TIMER_DIV(PIT_8254_FREQ, 100); | ||||
c->now_sbt = sbinuptime(); | binuptime(&c->now_bt); | ||||
c->status &= ~TIMER_STS_NULLCNT; | c->status &= ~TIMER_STS_NULLCNT; | ||||
} | } | ||||
delta_ticks = (sbinuptime() - c->now_sbt) / vatpit->freq_sbt; | delta_ticks = vatpit_delta_ticks(vatpit, c); | ||||
lval = c->initial - delta_ticks % c->initial; | lval = c->initial - delta_ticks % c->initial; | ||||
if (latch) { | if (latch) { | ||||
c->olbyte = 2; | c->olbyte = 2; | ||||
c->ol[1] = lval; /* LSB */ | c->ol[1] = lval; /* LSB */ | ||||
c->ol[0] = lval >> 8; /* MSB */ | c->ol[0] = lval >> 8; /* MSB */ | ||||
} | } | ||||
▲ Show 20 Lines • Show All 154 Lines • ▼ Show 20 Lines | if (c->olbyte == 0) { | ||||
*eax = c->ol[--c->olbyte]; | *eax = c->ol[--c->olbyte]; | ||||
} else { | } else { | ||||
c->cr[c->crbyte++] = *eax; | c->cr[c->crbyte++] = *eax; | ||||
if (c->crbyte == 2) { | if (c->crbyte == 2) { | ||||
c->status &= ~TIMER_STS_NULLCNT; | c->status &= ~TIMER_STS_NULLCNT; | ||||
c->frbyte = 0; | c->frbyte = 0; | ||||
c->crbyte = 0; | c->crbyte = 0; | ||||
c->initial = c->cr[0] | (uint16_t)c->cr[1] << 8; | c->initial = c->cr[0] | (uint16_t)c->cr[1] << 8; | ||||
c->now_sbt = sbinuptime(); | binuptime(&c->now_bt); | ||||
/* Start an interval timer for channel 0 */ | /* Start an interval timer for channel 0 */ | ||||
if (port == TIMER_CNTR0) { | if (port == TIMER_CNTR0) { | ||||
c->callout_sbt = c->now_sbt; | c->callout_bt = c->now_bt; | ||||
pit_timer_start_cntr0(vatpit); | pit_timer_start_cntr0(vatpit); | ||||
} | } | ||||
if (c->initial == 0) | if (c->initial == 0) | ||||
c->initial = 0xffff; | c->initial = 0xffff; | ||||
} | } | ||||
} | } | ||||
VATPIT_UNLOCK(vatpit); | VATPIT_UNLOCK(vatpit); | ||||
Show All 20 Lines | vatpit_nmisc_handler(struct vm *vm, int vcpuid, bool in, int port, int bytes, | ||||
return (0); | return (0); | ||||
} | } | ||||
struct vatpit * | struct vatpit * | ||||
vatpit_init(struct vm *vm) | vatpit_init(struct vm *vm) | ||||
{ | { | ||||
struct vatpit *vatpit; | struct vatpit *vatpit; | ||||
struct bintime bt; | |||||
struct vatpit_callout_arg *arg; | struct vatpit_callout_arg *arg; | ||||
int i; | int i; | ||||
vatpit = malloc(sizeof(struct vatpit), M_VATPIT, M_WAITOK | M_ZERO); | vatpit = malloc(sizeof(struct vatpit), M_VATPIT, M_WAITOK | M_ZERO); | ||||
vatpit->vm = vm; | vatpit->vm = vm; | ||||
mtx_init(&vatpit->mtx, "vatpit lock", NULL, MTX_SPIN); | mtx_init(&vatpit->mtx, "vatpit lock", NULL, MTX_SPIN); | ||||
FREQ2BT(PIT_8254_FREQ, &bt); | FREQ2BT(PIT_8254_FREQ, &vatpit->freq_bt); | ||||
vatpit->freq_sbt = bttosbt(bt); | |||||
for (i = 0; i < 3; i++) { | for (i = 0; i < 3; i++) { | ||||
callout_init(&vatpit->channel[i].callout, 1); | callout_init(&vatpit->channel[i].callout, 1); | ||||
arg = &vatpit->channel[i].callout_arg; | arg = &vatpit->channel[i].callout_arg; | ||||
arg->vatpit = vatpit; | arg->vatpit = vatpit; | ||||
arg->channel_num = i; | arg->channel_num = i; | ||||
} | } | ||||
Show All 13 Lines |