grml...
[seabios.git] / src / post.c
index 8d35f97a76f635c089782ccacadc7949c51ddc1c..cf7501ebbd67e475c0b2c6fd30f11b7051cd9f5e 100644 (file)
 // 32bit code to Power On Self Test (POST) a machine.
 //
-// Copyright (C) 2008  Kevin O'Connor <kevin@koconnor.net>
+// Copyright (C) 2008-2010  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.
+// This file may be distributed under the terms of the GNU LGPLv3 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))
+#include "disk.h" // floppy_drive_setup
+#include "ata.h" // ata_setup
+#include "ahci.h" // ahci_setup
+#include "memmap.h" // add_e820
+#include "pic.h" // pic_setup
+#include "pci.h" // create_pirtable
+#include "acpi.h" // acpi_bios_init
+#include "bregs.h" // struct bregs
+#include "mptable.h" // mptable_init
+#include "boot.h" // IPL
+#include "usb.h" // usb_setup
+#include "smbios.h" // smbios_init
+#include "paravirt.h" // qemu_cfg_port_probe
+#include "xen.h" // xen_probe_hvm_info
+#include "ps2port.h" // ps2port_setup
+#include "virtio-blk.h" // virtio_blk_setup
+
+
+/****************************************************************
+ * BIOS init
+ ****************************************************************/
 
 static void
-init_bda()
+init_ivt(void)
 {
-    memset(bda, 0, sizeof(*bda));
+    dprintf(3, "init ivt\n");
 
+    // Initialize all vectors to the default handler.
     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;
-}
+    for (i=0; i<256; i++)
+        SET_IVT(i, FUNC16(entry_iret_official));
+
+    // Initialize all hw vectors to a default hw handler.
+    for (i=0x08; i<=0x0f; i++)
+        SET_IVT(i, FUNC16(entry_hwpic1));
+    for (i=0x70; i<=0x77; i++)
+        SET_IVT(i, FUNC16(entry_hwpic2));
+
+    // Initialize software handlers.
+    SET_IVT(0x02, FUNC16(entry_02));
+    SET_IVT(0x10, FUNC16(entry_10));
+    SET_IVT(0x11, FUNC16(entry_11));
+    SET_IVT(0x12, FUNC16(entry_12));
+    SET_IVT(0x13, FUNC16(entry_13_official));
+    SET_IVT(0x14, FUNC16(entry_14));
+    SET_IVT(0x15, FUNC16(entry_15));
+    SET_IVT(0x16, FUNC16(entry_16));
+    SET_IVT(0x17, FUNC16(entry_17));
+    SET_IVT(0x18, FUNC16(entry_18));
+    SET_IVT(0x19, FUNC16(entry_19_official));
+    SET_IVT(0x1a, FUNC16(entry_1a));
+    SET_IVT(0x40, FUNC16(entry_40));
+
+    // INT 60h-66h reserved for user interrupt
+    for (i=0x60; i<=0x66; i++)
+        SET_IVT(i, SEGOFF(0, 0));
 
-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;
-}
+    SET_IVT(0x79, SEGOFF(0, 0));
 
-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
+    SET_IVT(0x1E, SEGOFF(SEG_BIOS, (u32)&diskette_param_table2 - BUILD_BIOS_ADDR));
 }
 
 static void
-pit_setup()
+init_bda(void)
 {
-    // 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);
-}
+    dprintf(3, "init bda\n");
 
-static void
-kbd_init()
-{
-}
+    struct bios_data_area_s *bda = MAKE_FLATPTR(SEG_BDA, 0);
+    memset(bda, 0, sizeof(*bda));
 
-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;
-}
+    int esize = EBDA_SIZE_START;
+    SET_BDA(mem_size_kb, BUILD_LOWRAM_END/1024 - esize);
+    u16 ebda_seg = EBDA_SEGMENT_START;
+    SET_BDA(ebda_seg, ebda_seg);
 
