Merged patchfix by Sebastian
[bochs-mirror.git] / pc_system.cc
blob1e497bd6dbcfea6afcac2b77f077856044a435f2
1 /////////////////////////////////////////////////////////////////////////
2 // $Id: pc_system.cc,v 1.72 2008/08/13 21:51:53 sshwarts Exp $
3 /////////////////////////////////////////////////////////////////////////
4 //
5 // Copyright (C) 2002 MandrakeSoft S.A.
6 //
7 // MandrakeSoft S.A.
8 // 43, rue d'Aboukir
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
28 #include "bochs.h"
29 #include "cpu/cpu.h"
30 #include "iodev/iodev.h"
31 #define LOG_THIS bx_pc_system.
33 #ifdef WIN32
34 #ifndef __MINGW32__
35 // #include <winsock2.h> // +++
36 #include <winsock.h>
37 #endif
38 #endif
40 #if defined(PROVIDE_M_IPS)
41 double m_ips; // Millions of Instructions Per Second
42 #endif
44 // Option for turning off BX_TIMER_DEBUG?
45 // Check out m_ips and ips
47 #define SpewPeriodicTimerInfo 0
48 #define MinAllowableTimerPeriod 1
50 #if BX_SUPPORT_ICACHE
51 const Bit64u bx_pc_system_c::NullTimerInterval = ICacheWriteStampStart;
52 #else
53 // This must be the maximum 32-bit unsigned int value, NOT (Bit64u) -1.
54 const Bit64u bx_pc_system_c::NullTimerInterval = 0xffffffff;
55 #endif
57 // constructor
58 bx_pc_system_c::bx_pc_system_c()
60 this->put("SYS");
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.
68 timer[0].inUse = 1;
69 timer[0].period = NullTimerInterval;
70 timer[0].active = 1;
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)
79 ticksTotal = 0;
80 timer[0].timeToFire = NullTimerInterval;
81 currCountdown = NullTimerInterval;
82 currCountdownPeriod = NullTimerInterval;
83 lastTimeUsec = 0;
84 usecSinceLast = 0;
85 triggeredTimer = 0;
86 HRQ = 0;
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)
97 HRQ = val;
98 if (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);
117 return ret;
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)
132 #if BX_SUPPORT_A20
133 bx_bool old_enable_a20 = enable_a20;
135 if (value) {
136 enable_a20 = 1;
137 #if BX_CPU_LEVEL < 2
138 a20_mask = 0xfffff;
139 #elif BX_CPU_LEVEL == 2
140 a20_mask = 0xffffff;
141 #elif BX_PHY_ADDRESS_LONG
142 a20_mask = BX_CONST64(0xffffffffffffffff);
143 #else /* 386+ */
144 a20_mask = 0xffffffff;
145 #endif
147 else {
148 enable_a20 = 0;
149 /* mask off A20 address line */
150 #if BX_PHY_ADDRESS_LONG
151 a20_mask = BX_CONST64(0xffffffffffefffff);
152 #else
153 a20_mask = 0xffefffff;
154 #endif
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();
165 #else
166 BX_DEBUG(("set_enable_a20: ignoring: BX_SUPPORT_A20 = 0"));
167 #endif
170 bx_bool bx_pc_system_c::get_enable_a20(void)
172 #if BX_SUPPORT_A20
173 if (bx_dbg.a20)
174 BX_INFO(("A20: get() = %u", (unsigned) enable_a20));
176 return enable_a20;
177 #else
178 BX_DEBUG(("get_enable_a20: ignoring: BX_SUPPORT_A20 = 0"));
179 return 1;
180 #endif
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"));
200 set_enable_a20(1);
202 // Always reset cpu
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);
212 return(0);
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;
224 bx_devices.exit();
225 if (bx_gui) {
226 bx_gui->cleanup();
227 bx_gui->exit();
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++) {
245 char name[4];
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)
272 unsigned i;
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)
285 break;
288 #if BX_TIMER_DEBUG
289 if (i==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!"));
295 if (funct == NULL)
296 BX_PANIC(("register_timer_ticks: funct is NULL!"));
297 #endif
299 timer[i].inUse = 1;
300 timer[i].period = ticks;
301 timer[i].timeToFire = (ticksTotal + Bit64u(currCountdownPeriod-currCountdown)) +
302 ticks;
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.
310 if (active) {
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
314 // by the delta.
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.
322 if (i==numTimers)
323 numTimers++; // One new timer installed.
325 // Return timer id.
326 return(i);
329 void bx_pc_system_c::countdownEvent(void)
331 unsigned i;
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.
337 #if BX_TIMER_DEBUG
338 if (currCountdown != 0)
339 BX_PANIC(("countdownEvent: ticks!=0"));
340 #endif
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) {
350 #if BX_TIMER_DEBUG
351 if (ticksTotal > timer[i].timeToFire)
352 BX_PANIC(("countdownEvent: ticksTotal > timeToFire[%u], D " FMT_LL "u", i,
353 timer[i].timeToFire-ticksTotal));
354 #endif
355 if (ticksTotal == timer[i].timeToFire) {
356 // This timer is ready to fire.
357 triggered[i] = 1;
359 if (timer[i].continuous==0) {
360 // If triggered timer is one-shot, deactive.
361 timer[i].active = 0;
363 else {
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;
370 else {
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.
387 if (triggered[i]) {
388 triggeredTimer = i;
389 timer[i].funct(timer[i].this_ptr);
390 triggeredTimer = 0;
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.
407 UNUSED(this_ptr);
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));
418 #endif
420 #if BX_SUPPORT_ICACHE
421 purgeICaches();
422 #endif
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;
429 bx_user_quit = 1;
432 #if BX_DEBUGGER
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);
442 timebp_queue_size--;
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) {
455 usecSinceLast = 0;
456 } else {
457 usecSinceLast -= diff_usec;
460 usecSinceLast++;
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)
473 #if BX_TIMER_DEBUG
474 if (i >= numTimers)
475 BX_PANIC(("activate_timer_ticks: timer %u OOB", i));
476 if (i == 0)
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));
481 #endif
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)) +
493 ticks;
494 timer[i].active = 1;
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
500 // by the delta.
501 currCountdownPeriod -= (currCountdown - Bit32u(ticks));
502 currCountdown = Bit32u(ticks);
506 void bx_pc_system_c::activate_timer(unsigned i, Bit32u useconds, bx_bool continuous)
508 Bit64u ticks;
510 #if BX_TIMER_DEBUG
511 if (i >= numTimers)
512 BX_PANIC(("activate_timer: timer %u OOB", i));
513 if (i == 0)
514 BX_PANIC(("activate_timer: timer 0 is the nullTimer!"));
515 #endif
517 // if useconds = 0, use default stored in period field
518 // else set new period from useconds
519 if (useconds==0) {
520 ticks = timer[i].period;
522 else {
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)
542 #if BX_TIMER_DEBUG
543 if (i >= numTimers)
544 BX_PANIC(("deactivate_timer: timer %u OOB", i));
545 if (i == 0)
546 BX_PANIC(("deactivate_timer: timer 0 is the nullTimer!"));
547 #endif
549 timer[i].active = 0;
552 bx_bool bx_pc_system_c::unregisterTimer(unsigned timerIndex)
554 #if BX_TIMER_DEBUG
555 if (timerIndex >= numTimers)
556 BX_PANIC(("unregisterTimer: timer %u OOB", timerIndex));
557 if (timerIndex == 0)
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));
561 #endif
563 if (timer[timerIndex].active) {
564 BX_PANIC(("unregisterTimer: timer '%s' is still active!", timer[timerIndex].id));
565 return(0); // Fail.
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--;
579 return(1); // OK