1 /////////////////////////////////////////////////////////////////////////
2 // $Id: pc_system.cc,v 1.68 2007/11/01 18:03:48 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
);
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 void bx_pc_system_c::invlpg(bx_address addr
)
192 for (unsigned i
=0; i
<BX_SMP_PROCESSORS
; i
++)
193 BX_CPU(i
)->TLB_invlpg(addr
);
196 int bx_pc_system_c::Reset(unsigned type
)
198 // type is BX_RESET_HARDWARE or BX_RESET_SOFTWARE
199 BX_INFO(("bx_pc_system_c::Reset(%s) called",type
==BX_RESET_HARDWARE
?"HARDWARE":"SOFTWARE"));
204 for (int i
=0; i
<BX_SMP_PROCESSORS
; i
++) {
205 BX_CPU(i
)->reset(type
);
208 // Reset devices only on Hardware resets
209 if (type
==BX_RESET_HARDWARE
) {
210 DEV_reset_devices(type
);
216 Bit8u
bx_pc_system_c::IAC(void)
218 return DEV_pic_iac();
221 void bx_pc_system_c::exit(void)
223 // delete all registered timers (exception: null timer and APIC timer)
224 numTimers
= 1 + BX_SUPPORT_APIC
;
232 void bx_pc_system_c::register_state(void)
235 bx_list_c
*list
= new bx_list_c(SIM
->get_bochs_root(), "pc_system", "PC System State", 8);
236 BXRS_PARAM_BOOL(list
, enable_a20
, enable_a20
);
237 BXRS_DEC_PARAM_SIMPLE(list
, currCountdown
);
238 BXRS_DEC_PARAM_SIMPLE(list
, currCountdownPeriod
);
239 BXRS_DEC_PARAM_SIMPLE(list
, ticksTotal
);
240 BXRS_DEC_PARAM_SIMPLE(list
, lastTimeUsec
);
241 BXRS_DEC_PARAM_SIMPLE(list
, usecSinceLast
);
242 BXRS_PARAM_BOOL(list
, HRQ
, HRQ
);
244 bx_list_c
*timers
= new bx_list_c(list
, "timer", numTimers
);
245 for (unsigned i
= 0; i
< numTimers
; i
++) {
247 sprintf(name
, "%d", i
);
248 bx_list_c
*bxtimer
= new bx_list_c(timers
, name
, 5);
249 BXRS_PARAM_BOOL(bxtimer
, inUse
, timer
[i
].inUse
);
250 BXRS_DEC_PARAM_FIELD(bxtimer
, period
, timer
[i
].period
);
251 BXRS_DEC_PARAM_FIELD(bxtimer
, timeToFire
, timer
[i
].timeToFire
);
252 BXRS_PARAM_BOOL(bxtimer
, active
, timer
[i
].active
);
253 BXRS_PARAM_BOOL(bxtimer
, continuous
, timer
[i
].continuous
);
257 // ================================================
258 // Bochs internal timer delivery framework features
259 // ================================================
261 int bx_pc_system_c::register_timer( void *this_ptr
, void (*funct
)(void *),
262 Bit32u useconds
, bx_bool continuous
, bx_bool active
, const char *id
)
264 // Convert useconds to number of ticks.
265 Bit64u ticks
= (Bit64u
) (double(useconds
) * m_ips
);
267 return register_timer_ticks(this_ptr
, funct
, ticks
, continuous
, active
, id
);
270 int bx_pc_system_c::register_timer_ticks(void* this_ptr
, bx_timer_handler_t funct
,
271 Bit64u ticks
, bx_bool continuous
, bx_bool active
, const char *id
)
275 // If the timer frequency is rediculously low, make it more sane.
276 // This happens when 'ips' is too low.
277 if (ticks
< MinAllowableTimerPeriod
) {
278 //BX_INFO(("register_timer_ticks: adjusting ticks of %llu to min of %u",
279 // ticks, MinAllowableTimerPeriod));
280 ticks
= MinAllowableTimerPeriod
;
283 // search for new timer for i=1, i=0 is reserved for NullTimer
284 for (i
=1; i
< numTimers
; i
++) {
285 if (timer
[i
].inUse
== 0)
291 BX_PANIC(("register_timer: cannot register NullTimer again!"));
292 if (numTimers
>= BX_MAX_TIMERS
)
293 BX_PANIC(("register_timer: too many registered timers"));
294 if (this_ptr
== NULL
)
295 BX_PANIC(("register_timer_ticks: this_ptr is NULL!"));
297 BX_PANIC(("register_timer_ticks: funct is NULL!"));
301 timer
[i
].period
= ticks
;
302 timer
[i
].timeToFire
= (ticksTotal
+ Bit64u(currCountdownPeriod
-currCountdown
)) +
304 timer
[i
].active
= active
;
305 timer
[i
].continuous
= continuous
;
306 timer
[i
].funct
= funct
;
307 timer
[i
].this_ptr
= this_ptr
;
308 strncpy(timer
[i
].id
, id
, BxMaxTimerIDLen
);
309 timer
[i
].id
[BxMaxTimerIDLen
-1] = 0; // Null terminate if not already.
312 if (ticks
< Bit64u(currCountdown
)) {
313 // This new timer needs to fire before the current countdown.
314 // Skew the current countdown and countdown period to be smaller
316 currCountdownPeriod
-= (currCountdown
- Bit32u(ticks
));
317 currCountdown
= Bit32u(ticks
);
321 BX_DEBUG(("timer id %d registered for '%s'", i
, id
));
322 // If we didn't find a free slot, increment the bound, numTimers.
324 numTimers
++; // One new timer installed.
330 void bx_pc_system_c::countdownEvent(void)
333 Bit64u minTimeToFire
;
334 bx_bool triggered
[BX_MAX_TIMERS
];
336 // The countdown decremented to 0. We need to service all the active
337 // timers, and invoke callbacks from those timers which have fired.
339 if (currCountdown
!= 0)
340 BX_PANIC(("countdownEvent: ticks!=0"));
343 // Increment global ticks counter by number of ticks which have
344 // elapsed since the last update.
345 ticksTotal
+= Bit64u(currCountdownPeriod
);
346 minTimeToFire
= (Bit64u
) -1;
348 for (i
=0; i
< numTimers
; i
++) {
349 triggered
[i
] = 0; // Reset triggered flag.
350 if (timer
[i
].active
) {
352 if (ticksTotal
> timer
[i
].timeToFire
)
353 BX_PANIC(("countdownEvent: ticksTotal > timeToFire[%u], D " FMT_LL
"u", i
,
354 timer
[i
].timeToFire
-ticksTotal
));
356 if (ticksTotal
== timer
[i
].timeToFire
) {
357 // This timer is ready to fire.
360 if (timer
[i
].continuous
==0) {
361 // If triggered timer is one-shot, deactive.
365 // Continuous timer, increment time-to-fire by period.
366 timer
[i
].timeToFire
+= timer
[i
].period
;
367 if (timer
[i
].timeToFire
< minTimeToFire
)
368 minTimeToFire
= timer
[i
].timeToFire
;
372 // This timer is not ready to fire yet.
373 if (timer
[i
].timeToFire
< minTimeToFire
)
374 minTimeToFire
= timer
[i
].timeToFire
;
379 // Calculate next countdown period. We need to do this before calling
380 // any of the callbacks, as they may call timer features, which need
381 // to be advanced to the next countdown cycle.
382 currCountdown
= currCountdownPeriod
=
383 Bit32u(minTimeToFire
- ticksTotal
);
385 for (i
=0; i
< numTimers
; i
++) {
386 // Call requested timer function. It may request a different
387 // timer period or deactivate etc.
390 timer
[i
].funct(timer
[i
].this_ptr
);
396 void bx_pc_system_c::nullTimer(void* this_ptr
)
398 // This function is always inserted in timer[0]. It is sort of
399 // a heartbeat timer. It ensures that at least one timer is
400 // always active to make the timer logic more simple, and has
401 // a duration of less than the maximum 32-bit integer, so that
402 // a 32-bit size can be used for the hot countdown timer. The
403 // rest of the timer info can be 64-bits. This is also a good
404 // place for some logic to report actual emulated
405 // instructions-per-second (IPS) data when measured relative to
406 // the host computer's wall clock.
410 #if SpewPeriodicTimerInfo
411 BX_INFO(("==================================="));
412 for (unsigned i
=0; i
< bx_pc_system
.numTimers
; i
++) {
413 if (bx_pc_system
.timer
[i
].active
) {
414 BX_INFO(("BxTimer(%s): period=" FMT_LL
"u, continuous=%u",
415 bx_pc_system
.timer
[i
].id
, bx_pc_system
.timer
[i
].period
,
416 bx_pc_system
.timer
[i
].continuous
));
421 #if BX_SUPPORT_ICACHE
426 void bx_pc_system_c::benchmarkTimer(void* this_ptr
)
428 bx_pc_system_c
*class_ptr
= (bx_pc_system_c
*) this_ptr
;
429 class_ptr
->kill_bochs_request
= 1;
434 void bx_pc_system_c::timebp_handler(void* this_ptr
)
436 BX_CPU(0)->break_point
= BREAK_POINT_TIME
;
437 BX_DEBUG(("Time breakpoint triggered"));
439 if (timebp_queue_size
> 1) {
440 Bit64s new_diff
= timebp_queue
[1] - bx_pc_system
.time_ticks();
441 bx_pc_system
.activate_timer_ticks(timebp_timer
, new_diff
, 1);
444 for (int i
= 0; i
< timebp_queue_size
; i
++)
445 timebp_queue
[i
] = timebp_queue
[i
+1];
447 #endif // BX_DEBUGGER
449 Bit64u
bx_pc_system_c::time_usec_sequential()
451 Bit64u this_time_usec
= time_usec();
452 if(this_time_usec
!= lastTimeUsec
) {
453 Bit64u diff_usec
= this_time_usec
-lastTimeUsec
;
454 lastTimeUsec
= this_time_usec
;
455 if(diff_usec
>= usecSinceLast
) {
458 usecSinceLast
-= diff_usec
;
462 return (this_time_usec
+usecSinceLast
);
465 Bit64u
bx_pc_system_c::time_usec()
467 return (Bit64u
) (((double)(Bit64s
)time_ticks()) / m_ips
);
470 void bx_pc_system_c::start_timers(void) { }
472 void bx_pc_system_c::activate_timer_ticks(unsigned i
, Bit64u ticks
, bx_bool continuous
)
476 BX_PANIC(("activate_timer_ticks: timer %u OOB", i
));
478 BX_PANIC(("activate_timer_ticks: timer 0 is the NullTimer!"));
479 if (timer
[i
].period
< MinAllowableTimerPeriod
)
480 BX_PANIC(("activate_timer_ticks: timer[%u].period of " FMT_LL
"u < min of %u",
481 i
, timer
[i
].period
, MinAllowableTimerPeriod
));
484 // If the timer frequency is rediculously low, make it more sane.
485 // This happens when 'ips' is too low.
486 if (ticks
< MinAllowableTimerPeriod
) {
487 //BX_INFO(("activate_timer_ticks: adjusting ticks of %llu to min of %u",
488 // ticks, MinAllowableTimerPeriod));
489 ticks
= MinAllowableTimerPeriod
;
492 timer
[i
].period
= ticks
;
493 timer
[i
].timeToFire
= (ticksTotal
+ Bit64u(currCountdownPeriod
-currCountdown
)) +
496 timer
[i
].continuous
= continuous
;
498 if (ticks
< Bit64u(currCountdown
)) {
499 // This new timer needs to fire before the current countdown.
500 // Skew the current countdown and countdown period to be smaller
502 currCountdownPeriod
-= (currCountdown
- Bit32u(ticks
));
503 currCountdown
= Bit32u(ticks
);
507 void bx_pc_system_c::activate_timer(unsigned i
, Bit32u useconds
, bx_bool continuous
)
513 BX_PANIC(("activate_timer: timer %u OOB", i
));
515 BX_PANIC(("activate_timer: timer 0 is the nullTimer!"));
518 // if useconds = 0, use default stored in period field
519 // else set new period from useconds
521 ticks
= timer
[i
].period
;
524 // convert useconds to number of ticks
525 ticks
= (Bit64u
) (double(useconds
) * m_ips
);
527 // If the timer frequency is rediculously low, make it more sane.
528 // This happens when 'ips' is too low.
529 if (ticks
< MinAllowableTimerPeriod
) {
530 //BX_INFO(("activate_timer: adjusting ticks of %llu to min of %u",
531 // ticks, MinAllowableTimerPeriod));
532 ticks
= MinAllowableTimerPeriod
;
535 timer
[i
].period
= ticks
;
538 activate_timer_ticks(i
, ticks
, continuous
);
541 void bx_pc_system_c::deactivate_timer(unsigned i
)
545 BX_PANIC(("deactivate_timer: timer %u OOB", i
));
547 BX_PANIC(("deactivate_timer: timer 0 is the nullTimer!"));
553 bx_bool
bx_pc_system_c::unregisterTimer(unsigned timerIndex
)
556 if (timerIndex
>= numTimers
)
557 BX_PANIC(("unregisterTimer: timer %u OOB", timerIndex
));
559 BX_PANIC(("unregisterTimer: timer 0 is the nullTimer!"));
560 if (timer
[timerIndex
].inUse
== 0)
561 BX_PANIC(("unregisterTimer: timer %u is not in-use!", timerIndex
));
564 if (timer
[timerIndex
].active
) {
565 BX_PANIC(("unregisterTimer: timer '%s' is still active!", timer
[timerIndex
].id
));
569 // Reset timer fields for good measure.
570 timer
[timerIndex
].inUse
= 0; // No longer registered.
571 timer
[timerIndex
].period
= BX_MAX_BIT64S
; // Max value (invalid)
572 timer
[timerIndex
].timeToFire
= BX_MAX_BIT64S
; // Max value (invalid)
573 timer
[timerIndex
].continuous
= 0;
574 timer
[timerIndex
].funct
= NULL
;
575 timer
[timerIndex
].this_ptr
= NULL
;
576 memset(timer
[timerIndex
].id
, 0, BxMaxTimerIDLen
);
578 if (timerIndex
== (numTimers
-1)) numTimers
--;