coreboot-kgpe-d16/util/x86emu/yabel/biosemu.c
Stefan Reinauer 38cd29ebd7 Don't pull in x86emu from a foreign directory anymore. This
produced numerous problems in the past, including the fact that
x86emu doesn't work in v3 anymore even though it lives in the v3
repository.

Since this is a cross-repository move, keeping the history in the v2 tree 
would make life hard for everone. So check the v3 repository for x86emu history
since the merger. The his commit is based on an svn export of r1175 of the
coreboot-v3 repository.

Signed-off-by: Stefan Reinauer <stepan@coresystems.de>
Acked-by: Patrick Georgi <patrick.georgi@coresystems.de>



git-svn-id: svn://svn.coreboot.org/coreboot/trunk@4532 2b7e53f0-3cfb-0310-b3e9-8179ed1497e1
2009-08-11 21:28:25 +00:00

368 lines
11 KiB
C

/******************************************************************************
* Copyright (c) 2004, 2008 IBM Corporation
* Copyright (c) 2008, 2009 Pattrick Hueper <phueper@hueper.net>
* All rights reserved.
* This program and the accompanying materials
* are made available under the terms of the BSD License
* which accompanies this distribution, and is available at
* http://www.opensource.org/licenses/bsd-license.php
*
* Contributors:
* IBM Corporation - initial implementation
*****************************************************************************/
#include <string.h>
#include <types.h>
#ifndef CONFIG_COREBOOT_V2
#include <cpu.h>
#endif
#include "debug.h"
#include <x86emu/x86emu.h>
#include <x86emu/regs.h>
#ifdef CONFIG_COREBOOT_V2
#include "../x86emu/prim_ops.h"
#else
#include <x86emu/prim_ops.h> // for push_word
#endif
#include "biosemu.h"
#include "io.h"
#include "mem.h"
#include "interrupt.h"
#include "device.h"
#include "pmm.h"
#ifdef CONFIG_COREBOOT_V2
#include "compat/rtas.h"
#else
#include <rtas.h>
#endif
#include <device/device.h>
static X86EMU_memFuncs my_mem_funcs = {
my_rdb, my_rdw, my_rdl,
my_wrb, my_wrw, my_wrl
};
static X86EMU_pioFuncs my_pio_funcs = {
my_inb, my_inw, my_inl,
my_outb, my_outw, my_outl
};
/* interrupt function override array (see biosemu.h) */
yabel_handleIntFunc yabel_intFuncArray[256];
void dump(u8 * addr, u32 len);
/* main entry into YABEL biosemu, arguments are:
* *biosmem = pointer to virtual memory
* biosmem_size = size of the virtual memory
* *dev = pointer to the device to be initialised
* rom_addr = address of the OptionROM to be executed, if this is = 0, YABEL
* will look for an ExpansionROM BAR and use the code from there.
*/
u32
biosemu(u8 *biosmem, u32 biosmem_size, struct device * dev, unsigned long rom_addr)
{
u8 *rom_image;
int i = 0;
#ifdef CONFIG_DEBUG
debug_flags = 0;//DEBUG_PRINT_INT10 | DEBUG_PNP | DEBUG_INTR | DEBUG_CHECK_VMEM_ACCESS | DEBUG_MEM | DEBUG_IO;
// | DEBUG_CHECK_VMEM_ACCESS | DEBUG_MEM | DEBUG_IO;
// | DEBUG_TRACE_X86EMU | DEBUG_JMP;
/* use CONFIG_YABEL_DEBUG_FLAGS, too... */
debug_flags |= CONFIG_YABEL_DEBUG_FLAGS;
#endif
if (biosmem_size < MIN_REQUIRED_VMEM_SIZE) {
printf("Error: Not enough virtual memory: %x, required: %x!\n",
biosmem_size, MIN_REQUIRED_VMEM_SIZE);
return -1;
}
if (biosemu_dev_init(dev) != 0) {
printf("Error initializing device!\n");
return -1;
}
if (biosemu_dev_check_exprom(rom_addr) != 0) {
printf("Error: Device Expansion ROM invalid!\n");
return -1;
}
rom_image = (u8 *) bios_device.img_addr;
DEBUG_PRINTF("executing rom_image from %p\n", rom_image);
DEBUG_PRINTF("biosmem at %p\n", biosmem);
DEBUG_PRINTF("Image Size: %d\n", bios_device.img_size);
// in case we jump somewhere unexpected, or execution is finished,
// fill the biosmem with hlt instructions (0xf4)
memset(biosmem, 0xf4, biosmem_size);
M.mem_base = (long) biosmem;
M.mem_size = biosmem_size;
DEBUG_PRINTF("membase set: %08x, size: %08x\n", (int) M.mem_base,
(int) M.mem_size);
// copy expansion ROM image to segment OPTION_ROM_CODE_SEGMENT
// NOTE: this sometimes fails, some bytes are 0x00... so we compare
// after copying and do some retries...
u8 *mem_img = biosmem + (OPTION_ROM_CODE_SEGMENT << 4);
u8 copy_count = 0;
u8 cmp_result = 0;
do {
#if 0
set_ci();
memcpy(mem_img, rom_image, len);
clr_ci();
#else
// memcpy fails... try copy byte-by-byte with set/clr_ci
u8 c;
for (i = 0; i < bios_device.img_size; i++) {
set_ci();
c = *(rom_image + i);
if (c != *(rom_image + i)) {
clr_ci();
printf("Copy failed at: %x/%x\n", i,
bios_device.img_size);
printf("rom_image(%x): %x, mem_img(%x): %x\n",
i, *(rom_image + i), i, *(mem_img + i));
break;
}
clr_ci();
*(mem_img + i) = c;
}
#endif
copy_count++;
set_ci();
cmp_result = memcmp(mem_img, rom_image, bios_device.img_size);
clr_ci();
}
while ((copy_count < 5) && (cmp_result != 0));
if (cmp_result != 0) {
printf
("\nCopying Expansion ROM Image to Memory failed after %d retries! (%x)\n",
copy_count, cmp_result);
dump(rom_image, 0x20);
dump(mem_img, 0x20);
return 0;
}
// setup default Interrupt Vectors
// some expansion ROMs seem to check for these addresses..
// each handler is only an IRET (0xCF) instruction
// ROM BIOS Int 10 Handler F000:F065
my_wrl(0x10 * 4, 0xf000f065);
my_wrb(0x000ff065, 0xcf);
// ROM BIOS Int 11 Handler F000:F84D
my_wrl(0x11 * 4, 0xf000f84d);
my_wrb(0x000ff84d, 0xcf);
// ROM BIOS Int 12 Handler F000:F841
my_wrl(0x12 * 4, 0xf000f841);
my_wrb(0x000ff841, 0xcf);
// ROM BIOS Int 13 Handler F000:EC59
my_wrl(0x13 * 4, 0xf000ec59);
my_wrb(0x000fec59, 0xcf);
// ROM BIOS Int 14 Handler F000:E739
my_wrl(0x14 * 4, 0xf000e739);
my_wrb(0x000fe739, 0xcf);
// ROM BIOS Int 15 Handler F000:F859
my_wrl(0x15 * 4, 0xf000f859);
my_wrb(0x000ff859, 0xcf);
// ROM BIOS Int 16 Handler F000:E82E
my_wrl(0x16 * 4, 0xf000e82e);
my_wrb(0x000fe82e, 0xcf);
// ROM BIOS Int 17 Handler F000:EFD2
my_wrl(0x17 * 4, 0xf000efd2);
my_wrb(0x000fefd2, 0xcf);
// ROM BIOS Int 1A Handler F000:FE6E
my_wrl(0x1a * 4, 0xf000fe6e);
my_wrb(0x000ffe6e, 0xcf);
// setup BIOS Data Area (0000:04xx, or 0040:00xx)
// we currently 0 this area, meaning "we dont have
// any hardware" :-) no serial/parallel ports, floppys, ...
memset(biosmem + 0x400, 0x0, 0x100);
// at offset 13h in BDA is the memory size in kbytes
my_wrw(0x413, biosmem_size / 1024);
// at offset 0eh in BDA is the segment of the Extended BIOS Data Area
// see setup further down
my_wrw(0x40e, INITIAL_EBDA_SEGMENT);
// TODO: setup BDA Video Data ( offset 49h-66h)
// e.g. to store video mode, cursor position, ...
// in int10 (done) handler and VBE Functions
// TODO: setup BDA Fixed Disk Data
// 74h: Fixed Disk Last Operation Status
// 75h: Fixed Disk Number of Disk Drives
// TODO: check BDA for further needed data...
//setup Extended BIOS Data Area
//we currently 0 this area
memset(biosmem + (INITIAL_EBDA_SEGMENT << 4), 0, INITIAL_EBDA_SIZE);
// at offset 0h in EBDA is the size of the EBDA in KB
my_wrw((INITIAL_EBDA_SEGMENT << 4) + 0x0, INITIAL_EBDA_SIZE / 1024);
//TODO: check for further needed EBDA data...
// setup original ROM BIOS Area (F000:xxxx)
char *date = "06/11/99";
for (i = 0; date[i]; i++)
my_wrb(0xffff5 + i, date[i]);
// set up eisa ident string
char *ident = "PCI_ISA";
for (i = 0; ident[i]; i++)
my_wrb(0xfffd9 + i, ident[i]);
// write system model id for IBM-AT
// according to "Ralf Browns Interrupt List" Int15 AH=C0 Table 515,
// model FC is the original AT and also used in all DOSEMU Versions.
my_wrb(0xFFFFE, 0xfc);
//setup interrupt handler
X86EMU_intrFuncs intrFuncs[256];
for (i = 0; i < 256; i++)
intrFuncs[i] = handleInterrupt;
X86EMU_setupIntrFuncs(intrFuncs);
X86EMU_setupPioFuncs(&my_pio_funcs);
X86EMU_setupMemFuncs(&my_mem_funcs);
//setup PMM struct in BIOS_DATA_SEGMENT, offset 0x0
u8 pmm_length = pmm_setup(BIOS_DATA_SEGMENT, 0x0);
if (pmm_length <= 0) {
printf ("\nYABEL: Warning: PMM Area could not be setup. PMM not available (%x)\n",
pmm_length);
return 0;
} else {
CHECK_DBG(DEBUG_PMM) {
/* test the PMM */
pmm_test();
/* and clean it again by calling pmm_setup... */
pmm_length = pmm_setup(BIOS_DATA_SEGMENT, 0x0);
}
}
// setup the CPU
M.x86.R_AH = bios_device.bus;
M.x86.R_AL = bios_device.devfn;
M.x86.R_DX = 0x80;
M.x86.R_EIP = 3;
M.x86.R_CS = OPTION_ROM_CODE_SEGMENT;
// Initialize stack and data segment
M.x86.R_SS = STACK_SEGMENT;
M.x86.R_SP = STACK_START_OFFSET;
M.x86.R_DS = DATA_SEGMENT;
// push a HLT instruction and a pointer to it onto the stack
// any return will pop the pointer and jump to the HLT, thus
// exiting (more or less) cleanly
push_word(0xf4f4); //F4=HLT
push_word(M.x86.R_SS);
push_word(M.x86.R_SP + 2);
CHECK_DBG(DEBUG_TRACE_X86EMU) {
X86EMU_trace_on();
} else {
#ifdef CONFIG_DEBUG
M.x86.debug |= DEBUG_SAVE_IP_CS_F;
M.x86.debug |= DEBUG_DECODE_F;
M.x86.debug |= DEBUG_DECODE_NOPRINT_F;
#endif
}
CHECK_DBG(DEBUG_JMP) {
M.x86.debug |= DEBUG_TRACEJMP_F;
M.x86.debug |= DEBUG_TRACEJMP_REGS_F;
M.x86.debug |= DEBUG_TRACECALL_F;
M.x86.debug |= DEBUG_TRACECALL_REGS_F;
}
DEBUG_PRINTF("Executing Initialization Vector...\n");
X86EMU_exec();
DEBUG_PRINTF("done\n");
/* According to the PNP BIOS Spec, Option ROMs should upon exit, return
* some boot device status in AX (see PNP BIOS Spec Section 3.3
*/
DEBUG_PRINTF_CS_IP("Option ROM Exit Status: %04x\n", M.x86.R_AX);
#ifdef CONFIG_DEBUG
DEBUG_PRINTF("Exit Status Decode:\n");
if (M.x86.R_AX & 0x100) { // bit 8
DEBUG_PRINTF
(" IPL Device supporting INT 13h Block Device Format:\n");
switch (((M.x86.R_AX >> 4) & 0x3)) { // bits 5:4
case 0:
DEBUG_PRINTF(" No IPL Device attached\n");
break;
case 1:
DEBUG_PRINTF(" IPL Device status unknown\n");
break;
case 2:
DEBUG_PRINTF(" IPL Device attached\n");
break;
case 3:
DEBUG_PRINTF(" IPL Device status RESERVED!!\n");
break;
}
}
if (M.x86.R_AX & 0x80) { // bit 7
DEBUG_PRINTF
(" Output Device supporting INT 10h Character Output:\n");
switch (((M.x86.R_AX >> 4) & 0x3)) { // bits 5:4
case 0:
DEBUG_PRINTF(" No Display Device attached\n");
break;
case 1:
DEBUG_PRINTF(" Display Device status unknown\n");
break;
case 2:
DEBUG_PRINTF(" Display Device attached\n");
break;
case 3:
DEBUG_PRINTF(" Display Device status RESERVED!!\n");
break;
}
}
if (M.x86.R_AX & 0x40) { // bit 6
DEBUG_PRINTF
(" Input Device supporting INT 9h Character Input:\n");
switch (((M.x86.R_AX >> 4) & 0x3)) { // bits 5:4
case 0:
DEBUG_PRINTF(" No Input Device attached\n");
break;
case 1:
DEBUG_PRINTF(" Input Device status unknown\n");
break;
case 2:
DEBUG_PRINTF(" Input Device attached\n");
break;
case 3:
DEBUG_PRINTF(" Input Device status RESERVED!!\n");
break;
}
}
#endif
/* Check whether the stack is "clean" i.e. containing the HLT
* instruction we pushed before executing and pointing to the original
* stack address... indicating that the initialization probably was
* successful
*/
if ((pop_word() == 0xf4f4) && (M.x86.R_SS == STACK_SEGMENT)
&& (M.x86.R_SP == STACK_START_OFFSET)) {
DEBUG_PRINTF("Stack is clean, initialization successfull!\n");
} else {
DEBUG_PRINTF
("Stack unclean, initialization probably NOT COMPLETE!!\n");
DEBUG_PRINTF("SS:SP = %04x:%04x, expected: %04x:%04x\n",
M.x86.R_SS, M.x86.R_SP, STACK_SEGMENT,
STACK_START_OFFSET);
}
// TODO: according to the BIOS Boot Spec initializations may be ended using INT18h and setting
// the status.
// We need to implement INT18 accordingly, pseudo code is in specsbbs101.pdf page 30
// (also for Int19)
return 0;
}