1 /////////////////////////////////////////////////////////////////////////
2 // $Id: pc_system.cc,v 1.70 2008/04/06 18:27:24 sshwarts Exp $
3 /////////////////////////////////////////////////////////////////////////
5 // Copyright (C) 2002 MandrakeSoft S.A.
9 // 75002 Paris - France
10 // http://www.linux-mandrake.com/
11 // http://www.mandrakesoft.com/
13 // This library is free software; you can redistribute it and/or
14 // modify it under the terms of the GNU Lesser General Public
15 // License as published by the Free Software Foundation; either
16 // version 2 of the License, or (at your option) any later version.
18 // This library is distributed in the hope that it will be useful,
19 // but WITHOUT ANY WARRANTY; without even the implied warranty of
20 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 // Lesser General Public License for more details.
23 // You should have received a copy of the GNU Lesser General Public
24 // License along with this library; if not, write to the Free Software
25 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
30 #include "iodev/iodev.h"
31 #define LOG_THIS bx_pc_system.
35 // #include <winsock2.h> // +++
40 #if defined(PROVIDE_M_IPS)
41 double m_ips
; // Millions of Instructions Per Second
44 // Option for turning off BX_TIMER_DEBUG?
45 // Check out m_ips and ips
47 #define SpewPeriodicTimerInfo 0
48 #define MinAllowableTimerPeriod 1
51 const Bit64u
bx_pc_system_c::NullTimerInterval
= ICacheWriteStampStart
;
53 // This must be the maximum 32-bit unsigned int value, NOT (Bit64u) -1.
54 const Bit64u
bx_pc_system_c::NullTimerInterval
= 0xffffffff;
58 bx_pc_system_c::bx_pc_system_c()
62 BX_ASSERT(numTimers
== 0);
64 // Timer[0] is the null timer. It is initialized as a special
65 // case here. It should never be turned off or modified, and its
66 // duration should always remain the same.
67 ticksTotal
= 0; // Reset ticks since emulator started.
69 timer
[0].period
= NullTimerInterval
;
71 timer
[0].continuous
= 1;
72 timer
[0].funct
= nullTimer
;
73 timer
[0].this_ptr
= this;
74 numTimers
= 1; // So far, only the nullTimer.
77 void bx_pc_system_c::initialize(Bit32u ips
)
80 timer
[0].timeToFire
= NullTimerInterval
;
81 currCountdown
= NullTimerInterval
;
82 currCountdownPeriod
= NullTimerInterval
;
87 kill_bochs_request
= 0;
89 // parameter 'ips' is the processor speed in Instructions-Per-Second
90 m_ips
= double(ips
) / 1000000.0L;
92 BX_DEBUG(("ips = %u", (unsigned) ips
));
95 void bx_pc_system_c::set_HRQ(bx_bool val
)
99 BX_CPU(0)->async_event
= 1;
102 void bx_pc_system_c::set_INTR(bx_bool value
)
104 if (bx_dbg
.interrupts
)
105 BX_INFO(("pc_system: Setting INTR=%d on bootstrap processor %d", (int)value
, BX_BOOTSTRAP_PROCESSOR
));
106 BX_CPU(BX_BOOTSTRAP_PROCESSOR
)->set_INTR(value
);
110 // Read from the IO memory address space
113 Bit32u
BX_CPP_AttrRegparmN(2)
114 bx_pc_system_c::inp(Bit16u addr
, unsigned io_len
)
116 Bit32u ret
= bx_devices
.inp(addr
, io_len
);
121 // Write to the IO memory address space.
124 void BX_CPP_AttrRegparmN(3)
125 bx_pc_system_c::outp(Bit16u addr
, Bit32u value
, unsigned io_len
)
127 bx_devices
.outp(addr
, value
, io_len
);
130 void bx_pc_system_c::set_enable_a20(bx_bool value
)
133 bx_bool old_enable_a20
= enable_a20
;
139 #elif BX_CPU_LEVEL == 2
142 a20_mask
= 0xffffffff;
147 a20_mask
= 0xffefffff; /* mask off A20 address line */
150 BX_DBG_A20_REPORT(enable_a20
);
152 BX_DEBUG(("A20: set() = %u", (unsigned) enable_a20
));
154 // If there has been a transition, we need to notify the CPUs so
155 // they can potentially invalidate certain cache info based on
156 // A20-line-applied physical addresses.
157 if (old_enable_a20
!= enable_a20
) MemoryMappingChanged();
159 BX_DEBUG(("set_enable_a20: ignoring: BX_SUPPORT_A20 = 0"));
163 bx_bool
bx_pc_system_c::get_enable_a20(void)
167 BX_INFO(("A20: get() = %u", (unsigned) enable_a20
));
171 BX_DEBUG(("get_enable_a20: ignoring: BX_SUPPORT_A20 = 0"));
176 void bx_pc_system_c::MemoryMappingChanged(void)
178 for (unsigned i
=0; i
<BX_SMP_PROCESSORS
; i
++)
179 BX_CPU(i
)->TLB_flush(1);
182 void bx_pc_system_c::invlpg(bx_address addr
)
184 for (unsigned i
=0; i
<BX_SMP_PROCESSORS
; i
++)
185 BX_CPU(i
)->TLB_invlpg(addr
);
188 int bx_pc_system_c::Reset(unsigned type
)
190 // type is BX_RESET_HARDWARE or BX_RESET_SOFTWARE
191 BX_INFO(("bx_pc_system_c::Reset(%s) called",type
==BX_RESET_HARDWARE
?"HARDWARE":"SOFTWARE"));
196 for (int i
=0; i
<BX_SMP_PROCESSORS
; i
++) {
197 BX_CPU(i
)->reset(type
);
200 // Reset devices only on Hardware resets
201 if (type
==BX_RESET_HARDWARE
) {
202 DEV_reset_devices(type
);
208 Bit8u
bx_pc_system_c::IAC(void)
210 return DEV_pic_iac();
213 void bx_pc_system_c::exit(void)
215 // delete all registered timers (exception: null timer and APIC timer)
216 numTimers
= 1 + BX_SUPPORT_APIC
;
224 void bx_pc_system_c::register_state(void)
227 bx_list_c
*list
= new bx_list_c(SIM
->get_bochs_root(), "pc_system", "PC System State", 8);
228 BXRS_PARAM_BOOL(list
, enable_a20
, enable_a20
);
229 BXRS_DEC_PARAM_SIMPLE(list
, currCountdown
);
230 BXRS_DEC_PARAM_SIMPLE(list
, currCountdownPeriod
);
231 BXRS_DEC_PARAM_SIMPLE(list
, ticksTotal
);
232 BXRS_DEC_PARAM_SIMPLE(list
, lastTimeUsec
);
233 BXRS_DEC_PARAM_SIMPLE(list
, usecSinceLast
);
234 BXRS_PARAM_BOOL(list
, HRQ
, HRQ
);
236 bx_list_c
*timers
= new bx_list_c(list
, "timer", numTimers
);
237 for (unsigned i
= 0; i
< numTimers
; i
++) {
239 sprintf(name
, "%d", i
);
240 bx_list_c
*bxtimer
= new bx_list_c(timers
, name
, 5);
241 BXRS_PARAM_BOOL(bxtimer
, inUse
, timer
[i
].inUse
);
242 BXRS_DEC_PARAM_FIELD(bxtimer
, period
, timer
[i
].period
);
243 BXRS_DEC_PARAM_FIELD(bxtimer
, timeToFire
, timer
[i
].timeToFire
);
244 BXRS_PARAM_BOOL(bxtimer
, active
, timer
[i
].active
);
245 BXRS_PARAM_BOOL(bxtimer
, continuous
, timer
[i
].continuous
);
249 // ================================================
250 // Bochs internal timer delivery framework features
251 // ================================================
253 int bx_pc_system_c::register_timer(void *this_ptr
, void (*funct
)(void *),
254 Bit32u useconds
, bx_bool continuous
, bx_bool active
, const char *id
)
256 // Convert useconds to number of ticks.
257 Bit64u ticks
= (Bit64u
) (double(useconds
) * m_ips
);
259 return register_timer_ticks(this_ptr
, funct
, ticks
, continuous
, active
, id
);
262 int bx_pc_system_c::register_timer_ticks(void* this_ptr
, bx_timer_handler_t funct
,
263 Bit64u ticks
, bx_bool continuous
, bx_bool active
, const char *id
)
267 // If the timer frequency is rediculously low, make it more sane.
268 // This happens when 'ips' is too low.
269 if (ticks
< MinAllowableTimerPeriod
) {
270 //BX_INFO(("register_timer_ticks: adjusting ticks of %llu to min of %u",
271 // ticks, MinAllowableTimerPeriod));
272 ticks
= MinAllowableTimerPeriod
;
275 // search for new timer for i=1, i=0 is reserved for NullTimer
276 for (i
=1; i
< numTimers
; i
++) {
277 if (timer
[i
].inUse
== 0)
283 BX_PANIC(("register_timer: cannot register NullTimer again!"));
284 if (numTimers
>= BX_MAX_TIMERS
)
285 BX_PANIC(("register_timer: too many registered timers"));
286 if (this_ptr
== NULL
)
287 BX_PANIC(("register_timer_ticks: this_ptr is NULL!"));
289 BX_PANIC(("register_timer_ticks: funct is NULL!"));
293 timer
[i
].period
= ticks
;
294 timer
[i
].timeToFire
= (ticksTotal
+ Bit64u(currCountdownPeriod
-currCountdown
)) +
296 timer
[i
].active
= active
;
297 timer
[i
].continuous
= continuous
;
298 timer
[i
].funct
= funct
;
299 timer
[i
].this_ptr
= this_ptr
;
300 strncpy(timer
[i
].id
, id
, BxMaxTimerIDLen
);
301 timer
[i
].id
[BxMaxTimerIDLen
-1] = 0; // Null terminate if not already.
304 if (ticks
< Bit64u(currCountdown
)) {
305 // This new timer needs to fire before the current countdown.
306 // Skew the current countdown and countdown period to be smaller
308 currCountdownPeriod
-= (currCountdown
- Bit32u(ticks
));
309 currCountdown
= Bit32u(ticks
);
313 BX_DEBUG(("timer id %d registered for '%s'", i
, id
));
314 // If we didn't find a free slot, increment the bound, numTimers.
316 numTimers
++; // One new timer installed.
322 void bx_pc_system_c::countdownEvent(void)
325 Bit64u minTimeToFire
;
326 bx_bool triggered
[BX_MAX_TIMERS
];
328 // The countdown decremented to 0. We need to service all the active
329 // timers, and invoke callbacks from those timers which have fired.
331 if (currCountdown
!= 0)
332 BX_PANIC(("countdownEvent: ticks!=0"));
335 // Increment global ticks counter by number of ticks which have
336 // elapsed since the last update.
337 ticksTotal
+= Bit64u(currCountdownPeriod
);
338 minTimeToFire
= (Bit64u
) -1;
340 for (i
=0; i
< numTimers
; i
++) {
341 triggered
[i
] = 0; // Reset triggered flag.
342 if (timer
[i
].active
) {
344 if (ticksTotal
> timer
[i
].timeToFire
)
345 BX_PANIC(("countdownEvent: ticksTotal > timeToFire[%u], D " FMT_LL
"u", i
,
346 timer
[i
].timeToFire
-ticksTotal
));
348 if (ticksTotal
== timer
[i
].timeToFire
) {
349 // This timer is ready to fire.
352 if (timer
[i
].continuous
==0) {
353 // If triggered timer is one-shot, deactive.
357 // Continuous timer, increment time-to-fire by period.
358 timer
[i
].timeToFire
+= timer
[i
].period
;
359 if (timer
[i
].timeToFire
< minTimeToFire
)
360 minTimeToFire
= timer
[i
].timeToFire
;
364 // This timer is not ready to fire yet.
365 if (timer
[i
].timeToFire
< minTimeToFire
)
366 minTimeToFire
= timer
[i
].timeToFire
;
371 // Calculate next countdown period. We need to do this before calling
372 // any of the callbacks, as they may call timer features, which need
373 // to be advanced to the next countdown cycle.
374 currCountdown
= currCountdownPeriod
=
375 Bit32u(minTimeToFire
- ticksTotal
);
377 for (i
=0; i
< numTimers
; i
++) {
378 // Call requested timer function. It may request a different
379 // timer period or deactivate etc.
382 timer
[i
].funct(timer
[i
].this_ptr
);
388 void bx_pc_system_c::nullTimer(void* this_ptr
)
390 // This function is always inserted in timer[0]. It is sort of
391 // a heartbeat timer. It ensures that at least one timer is
392 // always active to make the timer logic more simple, and has
393 // a duration of less than the maximum 32-bit integer, so that
394 // a 32-bit size can be used for the hot countdown timer. The
395 // rest of the timer info can be 64-bits. This is also a good
396 // place for some logic to report actual emulated
397 // instructions-per-second (IPS) data when measured relative to
398 // the host computer's wall clock.
402 #if SpewPeriodicTimerInfo
403 BX_INFO(("==================================="));
404 for (unsigned i
=0; i
< bx_pc_system
.numTimers
; i
++) {
405 if (bx_pc_system
.timer
[i
].active
) {
406 BX_INFO(("BxTimer(%s): period=" FMT_LL
"u, continuous=%u",
407 bx_pc_system
.timer
[i
].id
, bx_pc_system
.timer
[i
].period
,
408 bx_pc_system
.timer
[i
].continuous
));
413 #if BX_SUPPORT_ICACHE
418 void bx_pc_system_c::benchmarkTimer(void* this_ptr
)
420 bx_pc_system_c
*class_ptr
= (bx_pc_system_c
*) this_ptr
;
421 class_ptr
->kill_bochs_request
= 1;
426 void bx_pc_system_c::timebp_handler(void* this_ptr
)
428 BX_CPU(0)->break_point
= BREAK_POINT_TIME
;
429 BX_DEBUG(("Time breakpoint triggered"));
431 if (timebp_queue_size
> 1) {
432 Bit64s new_diff
= timebp_queue
[1] - bx_pc_system
.time_ticks();
433 bx_pc_system
.activate_timer_ticks(timebp_timer
, new_diff
, 1);
436 for (int i
= 0; i
< timebp_queue_size
; i
++)
437 timebp_queue
[i
] = timebp_queue
[i
+1];
439 #endif // BX_DEBUGGER
441 Bit64u
bx_pc_system_c::time_usec_sequential()
443 Bit64u this_time_usec
= time_usec();
444 if(this_time_usec
!= lastTimeUsec
) {
445 Bit64u diff_usec
= this_time_usec
-lastTimeUsec
;
446 lastTimeUsec
= this_time_usec
;
447 if(diff_usec
>= usecSinceLast
) {
450 usecSinceLast
-= diff_usec
;
454 return (this_time_usec
+usecSinceLast
);
457 Bit64u
bx_pc_system_c::time_usec()
459 return (Bit64u
) (((double)(Bit64s
)time_ticks()) / m_ips
);
462 void bx_pc_system_c::start_timers(void) { }
464 void bx_pc_system_c::activate_timer_ticks(unsigned i
, Bit64u ticks
, bx_bool continuous
)
468 BX_PANIC(("activate_timer_ticks: timer %u OOB", i
));
470 BX_PANIC(("activate_timer_ticks: timer 0 is the NullTimer!"));
471 if (timer
[i
].period
< MinAllowableTimerPeriod
)
472 BX_PANIC(("activate_timer_ticks: timer[%u].period of " FMT_LL
"u < min of %u",
473 i
, timer
[i
].period
, MinAllowableTimerPeriod
));
476 // If the timer frequency is rediculously low, make it more sane.
477 // This happens when 'ips' is too low.
478 if (ticks
< MinAllowableTimerPeriod
) {
479 //BX_INFO(("activate_timer_ticks: adjusting ticks of %llu to min of %u",
480 // ticks, MinAllowableTimerPeriod));
481 ticks
= MinAllowableTimerPeriod
;
484 timer
[i
].period
= ticks
;
485 timer
[i
].timeToFire
= (ticksTotal
+ Bit64u(currCountdownPeriod
-currCountdown
)) +
488 timer
[i
].continuous
= continuous
;
490 if (ticks
< Bit64u(currCountdown
)) {
491 // This new timer needs to fire before the current countdown.
492 // Skew the current countdown and countdown period to be smaller
494 currCountdownPeriod
-= (currCountdown
- Bit32u(ticks
));
495 currCountdown
= Bit32u(ticks
);
499 void bx_pc_system_c::activate_timer(unsigned i
, Bit32u useconds
, bx_bool continuous
)
505 BX_PANIC(("activate_timer: timer %u OOB", i
));
507 BX_PANIC(("activate_timer: timer 0 is the nullTimer!"));
510 // if useconds = 0, use default stored in period field
511 // else set new period from useconds
513 ticks
= timer
[i
].period
;
516 // convert useconds to number of ticks
517 ticks
= (Bit64u
) (double(useconds
) * m_ips
);
519 // If the timer frequency is rediculously low, make it more sane.
520 // This happens when 'ips' is too low.
521 if (ticks
< MinAllowableTimerPeriod
) {
522 //BX_INFO(("activate_timer: adjusting ticks of %llu to min of %u",
523 // ticks, MinAllowableTimerPeriod));
524 ticks
= MinAllowableTimerPeriod
;
527 timer
[i
].period
= ticks
;
530 activate_timer_ticks(i
, ticks
, continuous
);
533 void bx_pc_system_c::deactivate_timer(unsigned i
)
537 BX_PANIC(("deactivate_timer: timer %u OOB", i
));
539 BX_PANIC(("deactivate_timer: timer 0 is the nullTimer!"));
545 bx_bool
bx_pc_system_c::unregisterTimer(unsigned timerIndex
)
548 if (timerIndex
>= numTimers
)
549 BX_PANIC(("unregisterTimer: timer %u OOB", timerIndex
));
551 BX_PANIC(("unregisterTimer: timer 0 is the nullTimer!"));
552 if (timer
[timerIndex
].inUse
== 0)
553 BX_PANIC(("unregisterTimer: timer %u is not in-use!", timerIndex
));
556 if (timer
[timerIndex
].active
) {
557 BX_PANIC(("unregisterTimer: timer '%s' is still active!", timer
[timerIndex
].id
));
561 // Reset timer fields for good measure.
562 timer
[timerIndex
].inUse
= 0; // No longer registered.
563 timer
[timerIndex
].period
= BX_MAX_BIT64S
; // Max value (invalid)
564 timer
[timerIndex
].timeToFire
= BX_MAX_BIT64S
; // Max value (invalid)
565 timer
[timerIndex
].continuous
= 0;
566 timer
[timerIndex
].funct
= NULL
;
567 timer
[timerIndex
].this_ptr
= NULL
;
568 memset(timer
[timerIndex
].id
, 0, BxMaxTimerIDLen
);
570 if (timerIndex
== (numTimers
-1)) numTimers
--;