diff options
Diffstat (limited to 'tools/ioemu/iodev/pc_system.cc')
-rw-r--r-- | tools/ioemu/iodev/pc_system.cc | 570 |
1 files changed, 570 insertions, 0 deletions
diff --git a/tools/ioemu/iodev/pc_system.cc b/tools/ioemu/iodev/pc_system.cc new file mode 100644 index 0000000000..de34d9914b --- /dev/null +++ b/tools/ioemu/iodev/pc_system.cc @@ -0,0 +1,570 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: pc_system.cc,v 1.34 2003/06/07 19:16:51 vruppert Exp $ +///////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2002 MandrakeSoft S.A. +// +// MandrakeSoft S.A. +// 43, rue d'Aboukir +// 75002 Paris - France +// http://www.linux-mandrake.com/ +// http://www.mandrakesoft.com/ +// +// 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + + +#include "bochs.h" +#define LOG_THIS bx_pc_system. + +#ifdef WIN32 +#ifndef __MINGW32__ +// #include <winsock2.h> // +++ +#include <winsock.h> +#endif +#endif + +#if BX_SHOW_IPS +unsigned long ips_count=0; +#endif + +#if defined(PROVIDE_M_IPS) +double m_ips; // Millions of Instructions Per Second +#endif + +#ifdef BX_USE_VMX +unsigned int tsc_per_bx_tick; +#endif + +// Option for turning off BX_TIMER_DEBUG? +// Check out m_ips and ips + +#define SpewPeriodicTimerInfo 0 +#define MinAllowableTimerPeriod 1 + + +#if SpewPeriodicTimerInfo +// If debugging, set the heartbeat to 5M cycles. Each heartbeat +// spews the active timer info. +const Bit64u bx_pc_system_c::NullTimerInterval = 5000000; +#else +// This must be the maximum 32-bit unsigned int value, NOT (Bit64u) -1. +const Bit64u bx_pc_system_c::NullTimerInterval = 0xffffffff; +#endif + + // constructor +bx_pc_system_c::bx_pc_system_c(void) +{ + this->put("SYS"); + + // Timer[0] is the null timer. It is initialized as a special + // case here. It should never be turned off or modified, and its + // duration should always remain the same. + ticksTotal = 0; // Reset ticks since emulator started. + timer[0].period = NullTimerInterval; + timer[0].timeToFire = ticksTotal + NullTimerInterval; + timer[0].active = 1; + timer[0].continuous = 1; + timer[0].funct = nullTimer; + timer[0].this_ptr = this; + currCountdown = NullTimerInterval; + currCountdownPeriod = NullTimerInterval; + numTimers = 1; // So far, only the nullTimer. + lastTimeUsec = 0; + usecSinceLast = 0; +} + + void +bx_pc_system_c::init_ips(Bit32u ips) +{ + HRQ = 0; + + enable_a20 = 1; + //set_INTR (0); + +#if BX_CPU_LEVEL < 2 + a20_mask = 0xfffff; +#elif BX_CPU_LEVEL == 2 + a20_mask = 0xffffff; +#else /* 386+ */ + a20_mask = 0xffffffff; +#endif + +#ifdef BX_USE_VMX + Bit64u phy_cpu_freq = cpu_calibrate_ticks(); + + if (ips == 500000) { //default ips: we use fixed scaling factor to calulate ips + tsc_per_bx_tick = 2000; + ips = phy_cpu_freq / tsc_per_bx_tick; + } else //use uesr defined ips to calulate factor + tsc_per_bx_tick = ((phy_cpu_freq + (ips>>1)) / ips); +#endif + + // parameter 'ips' is the processor speed in Instructions-Per-Second + m_ips = double(ips) / 1000000.0L; + + BX_DEBUG(("ips = %u", (unsigned) ips)); +} + + void +bx_pc_system_c::set_HRQ(bx_bool val) +{ + HRQ = val; + if (val) + BX_CPU(0)->async_event = 1; +} + + +#if (BX_NUM_SIMULATORS < 2) + void +bx_pc_system_c::set_INTR(bx_bool value) +{ + if (bx_dbg.interrupts) + BX_INFO(("pc_system: Setting INTR=%d on bootstrap processor %d", (int)value, BX_BOOTSTRAP_PROCESSOR)); + //INTR = value; + BX_CPU(BX_BOOTSTRAP_PROCESSOR)->set_INTR(value); +} +#endif + +// +// Read from the IO memory address space +// + + Bit32u BX_CPP_AttrRegparmN(2) +bx_pc_system_c::inp(Bit16u addr, unsigned io_len) +{ + Bit32u ret; + + ret = bx_devices.inp(addr, io_len); + + return( ret ); +} + + +// +// Write to the IO memory address space. +// + + void BX_CPP_AttrRegparmN(3) +bx_pc_system_c::outp(Bit16u addr, Bit32u value, unsigned io_len) +{ + bx_devices.outp(addr, value, io_len); +} + + void BX_CPP_AttrRegparmN(1) +bx_pc_system_c::set_enable_a20(Bit8u value) +{ +#if BX_CPU_LEVEL < 2 + BX_PANIC(("set_enable_a20() called: 8086 emulation")); +#else + +#if BX_SUPPORT_A20 + unsigned old_enable_a20 = enable_a20; + + if (value) { + enable_a20 = 1; +#if BX_CPU_LEVEL == 2 + a20_mask = 0xffffff; /* 286: enable all 24 address lines */ +#else /* 386+ */ + a20_mask = 0xffffffff; /* 386: enable all 32 address lines */ +#endif + } + else { + enable_a20 = 0; + a20_mask = 0xffefffff; /* mask off A20 address line */ + } + + BX_DBG_A20_REPORT(value); + + BX_DEBUG(("A20: set() = %u", (unsigned) enable_a20)); + + // If there has been a transition, we need to notify the CPUs so + // they can potentially invalidate certain cache info based on + // A20-line-applied physical addresses. + if (old_enable_a20 != enable_a20) { + for (unsigned i=0; i<BX_SMP_PROCESSORS; i++) + BX_CPU(i)->pagingA20Changed(); + } +#else + BX_DEBUG(("set_enable_a20: ignoring: SUPPORT_A20 = 0")); +#endif // #if BX_SUPPORT_A20 + +#endif +} + + bx_bool +bx_pc_system_c::get_enable_a20(void) +{ +#if BX_SUPPORT_A20 + if (bx_dbg.a20) + BX_INFO(("A20: get() = %u", (unsigned) enable_a20)); + + if (enable_a20) return(1); + else return(0); +#else + BX_INFO(("get_enable_a20: ignoring: SUPPORT_A20 = 0")); + return(1); +#endif // #if BX_SUPPORT_A20 +} + + int +bx_pc_system_c::ResetSignal( PCS_OP operation ) +{ + UNUSED( operation ); + // Reset the processor. + + BX_ERROR(( "# bx_pc_system_c::ResetSignal() called" )); + for (int i=0; i<BX_SMP_PROCESSORS; i++) + BX_CPU(i)->reset(BX_RESET_SOFTWARE); + DEV_reset_devices(BX_RESET_SOFTWARE); + return(0); +} + + + Bit8u +bx_pc_system_c::IAC(void) +{ + return( DEV_pic_iac() ); +} + + void +bx_pc_system_c::exit(void) +{ + if (DEV_hd_present()) + DEV_hd_close_harddrive(); + + BX_INFO(("Last time is %u", (unsigned) DEV_cmos_get_timeval())); + + if (bx_gui) bx_gui->exit(); +} + + +// ================================================ +// Bochs internal timer delivery framework features +// ================================================ + + int +bx_pc_system_c::register_timer( void *this_ptr, void (*funct)(void *), + Bit32u useconds, bx_bool continuous, bx_bool active, const char *id) +{ + Bit64u ticks; + + // Convert useconds to number of ticks. + ticks = (Bit64u) (double(useconds) * m_ips); + + return register_timer_ticks(this_ptr, funct, ticks, continuous, active, id); +} + + int +bx_pc_system_c::register_timer_ticks(void* this_ptr, bx_timer_handler_t funct, + Bit64u ticks, bx_bool continuous, bx_bool active, const char *id) +{ + unsigned i; + +#if BX_TIMER_DEBUG + if (numTimers >= BX_MAX_TIMERS) { + BX_PANIC(("register_timer: too many registered timers.")); + } + if (this_ptr == NULL) + BX_PANIC(("register_timer_ticks: this_ptr is NULL")); + if (funct == NULL) + BX_PANIC(("register_timer_ticks: funct is NULL")); +#endif + + // If the timer frequency is rediculously low, make it more sane. + // This happens when 'ips' is too low. + if (ticks < MinAllowableTimerPeriod) { + //BX_INFO(("register_timer_ticks: adjusting ticks of %llu to min of %u", + // ticks, MinAllowableTimerPeriod)); + ticks = MinAllowableTimerPeriod; + } + + for (i=0; i < numTimers; i++) { + if (timer[i].inUse == 0) + break; + } + + timer[i].inUse = 1; + timer[i].period = ticks; + timer[i].timeToFire = (ticksTotal + Bit64u(currCountdownPeriod-currCountdown)) + + ticks; + timer[i].active = active; + timer[i].continuous = continuous; + timer[i].funct = funct; + timer[i].this_ptr = this_ptr; + strncpy(timer[i].id, id, BxMaxTimerIDLen); + timer[i].id[BxMaxTimerIDLen-1] = 0; // Null terminate if not already. + + if (active) { + if (ticks < Bit64u(currCountdown)) { + // This new timer needs to fire before the current countdown. + // Skew the current countdown and countdown period to be smaller + // by the delta. + currCountdownPeriod -= (currCountdown - Bit32u(ticks)); + currCountdown = Bit32u(ticks); + } + } + + // If we didn't find a free slot, increment the bound, numTimers. + if (i==numTimers) + numTimers++; // One new timer installed. + + // Return timer id. + return(i); +} + + + void +bx_pc_system_c::countdownEvent(void) +{ + unsigned i; + Bit64u minTimeToFire; + bx_bool triggered[BX_MAX_TIMERS]; + + // The countdown decremented to 0. We need to service all the active + // timers, and invoke callbacks from those timers which have fired. +#if BX_TIMER_DEBUG + if (currCountdown != 0) + BX_PANIC(("countdownEvent: ticks!=0")); +#endif + + // Increment global ticks counter by number of ticks which have + // elapsed since the last update. + ticksTotal += Bit64u(currCountdownPeriod); + minTimeToFire = (Bit64u) -1; + + for (i=0; i < numTimers; i++) { + triggered[i] = 0; // Reset triggered flag. + if (timer[i].active) { +#if BX_TIMER_DEBUG + if (ticksTotal > timer[i].timeToFire) + BX_PANIC(("countdownEvent: ticksTotal > timeToFire[%u], D " FMT_LL "u", i, + timer[i].timeToFire-ticksTotal)); +#endif + if (ticksTotal == timer[i].timeToFire) { + // This timer is ready to fire. + triggered[i] = 1; + + if (timer[i].continuous==0) { + // If triggered timer is one-shot, deactive. + timer[i].active = 0; + } + else { + // Continuous timer, increment time-to-fire by period. + timer[i].timeToFire += timer[i].period; + if (timer[i].timeToFire < minTimeToFire) + minTimeToFire = timer[i].timeToFire; + } + } + else { + // This timer is not ready to fire yet. + if (timer[i].timeToFire < minTimeToFire) + minTimeToFire = timer[i].timeToFire; + } + } + } + + // Calculate next countdown period. We need to do this before calling + // any of the callbacks, as they may call timer features, which need + // to be advanced to the next countdown cycle. + currCountdown = currCountdownPeriod = + Bit32u(minTimeToFire - ticksTotal); + + for (i=0; i < numTimers; i++) { + // Call requested timer function. It may request a different + // timer period or deactivate etc. + if (triggered[i]) { + timer[i].funct(timer[i].this_ptr); + } + } +} + + void +bx_pc_system_c::nullTimer(void* this_ptr) +{ + // This function is always inserted in timer[0]. It is sort of + // a heartbeat timer. It ensures that at least one timer is + // always active to make the timer logic more simple, and has + // a duration of less than the maximum 32-bit integer, so that + // a 32-bit size can be used for the hot countdown timer. The + // rest of the timer info can be 64-bits. This is also a good + // place for some logic to report actual emulated + // instructions-per-second (IPS) data when measured relative to + // the host computer's wall clock. + + UNUSED(this_ptr); + +#if SpewPeriodicTimerInfo + BX_INFO(("===================================")); + for (unsigned i=0; i < bx_pc_system.numTimers; i++) { + if (bx_pc_system.timer[i].active) { + BX_INFO(("BxTimer(%s): period=" FMT_LL "u, continuous=%u", + bx_pc_system.timer[i].id, bx_pc_system.timer[i].period, + bx_pc_system.timer[i].continuous)); + } + } +#endif +} + +#if BX_DEBUGGER + void +bx_pc_system_c::timebp_handler(void* this_ptr) +{ + BX_CPU(0)->break_point = BREAK_POINT_TIME; + BX_DEBUG(( "Time breakpoint triggered" )); + + if (timebp_queue_size > 1) { + Bit64s new_diff = timebp_queue[1] - bx_pc_system.time_ticks(); + bx_pc_system.activate_timer_ticks(timebp_timer, new_diff, 1); + } + timebp_queue_size--; + for (int i = 0; i < timebp_queue_size; i++) + timebp_queue[i] = timebp_queue[i+1]; +} +#endif // BX_DEBUGGER + + Bit64u +bx_pc_system_c::time_usec_sequential() { + Bit64u this_time_usec = time_usec(); + if(this_time_usec != lastTimeUsec) { + Bit64u diff_usec = this_time_usec-lastTimeUsec; + lastTimeUsec = this_time_usec; + if(diff_usec >= usecSinceLast) { + usecSinceLast = 0; + } else { + usecSinceLast -= diff_usec; + } + } + usecSinceLast++; + return (this_time_usec+usecSinceLast); +} + Bit64u +bx_pc_system_c::time_usec() { + return (Bit64u) (((double)(Bit64s)time_ticks()) / m_ips ); +} + + void +bx_pc_system_c::start_timers(void) +{ +} + + void +bx_pc_system_c::activate_timer_ticks(unsigned i, Bit64u ticks, bx_bool continuous) +{ +#if BX_TIMER_DEBUG + if (i >= numTimers) + BX_PANIC(("activate_timer_ticks: timer %u OOB", i)); + if (timer[i].period < MinAllowableTimerPeriod) + BX_PANIC(("activate_timer_ticks: timer[%u].period of " FMT_LL "u < min of %u", + i, timer[i].period, MinAllowableTimerPeriod)); +#endif + + // If the timer frequency is rediculously low, make it more sane. + // This happens when 'ips' is too low. + if (ticks < MinAllowableTimerPeriod) { + //BX_INFO(("activate_timer_ticks: adjusting ticks of %llu to min of %u", + // ticks, MinAllowableTimerPeriod)); + ticks = MinAllowableTimerPeriod; + } + + timer[i].period = ticks; + timer[i].timeToFire = (ticksTotal + Bit64u(currCountdownPeriod-currCountdown)) + + ticks; + timer[i].active = 1; + timer[i].continuous = continuous; + + if (ticks < Bit64u(currCountdown)) { + // This new timer needs to fire before the current countdown. + // Skew the current countdown and countdown period to be smaller + // by the delta. + currCountdownPeriod -= (currCountdown - Bit32u(ticks)); + currCountdown = Bit32u(ticks); + } +} + + void +bx_pc_system_c::activate_timer(unsigned i, Bit32u useconds, bx_bool continuous) +{ + Bit64u ticks; + +#if BX_TIMER_DEBUG + if (i >= numTimers) + BX_PANIC(("activate_timer: timer %u OOB", i)); +#endif + + // if useconds = 0, use default stored in period field + // else set new period from useconds + if (useconds==0) { + ticks = timer[i].period; + } + else { + // convert useconds to number of ticks + ticks = (Bit64u) (double(useconds) * m_ips); + + // If the timer frequency is rediculously low, make it more sane. + // This happens when 'ips' is too low. + if (ticks < MinAllowableTimerPeriod) { + //BX_INFO(("activate_timer: adjusting ticks of %llu to min of %u", + // ticks, MinAllowableTimerPeriod)); + ticks = MinAllowableTimerPeriod; + } + + timer[i].period = ticks; + } + + activate_timer_ticks(i, ticks, continuous); +} + + void +bx_pc_system_c::deactivate_timer( unsigned i ) +{ +#if BX_TIMER_DEBUG + if (i >= numTimers) + BX_PANIC(("deactivate_timer: timer %u OOB", i)); +#endif + + timer[i].active = 0; +} + + unsigned +bx_pc_system_c::unregisterTimer(int timerIndex) +{ + unsigned i = (unsigned) timerIndex; + +#if BX_TIMER_DEBUG + if (i >= numTimers) + BX_PANIC(("unregisterTimer: timer %u OOB", i)); + if (i == 0) + BX_PANIC(("unregisterTimer: timer 0 is the nullTimer!")); + if (timer[i].inUse == 0) + BX_PANIC(("unregisterTimer: timer %u is not in-use!", i)); +#endif + + if (timer[i].active) { + BX_PANIC(("unregisterTimer: timer '%s' is still active!", timer[i].id)); + return(0); // Fail. + } + + // Reset timer fields for good measure. + timer[i].inUse = 0; // No longer registered. + timer[i].period = BX_MAX_BIT64S; // Max value (invalid) + timer[i].timeToFire = BX_MAX_BIT64S; // Max value (invalid) + timer[i].continuous = 0; + timer[i].funct = NULL; + timer[i].this_ptr = NULL; + memset(timer[i].id, 0, BxMaxTimerIDLen); + + return(1); // OK +} |