1 /////////////////////////////////////////////////////////////////////////
2 // $Id: cmos.cc 14175 2021-03-07 16:01:39Z vruppert $
3 /////////////////////////////////////////////////////////////////////////
4 //
5 //  Copyright (C) 2002-2021  The Bochs Project
6 //
7 //  This library is free software; you can redistribute it and/or
8 //  modify it under the terms of the GNU Lesser General Public
9 //  License as published by the Free Software Foundation; either
10 //  version 2 of the License, or (at your option) any later version.
11 //
12 //  This library is distributed in the hope that it will be useful,
13 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
14 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 //  Lesser General Public License for more details.
16 //
17 //  You should have received a copy of the GNU Lesser General Public
18 //  License along with this library; if not, write to the Free Software
19 //  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
20 /////////////////////////////////////////////////////////////////////////
21 
22 // Define BX_PLUGGABLE in files that can be compiled into plugins.  For
23 // platforms that require a special tag on exported symbols, BX_PLUGGABLE
24 // is used to know when we are exporting symbols and when we are importing.
25 #define BX_PLUGGABLE
26 
27 #include "iodev.h"
28 #include "cmos.h"
29 #include "virt_timer.h"
30 
31 #define LOG_THIS theCmosDevice->
32 
33 bx_cmos_c *theCmosDevice = NULL;
34 
35 // CMOS register definitions from Ralf Brown's interrupt list v6.1, in a file
36 // called cmos.lst.  In cases where there are multiple uses for a given
37 // register in the interrupt list, I only listed the purpose that Bochs
38 // actually uses it for, but I wrote "alternatives" next to it.
39 #define  REG_SEC                     0x00
40 #define  REG_SEC_ALARM               0x01
41 #define  REG_MIN                     0x02
42 #define  REG_MIN_ALARM               0x03
43 #define  REG_HOUR                    0x04
44 #define  REG_HOUR_ALARM              0x05
45 #define  REG_WEEK_DAY                0x06
46 #define  REG_MONTH_DAY               0x07
47 #define  REG_MONTH                   0x08
48 #define  REG_YEAR                    0x09
49 #define  REG_STAT_A                  0x0a
50 #define  REG_STAT_B                  0x0b
51 #define  REG_STAT_C                  0x0c
52 #define  REG_STAT_D                  0x0d
53 #define  REG_DIAGNOSTIC_STATUS       0x0e  /* alternatives */
54 #define  REG_SHUTDOWN_STATUS         0x0f
55 #define  REG_EQUIPMENT_BYTE          0x14
56 #define  REG_CSUM_HIGH               0x2e
57 #define  REG_CSUM_LOW                0x2f
58 #define  REG_IBM_CENTURY_BYTE        0x32  /* alternatives */
59 #define  REG_IBM_PS2_CENTURY_BYTE    0x37  /* alternatives */
60 
61 // Bochs CMOS map
62 //
63 // Idx  Len   Description
64 // 0x10   1   floppy drive types
65 // 0x11   1   configuration bits
66 // 0x12   1   harddisk types
67 // 0x13   1   advanced configuration bits
68 // 0x15   2   base memory in 1k
69 // 0x17   2   memory size above 1M in 1k
70 // 0x19   2   extended harddisk types
71 // 0x1b   9   harddisk configuration (hd0)
72 // 0x24   9   harddisk configuration (hd1)
73 // 0x2d   1   boot sequence (fd/hd)
74 // 0x30   2   memory size above 1M in 1k
75 // 0x34   2   memory size above 16M in 64k
76 // 0x38   1   eltorito boot sequence (#3) + bootsig check
77 // 0x39   2   ata translation policy (ata0...ata3)
78 // 0x3d   1   eltorito boot sequence (#1 + #2)
79 //
80 // Qemu CMOS map
81 //
82 // Idx  Len   Description
83 // 0x5b   3   extra memory above 4GB
84 // 0x5f   1   number of processors
85 
86 
bcd_to_bin(Bit8u value,bool is_binary)87 Bit8u bcd_to_bin(Bit8u value, bool is_binary)
88 {
89   if (is_binary)
90     return value;
91   else
92     return ((value >> 4) * 10) + (value & 0x0f);
93 }
94 
bin_to_bcd(Bit8u value,bool is_binary)95 Bit8u bin_to_bcd(Bit8u value, bool is_binary)
96 {
97   if (is_binary)
98     return value;
99   else
100     return ((value  / 10) << 4) | (value % 10);
101 }
102 
PLUGIN_ENTRY_FOR_MODULE(cmos)103 PLUGIN_ENTRY_FOR_MODULE(cmos)
104 {
105   if (mode == PLUGIN_INIT) {
106     theCmosDevice = new bx_cmos_c();
107     bx_devices.pluginCmosDevice = theCmosDevice;
108     BX_REGISTER_DEVICE_DEVMODEL(plugin, type, theCmosDevice, BX_PLUGIN_CMOS);
109   } else if (mode == PLUGIN_FINI) {
110     delete theCmosDevice;
111   } else if (mode == PLUGIN_PROBE) {
112     return (int)PLUGTYPE_CORE;
113   }
114   return 0; // Success
115 }
116 
bx_cmos_c(void)117 bx_cmos_c::bx_cmos_c(void)
118 {
119   put("CMOS");
120   memset(&s, 0, sizeof(s));
121   s.periodic_timer_index = BX_NULL_TIMER_HANDLE;
122   s.one_second_timer_index = BX_NULL_TIMER_HANDLE;
123   s.uip_timer_index = BX_NULL_TIMER_HANDLE;
124 }
125 
~bx_cmos_c(void)126 bx_cmos_c::~bx_cmos_c(void)
127 {
128   save_image();
129   char *tmptime;
130   if ((tmptime = strdup(ctime(&(BX_CMOS_THIS s.timeval)))) != NULL) {
131     tmptime[strlen(tmptime)-1]='\0';
132     BX_INFO(("Last time is %u (%s)", (unsigned) get_timeval(), tmptime));
133     free(tmptime);
134   }
135   SIM->get_bochs_root()->remove("cmos");
136   BX_DEBUG(("Exit"));
137 }
138 
init(void)139 void bx_cmos_c::init(void)
140 {
141   BX_DEBUG(("Init $Id: cmos.cc 14175 2021-03-07 16:01:39Z vruppert $"));
142   // CMOS RAM & RTC
143 
144   DEV_register_ioread_handler(this, read_handler, 0x0070, "CMOS RAM", 1);
145   DEV_register_ioread_handler(this, read_handler, 0x0071, "CMOS RAM", 1);
146   DEV_register_iowrite_handler(this, write_handler, 0x0070, "CMOS RAM", 1);
147   DEV_register_iowrite_handler(this, write_handler, 0x0071, "CMOS RAM", 1);
148   DEV_register_irq(8, "CMOS RTC");
149 
150   int clock_sync = SIM->get_param_enum(BXPN_CLOCK_SYNC)->get();
151   BX_CMOS_THIS s.rtc_sync = ((clock_sync == BX_CLOCK_SYNC_REALTIME) ||
152                              (clock_sync == BX_CLOCK_SYNC_BOTH)) &&
153                             SIM->get_param_bool(BXPN_CLOCK_RTC_SYNC)->get();
154 
155   if (BX_CMOS_THIS s.periodic_timer_index == BX_NULL_TIMER_HANDLE) {
156     BX_CMOS_THIS s.periodic_timer_index =
157       DEV_register_timer(this, periodic_timer_handler,
158         1000000, 1,0, "cmos"); // continuous, not-active
159   }
160   if (BX_CMOS_THIS s.one_second_timer_index == BX_NULL_TIMER_HANDLE) {
161     BX_CMOS_THIS s.one_second_timer_index =
162       bx_virt_timer.register_timer(this, one_second_timer_handler,
163       1000000, 1, 0, BX_CMOS_THIS s.rtc_sync, "cmos"); // continuous, not-active
164       if (BX_CMOS_THIS s.rtc_sync) {
165         BX_INFO(("CMOS RTC using realtime synchronisation method"));
166       }
167   }
168   if (BX_CMOS_THIS s.uip_timer_index == BX_NULL_TIMER_HANDLE) {
169     BX_CMOS_THIS s.uip_timer_index =
170       DEV_register_timer(this, uip_timer_handler,
171         244, 0, 0, "cmos"); // one-shot, not-active
172   }
173 
174   if (SIM->get_param_num(BXPN_CLOCK_TIME0)->get() == BX_CLOCK_TIME0_LOCAL) {
175     BX_INFO(("Using local time for initial clock"));
176     BX_CMOS_THIS s.timeval = time(NULL);
177   } else if (SIM->get_param_num(BXPN_CLOCK_TIME0)->get() == BX_CLOCK_TIME0_UTC) {
178     bool utc_ok = 0;
179 
180     BX_INFO(("Using utc time for initial clock"));
181 
182     BX_CMOS_THIS s.timeval = time(NULL);
183 
184 #if BX_HAVE_GMTIME
185 #if BX_HAVE_MKTIME
186     struct tm *utc_holder = gmtime(&BX_CMOS_THIS s.timeval);
187     utc_holder->tm_isdst = -1;
188     utc_ok = 1;
189     BX_CMOS_THIS s.timeval = mktime(utc_holder);
190 #elif BX_HAVE_TIMELOCAL
191     struct tm *utc_holder = gmtime(&BX_CMOS_THIS s.timeval);
192     utc_holder->tm_isdst = 0;	// XXX Is this correct???
193     utc_ok = 1;
194     BX_CMOS_THIS s.timeval = timelocal(utc_holder);
195 #endif //BX_HAVE_MKTIME
196 #endif //BX_HAVE_GMTIME
197 
198     if (!utc_ok) {
199       BX_ERROR(("UTC time is not supported on your platform. Using current time(NULL)"));
200     }
201   } else {
202     BX_INFO(("Using specified time for initial clock"));
203     BX_CMOS_THIS s.timeval = SIM->get_param_num(BXPN_CLOCK_TIME0)->get();
204   }
205 
206   // load CMOS from image file if requested.
207   BX_CMOS_THIS s.use_image = SIM->get_param_bool(BXPN_CMOSIMAGE_ENABLED)->get();
208   if (BX_CMOS_THIS s.use_image) {
209     int fd, ret;
210     struct stat stat_buf;
211 
212     fd = open(SIM->get_param_string(BXPN_CMOSIMAGE_PATH)->getptr(), O_RDONLY
213 #ifdef O_BINARY
214        | O_BINARY
215 #endif
216         );
217     if (fd < 0) {
218       BX_PANIC(("trying to open cmos image file '%s'",
219         SIM->get_param_string(BXPN_CMOSIMAGE_PATH)->getptr()));
220     }
221     ret = fstat(fd, &stat_buf);
222     if (ret) {
223       BX_PANIC(("CMOS: could not fstat() image file."));
224     }
225     if ((stat_buf.st_size != 64) && (stat_buf.st_size != 128) &&
226         (stat_buf.st_size != 256)) {
227       BX_PANIC(("CMOS: image file size must be 64, 128 or 256"));
228     } else {
229       BX_CMOS_THIS s.max_reg = (Bit8u)(stat_buf.st_size - 1);
230       if (BX_CMOS_THIS s.max_reg == 255) {
231         DEV_register_ioread_handler(this, read_handler, 0x0072, "Ext CMOS RAM", 1);
232         DEV_register_ioread_handler(this, read_handler, 0x0073, "Ext CMOS RAM", 1);
233         DEV_register_iowrite_handler(this, write_handler, 0x0072, "Ext CMOS RAM", 1);
234         DEV_register_iowrite_handler(this, write_handler, 0x0073, "Ext CMOS RAM", 1);
235       }
236     }
237 
238     ret = ::read(fd, (bx_ptr_t) BX_CMOS_THIS s.reg, (unsigned)stat_buf.st_size);
239     if (ret != stat_buf.st_size) {
240       BX_PANIC(("CMOS: error reading cmos file."));
241     }
242     close(fd);
243     BX_INFO(("successfully read from image file '%s'.",
244       SIM->get_param_string(BXPN_CMOSIMAGE_PATH)->getptr()));
245     BX_CMOS_THIS s.rtc_mode_12hour = ((BX_CMOS_THIS s.reg[REG_STAT_B] & 0x02) == 0);
246     BX_CMOS_THIS s.rtc_mode_binary = ((BX_CMOS_THIS s.reg[REG_STAT_B] & 0x04) != 0);
247     if (SIM->get_param_bool(BXPN_CMOSIMAGE_RTC_INIT)->get()) {
248       update_timeval();
249     } else {
250       update_clock();
251     }
252   } else {
253     BX_CMOS_THIS s.max_reg = 128;
254     // CMOS values generated
255     BX_CMOS_THIS s.reg[REG_STAT_A] = 0x26;
256     BX_CMOS_THIS s.reg[REG_STAT_B] = 0x02;
257     BX_CMOS_THIS s.reg[REG_STAT_C] = 0x00;
258     BX_CMOS_THIS s.reg[REG_STAT_D] = 0x80;
259 #if BX_SUPPORT_FPU == 1
260     BX_CMOS_THIS s.reg[REG_EQUIPMENT_BYTE] |= 0x02;
261 #endif
262     BX_CMOS_THIS s.rtc_mode_12hour = 0;
263     BX_CMOS_THIS s.rtc_mode_binary = 0;
264     update_clock();
265   }
266 
267   char *tmptime;
268   while((tmptime = strdup(ctime(&(BX_CMOS_THIS s.timeval)))) == NULL) {
269     BX_PANIC(("Out of memory."));
270   }
271   tmptime[strlen(tmptime)-1]='\0';
272   BX_INFO(("Setting initial clock to: %s (time0=%u)", tmptime, (Bit32u)BX_CMOS_THIS s.timeval));
273   free(tmptime);
274 
275   BX_CMOS_THIS s.timeval_change = 0;
276 
277 #if BX_DEBUGGER
278   // register device for the 'info device' command (calls debug_dump())
279   bx_dbg_register_debug_info("cmos", this);
280 #endif
281 }
282 
reset(unsigned type)283 void bx_cmos_c::reset(unsigned type)
284 {
285   BX_CMOS_THIS s.cmos_mem_address = 0;
286   BX_CMOS_THIS s.irq_enabled = 1;
287 
288   // RESET affects the following registers:
289   //  CRA: no effects
290   //  CRB: bits 4,5,6 forced to 0
291   //  CRC: bits 4,5,6,7 forced to 0
292   //  CRD: no effects
293   BX_CMOS_THIS s.reg[REG_STAT_B] &= 0x8f;
294   BX_CMOS_THIS s.reg[REG_STAT_C] = 0;
295 
296   // One second timer for updating clock & alarm functions
297   bx_virt_timer.activate_timer(BX_CMOS_THIS s.one_second_timer_index,
298                                1000000, 1);
299 
300   // handle periodic interrupt rate select
301   BX_CMOS_THIS CRA_change();
302 }
303 
save_image(void)304 void bx_cmos_c::save_image(void)
305 {
306   int fd, ret;
307 
308   // save CMOS to image file if requested.
309   if (BX_CMOS_THIS s.use_image) {
310     fd = open(SIM->get_param_string(BXPN_CMOSIMAGE_PATH)->getptr(), O_WRONLY
311 #ifdef O_BINARY
312        | O_BINARY
313 #endif
314         );
315     ret = ::write(fd, (bx_ptr_t) BX_CMOS_THIS s.reg, BX_CMOS_THIS s.max_reg + 1);
316     if (ret != (BX_CMOS_THIS s.max_reg + 1)) {
317       BX_PANIC(("CMOS: error writing cmos file."));
318     }
319     close(fd);
320   }
321 }
322 
register_state(void)323 void bx_cmos_c::register_state(void)
324 {
325   bx_list_c *list = new bx_list_c(SIM->get_bochs_root(), "cmos", "CMOS State");
326   BXRS_HEX_PARAM_FIELD(list, mem_address, BX_CMOS_THIS s.cmos_mem_address);
327   BXRS_PARAM_BOOL(list, irq_enabled, BX_CMOS_THIS s.irq_enabled);
328   new bx_shadow_data_c(list, "ram", BX_CMOS_THIS s.reg, 128, 1);
329 }
330 
after_restore_state(void)331 void bx_cmos_c::after_restore_state(void)
332 {
333   BX_CMOS_THIS s.rtc_mode_12hour = ((BX_CMOS_THIS s.reg[REG_STAT_B] & 0x02) == 0);
334   BX_CMOS_THIS s.rtc_mode_binary = ((BX_CMOS_THIS s.reg[REG_STAT_B] & 0x04) != 0);
335   BX_CMOS_THIS update_timeval();
336   BX_CMOS_THIS CRA_change();
337 }
338 
CRA_change(void)339 void bx_cmos_c::CRA_change(void)
340 {
341   Bit8u nibble, dcc;
342 
343   // Periodic Interrupt timer
344   nibble = BX_CMOS_THIS s.reg[REG_STAT_A] & 0x0f;
345   dcc = (BX_CMOS_THIS s.reg[REG_STAT_A] >> 4) & 0x07;
346   if ((nibble == 0) || ((dcc & 0x06) == 0)) {
347     // No Periodic Interrupt Rate when 0, deactivate timer
348     bx_pc_system.deactivate_timer(BX_CMOS_THIS s.periodic_timer_index);
349     BX_CMOS_THIS s.periodic_interval_usec = (Bit32u) -1; // max value
350   } else {
351     // values 0001b and 0010b are the same as 1000b and 1001b
352     if (nibble <= 2)
353       nibble += 7;
354     BX_CMOS_THIS s.periodic_interval_usec = (unsigned) (1000000.0L /
355       (32768.0L / (1 << (nibble - 1))));
356 
357     // if Periodic Interrupt Enable bit set, activate timer
358     if (BX_CMOS_THIS s.reg[REG_STAT_B] & 0x40)
359       bx_pc_system.activate_timer(BX_CMOS_THIS s.periodic_timer_index,
360         BX_CMOS_THIS s.periodic_interval_usec, 1);
361     else
362       bx_pc_system.deactivate_timer(BX_CMOS_THIS s.periodic_timer_index);
363   }
364 }
365 
366 
367   // static IO port read callback handler
368   // redirects to non-static class handler to avoid virtual functions
369 
read_handler(void * this_ptr,Bit32u address,unsigned io_len)370 Bit32u bx_cmos_c::read_handler(void *this_ptr, Bit32u address, unsigned io_len)
371 {
372 #if !BX_USE_CMOS_SMF
373   bx_cmos_c *class_ptr = (bx_cmos_c *) this_ptr;
374   return class_ptr->read(address, io_len);
375 }
376 
read(Bit32u address,unsigned io_len)377 Bit32u bx_cmos_c::read(Bit32u address, unsigned io_len)
378 {
379 #else
380   UNUSED(this_ptr);
381 #endif
382   Bit8u ret8;
383 
384   BX_DEBUG(("CMOS read of CMOS register 0x%02x", (unsigned) BX_CMOS_THIS s.cmos_mem_address));
385 
386   switch (address) {
387     case 0x0070:
388     case 0x0072:
389       // this register is write-only on most machines
390       BX_DEBUG(("read of index port 0x%02x returning 0xff", address));
391       return 0xff;
392 
393     case 0x0071:
394       ret8 = BX_CMOS_THIS s.reg[BX_CMOS_THIS s.cmos_mem_address];
395       // all bits of Register C are cleared after a read occurs.
396       if (BX_CMOS_THIS s.cmos_mem_address == REG_STAT_C) {
397         BX_CMOS_THIS s.reg[REG_STAT_C] = 0x00;
398         if (BX_CMOS_THIS s.irq_enabled) {
399           DEV_pic_lower_irq(8);
400         }
401       }
402       return ret8;
403 
404     case 0x0073:
405       return BX_CMOS_THIS s.reg[BX_CMOS_THIS s.cmos_ext_mem_addr];
406 
407     default:
408       BX_PANIC(("unsupported cmos read, address=0x%04x!", (unsigned) address));
409       return 0;
410   }
411 }
412 
413 
414 // static IO port write callback handler
415 // redirects to non-static class handler to avoid virtual functions
write_handler(void * this_ptr,Bit32u address,Bit32u value,unsigned io_len)416 void bx_cmos_c::write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len)
417 {
418 #if !BX_USE_CMOS_SMF
419   bx_cmos_c *class_ptr = (bx_cmos_c *) this_ptr;
420   class_ptr->write(address, value, io_len);
421 }
422 
write(Bit32u address,Bit32u value,unsigned io_len)423 void bx_cmos_c::write(Bit32u address, Bit32u value, unsigned io_len)
424 {
425 #else
426   UNUSED(this_ptr);
427 #endif  // !BX_USE_CMOS_SMF
428 
429   BX_DEBUG(("CMOS write to address: 0x%04x = 0x%02x", address, value));
430 
431   switch (address) {
432     case 0x0070:
433       BX_CMOS_THIS s.cmos_mem_address = value & 0x7F;
434       break;
435 
436     case 0x0072:
437       BX_CMOS_THIS s.cmos_ext_mem_addr = value | 0x80;
438       break;
439 
440     case 0x0071:
441       switch (BX_CMOS_THIS s.cmos_mem_address) {
442         case REG_SEC_ALARM:             // seconds alarm
443         case REG_MIN_ALARM:             // minutes alarm
444         case REG_HOUR_ALARM:            // hours alarm
445           BX_CMOS_THIS s.reg[BX_CMOS_THIS s.cmos_mem_address] = value;
446           BX_DEBUG(("alarm time changed to %02x:%02x:%02x", BX_CMOS_THIS s.reg[REG_HOUR_ALARM],
447                     BX_CMOS_THIS s.reg[REG_MIN_ALARM], BX_CMOS_THIS s.reg[REG_SEC_ALARM]));
448           break;
449 
450         case REG_SEC:                   // seconds
451         case REG_MIN:                   // minutes
452         case REG_HOUR:                  // hours
453         case REG_WEEK_DAY:              // day of the week
454         case REG_MONTH_DAY:             // day of the month
455         case REG_MONTH:                 // month
456         case REG_YEAR:                  // year
457         case REG_IBM_CENTURY_BYTE:      // century
458         case REG_IBM_PS2_CENTURY_BYTE:  // century (PS/2)
459           BX_CMOS_THIS s.reg[BX_CMOS_THIS s.cmos_mem_address] = value;
460           if (BX_CMOS_THIS s.cmos_mem_address == REG_IBM_PS2_CENTURY_BYTE) {
461             BX_CMOS_THIS s.reg[REG_IBM_CENTURY_BYTE] = value;
462           }
463           if (BX_CMOS_THIS s.reg[REG_STAT_B] & 0x80) {
464             BX_CMOS_THIS s.timeval_change = 1;
465           } else {
466             update_timeval();
467           }
468           break;
469 
470         case REG_STAT_A: // Control Register A
471           // bit 7: Update in Progress (read-only)
472           //   1 = signifies time registers will be updated within 244us
473           //   0 = time registers will not occur before 244us
474           //   note: this bit reads 0 when CRB bit 7 is 1
475           // bit 6..4: Divider Chain Control
476           //   000 oscillator disabled
477           //   001 oscillator disabled
478           //   010 Normal operation
479           //   011 TEST
480           //   100 TEST
481           //   101 TEST
482           //   110 Divider Chain RESET
483           //   111 Divider Chain RESET
484           // bit 3..0: Periodic Interrupt Rate Select
485           //   0000 None
486           //   0001 3.90625  ms
487           //   0010 7.8125   ms
488           //   0011 122.070  us
489           //   0100 244.141  us
490           //   0101 488.281  us
491           //   0110 976.562  us
492           //   0111 1.953125 ms
493           //   1000 3.90625  ms
494           //   1001 7.8125   ms
495           //   1010 15.625   ms
496           //   1011 31.25    ms
497           //   1100 62.5     ms
498           //   1101 125      ms
499           //   1110 250      ms
500           //   1111 500      ms
501 
502           unsigned dcc;
503           dcc = (value >> 4) & 0x07;
504           if ((dcc & 0x06) == 0x06) {
505             BX_INFO(("CRA: divider chain RESET"));
506           } else if (dcc > 0x02) {
507             BX_PANIC(("CRA: divider chain control 0x%02x", dcc));
508           }
509           BX_CMOS_THIS s.reg[REG_STAT_A] &= 0x80;
510           BX_CMOS_THIS s.reg[REG_STAT_A] |= (value & 0x7f);
511           BX_CMOS_THIS CRA_change();
512           break;
513 
514         case REG_STAT_B: // Control Register B
515           // bit 0: Daylight Savings Enable
516           //   1 = enable daylight savings
517           //   0 = disable daylight savings
518           // bit 1: 24/12 hour mode
519           //   1 = 24 hour format
520           //   0 = 12 hour format
521           // bit 2: Data Mode
522           //   1 = binary format
523           //   0 = BCD format
524           // bit 3: "square wave enable"
525           //   Not supported and always read as 0
526           // bit 4: Update Ended Interrupt Enable
527           //   1 = enable generation of update ended interrupt
528           //   0 = disable
529           // bit 5: Alarm Interrupt Enable
530           //   1 = enable generation of alarm interrupt
531           //   0 = disable
532           // bit 6: Periodic Interrupt Enable
533           //   1 = enable generation of periodic interrupt
534           //   0 = disable
535           // bit 7: Set mode
536           //   1 = user copy of time is "frozen" allowing time registers
537           //       to be accessed without regard for an occurance of an update
538           //   0 = time updates occur normally
539 
540           if (value & 0x01)
541             BX_ERROR(("write status reg B, daylight savings unsupported"));
542 
543           value &= 0xf7; // bit3 always 0
544           // Note: setting bit 7 clears bit 4
545           if (value & 0x80)
546             value &= 0xef;
547 
548           unsigned prev_CRB;
549           prev_CRB = BX_CMOS_THIS s.reg[REG_STAT_B];
550           BX_CMOS_THIS s.reg[REG_STAT_B] = value;
551           if ((prev_CRB & 0x02) != (value & 0x02)) {
552             BX_CMOS_THIS s.rtc_mode_12hour = ((value & 0x02) == 0);
553             update_clock();
554           }
555           if ((prev_CRB & 0x04) != (value & 0x04)) {
556             BX_CMOS_THIS s.rtc_mode_binary = ((value & 0x04) != 0);
557             update_clock();
558           }
559           if ((prev_CRB & 0x40) != (value & 0x40)) {
560             // Periodic Interrupt Enabled changed
561             if (prev_CRB & 0x40) {
562               // transition from 1 to 0, deactivate timer
563               bx_pc_system.deactivate_timer(BX_CMOS_THIS s.periodic_timer_index);
564             } else {
565               // transition from 0 to 1
566               // if rate select is not 0, activate timer
567               if ((BX_CMOS_THIS s.reg[REG_STAT_A] & 0x0f) != 0) {
568                 bx_pc_system.activate_timer(
569                   BX_CMOS_THIS s.periodic_timer_index,
570                   BX_CMOS_THIS s.periodic_interval_usec, 1);
571               }
572             }
573           }
574           if ((prev_CRB >= 0x80) && (value < 0x80) && BX_CMOS_THIS s.timeval_change) {
575             update_timeval();
576             BX_CMOS_THIS s.timeval_change = 0;
577           }
578           break;
579 
580         case REG_STAT_C: // Control Register C
581         case REG_STAT_D: // Control Register D
582           BX_ERROR(("write to control register 0x%02x ignored (read-only)",
583             BX_CMOS_THIS s.cmos_mem_address));
584           break;
585 
586         case REG_DIAGNOSTIC_STATUS:
587           BX_DEBUG(("write register 0x0e: 0x%02x", value));
588           BX_CMOS_THIS s.reg[REG_DIAGNOSTIC_STATUS] = value;
589           break;
590 
591         case REG_SHUTDOWN_STATUS:
592           switch (value) {
593             case 0x00: /* proceed with normal POST (soft reset) */
594               BX_DEBUG(("Reg 0Fh(00): shutdown action = normal POST"));
595               break;
596             case 0x01: /* shutdown after memory size check */
597               BX_DEBUG(("Reg 0Fh(01): request to change shutdown action"
598                         " to shutdown after memory size check"));
599               break;
600             case 0x02: /* shutdown after successful memory test */
601               BX_DEBUG(("Reg 0Fh(02): request to change shutdown action"
602                         " to shutdown after successful memory test"));
603               break;
604             case 0x03: /* shutdown after failed memory test */
605               BX_DEBUG(("Reg 0Fh(03): request to change shutdown action"
606                         " to shutdown after successful memory test"));
607               break;
608             case 0x04: /* jump to disk bootstrap routine */
609               BX_DEBUG(("Reg 0Fh(04): request to change shutdown action "
610                         "to jump to disk bootstrap routine."));
611               break;
612             case 0x05: /* flush keyboard (issue EOI) and jump via 40h:0067h */
613               BX_DEBUG(("Reg 0Fh(05): request to change shutdown action "
614                         "to flush keyboard (issue EOI) and jump via 40h:0067h."));
615               break;
616             case 0x06:
617               BX_DEBUG(("Reg 0Fh(06): Shutdown after memory test !"));
618               break;
619             case 0x07: /* reset (after failed test in virtual mode) */
620               BX_DEBUG(("Reg 0Fh(07): request to change shutdown action "
621                         "to reset (after failed test in virtual mode)."));
622               break;
623             case 0x08: /* used by POST during protected-mode RAM test (return to POST) */
624               BX_DEBUG(("Reg 0Fh(08): request to change shutdown action "
625                         "to return to POST (used by POST during protected-mode RAM test)."));
626               break;
627             case 0x09: /* return to BIOS extended memory block move
628                   (interrupt 15h, func 87h was in progress) */
629               BX_DEBUG(("Reg 0Fh(09): request to change shutdown action "
630                         "to return to BIOS extended memory block move."));
631               break;
632             case 0x0a: /* jump to DWORD pointer at 40:67 */
633               BX_DEBUG(("Reg 0Fh(0a): request to change shutdown action"
634                         " to jump to DWORD at 40:67"));
635               break;
636             case 0x0b: /* iret to DWORD pointer at 40:67 */
637               BX_DEBUG(("Reg 0Fh(0b): request to change shutdown action"
638                         " to iret to DWORD at 40:67"));
639               break;
640             case 0x0c: /* retf to DWORD pointer at 40:67 */
641               BX_DEBUG(("Reg 0Fh(0c): request to change shutdown action"
642                         " to retf to DWORD at 40:67"));
643               break;
644             default:
645               if (!BX_CMOS_THIS s.use_image) {
646                 BX_ERROR(("unsupported shutdown status: 0x%02x!", value));
647               } else {
648                 BX_DEBUG(("shutdown status register set to 0x%02x", value));
649               }
650           }
651           BX_CMOS_THIS s.reg[REG_SHUTDOWN_STATUS] = value;
652           break;
653 
654         default:
655           BX_DEBUG(("write reg 0x%02x: value = 0x%02x",
656             BX_CMOS_THIS s.cmos_mem_address, value));
657           BX_CMOS_THIS s.reg[BX_CMOS_THIS s.cmos_mem_address] = value;
658       }
659       break;
660 
661     case 0x0073:
662       BX_CMOS_THIS s.reg[BX_CMOS_THIS s.cmos_ext_mem_addr] = value;
663       break;
664   }
665 }
666 
checksum_cmos(void)667 void bx_cmos_c::checksum_cmos(void)
668 {
669   Bit16u sum = 0;
670   for (unsigned i=0x10; i<=0x2d; i++)
671     sum += BX_CMOS_THIS s.reg[i];
672   BX_CMOS_THIS s.reg[REG_CSUM_HIGH] = (sum >> 8) & 0xff; /* checksum high */
673   BX_CMOS_THIS s.reg[REG_CSUM_LOW] = (sum & 0xff);      /* checksum low */
674 }
675 
periodic_timer_handler(void * this_ptr)676 void bx_cmos_c::periodic_timer_handler(void *this_ptr)
677 {
678   bx_cmos_c *class_ptr = (bx_cmos_c *) this_ptr;
679   class_ptr->periodic_timer();
680 }
681 
periodic_timer()682 void bx_cmos_c::periodic_timer()
683 {
684   // if periodic interrupts are enabled, trip IRQ 8, and
685   // update status register C
686   if (BX_CMOS_THIS s.reg[REG_STAT_B] & 0x40) {
687     BX_CMOS_THIS s.reg[REG_STAT_C] |= 0xc0; // Interrupt Request, Periodic Int
688     if (BX_CMOS_THIS s.irq_enabled) {
689       DEV_pic_raise_irq(8);
690     }
691   }
692 }
693 
one_second_timer_handler(void * this_ptr)694 void bx_cmos_c::one_second_timer_handler(void *this_ptr)
695 {
696   bx_cmos_c *class_ptr = (bx_cmos_c *) this_ptr;
697   class_ptr->one_second_timer();
698 }
699 
one_second_timer()700 void bx_cmos_c::one_second_timer()
701 {
702   // divider chain reset - RTC stopped
703   if ((BX_CMOS_THIS s.reg[REG_STAT_A] & 0x60) == 0x60)
704     return;
705 
706   // update internal time/date buffer
707   BX_CMOS_THIS s.timeval++;
708 
709   // Dont update CMOS user copy of time/date if CRB bit7 is 1
710   // Nothing else do to
711   if (BX_CMOS_THIS s.reg[REG_STAT_B] & 0x80)
712     return;
713 
714   BX_CMOS_THIS s.reg[REG_STAT_A] |= 0x80; // set UIP bit
715 
716   // UIP timer for updating clock & alarm functions
717   bx_pc_system.activate_timer(BX_CMOS_THIS s.uip_timer_index, 244, 0);
718 }
719 
uip_timer_handler(void * this_ptr)720 void bx_cmos_c::uip_timer_handler(void *this_ptr)
721 {
722   bx_cmos_c *class_ptr = (bx_cmos_c *) this_ptr;
723   class_ptr->uip_timer();
724 }
725 
uip_timer()726 void bx_cmos_c::uip_timer()
727 {
728   update_clock();
729 
730   // if update interrupts are enabled, trip IRQ 8, and
731   // update status register C
732   if (BX_CMOS_THIS s.reg[REG_STAT_B] & 0x10) {
733     BX_CMOS_THIS s.reg[REG_STAT_C] |= 0x90; // Interrupt Request, Update Ended
734     if (BX_CMOS_THIS s.irq_enabled) {
735       DEV_pic_raise_irq(8);
736     }
737   }
738 
739   // compare CMOS user copy of time/date to alarm time/date here
740   if (BX_CMOS_THIS s.reg[REG_STAT_B] & 0x20) {
741     // Alarm interrupts enabled
742     bool alarm_match = 1;
743     if ((BX_CMOS_THIS s.reg[REG_SEC_ALARM] & 0xc0) != 0xc0) {
744       // seconds alarm not in dont care mode
745       if (BX_CMOS_THIS s.reg[REG_SEC] != BX_CMOS_THIS s.reg[REG_SEC_ALARM])
746         alarm_match = 0;
747     }
748     if ((BX_CMOS_THIS s.reg[REG_MIN_ALARM] & 0xc0) != 0xc0) {
749       // minutes alarm not in dont care mode
750       if (BX_CMOS_THIS s.reg[REG_MIN] != BX_CMOS_THIS s.reg[REG_MIN_ALARM])
751         alarm_match = 0;
752     }
753     if ((BX_CMOS_THIS s.reg[REG_HOUR_ALARM] & 0xc0) != 0xc0) {
754       // hours alarm not in dont care mode
755       if (BX_CMOS_THIS s.reg[REG_HOUR] != BX_CMOS_THIS s.reg[REG_HOUR_ALARM])
756         alarm_match = 0;
757     }
758     if (alarm_match) {
759       BX_CMOS_THIS s.reg[REG_STAT_C] |= 0xa0; // Interrupt Request, Alarm Int
760       if (BX_CMOS_THIS s.irq_enabled) {
761         DEV_pic_raise_irq(8);
762       }
763     }
764   }
765   BX_CMOS_THIS s.reg[REG_STAT_A] &= 0x7f; // clear UIP bit
766 }
767 
update_clock()768 void bx_cmos_c::update_clock()
769 {
770   struct tm *time_calendar;
771   unsigned year, month, day, century;
772   Bit8u val_bcd, hour;
773 
774   time_calendar = localtime(& BX_CMOS_THIS s.timeval);
775 
776   // update seconds
777   BX_CMOS_THIS s.reg[REG_SEC] = bin_to_bcd(time_calendar->tm_sec,
778     BX_CMOS_THIS s.rtc_mode_binary);
779 
780   // update minutes
781   BX_CMOS_THIS s.reg[REG_MIN] = bin_to_bcd(time_calendar->tm_min,
782     BX_CMOS_THIS s.rtc_mode_binary);
783 
784   // update hours
785   if (BX_CMOS_THIS s.rtc_mode_12hour) {
786     hour = time_calendar->tm_hour;
787     val_bcd = (hour > 11) ? 0x80 : 0x00;
788     if (hour > 11) hour -= 12;
789     if (hour == 0) hour = 12;
790     val_bcd |= bin_to_bcd(hour, BX_CMOS_THIS s.rtc_mode_binary);
791     BX_CMOS_THIS s.reg[REG_HOUR] = val_bcd;
792   } else {
793     BX_CMOS_THIS s.reg[REG_HOUR] = bin_to_bcd(time_calendar->tm_hour,
794       BX_CMOS_THIS s.rtc_mode_binary);
795   }
796 
797   // update day of the week
798   day = time_calendar->tm_wday + 1; // 0..6 to 1..7
799   BX_CMOS_THIS s.reg[REG_WEEK_DAY] = bin_to_bcd(day,
800     BX_CMOS_THIS s.rtc_mode_binary);
801 
802   // update day of the month
803   day = time_calendar->tm_mday;
804   BX_CMOS_THIS s.reg[REG_MONTH_DAY] = bin_to_bcd(day,
805     BX_CMOS_THIS s.rtc_mode_binary);
806 
807   // update month
808   month   = time_calendar->tm_mon + 1;
809   BX_CMOS_THIS s.reg[REG_MONTH] = bin_to_bcd(month,
810     BX_CMOS_THIS s.rtc_mode_binary);
811 
812   // update year
813   year = time_calendar->tm_year % 100;
814   BX_CMOS_THIS s.reg[REG_YEAR] = bin_to_bcd(year,
815     BX_CMOS_THIS s.rtc_mode_binary);
816 
817   // update century
818   century = (time_calendar->tm_year / 100) + 19;
819   BX_CMOS_THIS s.reg[REG_IBM_CENTURY_BYTE] = bin_to_bcd(century,
820     BX_CMOS_THIS s.rtc_mode_binary);
821 
822   // Raul Hudea pointed out that some bioses also use reg 0x37 for the
823   // century byte.  Tony Heller says this is critical in getting WinXP to run.
824   BX_CMOS_THIS s.reg[REG_IBM_PS2_CENTURY_BYTE] =
825     BX_CMOS_THIS s.reg[REG_IBM_CENTURY_BYTE];
826 }
827 
update_timeval()828 void bx_cmos_c::update_timeval()
829 {
830   struct tm time_calendar;
831   Bit8u val_bin, pm_flag;
832 
833   // update seconds
834   time_calendar.tm_sec = bcd_to_bin(BX_CMOS_THIS s.reg[REG_SEC],
835     BX_CMOS_THIS s.rtc_mode_binary);
836 
837   // update minutes
838   time_calendar.tm_min = bcd_to_bin(BX_CMOS_THIS s.reg[REG_MIN],
839     BX_CMOS_THIS s.rtc_mode_binary);
840 
841   // update hours
842   if (BX_CMOS_THIS s.rtc_mode_12hour) {
843     pm_flag = BX_CMOS_THIS s.reg[REG_HOUR] & 0x80;
844     val_bin = bcd_to_bin(BX_CMOS_THIS s.reg[REG_HOUR] & 0x70,
845       BX_CMOS_THIS s.rtc_mode_binary);
846     if ((val_bin < 12) & (pm_flag > 0)) {
847       val_bin += 12;
848     } else if ((val_bin == 12) & (pm_flag == 0)) {
849       val_bin = 0;
850     }
851     time_calendar.tm_hour = val_bin;
852   } else {
853     time_calendar.tm_hour = bcd_to_bin(BX_CMOS_THIS s.reg[REG_HOUR],
854       BX_CMOS_THIS s.rtc_mode_binary);
855   }
856 
857   // update day of the month
858   time_calendar.tm_mday = bcd_to_bin(BX_CMOS_THIS s.reg[REG_MONTH_DAY],
859     BX_CMOS_THIS s.rtc_mode_binary);
860 
861   // update month
862   time_calendar.tm_mon = bcd_to_bin(BX_CMOS_THIS s.reg[REG_MONTH],
863     BX_CMOS_THIS s.rtc_mode_binary) - 1;
864 
865   // update year
866   val_bin = bcd_to_bin(BX_CMOS_THIS s.reg[REG_IBM_CENTURY_BYTE],
867     BX_CMOS_THIS s.rtc_mode_binary);
868   val_bin = (val_bin - 19) * 100;
869   val_bin += bcd_to_bin(BX_CMOS_THIS s.reg[REG_YEAR],
870     BX_CMOS_THIS s.rtc_mode_binary);
871   time_calendar.tm_year = val_bin;
872 
873   BX_CMOS_THIS s.timeval = mktime(& time_calendar);
874 }
875 
876 #if BX_DEBUGGER
debug_dump(int argc,char ** argv)877 void bx_cmos_c::debug_dump(int argc, char **argv)
878 {
879   int i, j, r;
880 
881   dbg_printf("CMOS RTC\n\n");
882   dbg_printf("Index register: 0x%02x\n\n", BX_CMOS_THIS s.cmos_mem_address);
883   r = 0;
884   for (i=0; i<8; i++) {
885     dbg_printf("%04x ", r);
886     for (j=0; j<16; j++) {
887       dbg_printf(" %02x", BX_CMOS_THIS s.reg[r++]);
888     }
889     dbg_printf("\n");
890   }
891   if (argc > 0) {
892     dbg_printf("\nAdditional options not supported\n");
893   }
894 }
895 #endif
896