diff options
author | Stefan Reinauer <stepan@coresystems.de> | 2009-08-11 21:28:25 +0000 |
---|---|---|
committer | Stefan Reinauer <stepan@openbios.org> | 2009-08-11 21:28:25 +0000 |
commit | 38cd29ebd7282333650cf11ed50c7f2fd4031e80 (patch) | |
tree | 61008ca7d9a8e4ad5e5237e5f7cbc982a8206c55 /util/x86emu/yabel | |
parent | b339e10f04869a3d8da31e7d52831c32c57302a2 (diff) |
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
Diffstat (limited to 'util/x86emu/yabel')
-rw-r--r-- | util/x86emu/yabel/Config.lb | 9 | ||||
-rw-r--r-- | util/x86emu/yabel/biosemu.c | 368 | ||||
-rw-r--r-- | util/x86emu/yabel/biosemu.h | 49 | ||||
-rw-r--r-- | util/x86emu/yabel/compat/Config.lb | 1 | ||||
-rw-r--r-- | util/x86emu/yabel/compat/functions.c | 96 | ||||
-rw-r--r-- | util/x86emu/yabel/compat/of.h | 55 | ||||
-rw-r--r-- | util/x86emu/yabel/compat/rtas.h | 45 | ||||
-rw-r--r-- | util/x86emu/yabel/compat/time.h | 17 | ||||
-rw-r--r-- | util/x86emu/yabel/debug.c | 58 | ||||
-rw-r--r-- | util/x86emu/yabel/debug.h | 114 | ||||
-rw-r--r-- | util/x86emu/yabel/device.c | 461 | ||||
-rw-r--r-- | util/x86emu/yabel/device.h | 188 | ||||
-rw-r--r-- | util/x86emu/yabel/interrupt.c | 687 | ||||
-rw-r--r-- | util/x86emu/yabel/interrupt.h | 21 | ||||
-rw-r--r-- | util/x86emu/yabel/io.c | 483 | ||||
-rw-r--r-- | util/x86emu/yabel/io.h | 30 | ||||
-rw-r--r-- | util/x86emu/yabel/mem.c | 469 | ||||
-rw-r--r-- | util/x86emu/yabel/mem.h | 36 | ||||
-rw-r--r-- | util/x86emu/yabel/pmm.c | 446 | ||||
-rw-r--r-- | util/x86emu/yabel/pmm.h | 46 | ||||
-rw-r--r-- | util/x86emu/yabel/vbe.c | 779 | ||||
-rw-r--r-- | util/x86emu/yabel/vbe.h | 16 |
22 files changed, 4474 insertions, 0 deletions
diff --git a/util/x86emu/yabel/Config.lb b/util/x86emu/yabel/Config.lb new file mode 100644 index 0000000000..523c794b83 --- /dev/null +++ b/util/x86emu/yabel/Config.lb @@ -0,0 +1,9 @@ +object biosemu.o +object debug.o +object device.o +object interrupt.o +object io.o +object mem.o +object pmm.o +#object vbe.o +dir compat diff --git a/util/x86emu/yabel/biosemu.c b/util/x86emu/yabel/biosemu.c new file mode 100644 index 0000000000..74772e8642 --- /dev/null +++ b/util/x86emu/yabel/biosemu.c @@ -0,0 +1,368 @@ +/****************************************************************************** + * 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; +} diff --git a/util/x86emu/yabel/biosemu.h b/util/x86emu/yabel/biosemu.h new file mode 100644 index 0000000000..fb8dab2b24 --- /dev/null +++ b/util/x86emu/yabel/biosemu.h @@ -0,0 +1,49 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * Copyright (c) 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 + *****************************************************************************/ + +#ifndef _BIOSEMU_BIOSEMU_H_ +#define _BIOSEMU_BIOSEMU_H_ + +#define MIN_REQUIRED_VMEM_SIZE 0x100000 // 1MB + +//define default segments for different components +#define STACK_SEGMENT 0x1000 //1000:xxxx +#define STACK_START_OFFSET 0xfffe + +#define DATA_SEGMENT 0x2000 +#define VBE_SEGMENT 0x3000 + +#define PMM_CONV_SEGMENT 0x4000 // 4000:xxxx is PMM conventional memory area, extended memory area + // will be anything beyound MIN_REQUIRED_MEMORY_SIZE +#define PNP_DATA_SEGMENT 0x5000 + +#define OPTION_ROM_CODE_SEGMENT 0xc000 + +#define BIOS_DATA_SEGMENT 0xF000 +// both EBDA values are _initial_ values, they may (and will be) changed at runtime by option ROMs!! +#define INITIAL_EBDA_SEGMENT 0xF600 // segment of the Extended BIOS Data Area +#define INITIAL_EBDA_SIZE 0x400 // size of the EBDA (at least 1KB!! since size is stored in KB!) + +#define PMM_INT_NUM 0xFC // we misuse INT FC for PMM functionality, at the PMM Entry Point + // Address, there will only be a call to this INT and a RETF +#define PNP_INT_NUM 0xFD + +/* array of funtion pointers to override generic interrupt handlers + * a YABEL caller can add functions to this array before calling YABEL + * if a interrupt occurs, YABEL checks wether a function is set in + * this array and only runs the generic interrupt handler code, if + * the function pointer is NULL */ +typedef int (* yabel_handleIntFunc)(void); +extern yabel_handleIntFunc yabel_intFuncArray[256]; + +#endif diff --git a/util/x86emu/yabel/compat/Config.lb b/util/x86emu/yabel/compat/Config.lb new file mode 100644 index 0000000000..919526d16b --- /dev/null +++ b/util/x86emu/yabel/compat/Config.lb @@ -0,0 +1 @@ +object functions.o diff --git a/util/x86emu/yabel/compat/functions.c b/util/x86emu/yabel/compat/functions.c new file mode 100644 index 0000000000..3b3d223b71 --- /dev/null +++ b/util/x86emu/yabel/compat/functions.c @@ -0,0 +1,96 @@ +/**************************************************************************** + * YABEL BIOS Emulator + * + * 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 + * + * Copyright (c) 2008 Pattrick Hueper <phueper@hueper.net> + ****************************************************************************/ + +/* this file contains functions provided by SLOF, that the current biosemu implementation needs + * they should go away inthe future... + */ + +#include <types.h> +#ifndef CONFIG_COREBOOT_V2 +#include <config.h> +#endif +#include <device/device.h> + +#define VMEM_SIZE 1024 *1024 /* 1 MB */ + +#ifdef CONFIG_YABEL_VIRTMEM_LOCATION +u8* vmem = (u8 *) CONFIG_YABEL_VIRTMEM_LOCATION; +#else +u8* vmem = (u8 *) (16*1024*1024); /* default to 16MB */ +#endif + +u32 biosemu(u8 *biosmem, u32 biosmem_size, struct device *dev, + unsigned long rom_addr); + +void run_bios(struct device * dev, unsigned long addr) +{ + biosemu(vmem, VMEM_SIZE, dev, addr); +} + +u64 get_time(void) +{ + u64 act; + u32 eax, edx; + + __asm__ __volatile__( + "rdtsc" + : "=a"(eax), "=d"(edx) + : /* no inputs, no clobber */); + act = ((u64) edx << 32) | eax; + return act; +} + +unsigned int +read_io(void *addr, size_t sz) +{ + unsigned int ret; + /* since we are using inb instructions, we need the port number as 16bit value */ + u16 port = (u16)(u32) addr; + + switch (sz) { + case 1: + asm volatile ("inb %1, %b0" : "=a"(ret) : "d" (port)); + break; + case 2: + asm volatile ("inw %1, %w0" : "=a"(ret) : "d" (port)); + break; + case 4: + asm volatile ("inl %1, %0" : "=a"(ret) : "d" (port)); + break; + default: + ret = 0; + } + + return ret; +} + +int +write_io(void *addr, unsigned int value, size_t sz) +{ + u16 port = (u16)(u32) addr; + switch (sz) { + /* since we are using inb instructions, we need the port number as 16bit value */ + case 1: + asm volatile ("outb %b0, %1" : : "a"(value), "d" (port)); + break; + case 2: + asm volatile ("outw %w0, %1" : : "a"(value), "d" (port)); + break; + case 4: + asm volatile ("outl %0, %1" : : "a"(value), "d" (port)); + break; + default: + return -1; + } + + return 0; +} + diff --git a/util/x86emu/yabel/compat/of.h b/util/x86emu/yabel/compat/of.h new file mode 100644 index 0000000000..907139951f --- /dev/null +++ b/util/x86emu/yabel/compat/of.h @@ -0,0 +1,55 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * 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 + *****************************************************************************/ + + +#ifndef OF_H +#define OF_H +#define p32 int +#define p32cast (int) (unsigned long) (void*) + +#define phandle_t p32 +#define ihandle_t p32 + +typedef struct +{ + unsigned int serv; + int nargs; + int nrets; + unsigned int args[16]; +} of_arg_t; + + +phandle_t of_finddevice (const char *); +phandle_t of_peer (phandle_t); +phandle_t of_child (phandle_t); +phandle_t of_parent (phandle_t); +int of_getprop (phandle_t, const char *, void *, int); +void * of_call_method_3 (const char *, ihandle_t, int); + + +ihandle_t of_open (const char *); +void of_close(ihandle_t); +int of_read (ihandle_t , void*, int); +int of_write (ihandle_t, void*, int); +int of_seek (ihandle_t, int, int); + +void * of_claim(void *, unsigned int , unsigned int ); +void of_release(void *, unsigned int ); + +int of_yield(void); +void * of_set_callback(void *); + +int vpd_read(unsigned int , unsigned int , char *); +int vpd_write(unsigned int , unsigned int , char *); +int write_mm_log(char *, unsigned int , unsigned short ); + +#endif diff --git a/util/x86emu/yabel/compat/rtas.h b/util/x86emu/yabel/compat/rtas.h new file mode 100644 index 0000000000..25cabf4d6a --- /dev/null +++ b/util/x86emu/yabel/compat/rtas.h @@ -0,0 +1,45 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * 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 + *****************************************************************************/ + + +#ifndef RTAS_H +#define RTAS_H + +#include "of.h" + +typedef struct dtime { + unsigned int year; + unsigned int month; + unsigned int day; + unsigned int hour; + unsigned int minute; + unsigned int second; + unsigned int nano; +} dtime; + +typedef void (*thread_t) (int); + +int rtas_token(const char *); +int rtas_call(int, int, int, int *, ...); +void rtas_init(void); +int rtas_pci_config_read (long long, int, int, int, int); +int rtas_pci_config_write (long long, int, int, int, int, int); +int rtas_set_time_of_day(dtime *); +int rtas_get_time_of_day(dtime *); +int rtas_ibm_update_flash_64(long long, long long); +int rtas_ibm_update_flash_64_and_reboot(long long, long long); +int rtas_system_reboot(void); +int rtas_start_cpu (int, thread_t, int); +int rtas_stop_self (void); +int rtas_ibm_manage_flash(int); + +#endif diff --git a/util/x86emu/yabel/compat/time.h b/util/x86emu/yabel/compat/time.h new file mode 100644 index 0000000000..c8432eca43 --- /dev/null +++ b/util/x86emu/yabel/compat/time.h @@ -0,0 +1,17 @@ +/**************************************************************************** + * YABEL BIOS Emulator + * + * 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 + * + * Copyright (c) 2008 Pattrick Hueper <phueper@hueper.net> + ****************************************************************************/ + +#ifndef _BIOSEMU_COMPAT_TIME_H +#define _BIOSEMU_COMPAT_TIME_H + +/* TODO: check how this works in x86 */ +static unsigned long tb_freq = 0; +#endif diff --git a/util/x86emu/yabel/debug.c b/util/x86emu/yabel/debug.c new file mode 100644 index 0000000000..fdac469d7f --- /dev/null +++ b/util/x86emu/yabel/debug.c @@ -0,0 +1,58 @@ +/****************************************************************************** + * 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 + *****************************************************************************/ + +#ifndef CONFIG_COREBOOT_V2 +#include <cpu.h> +#endif + +#include "debug.h" + +u32 debug_flags = 0; + +void +dump(u8 * addr, u32 len) +{ + printf("\n%s(%p, %x):\n", __func__, addr, len); + while (len) { + unsigned int tmpCnt = len; + unsigned char x; + if (tmpCnt > 8) + tmpCnt = 8; + printf("\n%p: ", addr); + // print hex + while (tmpCnt--) { + set_ci(); + x = *addr++; + clr_ci(); + printf("%02x ", x); + } + tmpCnt = len; + if (tmpCnt > 8) + tmpCnt = 8; + len -= tmpCnt; + //reset addr ptr to print ascii + addr = addr - tmpCnt; + // print ascii + while (tmpCnt--) { + set_ci(); + x = *addr++; + clr_ci(); + if ((x < 32) || (x >= 127)) { + //non-printable char + x = '.'; + } + printf("%c", x); + } + } + printf("\n"); +} diff --git a/util/x86emu/yabel/debug.h b/util/x86emu/yabel/debug.h new file mode 100644 index 0000000000..8126262ed9 --- /dev/null +++ b/util/x86emu/yabel/debug.h @@ -0,0 +1,114 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * Copyright (c) 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 + *****************************************************************************/ +#ifndef _BIOSEMU_DEBUG_H_ +#define _BIOSEMU_DEBUG_H_ + +#include <types.h> + +extern u32 debug_flags; +// from x86emu...needed for debugging +extern void x86emu_dump_xregs(void); + +/* printf is not available in coreboot... use printk */ +#ifdef CONFIG_COREBOOT_V2 +#include <console/console.h> +#else +#include <console.h> +#endif +/* uurgs... yuck... x86emu/x86emu.h is redefining printk... we include it here + * and use its redefinition of printk + * TODO: FIX!!!! */ +#include "x86emu/x86emu.h" +#define printf printk + +/* PH: empty versions of set/clr_ci + * TODO: remove! */ +static inline void clr_ci(void) {}; +static inline void set_ci(void) {}; + +/* Set CONFIG_YABEL_DEBUG_FLAGS is a binary switch that allows you + * to select the following items to debug. 1=on 0=off. After you + * decide what you want to debug create the binary value, convert to hex + * and set the Option (Ex. CONFIG_YABEL_DEBUG_FLAGS = 0x31FF //Debug All). + * + * |-DEBUG_JMP - print info about JMP and RETF opcodes from x86emu + * ||-DEBUG_TRACE_X86EMU - print _all_ opcodes that are executed by x86emu (WARNING: this will produce a LOT of output) + * |||-Currently unused + * ||||-Currently unused + * |||||-Currently unused + * ||||||-DEBUG_PNP - Print Plug And Play access made by option rom + * |||||||-DEBUG_DISK - Print Disk I/O related messages, currently unused + * ||||||||-DEBUG_PMM - Print messages related to POST Memory Manager (PMM) + * |||||||||-DEBUG_VBE - Print messages related to VESA BIOS Extension (VBE) functions + * ||||||||||-DEBUG_PRINT_INT10 - let INT10 (i.e. character output) calls print messages to Debug output + * |||||||||||-DEBUG_INTR - Print messages related to interrupt handling + * ||||||||||||-DEBUG_CHECK_VMEM_ACCESS - Print messages related to accesse to certain areas of the virtual Memory (e.g. BDA (BIOS Data Area) or Interrupt Vectors) + * |||||||||||||-DEBUG_MEM - Print memory access made by option rom (NOTE: this also includes accesses to fetch instructions) + * ||||||||||||||-DEBUG_IO - Print I/O access made by option rom + * 11000111111111 - Max Binary Value, Debug All (WARNING: - This could run for hours) + */ + +#define DEBUG_IO 0x1 +#define DEBUG_MEM 0x2 +// set this to print messages for certain virtual memory accesses (Interrupt Vectors, ...) +#define DEBUG_CHECK_VMEM_ACCESS 0x4 +#define DEBUG_INTR 0x8 +#define DEBUG_PRINT_INT10 0x10 // set to have the INT10 routine print characters +#define DEBUG_VBE 0x20 +#define DEBUG_PMM 0x40 +#define DEBUG_DISK 0x80 +#define DEBUG_PNP 0x100 + +#define DEBUG_TRACE_X86EMU 0x1000 +// set to enable tracing of JMPs in x86emu +#define DEBUG_JMP 0x2000 + +//#define CONFIG_DEBUG +//#undef CONFIG_DEBUG +#ifdef CONFIG_DEBUG + +#define CHECK_DBG(_flag) if (debug_flags & _flag) + +#define DEBUG_PRINTF(_x...) printf(_x); +// prints the CS:IP before the printout, NOTE: actually its CS:IP of the _next_ instruction +// to be executed, since the x86emu advances CS:IP _before_ actually executing an instruction +#define DEBUG_PRINTF_CS_IP(_x...) DEBUG_PRINTF("%x:%x ", M.x86.R_CS, M.x86.R_IP); DEBUG_PRINTF(_x); + +#define DEBUG_PRINTF_IO(_x...) CHECK_DBG(DEBUG_IO) { DEBUG_PRINTF_CS_IP(_x) } +#define DEBUG_PRINTF_MEM(_x...) CHECK_DBG(DEBUG_MEM) { DEBUG_PRINTF_CS_IP(_x) } +#define DEBUG_PRINTF_INTR(_x...) CHECK_DBG(DEBUG_INTR) { DEBUG_PRINTF_CS_IP(_x) } +#define DEBUG_PRINTF_VBE(_x...) CHECK_DBG(DEBUG_VBE) { DEBUG_PRINTF_CS_IP(_x) } +#define DEBUG_PRINTF_PMM(_x...) CHECK_DBG(DEBUG_PMM) { DEBUG_PRINTF_CS_IP(_x) } +#define DEBUG_PRINTF_DISK(_x...) CHECK_DBG(DEBUG_DISK) { DEBUG_PRINTF_CS_IP(_x) } +#define DEBUG_PRINTF_PNP(_x...) CHECK_DBG(DEBUG_PNP) { DEBUG_PRINTF_CS_IP(_x) } + +#else + +#define CHECK_DBG(_flag) if (0) + +#define DEBUG_PRINTF(_x...) +#define DEBUG_PRINTF_CS_IP(_x...) + +#define DEBUG_PRINTF_IO(_x...) +#define DEBUG_PRINTF_MEM(_x...) +#define DEBUG_PRINTF_INTR(_x...) +#define DEBUG_PRINTF_VBE(_x...) +#define DEBUG_PRINTF_PMM(_x...) +#define DEBUG_PRINTF_DISK(_x...) +#define DEBUG_PRINTF_PNP(_x...) + +#endif //CONFIG_DEBUG + +void dump(u8 * addr, u32 len); + +#endif diff --git a/util/x86emu/yabel/device.c b/util/x86emu/yabel/device.c new file mode 100644 index 0000000000..d5c114ccb6 --- /dev/null +++ b/util/x86emu/yabel/device.c @@ -0,0 +1,461 @@ +/****************************************************************************** + * 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 "device.h" +#ifdef CONFIG_COREBOOT_V2 +#include "compat/rtas.h" +#else +#include "rtas.h" +#endif +#include <string.h> +#include "debug.h" + +#include <device/device.h> +#include <device/pci.h> +#include <device/pci_ops.h> +#include <device/resource.h> + +/* the device we are working with... */ +biosemu_device_t bios_device; +//max. 6 BARs and 1 Exp.ROM plus CfgSpace and 3 legacy ranges +translate_address_t translate_address_array[11]; +u8 taa_last_entry; + +typedef struct { + u8 info; + u8 bus; + u8 devfn; + u8 cfg_space_offset; + u64 address; + u64 size; +} __attribute__ ((__packed__)) assigned_address_t; + +#ifdef CONFIG_PCI_OPTION_ROM_RUN_YABEL +/* coreboot version */ + +void +biosemu_dev_get_addr_info(void) +{ + int taa_index = 0; + int i = 0; + struct resource *r; + u8 bus = bios_device.dev->bus->link; + u16 devfn = bios_device.dev->path.pci.devfn; + + bios_device.bus = bus; + bios_device.devfn = devfn; + + DEBUG_PRINTF("bus: %x, devfn: %x\n", bus, devfn); + for (i = 0; i < bios_device.dev->resources; i++) { + r = &bios_device.dev->resource[i]; + translate_address_array[taa_index].info = r->flags; + translate_address_array[taa_index].bus = bus; + translate_address_array[taa_index].devfn = devfn; + translate_address_array[taa_index].cfg_space_offset = + r->index; + translate_address_array[taa_index].address = r->base; + translate_address_array[taa_index].size = r->size; + /* dont translate addresses... all addresses are 1:1 */ + translate_address_array[taa_index].address_offset = 0; + taa_index++; + } + /* Expansion ROM */ + translate_address_array[taa_index].info = IORESOURCE_MEM | IORESOURCE_READONLY; + translate_address_array[taa_index].bus = bus; + translate_address_array[taa_index].devfn = devfn; + translate_address_array[taa_index].cfg_space_offset = 0x30; + translate_address_array[taa_index].address = bios_device.dev->rom_address; + translate_address_array[taa_index].size = 0; /* TODO: do we need the size? */ + /* dont translate addresses... all addresses are 1:1 */ + translate_address_array[taa_index].address_offset = 0; + taa_index++; + /* legacy ranges if its a VGA card... */ + if ((bios_device.dev->class & 0xFF0000) == 0x030000) { + DEBUG_PRINTF("%s: VGA device found, adding legacy resources... \n", __func__); + /* I/O 0x3B0-0x3BB */ + translate_address_array[taa_index].info = IORESOURCE_FIXED | IORESOURCE_IO; + translate_address_array[taa_index].bus = bus; + translate_address_array[taa_index].devfn = devfn; + translate_address_array[taa_index].cfg_space_offset = 0; + translate_address_array[taa_index].address = 0x3b0; + translate_address_array[taa_index].size = 0xc; + /* dont translate addresses... all addresses are 1:1 */ + translate_address_array[taa_index].address_offset = 0; + taa_index++; + /* I/O 0x3C0-0x3DF */ + translate_address_array[taa_index].info = IORESOURCE_FIXED | IORESOURCE_IO; + translate_address_array[taa_index].bus = bus; + translate_address_array[taa_index].devfn = devfn; + translate_address_array[taa_index].cfg_space_offset = 0; + translate_address_array[taa_index].address = 0x3c0; + translate_address_array[taa_index].size = 0x20; + /* dont translate addresses... all addresses are 1:1 */ + translate_address_array[taa_index].address_offset = 0; + taa_index++; + /* Mem 0xA0000-0xBFFFF */ + translate_address_array[taa_index].info = IORESOURCE_FIXED | IORESOURCE_MEM; + translate_address_array[taa_index].bus = bus; + translate_address_array[taa_index].devfn = devfn; + translate_address_array[taa_index].cfg_space_offset = 0; + translate_address_array[taa_index].address = 0xa0000; + translate_address_array[taa_index].size = 0x20000; + /* dont translate addresses... all addresses are 1:1 */ + translate_address_array[taa_index].address_offset = 0; + taa_index++; + } + // store last entry index of translate_address_array + taa_last_entry = taa_index - 1; +#ifdef CONFIG_DEBUG + //dump translate_address_array + printf("translate_address_array: \n"); + translate_address_t ta; + for (i = 0; i <= taa_last_entry; i++) { + ta = translate_address_array[i]; + printf + ("%d: info: %08lx bus: %02x devfn: %02x cfg_space_offset: %02x\n\taddr: %016llx\n\toffs: %016llx\n\tsize: %016llx\n", + i, ta.info, ta.bus, ta.devfn, ta.cfg_space_offset, + ta.address, ta.address_offset, ta.size); + } +#endif +} +#else +// use translate_address_dev and get_puid from net-snk's net_support.c +void translate_address_dev(u64 *, phandle_t); +u64 get_puid(phandle_t node); + + +// scan all adresses assigned to the device ("assigned-addresses" and "reg") +// store in translate_address_array for faster translation using dev_translate_address +void +biosemu_dev_get_addr_info(void) +{ + // get bus/dev/fn from assigned-addresses + int32_t len; + //max. 6 BARs and 1 Exp.ROM plus CfgSpace and 3 legacy ranges + assigned_address_t buf[11]; + len = + of_getprop(bios_device.phandle, "assigned-addresses", buf, + sizeof(buf)); + bios_device.bus = buf[0].bus; + bios_device.devfn = buf[0].devfn; + DEBUG_PRINTF("bus: %x, devfn: %x\n", bios_device.bus, + bios_device.devfn); + //store address translations for all assigned-addresses and regs in + //translate_address_array for faster translation later on... + int i = 0; + // index to insert data into translate_address_array + int taa_index = 0; + u64 address_offset; + for (i = 0; i < (len / sizeof(assigned_address_t)); i++, taa_index++) { + //copy all info stored in assigned-addresses + translate_address_array[taa_index].info = buf[i].info; + translate_address_array[taa_index].bus = buf[i].bus; + translate_address_array[taa_index].devfn = buf[i].devfn; + translate_address_array[taa_index].cfg_space_offset = + buf[i].cfg_space_offset; + translate_address_array[taa_index].address = buf[i].address; + translate_address_array[taa_index].size = buf[i].size; + // translate first address and store it as address_offset + address_offset = buf[i].address; + translate_address_dev(&address_offset, bios_device.phandle); + translate_address_array[taa_index].address_offset = + address_offset - buf[i].address; + } + //get "reg" property + len = of_getprop(bios_device.phandle, "reg", buf, sizeof(buf)); + for (i = 0; i < (len / sizeof(assigned_address_t)); i++) { + if ((buf[i].size == 0) || (buf[i].cfg_space_offset != 0)) { + // we dont care for ranges with size 0 and + // BARs and Expansion ROM must be in assigned-addresses... so in reg + // we only look for those without config space offset set... + // i.e. the legacy ranges + continue; + } + //copy all info stored in assigned-addresses + translate_address_array[taa_index].info = buf[i].info; + translate_address_array[taa_index].bus = buf[i].bus; + translate_address_array[taa_index].devfn = buf[i].devfn; + translate_address_array[taa_index].cfg_space_offset = + buf[i].cfg_space_offset; + translate_address_array[taa_index].address = buf[i].address; + translate_address_array[taa_index].size = buf[i].size; + // translate first address and store it as address_offset + address_offset = buf[i].address; + translate_address_dev(&address_offset, bios_device.phandle); + translate_address_array[taa_index].address_offset = + address_offset - buf[i].address; + taa_index++; + } + // store last entry index of translate_address_array + taa_last_entry = taa_index - 1; +#ifdef CONFIG_DEBUG + //dump translate_address_array + printf("translate_address_array: \n"); + translate_address_t ta; + for (i = 0; i <= taa_last_entry; i++) { + ta = translate_address_array[i]; + printf + ("%d: %02x%02x%02x%02x\n\taddr: %016llx\n\toffs: %016llx\n\tsize: %016llx\n", + i, ta.info, ta.bus, ta.devfn, ta.cfg_space_offset, + ta.address, ta.address_offset, ta.size); + } +#endif +} +#endif + +// to simulate accesses to legacy VGA Memory (0xA0000-0xBFFFF) +// we look for the first prefetchable memory BAR, if no prefetchable BAR found, +// we use the first memory BAR +// dev_translate_addr will translate accesses to the legacy VGA Memory into the found vmem BAR +void +biosemu_dev_find_vmem_addr(void) +{ + int i = 0; + translate_address_t ta; + s8 tai_np = -1, tai_p = -1; // translate_address_array index for non-prefetchable and prefetchable memory + //search backwards to find first entry + for (i = taa_last_entry; i >= 0; i--) { + ta = translate_address_array[i]; + if ((ta.cfg_space_offset >= 0x10) + && (ta.cfg_space_offset <= 0x24)) { + //only BARs + if ((ta.info & 0x03) >= 0x02) { + //32/64bit memory + tai_np = i; + if ((ta.info & 0x40) != 0) { + // prefetchable + tai_p = i; + } + } + } + } + if (tai_p != -1) { + ta = translate_address_array[tai_p]; + bios_device.vmem_addr = ta.address; + bios_device.vmem_size = ta.size; + DEBUG_PRINTF + ("%s: Found prefetchable Virtual Legacy Memory BAR: %llx, size: %llx\n", + __func__, bios_device.vmem_addr, + bios_device.vmem_size); + } else if (tai_np != -1) { + ta = translate_address_array[tai_np]; + bios_device.vmem_addr = ta.address; + bios_device.vmem_size = ta.size; + DEBUG_PRINTF + ("%s: Found non-prefetchable Virtual Legacy Memory BAR: %llx, size: %llx", + __func__, bios_device.vmem_addr, + bios_device.vmem_size); + } + // disable vmem + //bios_device.vmem_size = 0; +} + +#ifndef CONFIG_PCI_OPTION_ROM_RUN_YABEL +void +biosemu_dev_get_puid(void) +{ + // get puid + bios_device.puid = get_puid(bios_device.phandle); + DEBUG_PRINTF("puid: 0x%llx\n", bios_device.puid); +} +#endif + +void +biosemu_dev_get_device_vendor_id(void) +{ + + u32 pci_config_0; +#ifdef CONFIG_PCI_OPTION_ROM_RUN_YABEL + pci_config_0 = pci_read_config32(bios_device.dev, 0x0); +#else + pci_config_0 = + rtas_pci_config_read(bios_device.puid, 4, bios_device.bus, + bios_device.devfn, 0x0); +#endif + bios_device.pci_device_id = + (u16) ((pci_config_0 & 0xFFFF0000) >> 16); + bios_device.pci_vendor_id = (u16) (pci_config_0 & 0x0000FFFF); + DEBUG_PRINTF("PCI Device ID: %04x, PCI Vendor ID: %x\n", + bios_device.pci_device_id, bios_device.pci_vendor_id); +} + +/* Check whether the device has a valid Expansion ROM and search the PCI Data + * Structure and any Expansion ROM Header (using dev_scan_exp_header()) for + * needed information. If the rom_addr parameter is != 0, it is the address of + * the Expansion ROM image and will be used, if it is == 0, the Expansion ROM + * BAR address will be used. + */ +u8 +biosemu_dev_check_exprom(unsigned long rom_base_addr) +{ + int i = 0; + translate_address_t ta; + u16 pci_ds_offset; + pci_data_struct_t pci_ds; + if (rom_base_addr == 0) { + // check for ExpROM Address (Offset 30) in taa + for (i = 0; i <= taa_last_entry; i++) { + ta = translate_address_array[i]; + if (ta.cfg_space_offset == 0x30) { + //translated address + rom_base_addr = ta.address + ta.address_offset; + break; + } + } + } + /* In the ROM there could be multiple Expansion ROM Images... start + * searching them for an x86 image. + */ + do { + if (rom_base_addr == 0) { + printf("Error: no Expansion ROM address found!\n"); + return -1; + } + set_ci(); + u16 rom_signature = in16le((void *) rom_base_addr); + clr_ci(); + if (rom_signature != 0xaa55) { + printf + ("Error: invalid Expansion ROM signature: %02x!\n", + *((u16 *) rom_base_addr)); + return -1; + } + set_ci(); + // at offset 0x18 is the (16bit little-endian) pointer to the PCI Data Structure + pci_ds_offset = in16le((void *) (rom_base_addr + 0x18)); + //copy the PCI Data Structure + memcpy(&pci_ds, (void *) (rom_base_addr + pci_ds_offset), + sizeof(pci_ds)); + clr_ci(); +#ifdef CONFIG_DEBUG + DEBUG_PRINTF("PCI Data Structure @%lx:\n", + rom_base_addr + pci_ds_offset); + dump((void *) &pci_ds, sizeof(pci_ds)); +#endif + if (strncmp((const char *) pci_ds.signature, "PCIR", 4) != 0) { + printf("Invalid PCI Data Structure found!\n"); + break; + } + //little-endian conversion + pci_ds.vendor_id = in16le(&pci_ds.vendor_id); + pci_ds.device_id = in16le(&pci_ds.device_id); + pci_ds.img_length = in16le(&pci_ds.img_length); + pci_ds.pci_ds_length = in16le(&pci_ds.pci_ds_length); + if (pci_ds.vendor_id != bios_device.pci_vendor_id) { + printf + ("Image has invalid Vendor ID: %04x, expected: %04x\n", + pci_ds.vendor_id, bios_device.pci_vendor_id); + break; + } + if (pci_ds.device_id != bios_device.pci_device_id) { + printf + ("Image has invalid Device ID: %04x, expected: %04x\n", + pci_ds.device_id, bios_device.pci_device_id); + break; + } + DEBUG_PRINTF("Image Length: %d\n", pci_ds.img_length * 512); + DEBUG_PRINTF("Image Code Type: %d\n", pci_ds.code_type); + if (pci_ds.code_type == 0) { + //x86 image + //store image address and image length in bios_device struct + bios_device.img_addr = rom_base_addr; + bios_device.img_size = pci_ds.img_length * 512; + // we found the image, exit the loop + break; + } else { + // no x86 image, check next image (if any) + rom_base_addr += pci_ds.img_length * 512; + } + if ((pci_ds.indicator & 0x80) == 0x80) { + //last image found, exit the loop + DEBUG_PRINTF("Last PCI Expansion ROM Image found.\n"); + break; + } + } + while (bios_device.img_addr == 0); + // in case we did not find a valid x86 Expansion ROM Image + if (bios_device.img_addr == 0) { + printf("Error: no valid x86 Expansion ROM Image found!\n"); + return -1; + } + return 0; +} + +u8 +biosemu_dev_init(struct device * device) +{ + u8 rval = 0; + //init bios_device struct +#ifdef CONFIG_COREBOOT_V2 + DEBUG_PRINTF("%s\n", __func__); +#else + DEBUG_PRINTF("%s(%s)\n", __func__, device->dtsname); +#endif + memset(&bios_device, 0, sizeof(bios_device)); + +#ifndef CONFIG_PCI_OPTION_ROM_RUN_YABEL + bios_device.ihandle = of_open(device_name); + if (bios_device.ihandle == 0) { + DEBUG_PRINTF("%s is no valid device!\n", device_name); + return -1; + } + bios_device.phandle = of_finddevice(device_name); +#else + bios_device.dev = device; +#endif + biosemu_dev_get_addr_info(); +#ifndef CONFIG_PCI_OPTION_ROM_RUN_YABEL + biosemu_dev_find_vmem_addr(); + biosemu_dev_get_puid(); +#endif + biosemu_dev_get_device_vendor_id(); + return rval; +} + +// translate address function using translate_address_array assembled +// by dev_get_addr_info... MUCH faster than calling translate_address_dev +// and accessing client interface for every translation... +// returns: 0 if addr not found in translate_address_array, 1 if found. +u8 +biosemu_dev_translate_address(unsigned long * addr) +{ + int i = 0; + translate_address_t ta; +#ifndef CONFIG_PCI_OPTION_ROM_RUN_YABEL + /* we dont need this hack for coreboot... we can access legacy areas */ + //check if it is an access to legacy VGA Mem... if it is, map the address + //to the vmem BAR and then translate it... + // (translation info provided by Ben Herrenschmidt) + // NOTE: the translation seems to only work for NVIDIA cards... but it is needed + // to make some NVIDIA cards work at all... + if ((bios_device.vmem_size > 0) + && ((*addr >= 0xA0000) && (*addr < 0xB8000))) { + *addr = (*addr - 0xA0000) * 4 + 2 + bios_device.vmem_addr; + } + if ((bios_device.vmem_size > 0) + && ((*addr >= 0xB8000) && (*addr < 0xC0000))) { + u8 shift = *addr & 1; + *addr &= 0xfffffffe; + *addr = (*addr - 0xB8000) * 4 + shift + bios_device.vmem_addr; + } +#endif + for (i = 0; i <= taa_last_entry; i++) { + ta = translate_address_array[i]; + if ((*addr >= ta.address) && (*addr <= (ta.address + ta.size))) { + *addr += ta.address_offset; + return 1; + } + } + return 0; +} diff --git a/util/x86emu/yabel/device.h b/util/x86emu/yabel/device.h new file mode 100644 index 0000000000..b350341947 --- /dev/null +++ b/util/x86emu/yabel/device.h @@ -0,0 +1,188 @@ +/****************************************************************************** + * 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 + *****************************************************************************/ + +#ifndef DEVICE_LIB_H +#define DEVICE_LIB_H + +#include <types.h> +#ifdef CONFIG_COREBOOT_V2 +#include <arch/byteorder.h> +#include "compat/of.h" +#else +#include <cpu.h> +#include <byteorder.h> +#include "of.h" +#endif +#include "debug.h" + + +// a Expansion Header Struct as defined in Plug and Play BIOS Spec 1.0a Chapter 3.2 +typedef struct { + char signature[4]; // signature + u8 structure_revision; + u8 length; // in 16 byte blocks + u16 next_header_offset; // offset to next Expansion Header as 16bit little-endian value, as offset from the start of the Expansion ROM + u8 reserved; + u8 checksum; // the sum of all bytes of the Expansion Header must be 0 + u32 device_id; // PnP Device ID as 32bit little-endian value + u16 p_manufacturer_string; //16bit little-endian offset from start of Expansion ROM + u16 p_product_string; //16bit little-endian offset from start of Expansion ROM + u8 device_base_type; + u8 device_sub_type; + u8 device_if_type; + u8 device_indicators; + // the following vectors are all 16bit little-endian offsets from start of Expansion ROM + u16 bcv; // Boot Connection Vector + u16 dv; // Disconnect Vector + u16 bev; // Bootstrap Entry Vector + u16 reserved_2; + u16 sriv; // Static Resource Information Vector +} __attribute__ ((__packed__)) exp_header_struct_t; + +// a PCI Data Struct as defined in PCI 2.3 Spec Chapter 6.3.1.2 +typedef struct { + u8 signature[4]; // signature, the String "PCIR" + u16 vendor_id; + u16 device_id; + u16 reserved; + u16 pci_ds_length; // PCI Data Structure Length, 16bit little-endian value + u8 pci_ds_revision; + u8 class_code[3]; + u16 img_length; // length of the Exp.ROM Image, 16bit little-endian value in 512 bytes + u16 img_revision; + u8 code_type; + u8 indicator; + u16 reserved_2; +} __attribute__ ((__packed__)) pci_data_struct_t; + +typedef struct { + u8 bus; + u8 devfn; +#ifdef CONFIG_PCI_OPTION_ROM_RUN_YABEL + struct device* dev; +#else + u64 puid; + phandle_t phandle; + ihandle_t ihandle; +#endif + // store the address of the BAR that is used to simulate + // legacy VGA memory accesses + u64 vmem_addr; + u64 vmem_size; + // used to buffer I/O Accesses, that do not access the I/O Range of the device... + // 64k might be overkill, but we can buffer all I/O accesses... + u8 io_buffer[64 * 1024]; + u16 pci_vendor_id; + u16 pci_device_id; + // translated address of the "PC-Compatible" Expansion ROM Image for this device + unsigned long img_addr; + u32 img_size; // size of the Expansion ROM Image (read from the PCI Data Structure) +} biosemu_device_t; + +typedef struct { +#ifdef CONFIG_PCI_OPTION_ROM_RUN_YABEL + unsigned long info; +#else + u8 info; +#endif + u8 bus; + u8 devfn; + u8 cfg_space_offset; + u64 address; + u64 address_offset; + u64 size; +} __attribute__ ((__packed__)) translate_address_t; + +// array to store address translations for this +// device. Needed for faster address translation, so +// not every I/O or Memory Access needs to call translate_address_dev +// and access the device tree +// 6 BARs, 1 Exp. ROM, 1 Cfg.Space, and 3 Legacy +// translations are supported... this should be enough for +// most devices... for VGA it is enough anyways... +extern translate_address_t translate_address_array[11]; + +// index of last translate_address_array entry +// set by get_dev_addr_info function +extern u8 taa_last_entry; + +/* the device we are working with... */ +extern biosemu_device_t bios_device; + +u8 biosemu_dev_init(struct device * device); +// NOTE: for dev_check_exprom to work, biosemu_dev_init MUST be called first! +u8 biosemu_dev_check_exprom(unsigned long rom_base_addr); + +u8 biosemu_dev_translate_address(unsigned long * addr); + +/* endianness swap functions for 16 and 32 bit words + * copied from axon_pciconfig.c + */ +static inline void +out32le(void *addr, u32 val) +{ +#ifdef __i386 + *((u32*) addr) = cpu_to_le32(val); +#else + asm volatile ("stwbrx %0, 0, %1"::"r" (val), "r"(addr)); +#endif +} + +static inline u32 +in32le(void *addr) +{ + u32 val; +#ifdef __i386 + val = cpu_to_le32(*((u32 *) addr)); +#else + asm volatile ("lwbrx %0, 0, %1":"=r" (val):"r"(addr)); +#endif + return val; +} + +static inline void +out16le(void *addr, u16 val) +{ +#ifdef __i386 + *((u16*) addr) = cpu_to_le16(val); +#else + asm volatile ("sthbrx %0, 0, %1"::"r" (val), "r"(addr)); +#endif +} + +static inline u16 +in16le(void *addr) +{ + u16 val; +#ifdef __i386 + val = cpu_to_le16(*((u16*) addr)); +#else + asm volatile ("lhbrx %0, 0, %1":"=r" (val):"r"(addr)); +#endif + return val; +} + +/* debug function, dumps HID1 and HID4 to detect whether caches are on/off */ +static inline void +dumpHID(void) +{ + u64 hid; + //HID1 = 1009 + __asm__ __volatile__("mfspr %0, 1009":"=r"(hid)); + printf("HID1: %016llx\n", hid); + //HID4 = 1012 + __asm__ __volatile__("mfspr %0, 1012":"=r"(hid)); + printf("HID4: %016llx\n", hid); +} + +#endif diff --git a/util/x86emu/yabel/interrupt.c b/util/x86emu/yabel/interrupt.c new file mode 100644 index 0000000000..5542588571 --- /dev/null +++ b/util/x86emu/yabel/interrupt.c @@ -0,0 +1,687 @@ +/****************************************************************************** + * 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 + *****************************************************************************/ + +#ifdef CONFIG_COREBOOT_V2 +#include "compat/rtas.h" +#else +#include <rtas.h> +#endif + +#include "biosemu.h" +#include "mem.h" +#include "device.h" +#include "debug.h" +#include "pmm.h" + +#include <x86emu/x86emu.h> +#ifdef CONFIG_COREBOOT_V2 +#include "../x86emu/prim_ops.h" +#else +#include <x86emu/prim_ops.h> +#endif + +#ifdef CONFIG_PCI_OPTION_ROM_RUN_YABEL +#include <device/pci.h> +#include <device/pci_ops.h> +#endif + + +//setup to run the code at the address, that the Interrupt Vector points to... +void +setupInt(int intNum) +{ + DEBUG_PRINTF_INTR("%s(%x): executing interrupt handler @%08x\n", + __func__, intNum, my_rdl(intNum * 4)); + // push current R_FLG... will be popped by IRET + push_word((u16) M.x86.R_FLG); + CLEAR_FLAG(F_IF); + CLEAR_FLAG(F_TF); + // push current CS:IP to the stack, will be popped by IRET + push_word(M.x86.R_CS); + push_word(M.x86.R_IP); + // set CS:IP to the interrupt handler address... so the next executed instruction will + // be the interrupt handler + M.x86.R_CS = my_rdw(intNum * 4 + 2); + M.x86.R_IP = my_rdw(intNum * 4); +} + +// handle int10 (VGA BIOS Interrupt) +void +handleInt10(void) +{ + // the data for INT10 is stored in BDA (0000:0400h) offset 49h-66h + // function number in AH + //DEBUG_PRINTF_CS_IP("%s:\n", __func__); + //x86emu_dump_xregs(); + //if ((M.x86.R_IP == 0x32c2) && (M.x86.R_SI == 0x1ce2)){ + //X86EMU_trace_on(); + //M.x86.debug &= ~DEBUG_DECODE_NOPRINT_F; + //} + switch (M.x86.R_AH) { + case 0x00: + // set video mode + // BDA offset 49h is current video mode + my_wrb(0x449, M.x86.R_AL); + if (M.x86.R_AL > 7) + M.x86.R_AL = 0x20; + else if (M.x86.R_AL == 6) + M.x86.R_AL = 0x3f; + else + M.x86.R_AL = 0x30; + break; + case 0x01: + // set cursor shape + // ignore + break; + case 0x02: + // set cursor position + // BH: pagenumber, DX: cursor_pos (DH:row, DL:col) + // BDA offset 50h-60h are 8 cursor position words for + // eight possible video pages + my_wrw(0x450 + (M.x86.R_BH * 2), M.x86.R_DX); + break; + case 0x03: + //get cursor position + // BH: pagenumber + // BDA offset 50h-60h are 8 cursor position words for + // eight possible video pages + M.x86.R_AX = 0; + M.x86.R_CH = 0; // start scan line ??? + M.x86.R_CL = 0; // end scan line ??? + M.x86.R_DX = my_rdw(0x450 + (M.x86.R_BH * 2)); + break; + case 0x05: + // set active page + // BDA offset 62h is current page number + my_wrb(0x462, M.x86.R_AL); + break; + case 0x06: + //scroll up windows + break; + case 0x07: + //scroll down windows + break; + case 0x08: + //read character and attribute at position + M.x86.R_AH = 0x07; // white-on-black + M.x86.R_AL = 0x20; // a space... + break; + case 0x09: + // write character and attribute + //AL: char, BH: page number, BL: attribute, CX: number of times to write + //BDA offset 62h is current page number + CHECK_DBG(DEBUG_PRINT_INT10) { + u32 i = 0; + if (M.x86.R_BH == my_rdb(0x462)) { + for (i = 0; i < M.x86.R_CX; i++) + printf("%c", M.x86.R_AL); + } + } + break; + case 0x0a: + // write character + //AL: char, BH: page number, BL: attribute, CX: number of times to write + //BDA offset 62h is current page number + CHECK_DBG(DEBUG_PRINT_INT10) { + u32 i = 0; + if (M.x86.R_BH == my_rdb(0x462)) { + for (i = 0; i < M.x86.R_CX; i++) + printf("%c", M.x86.R_AL); + } + } + break; + case 0x0e: + // teletype output: write character and advance cursor... + //AL: char, BH: page number, BL: attribute + //BDA offset 62h is current page number + CHECK_DBG(DEBUG_PRINT_INT10) { + // we ignore the pagenumber on this call... + //if (M.x86.R_BH == my_rdb(0x462)) + { + printf("%c", M.x86.R_AL); + // for debugging, to read all lines + //if (M.x86.R_AL == 0xd) // carriage return + // printf("\n"); + } + } + break; + case 0x0f: + // get video mode + // BDA offset 49h is current video mode + // BDA offset 62h is current page number + // BDA offset 4ah is columns on screen + M.x86.R_AH = 80; //number of character columns... we hardcode it to 80 + M.x86.R_AL = my_rdb(0x449); + M.x86.R_BH = my_rdb(0x462); + break; + default: + printf("%s(): unknown function (%x) for int10 handler.\n", + __func__, M.x86.R_AH); + DEBUG_PRINTF_INTR("AX=%04x BX=%04x CX=%04x DX=%04x\n", + M.x86.R_AX, M.x86.R_BX, M.x86.R_CX, + M.x86.R_DX); + HALT_SYS(); + break; + } +} + +// this table translates ASCII chars into their XT scan codes: +static u8 keycode_table[256] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0 - 7 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 8 - 15 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 16 - 23 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 24 - 31 + 0x39, 0x02, 0x28, 0x04, 0x05, 0x06, 0x08, 0x28, // 32 - 39 + 0x0a, 0x0b, 0x09, 0x2b, 0x33, 0x0d, 0x34, 0x35, // 40 - 47 + 0x0b, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // 48 - 55 + 0x09, 0x0a, 0x27, 0x27, 0x33, 0x2b, 0x34, 0x35, // 56 - 63 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 64 - 71 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 72 - 79 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 80 - 87 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 88 - 95 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 96 - 103 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 104 - 111 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 112 - 119 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 120 - 127 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ... + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +} + +; + +void +translate_keycode(u64 * keycode) +{ + u8 scan_code = 0; + u8 char_code = 0; + if (*keycode < 256) { + scan_code = keycode_table[*keycode]; + char_code = (u8) * keycode & 0xff; + } else { + switch (*keycode) { + case 0x1b50: + // F1 + scan_code = 0x3b; + char_code = 0x0; + break; + default: + printf("%s(): unknown multibyte keycode: %llx\n", + __func__, *keycode); + break; + } + } + //assemble scan/char code in keycode + *keycode = (u64) ((((u16) scan_code) << 8) | char_code); +} + +// handle int16 (Keyboard BIOS Interrupt) +void +handleInt16(void) +{ + // keyboard buffer is in BIOS Memory Area: + // offset 0x1a (WORD) pointer to next char in keybuffer + // offset 0x1c (WORD) pointer to next insert slot in keybuffer + // offset 0x1e-0x3e: 16 WORD Ring Buffer + // since we currently always read the char from the FW buffer, + // we misuse the ring buffer, we use it as pointer to a u64 that stores + // multi-byte keys (e.g. special keys in VT100 terminal) + // and as long as a key is available (not 0) we dont read further keys + u64 *keycode = (u64 *) (M.mem_base + 0x41e); + s8 c; + // function number in AH + DEBUG_PRINTF_INTR("%s(): Keyboard Interrupt: function: %x.\n", + __func__, M.x86.R_AH); + DEBUG_PRINTF_INTR("AX=%04x BX=%04x CX=%04x DX=%04x\n", M.x86.R_AX, + M.x86.R_BX, M.x86.R_CX, M.x86.R_DX); + switch (M.x86.R_AH) { + case 0x00: + // get keystroke + if (*keycode) { + M.x86.R_AX = (u16) * keycode; + // clear keycode + *keycode = 0; + } else { + M.x86.R_AH = 0x61; // scancode for space key + M.x86.R_AL = 0x20; // a space + } + break; + case 0x01: + // check keystroke + // ZF set = no keystroke + // read first byte of key code + if (*keycode) { + // already read, but not yet taken + CLEAR_FLAG(F_ZF); + M.x86.R_AX = (u16) * keycode; + } else { + /* TODO: we need getchar... */ + c = -1; //getchar(); + if (c == -1) { + // no key available + SET_FLAG(F_ZF); + } else { + *keycode = c; + + // since after an ESC it may take a while to receive the next char, + // we send something that is not shown on the screen, and then try to get + // the next char + // TODO: only after ESC?? what about other multibyte keys + printf("tt%c%c", 0x08, 0x08); // 0x08 == Backspace + + /* TODO: we need getchar... */ + while ((c = -1 /*getchar()*/) != -1) { + *keycode = (*keycode << 8) | c; + DEBUG_PRINTF(" key read: %0llx\n", + *keycode); + } + translate_keycode(keycode); + DEBUG_PRINTF(" translated key: %0llx\n", + *keycode); + if (*keycode == 0) { + //not found + SET_FLAG(F_ZF); + } else { + CLEAR_FLAG(F_ZF); + M.x86.R_AX = (u16) * keycode; + //X86EMU_trace_on(); + //M.x86.debug &= ~DEBUG_DECODE_NOPRINT_F; + } + } + } + break; + default: + printf("%s(): unknown function (%x) for int16 handler.\n", + __func__, M.x86.R_AH); + DEBUG_PRINTF_INTR("AX=%04x BX=%04x CX=%04x DX=%04x\n", + M.x86.R_AX, M.x86.R_BX, M.x86.R_CX, + M.x86.R_DX); + HALT_SYS(); + break; + } +} + +// handle int1a (PCI BIOS Interrupt) +void +handleInt1a(void) +{ + // function number in AX + u8 bus, devfn, offs; + struct device* dev; + switch (M.x86.R_AX) { + case 0xb101: + // Installation check + CLEAR_FLAG(F_CF); // clear CF + M.x86.R_EDX = 0x20494350; // " ICP" endian swapped "PCI " + M.x86.R_AL = 0x1; // Config Space Mechanism 1 supported + M.x86.R_BX = 0x0210; // PCI Interface Level Version 2.10 + M.x86.R_CL = 0xff; // number of last PCI Bus in system TODO: check! + break; + case 0xb102: + // Find PCI Device + // device_id in CX, vendor_id in DX + // device index in SI (i.e. if multiple devices with same vendor/device id + // are connected). We currently only support device index 0 + // + DEBUG_PRINTF_INTR("%s(): function: %x: PCI Find Device\n", + __func__, M.x86.R_AX); + /* FixME: support SI != 0 */ +#if defined(CONFIG_YABEL_PCI_ACCESS_OTHER_DEVICES) && CONFIG_YABEL_PCI_ACCESS_OTHER_DEVICES==1 +#ifdef CONFIG_COREBOOT_V2 + dev = dev_find_device(M.x86.R_DX, M.x86.R_CX, 0); +#else + dev = dev_find_pci_device(M.x86.R_DX, M.x86.R_CX, 0); +#endif + if (dev != 0) { + DEBUG_PRINTF_INTR + ("%s(): function %x: PCI Find Device --> 0x%04x\n", + __func__, M.x86.R_AX, M.x86.R_BX); + + M.x86.R_BH = dev->bus->secondary; + M.x86.R_BL = dev->path.pci.devfn; + M.x86.R_AH = 0x00; // return code: success + CLEAR_FLAG(F_CF); +#else + // only allow the device to find itself... + if ((M.x86.R_CX == bios_device.pci_device_id) + && (M.x86.R_DX == bios_device.pci_vendor_id) + // device index must be 0 + && (M.x86.R_SI == 0)) { + CLEAR_FLAG(F_CF); + M.x86.R_AH = 0x00; // return code: success + M.x86.R_BH = bios_device.bus; + M.x86.R_BL = bios_device.devfn; +#endif + } else { + DEBUG_PRINTF_INTR + ("%s(): function %x: invalid device/vendor/device index! (%04x/%04x/%02x expected: %04x/%04x/00) \n", + __func__, M.x86.R_AX, M.x86.R_CX, M.x86.R_DX, + M.x86.R_SI, bios_device.pci_device_id, + bios_device.pci_vendor_id); + + SET_FLAG(F_CF); + M.x86.R_AH = 0x86; // return code: device not found + } + break; + case 0xb108: //read configuration byte + case 0xb109: //read configuration word + case 0xb10a: //read configuration dword + bus = M.x86.R_BH; + devfn = M.x86.R_BL; + offs = M.x86.R_DI; + DEBUG_PRINTF_INTR("%s(): function: %x: PCI Config Read from device: bus: %02x, devfn: %02x, offset: %02x\n", + __func__, M.x86.R_AX, bus, devfn, offs); +#if defined(CONFIG_YABEL_PCI_ACCESS_OTHER_DEVICES) && CONFIG_YABEL_PCI_ACCESS_OTHER_DEVICES==1 + dev = dev_find_slot(bus, devfn); + DEBUG_PRINTF_INTR("%s(): function: %x: dev_find_slot() returned: %s\n", + __func__, M.x86.R_AX, dev_path(dev)); + if (dev == 0) { + // fail accesses to non-existent devices... +#else + dev = bios_device.dev; + if ((bus != bios_device.bus) + || (devfn != bios_device.devfn)) { + // fail accesses to any device but ours... +#endif + printf + ("%s(): Config read access invalid device! bus: %02x (%02x), devfn: %02x (%02x), offs: %02x\n", + __func__, bus, bios_device.bus, devfn, + bios_device.devfn, offs); + SET_FLAG(F_CF); + M.x86.R_AH = 0x87; //return code: bad pci register + HALT_SYS(); + return; + } else { + switch (M.x86.R_AX) { + case 0xb108: + M.x86.R_CL = +#ifdef CONFIG_PCI_OPTION_ROM_RUN_YABEL + pci_read_config8(dev, offs); +#else + (u8) rtas_pci_config_read(bios_device. + puid, 1, + bus, devfn, + offs); +#endif + DEBUG_PRINTF_INTR + ("%s(): function %x: PCI Config Read @%02x --> 0x%02x\n", + __func__, M.x86.R_AX, offs, + M.x86.R_CL); + break; + case 0xb109: + M.x86.R_CX = +#ifdef CONFIG_PCI_OPTION_ROM_RUN_YABEL + pci_read_config16(dev, offs); +#else + (u16) rtas_pci_config_read(bios_device. + puid, 2, + bus, devfn, + offs); +#endif + DEBUG_PRINTF_INTR + ("%s(): function %x: PCI Config Read @%02x --> 0x%04x\n", + __func__, M.x86.R_AX, offs, + M.x86.R_CX); + break; + case 0xb10a: + M.x86.R_ECX = +#ifdef CONFIG_PCI_OPTION_ROM_RUN_YABEL + pci_read_config32(dev, offs); +#else + (u32) rtas_pci_config_read(bios_device. + puid, 4, + bus, devfn, + offs); +#endif + DEBUG_PRINTF_INTR + ("%s(): function %x: PCI Config Read @%02x --> 0x%08x\n", + __func__, M.x86.R_AX, offs, + M.x86.R_ECX); + break; + } + CLEAR_FLAG(F_CF); + M.x86.R_AH = 0x0; // return code: success + } + break; + case 0xb10b: //write configuration byte + case 0xb10c: //write configuration word + case 0xb10d: //write configuration dword + bus = M.x86.R_BH; + devfn = M.x86.R_BL; + offs = M.x86.R_DI; + if ((bus != bios_device.bus) + || (devfn != bios_device.devfn)) { + // fail accesses to any device but ours... + printf + ("%s(): Config read access invalid! bus: %x (%x), devfn: %x (%x), offs: %x\n", + __func__, bus, bios_device.bus, devfn, + bios_device.devfn, offs); + SET_FLAG(F_CF); + M.x86.R_AH = 0x87; //return code: bad pci register + HALT_SYS(); + return; + } else { + switch (M.x86.R_AX) { + case 0xb10b: +#ifdef CONFIG_PCI_OPTION_ROM_RUN_YABEL + pci_write_config8(bios_device.dev, offs, M.x86.R_CL); +#else + rtas_pci_config_write(bios_device.puid, 1, bus, + devfn, offs, M.x86.R_CL); +#endif + DEBUG_PRINTF_INTR + ("%s(): function %x: PCI Config Write @%02x <-- 0x%02x\n", + __func__, M.x86.R_AX, offs, + M.x86.R_CL); + break; + case 0xb10c: +#ifdef CONFIG_PCI_OPTION_ROM_RUN_YABEL + pci_write_config16(bios_device.dev, offs, M.x86.R_CX); +#else + rtas_pci_config_write(bios_device.puid, 2, bus, + devfn, offs, M.x86.R_CX); +#endif + DEBUG_PRINTF_INTR + ("%s(): function %x: PCI Config Write @%02x <-- 0x%04x\n", + __func__, M.x86.R_AX, offs, + M.x86.R_CX); + break; + case 0xb10d: +#ifdef CONFIG_PCI_OPTION_ROM_RUN_YABEL + pci_write_config32(bios_device.dev, offs, M.x86.R_ECX); +#else + rtas_pci_config_write(bios_device.puid, 4, bus, + devfn, offs, M.x86.R_ECX); +#endif + DEBUG_PRINTF_INTR + ("%s(): function %x: PCI Config Write @%02x <-- 0x%08x\n", + __func__, M.x86.R_AX, offs, + M.x86.R_ECX); + break; + } + CLEAR_FLAG(F_CF); + M.x86.R_AH = 0x0; // return code: success + } + break; + default: + printf("%s(): unknown function (%x) for int1a handler.\n", + __func__, M.x86.R_AX); + DEBUG_PRINTF_INTR("AX=%04x BX=%04x CX=%04x DX=%04x\n", + M.x86.R_AX, M.x86.R_BX, M.x86.R_CX, + M.x86.R_DX); + HALT_SYS(); + break; + } +} + +// main Interrupt Handler routine, should be registered as x86emu interrupt handler +void +handleInterrupt(int intNum) +{ + u8 int_handled = 0; +#ifndef DEBUG_PRINT_INT10 + // this printf makes output by int 10 unreadable... + // so we only enable it, if int10 print is disabled + DEBUG_PRINTF_INTR("%s(%x)\n", __func__, intNum); +#endif + + /* check wether this interrupt has a function pointer set in yabel_intFuncArray and run that */ + if (yabel_intFuncArray[intNum]) { + DEBUG_PRINTF_INTR("%s(%x) intHandler overridden, calling it...\n", __func__, intNum); + int_handled = (*yabel_intFuncArray[intNum])(); + } else { + switch (intNum) { + case 0x10: //BIOS video interrupt + case 0x42: // INT 10h relocated by EGA/VGA BIOS + case 0x6d: // INT 10h relocated by VGA BIOS + // get interrupt vector from IDT (4 bytes per Interrupt starting at address 0 + if ((my_rdl(intNum * 4) == 0xF000F065) || //F000:F065 is default BIOS interrupt handler address + (my_rdl(intNum * 4) == 0xF4F4F4F4)) //invalid + { +#if 0 + // ignore interrupt... + DEBUG_PRINTF_INTR + ("%s(%x): invalid interrupt Vector (%08x) found, interrupt ignored...\n", + __func__, intNum, my_rdl(intNum * 4)); + DEBUG_PRINTF_INTR("AX=%04x BX=%04x CX=%04x DX=%04x\n", + M.x86.R_AX, M.x86.R_BX, M.x86.R_CX, + M.x86.R_DX); + //HALT_SYS(); +#endif + handleInt10(); + int_handled = 1; + } + break; + case 0x16: + // Keyboard BIOS Interrupt + handleInt16(); + int_handled = 1; + break; + case 0x1a: + // PCI BIOS Interrupt + handleInt1a(); + int_handled = 1; + break; + case PMM_INT_NUM: + /* the selfdefined PMM INT number, this is called by the code in PMM struct, it + * is handled by pmm_handleInt() + */ + pmm_handleInt(); + int_handled = 1; + break; + default: + printf("Interrupt %#x (Vector: %x) not implemented\n", intNum, + my_rdl(intNum * 4)); + DEBUG_PRINTF_INTR("AX=%04x BX=%04x CX=%04x DX=%04x\n", + M.x86.R_AX, M.x86.R_BX, M.x86.R_CX, + M.x86.R_DX); + int_handled = 1; + HALT_SYS(); + break; + } + } + // if we did not handle the interrupt, jump to the interrupt vector... + if (!int_handled) { + setupInt(intNum); + } +} + +// prepare and execute Interrupt 10 (VGA Interrupt) +void +runInt10(void) +{ + // Initialize stack and data segment + M.x86.R_SS = STACK_SEGMENT; + M.x86.R_DS = DATA_SEGMENT; + M.x86.R_SP = STACK_START_OFFSET; + + // 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); + + // setupInt will push the current CS and IP to the stack to return to it, + // but we want to halt, so set CS:IP to the HLT instruction we just pushed + // to the stack + M.x86.R_CS = M.x86.R_SS; + M.x86.R_IP = M.x86.R_SP; // + 4; + + CHECK_DBG(DEBUG_TRACE_X86EMU) { + X86EMU_trace_on(); + } + CHECK_DBG(DEBUG_JMP) { + M.x86.debug |= DEBUG_TRACEJMP_REGS_F; + M.x86.debug |= DEBUG_TRACEJMP_REGS_F; + M.x86.debug |= DEBUG_TRACECALL_F; + M.x86.debug |= DEBUG_TRACECALL_REGS_F; + } + setupInt(0x10); + DEBUG_PRINTF_INTR("%s(): starting execution of INT10...\n", + __func__); + X86EMU_exec(); + DEBUG_PRINTF_INTR("%s(): execution finished\n", __func__); +} + +// prepare and execute Interrupt 13 (Disk Interrupt) +void +runInt13(void) +{ + // Initialize stack and data segment + M.x86.R_SS = STACK_SEGMENT; + M.x86.R_DS = DATA_SEGMENT; + M.x86.R_SP = STACK_START_OFFSET; + + // 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); + + // setupInt will push the current CS and IP to the stack to return to it, + // but we want to halt, so set CS:IP to the HLT instruction we just pushed + // to the stack + M.x86.R_CS = M.x86.R_SS; + M.x86.R_IP = M.x86.R_SP; + + CHECK_DBG(DEBUG_TRACE_X86EMU) { + X86EMU_trace_on(); + } + CHECK_DBG(DEBUG_JMP) { + M.x86.debug |= DEBUG_TRACEJMP_REGS_F; + M.x86.debug |= DEBUG_TRACEJMP_REGS_F; + M.x86.debug |= DEBUG_TRACECALL_F; + M.x86.debug |= DEBUG_TRACECALL_REGS_F; + } + + setupInt(0x13); + DEBUG_PRINTF_INTR("%s(): starting execution of INT13...\n", + __func__); + X86EMU_exec(); + DEBUG_PRINTF_INTR("%s(): execution finished\n", __func__); +} diff --git a/util/x86emu/yabel/interrupt.h b/util/x86emu/yabel/interrupt.h new file mode 100644 index 0000000000..11755e102a --- /dev/null +++ b/util/x86emu/yabel/interrupt.h @@ -0,0 +1,21 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * 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 + *****************************************************************************/ +#ifndef _BIOSEMU_INTERRUPT_H_ +#define _BIOSEMU_INTERRUPT_H_ + +void handleInterrupt(int intNum); + +void runInt10(void); + +void runInt13(void); + +#endif diff --git a/util/x86emu/yabel/io.c b/util/x86emu/yabel/io.c new file mode 100644 index 0000000000..16a8b58331 --- /dev/null +++ b/util/x86emu/yabel/io.c @@ -0,0 +1,483 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * Copyright (c) 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 <types.h> +#ifdef CONFIG_COREBOOT_V2 +#include "compat/rtas.h" +#include "compat/time.h" +#else +#include <cpu.h> +#include "rtas.h" +#include <time.h> +#endif +#include "device.h" +#include "debug.h" +#include <x86emu/x86emu.h> + +#ifdef CONFIG_PCI_OPTION_ROM_RUN_YABEL +#include <device/pci.h> +#include <device/pci_ops.h> +#endif + +// those are defined in net-snk/oflib/pci.c +extern unsigned int read_io(void *, size_t); +extern int write_io(void *, unsigned int, size_t); + +//defined in net-snk/kernel/timer.c +extern u64 get_time(void); + +// these are not used, only needed for linking, must be overridden using X86emu_setupPioFuncs +// with the functions and struct below +void +outb(u8 val, u16 port) +{ + printf("WARNING: outb not implemented!\n"); + HALT_SYS(); +} + +void +outw(u16 val, u16 port) +{ + printf("WARNING: outw not implemented!\n"); + HALT_SYS(); +} + +void +outl(u32 val, u16 port) +{ + printf("WARNING: outl not implemented!\n"); + HALT_SYS(); +} + +u8 +inb(u16 port) +{ + printf("WARNING: inb not implemented!\n"); + HALT_SYS(); + return 0; +} + +u16 +inw(u16 port) +{ + printf("WARNING: inw not implemented!\n"); + HALT_SYS(); + return 0; +} + +u32 +inl(u16 port) +{ + printf("WARNING: inl not implemented!\n"); + HALT_SYS(); + return 0; +} + +u32 pci_cfg_read(X86EMU_pioAddr addr, u8 size); +void pci_cfg_write(X86EMU_pioAddr addr, u32 val, u8 size); +u8 handle_port_61h(void); + +u8 +my_inb(X86EMU_pioAddr addr) +{ + u8 rval = 0xFF; + unsigned long translated_addr = addr; + u8 translated = biosemu_dev_translate_address(&translated_addr); + if (translated != 0) { + //translation successfull, access Device I/O (BAR or Legacy...) + DEBUG_PRINTF_IO("%s(%x): access to Device I/O\n", __func__, + addr); + //DEBUG_PRINTF_IO("%s(%04x): translated_addr: %llx\n", __func__, addr, translated_addr); + rval = read_io((void *)translated_addr, 1); + DEBUG_PRINTF_IO("%s(%04x) Device I/O --> %02x\n", __func__, + addr, rval); + return rval; + } else { + switch (addr) { + case 0x61: + //8254 KB Controller / Timer Port + rval = handle_port_61h(); + //DEBUG_PRINTF_IO("%s(%04x) KB / Timer Port B --> %02x\n", __func__, addr, rval); + return rval; + break; + case 0xCFC: + case 0xCFD: + case 0xCFE: + case 0xCFF: + // PCI Config Mechanism 1 Ports + return (u8) pci_cfg_read(addr, 1); + break; + case 0x0a: + CHECK_DBG(DEBUG_INTR) { + X86EMU_trace_on(); + } + M.x86.debug &= ~DEBUG_DECODE_NOPRINT_F; + //HALT_SYS(); + // no break, intentional fall-through to default!! + default: + DEBUG_PRINTF_IO + ("%s(%04x) reading from bios_device.io_buffer\n", + __func__, addr); + rval = *((u8 *) (bios_device.io_buffer + addr)); + DEBUG_PRINTF_IO("%s(%04x) I/O Buffer --> %02x\n", + __func__, addr, rval); + return rval; + break; + } + } +} + +u16 +my_inw(X86EMU_pioAddr addr) +{ + unsigned long translated_addr = addr; + u8 translated = biosemu_dev_translate_address(&translated_addr); + if (translated != 0) { + //translation successfull, access Device I/O (BAR or Legacy...) + DEBUG_PRINTF_IO("%s(%x): access to Device I/O\n", __func__, + addr); + //DEBUG_PRINTF_IO("%s(%04x): translated_addr: %llx\n", __func__, addr, translated_addr); + u16 rval; + if ((translated_addr & (u64) 0x1) == 0) { + // 16 bit aligned access... + u16 tempval = read_io((void *)translated_addr, 2); + //little endian conversion + rval = in16le((void *) &tempval); + } else { + // unaligned access, read single bytes, little-endian + rval = (read_io((void *)translated_addr, 1) << 8) + | (read_io((void *)(translated_addr + 1), 1)); + } + DEBUG_PRINTF_IO("%s(%04x) Device I/O --> %04x\n", __func__, + addr, rval); + return rval; + } else { + switch (addr) { + case 0xCFC: + case 0xCFE: + //PCI Config Mechanism 1 + return (u16) pci_cfg_read(addr, 2); + break; + default: + DEBUG_PRINTF_IO + ("%s(%04x) reading from bios_device.io_buffer\n", + __func__, addr); + u16 rval = + in16le((void *) bios_device.io_buffer + addr); + DEBUG_PRINTF_IO("%s(%04x) I/O Buffer --> %04x\n", + __func__, addr, rval); + return rval; + break; + } + } +} + +u32 +my_inl(X86EMU_pioAddr addr) +{ + unsigned long translated_addr = addr; + u8 translated = biosemu_dev_translate_address(&translated_addr); + if (translated != 0) { + //translation successfull, access Device I/O (BAR or Legacy...) + DEBUG_PRINTF_IO("%s(%x): access to Device I/O\n", __func__, + addr); + //DEBUG_PRINTF_IO("%s(%04x): translated_addr: %llx\n", __func__, addr, translated_addr); + u32 rval; + if ((translated_addr & (u64) 0x3) == 0) { + // 32 bit aligned access... + u32 tempval = read_io((void *) translated_addr, 4); + //little endian conversion + rval = in32le((void *) &tempval); + } else { + // unaligned access, read single bytes, little-endian + rval = (read_io((void *)(translated_addr), 1) << 24) + | (read_io((void *)(translated_addr + 1), 1) << 16) + | (read_io((void *)(translated_addr + 2), 1) << 8) + | (read_io((void *)(translated_addr + 3), 1)); + } + DEBUG_PRINTF_IO("%s(%04x) Device I/O --> %08x\n", __func__, + addr, rval); + return rval; + } else { + switch (addr) { + case 0xCFC: + //PCI Config Mechanism 1 + return pci_cfg_read(addr, 4); + break; + default: + DEBUG_PRINTF_IO + ("%s(%04x) reading from bios_device.io_buffer\n", + __func__, addr); + u32 rval = + in32le((void *) bios_device.io_buffer + addr); + DEBUG_PRINTF_IO("%s(%04x) I/O Buffer --> %08x\n", + __func__, addr, rval); + return rval; + break; + } + } +} + +void +my_outb(X86EMU_pioAddr addr, u8 val) +{ + unsigned long translated_addr = addr; + u8 translated = biosemu_dev_translate_address(&translated_addr); + if (translated != 0) { + //translation successfull, access Device I/O (BAR or Legacy...) + DEBUG_PRINTF_IO("%s(%x, %x): access to Device I/O\n", + __func__, addr, val); + //DEBUG_PRINTF_IO("%s(%04x): translated_addr: %llx\n", __func__, addr, translated_addr); + write_io((void *) translated_addr, val, 1); + DEBUG_PRINTF_IO("%s(%04x) Device I/O <-- %02x\n", __func__, + addr, val); + } else { + switch (addr) { + case 0xCFC: + case 0xCFD: + case 0xCFE: + case 0xCFF: + // PCI Config Mechanism 1 Ports + pci_cfg_write(addr, val, 1); + break; + default: + DEBUG_PRINTF_IO + ("%s(%04x,%02x) writing to bios_device.io_buffer\n", + __func__, addr, val); + *((u8 *) (bios_device.io_buffer + addr)) = val; + break; + } + } +} + +void +my_outw(X86EMU_pioAddr addr, u16 val) +{ + unsigned long translated_addr = addr; + u8 translated = biosemu_dev_translate_address(&translated_addr); + if (translated != 0) { + //translation successfull, access Device I/O (BAR or Legacy...) + DEBUG_PRINTF_IO("%s(%x, %x): access to Device I/O\n", + __func__, addr, val); + //DEBUG_PRINTF_IO("%s(%04x): translated_addr: %llx\n", __func__, addr, translated_addr); + if ((translated_addr & (u64) 0x1) == 0) { + // little-endian conversion + u16 tempval = in16le((void *) &val); + // 16 bit aligned access... + write_io((void *) translated_addr, tempval, 2); + } else { + // unaligned access, write single bytes, little-endian + write_io(((void *) (translated_addr + 1)), + (u8) ((val & 0xFF00) >> 8), 1); + write_io(((void *) translated_addr), + (u8) (val & 0x00FF), 1); + } + DEBUG_PRINTF_IO("%s(%04x) Device I/O <-- %04x\n", __func__, + addr, val); + } else { + switch (addr) { + case 0xCFC: + case 0xCFE: + // PCI Config Mechanism 1 Ports + pci_cfg_write(addr, val, 2); + break; + default: + DEBUG_PRINTF_IO + ("%s(%04x,%04x) writing to bios_device.io_buffer\n", + __func__, addr, val); + out16le((void *) bios_device.io_buffer + addr, val); + break; + } + } +} + +void +my_outl(X86EMU_pioAddr addr, u32 val) +{ + unsigned long translated_addr = addr; + u8 translated = biosemu_dev_translate_address(&translated_addr); + if (translated != 0) { + //translation successfull, access Device I/O (BAR or Legacy...) + DEBUG_PRINTF_IO("%s(%x, %x): access to Device I/O\n", + __func__, addr, val); + //DEBUG_PRINTF_IO("%s(%04x): translated_addr: %llx\n", __func__, addr, translated_addr); + if ((translated_addr & (u64) 0x3) == 0) { + // little-endian conversion + u32 tempval = in32le((void *) &val); + // 32 bit aligned access... + write_io((void *) translated_addr, tempval, 4); + } else { + // unaligned access, write single bytes, little-endian + write_io(((void *) translated_addr + 3), + (u8) ((val & 0xFF000000) >> 24), 1); + write_io(((void *) translated_addr + 2), + (u8) ((val & 0x00FF0000) >> 16), 1); + write_io(((void *) translated_addr + 1), + (u8) ((val & 0x0000FF00) >> 8), 1); + write_io(((void *) translated_addr), + (u8) (val & 0x000000FF), 1); + } + DEBUG_PRINTF_IO("%s(%04x) Device I/O <-- %08x\n", __func__, + addr, val); + } else { + switch (addr) { + case 0xCFC: + // PCI Config Mechanism 1 Ports + pci_cfg_write(addr, val, 4); + break; + default: + DEBUG_PRINTF_IO + ("%s(%04x,%08x) writing to bios_device.io_buffer\n", + __func__, addr, val); + out32le((void *) bios_device.io_buffer + addr, val); + break; + } + } +} + +u32 +pci_cfg_read(X86EMU_pioAddr addr, u8 size) +{ + u32 rval = 0xFFFFFFFF; + struct device * dev; + if ((addr >= 0xCFC) && ((addr + size) <= 0xD00)) { + // PCI Configuration Mechanism 1 step 1 + // write to 0xCF8, sets bus, device, function and Config Space offset + // later read from 0xCFC-0xCFF returns the value... + u8 bus, devfn, offs; + u32 port_cf8_val = my_inl(0xCF8); + if ((port_cf8_val & 0x80000000) != 0) { + //highest bit enables config space mapping + bus = (port_cf8_val & 0x00FF0000) >> 16; + devfn = (port_cf8_val & 0x0000FF00) >> 8; + offs = (port_cf8_val & 0x000000FF); + offs += (addr - 0xCFC); // if addr is not 0xcfc, the offset is moved accordingly + DEBUG_PRINTF_INTR("%s(): PCI Config Read from device: bus: %02x, devfn: %02x, offset: %02x\n", + __func__, bus, devfn, offs); +#if defined(CONFIG_YABEL_PCI_ACCESS_OTHER_DEVICES) && CONFIG_YABEL_PCI_ACCESS_OTHER_DEVICES==1 + dev = dev_find_slot(bus, devfn); + DEBUG_PRINTF_INTR("%s(): dev_find_slot() returned: %s\n", + __func__, dev_path(dev)); + if (dev == 0) { + // fail accesses to non-existent devices... +#else + dev = bios_device.dev; + if ((bus != bios_device.bus) + || (devfn != bios_device.devfn)) { + // fail accesses to any device but ours... +#endif + printf + ("%s(): Config read access invalid device! bus: %02x (%02x), devfn: %02x (%02x), offs: %02x\n", + __func__, bus, bios_device.bus, devfn, + bios_device.devfn, offs); + SET_FLAG(F_CF); + HALT_SYS(); + return 0; + } else { +#ifdef CONFIG_PCI_OPTION_ROM_RUN_YABEL + switch (size) { + case 1: + rval = pci_read_config8(dev, offs); + break; + case 2: + rval = pci_read_config16(dev, offs); + break; + case 4: + rval = pci_read_config32(dev, offs); + break; + } +#else + rval = + (u32) rtas_pci_config_read(bios_device. + puid, size, + bus, devfn, + offs); +#endif + DEBUG_PRINTF_IO + ("%s(%04x) PCI Config Read @%02x, size: %d --> 0x%08x\n", + __func__, addr, offs, size, rval); + } + } + } + return rval; +} + +void +pci_cfg_write(X86EMU_pioAddr addr, u32 val, u8 size) +{ + if ((addr >= 0xCFC) && ((addr + size) <= 0xD00)) { + // PCI Configuration Mechanism 1 step 1 + // write to 0xCF8, sets bus, device, function and Config Space offset + // later write to 0xCFC-0xCFF sets the value... + u8 bus, devfn, offs; + u32 port_cf8_val = my_inl(0xCF8); + if ((port_cf8_val & 0x80000000) != 0) { + //highest bit enables config space mapping + bus = (port_cf8_val & 0x00FF0000) >> 16; + devfn = (port_cf8_val & 0x0000FF00) >> 8; + offs = (port_cf8_val & 0x000000FF); + offs += (addr - 0xCFC); // if addr is not 0xcfc, the offset is moved accordingly + if ((bus != bios_device.bus) + || (devfn != bios_device.devfn)) { + // fail accesses to any device but ours... + printf + ("Config write access invalid! PCI device %x:%x.%x, offs: %x\n", + bus, devfn >> 3, devfn & 7, offs); + HALT_SYS(); + } else { +#ifdef CONFIG_PCI_OPTION_ROM_RUN_YABEL + switch (size) { + case 1: + pci_write_config8(bios_device.dev, offs, val); + break; + case 2: + pci_write_config16(bios_device.dev, offs, val); + break; + case 4: + pci_write_config32(bios_device.dev, offs, val); + break; + } +#else + rtas_pci_config_write(bios_device.puid, + size, bus, devfn, offs, + val); +#endif + DEBUG_PRINTF_IO + ("%s(%04x) PCI Config Write @%02x, size: %d <-- 0x%08x\n", + __func__, addr, offs, size, val); + } + } + } +} + +u8 +handle_port_61h(void) +{ + static u64 last_time = 0; + u64 curr_time = get_time(); + u64 time_diff; // time since last call + u32 period_ticks; // length of a period in ticks + u32 nr_periods; //number of periods passed since last call + // bit 4 should toggle with every (DRAM) refresh cycle... (66kHz??) + time_diff = curr_time - last_time; + // at 66kHz a period is ~ 15 ns long, converted to ticks: (tb_freq is ticks/second) + // TODO: as long as the frequency does not change, we should not calculate this every time + period_ticks = (15 * tb_freq) / 1000000; + nr_periods = time_diff / period_ticks; + // if the number if ticks passed since last call is odd, we toggle bit 4 + if ((nr_periods % 2) != 0) { + *((u8 *) (bios_device.io_buffer + 0x61)) ^= 0x10; + } + //finally read the value from the io_buffer + return *((u8 *) (bios_device.io_buffer + 0x61)); +} diff --git a/util/x86emu/yabel/io.h b/util/x86emu/yabel/io.h new file mode 100644 index 0000000000..6b2dcc4504 --- /dev/null +++ b/util/x86emu/yabel/io.h @@ -0,0 +1,30 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * 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 + *****************************************************************************/ + +#ifndef _BIOSEMU_IO_H_ +#define _BIOSEMU_IO_H_ +#include <x86emu/x86emu.h> +#include <types.h> + +u8 my_inb(X86EMU_pioAddr addr); + +u16 my_inw(X86EMU_pioAddr addr); + +u32 my_inl(X86EMU_pioAddr addr); + +void my_outb(X86EMU_pioAddr addr, u8 val); + +void my_outw(X86EMU_pioAddr addr, u16 val); + +void my_outl(X86EMU_pioAddr addr, u32 val); + +#endif diff --git a/util/x86emu/yabel/mem.c b/util/x86emu/yabel/mem.c new file mode 100644 index 0000000000..91dc3da3f9 --- /dev/null +++ b/util/x86emu/yabel/mem.c @@ -0,0 +1,469 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * Copyright (c) 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 <types.h> +#ifndef CONFIG_COREBOOT_V2 +#include <cpu.h> +#endif +#include "debug.h" +#include "device.h" +#include "x86emu/x86emu.h" +#include "biosemu.h" +#ifdef CONFIG_COREBOOT_V2 +#include "compat/time.h" +#else +#include <time.h> +#endif + +// define a check for access to certain (virtual) memory regions (interrupt handlers, BIOS Data Area, ...) +#ifdef CONFIG_DEBUG +static u8 in_check = 0; // to avoid recursion... +u16 ebda_segment; +u32 ebda_size; + +//TODO: these macros have grown so large, that they should be changed to an inline function, +//just for the sake of readability... + +//declare prototypes of the functions to follow, for use in DEBUG_CHECK_VMEM_ACCESS +u8 my_rdb(u32); +u16 my_rdw(u32); +u32 my_rdl(u32); + +#define DEBUG_CHECK_VMEM_READ(_addr, _rval) \ + if ((debug_flags & DEBUG_CHECK_VMEM_ACCESS) && (in_check == 0)) { \ + in_check = 1; \ + /* determine ebda_segment and size \ + * since we are using my_rdx calls, make sure, this is after setting in_check! */ \ + /* offset 03 in BDA is EBDA segment */ \ + ebda_segment = my_rdw(0x40e); \ + /* first value in ebda is size in KB */ \ + ebda_size = my_rdb(ebda_segment << 4) * 1024; \ + /* check Interrupt Vector Access (0000:0000h - 0000:0400h) */ \ + if (_addr < 0x400) { \ + DEBUG_PRINTF_CS_IP("%s: read from Interrupt Vector %x --> %x\n", \ + __func__, _addr / 4, _rval); \ + } \ + /* access to BIOS Data Area (0000:0400h - 0000:0500h)*/ \ + else if ((_addr >= 0x400) && (addr < 0x500)) { \ + DEBUG_PRINTF_CS_IP("%s: read from BIOS Data Area: addr: %x --> %x\n", \ + __func__, _addr, _rval); \ + /* dump registers */ \ + /* x86emu_dump_xregs(); */ \ + } \ + /* access to first 64k of memory... */ \ + else if (_addr < 0x10000) { \ + DEBUG_PRINTF_CS_IP("%s: read from segment 0000h: addr: %x --> %x\n", \ + __func__, _addr, _rval); \ + /* dump registers */ \ + /* x86emu_dump_xregs(); */ \ + } \ + /* read from PMM_CONV_SEGMENT */ \ + else if ((_addr <= ((PMM_CONV_SEGMENT << 4) | 0xffff)) && (_addr >= (PMM_CONV_SEGMENT << 4))) { \ + DEBUG_PRINTF_CS_IP("%s: read from PMM Segment %04xh: addr: %x --> %x\n", \ + __func__, PMM_CONV_SEGMENT, _addr, _rval); \ + /* HALT_SYS(); */ \ + /* dump registers */ \ + /* x86emu_dump_xregs(); */ \ + } \ + /* read from PNP_DATA_SEGMENT */ \ + else if ((_addr <= ((PNP_DATA_SEGMENT << 4) | 0xffff)) && (_addr >= (PNP_DATA_SEGMENT << 4))) { \ + DEBUG_PRINTF_CS_IP("%s: read from PnP Data Segment %04xh: addr: %x --> %x\n", \ + __func__, PNP_DATA_SEGMENT, _addr, _rval); \ + /* HALT_SYS(); */ \ + /* dump registers */ \ + /* x86emu_dump_xregs(); */ \ + } \ + /* read from EBDA Segment */ \ + else if ((_addr <= ((ebda_segment << 4) | (ebda_size - 1))) && (_addr >= (ebda_segment << 4))) { \ + DEBUG_PRINTF_CS_IP("%s: read from Extended BIOS Data Area %04xh, size: %04x: addr: %x --> %x\n", \ + __func__, ebda_segment, ebda_size, _addr, _rval); \ + } \ + /* read from BIOS_DATA_SEGMENT */ \ + else if ((_addr <= ((BIOS_DATA_SEGMENT << 4) | 0xffff)) && (_addr >= (BIOS_DATA_SEGMENT << 4))) { \ + DEBUG_PRINTF_CS_IP("%s: read from BIOS Data Segment %04xh: addr: %x --> %x\n", \ + __func__, BIOS_DATA_SEGMENT, _addr, _rval); \ + /* for PMM debugging */ \ + /*if (_addr == BIOS_DATA_SEGMENT << 4) { \ + X86EMU_trace_on(); \ + M.x86.debug &= ~DEBUG_DECODE_NOPRINT_F; \ + }*/ \ + /* dump registers */ \ + /* x86emu_dump_xregs(); */ \ + } \ + in_check = 0; \ + } +#define DEBUG_CHECK_VMEM_WRITE(_addr, _val) \ + if ((debug_flags & DEBUG_CHECK_VMEM_ACCESS) && (in_check == 0)) { \ + in_check = 1; \ + /* determine ebda_segment and size \ + * since we are using my_rdx calls, make sure, this is after setting in_check! */ \ + /* offset 03 in BDA is EBDA segment */ \ + ebda_segment = my_rdw(0x40e); \ + /* first value in ebda is size in KB */ \ + ebda_size = my_rdb(ebda_segment << 4) * 1024; \ + /* check Interrupt Vector Access (0000:0000h - 0000:0400h) */ \ + if (_addr < 0x400) { \ + DEBUG_PRINTF_CS_IP("%s: write to Interrupt Vector %x <-- %x\n", \ + __func__, _addr / 4, _val); \ + } \ + /* access to BIOS Data Area (0000:0400h - 0000:0500h)*/ \ + else if ((_addr >= 0x400) && (addr < 0x500)) { \ + DEBUG_PRINTF_CS_IP("%s: write to BIOS Data Area: addr: %x <-- %x\n", \ + __func__, _addr, _val); \ + /* dump registers */ \ + /* x86emu_dump_xregs(); */ \ + } \ + /* access to first 64k of memory...*/ \ + else if (_addr < 0x10000) { \ + DEBUG_PRINTF_CS_IP("%s: write to segment 0000h: addr: %x <-- %x\n", \ + __func__, _addr, _val); \ + /* dump registers */ \ + /* x86emu_dump_xregs(); */ \ + } \ + /* write to PMM_CONV_SEGMENT... */ \ + else if ((_addr <= ((PMM_CONV_SEGMENT << 4) | 0xffff)) && (_addr >= (PMM_CONV_SEGMENT << 4))) { \ + DEBUG_PRINTF_CS_IP("%s: write to PMM Segment %04xh: addr: %x <-- %x\n", \ + __func__, PMM_CONV_SEGMENT, _addr, _val); \ + /* dump registers */ \ + /* x86emu_dump_xregs(); */ \ + } \ + /* write to PNP_DATA_SEGMENT... */ \ + else if ((_addr <= ((PNP_DATA_SEGMENT << 4) | 0xffff)) && (_addr >= (PNP_DATA_SEGMENT << 4))) { \ + DEBUG_PRINTF_CS_IP("%s: write to PnP Data Segment %04xh: addr: %x <-- %x\n", \ + __func__, PNP_DATA_SEGMENT, _addr, _val); \ + /* dump registers */ \ + /* x86emu_dump_xregs(); */ \ + } \ + /* write to EBDA Segment... */ \ + else if ((_addr <= ((ebda_segment << 4) | (ebda_size - 1))) && (_addr >= (ebda_segment << 4))) { \ + DEBUG_PRINTF_CS_IP("%s: write to Extended BIOS Data Area %04xh, size: %04x: addr: %x <-- %x\n", \ + __func__, ebda_segment, ebda_size, _addr, _val); \ + } \ + /* write to BIOS_DATA_SEGMENT... */ \ + else if ((_addr <= ((BIOS_DATA_SEGMENT << 4) | 0xffff)) && (_addr >= (BIOS_DATA_SEGMENT << 4))) { \ + DEBUG_PRINTF_CS_IP("%s: write to BIOS Data Segment %04xh: addr: %x <-- %x\n", \ + __func__, BIOS_DATA_SEGMENT, _addr, _val); \ + /* dump registers */ \ + /* x86emu_dump_xregs(); */ \ + } \ + /* write to current CS segment... */ \ + else if ((_addr < ((M.x86.R_CS << 4) | 0xffff)) && (_addr > (M.x86.R_CS << 4))) { \ + DEBUG_PRINTF_CS_IP("%s: write to CS segment %04xh: addr: %x <-- %x\n", \ + __func__, M.x86.R_CS, _addr, _val); \ + /* dump registers */ \ + /* x86emu_dump_xregs(); */ \ + } \ + in_check = 0; \ + } +#else +#define DEBUG_CHECK_VMEM_READ(_addr, _rval) +#define DEBUG_CHECK_VMEM_WRITE(_addr, _val) +#endif + +//defined in net-snk/kernel/timer.c +extern u64 get_time(void); + +void update_time(u32); + +// read byte from memory +u8 +my_rdb(u32 addr) +{ + unsigned long translated_addr = addr; + u8 translated = biosemu_dev_translate_address(&translated_addr); + u8 rval; + if (translated != 0) { + //translation successfull, access VGA Memory (BAR or Legacy...) + DEBUG_PRINTF_MEM("%s(%08x): access to VGA Memory\n", + __func__, addr); + //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __func__, addr, translated_addr); + set_ci(); + rval = *((u8 *) translated_addr); + clr_ci(); + DEBUG_PRINTF_MEM("%s(%08x) VGA --> %02x\n", __func__, addr, + rval); + return rval; + } else if (addr > M.mem_size) { + DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n", + __func__, addr); + //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1); + HALT_SYS(); + } else { + /* read from virtual memory */ + rval = *((u8 *) (M.mem_base + addr)); + DEBUG_CHECK_VMEM_READ(addr, rval); + return rval; + } + return -1; +} + +//read word from memory +u16 +my_rdw(u32 addr) +{ + unsigned long translated_addr = addr; + u8 translated = biosemu_dev_translate_address(&translated_addr); + u16 rval; + if (translated != 0) { + //translation successfull, access VGA Memory (BAR or Legacy...) + DEBUG_PRINTF_MEM("%s(%08x): access to VGA Memory\n", + __func__, addr); + //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __func__, addr, translated_addr); + // check for legacy memory, because of the remapping to BARs, the reads must + // be byte reads... + if ((addr >= 0xa0000) && (addr < 0xc0000)) { + //read bytes a using my_rdb, because of the remapping to BARs + //words may not be contiguous in memory, so we need to translate + //every address... + rval = ((u8) my_rdb(addr)) | + (((u8) my_rdb(addr + 1)) << 8); + } else { + if ((translated_addr & (u64) 0x1) == 0) { + // 16 bit aligned access... + set_ci(); + rval = in16le((void *) translated_addr); + clr_ci(); + } else { + // unaligned access, read single bytes + set_ci(); + rval = (*((u8 *) translated_addr)) | + (*((u8 *) translated_addr + 1) << 8); + clr_ci(); + } + } + DEBUG_PRINTF_MEM("%s(%08x) VGA --> %04x\n", __func__, addr, + rval); + return rval; + } else if (addr > M.mem_size) { + DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n", + __func__, addr); + //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1); + HALT_SYS(); + } else { + /* read from virtual memory */ + rval = in16le((void *) (M.mem_base + addr)); + DEBUG_CHECK_VMEM_READ(addr, rval); + return rval; + } + return -1; +} + +//read long from memory +u32 +my_rdl(u32 addr) +{ + unsigned long translated_addr = addr; + u8 translated = biosemu_dev_translate_address(&translated_addr); + u32 rval; + if (translated != 0) { + //translation successfull, access VGA Memory (BAR or Legacy...) + DEBUG_PRINTF_MEM("%s(%x): access to VGA Memory\n", + __func__, addr); + //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __func__, addr, translated_addr); + // check for legacy memory, because of the remapping to BARs, the reads must + // be byte reads... + if ((addr >= 0xa0000) && (addr < 0xc0000)) { + //read bytes a using my_rdb, because of the remapping to BARs + //dwords may not be contiguous in memory, so we need to translate + //every address... + rval = ((u8) my_rdb(addr)) | + (((u8) my_rdb(addr + 1)) << 8) | + (((u8) my_rdb(addr + 2)) << 16) | + (((u8) my_rdb(addr + 3)) << 24); + } else { + if ((translated_addr & (u64) 0x3) == 0) { + // 32 bit aligned access... + set_ci(); + rval = in32le((void *) translated_addr); + clr_ci(); + } else { + // unaligned access, read single bytes + set_ci(); + rval = (*((u8 *) translated_addr)) | + (*((u8 *) translated_addr + 1) << 8) | + (*((u8 *) translated_addr + 2) << 16) | + (*((u8 *) translated_addr + 3) << 24); + clr_ci(); + } + } + DEBUG_PRINTF_MEM("%s(%08x) VGA --> %08x\n", __func__, addr, + rval); + //HALT_SYS(); + return rval; + } else if (addr > M.mem_size) { + DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n", + __func__, addr); + //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1); + HALT_SYS(); + } else { + /* read from virtual memory */ + rval = in32le((void *) (M.mem_base + addr)); + switch (addr) { + case 0x46c: + //BDA Time Data, update it, before reading + update_time(rval); + rval = in32le((void *) (M.mem_base + addr)); + break; + } + DEBUG_CHECK_VMEM_READ(addr, rval); + return rval; + } + return -1; +} + +//write byte to memory +void +my_wrb(u32 addr, u8 val) +{ + unsigned long translated_addr = addr; + u8 translated = biosemu_dev_translate_address(&translated_addr); + if (translated != 0) { + //translation successfull, access VGA Memory (BAR or Legacy...) + DEBUG_PRINTF_MEM("%s(%x, %x): access to VGA Memory\n", + __func__, addr, val); + //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __func__, addr, translated_addr); + set_ci(); + *((u8 *) translated_addr) = val; + clr_ci(); + } else if (addr > M.mem_size) { + DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n", + __func__, addr); + //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1); + HALT_SYS(); + } else { + /* write to virtual memory */ + DEBUG_CHECK_VMEM_WRITE(addr, val); + *((u8 *) (M.mem_base + addr)) = val; + } +} + +void +my_wrw(u32 addr, u16 val) +{ + unsigned long translated_addr = addr; + u8 translated = biosemu_dev_translate_address(&translated_addr); + if (translated != 0) { + //translation successfull, access VGA Memory (BAR or Legacy...) + DEBUG_PRINTF_MEM("%s(%x, %x): access to VGA Memory\n", + __func__, addr, val); + //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __func__, addr, translated_addr); + // check for legacy memory, because of the remapping to BARs, the reads must + // be byte reads... + if ((addr >= 0xa0000) && (addr < 0xc0000)) { + //read bytes a using my_rdb, because of the remapping to BARs + //words may not be contiguous in memory, so we need to translate + //every address... + my_wrb(addr, (u8) (val & 0x00FF)); + my_wrb(addr + 1, (u8) ((val & 0xFF00) >> 8)); + } else { + if ((translated_addr & (u64) 0x1) == 0) { + // 16 bit aligned access... + set_ci(); + out16le((void *) translated_addr, val); + clr_ci(); + } else { + // unaligned access, write single bytes + set_ci(); + *((u8 *) translated_addr) = + (u8) (val & 0x00FF); + *((u8 *) translated_addr + 1) = + (u8) ((val & 0xFF00) >> 8); + clr_ci(); + } + } + } else if (addr > M.mem_size) { + DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n", + __func__, addr); + //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1); + HALT_SYS(); + } else { + /* write to virtual memory */ + DEBUG_CHECK_VMEM_WRITE(addr, val); + out16le((void *) (M.mem_base + addr), val); + } +} +void +my_wrl(u32 addr, u32 val) +{ + unsigned long translated_addr = addr; + u8 translated = biosemu_dev_translate_address(&translated_addr); + if (translated != 0) { + //translation successfull, access VGA Memory (BAR or Legacy...) + DEBUG_PRINTF_MEM("%s(%x, %x): access to VGA Memory\n", + __func__, addr, val); + //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __func__, addr, translated_addr); + // check for legacy memory, because of the remapping to BARs, the reads must + // be byte reads... + if ((addr >= 0xa0000) && (addr < 0xc0000)) { + //read bytes a using my_rdb, because of the remapping to BARs + //words may not be contiguous in memory, so we need to translate + //every address... + my_wrb(addr, (u8) (val & 0x000000FF)); + my_wrb(addr + 1, (u8) ((val & 0x0000FF00) >> 8)); + my_wrb(addr + 2, (u8) ((val & 0x00FF0000) >> 16)); + my_wrb(addr + 3, (u8) ((val & 0xFF000000) >> 24)); + } else { + if ((translated_addr & (u64) 0x3) == 0) { + // 32 bit aligned access... + set_ci(); + out32le((void *) translated_addr, val); + clr_ci(); + } else { + // unaligned access, write single bytes + set_ci(); + *((u8 *) translated_addr) = + (u8) (val & 0x000000FF); + *((u8 *) translated_addr + 1) = + (u8) ((val & 0x0000FF00) >> 8); + *((u8 *) translated_addr + 2) = + (u8) ((val & 0x00FF0000) >> 16); + *((u8 *) translated_addr + 3) = + (u8) ((val & 0xFF000000) >> 24); + clr_ci(); + } + } + } else if (addr > M.mem_size) { + DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n", + __func__, addr); + //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1); + HALT_SYS(); + } else { + /* write to virtual memory */ + DEBUG_CHECK_VMEM_WRITE(addr, val); + out32le((void *) (M.mem_base + addr), val); + } +} + +//update time in BIOS Data Area +//DWord at offset 0x6c is the timer ticks since midnight, timer is running at 18Hz +//byte at 0x70 is timer overflow (set if midnight passed since last call to interrupt 1a function 00 +//cur_val is the current value, of offset 6c... +void +update_time(u32 cur_val) +{ + //for convenience, we let the start of timebase be at midnight, we currently dont support + //real daytime anyway... + u64 ticks_per_day = tb_freq * 60 * 24; + // at 18Hz a period is ~55ms, converted to ticks (tb_freq is ticks/second) + u32 period_ticks = (55 * tb_freq) / 1000; + u64 curr_time = get_time(); + u64 ticks_since_midnight = curr_time % ticks_per_day; + u32 periods_since_midnight = ticks_since_midnight / period_ticks; + // if periods since midnight is smaller than last value, set overflow + // at BDA Offset 0x70 + if (periods_since_midnight < cur_val) { + my_wrb(0x470, 1); + } + // store periods since midnight at BDA offset 0x6c + my_wrl(0x46c, periods_since_midnight); +} diff --git a/util/x86emu/yabel/mem.h b/util/x86emu/yabel/mem.h new file mode 100644 index 0000000000..dca8cfc192 --- /dev/null +++ b/util/x86emu/yabel/mem.h @@ -0,0 +1,36 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * 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 + *****************************************************************************/ + +#ifndef _BIOSEMU_MEM_H_ +#define _BIOSEMU_MEM_H_ +#include <x86emu/x86emu.h> +#include <types.h> + +// read byte from memory +u8 my_rdb(u32 addr); + +//read word from memory +u16 my_rdw(u32 addr); + +//read long from memory +u32 my_rdl(u32 addr); + +//write byte to memory +void my_wrb(u32 addr, u8 val); + +//write word to memory +void my_wrw(u32 addr, u16 val); + +//write long to memory +void my_wrl(u32 addr, u32 val); + +#endif diff --git a/util/x86emu/yabel/pmm.c b/util/x86emu/yabel/pmm.c new file mode 100644 index 0000000000..0b78ca5330 --- /dev/null +++ b/util/x86emu/yabel/pmm.c @@ -0,0 +1,446 @@ +/**************************************************************************** + * YABEL BIOS Emulator + * + * 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 + * + * Copyright (c) 2008 Pattrick Hueper <phueper@hueper.net> + ****************************************************************************/ + +#include <x86emu/x86emu.h> +#ifdef CONFIG_COREBOOT_V2 +#include "../x86emu/prim_ops.h" +#else +#include <x86emu/prim_ops.h> +#endif +#include <string.h> + +#include "biosemu.h" +#include "pmm.h" +#include "debug.h" +#include "device.h" + +/* this struct is used to remember which PMM spaces + * have been assigned. MAX_PMM_AREAS defines how many + * PMM areas we can assign. + * All areas are assigned in PMM_CONV_SEGMENT + */ +typedef struct { + u32 handle; /* handle that is returned to PMM caller */ + u32 offset; /* in PMM_CONV_SEGMENT */ + u32 length; /* length of this area */ +} pmm_allocation_t; + +#define MAX_PMM_AREAS 10 + +/* array to store the above structs */ +static pmm_allocation_t pmm_allocation_array[MAX_PMM_AREAS]; + +/* index into pmm_allocation_array */ +static u32 curr_pmm_allocation_index = 0; + +/* This function is used to setup the PMM struct in virtual memory + * at a certain offset, the length of the PMM struct is returned */ +u8 pmm_setup(u16 segment, u16 offset) +{ + /* setup the PMM structure */ + pmm_information_t *pis = + (pmm_information_t *) (M.mem_base + (((u32) segment) << 4) + + offset); + memset(pis, 0, sizeof(pmm_information_t)); + /* set signature to $PMM */ + pis->signature[0] = '$'; + pis->signature[1] = 'P'; + pis->signature[2] = 'M'; + pis->signature[3] = 'M'; + /* revision as specified */ + pis->struct_rev = 0x01; + /* internal length, excluding code */ + pis->length = ((void *)&(pis->code) - (void *)&(pis->signature)); + /* the code to be executed, pointed to by entry_point_offset */ + pis->code[0] = 0xCD; /* INT */ + pis->code[1] = PMM_INT_NUM; /* my selfdefined PMM INT number */ + pis->code[2] = 0xCB; /* RETF */ + /* set the entry_point_offset, it should point to pis->code, segment is the segment of + * this struct. Since pis->length is the length of the struct excluding code, offset+pis->length + * points to the code... it's that simple ;-) + */ + out32le(&(pis->entry_point_offset), + (u32) segment << 16 | (u32) (offset + pis->length)); + /* checksum calculation */ + u8 i; + u8 checksum = 0; + for (i = 0; i < pis->length; i++) { + checksum += *(((u8 *) pis) + i); + } + pis->checksum = ((u8) 0) - checksum; + CHECK_DBG(DEBUG_PMM) { + DEBUG_PRINTF_PMM("PMM Structure:\n"); + dump((void *)pis, sizeof(pmm_information_t)); + } + return sizeof(pmm_information_t); +} + +/* handle the selfdefined interrupt, this is executed, when the PMM Entry Point + * is executed, it must handle all PMM requests + */ +void pmm_handleInt() +{ + u32 rval = 0; + u16 function, flags; + u32 handle, length; + u32 i, j; + u32 buffer; + /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * according to the PMM Spec "the flags and all registers, except DX and AX + * are preserved across calls to PMM" + * so we save M.x86 and in :exit label we restore it, however, this means that no + * returns must be used in this function, any exit must use goto exit! + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + */ + X86EMU_regs backup_regs = M.x86; + pop_long(); /* pop the return address, this is already saved in INT handler, we don't need + to remember this. */ + function = pop_word(); + switch (function) { + case 0: + /* function pmmAllocate */ + length = pop_long(); + length *= 16; /* length is passed in "paragraphs" of 16 bytes each */ + handle = pop_long(); + flags = pop_word(); + DEBUG_PRINTF_PMM + ("%s: pmmAllocate: Length: %x, Handle: %x, Flags: %x\n", + __func__, length, handle, flags); + if ((flags & 0x1) != 0) { + /* request to allocate in conventional memory */ + if (curr_pmm_allocation_index >= MAX_PMM_AREAS) { + printf + ("%s: pmmAllocate: Maximum Number of allocatable areas reached (%d), cannot allocate more memory!\n", + __func__, MAX_PMM_AREAS); + rval = 0; + goto exit; + } + /* some ROMs seem to be confused by offset 0, so lets start at 0x100 */ + u32 next_offset = 0x100; + pmm_allocation_t *pmm_alloc = + &(pmm_allocation_array[curr_pmm_allocation_index]); + if (curr_pmm_allocation_index != 0) { + /* we have already allocated... get the new next_offset + * from the previous pmm_allocation_t */ + next_offset = + pmm_allocation_array + [curr_pmm_allocation_index - 1].offset + + pmm_allocation_array + [curr_pmm_allocation_index - 1].length; + } + DEBUG_PRINTF_PMM("%s: next_offset: 0x%x\n", + __func__, next_offset); + if (length == 0) { + /* largest possible block size requested, we have on segment + * to allocate, so largest possible is segment size (0xFFFF) + * minus next_offset + */ + rval = 0xFFFF - next_offset; + goto exit; + } + u32 align = 0; + if (((flags & 0x4) != 0) && (length > 0)) { + /* align to least significant bit set in length param */ + u8 lsb = 0; + while (((length >> lsb) & 0x1) == 0) { + lsb++; + } + align = 1 << lsb; + } + /* always align at least to paragraph (16byte) boundary + * hm... since the length is always in paragraphs, we cannot + * align outside of paragraphs anyway... so this check might + * be unnecessary...*/ + if (align < 0x10) { + align = 0x10; + } + DEBUG_PRINTF_PMM("%s: align: 0x%x\n", __func__, + align); + if ((next_offset & (align - 1)) != 0) { + /* not yet aligned... align! */ + next_offset += align; + next_offset &= ~(align - 1); + } + if ((next_offset + length) > 0xFFFF) { + rval = 0; + printf + ("%s: pmmAllocate: Not enough memory available for allocation!\n", + __func__); + goto exit; + } + curr_pmm_allocation_index++; + /* remember the values in pmm_allocation_array */ + pmm_alloc->handle = handle; + pmm_alloc->offset = next_offset; + pmm_alloc->length = length; + /* return the 32bit "physical" address, i.e. combination of segment and offset */ + rval = ((u32) (PMM_CONV_SEGMENT << 16)) | next_offset; + DEBUG_PRINTF_PMM + ("%s: pmmAllocate: allocated memory at %x\n", + __func__, rval); + } else { + rval = 0; + printf + ("%s: pmmAllocate: allocation in extended memory not supported!\n", + __func__); + } + goto exit; + case 1: + /* function pmmFind */ + handle = pop_long(); /* the handle to lookup */ + DEBUG_PRINTF_PMM("%s: pmmFind: Handle: %x\n", __func__, + handle); + i = 0; + for (i = 0; i < curr_pmm_allocation_index; i++) { + if (pmm_allocation_array[i].handle == handle) { + DEBUG_PRINTF_PMM + ("%s: pmmFind: found allocated memory at %x\n", + __func__, rval); + /* return the 32bit "physical" address, i.e. combination of segment and offset */ + rval = + ((u32) (PMM_CONV_SEGMENT << 16)) | + pmm_allocation_array[i].offset; + } + } + if (rval == 0) { + DEBUG_PRINTF_PMM + ("%s: pmmFind: handle (%x) not found!\n", + __func__, handle); + } + goto exit; + case 2: + /* function pmmDeallocate */ + buffer = pop_long(); + /* since argument is the address of the PMM block (including the segment, + * we need to remove the segment to get the offset + */ + buffer = buffer ^ ((u32) PMM_CONV_SEGMENT << 16); + DEBUG_PRINTF_PMM("%s: pmmDeallocate: PMM segment offset: %x\n", + __func__, buffer); + i = 0; + /* rval = 0 means we deallocated the buffer, so set it to 1 in case we dont find it and + * thus cannot deallocate + */ + rval = 1; + for (i = 0; i < curr_pmm_allocation_index; i++) { + DEBUG_PRINTF_PMM("%d: %x\n", i, + pmm_allocation_array[i].handle); + if (pmm_allocation_array[i].offset == buffer) { + /* we found the requested buffer, rval = 0 */ + rval = 0; + DEBUG_PRINTF_PMM + ("%s: pmmDeallocate: found allocated memory at index: %d\n", + __func__, i); + /* copy the remaining elements in pmm_allocation_array one position up */ + j = i; + for (; j < curr_pmm_allocation_index; j++) { + pmm_allocation_array[j] = + pmm_allocation_array[j + 1]; + } + /* move curr_pmm_allocation_index one up, too */ + curr_pmm_allocation_index--; + /* finally clean last element */ + pmm_allocation_array[curr_pmm_allocation_index]. + handle = 0; + pmm_allocation_array[curr_pmm_allocation_index]. + offset = 0; + pmm_allocation_array[curr_pmm_allocation_index]. + length = 0; + break; + } + } + if (rval != 0) { + DEBUG_PRINTF_PMM + ("%s: pmmDeallocate: offset (%x) not found, cannot deallocate!\n", + __func__, buffer); + } + goto exit; + default: + /* invalid/unimplemented function */ + printf("%s: invalid PMM function (0x%04x) called!\n", + __func__, function); + /* PMM spec says if function is invalid, return 0xFFFFFFFF */ + rval = 0xFFFFFFFF; + goto exit; + } + exit: + /* exit handler of this function, restore registers, put return value in DX:AX */ + M.x86 = backup_regs; + M.x86.R_DX = (u16) ((rval >> 16) & 0xFFFF); + M.x86.R_AX = (u16) (rval & 0xFFFF); + CHECK_DBG(DEBUG_PMM) { + DEBUG_PRINTF_PMM("%s: dump of pmm_allocation_array:\n", + __func__); + for (i = 0; i < MAX_PMM_AREAS; i++) { + DEBUG_PRINTF_PMM + ("%d:\n\thandle: %x\n\toffset: %x\n\tlength: %x\n", + i, pmm_allocation_array[i].handle, + pmm_allocation_array[i].offset, + pmm_allocation_array[i].length); + } + } + return; +} + +/* This function tests the pmm_handleInt() function above. */ +void pmm_test(void) +{ + u32 handle, length, addr; + u16 function, flags; + /*-------------------- Test simple allocation/find/deallocation ----------------------------- */ + function = 0; /* pmmAllocate */ + handle = 0xdeadbeef; + length = 16; /* in 16byte paragraphs, so we allocate 256 bytes... */ + flags = 0x1; /* conventional memory, unaligned */ + /* setup stack for call to pmm_handleInt() */ + push_word(flags); + push_long(handle); + push_long(length); + push_word(function); + push_long(0); /* This is the return address for the ABI, unused in this implementation */ + pmm_handleInt(); + addr = ((u32) M.x86.R_DX << 16) | M.x86.R_AX; + DEBUG_PRINTF_PMM("%s: allocated memory at: %04x:%04x\n", __func__, + M.x86.R_DX, M.x86.R_AX); + function = 1; /* pmmFind */ + push_long(handle); + push_word(function); + push_long(0); /* This is the return address for the ABI, unused in this implementation */ + pmm_handleInt(); + DEBUG_PRINTF_PMM("%s: found memory at: %04x:%04x (expected: %08x)\n", + __func__, M.x86.R_DX, M.x86.R_AX, addr); + function = 2; /* pmmDeallocate */ + push_long(addr); + push_word(function); + push_long(0); /* This is the return address for the ABI, unused in this implementation */ + pmm_handleInt(); + DEBUG_PRINTF_PMM + ("%s: freed memory rval: %04x:%04x (expected: 0000:0000)\n", + __func__, M.x86.R_DX, M.x86.R_AX); + /*-------------------- Test aligned allocation/deallocation ----------------------------- */ + function = 0; /* pmmAllocate */ + handle = 0xdeadbeef; + length = 257; /* in 16byte paragraphs, so we allocate 4KB + 16 bytes... */ + flags = 0x1; /* conventional memory, unaligned */ + /* setup stack for call to pmm_handleInt() */ + push_word(flags); + push_long(handle); + push_long(length); + push_word(function); + push_long(0); /* This is the return address for the ABI, unused in this implementation */ + pmm_handleInt(); + addr = ((u32) M.x86.R_DX << 16) | M.x86.R_AX; + DEBUG_PRINTF_PMM("%s: allocated memory at: %04x:%04x\n", __func__, + M.x86.R_DX, M.x86.R_AX); + function = 0; /* pmmAllocate */ + handle = 0xf00d4b0b; + length = 128; /* in 16byte paragraphs, so we allocate 2KB... */ + flags = 0x5; /* conventional memory, aligned */ + /* setup stack for call to pmm_handleInt() */ + push_word(flags); + push_long(handle); + push_long(length); + push_word(function); + push_long(0); /* This is the return address for the ABI, unused in this implementation */ + pmm_handleInt(); + /* the address should be aligned to 0x800, so probably it is at offset 0x1800... */ + addr = ((u32) M.x86.R_DX << 16) | M.x86.R_AX; + DEBUG_PRINTF_PMM("%s: allocated memory at: %04x:%04x\n", __func__, + M.x86.R_DX, M.x86.R_AX); + function = 1; /* pmmFind */ + push_long(handle); + push_word(function); + push_long(0); /* This is the return address for the ABI, unused in this implementation */ + pmm_handleInt(); + addr = ((u32) M.x86.R_DX << 16) | M.x86.R_AX; + function = 2; /* pmmDeallocate */ + push_long(addr); + push_word(function); + push_long(0); /* This is the return address for the ABI, unused in this implementation */ + pmm_handleInt(); + DEBUG_PRINTF_PMM + ("%s: freed memory rval: %04x:%04x (expected: 0000:0000)\n", + __func__, M.x86.R_DX, M.x86.R_AX); + handle = 0xdeadbeef; + function = 1; /* pmmFind */ + push_long(handle); + push_word(function); + push_long(0); /* This is the return address for the ABI, unused in this implementation */ + pmm_handleInt(); + addr = ((u32) M.x86.R_DX << 16) | M.x86.R_AX; + function = 2; /* pmmDeallocate */ + push_long(addr); + push_word(function); + push_long(0); /* This is the return address for the ABI, unused in this implementation */ + pmm_handleInt(); + DEBUG_PRINTF_PMM + ("%s: freed memory rval: %04x:%04x (expected: 0000:0000)\n", + __func__, M.x86.R_DX, M.x86.R_AX); + /*-------------------- Test out of memory allocation ----------------------------- */ + function = 0; /* pmmAllocate */ + handle = 0xdeadbeef; + length = 0; /* length zero means, give me the largest possible block */ + flags = 0x1; /* conventional memory, unaligned */ + /* setup stack for call to pmm_handleInt() */ + push_word(flags); + push_long(handle); + push_long(length); + push_word(function); + push_long(0); /* This is the return address for the ABI, unused in this implementation */ + pmm_handleInt(); + length = ((u32) M.x86.R_DX << 16) | M.x86.R_AX; + length /= 16; /* length in paragraphs */ + DEBUG_PRINTF_PMM("%s: largest possible length: %08x\n", __func__, + length); + function = 0; /* pmmAllocate */ + flags = 0x1; /* conventional memory, aligned */ + /* setup stack for call to pmm_handleInt() */ + push_word(flags); + push_long(handle); + push_long(length); + push_word(function); + push_long(0); /* This is the return address for the ABI, unused in this implementation */ + pmm_handleInt(); + addr = ((u32) M.x86.R_DX << 16) | M.x86.R_AX; + DEBUG_PRINTF_PMM("%s: allocated memory at: %04x:%04x\n", __func__, + M.x86.R_DX, M.x86.R_AX); + function = 0; /* pmmAllocate */ + length = 1; + handle = 0xf00d4b0b; + flags = 0x1; /* conventional memory, aligned */ + /* setup stack for call to pmm_handleInt() */ + push_word(flags); + push_long(handle); + push_long(length); + push_word(function); + push_long(0); /* This is the return address for the ABI, unused in this implementation */ + pmm_handleInt(); + /* this should fail, so 0x0 should be returned */ + addr = ((u32) M.x86.R_DX << 16) | M.x86.R_AX; + DEBUG_PRINTF_PMM + ("%s: allocated memory at: %04x:%04x expected: 0000:0000\n", + __func__, M.x86.R_DX, M.x86.R_AX); + handle = 0xdeadbeef; + function = 1; /* pmmFind */ + push_long(handle); + push_word(function); + push_long(0); /* This is the return address for the ABI, unused in this implementation */ + pmm_handleInt(); + addr = ((u32) M.x86.R_DX << 16) | M.x86.R_AX; + function = 2; /* pmmDeallocate */ + push_long(addr); + push_word(function); + push_long(0); /* This is the return address for the ABI, unused in this implementation */ + pmm_handleInt(); + DEBUG_PRINTF_PMM + ("%s: freed memory rval: %04x:%04x (expected: 0000:0000)\n", + __func__, M.x86.R_DX, M.x86.R_AX); +} diff --git a/util/x86emu/yabel/pmm.h b/util/x86emu/yabel/pmm.h new file mode 100644 index 0000000000..95645dffdc --- /dev/null +++ b/util/x86emu/yabel/pmm.h @@ -0,0 +1,46 @@ +/**************************************************************************** + * YABEL BIOS Emulator + * + * 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 + * + * Copyright (c) 2008 Pattrick Hueper <phueper@hueper.net> + ****************************************************************************/ + +#ifndef _YABEL_PMM_H_ +#define _YABEL_PMM_H_ + +#include <types.h> + +/* PMM Structure see PMM Spec Version 1.01 Chapter 3.1.1 + * (search web for specspmm101.pdf) + */ +typedef struct { + u8 signature[4]; + u8 struct_rev; + u8 length; + u8 checksum; + u32 entry_point_offset; + u8 reserved[5]; + /* Code is not part of the speced PMM struct, however, since I cannot + * put the handling of PMM in the virtual memory (I dont want to hack it + * together in x86 assembly ;-)) this code array is pointed to by + * entry_point_offset, in code there is only a INT call and a RETF, + * thus every PMM call will issue a PMM INT (only defined in YABEL, + * see interrupt.c) and the INT Handler will do the actual PMM work. + */ + u8 code[3]; +} __attribute__ ((__packed__)) pmm_information_t; + +/* This function is used to setup the PMM struct in virtual memory + * at a certain offset */ +u8 pmm_setup(u16 segment, u16 offset); + +/* This is the INT Handler mentioned above, called by my special PMM INT. */ +void pmm_handleInt(void); + +void pmm_test(void); + +#endif // _YABEL_PMM_H diff --git a/util/x86emu/yabel/vbe.c b/util/x86emu/yabel/vbe.c new file mode 100644 index 0000000000..a2c9f82655 --- /dev/null +++ b/util/x86emu/yabel/vbe.c @@ -0,0 +1,779 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * Copyright (c) 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" + +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 +}; + +// pointer to VBEInfoBuffer, set by vbe_prepare +u8 *vbe_info_buffer = 0; +// virtual BIOS Memory +u8 *biosmem; +u32 biosmem_size; + +// these structs are for input from and output to OF +typedef struct { + u8 display_type; // 0=NONE, 1= analog, 2=digital + u16 screen_width; + u16 screen_height; + u16 screen_linebytes; // bytes per line in framebuffer, may be more than screen_width + u8 color_depth; // color depth in bpp + u32 framebuffer_address; + u8 edid_block_zero[128]; +} __attribute__ ((__packed__)) screen_info_t; + +typedef struct { + u8 signature[4]; + u16 size_reserved; + u8 monitor_number; + u16 max_screen_width; + u8 color_depth; +} __attribute__ ((__packed__)) screen_info_input_t; + +// these structs only store a subset of the VBE defined fields +// only those needed. +typedef struct { + char signature[4]; + u16 version; + u8 *oem_string_ptr; + u32 capabilities; + u16 video_mode_list[256]; // lets hope we never have more than 256 video modes... + u16 total_memory; +} vbe_info_t; + +typedef struct { + u16 video_mode; + u8 mode_info_block[256]; + u16 attributes; + u16 linebytes; + u16 x_resolution; + u16 y_resolution; + u8 x_charsize; + u8 y_charsize; + u8 bits_per_pixel; + u8 memory_model; + u32 framebuffer_address; +} vbe_mode_info_t; + +typedef struct { + u8 port_number; // i.e. monitor number + u8 edid_transfer_time; + u8 ddc_level; + u8 edid_block_zero[128]; +} vbe_ddc_info_t; + +static inline u8 +vbe_prepare() +{ + vbe_info_buffer = biosmem + (VBE_SEGMENT << 4); // segment:offset off VBE Data Area + //clear buffer + memset(vbe_info_buffer, 0, 512); + //set VbeSignature to "VBE2" to indicate VBE 2.0+ request + vbe_info_buffer[0] = 'V'; + vbe_info_buffer[0] = 'B'; + vbe_info_buffer[0] = 'E'; + vbe_info_buffer[0] = '2'; + // ES:DI store pointer to buffer in virtual mem see vbe_info_buffer above... + M.x86.R_EDI = 0x0; + M.x86.R_ES = VBE_SEGMENT; + + return 0; // successfull init +} + +// VBE Function 00h +u8 +vbe_info(vbe_info_t * info) +{ + vbe_prepare(); + // call VBE function 00h (Info Function) + M.x86.R_EAX = 0x4f00; + + // enable trace + CHECK_DBG(DEBUG_TRACE_X86EMU) { + X86EMU_trace_on(); + } + // run VESA Interrupt + runInt10(); + + if (M.x86.R_AL != 0x4f) { + DEBUG_PRINTF_VBE("%s: VBE Info Function NOT supported! AL=%x\n", + __func__, M.x86.R_AL); + return -1; + } + + if (M.x86.R_AH != 0x0) { + DEBUG_PRINTF_VBE + ("%s: VBE Info Function Return Code NOT OK! AH=%x\n", + __func__, M.x86.R_AH); + return M.x86.R_AH; + } + //printf("VBE Info Dump:"); + //dump(vbe_info_buffer, 64); + + //offset 0: signature + info->signature[0] = vbe_info_buffer[0]; + info->signature[1] = vbe_info_buffer[1]; + info->signature[2] = vbe_info_buffer[2]; + info->signature[3] = vbe_info_buffer[3]; + + // offset 4: 16bit le containing VbeVersion + info->version = in16le(vbe_info_buffer + 4); + + // offset 6: 32bit le containg segment:offset of OEM String in virtual Mem. + info->oem_string_ptr = + biosmem + ((in16le(vbe_info_buffer + 8) << 4) + + in16le(vbe_info_buffer + 6)); + + // offset 10: 32bit le capabilities + info->capabilities = in32le(vbe_info_buffer + 10); + + // offset 14: 32 bit le containing segment:offset of supported video mode table + u16 *video_mode_ptr; + video_mode_ptr = + (u16 *) (biosmem + + ((in16le(vbe_info_buffer + 16) << 4) + + in16le(vbe_info_buffer + 14))); + u32 i = 0; + do { + info->video_mode_list[i] = in16le(video_mode_ptr + i); + i++; + } + while ((i < + (sizeof(info->video_mode_list) / + sizeof(info->video_mode_list[0]))) + && (info->video_mode_list[i - 1] != 0xFFFF)); + + //offset 18: 16bit le total memory in 64KB blocks + info->total_memory = in16le(vbe_info_buffer + 18); + + return 0; +} + +// VBE Function 01h +u8 +vbe_get_mode_info(vbe_mode_info_t * mode_info) +{ + vbe_prepare(); + // call VBE function 01h (Return VBE Mode Info Function) + M.x86.R_EAX = 0x4f01; + M.x86.R_CX = mode_info->video_mode; + + // enable trace + CHECK_DBG(DEBUG_TRACE_X86EMU) { + X86EMU_trace_on(); + } + // run VESA Interrupt + runInt10(); + + if (M.x86.R_AL != 0x4f) { + DEBUG_PRINTF_VBE + ("%s: VBE Return Mode Info Function NOT supported! AL=%x\n", + __func__, M.x86.R_AL); + return -1; + } + + if (M.x86.R_AH != 0x0) { + DEBUG_PRINTF_VBE + ("%s: VBE Return Mode Info (mode: %04x) Function Return Code NOT OK! AH=%02x\n", + __func__, mode_info->video_mode, M.x86.R_AH); + return M.x86.R_AH; + } + //pointer to mode_info_block is in ES:DI + memcpy(mode_info->mode_info_block, + biosmem + ((M.x86.R_ES << 4) + M.x86.R_DI), + sizeof(mode_info->mode_info_block)); + + //printf("Mode Info Dump:"); + //dump(mode_info_block, 64); + + // offset 0: 16bit le mode attributes + mode_info->attributes = in16le(mode_info->mode_info_block); + + // offset 16: 16bit le bytes per scan line + mode_info->linebytes = in16le(mode_info->mode_info_block + 16); + + // offset 18: 16bit le x resolution + mode_info->x_resolution = in16le(mode_info->mode_info_block + 18); + + // offset 20: 16bit le y resolution + mode_info->y_resolution = in16le(mode_info->mode_info_block + 20); + + // offset 22: 8bit le x charsize + mode_info->x_charsize = *(mode_info->mode_info_block + 22); + + // offset 23: 8bit le y charsize + mode_info->y_charsize = *(mode_info->mode_info_block + 23); + + // offset 25: 8bit le bits per pixel + mode_info->bits_per_pixel = *(mode_info->mode_info_block + 25); + + // offset 27: 8bit le memory model + mode_info->memory_model = *(mode_info->mode_info_block + 27); + + // offset 40: 32bit le containg offset of frame buffer memory ptr + mode_info->framebuffer_address = + in32le(mode_info->mode_info_block + 40); + + return 0; +} + +// VBE Function 02h +u8 +vbe_set_mode(vbe_mode_info_t * mode_info) +{ + vbe_prepare(); + // call VBE function 02h (Set VBE Mode Function) + M.x86.R_EAX = 0x4f02; + M.x86.R_BX = mode_info->video_mode; + M.x86.R_BX |= 0x4000; // set bit 14 to request linear framebuffer mode + M.x86.R_BX &= 0x7FFF; // clear bit 15 to request clearing of framebuffer + + DEBUG_PRINTF_VBE("%s: setting mode: 0x%04x\n", __func__, + M.x86.R_BX); + + // enable trace + CHECK_DBG(DEBUG_TRACE_X86EMU) { + X86EMU_trace_on(); + } + // run VESA Interrupt + runInt10(); + + if (M.x86.R_AL != 0x4f) { + DEBUG_PRINTF_VBE + ("%s: VBE Set Mode Function NOT supported! AL=%x\n", + __func__, M.x86.R_AL); + return -1; + } + + if (M.x86.R_AH != 0x0) { + DEBUG_PRINTF_VBE + ("%s: mode: %x VBE Set Mode Function Return Code NOT OK! AH=%x\n", + __func__, mode_info->video_mode, M.x86.R_AH); + return M.x86.R_AH; + } + return 0; +} + +//VBE Function 08h +u8 +vbe_set_palette_format(u8 format) +{ + vbe_prepare(); + // call VBE function 09h (Set/Get Palette Data Function) + M.x86.R_EAX = 0x4f08; + M.x86.R_BL = 0x00; // set format + M.x86.R_BH = format; + + DEBUG_PRINTF_VBE("%s: setting palette format: %d\n", __func__, + format); + + // enable trace + CHECK_DBG(DEBUG_TRACE_X86EMU) { + X86EMU_trace_on(); + } + // run VESA Interrupt + runInt10(); + + if (M.x86.R_AL != 0x4f) { + DEBUG_PRINTF_VBE + ("%s: VBE Set Palette Format Function NOT supported! AL=%x\n", + __func__, M.x86.R_AL); + return -1; + } + + if (M.x86.R_AH != 0x0) { + DEBUG_PRINTF_VBE + ("%s: VBE Set Palette Format Function Return Code NOT OK! AH=%x\n", + __func__, M.x86.R_AH); + return M.x86.R_AH; + } + return 0; +} + +// VBE Function 09h +u8 +vbe_set_color(u16 color_number, u32 color_value) +{ + vbe_prepare(); + // call VBE function 09h (Set/Get Palette Data Function) + M.x86.R_EAX = 0x4f09; + M.x86.R_BL = 0x00; // set color + M.x86.R_CX = 0x01; // set only one entry + M.x86.R_DX = color_number; + // ES:DI is address where color_value is stored, we store it at 2000:0000 + M.x86.R_ES = 0x2000; + M.x86.R_DI = 0x0; + + // store color value at ES:DI + out32le(biosmem + (M.x86.R_ES << 4) + M.x86.R_DI, color_value); + + DEBUG_PRINTF_VBE("%s: setting color #%x: 0x%04x\n", __func__, + color_number, color_value); + + // enable trace + CHECK_DBG(DEBUG_TRACE_X86EMU) { + X86EMU_trace_on(); + } + // run VESA Interrupt + runInt10(); + + if (M.x86.R_AL != 0x4f) { + DEBUG_PRINTF_VBE + ("%s: VBE Set Palette Function NOT supported! AL=%x\n", + __func__, M.x86.R_AL); + return -1; + } + + if (M.x86.R_AH != 0x0) { + DEBUG_PRINTF_VBE + ("%s: VBE Set Palette Function Return Code NOT OK! AH=%x\n", + __func__, M.x86.R_AH); + return M.x86.R_AH; + } + return 0; +} + +u8 +vbe_get_color(u16 color_number, u32 * color_value) +{ + vbe_prepare(); + // call VBE function 09h (Set/Get Palette Data Function) + M.x86.R_EAX = 0x4f09; + M.x86.R_BL = 0x00; // get color + M.x86.R_CX = 0x01; // get only one entry + M.x86.R_DX = color_number; + // ES:DI is address where color_value is stored, we store it at 2000:0000 + M.x86.R_ES = 0x2000; + M.x86.R_DI = 0x0; + + // enable trace + CHECK_DBG(DEBUG_TRACE_X86EMU) { + X86EMU_trace_on(); + } + // run VESA Interrupt + runInt10(); + + if (M.x86.R_AL != 0x4f) { + DEBUG_PRINTF_VBE + ("%s: VBE Set Palette Function NOT supported! AL=%x\n", + __func__, M.x86.R_AL); + return -1; + } + + if (M.x86.R_AH != 0x0) { + DEBUG_PRINTF_VBE + ("%s: VBE Set Palette Function Return Code NOT OK! AH=%x\n", + __func__, M.x86.R_AH); + return M.x86.R_AH; + } + // read color value from ES:DI + *color_value = in32le(biosmem + (M.x86.R_ES << 4) + M.x86.R_DI); + + DEBUG_PRINTF_VBE("%s: getting color #%x --> 0x%04x\n", __func__, + color_number, *color_value); + + return 0; +} + +// VBE Function 15h +u8 +vbe_get_ddc_info(vbe_ddc_info_t * ddc_info) +{ + vbe_prepare(); + // call VBE function 15h (DDC Info Function) + M.x86.R_EAX = 0x4f15; + M.x86.R_BL = 0x00; // get DDC Info + M.x86.R_CX = ddc_info->port_number; + M.x86.R_ES = 0x0; + M.x86.R_DI = 0x0; + + // enable trace + CHECK_DBG(DEBUG_TRACE_X86EMU) { + X86EMU_trace_on(); + } + // run VESA Interrupt + runInt10(); + + if (M.x86.R_AL != 0x4f) { + DEBUG_PRINTF_VBE + ("%s: VBE Get DDC Info Function NOT supported! AL=%x\n", + __func__, M.x86.R_AL); + return -1; + } + + if (M.x86.R_AH != 0x0) { + DEBUG_PRINTF_VBE + ("%s: port: %x VBE Get DDC Info Function Return Code NOT OK! AH=%x\n", + __func__, ddc_info->port_number, M.x86.R_AH); + return M.x86.R_AH; + } + // BH = approx. time in seconds to transfer one EDID block + ddc_info->edid_transfer_time = M.x86.R_BH; + // BL = DDC Level + ddc_info->ddc_level = M.x86.R_BL; + + vbe_prepare(); + // call VBE function 15h (DDC Info Function) + M.x86.R_EAX = 0x4f15; + M.x86.R_BL = 0x01; // read EDID + M.x86.R_CX = ddc_info->port_number; + M.x86.R_DX = 0x0; // block number + // ES:DI is address where EDID is stored, we store it at 2000:0000 + M.x86.R_ES = 0x2000; + M.x86.R_DI = 0x0; + + // enable trace + CHECK_DBG(DEBUG_TRACE_X86EMU) { + X86EMU_trace_on(); + } + // run VESA Interrupt + runInt10(); + + if (M.x86.R_AL != 0x4f) { + DEBUG_PRINTF_VBE + ("%s: VBE Read EDID Function NOT supported! AL=%x\n", + __func__, M.x86.R_AL); + return -1; + } + + if (M.x86.R_AH != 0x0) { + DEBUG_PRINTF_VBE + ("%s: port: %x VBE Read EDID Function Return Code NOT OK! AH=%x\n", + __func__, ddc_info->port_number, M.x86.R_AH); + return M.x86.R_AH; + } + + memcpy(ddc_info->edid_block_zero, + biosmem + (M.x86.R_ES << 4) + M.x86.R_DI, + sizeof(ddc_info->edid_block_zero)); + + return 0; +} + +u32 +vbe_get_info(u8 argc, char ** argv) +{ + u8 rval; + u32 i; + if (argc < 4) { + printf + ("Usage %s <vmem_base> <device_path> <address of screen_info_t>\n", + argv[0]); + int i = 0; + for (i = 0; i < argc; i++) { + printf("argv[%d]: %s\n", i, argv[i]); + } + return -1; + } + // get a copy of input struct... + screen_info_input_t input = + *((screen_info_input_t *) strtoul((char *) argv[4], 0, 16)); + // output is pointer to the address passed as argv[4] + screen_info_t *output = + (screen_info_t *) strtoul((char *) argv[4], 0, 16); + // zero output + memset(output, 0, sizeof(screen_info_t)); + + // argv[1] is address of virtual BIOS mem... + // argv[2] is the size + biosmem = (u8 *) strtoul(argv[1], 0, 16); + biosmem_size = strtoul(argv[2], 0, 16);; + 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; + } + // argv[3] is the device to open and use... + if (dev_init((char *) argv[3]) != 0) { + printf("Error initializing device!\n"); + return -1; + } + //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); + + // set mem_base + M.mem_base = (long) biosmem; + M.mem_size = biosmem_size; + DEBUG_PRINTF_VBE("membase set: %08x, size: %08x\n", (int) M.mem_base, + (int) M.mem_size); + + vbe_info_t info; + rval = vbe_info(&info); + if (rval != 0) + return rval; + + DEBUG_PRINTF_VBE("VbeSignature: %s\n", info.signature); + DEBUG_PRINTF_VBE("VbeVersion: 0x%04x\n", info.version); + DEBUG_PRINTF_VBE("OemString: %s\n", info.oem_string_ptr); + DEBUG_PRINTF_VBE("Capabilities:\n"); + DEBUG_PRINTF_VBE("\tDAC: %s\n", + (info.capabilities & 0x1) == + 0 ? "fixed 6bit" : "switchable 6/8bit"); + DEBUG_PRINTF_VBE("\tVGA: %s\n", + (info.capabilities & 0x2) == + 0 ? "compatible" : "not compatible"); + DEBUG_PRINTF_VBE("\tRAMDAC: %s\n", + (info.capabilities & 0x4) == + 0 ? "normal" : "use blank bit in Function 09h"); + + // argv[4] may be a pointer with enough space to return screen_info_t + // as input, it must contain a screen_info_input_t with the following content: + // byte[0:3] = "DDC\0" (zero-terminated signature header) + // byte[4:5] = reserved space for the return struct... just in case we ever change + // the struct and dont have reserved enough memory (and let's hope the struct + // never gets larger than 64KB) + // byte[6] = monitor port number for DDC requests ("only" one byte... so lets hope we never have more than 255 monitors... + // byte[7:8] = max. screen width (OF may want to limit this) + // byte[9] = required color depth in bpp + if (strncmp((char *) input.signature, "DDC", 4) != 0) { + printf + ("%s: Invalid input signature! expected: %s, is: %s\n", + __func__, "DDC", input.signature); + return -1; + } + if (input.size_reserved != sizeof(screen_info_t)) { + printf + ("%s: Size of return struct is wrong, required: %d, available: %d\n", + __func__, (int) sizeof(screen_info_t), + input.size_reserved); + return -1; + } + + vbe_ddc_info_t ddc_info; + ddc_info.port_number = input.monitor_number; + vbe_get_ddc_info(&ddc_info); + +#if 0 + DEBUG_PRINTF_VBE("DDC: edid_tranfer_time: %d\n", + ddc_info.edid_transfer_time); + DEBUG_PRINTF_VBE("DDC: ddc_level: %x\n", ddc_info.ddc_level); + DEBUG_PRINTF_VBE("DDC: EDID: \n"); + CHECK_DBG(DEBUG_VBE) { + dump(ddc_info.edid_block_zero, + sizeof(ddc_info.edid_block_zero)); + } +#endif + if (*((u64 *) ddc_info.edid_block_zero) != + (u64) 0x00FFFFFFFFFFFF00) { + // invalid EDID signature... probably no monitor + + output->display_type = 0x0; + return 0; + } else if ((ddc_info.edid_block_zero[20] & 0x80) != 0) { + // digital display + output->display_type = 2; + } else { + // analog + output->display_type = 1; + } + DEBUG_PRINTF_VBE("DDC: found display type %d\n", output->display_type); + memcpy(output->edid_block_zero, ddc_info.edid_block_zero, + sizeof(ddc_info.edid_block_zero)); + i = 0; + vbe_mode_info_t mode_info; + vbe_mode_info_t best_mode_info; + // initialize best_mode to 0 + memset(&best_mode_info, 0, sizeof(best_mode_info)); + while ((mode_info.video_mode = info.video_mode_list[i]) != 0xFFFF) { + //DEBUG_PRINTF_VBE("%x: Mode: %04x\n", i, mode_info.video_mode); + vbe_get_mode_info(&mode_info); +#if 0 + DEBUG_PRINTF_VBE("Video Mode 0x%04x available, %s\n", + mode_info.video_mode, + (mode_info.attributes & 0x1) == + 0 ? "not supported" : "supported"); + DEBUG_PRINTF_VBE("\tTTY: %s\n", + (mode_info.attributes & 0x4) == + 0 ? "no" : "yes"); + DEBUG_PRINTF_VBE("\tMode: %s %s\n", + (mode_info.attributes & 0x8) == + 0 ? "monochrome" : "color", + (mode_info.attributes & 0x10) == + 0 ? "text" : "graphics"); + DEBUG_PRINTF_VBE("\tVGA: %s\n", + (mode_info.attributes & 0x20) == + 0 ? "compatible" : "not compatible"); + DEBUG_PRINTF_VBE("\tWindowed Mode: %s\n", + (mode_info.attributes & 0x40) == + 0 ? "yes" : "no"); + DEBUG_PRINTF_VBE("\tFramebuffer: %s\n", + (mode_info.attributes & 0x80) == + 0 ? "no" : "yes"); + DEBUG_PRINTF_VBE("\tResolution: %dx%d\n", + mode_info.x_resolution, + mode_info.y_resolution); + DEBUG_PRINTF_VBE("\tChar Size: %dx%d\n", + mode_info.x_charsize, mode_info.y_charsize); + DEBUG_PRINTF_VBE("\tColor Depth: %dbpp\n", + mode_info.bits_per_pixel); + DEBUG_PRINTF_VBE("\tMemory Model: 0x%x\n", + mode_info.memory_model); + DEBUG_PRINTF_VBE("\tFramebuffer Offset: %08x\n", + mode_info.framebuffer_address); +#endif + if ((mode_info.bits_per_pixel == input.color_depth) + && (mode_info.x_resolution <= input.max_screen_width) + && ((mode_info.attributes & 0x80) != 0) // framebuffer mode + && ((mode_info.attributes & 0x10) != 0) // graphics + && ((mode_info.attributes & 0x8) != 0) // color + && (mode_info.x_resolution > best_mode_info.x_resolution)) // better than previous best_mode + { + // yiiiihaah... we found a new best mode + memcpy(&best_mode_info, &mode_info, sizeof(mode_info)); + } + i++; + } + + if (best_mode_info.video_mode != 0) { + DEBUG_PRINTF_VBE + ("Best Video Mode found: 0x%x, %dx%d, %dbpp, framebuffer_address: 0x%x\n", + best_mode_info.video_mode, + best_mode_info.x_resolution, + best_mode_info.y_resolution, + best_mode_info.bits_per_pixel, + best_mode_info.framebuffer_address); + + //printf("Mode Info Dump:"); + //dump(best_mode_info.mode_info_block, 64); + + // set the video mode + vbe_set_mode(&best_mode_info); + + if ((info.capabilities & 0x1) != 0) { + // switch to 8 bit palette format + vbe_set_palette_format(8); + } + // setup a palette: + // - first 216 colors are mixed colors for each component in 6 steps + // (6*6*6=216) + // - then 10 shades of the three primary colors + // - then 10 shades of grey + // ------- + // = 256 colors + // + // - finally black is color 0 and white color FF (because SLOF expects it + // this way...) + // this resembles the palette that the kernel/X Server seems to expect... + + u8 mixed_color_values[6] = + { 0xFF, 0xDA, 0xB3, 0x87, 0x54, 0x00 }; + u8 primary_color_values[10] = + { 0xF3, 0xE7, 0xCD, 0xC0, 0xA5, 0x96, 0x77, 0x66, 0x3F, + 0x27 + }; + u8 mc_size = sizeof(mixed_color_values); + u8 prim_size = sizeof(primary_color_values); + + u8 curr_color_index; + u32 curr_color; + + u8 r, g, b; + // 216 mixed colors + for (r = 0; r < mc_size; r++) { + for (g = 0; g < mc_size; g++) { + for (b = 0; b < mc_size; b++) { + curr_color_index = + (r * mc_size * mc_size) + + (g * mc_size) + b; + curr_color = 0; + curr_color |= ((u32) mixed_color_values[r]) << 16; //red value + curr_color |= ((u32) mixed_color_values[g]) << 8; //green value + curr_color |= (u32) mixed_color_values[b]; //blue value + vbe_set_color(curr_color_index, + curr_color); + } + } + } + + // 10 shades of each primary color + // red + for (r = 0; r < prim_size; r++) { + curr_color_index = mc_size * mc_size * mc_size + r; + curr_color = ((u32) primary_color_values[r]) << 16; + vbe_set_color(curr_color_index, curr_color); + } + //green + for (g = 0; g < prim_size; g++) { + curr_color_index = + mc_size * mc_size * mc_size + prim_size + g; + curr_color = ((u32) primary_color_values[g]) << 8; + vbe_set_color(curr_color_index, curr_color); + } + //blue + for (b = 0; b < prim_size; b++) { + curr_color_index = + mc_size * mc_size * mc_size + prim_size * 2 + b; + curr_color = (u32) primary_color_values[b]; + vbe_set_color(curr_color_index, curr_color); + } + // 10 shades of grey + for (i = 0; i < prim_size; i++) { + curr_color_index = + mc_size * mc_size * mc_size + prim_size * 3 + i; + curr_color = 0; + curr_color |= ((u32) primary_color_values[i]) << 16; //red + curr_color |= ((u32) primary_color_values[i]) << 8; //green + curr_color |= ((u32) primary_color_values[i]); //blue + vbe_set_color(curr_color_index, curr_color); + } + + // SLOF is using color 0x0 (black) and 0xFF (white) to draw to the screen... + vbe_set_color(0x00, 0x00000000); + vbe_set_color(0xFF, 0x00FFFFFF); + + output->screen_width = best_mode_info.x_resolution; + output->screen_height = best_mode_info.y_resolution; + output->screen_linebytes = best_mode_info.linebytes; + output->color_depth = best_mode_info.bits_per_pixel; + output->framebuffer_address = + best_mode_info.framebuffer_address; + } else { + printf("%s: No suitable video mode found!\n", __func__); + //unset display_type... + output->display_type = 0; + } + return 0; +} diff --git a/util/x86emu/yabel/vbe.h b/util/x86emu/yabel/vbe.h new file mode 100644 index 0000000000..07daedb672 --- /dev/null +++ b/util/x86emu/yabel/vbe.h @@ -0,0 +1,16 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * 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 + *****************************************************************************/ + +#ifndef _BIOSEMU_VBE_H_ +#define _BIOSEMU_VBE_H_ + +#endif |