coreboot-v3/util/x86emu/yabel/biosemu.c
<<
>>
Prefs
   1/******************************************************************************
   2 * Copyright (c) 2004, 2008 IBM Corporation
   3 * Copyright (c) 2008, 2009 Pattrick Hueper <phueper@hueper.net>
   4 * All rights reserved.
   5 * This program and the accompanying materials
   6 * are made available under the terms of the BSD License
   7 * which accompanies this distribution, and is available at
   8 * http://www.opensource.org/licenses/bsd-license.php
   9 *
  10 * Contributors:
  11 *     IBM Corporation - initial implementation
  12 *****************************************************************************/
  13
  14#include <string.h>
  15
  16#include <types.h>
  17#ifndef COREBOOT_V2
  18#include <cpu.h>
  19#endif
  20
  21#include "debug.h"
  22
  23#include <x86emu/x86emu.h>
  24#include <x86emu/regs.h>
  25#ifdef COREBOOT_V2
  26#include "../x86emu/prim_ops.h"
  27#else
  28#include <x86emu/prim_ops.h>    // for push_word
  29#endif
  30
  31#include "biosemu.h"
  32#include "io.h"
  33#include "mem.h"
  34#include "interrupt.h"
  35#include "device.h"
  36#include "pmm.h"
  37
  38#ifdef COREBOOT_V2
  39#include "compat/rtas.h"
  40#else
  41#include <rtas.h>
  42#endif
  43
  44#include <device/device.h>
  45
  46static X86EMU_memFuncs my_mem_funcs = {
  47        my_rdb, my_rdw, my_rdl,
  48        my_wrb, my_wrw, my_wrl
  49};
  50
  51static X86EMU_pioFuncs my_pio_funcs = {
  52        my_inb, my_inw, my_inl,
  53        my_outb, my_outw, my_outl
  54};
  55
  56/* interrupt function override array (see biosemu.h) */
  57yabel_handleIntFunc yabel_intFuncArray[256];
  58
  59void dump(u8 * addr, u32 len);
  60
  61/* main entry into YABEL biosemu, arguments are:
  62 * *biosmem = pointer to virtual memory
  63 * biosmem_size = size of the virtual memory
  64 * *dev = pointer to the device to be initialised
  65 * rom_addr = address of the OptionROM to be executed, if this is = 0, YABEL
  66 *      will look for an ExpansionROM BAR and use the code from there.
  67 */
  68u32
  69biosemu(u8 *biosmem, u32 biosmem_size, struct device * dev, unsigned long rom_addr)
  70{
  71        u8 *rom_image;
  72        int i = 0;
  73#ifdef DEBUG
  74        debug_flags = 0;//DEBUG_PRINT_INT10 | DEBUG_PNP | DEBUG_INTR | DEBUG_CHECK_VMEM_ACCESS | DEBUG_MEM | DEBUG_IO;
  75                // | DEBUG_CHECK_VMEM_ACCESS | DEBUG_MEM | DEBUG_IO;
  76                // | DEBUG_TRACE_X86EMU | DEBUG_JMP;
  77
  78        /* use CONFIG_YABEL_DEBUG_FLAGS, too... */
  79        debug_flags |= CONFIG_YABEL_DEBUG_FLAGS;
  80#endif
  81        if (biosmem_size < MIN_REQUIRED_VMEM_SIZE) {
  82                printf("Error: Not enough virtual memory: %x, required: %x!\n",
  83                       biosmem_size, MIN_REQUIRED_VMEM_SIZE);
  84                return -1;
  85        }
  86        if (biosemu_dev_init(dev) != 0) {
  87                printf("Error initializing device!\n");
  88                return -1;
  89        }
  90        if (biosemu_dev_check_exprom(rom_addr) != 0) {
  91                printf("Error: Device Expansion ROM invalid!\n");
  92                return -1;
  93        }
  94        rom_image = (u8 *) bios_device.img_addr;
  95        DEBUG_PRINTF("executing rom_image from %p\n", rom_image);
  96        DEBUG_PRINTF("biosmem at %p\n", biosmem);
  97
  98        DEBUG_PRINTF("Image Size: %d\n", bios_device.img_size);
  99
 100        // in case we jump somewhere unexpected, or execution is finished,
 101        // fill the biosmem with hlt instructions (0xf4)
 102        memset(biosmem, 0xf4, biosmem_size);
 103
 104        M.mem_base = (long) biosmem;
 105        M.mem_size = biosmem_size;
 106        DEBUG_PRINTF("membase set: %08x, size: %08x\n", (int) M.mem_base,
 107                     (int) M.mem_size);
 108
 109        // copy expansion ROM image to segment OPTION_ROM_CODE_SEGMENT
 110        // NOTE: this sometimes fails, some bytes are 0x00... so we compare
 111        // after copying and do some retries...
 112        u8 *mem_img = biosmem + (OPTION_ROM_CODE_SEGMENT << 4);
 113        u8 copy_count = 0;
 114        u8 cmp_result = 0;
 115        do {
 116#if 0
 117                set_ci();
 118                memcpy(mem_img, rom_image, len);
 119                clr_ci();
 120#else
 121                // memcpy fails... try copy byte-by-byte with set/clr_ci
 122                u8 c;
 123                for (i = 0; i < bios_device.img_size; i++) {
 124                        set_ci();
 125                        c = *(rom_image + i);
 126                        if (c != *(rom_image + i)) {
 127                                clr_ci();
 128                                printf("Copy failed at: %x/%x\n", i,
 129                                       bios_device.img_size);
 130                                printf("rom_image(%x): %x, mem_img(%x): %x\n",
 131                                       i, *(rom_image + i), i, *(mem_img + i));
 132                                break;
 133                        }
 134                        clr_ci();
 135                        *(mem_img + i) = c;
 136                }
 137#endif
 138                copy_count++;
 139                set_ci();
 140                cmp_result = memcmp(mem_img, rom_image, bios_device.img_size);
 141                clr_ci();
 142        }
 143        while ((copy_count < 5) && (cmp_result != 0));
 144        if (cmp_result != 0) {
 145                printf
 146                    ("\nCopying Expansion ROM Image to Memory failed after %d retries! (%x)\n",
 147                     copy_count, cmp_result);
 148                dump(rom_image, 0x20);
 149                dump(mem_img, 0x20);
 150                return 0;
 151        }
 152        // setup default Interrupt Vectors
 153        // some expansion ROMs seem to check for these addresses..
 154        // each handler is only an IRET (0xCF) instruction
 155        // ROM BIOS Int 10 Handler F000:F065
 156        my_wrl(0x10 * 4, 0xf000f065);
 157        my_wrb(0x000ff065, 0xcf);
 158        // ROM BIOS Int 11 Handler F000:F84D
 159        my_wrl(0x11 * 4, 0xf000f84d);
 160        my_wrb(0x000ff84d, 0xcf);
 161        // ROM BIOS Int 12 Handler F000:F841
 162        my_wrl(0x12 * 4, 0xf000f841);
 163        my_wrb(0x000ff841, 0xcf);
 164        // ROM BIOS Int 13 Handler F000:EC59
 165        my_wrl(0x13 * 4, 0xf000ec59);
 166        my_wrb(0x000fec59, 0xcf);
 167        // ROM BIOS Int 14 Handler F000:E739
 168        my_wrl(0x14 * 4, 0xf000e739);
 169        my_wrb(0x000fe739, 0xcf);
 170        // ROM BIOS Int 15 Handler F000:F859
 171        my_wrl(0x15 * 4, 0xf000f859);
 172        my_wrb(0x000ff859, 0xcf);
 173        // ROM BIOS Int 16 Handler F000:E82E
 174        my_wrl(0x16 * 4, 0xf000e82e);
 175        my_wrb(0x000fe82e, 0xcf);
 176        // ROM BIOS Int 17 Handler F000:EFD2
 177        my_wrl(0x17 * 4, 0xf000efd2);
 178        my_wrb(0x000fefd2, 0xcf);
 179        // ROM BIOS Int 1A Handler F000:FE6E
 180        my_wrl(0x1a * 4, 0xf000fe6e);
 181        my_wrb(0x000ffe6e, 0xcf);
 182
 183        // setup BIOS Data Area (0000:04xx, or 0040:00xx)
 184        // we currently 0 this area, meaning "we dont have
 185        // any hardware" :-) no serial/parallel ports, floppys, ...
 186        memset(biosmem + 0x400, 0x0, 0x100);
 187
 188        // at offset 13h in BDA is the memory size in kbytes
 189        my_wrw(0x413, biosmem_size / 1024);
 190        // at offset 0eh in BDA is the segment of the Extended BIOS Data Area
 191        // see setup further down
 192        my_wrw(0x40e, INITIAL_EBDA_SEGMENT);
 193        // TODO: setup BDA Video Data ( offset 49h-66h)
 194        // e.g. to store video mode, cursor position, ...
 195        // in int10 (done) handler and VBE Functions
 196
 197        // TODO: setup BDA Fixed Disk Data
 198        // 74h: Fixed Disk Last Operation Status
 199        // 75h: Fixed Disk Number of Disk Drives
 200
 201        // TODO: check BDA for further needed data...
 202
 203        //setup Extended BIOS Data Area
 204        //we currently 0 this area
 205        memset(biosmem + (INITIAL_EBDA_SEGMENT << 4), 0, INITIAL_EBDA_SIZE);
 206        // at offset 0h in EBDA is the size of the EBDA in KB
 207        my_wrw((INITIAL_EBDA_SEGMENT << 4) + 0x0, INITIAL_EBDA_SIZE / 1024);
 208        //TODO: check for further needed EBDA data...
 209
 210        // setup  original ROM BIOS Area (F000:xxxx)
 211        char *date = "06/11/99";
 212        for (i = 0; date[i]; i++)
 213                my_wrb(0xffff5 + i, date[i]);
 214        // set up eisa ident string
 215        char *ident = "PCI_ISA";
 216        for (i = 0; ident[i]; i++)
 217                my_wrb(0xfffd9 + i, ident[i]);
 218
 219        // write system model id for IBM-AT
 220        // according to "Ralf Browns Interrupt List" Int15 AH=C0 Table 515,
 221        // model FC is the original AT and also used in all DOSEMU Versions.
 222        my_wrb(0xFFFFE, 0xfc);
 223
 224        //setup interrupt handler
 225        X86EMU_intrFuncs intrFuncs[256];
 226        for (i = 0; i < 256; i++)
 227                intrFuncs[i] = handleInterrupt;
 228        X86EMU_setupIntrFuncs(intrFuncs);
 229        X86EMU_setupPioFuncs(&my_pio_funcs);
 230        X86EMU_setupMemFuncs(&my_mem_funcs);
 231
 232        //setup PMM struct in BIOS_DATA_SEGMENT, offset 0x0
 233        u8 pmm_length = pmm_setup(BIOS_DATA_SEGMENT, 0x0);      
 234        if (pmm_length <= 0) {
 235                printf ("\nYABEL: Warning: PMM Area could not be setup. PMM not available (%x)\n",
 236                     pmm_length);
 237                return 0;
 238        } else {
 239                CHECK_DBG(DEBUG_PMM) {
 240                        /* test the PMM */
 241                        pmm_test();
 242                        /* and clean it again by calling pmm_setup... */
 243                        pmm_length = pmm_setup(BIOS_DATA_SEGMENT, 0x0);
 244                }
 245        }
 246        // setup the CPU
 247        M.x86.R_AH = bios_device.bus;
 248        M.x86.R_AL = bios_device.devfn;
 249        M.x86.R_DX = 0x80;
 250        M.x86.R_EIP = 3;
 251        M.x86.R_CS = OPTION_ROM_CODE_SEGMENT;
 252
 253        // Initialize stack and data segment
 254        M.x86.R_SS = STACK_SEGMENT;
 255        M.x86.R_SP = STACK_START_OFFSET;
 256        M.x86.R_DS = DATA_SEGMENT;
 257
 258        // push a HLT instruction and a pointer to it onto the stack
 259        // any return will pop the pointer and jump to the HLT, thus
 260        // exiting (more or less) cleanly
 261        push_word(0xf4f4);      //F4=HLT
 262        push_word(M.x86.R_SS);
 263        push_word(M.x86.R_SP + 2);
 264
 265        CHECK_DBG(DEBUG_TRACE_X86EMU) {
 266                X86EMU_trace_on();
 267        } else {
 268#ifdef DEBUG
 269                M.x86.debug |= DEBUG_SAVE_IP_CS_F;
 270                M.x86.debug |= DEBUG_DECODE_F;
 271                M.x86.debug |= DEBUG_DECODE_NOPRINT_F;
 272#endif
 273        }
 274        CHECK_DBG(DEBUG_JMP) {
 275                M.x86.debug |= DEBUG_TRACEJMP_F;
 276                M.x86.debug |= DEBUG_TRACEJMP_REGS_F;
 277                M.x86.debug |= DEBUG_TRACECALL_F;
 278                M.x86.debug |= DEBUG_TRACECALL_REGS_F;
 279                }
 280
 281        DEBUG_PRINTF("Executing Initialization Vector...\n");
 282        X86EMU_exec();
 283        DEBUG_PRINTF("done\n");
 284
 285        /* According to the PNP BIOS Spec, Option ROMs should upon exit, return
 286         * some boot device status in AX (see PNP BIOS Spec Section 3.3
 287         */
 288        DEBUG_PRINTF_CS_IP("Option ROM Exit Status: %04x\n", M.x86.R_AX);
 289#ifdef DEBUG
 290        DEBUG_PRINTF("Exit Status Decode:\n");
 291        if (M.x86.R_AX & 0x100) {       // bit 8
 292                DEBUG_PRINTF
 293                    ("  IPL Device supporting INT 13h Block Device Format:\n");
 294                switch (((M.x86.R_AX >> 4) & 0x3)) {    // bits 5:4
 295                case 0:
 296                        DEBUG_PRINTF("    No IPL Device attached\n");
 297                        break;
 298                case 1:
 299                        DEBUG_PRINTF("    IPL Device status unknown\n");
 300                        break;
 301                case 2:
 302                        DEBUG_PRINTF("    IPL Device attached\n");
 303                        break;
 304                case 3:
 305                        DEBUG_PRINTF("    IPL Device status RESERVED!!\n");
 306                        break;
 307                }
 308        }
 309        if (M.x86.R_AX & 0x80) {        // bit 7
 310                DEBUG_PRINTF
 311                    ("  Output Device supporting INT 10h Character Output:\n");
 312                switch (((M.x86.R_AX >> 4) & 0x3)) {    // bits 5:4
 313                case 0:
 314                        DEBUG_PRINTF("    No Display Device attached\n");
 315                        break;
 316                case 1:
 317                        DEBUG_PRINTF("    Display Device status unknown\n");
 318                        break;
 319                case 2:
 320                        DEBUG_PRINTF("    Display Device attached\n");
 321                        break;
 322                case 3:
 323                        DEBUG_PRINTF("    Display Device status RESERVED!!\n");
 324                        break;
 325                }
 326        }
 327        if (M.x86.R_AX & 0x40) {        // bit 6
 328                DEBUG_PRINTF
 329                    ("  Input Device supporting INT 9h Character Input:\n");
 330                switch (((M.x86.R_AX >> 4) & 0x3)) {    // bits 5:4
 331                case 0:
 332                        DEBUG_PRINTF("    No Input Device attached\n");
 333                        break;
 334                case 1:
 335                        DEBUG_PRINTF("    Input Device status unknown\n");
 336                        break;
 337                case 2:
 338                        DEBUG_PRINTF("    Input Device attached\n");
 339                        break;
 340                case 3:
 341                        DEBUG_PRINTF("    Input Device status RESERVED!!\n");
 342                        break;
 343                }
 344        }
 345#endif
 346        /* Check whether the stack is "clean" i.e. containing the HLT
 347         * instruction we pushed before executing and pointing to the original
 348         * stack address... indicating that the initialization probably was
 349         * successful
 350         */
 351        if ((pop_word() == 0xf4f4) && (M.x86.R_SS == STACK_SEGMENT)
 352            && (M.x86.R_SP == STACK_START_OFFSET)) {
 353                DEBUG_PRINTF("Stack is clean, initialization successfull!\n");
 354        } else {
 355                DEBUG_PRINTF
 356                    ("Stack unclean, initialization probably NOT COMPLETE!!\n");
 357                DEBUG_PRINTF("SS:SP = %04x:%04x, expected: %04x:%04x\n",
 358                             M.x86.R_SS, M.x86.R_SP, STACK_SEGMENT,
 359                             STACK_START_OFFSET);
 360        }
 361
 362
 363        // TODO: according to the BIOS Boot Spec initializations may be ended using INT18h and setting
 364        // the status.
 365        // We need to implement INT18 accordingly, pseudo code is in specsbbs101.pdf page 30
 366        // (also for Int19)
 367        return 0;
 368}
 369
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.