-static void
-lpt_setup()
-{
-    // XXX
+    // Init ebda
+    struct extended_bios_data_area_s *ebda = get_ebda_ptr();
+    memset(ebda, 0, sizeof(*ebda));
+    ebda->size = esize;
+
+    add_e820((u32)MAKE_FLATPTR(ebda_seg, 0), GET_EBDA2(ebda_seg, size) * 1024
+             , E820_RESERVED);
 }
 
 static void
-serial_setup()
+ram_probe(void)
 {
-    // XXX
-}
+    dprintf(3, "Find memory size\n");
+    if (CONFIG_COREBOOT) {
+        coreboot_setup();
+    } else if (usingXen()) {
+       xen_setup();
+    } else {
+        // On emulators, get memory size from nvram.
+        u32 rs = ((inb_cmos(CMOS_MEM_EXTMEM2_LOW) << 16)
+                  | (inb_cmos(CMOS_MEM_EXTMEM2_HIGH) << 24));
+        if (rs)
+            rs += 16 * 1024 * 1024;
+        else
+            rs = (((inb_cmos(CMOS_MEM_EXTMEM_LOW) << 10)
+                   | (inb_cmos(CMOS_MEM_EXTMEM_HIGH) << 18))
+                  + 1 * 1024 * 1024);
+        RamSize = rs;
+        add_e820(0, rs, E820_RAM);
+
+        // Check for memory over 4Gig
+        u64 high = ((inb_cmos(CMOS_MEM_HIGHMEM_LOW) << 16)
+                    | ((u32)inb_cmos(CMOS_MEM_HIGHMEM_MID) << 24)
+                    | ((u64)inb_cmos(CMOS_MEM_HIGHMEM_HIGH) << 32));
+        RamSizeOver4G = high;
+        add_e820(0x100000000ull, high, E820_RAM);
+
+        /* reserve 256KB BIOS area at the end of 4 GB */
+        add_e820(0xfffc0000, 256*1024, E820_RESERVED);
+    }
 
-static u32
-bcd2bin(u8 val)
-{
-    return (val & 0xf) + ((val >> 4) * 10);
-}
+    // Don't declare any memory between 0xa0000 and 0x100000
+    add_e820(BUILD_LOWRAM_END, BUILD_BIOS_ADDR-BUILD_LOWRAM_END, E820_HOLE);
+
+    // Mark known areas as reserved.
+    add_e820(BUILD_BIOS_ADDR, BUILD_BIOS_SIZE, E820_RESERVED);
+
+    u32 count = qemu_cfg_e820_entries();
+    if (count) {
+        struct e820_reservation entry;
+        int i;
+
+        for (i = 0; i < count; i++) {
+            qemu_cfg_e820_load_next(&entry);
+            add_e820(entry.address, entry.length, entry.type);
+        }
+    } else if (kvm_para_available()) {
+        // Backwards compatibility - provide hard coded range.
+        // 4 pages before the bios, 3 pages for vmx tss pages, the
+        // other page for EPT real mode pagetable
+        add_e820(0xfffbc000, 4*4096, E820_RESERVED);
+    }
 
-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;
+    dprintf(1, "Ram Size=0x%08x (0x%08x%08x high)\n"
+            , RamSize, (u32)(RamSizeOver4G >> 32), (u32)RamSizeOver4G);
 }
 
 static void
-pic_setup()
+init_bios_tables(void)
 {
-    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);
-}
+    if (CONFIG_COREBOOT) {
+        coreboot_copy_biostable();
+        return;
+    }
+    if (usingXen()) {
+       xen_copy_biostables();
+       return;
+    }
 
-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;
-}
+    create_pirtable();
 
-static void
-cdemu_init()
-{
-    //ebda->cdemu.active = 0;
-}
+    mptable_init();
 
-static void
-ata_init()
-{
+    smbios_init();
+
+    acpi_bios_init();
 }
 
+// Initialize hardware devices
 static void
-ata_detect()
+init_hw(void)
 {
+    usb_setup();
+    ps2port_setup();
+    lpt_setup();
+    serial_setup();
+
+    floppy_setup();
+    ata_setup();
+    ahci_setup();
+    cbfs_payload_setup();
+    ramdisk_setup();
+    virtio_blk_setup();
 }
 
