Author: myles Date: 2008-12-16 23:40:27 +0100 (Tue, 16 Dec 2008) New Revision: 1073
Added: coreboot-v3/util/x86emu/biosemu/ coreboot-v3/util/x86emu/biosemu/Makefile coreboot-v3/util/x86emu/biosemu/biosemu.c coreboot-v3/util/x86emu/biosemu/biosemu.h coreboot-v3/util/x86emu/biosemu/debug.c coreboot-v3/util/x86emu/biosemu/debug.h coreboot-v3/util/x86emu/biosemu/device.c coreboot-v3/util/x86emu/biosemu/device.h coreboot-v3/util/x86emu/biosemu/interrupt.c coreboot-v3/util/x86emu/biosemu/interrupt.h coreboot-v3/util/x86emu/biosemu/io.c coreboot-v3/util/x86emu/biosemu/io.h coreboot-v3/util/x86emu/biosemu/mem.c coreboot-v3/util/x86emu/biosemu/mem.h coreboot-v3/util/x86emu/biosemu/vbe.c coreboot-v3/util/x86emu/biosemu/vbe.h Log:
Subject: [PATCH 1/5] original biosemu version, from slof-JX-1.7.0-4
Signed-off-by: Pattrick Hueper phueper@hueper.net Acked-by: Myles Watson mylesgw@gmail.com
Added: coreboot-v3/util/x86emu/biosemu/Makefile =================================================================== --- coreboot-v3/util/x86emu/biosemu/Makefile (rev 0) +++ coreboot-v3/util/x86emu/biosemu/Makefile 2008-12-16 22:40:27 UTC (rev 1073) @@ -0,0 +1,38 @@ +# ***************************************************************************** +# * 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 TOP + TOP = $(shell while ! test -e make.rules; do cd .. ; done; pwd) + export TOP +endif +include $(TOP)/make.rules + +CFLAGS += -I$(ROOTDIR)/other-licence/x86emu -I$(ROOTDIR)/other-licence/x86emu/include + +OBJS = biosemu.o debug.o device.o mem.o io.o interrupt.o vbe.o +LIBX86EMU = $(ROOTDIR)/other-licence/x86emu/libx86emu.a + +.PHONY: $(LIBX86EMU) + +all: biosemu_app.o + +# special rule for libx86emu.a +$(LIBX86EMU): + $(MAKE) -C $(dir $@) + +biosemu_app.o: $(OBJS) $(LIBX86EMU) + $(LD) $(LDFLAGS) $^ -o $@ -r + +clean: + $(RM) -f *.o *.a *.i *.s + +include $(TOP)/make.depend
Added: coreboot-v3/util/x86emu/biosemu/biosemu.c =================================================================== --- coreboot-v3/util/x86emu/biosemu/biosemu.c (rev 0) +++ coreboot-v3/util/x86emu/biosemu/biosemu.c 2008-12-16 22:40:27 UTC (rev 1073) @@ -0,0 +1,340 @@ +/****************************************************************************** + * 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 + *****************************************************************************/ + + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <stdint.h> +#include <cpu.h> + +#include "debug.h" + +#include <x86emu/x86emu.h> +#include <x86emu/regs.h> +#include <x86emu/prim_ops.h> // for push_word + +#include "biosemu.h" +#include "io.h" +#include "mem.h" +#include "interrupt.h" +#include "device.h" + +#include <rtas.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 +}; + +void dump(uint8_t * addr, uint32_t len); + +uint32_t +biosemu(char argc, char **argv) +{ + uint8_t *rom_image; + int i = 0; + uint8_t *biosmem; + uint32_t biosmem_size; +#ifdef DEBUG + debug_flags = DEBUG_PRINT_INT10 | DEBUG_PNP;// | DEBUG_PMM;// | DEBUG_INTR | DEBUG_CHECK_VMEM_ACCESS | DEBUG_MEM | DEBUG_IO;// | DEBUG_TRACE_X86EMU | DEBUG_JMP; +#endif + if (argc < 3) { + printf("Usage %s <vmem_base> <device_path>\n", argv[0]); + for (i = 0; i < argc; i++) { + printf("argv[%d]: %s\n", i, argv[i]); + } + return -1; + } + // argv[1] is address of virtual BIOS mem... + // argv[2] is the size + biosmem = (uint8_t *) 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(argv[3]) != 0) { + printf("Error initializing device!\n"); + return -1; + } + if (dev_check_exprom() != 0) { + printf("Error: Device Expansion ROM invalid!\n"); + return -1; + } + rom_image = (uint8_t *) 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... + uint8_t *mem_img = biosmem + (OPTION_ROM_CODE_SEGMENT << 4); + uint8_t copy_count = 0; + uint8_t 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 + uint8_t 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 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 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 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 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 wether 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; +}
Added: coreboot-v3/util/x86emu/biosemu/biosemu.h =================================================================== --- coreboot-v3/util/x86emu/biosemu/biosemu.h (rev 0) +++ coreboot-v3/util/x86emu/biosemu/biosemu.h 2008-12-16 22:40:27 UTC (rev 1073) @@ -0,0 +1,40 @@ +/****************************************************************************** + * 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_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 + +#endif
Added: coreboot-v3/util/x86emu/biosemu/debug.c =================================================================== --- coreboot-v3/util/x86emu/biosemu/debug.c (rev 0) +++ coreboot-v3/util/x86emu/biosemu/debug.c 2008-12-16 22:40:27 UTC (rev 1073) @@ -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 + *****************************************************************************/ + +#include <cpu.h> + +#include "debug.h" + +uint32_t debug_flags = 0; + +void +dump(uint8_t * addr, uint32_t len) +{ + printf("\n\r%s(%p, %x):\n", __FUNCTION__, addr, len); + while (len) { + unsigned int tmpCnt = len; + unsigned char x; + if (tmpCnt > 8) + tmpCnt = 8; + printf("\n\r%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"); +}
Added: coreboot-v3/util/x86emu/biosemu/debug.h =================================================================== --- coreboot-v3/util/x86emu/biosemu/debug.h (rev 0) +++ coreboot-v3/util/x86emu/biosemu/debug.h 2008-12-16 22:40:27 UTC (rev 1073) @@ -0,0 +1,74 @@ +/****************************************************************************** + * 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_DEBUG_H_ +#define _BIOSEMU_DEBUG_H_ + +#include <stdio.h> +#include <stdint.h> + +extern uint32_t debug_flags; +// from x86emu...needed for debugging +extern void x86emu_dump_xregs(); + +#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 DEBUG +#ifdef 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 //DEBUG + +void dump(uint8_t * addr, uint32_t len); + +#endif
Added: coreboot-v3/util/x86emu/biosemu/device.c =================================================================== --- coreboot-v3/util/x86emu/biosemu/device.c (rev 0) +++ coreboot-v3/util/x86emu/biosemu/device.c 2008-12-16 22:40:27 UTC (rev 1073) @@ -0,0 +1,327 @@ +/****************************************************************************** + * 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 + *****************************************************************************/ + + +#include "device.h" +#include "rtas.h" +#include <stdio.h> +#include <string.h> +#include "debug.h" + +typedef struct { + uint8_t info; + uint8_t bus; + uint8_t devfn; + uint8_t cfg_space_offset; + uint64_t address; + uint64_t size; +} __attribute__ ((__packed__)) assigned_address_t; + +// use translate_address_dev and get_puid from net-snk's net_support.c +void translate_address_dev(uint64_t *, phandle_t); +uint64_t 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 +dev_get_addr_info() +{ + // 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; + uint64_t 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 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 +} + +// 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 +dev_find_vmem_addr() +{ + int i = 0; + translate_address_t ta; + int8_t 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", + __FUNCTION__, 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", + __FUNCTION__, bios_device.vmem_addr, + bios_device.vmem_size); + } + // disable vmem + //bios_device.vmem_size = 0; +} + +void +dev_get_puid() +{ + // get puid + bios_device.puid = get_puid(bios_device.phandle); + DEBUG_PRINTF("puid: 0x%llx\n", bios_device.puid); +} + +void +dev_get_device_vendor_id() +{ + uint32_t pci_config_0 = + rtas_pci_config_read(bios_device.puid, 4, bios_device.bus, + bios_device.devfn, 0x0); + bios_device.pci_device_id = + (uint16_t) ((pci_config_0 & 0xFFFF0000) >> 16); + bios_device.pci_vendor_id = (uint16_t) (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, wether the device has a valid Expansion ROM, also search the PCI Data Structure and + * any Expansion ROM Header (using dev_scan_exp_header()) for needed information */ +uint8_t +dev_check_exprom() +{ + int i = 0; + translate_address_t ta; + uint64_t rom_base_addr = 0; + uint16_t pci_ds_offset; + pci_data_struct_t pci_ds; + // 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) { + rom_base_addr = ta.address + ta.address_offset; //translated address + break; + } + } + // in the ROM there could be multiple Expansion ROM Images... start searching + // them for a x86 image + do { + if (rom_base_addr == 0) { + printf("Error: no Expansion ROM address found!\n"); + return -1; + } + set_ci(); + uint16_t rom_signature = *((uint16_t *) rom_base_addr); + clr_ci(); + if (rom_signature != 0x55aa) { + printf + ("Error: invalid Expansion ROM signature: %02x!\n", + *((uint16_t *) 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 DEBUG + DEBUG_PRINTF("PCI Data Structure @%llx:\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; +} + +uint8_t +dev_init(char *device_name) +{ + uint8_t rval = 0; + //init bios_device struct + DEBUG_PRINTF("%s(%s)\n", __FUNCTION__, device_name); + memset(&bios_device, 0, sizeof(bios_device)); + 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); + dev_get_addr_info(); + dev_find_vmem_addr(); + dev_get_puid(); + 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. +uint8_t +dev_translate_address(uint64_t * addr) +{ + int i = 0; + translate_address_t ta; + //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))) { + uint8_t shift = *addr & 1; + *addr &= 0xfffffffe; + *addr = (*addr - 0xB8000) * 4 + shift + bios_device.vmem_addr; + } + 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; +}
Added: coreboot-v3/util/x86emu/biosemu/device.h =================================================================== --- coreboot-v3/util/x86emu/biosemu/device.h (rev 0) +++ coreboot-v3/util/x86emu/biosemu/device.h 2008-12-16 22:40:27 UTC (rev 1073) @@ -0,0 +1,155 @@ +/****************************************************************************** + * 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 DEVICE_LIB_H +#define DEVICE_LIB_H + +#include <stdint.h> +#include <cpu.h> +#include "of.h" +#include <stdio.h> + +// a Expansion Header Struct as defined in Plug and Play BIOS Spec 1.0a Chapter 3.2 +typedef struct { + char signature[4]; // signature + uint8_t structure_revision; + uint8_t length; // in 16 byte blocks + uint16_t next_header_offset; // offset to next Expansion Header as 16bit little-endian value, as offset from the start of the Expansion ROM + uint8_t reserved; + uint8_t checksum; // the sum of all bytes of the Expansion Header must be 0 + uint32_t device_id; // PnP Device ID as 32bit little-endian value + uint16_t p_manufacturer_string; //16bit little-endian offset from start of Expansion ROM + uint16_t p_product_string; //16bit little-endian offset from start of Expansion ROM + uint8_t device_base_type; + uint8_t device_sub_type; + uint8_t device_if_type; + uint8_t device_indicators; + // the following vectors are all 16bit little-endian offsets from start of Expansion ROM + uint16_t bcv; // Boot Connection Vector + uint16_t dv; // Disconnect Vector + uint16_t bev; // Bootstrap Entry Vector + uint16_t reserved_2; + uint16_t 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 { + uint8_t signature[4]; // signature, the String "PCIR" + uint16_t vendor_id; + uint16_t device_id; + uint16_t reserved; + uint16_t pci_ds_length; // PCI Data Structure Length, 16bit little-endian value + uint8_t pci_ds_revision; + uint8_t class_code[3]; + uint16_t img_length; // length of the Exp.ROM Image, 16bit little-endian value in 512 bytes + uint16_t img_revision; + uint8_t code_type; + uint8_t indicator; + uint16_t reserved_2; +} __attribute__ ((__packed__)) pci_data_struct_t; + +typedef struct { + uint8_t bus; + uint8_t devfn; + uint64_t puid; + phandle_t phandle; + ihandle_t ihandle; + // store the address of the BAR that is used to simulate + // legacy VGA memory accesses + uint64_t vmem_addr; + uint64_t 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... + uint8_t io_buffer[64 * 1024]; + uint16_t pci_vendor_id; + uint16_t pci_device_id; + // translated address of the "PC-Compatible" Expansion ROM Image for this device + uint64_t img_addr; + uint32_t img_size; // size of the Expansion ROM Image (read from the PCI Data Structure) +} device_t; + +typedef struct { + uint8_t info; + uint8_t bus; + uint8_t devfn; + uint8_t cfg_space_offset; + uint64_t address; + uint64_t address_offset; + uint64_t 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... +translate_address_t translate_address_array[11]; + +// index of last translate_address_array entry +// set by get_dev_addr_info function +uint8_t taa_last_entry; + +device_t bios_device; + +uint8_t dev_init(char *device_name); +// NOTE: for dev_check_exprom to work, dev_init MUST be called first! +uint8_t dev_check_exprom(); + +uint8_t dev_translate_address(uint64_t * addr); + +/* endianness swap functions for 16 and 32 bit words + * copied from axon_pciconfig.c + */ +static inline void +out32le(void *addr, uint32_t val) +{ + asm volatile ("stwbrx %0, 0, %1"::"r" (val), "r"(addr)); +} + +static inline uint32_t +in32le(void *addr) +{ + uint32_t val; + asm volatile ("lwbrx %0, 0, %1":"=r" (val):"r"(addr)); + return val; +} + +static inline void +out16le(void *addr, uint16_t val) +{ + asm volatile ("sthbrx %0, 0, %1"::"r" (val), "r"(addr)); +} + +static inline uint16_t +in16le(void *addr) +{ + uint16_t val; + asm volatile ("lhbrx %0, 0, %1":"=r" (val):"r"(addr)); + return val; +} + +/* debug function, dumps HID1 and HID4 to detect wether caches are on/off */ +static inline void +dumpHID() +{ + uint64_t 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
Added: coreboot-v3/util/x86emu/biosemu/interrupt.c =================================================================== --- coreboot-v3/util/x86emu/biosemu/interrupt.c (rev 0) +++ coreboot-v3/util/x86emu/biosemu/interrupt.c 2008-12-16 22:40:27 UTC (rev 1073) @@ -0,0 +1,607 @@ +/****************************************************************************** + * 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 + *****************************************************************************/ + +#include <stdio.h> + +#include <rtas.h> + +#include "biosemu.h" +#include "mem.h" +#include "device.h" +#include "debug.h" + +#include <x86emu/x86emu.h> +#include <x86emu/prim_ops.h> + + + +//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", + __FUNCTION__, 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() +{ + // the data for INT10 is stored in BDA (0000:0400h) offset 49h-66h + // function number in AH + //DEBUG_PRINTF_CS_IP("%s:\n", __FUNCTION__); + //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) { + uint32_t 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) { + uint32_t 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", + __FUNCTION__, 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 uint8_t 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(uint64_t * keycode) +{ + uint8_t scan_code = 0; + uint8_t char_code = 0; + if (*keycode < 256) { + scan_code = keycode_table[*keycode]; + char_code = (uint8_t) * keycode & 0xff; + } else { + switch (*keycode) { + case 0x1b50: + // F1 + scan_code = 0x3b; + char_code = 0x0; + break; + default: + printf("%s(): unknown multibyte keycode: %llx\n", + __FUNCTION__, *keycode); + break; + } + } + //assemble scan/char code in keycode + *keycode = (uint64_t) ((((uint16_t) scan_code) << 8) | char_code); +} + +// handle int16 (Keyboard BIOS Interrupt) +void +handleInt16() +{ + // 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 uint64_t 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 + uint64_t *keycode = (uint64_t *) (M.mem_base + 0x41e); + int8_t c; + // function number in AH + DEBUG_PRINTF_INTR("%s(): Keyboard Interrupt: function: %x.\n", + __FUNCTION__, 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 = (uint16_t) * 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 = (uint16_t) * keycode; + } else { + c = 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 + + while ((c = 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 = (uint16_t) * keycode; + //X86EMU_trace_on(); + //M.x86.debug &= ~DEBUG_DECODE_NOPRINT_F; + } + } + } + break; + default: + printf("%s(): unknown function (%x) for int16 handler.\n", + __FUNCTION__, 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() +{ + // function number in AX + uint8_t bus, devfn, offs; + 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 + // NOTE: we currently only allow the device to find itself... + // it SHOULD be all we ever need... + // 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", + __FUNCTION__, M.x86.R_AX); + 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; + DEBUG_PRINTF_INTR + ("%s(): function %x: PCI Find Device --> 0x%04x\n", + __FUNCTION__, M.x86.R_AX, M.x86.R_BX); + } else { + DEBUG_PRINTF_INTR + ("%s(): function %x: invalid device/vendor/device index! (%04x/%04x/%02x expected: %04x/%04x/0) \n", + __FUNCTION__, 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; + 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", + __FUNCTION__, 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 = + (uint8_t) rtas_pci_config_read(bios_device. + puid, 1, + bus, devfn, + offs); + DEBUG_PRINTF_INTR + ("%s(): function %x: PCI Config Read @%02x --> 0x%02x\n", + __FUNCTION__, M.x86.R_AX, offs, + M.x86.R_CL); + break; + case 0xb109: + M.x86.R_CX = + (uint16_t) rtas_pci_config_read(bios_device. + puid, 2, + bus, devfn, + offs); + DEBUG_PRINTF_INTR + ("%s(): function %x: PCI Config Read @%02x --> 0x%04x\n", + __FUNCTION__, M.x86.R_AX, offs, + M.x86.R_CX); + break; + case 0xb10a: + M.x86.R_ECX = + (uint32_t) rtas_pci_config_read(bios_device. + puid, 4, + bus, devfn, + offs); + DEBUG_PRINTF_INTR + ("%s(): function %x: PCI Config Read @%02x --> 0x%08x\n", + __FUNCTION__, 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", + __FUNCTION__, 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: + rtas_pci_config_write(bios_device.puid, 1, bus, + devfn, offs, M.x86.R_CL); + DEBUG_PRINTF_INTR + ("%s(): function %x: PCI Config Write @%02x <-- 0x%02x\n", + __FUNCTION__, M.x86.R_AX, offs, + M.x86.R_CL); + break; + case 0xb10c: + rtas_pci_config_write(bios_device.puid, 2, bus, + devfn, offs, M.x86.R_CX); + DEBUG_PRINTF_INTR + ("%s(): function %x: PCI Config Write @%02x <-- 0x%04x\n", + __FUNCTION__, M.x86.R_AX, offs, + M.x86.R_CX); + break; + case 0xb10d: + rtas_pci_config_write(bios_device.puid, 4, bus, + devfn, offs, M.x86.R_ECX); + DEBUG_PRINTF_INTR + ("%s(): function %x: PCI Config Write @%02x <-- 0x%08x\n", + __FUNCTION__, 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", + __FUNCTION__, 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) +{ + uint8_t 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", __FUNCTION__, intNum); +#endif + 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", + __FUNCTION__, 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; + 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() +{ + // 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", + __FUNCTION__); + X86EMU_exec(); + DEBUG_PRINTF_INTR("%s(): execution finished\n", __FUNCTION__); +} + +// prepare and execute Interrupt 13 (Disk Interrupt) +void +runInt13() +{ + // 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", + __FUNCTION__); + X86EMU_exec(); + DEBUG_PRINTF_INTR("%s(): execution finished\n", __FUNCTION__); +}
Added: coreboot-v3/util/x86emu/biosemu/interrupt.h =================================================================== --- coreboot-v3/util/x86emu/biosemu/interrupt.h (rev 0) +++ coreboot-v3/util/x86emu/biosemu/interrupt.h 2008-12-16 22:40:27 UTC (rev 1073) @@ -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 runInt13(); + +#endif
Added: coreboot-v3/util/x86emu/biosemu/io.c =================================================================== --- coreboot-v3/util/x86emu/biosemu/io.c (rev 0) +++ coreboot-v3/util/x86emu/biosemu/io.c 2008-12-16 22:40:27 UTC (rev 1073) @@ -0,0 +1,431 @@ +/****************************************************************************** + * 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 + *****************************************************************************/ + +#include <stdio.h> +#include <cpu.h> +#include "device.h" +#include "rtas.h" +#include "debug.h" +#include "device.h" +#include <stdint.h> +#include <x86emu/x86emu.h> +#include <time.h> + +// 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 uint64_t 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(uint8_t val, uint16_t port) +{ + printf("WARNING: outb not implemented!\n"); + HALT_SYS(); +} + +void +outw(uint16_t val, uint16_t port) +{ + printf("WARNING: outw not implemented!\n"); + HALT_SYS(); +} + +void +outl(uint32_t val, uint16_t port) +{ + printf("WARNING: outl not implemented!\n"); + HALT_SYS(); +} + +uint8_t +inb(uint16_t port) +{ + printf("WARNING: inb not implemented!\n"); + HALT_SYS(); + return 0; +} + +uint16_t +inw(uint16_t port) +{ + printf("WARNING: inw not implemented!\n"); + HALT_SYS(); + return 0; +} + +uint32_t +inl(uint16_t port) +{ + printf("WARNING: inl not implemented!\n"); + HALT_SYS(); + return 0; +} + +uint32_t pci_cfg_read(X86EMU_pioAddr addr, uint8_t size); +void pci_cfg_write(X86EMU_pioAddr addr, uint32_t val, uint8_t size); +uint8_t handle_port_61h(); + +uint8_t +my_inb(X86EMU_pioAddr addr) +{ + uint8_t rval = 0xFF; + uint64_t translated_addr = addr; + uint8_t translated = 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", __FUNCTION__, + addr); + //DEBUG_PRINTF_IO("%s(%04x): translated_addr: %llx\n", __FUNCTION__, addr, translated_addr); + rval = read_io((void *)translated_addr, 1); + DEBUG_PRINTF_IO("%s(%04x) Device I/O --> %02x\n", __FUNCTION__, + 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", __FUNCTION__, addr, rval); + return rval; + break; + case 0xCFC: + case 0xCFD: + case 0xCFE: + case 0xCFF: + // PCI Config Mechanism 1 Ports + return (uint8_t) 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", + __FUNCTION__, addr); + rval = *((uint8_t *) (bios_device.io_buffer + addr)); + DEBUG_PRINTF_IO("%s(%04x) I/O Buffer --> %02x\n", + __FUNCTION__, addr, rval); + return rval; + break; + } + } +} + +uint16_t +my_inw(X86EMU_pioAddr addr) +{ + uint64_t translated_addr = addr; + uint8_t translated = 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", __FUNCTION__, + addr); + //DEBUG_PRINTF_IO("%s(%04x): translated_addr: %llx\n", __FUNCTION__, addr, translated_addr); + uint16_t rval; + if ((translated_addr & (uint64_t) 0x1) == 0) { + // 16 bit aligned access... + uint16_t 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", __FUNCTION__, + addr, rval); + return rval; + } else { + switch (addr) { + case 0xCFC: + case 0xCFE: + //PCI Config Mechanism 1 + return (uint16_t) pci_cfg_read(addr, 2); + break; + default: + DEBUG_PRINTF_IO + ("%s(%04x) reading from bios_device.io_buffer\n", + __FUNCTION__, addr); + uint16_t rval = + in16le((void *) bios_device.io_buffer + addr); + DEBUG_PRINTF_IO("%s(%04x) I/O Buffer --> %04x\n", + __FUNCTION__, addr, rval); + return rval; + break; + } + } +} + +uint32_t +my_inl(X86EMU_pioAddr addr) +{ + uint64_t translated_addr = addr; + uint8_t translated = 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", __FUNCTION__, + addr); + //DEBUG_PRINTF_IO("%s(%04x): translated_addr: %llx\n", __FUNCTION__, addr, translated_addr); + uint32_t rval; + if ((translated_addr & (uint64_t) 0x3) == 0) { + // 32 bit aligned access... + uint32_t 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", __FUNCTION__, + 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", + __FUNCTION__, addr); + uint32_t rval = + in32le((void *) bios_device.io_buffer + addr); + DEBUG_PRINTF_IO("%s(%04x) I/O Buffer --> %08x\n", + __FUNCTION__, addr, rval); + return rval; + break; + } + } +} + +void +my_outb(X86EMU_pioAddr addr, uint8_t val) +{ + uint64_t translated_addr = addr; + uint8_t translated = 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", + __FUNCTION__, addr, val); + //DEBUG_PRINTF_IO("%s(%04x): translated_addr: %llx\n", __FUNCTION__, addr, translated_addr); + write_io((void *) translated_addr, val, 1); + DEBUG_PRINTF_IO("%s(%04x) Device I/O <-- %02x\n", __FUNCTION__, + 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", + __FUNCTION__, addr, val); + *((uint8_t *) (bios_device.io_buffer + addr)) = val; + break; + } + } +} + +void +my_outw(X86EMU_pioAddr addr, uint16_t val) +{ + uint64_t translated_addr = addr; + uint8_t translated = 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", + __FUNCTION__, addr, val); + //DEBUG_PRINTF_IO("%s(%04x): translated_addr: %llx\n", __FUNCTION__, addr, translated_addr); + if ((translated_addr & (uint64_t) 0x1) == 0) { + // little-endian conversion + uint16_t 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)), + (uint8_t) ((val & 0xFF00) >> 8), 1); + write_io(((void *) translated_addr), + (uint8_t) (val & 0x00FF), 1); + } + DEBUG_PRINTF_IO("%s(%04x) Device I/O <-- %04x\n", __FUNCTION__, + 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", + __FUNCTION__, addr, val); + out16le((void *) bios_device.io_buffer + addr, val); + break; + } + } +} + +void +my_outl(X86EMU_pioAddr addr, uint32_t val) +{ + uint64_t translated_addr = addr; + uint8_t translated = 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", + __FUNCTION__, addr, val); + //DEBUG_PRINTF_IO("%s(%04x): translated_addr: %llx\n", __FUNCTION__, addr, translated_addr); + if ((translated_addr & (uint64_t) 0x3) == 0) { + // little-endian conversion + uint32_t 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), + (uint8_t) ((val & 0xFF000000) >> 24), 1); + write_io(((void *) translated_addr + 2), + (uint8_t) ((val & 0x00FF0000) >> 16), 1); + write_io(((void *) translated_addr + 1), + (uint8_t) ((val & 0x0000FF00) >> 8), 1); + write_io(((void *) translated_addr), + (uint8_t) (val & 0x000000FF), 1); + } + DEBUG_PRINTF_IO("%s(%04x) Device I/O <-- %08x\n", __FUNCTION__, + 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", + __FUNCTION__, addr, val); + out32le((void *) bios_device.io_buffer + addr, val); + break; + } + } +} + +uint32_t +pci_cfg_read(X86EMU_pioAddr addr, uint8_t size) +{ + uint32_t rval = 0xFFFFFFFF; + if ((addr >= 0xCFC) && ((addr + size) <= 0xCFF)) { + // 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... + uint8_t bus, devfn, offs; + uint32_t 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 access invalid! bus: %x, devfn: %x, offs: %x\n", + bus, devfn, offs); + HALT_SYS(); + } else { + rval = + (uint32_t) rtas_pci_config_read(bios_device. + puid, size, + bus, devfn, + offs); + DEBUG_PRINTF_IO + ("%s(%04x) PCI Config Read @%02x, size: %d --> 0x%08x\n", + __FUNCTION__, addr, offs, size, rval); + } + } + } + return rval; +} + +void +pci_cfg_write(X86EMU_pioAddr addr, uint32_t val, uint8_t size) +{ + if ((addr >= 0xCFC) && ((addr + size) <= 0xCFF)) { + // 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... + uint8_t bus, devfn, offs; + uint32_t 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 access invalid! bus: %x, devfn: %x, offs: %x\n", + bus, devfn, offs); + HALT_SYS(); + } else { + rtas_pci_config_write(bios_device.puid, + size, bus, devfn, offs, + val); + DEBUG_PRINTF_IO + ("%s(%04x) PCI Config Write @%02x, size: %d <-- 0x%08x\n", + __FUNCTION__, addr, offs, size, val); + } + } + } +} + +uint8_t +handle_port_61h() +{ + static uint64_t last_time = 0; + uint64_t curr_time = get_time(); + uint64_t time_diff; // time since last call + uint32_t period_ticks; // length of a period in ticks + uint32_t 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) { + *((uint8_t *) (bios_device.io_buffer + 0x61)) ^= 0x10; + } + //finally read the value from the io_buffer + return *((uint8_t *) (bios_device.io_buffer + 0x61)); +}
Added: coreboot-v3/util/x86emu/biosemu/io.h =================================================================== --- coreboot-v3/util/x86emu/biosemu/io.h (rev 0) +++ coreboot-v3/util/x86emu/biosemu/io.h 2008-12-16 22:40:27 UTC (rev 1073) @@ -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 <stdint.h> + +uint8_t my_inb(X86EMU_pioAddr addr); + +uint16_t my_inw(X86EMU_pioAddr addr); + +uint32_t my_inl(X86EMU_pioAddr addr); + +void my_outb(X86EMU_pioAddr addr, uint8_t val); + +void my_outw(X86EMU_pioAddr addr, uint16_t val); + +void my_outl(X86EMU_pioAddr addr, uint32_t val); + +#endif
Added: coreboot-v3/util/x86emu/biosemu/mem.c =================================================================== --- coreboot-v3/util/x86emu/biosemu/mem.c (rev 0) +++ coreboot-v3/util/x86emu/biosemu/mem.c 2008-12-16 22:40:27 UTC (rev 1073) @@ -0,0 +1,463 @@ +/****************************************************************************** + * 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 + *****************************************************************************/ + +#include <stdio.h> +#include <stdint.h> +#include <cpu.h> +#include "debug.h" +#include "device.h" +#include "x86emu/x86emu.h" +#include "biosemu.h" +#include <time.h> + +// define a check for access to certain (virtual) memory regions (interrupt handlers, BIOS Data Area, ...) +#ifdef DEBUG +static uint8_t in_check = 0; // to avoid recursion... +uint16_t ebda_segment; +uint32_t 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 +uint8_t my_rdb(uint32_t); +uint16_t my_rdw(uint32_t); +uint32_t my_rdl(uint32_t); + +#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", \ + __FUNCTION__, _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", \ + __FUNCTION__, _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", \ + __FUNCTION__, _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", \ + __FUNCTION__, 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", \ + __FUNCTION__, 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", \ + __FUNCTION__, 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", \ + __FUNCTION__, 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", \ + __FUNCTION__, _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", \ + __FUNCTION__, _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", \ + __FUNCTION__, _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", \ + __FUNCTION__, 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", \ + __FUNCTION__, 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", \ + __FUNCTION__, 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", \ + __FUNCTION__, 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", \ + __FUNCTION__, 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 uint64_t get_time(void); + +void update_time(uint32_t); + +// read byte from memory +uint8_t +my_rdb(uint32_t addr) +{ + uint64_t translated_addr = addr; + uint8_t translated = dev_translate_address(&translated_addr); + uint8_t rval; + if (translated != 0) { + //translation successfull, access VGA Memory (BAR or Legacy...) + DEBUG_PRINTF_MEM("%s(%08x): access to VGA Memory\n", + __FUNCTION__, addr); + //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __FUNCTION__, addr, translated_addr); + set_ci(); + rval = *((uint8_t *) translated_addr); + clr_ci(); + DEBUG_PRINTF_MEM("%s(%08x) VGA --> %02x\n", __FUNCTION__, addr, + rval); + return rval; + } else if (addr > M.mem_size) { + DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n", + __FUNCTION__, addr); + //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1); + HALT_SYS(); + } else { + /* read from virtual memory */ + rval = *((uint8_t *) (M.mem_base + addr)); + DEBUG_CHECK_VMEM_READ(addr, rval); + return rval; + } + return -1; +} + +//read word from memory +uint16_t +my_rdw(uint32_t addr) +{ + uint64_t translated_addr = addr; + uint8_t translated = dev_translate_address(&translated_addr); + uint16_t rval; + if (translated != 0) { + //translation successfull, access VGA Memory (BAR or Legacy...) + DEBUG_PRINTF_MEM("%s(%08x): access to VGA Memory\n", + __FUNCTION__, addr); + //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __FUNCTION__, 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 = ((uint8_t) my_rdb(addr)) | + (((uint8_t) my_rdb(addr + 1)) << 8); + } else { + if ((translated_addr & (uint64_t) 0x1) == 0) { + // 16 bit aligned access... + set_ci(); + rval = in16le((void *) translated_addr); + clr_ci(); + } else { + // unaligned access, read single bytes + set_ci(); + rval = (*((uint8_t *) translated_addr)) | + (*((uint8_t *) translated_addr + 1) << 8); + clr_ci(); + } + } + DEBUG_PRINTF_MEM("%s(%08x) VGA --> %04x\n", __FUNCTION__, addr, + rval); + return rval; + } else if (addr > M.mem_size) { + DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n", + __FUNCTION__, 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 +uint32_t +my_rdl(uint32_t addr) +{ + uint64_t translated_addr = addr; + uint8_t translated = dev_translate_address(&translated_addr); + uint32_t rval; + if (translated != 0) { + //translation successfull, access VGA Memory (BAR or Legacy...) + DEBUG_PRINTF_MEM("%s(%x): access to VGA Memory\n", + __FUNCTION__, addr); + //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __FUNCTION__, 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 = ((uint8_t) my_rdb(addr)) | + (((uint8_t) my_rdb(addr + 1)) << 8) | + (((uint8_t) my_rdb(addr + 2)) << 16) | + (((uint8_t) my_rdb(addr + 3)) << 24); + } else { + if ((translated_addr & (uint64_t) 0x3) == 0) { + // 32 bit aligned access... + set_ci(); + rval = in32le((void *) translated_addr); + clr_ci(); + } else { + // unaligned access, read single bytes + set_ci(); + rval = (*((uint8_t *) translated_addr)) | + (*((uint8_t *) translated_addr + 1) << 8) | + (*((uint8_t *) translated_addr + 2) << 16) | + (*((uint8_t *) translated_addr + 3) << 24); + clr_ci(); + } + } + DEBUG_PRINTF_MEM("%s(%08x) VGA --> %08x\n", __FUNCTION__, addr, + rval); + //HALT_SYS(); + return rval; + } else if (addr > M.mem_size) { + DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n", + __FUNCTION__, 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(uint32_t addr, uint8_t val) +{ + uint64_t translated_addr = addr; + uint8_t translated = 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", + __FUNCTION__, addr, val); + //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __FUNCTION__, addr, translated_addr); + set_ci(); + *((uint8_t *) translated_addr) = val; + clr_ci(); + } else if (addr > M.mem_size) { + DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n", + __FUNCTION__, 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); + *((uint8_t *) (M.mem_base + addr)) = val; + } +} + +void +my_wrw(uint32_t addr, uint16_t val) +{ + uint64_t translated_addr = addr; + uint8_t translated = 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", + __FUNCTION__, addr, val); + //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __FUNCTION__, 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, (uint8_t) (val & 0x00FF)); + my_wrb(addr + 1, (uint8_t) ((val & 0xFF00) >> 8)); + } else { + if ((translated_addr & (uint64_t) 0x1) == 0) { + // 16 bit aligned access... + set_ci(); + out16le((void *) translated_addr, val); + clr_ci(); + } else { + // unaligned access, write single bytes + set_ci(); + *((uint8_t *) translated_addr) = + (uint8_t) (val & 0x00FF); + *((uint8_t *) translated_addr + 1) = + (uint8_t) ((val & 0xFF00) >> 8); + clr_ci(); + } + } + } else if (addr > M.mem_size) { + DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n", + __FUNCTION__, 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(uint32_t addr, uint32_t val) +{ + uint64_t translated_addr = addr; + uint8_t translated = 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", + __FUNCTION__, addr, val); + //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __FUNCTION__, 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, (uint8_t) (val & 0x000000FF)); + my_wrb(addr + 1, (uint8_t) ((val & 0x0000FF00) >> 8)); + my_wrb(addr + 2, (uint8_t) ((val & 0x00FF0000) >> 16)); + my_wrb(addr + 3, (uint8_t) ((val & 0xFF000000) >> 24)); + } else { + if ((translated_addr & (uint64_t) 0x3) == 0) { + // 32 bit aligned access... + set_ci(); + out32le((void *) translated_addr, val); + clr_ci(); + } else { + // unaligned access, write single bytes + set_ci(); + *((uint8_t *) translated_addr) = + (uint8_t) (val & 0x000000FF); + *((uint8_t *) translated_addr + 1) = + (uint8_t) ((val & 0x0000FF00) >> 8); + *((uint8_t *) translated_addr + 2) = + (uint8_t) ((val & 0x00FF0000) >> 16); + *((uint8_t *) translated_addr + 3) = + (uint8_t) ((val & 0xFF000000) >> 24); + clr_ci(); + } + } + } else if (addr > M.mem_size) { + DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n", + __FUNCTION__, 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(uint32_t cur_val) +{ + //for convenience, we let the start of timebase be at midnight, we currently dont support + //real daytime anyway... + uint64_t ticks_per_day = tb_freq * 60 * 24; + // at 18Hz a period is ~55ms, converted to ticks (tb_freq is ticks/second) + uint32_t period_ticks = (55 * tb_freq) / 1000; + uint64_t curr_time = get_time(); + uint64_t ticks_since_midnight = curr_time % ticks_per_day; + uint32_t 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); +}
Added: coreboot-v3/util/x86emu/biosemu/mem.h =================================================================== --- coreboot-v3/util/x86emu/biosemu/mem.h (rev 0) +++ coreboot-v3/util/x86emu/biosemu/mem.h 2008-12-16 22:40:27 UTC (rev 1073) @@ -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 <stdint.h> + +// read byte from memory +uint8_t my_rdb(uint32_t addr); + +//read word from memory +uint16_t my_rdw(uint32_t addr); + +//read long from memory +uint32_t my_rdl(uint32_t addr); + +//write byte to memory +void my_wrb(uint32_t addr, uint8_t val); + +//write word to memory +void my_wrw(uint32_t addr, uint16_t val); + +//write long to memory +void my_wrl(uint32_t addr, uint32_t val); + +#endif
Added: coreboot-v3/util/x86emu/biosemu/vbe.c =================================================================== --- coreboot-v3/util/x86emu/biosemu/vbe.c (rev 0) +++ coreboot-v3/util/x86emu/biosemu/vbe.c 2008-12-16 22:40:27 UTC (rev 1073) @@ -0,0 +1,775 @@ +/****************************************************************************** + * 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 + *****************************************************************************/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <stdint.h> +#include <cpu.h> + +#include "debug.h" + +#include <x86emu/x86emu.h> +#include <x86emu/regs.h> +#include <x86emu/prim_ops.h> // for push_word + +#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 +uint8_t *vbe_info_buffer = 0; +// virtual BIOS Memory +uint8_t *biosmem; +uint32_t biosmem_size; + +// these structs are for input from and output to OF +typedef struct { + uint8_t display_type; // 0=NONE, 1= analog, 2=digital + uint16_t screen_width; + uint16_t screen_height; + uint16_t screen_linebytes; // bytes per line in framebuffer, may be more than screen_width + uint8_t color_depth; // color depth in bpp + uint32_t framebuffer_address; + uint8_t edid_block_zero[128]; +} __attribute__ ((__packed__)) screen_info_t; + +typedef struct { + uint8_t signature[4]; + uint16_t size_reserved; + uint8_t monitor_number; + uint16_t max_screen_width; + uint8_t 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]; + uint16_t version; + uint8_t *oem_string_ptr; + uint32_t capabilities; + uint16_t video_mode_list[256]; // lets hope we never have more than 256 video modes... + uint16_t total_memory; +} vbe_info_t; + +typedef struct { + uint16_t video_mode; + uint8_t mode_info_block[256]; + uint16_t attributes; + uint16_t linebytes; + uint16_t x_resolution; + uint16_t y_resolution; + uint8_t x_charsize; + uint8_t y_charsize; + uint8_t bits_per_pixel; + uint8_t memory_model; + uint32_t framebuffer_address; +} vbe_mode_info_t; + +typedef struct { + uint8_t port_number; // i.e. monitor number + uint8_t edid_transfer_time; + uint8_t ddc_level; + uint8_t edid_block_zero[128]; +} vbe_ddc_info_t; + +static inline uint8_t +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 +uint8_t +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", + __FUNCTION__, 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", + __FUNCTION__, 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 + uint16_t *video_mode_ptr; + video_mode_ptr = + (uint16_t *) (biosmem + + ((in16le(vbe_info_buffer + 16) << 4) + + in16le(vbe_info_buffer + 14))); + uint32_t 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 +uint8_t +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", + __FUNCTION__, 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", + __FUNCTION__, 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 +uint8_t +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", __FUNCTION__, + 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", + __FUNCTION__, 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", + __FUNCTION__, mode_info->video_mode, M.x86.R_AH); + return M.x86.R_AH; + } + return 0; +} + +//VBE Function 08h +uint8_t +vbe_set_palette_format(uint8_t 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", __FUNCTION__, + 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", + __FUNCTION__, 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", + __FUNCTION__, M.x86.R_AH); + return M.x86.R_AH; + } + return 0; +} + +// VBE Function 09h +uint8_t +vbe_set_color(uint16_t color_number, uint32_t 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", __FUNCTION__, + 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", + __FUNCTION__, 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", + __FUNCTION__, M.x86.R_AH); + return M.x86.R_AH; + } + return 0; +} + +uint8_t +vbe_get_color(uint16_t color_number, uint32_t * 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", + __FUNCTION__, 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", + __FUNCTION__, 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", __FUNCTION__, + color_number, *color_value); + + return 0; +} + +// VBE Function 15h +uint8_t +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", + __FUNCTION__, 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", + __FUNCTION__, 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", + __FUNCTION__, 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", + __FUNCTION__, 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; +} + +uint32_t +vbe_get_info(uint8_t argc, char ** argv) +{ + uint8_t rval; + uint32_t 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 = (uint8_t *) 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", + __FUNCTION__, "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", + __FUNCTION__, (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 (*((uint64_t *) ddc_info.edid_block_zero) != + (uint64_t) 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... + + uint8_t mixed_color_values[6] = + { 0xFF, 0xDA, 0xB3, 0x87, 0x54, 0x00 }; + uint8_t primary_color_values[10] = + { 0xF3, 0xE7, 0xCD, 0xC0, 0xA5, 0x96, 0x77, 0x66, 0x3F, + 0x27 + }; + uint8_t mc_size = sizeof(mixed_color_values); + uint8_t prim_size = sizeof(primary_color_values); + + uint8_t curr_color_index; + uint32_t curr_color; + + uint8_t 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 |= ((uint32_t) mixed_color_values[r]) << 16; //red value + curr_color |= ((uint32_t) mixed_color_values[g]) << 8; //green value + curr_color |= (uint32_t) 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 = ((uint32_t) 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 = ((uint32_t) 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 = (uint32_t) 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 |= ((uint32_t) primary_color_values[i]) << 16; //red + curr_color |= ((uint32_t) primary_color_values[i]) << 8; //green + curr_color |= ((uint32_t) 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", __FUNCTION__); + //unset display_type... + output->display_type = 0; + } + return 0; +}
Added: coreboot-v3/util/x86emu/biosemu/vbe.h =================================================================== --- coreboot-v3/util/x86emu/biosemu/vbe.h (rev 0) +++ coreboot-v3/util/x86emu/biosemu/vbe.h 2008-12-16 22:40:27 UTC (rev 1073) @@ -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