1 /////////////////////////////////////////////////////////////////////////
2 // $Id: pc_system.cc,v 1.72 2008/08/13 21:51:53 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
141 #elif BX_PHY_ADDRESS_LONG
142 a20_mask
= BX_CONST64(0xffffffffffffffff);
144 a20_mask
= 0xffffffff;
149 /* mask off A20 address line */
150 #if BX_PHY_ADDRESS_LONG
151 a20_mask
= BX_CONST64(0xffffffffffefffff);
153 a20_mask
= 0xffefffff;
157 BX_DBG_A20_REPORT(enable_a20
);
159 BX_DEBUG(("A20: set() = %u", (unsigned) enable_a20
));
161 // If there has been a transition, we need to notify the CPUs so
162 // they can potentially invalidate certain cache info based on
163 // A20-line-applied physical addresses.
164 if (old_enable_a20
!= enable_a20
) MemoryMappingChanged();
166 BX_DEBUG(("set_enable_a20: ignoring: BX_SUPPORT_A20 = 0"));
170 bx_bool
bx_pc_system_c::get_enable_a20(void)
174 BX_INFO(("A20: get() = %u", (unsigned) enable_a20
));
178 BX_DEBUG(("get_enable_a20: ignoring: BX_SUPPORT_A20 = 0"));
183 void bx_pc_system_c::MemoryMappingChanged(void)
185 for (unsigned i
=0; i
<BX_SMP_PROCESSORS
; i
++)
186 BX_CPU(i
)->TLB_flush();
189 void bx_pc_system_c::invlpg(bx_address addr
)
191 for (unsigned i
=0; i
<BX_SMP_PROCESSORS
; i
++)
192 BX_CPU(i
)->TLB_invlpg(addr
);
195 int bx_pc_system_c::Reset(unsigned type
)
197 // type is BX_RESET_HARDWARE or BX_RESET_SOFTWARE
198 BX_INFO(("bx_pc_system_c::Reset(%s) called",type
==BX_RESET_HARDWARE
?"HARDWARE":"SOFTWARE"));
203 for (int i
=0; i
<BX_SMP_PROCESSORS
; i
++) {
204 BX_CPU(i
)->reset(type
);
207 // Reset devices only on Hardware resets
208 if (type
==BX_RESET_HARDWARE
) {
209 DEV_reset_devices(type
);
215 Bit8u
bx_pc_system_c::IAC(void)
217 return DEV_pic_iac();
220 void bx_pc_system_c::exit(void)
222 // delete all registered timers (exception: null timer and APIC timer)
223 numTimers
= 1 + BX_SUPPORT_APIC
;
231 void bx_pc_system_c::register_state(void)
234 bx_list_c
*list
= new bx_list_c(SIM
->get_bochs_root(), "pc_system", "PC System State", 8);
235 BXRS_PARAM_BOOL(list
, enable_a20
, enable_a20
);
236 BXRS_DEC_PARAM_SIMPLE(list
, currCountdown
);
237 BXRS_DEC_PARAM_SIMPLE(list
, currCountdownPeriod
);
238 BXRS_DEC_PARAM_SIMPLE(list
, ticksTotal
);
239 BXRS_DEC_PARAM_SIMPLE(list
, lastTimeUsec
);
240 BXRS_DEC_PARAM_SIMPLE(list
, usecSinceLast
);
241 BXRS_PARAM_BOOL(list
, HRQ
, HRQ
);
243 bx_list_c
*timers
= new bx_list_c(list
, "timer", numTimers
);
244 for (unsigned i
= 0; i
< numTimers
; i
++) {
246 sprintf(name
, "%d", i
);
247 bx_list_c
*bxtimer
= new bx_list_c(timers
, name
, 5);
248 BXRS_PARAM_BOOL(bxtimer
, inUse
, timer
[i
].inUse
);
249 BXRS_DEC_PARAM_FIELD(bxtimer
, period
, timer
[i
].period
);
250 BXRS_DEC_PARAM_FIELD(bxtimer
, timeToFire
, timer
[i
].timeToFire
);
251 BXRS_PARAM_BOOL(bxtimer
, active
, timer
[i
].active
);
252 BXRS_PARAM_BOOL(bxtimer
, continuous
, timer
[i
].continuous
);
256 // ================================================
257 // Bochs internal timer delivery framework features
258 // ================================================
260 int bx_pc_system_c::register_timer(void *this_ptr
, void (*funct
)(void *),
261 Bit32u useconds
, bx_bool continuous
, bx_bool active
, const char *id
)
263 // Convert useconds to number of ticks.
264 Bit64u ticks
= (Bit64u
) (double(useconds
) * m_ips
);
266 return register_timer_ticks(this_ptr
, funct
, ticks
, continuous
, active
, id
);
269 int bx_pc_system_c::register_timer_ticks(void* this_ptr
, bx_timer_handler_t funct
,
270 Bit64u ticks
, bx_bool continuous
, bx_bool active
, const char *id
)
274 // If the timer frequency is rediculously low, make it more sane.
275 // This happens when 'ips' is too low.
276 if (ticks
< MinAllowableTimerPeriod
) {
277 //BX_INFO(("register_timer_ticks: adjusting ticks of %llu to min of %u",
278 // ticks, MinAllowableTimerPeriod));
279 ticks
= MinAllowableTimerPeriod
;
282 // search for new timer for i=1, i=0 is reserved for NullTimer
283 for (i
=1; i
< numTimers
; i
++) {
284 if (timer
[i
].inUse
== 0)
290 BX_PANIC(("register_timer: cannot register NullTimer again!"));
291 if (numTimers
>= BX_MAX_TIMERS
)
292 BX_PANIC(("register_timer: too many registered timers"));
293 if (this_ptr
== NULL
)
294 BX_PANIC(("register_timer_ticks: this_ptr is NULL!"));
296 BX_PANIC(("register_timer_ticks: funct is NULL!"));
300 timer
[i
].period
= ticks
;
301 timer
[i
].timeToFire
= (ticksTotal
+ Bit64u(currCountdownPeriod
-currCountdown
)) +
303 timer
[i
].active
= active
;
304 timer
[i
].continuous
= continuous
;
305 timer
[i
].funct
= funct
;
306 timer
[i
].this_ptr
= this_ptr
;
307 strncpy(timer
[i
].id
, id
, BxMaxTimerIDLen
);
308 timer
[i
].id
[BxMaxTimerIDLen
-1] = 0; // Null terminate if not already.
311 if (ticks
< Bit64u(currCountdown
)) {
312 // This new timer needs to fire before the current countdown.
313 // Skew the current countdown and countdown period to be smaller
315 currCountdownPeriod
-= (currCountdown
- Bit32u(ticks
));
316 currCountdown
= Bit32u(ticks
);
320 BX_DEBUG(("timer id %d registered for '%s'", i
, id
));
321 // If we didn't find a free slot, increment the bound, numTimers.
323 numTimers
++; // One new timer installed.
329 void bx_pc_system_c::countdownEvent(void)
332 Bit64u minTimeToFire
;
333 bx_bool triggered
[BX_MAX_TIMERS
];
335 // The countdown decremented to 0. We need to service all the active
336 // timers, and invoke callbacks from those timers which have fired.
338 if (currCountdown
!= 0)
339 BX_PANIC(("countdownEvent: ticks!=0"));
342 // Increment global ticks counter by number of ticks which have
343 // elapsed since the last update.
344 ticksTotal
+= Bit64u(currCountdownPeriod
);
345 minTimeToFire
= (Bit64u
) -1;
347 for (i
=0; i
< numTimers
; i
++) {
348 triggered
[i
] = 0; // Reset triggered flag.
349 if (timer
[i
].active
) {
351 if (ticksTotal
> timer
[i
].timeToFire
)
352 BX_PANIC(("countdownEvent: ticksTotal > timeToFire[%u], D " FMT_LL
"u", i
,
353 timer
[i
].timeToFire
-ticksTotal
));
355 if (ticksTotal
== timer
[i
].timeToFire
) {
356 // This timer is ready to fire.
359 if (timer
[i
].continuous
==0) {
360 // If triggered timer is one-shot, deactive.
364 // Continuous timer, increment time-to-fire by period.
365 timer
[i
].timeToFire
+= timer
[i
].period
;
366 if (timer
[i
].timeToFire
< minTimeToFire
)
367 minTimeToFire
= timer
[i
].timeToFire
;
371 // This timer is not ready to fire yet.
372 if (timer
[i
].timeToFire
< minTimeToFire
)
373 minTimeToFire
= timer
[i
].timeToFire
;
378 // Calculate next countdown period. We need to do this before calling
379 // any of the callbacks, as they may call timer features, which need
380 // to be advanced to the next countdown cycle.
381 currCountdown
= currCountdownPeriod
=
382 Bit32u(minTimeToFire
- ticksTotal
);
384 for (i
=0; i
< numTimers
; i
++) {
385 // Call requested timer function. It may request a different
386 // timer period or deactivate etc.
389 timer
[i
].funct(timer
[i
].this_ptr
);
395 void bx_pc_system_c::nullTimer(void* this_ptr
)
397 // This function is always inserted in timer[0]. It is sort of
398 // a heartbeat timer. It ensures that at least one timer is
399 // always active to make the timer logic more simple, and has
400 // a duration of less than the maximum 32-bit integer, so that
401 // a 32-bit size can be used for the hot countdown timer. The
402 // rest of the timer info can be 64-bits. This is also a good
403 // place for some logic to report actual emulated
404 // instructions-per-second (IPS) data when measured relative to
405 // the host computer's wall clock.
409 #if SpewPeriodicTimerInfo
410 BX_INFO(("==================================="));
411 for (unsigned i
=0; i
< bx_pc_system
.numTimers
; i
++) {
412 if (bx_pc_system
.timer
[i
].active
) {
413 BX_INFO(("BxTimer(%s): period=" FMT_LL
"u, continuous=%u",
414 bx_pc_system
.timer
[i
].id
, bx_pc_system
.timer
[i
].period
,
415 bx_pc_system
.timer
[i
].continuous
));
420 #if BX_SUPPORT_ICACHE
425 void bx_pc_system_c::benchmarkTimer(void* this_ptr
)
427 bx_pc_system_c
*class_ptr
= (bx_pc_system_c
*) this_ptr
;
428 class_ptr
->kill_bochs_request
= 1;
433 void bx_pc_system_c::timebp_handler(void* this_ptr
)
435 BX_CPU(0)->break_point
= BREAK_POINT_TIME
;
436 BX_DEBUG(("Time breakpoint triggered"));
438 if (timebp_queue_size
> 1) {
439 Bit64s new_diff
= timebp_queue
[1] - bx_pc_system
.time_ticks();
440 bx_pc_system
.activate_timer_ticks(timebp_timer
, new_diff
, 1);
443 for (int i
= 0; i
< timebp_queue_size
; i
++)
444 timebp_queue
[i
] = timebp_queue
[i
+1];
446 #endif // BX_DEBUGGER
448 Bit64u
bx_pc_system_c::time_usec_sequential()
450 Bit64u this_time_usec
= time_usec();
451 if(this_time_usec
!= lastTimeUsec
) {
452 Bit64u diff_usec
= this_time_usec
-lastTimeUsec
;
453 lastTimeUsec
= this_time_usec
;
454 if(diff_usec
>= usecSinceLast
) {
457 usecSinceLast
-= diff_usec
;
461 return (this_time_usec
+usecSinceLast
);
464 Bit64u
bx_pc_system_c::time_usec()
466 return (Bit64u
) (((double)(Bit64s
)time_ticks()) / m_ips
);
469 void bx_pc_system_c::start_timers(void) { }
471 void bx_pc_system_c::activate_timer_ticks(unsigned i
, Bit64u ticks
, bx_bool continuous
)
475 BX_PANIC(("activate_timer_ticks: timer %u OOB", i
));
477 BX_PANIC(("activate_timer_ticks: timer 0 is the NullTimer!"));
478 if (timer
[i
].period
< MinAllowableTimerPeriod
)
479 BX_PANIC(("activate_timer_ticks: timer[%u].period of " FMT_LL
"u < min of %u",
480 i
, timer
[i
].period
, MinAllowableTimerPeriod
));
483 // If the timer frequency is rediculously low, make it more sane.
484 // This happens when 'ips' is too low.
485 if (ticks
< MinAllowableTimerPeriod
) {
486 //BX_INFO(("activate_timer_ticks: adjusting ticks of %llu to min of %u",
487 // ticks, MinAllowableTimerPeriod));
488 ticks
= MinAllowableTimerPeriod
;
491 timer
[i
].period
= ticks
;
492 timer
[i
].timeToFire
= (ticksTotal
+ Bit64u(currCountdownPeriod
-currCountdown
)) +
495 timer
[i
].continuous
= continuous
;
497 if (ticks
< Bit64u(currCountdown
)) {
498 // This new timer needs to fire before the current countdown.
499 // Skew the current countdown and countdown period to be smaller
501 currCountdownPeriod
-= (currCountdown
- Bit32u(ticks
));
502 currCountdown
= Bit32u(ticks
);
506 void bx_pc_system_c::activate_timer(unsigned i
, Bit32u useconds
, bx_bool continuous
)
512 BX_PANIC(("activate_timer: timer %u OOB", i
));
514 BX_PANIC(("activate_timer: timer 0 is the nullTimer!"));
517 // if useconds = 0, use default stored in period field
518 // else set new period from useconds
520 ticks
= timer
[i
].period
;
523 // convert useconds to number of ticks
524 ticks
= (Bit64u
) (double(useconds
) * m_ips
);
526 // If the timer frequency is rediculously low, make it more sane.
527 // This happens when 'ips' is too low.
528 if (ticks
< MinAllowableTimerPeriod
) {
529 //BX_INFO(("activate_timer: adjusting ticks of %llu to min of %u",
530 // ticks, MinAllowableTimerPeriod));
531 ticks
= MinAllowableTimerPeriod
;
534 timer
[i
].period
= ticks
;
537 activate_timer_ticks(i
, ticks
, continuous
);
540 void bx_pc_system_c::deactivate_timer(unsigned i
)
544 BX_PANIC(("deactivate_timer: timer %u OOB", i
));
546 BX_PANIC(("deactivate_timer: timer 0 is the nullTimer!"));
552 bx_bool
bx_pc_system_c::unregisterTimer(unsigned timerIndex
)
555 if (timerIndex
>= numTimers
)
556 BX_PANIC(("unregisterTimer: timer %u OOB", timerIndex
));
558 BX_PANIC(("unregisterTimer: timer 0 is the nullTimer!"));
559 if (timer
[timerIndex
].inUse
== 0)
560 BX_PANIC(("unregisterTimer: timer %u is not in-use!", timerIndex
));
563 if (timer
[timerIndex
].active
) {
564 BX_PANIC(("unregisterTimer: timer '%s' is still active!", timer
[timerIndex
].id
));
568 // Reset timer fields for good measure.
569 timer
[timerIndex
].inUse
= 0; // No longer registered.
570 timer
[timerIndex
].period
= BX_MAX_BIT64S
; // Max value (invalid)
571 timer
[timerIndex
].timeToFire
= BX_MAX_BIT64S
; // Max value (invalid)
572 timer
[timerIndex
].continuous
= 0;
573 timer
[timerIndex
].funct
= NULL
;
574 timer
[timerIndex
].this_ptr
= NULL
;
575 memset(timer
[timerIndex
].id
, 0, BxMaxTimerIDLen
);
577 if (timerIndex
== (numTimers
-1)) numTimers
--;