summaryrefslogtreecommitdiff
path: root/src/post.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/post.c')
-rw-r--r--src/post.c312
1 files changed, 312 insertions, 0 deletions
diff --git a/src/post.c b/src/post.c
new file mode 100644
index 0000000..8d35f97
--- /dev/null
+++ b/src/post.c
@@ -0,0 +1,312 @@
+// 32bit code to Power On Self Test (POST) a machine.
+//
+// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net>
+// Copyright (C) 2002 MandrakeSoft S.A.
+//
+// This file may be distributed under the terms of the GNU GPLv3 license.
+
+#include "ioport.h" // PORT_*
+#include "../out/rom16.offset.auto.h" // OFFSET_*
+#include "config.h" // CONFIG_*
+#include "cmos.h" // CMOS_*
+#include "util.h" // memset
+#include "biosvar.h" // struct bios_data_area_s
+
+#define bda ((struct bios_data_area_s *)0)
+#define ebda ((struct extended_bios_data_area_s *)(EBDA_SEG<<4))
+
+static void
+init_bda()
+{
+ memset(bda, 0, sizeof(*bda));
+
+ int i;
+ for (i=0; i<256; i++) {
+ bda->ivecs[i].seg = 0xf000;
+ bda->ivecs[i].offset = OFFSET_dummy_iret_handler;
+ }
+
+ bda->mem_size_kb = BASE_MEM_IN_K;
+}
+
+static void
+init_handlers()
+{
+ // set vector 0x79 to zero
+ // this is used by 'gardian angel' protection system
+ bda->ivecs[0x79].seg = 0;
+ bda->ivecs[0x79].offset = 0;
+
+ bda->ivecs[0x40].offset = OFFSET_entry_40;
+ bda->ivecs[0x0e].offset = OFFSET_entry_0e;
+ bda->ivecs[0x13].offset = OFFSET_entry_13;
+ bda->ivecs[0x76].offset = OFFSET_entry_76;
+ bda->ivecs[0x17].offset = OFFSET_entry_17;
+ bda->ivecs[0x18].offset = OFFSET_entry_18;
+ bda->ivecs[0x19].offset = OFFSET_entry_19;
+ bda->ivecs[0x1c].offset = OFFSET_entry_1c;
+ bda->ivecs[0x12].offset = OFFSET_entry_12;
+ bda->ivecs[0x11].offset = OFFSET_entry_11;
+ bda->ivecs[0x15].offset = OFFSET_entry_15;
+ bda->ivecs[0x08].offset = OFFSET_entry_08;
+ bda->ivecs[0x09].offset = OFFSET_entry_09;
+ bda->ivecs[0x16].offset = OFFSET_entry_16;
+ bda->ivecs[0x14].offset = OFFSET_entry_14;
+ bda->ivecs[0x1a].offset = OFFSET_entry_1a;
+ bda->ivecs[0x70].offset = OFFSET_entry_70;
+ bda->ivecs[0x74].offset = OFFSET_entry_74;
+ bda->ivecs[0x75].offset = OFFSET_entry_75;
+ bda->ivecs[0x10].offset = OFFSET_entry_10;
+}
+
+static void
+init_ebda()
+{
+ ebda->size = EBDA_SIZE;
+ bda->ebda_seg = EBDA_SEG;
+ bda->ivecs[0x41].seg = EBDA_SEG;
+ bda->ivecs[0x41].offset = 0x3d; // XXX
+ bda->ivecs[0x46].seg = EBDA_SEG;
+ bda->ivecs[0x46].offset = 0x4d; // XXX
+}
+
+static void
+pit_setup()
+{
+ // timer0: binary count, 16bit count, mode 2
+ outb(0x34, PORT_PIT_MODE);
+ // maximum count of 0000H = 18.2Hz
+ outb(0x0, PORT_PIT_COUNTER0);
+ outb(0x0, PORT_PIT_COUNTER0);
+}
+
+static void
+kbd_init()
+{
+}
+
+static void
+kbd_setup()
+{
+ bda->kbd_mode = 0x10;
+ bda->kbd_buf_head = bda->kbd_buf_tail = offsetof(struct bios_data_area_s, kbd_buf);
+ bda->kbd_buf_start_offset = offsetof(struct bios_data_area_s, kbd_buf);
+ bda->kbd_buf_end_offset = offsetof(struct bios_data_area_s, kbd_buf[sizeof(bda->kbd_buf)]);
+ kbd_init();
+
+ // XXX
+ u16 eqb = bda->equipment_list_flags;
+ eqb = (eqb & 0xff00) | inb_cmos(CMOS_EQUIPMENT_INFO);
+ bda->equipment_list_flags = eqb;
+}
+
+static void
+lpt_setup()
+{
+ // XXX
+}
+
+static void
+serial_setup()
+{
+ // XXX
+}
+
+static u32
+bcd2bin(u8 val)
+{
+ return (val & 0xf) + ((val >> 4) * 10);
+}
+
+static void
+timer_setup()
+{
+ u32 seconds = bcd2bin(inb_cmos(CMOS_RTC_SECONDS));
+ u32 ticks = (seconds * 18206507) / 1000000;
+ u32 minutes = bcd2bin(inb_cmos(CMOS_RTC_MINUTES));
+ ticks += (minutes * 10923904) / 10000;
+ u32 hours = bcd2bin(inb_cmos(CMOS_RTC_HOURS));
+ ticks += (hours * 65543427) / 1000;
+ bda->timer_counter = ticks;
+ bda->timer_rollover = 0;
+}
+
+static void
+pic_setup()
+{
+ outb(0x11, PORT_PIC1);
+ outb(0x11, PORT_PIC2_DATA);
+ outb(0x08, PORT_PIC1_DATA);
+ outb(0x70, PORT_PIC2_DATA);
+ outb(0x04, PORT_PIC1_DATA);
+ outb(0x02, PORT_PIC2_DATA);
+ outb(0x01, PORT_PIC1_DATA);
+ outb(0x01, PORT_PIC2_DATA);
+ outb(0xb8, PORT_PIC1_DATA);
+ if (CONFIG_PS2_MOUSE)
+ outb(0x8f, PORT_PIC2_DATA);
+ else
+ outb(0x9f, PORT_PIC2_DATA);
+}
+
+static void
+floppy_drive_post()
+{
+ u8 type = inb_cmos(CMOS_FLOPPY_DRIVE_TYPE);
+ u8 out = 0;
+ if (type & 0xf0)
+ out |= 0x07;
+ if (type & 0x0f)
+ out |= 0x70;
+ bda->floppy_harddisk_info = out;
+ outb(0x02, PORT_DMA1_MASK_REG);
+
+ bda->ivecs[0x1E].offset = OFFSET_diskette_param_table2;
+}
+
+static void
+cdemu_init()
+{
+ //ebda->cdemu.active = 0;
+}
+
+static void
+ata_init()
+{
+}
+
+static void
+ata_detect()
+{
+}
+
+static void
+hard_drive_post()
+{
+}
+
+static void
+init_boot_vectors()
+{
+}
+
+static void __attribute__((noinline))
+call16(u16 seg, u16 offset)
+{
+ u32 segoff = (seg << 16) | offset;
+ asm volatile(
+ "pushal\n" // Save registers
+ "ljmp $0x20, %0\n" // Jump to 16bit transition code
+ ".globl call16_resume\n"
+ "call16_resume:\n" // point of return
+ "popal\n" // restore registers
+ : : "Z" (OFFSET_call16), "b" (segoff));
+}
+
+static int
+checksum(u8 *p, u32 len)
+{
+ u32 i;
+ u8 sum = 0;
+ for (i=0; i<len; i++)
+ sum += p[i];
+ return sum;
+}
+
+#define PTR_TO_SEG(p) ((((u32)(p)) >> 4) & 0xf000)
+#define PTR_TO_OFFSET(p) (((u32)(p)) & 0xffff)
+
+static void
+rom_scan()
+{
+ u8 *p = (u8*)0xc0000;
+ for (; p <= (u8*)0xe0000; p += 2048) {
+ u8 *rom = p;
+ if (*(u16*)rom != 0xaa55)
+ continue;
+ u32 len = rom[2] * 512;
+ if (checksum(rom, len) != 0)
+ continue;
+ p = (u8*)(((u32)p + len) / 2048 * 2048);
+ call16(PTR_TO_SEG(rom), PTR_TO_OFFSET(rom + 3));
+
+ // Look at the ROM's PnP Expansion header. Properly, we're supposed
+ // to init all the ROMs and then go back and build an IPL table of
+ // all the bootable devices, but we can get away with one pass.
+ if (rom[0x1a] != '$' || rom[0x1b] != 'P'
+ || rom[0x1c] != 'n' || rom[0x1d] != 'P')
+ continue;
+ // 0x1A is also the offset into the expansion header of...
+ // the Bootstrap Entry Vector, or zero if there is none.
+ u16 entry = *(u16*)&rom[0x1a+0x1a];
+ if (!entry)
+ continue;
+ // Found a device that thinks it can boot the system. Record
+ // its BEV and product name string.
+
+ // XXX
+ }
+}
+
+static void
+status_restart(u8 status)
+{
+#if 0
+ if (status == 0x05)
+ eoi_jmp_post();
+#endif
+
+ BX_PANIC("Unimplemented shutdown status: %02x\n",(Bit8u)status);
+}
+
+static void
+post()
+{
+ // first reset the DMA controllers
+ outb(0, PORT_DMA1_MASTER_CLEAR);
+ outb(0, PORT_DMA2_MASTER_CLEAR);
+
+ // then initialize the DMA controllers
+ outb(0xc0, PORT_DMA2_MODE_REG);
+ outb(0x00, PORT_DMA2_MASK_REG);
+
+ // Get and then clear CMOS shutdown status.
+ u8 status = inb_cmos(CMOS_RESET_CODE);
+ outb_cmos(0, CMOS_RESET_CODE);
+
+ if (status != 0x00 && status != 0x09 && status < 0x0d)
+ status_restart(status);
+
+ BX_INFO("Start bios");
+
+ init_bda();
+ init_handlers();
+ init_ebda();
+
+ pit_setup();
+ kbd_setup();
+ lpt_setup();
+ serial_setup();
+ timer_setup();
+ pic_setup();
+ //pci_setup();
+ init_boot_vectors();
+ rom_scan();
+
+ printf("BIOS - begin\n\n");
+
+ floppy_drive_post();
+ hard_drive_post();
+ if (CONFIG_ATA) {
+ ata_init();
+ ata_detect();
+ }
+ cdemu_init();
+ call16(0xf000, OFFSET_begin_boot);
+}
+
+void VISIBLE
+_start()
+{
+ post();
+}