Missed the new HPET files.
This commit is contained in:
parent
e5370fa2ac
commit
e36b3175d1
594
bochs/iodev/hpet.cc
Normal file
594
bochs/iodev/hpet.cc
Normal file
@ -0,0 +1,594 @@
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
// $Id$
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// High Precision Event Timer emulation ported from Qemu
|
||||
//
|
||||
// Copyright (c) 2007 Alexander Graf
|
||||
// Copyright (c) 2008 IBM Corporation
|
||||
//
|
||||
// Authors: Beth Kon <bkon@us.ibm.com>
|
||||
//
|
||||
// Copyright (C) 2017 The Bochs Project
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Define BX_PLUGGABLE in files that can be compiled into plugins. For
|
||||
// platforms that require a special tag on exported symbols, BX_PLUGGABLE
|
||||
// is used to know when we are exporting symbols and when we are importing.
|
||||
#define BX_PLUGGABLE
|
||||
|
||||
#include "iodev.h"
|
||||
|
||||
#if BX_SUPPORT_PCI
|
||||
|
||||
#include "hpet.h"
|
||||
|
||||
#define LOG_THIS theHPET->
|
||||
|
||||
bx_hpet_c *theHPET = NULL;
|
||||
|
||||
// device plugin entry points
|
||||
|
||||
int CDECL libhpet_LTX_plugin_init(plugin_t *plugin, plugintype_t type)
|
||||
{
|
||||
theHPET = new bx_hpet_c();
|
||||
BX_REGISTER_DEVICE_DEVMODEL(plugin, type, theHPET, BX_PLUGIN_HPET);
|
||||
return(0); // Success
|
||||
}
|
||||
|
||||
void CDECL libhpet_LTX_plugin_fini(void)
|
||||
{
|
||||
delete theHPET;
|
||||
}
|
||||
|
||||
// helper functions
|
||||
|
||||
static Bit32u hpet_time_after(Bit64u a, Bit64u b)
|
||||
{
|
||||
return ((Bit32s)(b - a) < 0);
|
||||
}
|
||||
|
||||
static Bit32u hpet_time_after64(Bit64u a, Bit64u b)
|
||||
{
|
||||
return ((Bit64s)(b - a) < 0);
|
||||
}
|
||||
|
||||
static Bit64u ticks_to_ns(Bit64u value)
|
||||
{
|
||||
return value * HPET_CLK_PERIOD;
|
||||
}
|
||||
|
||||
static Bit64u ns_to_ticks(Bit64u value)
|
||||
{
|
||||
return value / HPET_CLK_PERIOD;
|
||||
}
|
||||
|
||||
static Bit64u hpet_fixup_reg(Bit64u _new, Bit64u old, Bit64u mask)
|
||||
{
|
||||
_new &= mask;
|
||||
_new |= old & ~mask;
|
||||
return _new;
|
||||
}
|
||||
|
||||
static int activating_bit(Bit64u old, Bit64u _new, Bit64u mask)
|
||||
{
|
||||
return (!(old & mask) && (_new & mask));
|
||||
}
|
||||
|
||||
static int deactivating_bit(Bit64u old, Bit64u _new, Bit64u mask)
|
||||
{
|
||||
return ((old & mask) && !(_new & mask));
|
||||
}
|
||||
|
||||
// static memory read/write functions
|
||||
|
||||
static bx_bool hpet_read(bx_phy_address a20addr, unsigned len, void *data, void *param)
|
||||
{
|
||||
Bit32u value1;
|
||||
Bit64u value2;
|
||||
|
||||
if (len == 4) { // must be 32-bit aligned
|
||||
if ((a20addr & 0x3) != 0) {
|
||||
BX_PANIC(("Unaligned HPET read at address 0x" FMT_PHY_ADDRX, a20addr));
|
||||
return 1;
|
||||
}
|
||||
value1 = theHPET->read_aligned(a20addr);
|
||||
*((Bit32u *)data) = value1;
|
||||
return 1;
|
||||
} else if (len == 8) { // must be 64-bit aligned
|
||||
if ((a20addr & 0x7) != 0) {
|
||||
BX_PANIC(("Unaligned HPET read at address 0x" FMT_PHY_ADDRX, a20addr));
|
||||
return 1;
|
||||
}
|
||||
value1 = theHPET->read_aligned(a20addr);
|
||||
value2 = theHPET->read_aligned(a20addr + 4);
|
||||
*((Bit64u *)data) = (value1 | (value2 << 32));
|
||||
return 1;
|
||||
} else {
|
||||
BX_PANIC(("Unsupported HPET read at address 0x" FMT_PHY_ADDRX, a20addr));
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bx_bool hpet_write(bx_phy_address a20addr, unsigned len, void *data, void *param)
|
||||
{
|
||||
if (len == 4) { // must be 32-bit aligned
|
||||
if ((a20addr & 0x3) != 0) {
|
||||
BX_PANIC(("Unaligned HPET write at address 0x" FMT_PHY_ADDRX, a20addr));
|
||||
return 1;
|
||||
}
|
||||
theHPET->write_aligned(a20addr, *((Bit32u*) data));
|
||||
return 1;
|
||||
} else if (len == 8) { // must be 64-bit aligned
|
||||
if ((a20addr & 0x7) != 0) {
|
||||
BX_PANIC(("Unaligned HPET write at address 0x" FMT_PHY_ADDRX, a20addr));
|
||||
return 1;
|
||||
}
|
||||
Bit64u val64 = *((Bit64u*) data);
|
||||
theHPET->write_aligned(a20addr, (Bit32u)val64);
|
||||
theHPET->write_aligned(a20addr + 4, (Bit32u)(val64 >> 32));
|
||||
} else {
|
||||
BX_PANIC(("Unsupported HPET write at address 0x" FMT_PHY_ADDRX, a20addr));
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
// the device object
|
||||
|
||||
bx_hpet_c::bx_hpet_c()
|
||||
{
|
||||
put("HPET");
|
||||
}
|
||||
|
||||
bx_hpet_c::~bx_hpet_c()
|
||||
{
|
||||
SIM->get_bochs_root()->remove("hpet");
|
||||
BX_DEBUG(("Exit"));
|
||||
}
|
||||
|
||||
void bx_hpet_c::init(void)
|
||||
{
|
||||
BX_INFO(("initializing HPET"));
|
||||
s.num_timers = HPET_MIN_TIMERS;
|
||||
s.capability = 0x8086a001ULL | ((s.num_timers - 1) << 8);
|
||||
s.capability |= ((Bit64u)(HPET_CLK_PERIOD * FS_PER_NS) << 32);
|
||||
s.isr = 0x00;
|
||||
DEV_register_memory_handlers(theHPET, hpet_read, hpet_write,
|
||||
HPET_BASE, HPET_BASE + HPET_LEN - 1);
|
||||
for (int i = 0; i < HPET_MAX_TIMERS; i++) {
|
||||
s.timer[i].tn = i;
|
||||
s.timer[i].timer_id =
|
||||
DEV_register_timer(this, timer_handler, 1, 0, 0, "hpet");
|
||||
bx_pc_system.setTimerParam(s.timer[i].timer_id, i);
|
||||
}
|
||||
#if BX_DEBUGGER
|
||||
// register device for the 'info device' command (calls debug_dump())
|
||||
bx_dbg_register_debug_info("hpet", this);
|
||||
#endif
|
||||
}
|
||||
|
||||
void bx_hpet_c::reset(unsigned type)
|
||||
{
|
||||
for (int i = 0; i < s.num_timers; i++) {
|
||||
HPETTimer *timer = &s.timer[i];
|
||||
|
||||
hpet_del_timer(timer);
|
||||
timer->cmp = ~0ULL;
|
||||
timer->config = HPET_TN_PERIODIC_CAP | HPET_TN_SIZE_CAP;
|
||||
timer->period = 0ULL;
|
||||
timer->wrap_flag = 0;
|
||||
}
|
||||
s.hpet_counter = 0ULL;
|
||||
s.hpet_offset = 0ULL;
|
||||
s.config = 0ULL;
|
||||
}
|
||||
|
||||
void bx_hpet_c::register_state(void)
|
||||
{
|
||||
char tnum[10];
|
||||
bx_list_c *tim;
|
||||
|
||||
bx_list_c *list = new bx_list_c(SIM->get_bochs_root(), "hpet", "HPET State");
|
||||
BXRS_HEX_PARAM_FIELD(list, config, s.config);
|
||||
BXRS_HEX_PARAM_FIELD(list, isr, s.isr);
|
||||
BXRS_HEX_PARAM_FIELD(list, hpet_counter, s.hpet_counter);
|
||||
for (int i = 0; i < s.num_timers; i++) {
|
||||
sprintf(tnum, "timer%d", i);
|
||||
tim = new bx_list_c(list, tnum);
|
||||
BXRS_HEX_PARAM_FIELD(tim, config, s.timer[i].config);
|
||||
BXRS_HEX_PARAM_FIELD(tim, cmp, s.timer[i].cmp);
|
||||
BXRS_HEX_PARAM_FIELD(tim, fsb, s.timer[i].fsb);
|
||||
BXRS_DEC_PARAM_FIELD(tim, period, s.timer[i].period);
|
||||
BXRS_DEC_PARAM_FIELD(tim, wrap_flag, s.timer[i].wrap_flag);
|
||||
}
|
||||
}
|
||||
|
||||
Bit64u bx_hpet_c::hpet_get_ticks(void)
|
||||
{
|
||||
return ns_to_ticks(bx_pc_system.time_nsec() + s.hpet_offset);
|
||||
}
|
||||
|
||||
/*
|
||||
* calculate diff between comparator value and current ticks
|
||||
*/
|
||||
Bit64u bx_hpet_c::hpet_calculate_diff(HPETTimer *t, Bit64u current)
|
||||
{
|
||||
if (t->config & HPET_TN_32BIT) {
|
||||
Bit32u diff, cmp;
|
||||
|
||||
cmp = (Bit32u)t->cmp;
|
||||
diff = cmp - (Bit32u)current;
|
||||
diff = (Bit32s)diff > 0 ? diff : (Bit32u)1;
|
||||
return (Bit64u)diff;
|
||||
} else {
|
||||
Bit64u diff2, cmp2;
|
||||
|
||||
cmp2 = t->cmp;
|
||||
diff2 = cmp2 - current;
|
||||
diff2 = (Bit64s)diff2 > 0 ? diff2 : (Bit64u)1;
|
||||
return diff2;
|
||||
}
|
||||
}
|
||||
|
||||
void bx_hpet_c::update_irq(HPETTimer *timer, bx_bool set)
|
||||
{
|
||||
Bit64u mask;
|
||||
int route;
|
||||
|
||||
if ((timer->tn <= 1) && hpet_in_legacy_mode()) {
|
||||
/* if LegacyReplacementRoute bit is set, HPET specification requires
|
||||
* timer0 be routed to IRQ0 in NON-APIC or IRQ2 in the I/O APIC,
|
||||
* timer1 be routed to IRQ8 in NON-APIC or IRQ8 in the I/O APIC.
|
||||
*/
|
||||
route = (timer->tn == 0) ? 0 : RTC_ISA_IRQ;
|
||||
} else {
|
||||
route = timer_int_route(timer);
|
||||
}
|
||||
mask = 1 << timer->tn;
|
||||
if (!set || !timer_enabled(timer) || !hpet_enabled()) {
|
||||
s.isr &= ~mask;
|
||||
if (!timer_fsb_route(timer)) {
|
||||
DEV_pic_lower_irq(route);
|
||||
}
|
||||
} else if (timer_fsb_route(timer)) {
|
||||
Bit32u val32 = (Bit32u)timer->fsb;
|
||||
DEV_MEM_WRITE_PHYSICAL((bx_phy_address) (timer->fsb >> 32), sizeof(Bit32u), (Bit8u *) &val32);
|
||||
} else if (timer->config & HPET_TN_TYPE_LEVEL) {
|
||||
s.isr |= mask;
|
||||
DEV_pic_raise_irq(route);
|
||||
} else {
|
||||
s.isr &= ~mask;
|
||||
DEV_pic_raise_irq(route);
|
||||
DEV_pic_lower_irq(route);
|
||||
}
|
||||
}
|
||||
|
||||
void bx_hpet_c::timer_handler(void *this_ptr)
|
||||
{
|
||||
bx_hpet_c *class_ptr = (bx_hpet_c *) this_ptr;
|
||||
class_ptr->hpet_timer();
|
||||
}
|
||||
|
||||
void bx_hpet_c::hpet_timer()
|
||||
{
|
||||
HPETTimer *t = &s.timer[bx_pc_system.triggeredTimerParam()];
|
||||
Bit64u diff;
|
||||
Bit64u period = t->period;
|
||||
Bit64u cur_tick = hpet_get_ticks();
|
||||
|
||||
if (timer_is_periodic(t) && (period != 0)) {
|
||||
if (t->config & HPET_TN_32BIT) {
|
||||
while (hpet_time_after(cur_tick, t->cmp)) {
|
||||
t->cmp = (Bit32u)(t->cmp + t->period);
|
||||
}
|
||||
} else {
|
||||
while (hpet_time_after64(cur_tick, t->cmp)) {
|
||||
t->cmp += period;
|
||||
}
|
||||
}
|
||||
diff = hpet_calculate_diff(t, cur_tick);
|
||||
bx_pc_system.activate_timer_nsec(t->timer_id, ticks_to_ns(diff),
|
||||
timer_is_periodic(t));
|
||||
} else if ((t->config & HPET_TN_32BIT) && !timer_is_periodic(t)) {
|
||||
if (t->wrap_flag) {
|
||||
diff = hpet_calculate_diff(t, cur_tick);
|
||||
bx_pc_system.activate_timer_nsec(t->timer_id, ticks_to_ns(diff),
|
||||
timer_is_periodic(t));
|
||||
t->wrap_flag = 0;
|
||||
}
|
||||
}
|
||||
update_irq(t, 1);
|
||||
}
|
||||
|
||||
void bx_hpet_c::hpet_set_timer(HPETTimer *t)
|
||||
{
|
||||
Bit64u diff;
|
||||
Bit32u wrap_diff; /* how many ticks until we wrap? */
|
||||
Bit64u cur_tick = hpet_get_ticks();
|
||||
|
||||
/* whenever new timer is being set up, make sure wrap_flag is 0 */
|
||||
t->wrap_flag = 0;
|
||||
diff = hpet_calculate_diff(t, cur_tick);
|
||||
|
||||
/* hpet spec says in one-shot 32-bit mode, generate an interrupt when
|
||||
* counter wraps in addition to an interrupt with comparator match.
|
||||
*/
|
||||
if ((t->config & HPET_TN_32BIT) && !timer_is_periodic(t)) {
|
||||
wrap_diff = 0xffffffff - (Bit32u)cur_tick;
|
||||
if (wrap_diff < (Bit32u)diff) {
|
||||
diff = wrap_diff;
|
||||
t->wrap_flag = 1;
|
||||
}
|
||||
}
|
||||
bx_pc_system.activate_timer_nsec(t->timer_id, ticks_to_ns(diff),
|
||||
timer_is_periodic(t));
|
||||
}
|
||||
|
||||
void bx_hpet_c::hpet_del_timer(HPETTimer *t)
|
||||
{
|
||||
bx_pc_system.deactivate_timer(t->timer_id);
|
||||
update_irq(t, 0);
|
||||
}
|
||||
|
||||
Bit32u bx_hpet_c::read_aligned(bx_phy_address address)
|
||||
{
|
||||
Bit32u value = 0;
|
||||
|
||||
BX_DEBUG(("read aligned addr=0x" FMT_PHY_ADDRX, address));
|
||||
Bit16u index = (Bit16u)(address & 0x3ff);
|
||||
if (index < 0x100) {
|
||||
switch (index) {
|
||||
case HPET_ID:
|
||||
value = (Bit32u)s.capability;
|
||||
break;
|
||||
case HPET_PERIOD:
|
||||
value = (Bit32u)(s.capability >> 32);
|
||||
break;
|
||||
case HPET_CFG:
|
||||
value = (Bit32u)s.config;
|
||||
break;
|
||||
case HPET_CFG + 4:
|
||||
value = (Bit32u)(s.config >> 32);
|
||||
break;
|
||||
case HPET_STATUS:
|
||||
value = (Bit32u)s.isr;
|
||||
break;
|
||||
case HPET_STATUS + 2:
|
||||
value = (Bit32u)(s.isr >> 32);
|
||||
break;
|
||||
case HPET_COUNTER:
|
||||
if (hpet_enabled()) {
|
||||
value = (Bit32u)hpet_get_ticks();
|
||||
} else {
|
||||
value = (Bit32u)s.hpet_counter;
|
||||
}
|
||||
break;
|
||||
case HPET_COUNTER + 4:
|
||||
if (hpet_enabled()) {
|
||||
value = (Bit32u)(hpet_get_ticks() >> 32);
|
||||
} else {
|
||||
value = (Bit32u)(s.hpet_counter >> 32);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
BX_ERROR(("read from reserved offset 0x%04x", index));
|
||||
}
|
||||
} else {
|
||||
Bit8u id = (index - 0x100) / 0x20;
|
||||
if (id >= s.num_timers) {
|
||||
BX_ERROR(("read: timer id out of range"));
|
||||
return 0;
|
||||
}
|
||||
HPETTimer *timer = &s.timer[id];
|
||||
switch (index & 0x1f) {
|
||||
case HPET_TN_CFG:
|
||||
value = (Bit32u)timer->config;
|
||||
break;
|
||||
case HPET_TN_CFG + 4:
|
||||
value = (Bit32u)(timer->config >> 32);
|
||||
break;
|
||||
case HPET_TN_CMP:
|
||||
value = (Bit32u)timer->cmp;
|
||||
break;
|
||||
case HPET_TN_CMP + 4:
|
||||
value = (Bit32u)(timer->cmp >> 32);
|
||||
break;
|
||||
case HPET_TN_ROUTE:
|
||||
value = (Bit32u)timer->fsb;
|
||||
break;
|
||||
case HPET_TN_ROUTE + 4:
|
||||
value = (Bit32u)(timer->fsb >> 32);
|
||||
break;
|
||||
default:
|
||||
BX_ERROR(("read from reserved offset 0x%04x", index));
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
void bx_hpet_c::write_aligned(bx_phy_address address, Bit32u value)
|
||||
{
|
||||
int i;
|
||||
Bit16u index = (Bit16u)(address & 0x3ff);
|
||||
Bit64u val, new_val = value, old_val = read_aligned(address);
|
||||
|
||||
BX_DEBUG(("write aligned addr=0x" FMT_PHY_ADDRX ", data=0x%08x", address, value));
|
||||
if (index < 0x100) {
|
||||
switch (index) {
|
||||
case HPET_ID:
|
||||
return;
|
||||
case HPET_CFG:
|
||||
val = hpet_fixup_reg(new_val, old_val, HPET_CFG_WRITE_MASK);
|
||||
s.config = (s.config & 0xffffffff00000000ULL) | val;
|
||||
if (activating_bit(old_val, new_val, HPET_CFG_ENABLE)) {
|
||||
/* Enable main counter and interrupt generation. */
|
||||
s.hpet_offset = ticks_to_ns(s.hpet_counter) - bx_pc_system.time_nsec();
|
||||
for (i = 0; i < s.num_timers; i++) {
|
||||
if (s.timer[i].cmp != ~0ULL) {
|
||||
hpet_set_timer(&s.timer[i]);
|
||||
}
|
||||
}
|
||||
} else if (deactivating_bit(old_val, new_val, HPET_CFG_ENABLE)) {
|
||||
/* Halt main counter and disable interrupt generation. */
|
||||
s.hpet_counter = hpet_get_ticks();
|
||||
for (i = 0; i < s.num_timers; i++) {
|
||||
hpet_del_timer(&s.timer[i]);
|
||||
}
|
||||
}
|
||||
/* i8254 and RTC output pins are disabled
|
||||
* when HPET is in legacy mode */
|
||||
if (activating_bit(old_val, new_val, HPET_CFG_LEGACY)) {
|
||||
BX_INFO(("Entering legacy mode"));
|
||||
DEV_pit_enable_irq(0);
|
||||
DEV_cmos_enable_irq(0);
|
||||
} else if (deactivating_bit(old_val, new_val, HPET_CFG_LEGACY)) {
|
||||
BX_INFO(("Leaving legacy mode"));
|
||||
DEV_pit_enable_irq(1);
|
||||
DEV_cmos_enable_irq(1);
|
||||
}
|
||||
break;
|
||||
case HPET_STATUS:
|
||||
val = new_val & s.isr;
|
||||
for (i = 0; i < s.num_timers; i++) {
|
||||
if (val & (1 << i)) {
|
||||
update_irq(&s.timer[i], 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case HPET_COUNTER:
|
||||
if (hpet_enabled()) {
|
||||
BX_DEBUG(("Writing counter while HPET enabled!"));
|
||||
}
|
||||
s.hpet_counter = (s.hpet_counter & 0xffffffff00000000ULL) | value;
|
||||
break;
|
||||
case HPET_COUNTER + 4:
|
||||
if (hpet_enabled()) {
|
||||
BX_DEBUG(("Writing counter while HPET enabled!"));
|
||||
}
|
||||
s.hpet_counter = (s.hpet_counter & 0xffffffffULL) | (((Bit64u)value) << 32);
|
||||
break;
|
||||
default:
|
||||
BX_ERROR(("write to reserved offset 0x%04x", index));
|
||||
}
|
||||
} else {
|
||||
Bit8u id = (index - 0x100) / 0x20;
|
||||
if (id >= s.num_timers) {
|
||||
BX_ERROR(("write: timer id out of range"));
|
||||
return;
|
||||
}
|
||||
HPETTimer *timer = &s.timer[id];
|
||||
switch (index & 0x1f) {
|
||||
case HPET_TN_CFG:
|
||||
if (activating_bit(old_val, new_val, HPET_TN_FSB_ENABLE)) {
|
||||
update_irq(timer, 0);
|
||||
}
|
||||
val = hpet_fixup_reg(new_val, old_val, HPET_TN_CFG_WRITE_MASK);
|
||||
timer->config = (timer->config & 0xffffffff00000000ULL) | val;
|
||||
if (new_val & HPET_TN_32BIT) {
|
||||
timer->cmp = (Bit32u)timer->cmp;
|
||||
timer->period = (Bit32u)timer->period;
|
||||
}
|
||||
if (activating_bit(old_val, new_val, HPET_TN_ENABLE) && hpet_enabled()) {
|
||||
hpet_set_timer(timer);
|
||||
} else if (deactivating_bit(old_val, new_val, HPET_TN_ENABLE)) {
|
||||
hpet_del_timer(timer);
|
||||
}
|
||||
break;
|
||||
case HPET_TN_CMP:
|
||||
if (timer->config & HPET_TN_32BIT) {
|
||||
new_val = (Bit32u)new_val;
|
||||
}
|
||||
if (!timer_is_periodic(timer) || (timer->config & HPET_TN_SETVAL)) {
|
||||
timer->cmp = (timer->cmp & 0xffffffff00000000ULL) | new_val;
|
||||
}
|
||||
if (timer_is_periodic(timer)) {
|
||||
/*
|
||||
* FIXME: Clamp period to reasonable min value?
|
||||
* Clamp period to reasonable max value
|
||||
*/
|
||||
new_val &= (timer->config & HPET_TN_32BIT ? ~0u : ~0ull) >> 1;
|
||||
timer->period = (timer->period & 0xffffffff00000000ULL) | new_val;
|
||||
}
|
||||
timer->config &= ~HPET_TN_SETVAL;
|
||||
if (hpet_enabled()) {
|
||||
hpet_set_timer(timer);
|
||||
}
|
||||
break;
|
||||
case HPET_TN_CMP + 4:
|
||||
if (!timer_is_periodic(timer) || (timer->config & HPET_TN_SETVAL)) {
|
||||
timer->cmp = (timer->cmp & 0xffffffffULL) | (new_val << 32);
|
||||
} else {
|
||||
/*
|
||||
* FIXME: Clamp period to reasonable min value?
|
||||
* Clamp period to reasonable max value
|
||||
*/
|
||||
new_val &= (timer->config & HPET_TN_32BIT ? ~0u : ~0ull) >> 1;
|
||||
timer->period = (timer->period & 0xffffffffULL) | (new_val << 32);
|
||||
}
|
||||
timer->config &= ~HPET_TN_SETVAL;
|
||||
if (hpet_enabled()) {
|
||||
hpet_set_timer(timer);
|
||||
}
|
||||
break;
|
||||
case HPET_TN_ROUTE:
|
||||
timer->fsb = (timer->fsb & 0xffffffff00000000ULL) | new_val;
|
||||
break;
|
||||
case HPET_TN_ROUTE + 4:
|
||||
timer->fsb = (new_val << 32) | (timer->fsb & 0xffffffff);
|
||||
break;
|
||||
default:
|
||||
BX_ERROR(("write to reserved offset 0x%04x", index));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if BX_DEBUGGER
|
||||
void bx_hpet_c::debug_dump(int argc, char **argv)
|
||||
{
|
||||
Bit64u value;
|
||||
|
||||
dbg_printf("HPET\n\n");
|
||||
dbg_printf("enable config = %d\n", s.config & 1);
|
||||
dbg_printf("legacy config = %d\n", (s.config >> 1) & 1);
|
||||
dbg_printf("interrupt status = 0x%08x\n", (Bit32u)s.isr);
|
||||
if (hpet_enabled()) {
|
||||
value = hpet_get_ticks();
|
||||
} else {
|
||||
value = s.hpet_counter;
|
||||
}
|
||||
dbg_printf("main counter = 0x" FMT_LL "x\n\n", value);
|
||||
for (int i = 0; i < s.num_timers; i++) {
|
||||
HPETTimer *timer = &s.timer[i];
|
||||
dbg_printf("timer #%d (%d-bit)\n", i, ((timer->config & HPET_TN_32BIT) > 0) ? 32:64);
|
||||
dbg_printf("interrupt enable = %d\n", timer_enabled(timer) > 0);
|
||||
dbg_printf("periodic mode = %d\n", timer_is_periodic(timer) > 0);
|
||||
dbg_printf("level sensitive = %d\n", (timer->config & HPET_TN_TYPE_LEVEL) > 0);
|
||||
if (timer->config & HPET_TN_32BIT) {
|
||||
dbg_printf("comparator value = 0x%08x\n", (Bit32u)timer->cmp);
|
||||
dbg_printf("period = 0x%08x\n", (Bit32u)timer->period);
|
||||
} else {
|
||||
dbg_printf("comparator value = 0x" FMT_LL "x\n", timer->cmp);
|
||||
dbg_printf("period = 0x" FMT_LL "x\n", timer->period);
|
||||
}
|
||||
}
|
||||
if (argc > 0) {
|
||||
dbg_printf("\nAdditional options not supported\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* if BX_SUPPORT_PCI */
|
126
bochs/iodev/hpet.h
Normal file
126
bochs/iodev/hpet.h
Normal file
@ -0,0 +1,126 @@
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
// $Id$
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// High Precision Event Timer emulation ported from Qemu
|
||||
//
|
||||
// Copyright (c) 2007 Alexander Graf
|
||||
// Copyright (c) 2008 IBM Corporation
|
||||
//
|
||||
// Authors: Beth Kon <bkon@us.ibm.com>
|
||||
//
|
||||
// Copyright (C) 2017 The Bochs Project
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef BX_IODEV_HPET_H
|
||||
#define BX_IODEV_HPET_H
|
||||
|
||||
#if BX_SUPPORT_PCI
|
||||
|
||||
#define RTC_ISA_IRQ 8
|
||||
|
||||
#define HPET_BASE 0xfed00000
|
||||
#define HPET_LEN 0x400
|
||||
#define HPET_CLK_PERIOD 10 // 10 ns
|
||||
|
||||
#define FS_PER_NS 1000000 /* 1000000 femtosectons == 1 ns */
|
||||
#define HPET_MIN_TIMERS 3
|
||||
#define HPET_MAX_TIMERS 32
|
||||
|
||||
#define HPET_CFG_ENABLE 0x001
|
||||
#define HPET_CFG_LEGACY 0x002
|
||||
|
||||
#define HPET_ID 0x000
|
||||
#define HPET_PERIOD 0x004
|
||||
#define HPET_CFG 0x010
|
||||
#define HPET_STATUS 0x020
|
||||
#define HPET_COUNTER 0x0f0
|
||||
#define HPET_TN_CFG 0x000
|
||||
#define HPET_TN_CMP 0x008
|
||||
#define HPET_TN_ROUTE 0x010
|
||||
#define HPET_CFG_WRITE_MASK 0x3
|
||||
|
||||
#define HPET_TN_TYPE_LEVEL 0x002
|
||||
#define HPET_TN_ENABLE 0x004
|
||||
#define HPET_TN_PERIODIC 0x008
|
||||
#define HPET_TN_PERIODIC_CAP 0x010
|
||||
#define HPET_TN_SIZE_CAP 0x020
|
||||
#define HPET_TN_SETVAL 0x040
|
||||
#define HPET_TN_32BIT 0x100
|
||||
#define HPET_TN_INT_ROUTE_MASK 0x3e00
|
||||
#define HPET_TN_FSB_ENABLE 0x4000
|
||||
#define HPET_TN_CFG_WRITE_MASK 0x7f4e
|
||||
#define HPET_TN_INT_ROUTE_SHIFT 9
|
||||
|
||||
typedef struct {
|
||||
Bit8u tn;
|
||||
Bit64u config;
|
||||
Bit64u cmp;
|
||||
Bit64u fsb;
|
||||
Bit64u period;
|
||||
Bit8u wrap_flag;
|
||||
int timer_id;
|
||||
} HPETTimer;
|
||||
|
||||
class bx_hpet_c : public bx_devmodel_c {
|
||||
public:
|
||||
bx_hpet_c();
|
||||
virtual ~bx_hpet_c();
|
||||
virtual void init();
|
||||
virtual void reset(unsigned type);
|
||||
virtual void register_state(void);
|
||||
#if BX_DEBUGGER
|
||||
virtual void debug_dump(int argc, char **argv);
|
||||
#endif
|
||||
|
||||
Bit32u read_aligned(bx_phy_address address);
|
||||
void write_aligned(bx_phy_address address, Bit32u data);
|
||||
|
||||
private:
|
||||
Bit32u hpet_in_legacy_mode(void) {return s.config & HPET_CFG_LEGACY;}
|
||||
Bit32u timer_int_route(HPETTimer *timer)
|
||||
{
|
||||
return (timer->config & HPET_TN_INT_ROUTE_MASK) >> HPET_TN_INT_ROUTE_SHIFT;
|
||||
}
|
||||
Bit32u timer_fsb_route(HPETTimer *t) {return t->config & HPET_TN_FSB_ENABLE;}
|
||||
Bit32u hpet_enabled(void) {return s.config & HPET_CFG_ENABLE;}
|
||||
Bit32u timer_is_periodic(HPETTimer *t) {return t->config & HPET_TN_PERIODIC;}
|
||||
Bit32u timer_enabled(HPETTimer *t) {return t->config & HPET_TN_ENABLE;}
|
||||
Bit64u hpet_get_ticks(void);
|
||||
Bit64u hpet_calculate_diff(HPETTimer *t, Bit64u current);
|
||||
void update_irq(HPETTimer *timer, bx_bool set);
|
||||
void hpet_set_timer(HPETTimer *t);
|
||||
void hpet_del_timer(HPETTimer *t);
|
||||
|
||||
static void timer_handler(void *);
|
||||
void hpet_timer(void);
|
||||
|
||||
struct {
|
||||
Bit8u num_timers;
|
||||
Bit64u hpet_offset;
|
||||
Bit64u capability;
|
||||
Bit64u config;
|
||||
Bit64u isr;
|
||||
Bit64u hpet_counter;
|
||||
HPETTimer timer[HPET_MAX_TIMERS];
|
||||
} s;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
Loading…
x
Reference in New Issue
Block a user