-static void
-hard_drive_post()
+// Begin the boot process by invoking an int0x19 in 16bit mode.
+void VISIBLE32FLAT
+startBoot(void)
 {
+    // Clear low-memory allocations (required by PMM spec).
+    memset((void*)BUILD_STACK_ADDR, 0, BUILD_EBDA_MINIMUM - BUILD_STACK_ADDR);
+
+    dprintf(3, "Jump to int19\n");
+    struct bregs br;
+    memset(&br, 0, sizeof(br));
+    br.flags = F_IF;
+    call16_int(0x19, &br);
 }
 
+// Main setup code.
 static void
-init_boot_vectors()
+maininit(void)
 {
-}
+    // Running at new code address - do code relocation fixups
+    malloc_fixupreloc();
 
-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));
-}
+    // Setup ivt/bda/ebda
+    init_ivt();
+    init_bda();
 
-static int
-checksum(u8 *p, u32 len)
-{
-    u32 i;
-    u8 sum = 0;
-    for (i=0; i<len; i++)
-        sum += p[i];
-    return sum;
+    // Init base pc hardware.
+    pic_setup();
+    timer_setup();
+    mathcp_setup();
+
+    // Initialize mtrr
+    mtrr_setup();
+
+    // Initialize pci
+    pci_setup();
+    smm_init();
+
+    // Setup Xen hypercalls
+    xen_init_hypercalls();
+
+    // Initialize internal tables
+    boot_setup();
+
+    // Start hardware initialization (if optionrom threading)
+    if (CONFIG_THREADS && CONFIG_THREAD_OPTIONROMS)
+        init_hw();
+
+    // Find and initialize other cpus
+    smp_probe();
+
+    // Setup interfaces that option roms may need
+    bios32_setup();
+       dprintf(3, "[wurm] pmm_setup\n");
+    pmm_setup();
+       dprintf(3, "[wurm] pnp_setup\n");
+    pnp_setup();
+       dprintf(3, "[wurm] kbd_setup\n");
+    kbd_setup();
+       dprintf(3, "[wurm] mouse_setup\n");
+    mouse_setup();
+       dprintf(3, "[wurm] init_bios_tables\n");
+    init_bios_tables();
+
+    // Run vga option rom
+       dprintf(3, "[wurm] vga_setup\n");
+    vga_setup();
+
+    // Do hardware initialization (if running synchronously)
+    if (!CONFIG_THREADS || !CONFIG_THREAD_OPTIONROMS) {
+        init_hw();
+        wait_threads();
+    }
+
+    // Run option roms
+       dprintf(3, "[wurm] optionrom_setup\n");
+    optionrom_setup();
+
+    // Run BCVs and show optional boot menu
+       dprintf(3, "[wurm] boot_prep\n");
+    boot_prep();
+
+    // Finalize data structures before boot
+       dprintf(3, "[wurm] cdemu_setup\n");
+    cdemu_setup();
+       dprintf(3, "[wurm] pmm_finalize\n");
+    pmm_finalize();
+       dprintf(3, "[wurm] malloc_finalize\n");
+    malloc_finalize();
+       dprintf(3, "[wurm] memmap_finalize\n");
+    memmap_finalize();
+
+    // Setup bios checksum.
+    BiosChecksum -= checksum((u8*)BUILD_BIOS_ADDR, BUILD_BIOS_SIZE);
+
+    // Write protect bios memory.
+       dprintf(3, "[wurm] make_bios_readonly\n");
+    make_bios_readonly();
+
+    // Invoke int 19 to start boot process.
+       dprintf(3, "[wurm] startBoot\n");
+    startBoot();
 }
 
-#define PTR_TO_SEG(p) ((((u32)(p)) >> 4) & 0xf000)
-#define PTR_TO_OFFSET(p) (((u32)(p)) & 0xffff)
 
+/****************************************************************
+ * POST entry and code relocation
+ ****************************************************************/
+
+// Update given relocs for the code at 'dest' with a given 'delta'
 static void
-rom_scan()
+updateRelocs(void *dest, u32 *rstart, u32 *rend, u32 delta)
 {
-    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
-    }
+    u32 *reloc;
+    for (reloc = rstart; reloc < rend; reloc++)
+        *((u32*)(dest + *reloc)) += delta;
 }
 
