1 /////////////////////////////////////////////////////////////////////////
2 // $Id: pc_system.cc,v 1.65 2006/09/17 20:37:27 vruppert 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
);
132 void bx_pc_system_c::set_enable_a20(bx_bool value
)
134 bx_bool old_enable_a20
= enable_a20
;
140 #elif BX_CPU_LEVEL == 2
143 a20_mask
= 0xffffffff;
148 a20_mask
= 0xffefffff; /* mask off A20 address line */
151 BX_DBG_A20_REPORT(enable_a20
);
153 BX_DEBUG(("A20: set() = %u", (unsigned) enable_a20
));
155 // If there has been a transition, we need to notify the CPUs so
156 // they can potentially invalidate certain cache info based on
157 // A20-line-applied physical addresses.
158 if (old_enable_a20
!= enable_a20
) MemoryMappingChanged();
161 bx_bool
bx_pc_system_c::get_enable_a20(void)
164 BX_INFO(("A20: get() = %u", (unsigned) enable_a20
));
171 void bx_pc_system_c::set_enable_a20(bx_bool value
)
173 BX_DEBUG(("set_enable_a20: ignoring: SUPPORT_A20 = 0"));
176 bx_bool
bx_pc_system_c::get_enable_a20(void)
178 BX_DEBUG(("get_enable_a20: ignoring: SUPPORT_A20 = 0"));
182 #endif // #if BX_SUPPORT_A20
184 void bx_pc_system_c::MemoryMappingChanged(void)
186 for (unsigned i
=0; i
<BX_SMP_PROCESSORS
; i
++)
187 BX_CPU(i
)->TLB_flush(1);
190 int bx_pc_system_c::Reset(unsigned type
)
192 // type is BX_RESET_HARDWARE or BX_RESET_SOFTWARE
193 BX_INFO(("bx_pc_system_c::Reset(%s) called",type
==BX_RESET_HARDWARE
?"HARDWARE":"SOFTWARE"));
198 for (int i
=0; i
<BX_SMP_PROCESSORS
; i
++) {
199 BX_CPU(i
)->reset(type
);
202 // Reset devices only on Hardware resets
203 if (type
==BX_RESET_HARDWARE
) {
204 DEV_reset_devices(type
);
210 Bit8u
bx_pc_system_c::IAC(void)
212 return DEV_pic_iac();
215 void bx_pc_system_c::exit(void)
217 // delete all registered timers (exception: null timer and APIC timer)
218 numTimers
= 1 + BX_SUPPORT_APIC
;
226 #if BX_SUPPORT_SAVE_RESTORE
227 void bx_pc_system_c::register_state(void)
230 bx_list_c
*list
= new bx_list_c(SIM
->get_sr_root(), "pc_system", "PC System State", 8);
231 BXRS_PARAM_BOOL(list
, enable_a20
, enable_a20
);
232 BXRS_DEC_PARAM_SIMPLE(list
, currCountdown
);
233 BXRS_DEC_PARAM_SIMPLE(list
, currCountdownPeriod
);
234 BXRS_DEC_PARAM_SIMPLE(list
, ticksTotal
);
235 BXRS_DEC_PARAM_SIMPLE(list
, lastTimeUsec
);
236 BXRS_DEC_PARAM_SIMPLE(list
, usecSinceLast
);
237 BXRS_PARAM_BOOL(list
, HRQ
, HRQ
);
239 bx_list_c
*timers
= new bx_list_c(list
, "timer", numTimers
);
240 for (unsigned i
= 0; i
< numTimers
; i
++) {
242 sprintf(name
, "%d", i
);
243 bx_list_c
*bxtimer
= new bx_list_c(timers
, name
, 5);
244 BXRS_PARAM_BOOL(bxtimer
, inUse
, timer
[i
].inUse
);
245 BXRS_DEC_PARAM_FIELD(bxtimer
, period
, timer
[i
].period
);
246 BXRS_DEC_PARAM_FIELD(bxtimer
, timeToFire
, timer
[i
].timeToFire
);
247 BXRS_PARAM_BOOL(bxtimer
, active
, timer
[i
].active
);
248 BXRS_PARAM_BOOL(bxtimer
, continuous
, timer
[i
].continuous
);
254 // ================================================
255 // Bochs internal timer delivery framework features
256 // ================================================
258 int bx_pc_system_c::register_timer( void *this_ptr
, void (*funct
)(void *),
259 Bit32u useconds
, bx_bool continuous
, bx_bool active
, const char *id
)
261 // Convert useconds to number of ticks.
262 Bit64u ticks
= (Bit64u
) (double(useconds
) * m_ips
);
264 return register_timer_ticks(this_ptr
, funct
, ticks
, continuous
, active
, id
);
267 int bx_pc_system_c::register_timer_ticks(void* this_ptr
, bx_timer_handler_t funct
,
268 Bit64u ticks
, bx_bool continuous
, bx_bool active
, const char *id
)
272 // If the timer frequency is rediculously low, make it more sane.
273 // This happens when 'ips' is too low.
274 if (ticks
< MinAllowableTimerPeriod
) {
275 //BX_INFO(("register_timer_ticks: adjusting ticks of %llu to min of %u",
276 // ticks, MinAllowableTimerPeriod));
277 ticks
= MinAllowableTimerPeriod
;
280 // search for new timer for i=1, i=0 is reserved for NullTimer
281 for (i
=1; i
< numTimers
; i
++) {
282 if (timer
[i
].inUse
== 0)
288 BX_PANIC(("register_timer: cannot register NullTimer again!"));
289 if (numTimers
>= BX_MAX_TIMERS
)
290 BX_PANIC(("register_timer: too many registered timers"));
291 if (this_ptr
== NULL
)
292 BX_PANIC(("register_timer_ticks: this_ptr is NULL!"));
294 BX_PANIC(("register_timer_ticks: funct is NULL!"));
298 timer
[i
].period
= ticks
;
299 timer
[i
].timeToFire
= (ticksTotal
+ Bit64u(currCountdownPeriod
-currCountdown
)) +
301 timer
[i
].active
= active
;
302 timer
[i
].continuous
= continuous
;
303 timer
[i
].funct
= funct
;
304 timer
[i
].this_ptr
= this_ptr
;
305 strncpy(timer
[i
].id
, id
, BxMaxTimerIDLen
);
306 timer
[i
].id
[BxMaxTimerIDLen
-1] = 0; // Null terminate if not already.
309 if (ticks
< Bit64u(currCountdown
)) {
310 // This new timer needs to fire before the current countdown.
311 // Skew the current countdown and countdown period to be smaller
313 currCountdownPeriod
-= (currCountdown
- Bit32u(ticks
));
314 currCountdown
= Bit32u(ticks
);
318 BX_DEBUG(("timer id %d registered for '%s'", i
, id
));
319 // If we didn't find a free slot, increment the bound, numTimers.
321 numTimers
++; // One new timer installed.
327 void bx_pc_system_c::countdownEvent(void)
330 Bit64u minTimeToFire
;
331 bx_bool triggered
[BX_MAX_TIMERS
];
333 // The countdown decremented to 0. We need to service all the active
334 // timers, and invoke callbacks from those timers which have fired.
336 if (currCountdown
!= 0)
337 BX_PANIC(("countdownEvent: ticks!=0"));
340 // Increment global ticks counter by number of ticks which have
341 // elapsed since the last update.
342 ticksTotal
+= Bit64u(currCountdownPeriod
);
343 minTimeToFire
= (Bit64u
) -1;
345 for (i
=0; i
< numTimers
; i
++) {
346 triggered
[i
] = 0; // Reset triggered flag.
347 if (timer
[i
].active
) {
349 if (ticksTotal
> timer
[i
].timeToFire
)
350 BX_PANIC(("countdownEvent: ticksTotal > timeToFire[%u], D " FMT_LL
"u", i
,
351 timer
[i
].timeToFire
-ticksTotal
));
353 if (ticksTotal
== timer
[i
].timeToFire
) {
354 // This timer is ready to fire.
357 if (timer
[i
].continuous
==0) {
358 // If triggered timer is one-shot, deactive.
362 // Continuous timer, increment time-to-fire by period.
363 timer
[i
].timeToFire
+= timer
[i
].period
;
364 if (timer
[i
].timeToFire
< minTimeToFire
)
365 minTimeToFire
= timer
[i
].timeToFire
;
369 // This timer is not ready to fire yet.
370 if (timer
[i
].timeToFire
< minTimeToFire
)
371 minTimeToFire
= timer
[i
].timeToFire
;
376 // Calculate next countdown period. We need to do this before calling
377 // any of the callbacks, as they may call timer features, which need
378 // to be advanced to the next countdown cycle.
379 currCountdown
= currCountdownPeriod
=
380 Bit32u(minTimeToFire
- ticksTotal
);
382 for (i
=0; i
< numTimers
; i
++) {
383 // Call requested timer function. It may request a different
384 // timer period or deactivate etc.
387 timer
[i
].funct(timer
[i
].this_ptr
);
393 void bx_pc_system_c::nullTimer(void* this_ptr
)
395 // This function is always inserted in timer[0]. It is sort of
396 // a heartbeat timer. It ensures that at least one timer is
397 // always active to make the timer logic more simple, and has
398 // a duration of less than the maximum 32-bit integer, so that
399 // a 32-bit size can be used for the hot countdown timer. The
400 // rest of the timer info can be 64-bits. This is also a good
401 // place for some logic to report actual emulated
402 // instructions-per-second (IPS) data when measured relative to
403 // the host computer's wall clock.
407 #if SpewPeriodicTimerInfo
408 BX_INFO(("==================================="));
409 for (unsigned i
=0; i
< bx_pc_system
.numTimers
; i
++) {
410 if (bx_pc_system
.timer
[i
].active
) {
411 BX_INFO(("BxTimer(%s): period=" FMT_LL
"u, continuous=%u",
412 bx_pc_system
.timer
[i
].id
, bx_pc_system
.timer
[i
].period
,
413 bx_pc_system
.timer
[i
].continuous
));
418 #if BX_SUPPORT_ICACHE
424 void bx_pc_system_c::timebp_handler(void* this_ptr
)
426 BX_CPU(0)->break_point
= BREAK_POINT_TIME
;
427 BX_DEBUG(("Time breakpoint triggered"));
429 if (timebp_queue_size
> 1) {
430 Bit64s new_diff
= timebp_queue
[1] - bx_pc_system
.time_ticks();
431 bx_pc_system
.activate_timer_ticks(timebp_timer
, new_diff
, 1);
434 for (int i
= 0; i
< timebp_queue_size
; i
++)
435 timebp_queue
[i
] = timebp_queue
[i
+1];
437 #endif // BX_DEBUGGER
439 Bit64u
bx_pc_system_c::time_usec_sequential()
441 Bit64u this_time_usec
= time_usec();
442 if(this_time_usec
!= lastTimeUsec
) {
443 Bit64u diff_usec
= this_time_usec
-lastTimeUsec
;
444 lastTimeUsec
= this_time_usec
;
445 if(diff_usec
>= usecSinceLast
) {
448 usecSinceLast
-= diff_usec
;
452 return (this_time_usec
+usecSinceLast
);
455 Bit64u
bx_pc_system_c::time_usec()
457 return (Bit64u
) (((double)(Bit64s
)time_ticks()) / m_ips
);
460 void bx_pc_system_c::start_timers(void) { }
462 void bx_pc_system_c::activate_timer_ticks(unsigned i
, Bit64u ticks
, bx_bool continuous
)
466 BX_PANIC(("activate_timer_ticks: timer %u OOB", i
));
468 BX_PANIC(("activate_timer_ticks: timer 0 is the NullTimer!"));
469 if (timer
[i
].period
< MinAllowableTimerPeriod
)
470 BX_PANIC(("activate_timer_ticks: timer[%u].period of " FMT_LL
"u < min of %u",
471 i
, timer
[i
].period
, MinAllowableTimerPeriod
));
474 // If the timer frequency is rediculously low, make it more sane.
475 // This happens when 'ips' is too low.
476 if (ticks
< MinAllowableTimerPeriod
) {
477 //BX_INFO(("activate_timer_ticks: adjusting ticks of %llu to min of %u",
478 // ticks, MinAllowableTimerPeriod));
479 ticks
= MinAllowableTimerPeriod
;
482 timer
[i
].period
= ticks
;
483 timer
[i
].timeToFire
= (ticksTotal
+ Bit64u(currCountdownPeriod
-currCountdown
)) +
486 timer
[i
].continuous
= continuous
;
488 if (ticks
< Bit64u(currCountdown
)) {
489 // This new timer needs to fire before the current countdown.
490 // Skew the current countdown and countdown period to be smaller
492 currCountdownPeriod
-= (currCountdown
- Bit32u(ticks
));
493 currCountdown
= Bit32u(ticks
);
497 void bx_pc_system_c::activate_timer(unsigned i
, Bit32u useconds
, bx_bool continuous
)
503 BX_PANIC(("activate_timer: timer %u OOB", i
));
505 BX_PANIC(("activate_timer: timer 0 is the nullTimer!"));
508 // if useconds = 0, use default stored in period field
509 // else set new period from useconds
511 ticks
= timer
[i
].period
;
514 // convert useconds to number of ticks
515 ticks
= (Bit64u
) (double(useconds
) * m_ips
);
517 // If the timer frequency is rediculously low, make it more sane.
518 // This happens when 'ips' is too low.
519 if (ticks
< MinAllowableTimerPeriod
) {
520 //BX_INFO(("activate_timer: adjusting ticks of %llu to min of %u",
521 // ticks, MinAllowableTimerPeriod));
522 ticks
= MinAllowableTimerPeriod
;
525 timer
[i
].period
= ticks
;
528 activate_timer_ticks(i
, ticks
, continuous
);
531 void bx_pc_system_c::deactivate_timer(unsigned i
)
535 BX_PANIC(("deactivate_timer: timer %u OOB", i
));
537 BX_PANIC(("deactivate_timer: timer 0 is the nullTimer!"));
543 bx_bool
bx_pc_system_c::unregisterTimer(unsigned timerIndex
)
546 if (timerIndex
>= numTimers
)
547 BX_PANIC(("unregisterTimer: timer %u OOB", timerIndex
));
549 BX_PANIC(("unregisterTimer: timer 0 is the nullTimer!"));
550 if (timer
[timerIndex
].inUse
== 0)
551 BX_PANIC(("unregisterTimer: timer %u is not in-use!", timerIndex
));
554 if (timer
[timerIndex
].active
) {
555 BX_PANIC(("unregisterTimer: timer '%s' is still active!", timer
[timerIndex
].id
));
559 // Reset timer fields for good measure.
560 timer
[timerIndex
].inUse
= 0; // No longer registered.
561 timer
[timerIndex
].period
= BX_MAX_BIT64S
; // Max value (invalid)
562 timer
[timerIndex
].timeToFire
= BX_MAX_BIT64S
; // Max value (invalid)
563 timer
[timerIndex
].continuous
= 0;
564 timer
[timerIndex
].funct
= NULL
;
565 timer
[timerIndex
].this_ptr
= NULL
;
566 memset(timer
[timerIndex
].id
, 0, BxMaxTimerIDLen
);
568 if (timerIndex
== (numTimers
-1)) numTimers
--;