void init_rtc(void) { outb_cmos(0x0a, 0x26); outb_cmos(0x0b, 0x02); inb_cmos(0x0c); inb_cmos(0x0d); }
void BIOSCALL int70_function(pusha_regs_t regs, uint16_t ds, uint16_t es, iret_addr_t iret_addr) { // INT 70h: IRQ 8 - CMOS RTC interrupt from periodic or alarm modes uint8_t registerB = 0, registerC = 0; // Check which modes are enabled and have occurred. registerB = inb_cmos( 0xB ); registerC = inb_cmos( 0xC ); if( ( registerB & 0x60 ) != 0 ) { if( ( registerC & 0x20 ) != 0 ) { // Handle Alarm Interrupt. int_enable(); call_int_4a(); int_disable(); } if( ( registerC & 0x40 ) != 0 ) { // Handle Periodic Interrupt. if( read_byte( 0x40, 0xA0 ) != 0 ) { // Wait Interval (Int 15, AH=83) active. uint32_t time; time = read_dword( 0x40, 0x9C ); // Time left in microseconds. if( time < 0x3D1 ) { // Done waiting. uint16_t segment, offset; segment = read_word( 0x40, 0x98 ); offset = read_word( 0x40, 0x9A ); write_byte( 0x40, 0xA0, 0 ); // Turn of status byte. outb_cmos( 0xB, registerB & 0x37 ); // Clear the Periodic Interrupt. write_byte( segment, offset, read_byte(segment, offset) | 0x80 ); // Write to specified flag byte. } else { // Continue waiting. time -= 0x3D1; write_dword( 0x40, 0x9C, time ); } } } } eoi_both_pics(); }
// Entry point for Power On Self Test (POST) - the BIOS initilization // phase. This function makes the memory at 0xc0000-0xfffff // read/writable and then calls dopost(). void VISIBLE32FLAT handle_post(void) { debug_serial_setup(); dprintf(1, "Start bios (version %s)\n", VERSION); // Enable CPU caching setcr0(getcr0() & ~(CR0_CD|CR0_NW)); // Clear CMOS reboot flag. outb_cmos(0, CMOS_RESET_CODE); // Make sure legacy DMA isn't running. init_dma(); // Check if we are running under Xen. xen_probe(); // Allow writes to modify bios area (0xf0000) make_bios_writable(); // Now that memory is read/writable - start post process. dopost(); }
void BIOSCALL int1a_function(pusha_regs_t regs, uint16_t ds, uint16_t es, iret_addr_t iret_addr) { uint8_t val8; BX_DEBUG_INT1A("int1a: AX=%04x BX=%04x CX=%04x DX=%04x DS=%04x\n", regs.u.r16.ax, regs.u.r16.bx, regs.u.r16.cx, regs.u.r16.dx, ds); int_enable(); switch (regs.u.r8.ah) { case 0: // get current clock count int_disable(); regs.u.r16.cx = BiosData->ticks_high; regs.u.r16.dx = BiosData->ticks_low; regs.u.r8.al = BiosData->midnight_flag; BiosData->midnight_flag = 0; // reset flag int_enable(); // AH already 0 ClearCF(iret_addr.flags); // OK break; case 1: // Set Current Clock Count int_disable(); BiosData->ticks_high = regs.u.r16.cx; BiosData->ticks_low = regs.u.r16.dx; BiosData->midnight_flag = 0; // reset flag int_enable(); regs.u.r8.ah = 0; ClearCF(iret_addr.flags); // OK break; case 2: // Read CMOS Time if (rtc_updating()) { SetCF(iret_addr.flags); break; } regs.u.r8.dh = inb_cmos(0x00); // Seconds regs.u.r8.cl = inb_cmos(0x02); // Minutes regs.u.r8.ch = inb_cmos(0x04); // Hours regs.u.r8.dl = inb_cmos(0x0b) & 0x01; // Stat Reg B regs.u.r8.ah = 0; regs.u.r8.al = regs.u.r8.ch; ClearCF(iret_addr.flags); // OK break; case 3: // Set CMOS Time // Using a debugger, I notice the following masking/setting // of bits in Status Register B, by setting Reg B to // a few values and getting its value after INT 1A was called. // // try#1 try#2 try#3 // before 1111 1101 0111 1101 0000 0000 // after 0110 0010 0110 0010 0000 0010 // // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1 // My assumption: RegB = ((RegB & 01100000b) | 00000010b) if (rtc_updating()) { init_rtc(); // fall through as if an update were not in progress } outb_cmos(0x00, regs.u.r8.dh); // Seconds outb_cmos(0x02, regs.u.r8.cl); // Minutes outb_cmos(0x04, regs.u.r8.ch); // Hours // Set Daylight Savings time enabled bit to requested value val8 = (inb_cmos(0x0b) & 0x60) | 0x02 | (regs.u.r8.dl & 0x01); // (reg B already selected) outb_cmos(0x0b, val8); regs.u.r8.ah = 0; regs.u.r8.al = val8; // val last written to Reg B ClearCF(iret_addr.flags); // OK break; case 4: // Read CMOS Date regs.u.r8.ah = 0; if (rtc_updating()) { SetCF(iret_addr.flags); break; } regs.u.r8.cl = inb_cmos(0x09); // Year regs.u.r8.dh = inb_cmos(0x08); // Month regs.u.r8.dl = inb_cmos(0x07); // Day of Month regs.u.r8.ch = inb_cmos(0x32); // Century regs.u.r8.al = regs.u.r8.ch; ClearCF(iret_addr.flags); // OK break; case 5: // Set CMOS Date // Using a debugger, I notice the following masking/setting // of bits in Status Register B, by setting Reg B to // a few values and getting its value after INT 1A was called. // // try#1 try#2 try#3 try#4 // before 1111 1101 0111 1101 0000 0010 0000 0000 // after 0110 1101 0111 1101 0000 0010 0000 0000 // // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1 // My assumption: RegB = (RegB & 01111111b) if (rtc_updating()) { init_rtc(); SetCF(iret_addr.flags); break; } outb_cmos(0x09, regs.u.r8.cl); // Year outb_cmos(0x08, regs.u.r8.dh); // Month outb_cmos(0x07, regs.u.r8.dl); // Day of Month outb_cmos(0x32, regs.u.r8.ch); // Century val8 = inb_cmos(0x0b) & 0x7f; // clear halt-clock bit outb_cmos(0x0b, val8); regs.u.r8.ah = 0; regs.u.r8.al = val8; // AL = val last written to Reg B ClearCF(iret_addr.flags); // OK break; case 6: // Set Alarm Time in CMOS // Using a debugger, I notice the following masking/setting // of bits in Status Register B, by setting Reg B to // a few values and getting its value after INT 1A was called. // // try#1 try#2 try#3 // before 1101 1111 0101 1111 0000 0000 // after 0110 1111 0111 1111 0010 0000 // // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1 // My assumption: RegB = ((RegB & 01111111b) | 00100000b) val8 = inb_cmos(0x0b); // Get Status Reg B regs.u.r16.ax = 0; if (val8 & 0x20) { // Alarm interrupt enabled already SetCF(iret_addr.flags); // Error: alarm in use break; } if (rtc_updating()) { init_rtc(); // fall through as if an update were not in progress } outb_cmos(0x01, regs.u.r8.dh); // Seconds alarm outb_cmos(0x03, regs.u.r8.cl); // Minutes alarm outb_cmos(0x05, regs.u.r8.ch); // Hours alarm outb(0xa1, inb(0xa1) & 0xfe); // enable IRQ 8 // enable Status Reg B alarm bit, clear halt clock bit outb_cmos(0x0b, (val8 & 0x7f) | 0x20); ClearCF(iret_addr.flags); // OK break; case 7: // Turn off Alarm // Using a debugger, I notice the following masking/setting // of bits in Status Register B, by setting Reg B to // a few values and getting its value after INT 1A was called. // // try#1 try#2 try#3 try#4 // before 1111 1101 0111 1101 0010 0000 0010 0010 // after 0100 0101 0101 0101 0000 0000 0000 0010 // // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1 // My assumption: RegB = (RegB & 01010111b) val8 = inb_cmos(0x0b); // Get Status Reg B // clear clock-halt bit, disable alarm bit outb_cmos(0x0b, val8 & 0x57); // disable alarm bit regs.u.r8.ah = 0; regs.u.r8.al = val8; // val last written to Reg B ClearCF(iret_addr.flags); // OK break; default: BX_DEBUG_INT1A("int1a: AX=%04x unsupported\n", regs.u.r16.ax); SetCF(iret_addr.flags); // Unsupported } }