+// Relocate init code and then call maininit() at new address.
 static void
-status_restart(u8 status)
+reloc_init(void)
 {
-#if 0
-    if (status == 0x05)
-        eoi_jmp_post();
-#endif
-
-    BX_PANIC("Unimplemented shutdown status: %02x\n",(Bit8u)status);
+    if (!CONFIG_RELOCATE_INIT) {
+        maininit();
+        return;
+    }
+    // Symbols populated by the build.
+    extern u8 code32flat_start[];
+    extern u8 _reloc_min_align[];
+    extern u32 _reloc_abs_start[], _reloc_abs_end[];
+    extern u32 _reloc_rel_start[], _reloc_rel_end[];
+    extern u32 _reloc_init_start[], _reloc_init_end[];
+    extern u8 code32init_start[], code32init_end[];
+
+    // Allocate space for init code.
+    u32 initsize = code32init_end - code32init_start;
+    u32 align = (u32)&_reloc_min_align;
+    void *dest = memalign_tmp(align, initsize);
+    if (!dest)
+        panic("No space for init relocation.\n");
+
+    // Copy code and update relocs (init absolute, init relative, and runtime)
+    dprintf(1, "Relocating init from %p to %p (size %d)\n"
+            , code32init_start, dest, initsize);
+    s32 delta = dest - (void*)code32init_start;
+    memcpy(dest, code32init_start, initsize);
+    updateRelocs(dest, _reloc_abs_start, _reloc_abs_end, delta);
+    updateRelocs(dest, _reloc_rel_start, _reloc_rel_end, -delta);
+    updateRelocs(code32flat_start, _reloc_init_start, _reloc_init_end, delta);
+
+    // Call maininit() in relocated code.
+    void (*func)(void) = (void*)maininit + delta;
+    barrier();
+    func();
 }
 
-static void
-post()
+// Setup for code relocation and then call reloc_init
+void VISIBLE32INIT
+dopost(void)
 {
-    // first reset the DMA controllers
-    outb(0, PORT_DMA1_MASTER_CLEAR);
-    outb(0, PORT_DMA2_MASTER_CLEAR);
+    HaveRunPost = 1;
 
-    // then initialize the DMA controllers
-    outb(0xc0, PORT_DMA2_MODE_REG);
-    outb(0x00, PORT_DMA2_MASK_REG);
+    // Detect ram and setup internal malloc.
+    qemu_cfg_port_probe();
+    ram_probe();
+    malloc_setup();
 
-    // Get and then clear CMOS shutdown status.
-    u8 status = inb_cmos(CMOS_RESET_CODE);
-    outb_cmos(0, CMOS_RESET_CODE);
+    // Relocate initialization code and call maininit().
+    reloc_init();
+}
 
-    if (status != 0x00 && status != 0x09 && status < 0x0d)
-        status_restart(status);
+// 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);
 
-    BX_INFO("Start bios");
+    // Enable CPU caching
+    setcr0(getcr0() & ~(CR0_CD|CR0_NW));
 
-    init_bda();
-    init_handlers();
-    init_ebda();
+    // Clear CMOS reboot flag.
+    outb_cmos(0, CMOS_RESET_CODE);
 
-    pit_setup();
-    kbd_setup();
-    lpt_setup();
-    serial_setup();
-    timer_setup();
-    pic_setup();
-    //pci_setup();
-    init_boot_vectors();
-    rom_scan();
+    // Make sure legacy DMA isn't running.
+    init_dma();
 
-    printf("BIOS - begin\n\n");
+    // Check if we are running under Xen.
+    xen_probe();
 
-    floppy_drive_post();
-    hard_drive_post();
-    if (CONFIG_ATA) {
-        ata_init();
-        ata_detect();
-    }
-    cdemu_init();
-    call16(0xf000, OFFSET_begin_boot);
-}
+    // Allow writes to modify bios area (0xf0000)
+    make_bios_writable();
 
-void VISIBLE
-_start()
-{
-    post();
+    // Now that memory is read/writable - start post process.
+    dopost();
 }