From 54f58ebae799367bfe394952ca69450050fd48dd Mon Sep 17 00:00:00 2001 From: Myles Watson Date: Tue, 23 Dec 2008 19:21:54 +0000 Subject: Subject: [PATCH] YABEL: add PMM functionality. Signed-off-by: Pattrick Hueper Acked-by: Myles Watson git-svn-id: svn://coreboot.org/repository/coreboot-v3@1086 f3766cd6-281f-0410-b1cd-43a5c92072e9 --- util/x86emu/Makefile | 1 + util/x86emu/yabel/biosemu.c | 24 ++- util/x86emu/yabel/interrupt.c | 8 + util/x86emu/yabel/pmm.c | 437 ++++++++++++++++++++++++++++++++++++++++++ util/x86emu/yabel/pmm.h | 41 ++++ 5 files changed, 508 insertions(+), 3 deletions(-) create mode 100644 util/x86emu/yabel/pmm.c create mode 100644 util/x86emu/yabel/pmm.h diff --git a/util/x86emu/Makefile b/util/x86emu/Makefile index b50b3b42a0c8..02f9f986b9d2 100644 --- a/util/x86emu/Makefile +++ b/util/x86emu/Makefile @@ -29,6 +29,7 @@ ifeq ($(CONFIG_PCI_OPTION_ROM_RUN_YABEL),y) BIOSEMU_SRC = biosemu.c debug.c device.c mem.c io.c interrupt.c #TODO: add vbe.c, currently not needed... #BIOSEMU_SRC +=vbe.c +BIOSEMU_SRC +=pmm.c #PH: TODO: remove the compat files?? BIOSEMU_SRC += compat/functions.c X86EMU_INCLUDE += -I $(src)/util/x86emu/yabel diff --git a/util/x86emu/yabel/biosemu.c b/util/x86emu/yabel/biosemu.c index c741e4fecdbe..c188e2baaa10 100644 --- a/util/x86emu/yabel/biosemu.c +++ b/util/x86emu/yabel/biosemu.c @@ -26,6 +26,7 @@ #include "mem.h" #include "interrupt.h" #include "device.h" +#include "pmm.h" #include @@ -56,7 +57,9 @@ biosemu(u8 *biosmem, u32 biosmem_size, struct device * dev, unsigned long rom_ad u8 *rom_image; int i = 0; #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; + 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 (biosmem_size < MIN_REQUIRED_VMEM_SIZE) { printf("Error: Not enough virtual memory: %x, required: %x!\n", @@ -209,6 +212,20 @@ biosemu(u8 *biosmem, u32 biosmem_size, struct device * dev, unsigned long rom_ad X86EMU_setupPioFuncs(&my_pio_funcs); X86EMU_setupMemFuncs(&my_mem_funcs); + //setup PMM struct in BIOS_DATA_SEGMENT, offset 0x0 + u8 pmm_length = pmm_setup(BIOS_DATA_SEGMENT, 0x0); + if (pmm_length <= 0) { + printf ("\nYABEL: Warning: PMM Area could not be setup. PMM not available (%x)\n", + pmm_length); + return 0; + } else { + CHECK_DBG(DEBUG_PMM) { + /* test the PMM */ + pmm_test(); + /* and clean it again by calling pmm_setup... */ + pmm_length = pmm_setup(BIOS_DATA_SEGMENT, 0x0); + } + } // setup the CPU M.x86.R_AH = bios_device.bus; M.x86.R_AL = bios_device.devfn; @@ -248,8 +265,9 @@ biosemu(u8 *biosmem, u32 biosmem_size, struct device * dev, unsigned long rom_ad 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 + /* According to the PNP BIOS Spec, Option ROMs should upon exit, return + * some boot device status in AX (see PNP BIOS Spec Section 3.3 + */ DEBUG_PRINTF_CS_IP("Option ROM Exit Status: %04x\n", M.x86.R_AX); #ifdef DEBUG DEBUG_PRINTF("Exit Status Decode:\n"); diff --git a/util/x86emu/yabel/interrupt.c b/util/x86emu/yabel/interrupt.c index 442125ac46fb..4573150c8506 100644 --- a/util/x86emu/yabel/interrupt.c +++ b/util/x86emu/yabel/interrupt.c @@ -16,6 +16,7 @@ #include "mem.h" #include "device.h" #include "debug.h" +#include "pmm.h" #include #include @@ -540,6 +541,13 @@ handleInterrupt(int intNum) handleInt1a(); int_handled = 1; break; + case PMM_INT_NUM: + /* the selfdefined PMM INT number, this is called by the code in PMM struct, it + * is handled by pmm_handleInt() + */ + pmm_handleInt(); + int_handled = 1; + break; default: printf("Interrupt %#x (Vector: %x) not implemented\n", intNum, my_rdl(intNum * 4)); diff --git a/util/x86emu/yabel/pmm.c b/util/x86emu/yabel/pmm.c new file mode 100644 index 000000000000..b6ec49f74374 --- /dev/null +++ b/util/x86emu/yabel/pmm.c @@ -0,0 +1,437 @@ +/**************************************************************************** + * YABEL BIOS Emulator + * + * Copyright 2008 Pattrick Hueper + ****************************************************************************/ + +#include +#include +#include + +#include "biosemu.h" +#include "pmm.h" +#include "debug.h" +#include "device.h" + +/* this struct is used to remember which PMM spaces + * have been assigned. MAX_PMM_AREAS defines how many + * PMM areas we can assign. + * All areas are assigned in PMM_CONV_SEGMENT + */ +typedef struct { + u32 handle; /* handle that is returned to PMM caller */ + u32 offset; /* in PMM_CONV_SEGMENT */ + u32 length; /* length of this area */ +} pmm_allocation_t; + +#define MAX_PMM_AREAS 10 + +/* array to store the above structs */ +static pmm_allocation_t pmm_allocation_array[MAX_PMM_AREAS]; + +/* index into pmm_allocation_array */ +static u32 curr_pmm_allocation_index = 0; + +/* This function is used to setup the PMM struct in virtual memory + * at a certain offset, the length of the PMM struct is returned */ +u8 pmm_setup(u16 segment, u16 offset) +{ + /* setup the PMM structure */ + pmm_information_t *pis = + (pmm_information_t *) (M.mem_base + (((u32) segment) << 4) + + offset); + memset(pis, 0, sizeof(pmm_information_t)); + /* set signature to $PMM */ + pis->signature[0] = '$'; + pis->signature[1] = 'P'; + pis->signature[2] = 'M'; + pis->signature[3] = 'M'; + /* revision as specified */ + pis->struct_rev = 0x01; + /* internal length, excluding code */ + pis->length = ((void *)&(pis->code) - (void *)&(pis->signature)); + /* the code to be executed, pointed to by entry_point_offset */ + pis->code[0] = 0xCD; /* INT */ + pis->code[1] = PMM_INT_NUM; /* my selfdefined PMM INT number */ + pis->code[2] = 0xCB; /* RETF */ + /* set the entry_point_offset, it should point to pis->code, segment is the segment of + * this struct. Since pis->length is the length of the struct excluding code, offset+pis->length + * points to the code... it's that simple ;-) + */ + out32le(&(pis->entry_point_offset), + (u32) segment << 16 | (u32) (offset + pis->length)); + /* checksum calculation */ + u8 i; + u8 checksum = 0; + for (i = 0; i < pis->length; i++) { + checksum += *(((u8 *) pis) + i); + } + pis->checksum = ((u8) 0) - checksum; + CHECK_DBG(DEBUG_PMM) { + DEBUG_PRINTF_PMM("PMM Structure:\n"); + dump((void *)pis, sizeof(pmm_information_t)); + } + return sizeof(pmm_information_t); +} + +/* handle the selfdefined interrupt, this is executed, when the PMM Entry Point + * is executed, it must handle all PMM requests + */ +void pmm_handleInt() +{ + u32 rval = 0; + u16 function, flags; + u32 handle, length; + u32 i, j; + u32 buffer; + /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * according to the PMM Spec "the flags and all registers, except DX and AX + * are preserved across calls to PMM" + * so we save M.x86 and in :exit label we restore it, however, this means that no + * returns must be used in this function, any exit must use goto exit! + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + */ + X86EMU_regs backup_regs = M.x86; + pop_long(); /* pop the return address, this is already saved in INT handler, we don't need + to remember this. */ + function = pop_word(); + switch (function) { + case 0: + /* function pmmAllocate */ + length = pop_long(); + length *= 16; /* length is passed in "paragraphs" of 16 bytes each */ + handle = pop_long(); + flags = pop_word(); + DEBUG_PRINTF_PMM + ("%s: pmmAllocate: Length: %x, Handle: %x, Flags: %x\n", + __FUNCTION__, length, handle, flags); + if ((flags & 0x1) != 0) { + /* request to allocate in conventional memory */ + if (curr_pmm_allocation_index >= MAX_PMM_AREAS) { + printf + ("%s: pmmAllocate: Maximum Number of allocatable areas reached (%d), cannot allocate more memory!\n", + __FUNCTION__, MAX_PMM_AREAS); + rval = 0; + goto exit; + } + /* some ROMs seem to be confused by offset 0, so lets start at 0x100 */ + u32 next_offset = 0x100; + pmm_allocation_t *pmm_alloc = + &(pmm_allocation_array[curr_pmm_allocation_index]); + if (curr_pmm_allocation_index != 0) { + /* we have already allocated... get the new next_offset + * from the previous pmm_allocation_t */ + next_offset = + pmm_allocation_array + [curr_pmm_allocation_index - 1].offset + + pmm_allocation_array + [curr_pmm_allocation_index - 1].length; + } + DEBUG_PRINTF_PMM("%s: next_offset: 0x%x\n", + __FUNCTION__, next_offset); + if (length == 0) { + /* largest possible block size requested, we have on segment + * to allocate, so largest possible is segment size (0xFFFF) + * minus next_offset + */ + rval = 0xFFFF - next_offset; + goto exit; + } + u32 align = 0; + if (((flags & 0x4) != 0) && (length > 0)) { + /* align to least significant bit set in length param */ + u8 lsb = 0; + while (((length >> lsb) & 0x1) == 0) { + lsb++; + } + align = 1 << lsb; + } + /* always align at least to paragraph (16byte) boundary + * hm... since the length is always in paragraphs, we cannot + * align outside of paragraphs anyway... so this check might + * be unnecessary...*/ + if (align < 0x10) { + align = 0x10; + } + DEBUG_PRINTF_PMM("%s: align: 0x%x\n", __FUNCTION__, + align); + if ((next_offset & (align - 1)) != 0) { + /* not yet aligned... align! */ + next_offset += align; + next_offset &= ~(align - 1); + } + if ((next_offset + length) > 0xFFFF) { + rval = 0; + printf + ("%s: pmmAllocate: Not enough memory available for allocation!\n", + __FUNCTION__); + goto exit; + } + curr_pmm_allocation_index++; + /* remember the values in pmm_allocation_array */ + pmm_alloc->handle = handle; + pmm_alloc->offset = next_offset; + pmm_alloc->length = length; + /* return the 32bit "physical" address, i.e. combination of segment and offset */ + rval = ((u32) (PMM_CONV_SEGMENT << 16)) | next_offset; + DEBUG_PRINTF_PMM + ("%s: pmmAllocate: allocated memory at %x\n", + __FUNCTION__, rval); + } else { + rval = 0; + printf + ("%s: pmmAllocate: allocation in extended memory not supported!\n", + __FUNCTION__); + } + goto exit; + case 1: + /* function pmmFind */ + handle = pop_long(); /* the handle to lookup */ + DEBUG_PRINTF_PMM("%s: pmmFind: Handle: %x\n", __FUNCTION__, + handle); + i = 0; + for (i = 0; i < curr_pmm_allocation_index; i++) { + if (pmm_allocation_array[i].handle == handle) { + DEBUG_PRINTF_PMM + ("%s: pmmFind: found allocated memory at %x\n", + __FUNCTION__, rval); + /* return the 32bit "physical" address, i.e. combination of segment and offset */ + rval = + ((u32) (PMM_CONV_SEGMENT << 16)) | + pmm_allocation_array[i].offset; + } + } + if (rval == 0) { + DEBUG_PRINTF_PMM + ("%s: pmmFind: handle (%x) not found!\n", + __FUNCTION__, handle); + } + goto exit; + case 2: + /* function pmmDeallocate */ + buffer = pop_long(); + /* since argument is the address of the PMM block (including the segment, + * we need to remove the segment to get the offset + */ + buffer = buffer ^ ((u32) PMM_CONV_SEGMENT << 16); + DEBUG_PRINTF_PMM("%s: pmmDeallocate: PMM segment offset: %x\n", + __FUNCTION__, buffer); + i = 0; + /* rval = 0 means we deallocated the buffer, so set it to 1 in case we dont find it and + * thus cannot deallocate + */ + rval = 1; + for (i = 0; i < curr_pmm_allocation_index; i++) { + DEBUG_PRINTF_PMM("%d: %x\n", i, + pmm_allocation_array[i].handle); + if (pmm_allocation_array[i].offset == buffer) { + /* we found the requested buffer, rval = 0 */ + rval = 0; + DEBUG_PRINTF_PMM + ("%s: pmmDeallocate: found allocated memory at index: %d\n", + __FUNCTION__, i); + /* copy the remaining elements in pmm_allocation_array one position up */ + j = i; + for (; j < curr_pmm_allocation_index; j++) { + pmm_allocation_array[j] = + pmm_allocation_array[j + 1]; + } + /* move curr_pmm_allocation_index one up, too */ + curr_pmm_allocation_index--; + /* finally clean last element */ + pmm_allocation_array[curr_pmm_allocation_index]. + handle = 0; + pmm_allocation_array[curr_pmm_allocation_index]. + offset = 0; + pmm_allocation_array[curr_pmm_allocation_index]. + length = 0; + break; + } + } + if (rval != 0) { + DEBUG_PRINTF_PMM + ("%s: pmmDeallocate: offset (%x) not found, cannot deallocate!\n", + __FUNCTION__, buffer); + } + goto exit; + default: + /* invalid/unimplemented function */ + printf("%s: invalid PMM function (0x%04x) called!\n", + __FUNCTION__, function); + /* PMM spec says if function is invalid, return 0xFFFFFFFF */ + rval = 0xFFFFFFFF; + goto exit; + } + exit: + /* exit handler of this function, restore registers, put return value in DX:AX */ + M.x86 = backup_regs; + M.x86.R_DX = (u16) ((rval >> 16) & 0xFFFF); + M.x86.R_AX = (u16) (rval & 0xFFFF); + CHECK_DBG(DEBUG_PMM) { + DEBUG_PRINTF_PMM("%s: dump of pmm_allocation_array:\n", + __FUNCTION__); + for (i = 0; i < MAX_PMM_AREAS; i++) { + DEBUG_PRINTF_PMM + ("%d:\n\thandle: %x\n\toffset: %x\n\tlength: %x\n", + i, pmm_allocation_array[i].handle, + pmm_allocation_array[i].offset, + pmm_allocation_array[i].length); + } + } + return; +} + +/* This function tests the pmm_handleInt() function above. */ +void pmm_test(void) +{ + u32 handle, length, addr; + u16 function, flags; + /*-------------------- Test simple allocation/find/deallocation ----------------------------- */ + function = 0; /* pmmAllocate */ + handle = 0xdeadbeef; + length = 16; /* in 16byte paragraphs, so we allocate 256 bytes... */ + flags = 0x1; /* conventional memory, unaligned */ + /* setup stack for call to pmm_handleInt() */ + push_word(flags); + push_long(handle); + push_long(length); + push_word(function); + push_long(0); /* This is the return address for the ABI, unused in this implementation */ + pmm_handleInt(); + addr = ((u32) M.x86.R_DX << 16) | M.x86.R_AX; + DEBUG_PRINTF_PMM("%s: allocated memory at: %04x:%04x\n", __FUNCTION__, + M.x86.R_DX, M.x86.R_AX); + function = 1; /* pmmFind */ + push_long(handle); + push_word(function); + push_long(0); /* This is the return address for the ABI, unused in this implementation */ + pmm_handleInt(); + DEBUG_PRINTF_PMM("%s: found memory at: %04x:%04x (expected: %08x)\n", + __FUNCTION__, M.x86.R_DX, M.x86.R_AX, addr); + function = 2; /* pmmDeallocate */ + push_long(addr); + push_word(function); + push_long(0); /* This is the return address for the ABI, unused in this implementation */ + pmm_handleInt(); + DEBUG_PRINTF_PMM + ("%s: freed memory rval: %04x:%04x (expected: 0000:0000)\n", + __FUNCTION__, M.x86.R_DX, M.x86.R_AX); + /*-------------------- Test aligned allocation/deallocation ----------------------------- */ + function = 0; /* pmmAllocate */ + handle = 0xdeadbeef; + length = 257; /* in 16byte paragraphs, so we allocate 4KB + 16 bytes... */ + flags = 0x1; /* conventional memory, unaligned */ + /* setup stack for call to pmm_handleInt() */ + push_word(flags); + push_long(handle); + push_long(length); + push_word(function); + push_long(0); /* This is the return address for the ABI, unused in this implementation */ + pmm_handleInt(); + addr = ((u32) M.x86.R_DX << 16) | M.x86.R_AX; + DEBUG_PRINTF_PMM("%s: allocated memory at: %04x:%04x\n", __FUNCTION__, + M.x86.R_DX, M.x86.R_AX); + function = 0; /* pmmAllocate */ + handle = 0xf00d4b0b; + length = 128; /* in 16byte paragraphs, so we allocate 2KB... */ + flags = 0x5; /* conventional memory, aligned */ + /* setup stack for call to pmm_handleInt() */ + push_word(flags); + push_long(handle); + push_long(length); + push_word(function); + push_long(0); /* This is the return address for the ABI, unused in this implementation */ + pmm_handleInt(); + /* the address should be aligned to 0x800, so probably it is at offset 0x1800... */ + addr = ((u32) M.x86.R_DX << 16) | M.x86.R_AX; + DEBUG_PRINTF_PMM("%s: allocated memory at: %04x:%04x\n", __FUNCTION__, + M.x86.R_DX, M.x86.R_AX); + function = 1; /* pmmFind */ + push_long(handle); + push_word(function); + push_long(0); /* This is the return address for the ABI, unused in this implementation */ + pmm_handleInt(); + addr = ((u32) M.x86.R_DX << 16) | M.x86.R_AX; + function = 2; /* pmmDeallocate */ + push_long(addr); + push_word(function); + push_long(0); /* This is the return address for the ABI, unused in this implementation */ + pmm_handleInt(); + DEBUG_PRINTF_PMM + ("%s: freed memory rval: %04x:%04x (expected: 0000:0000)\n", + __FUNCTION__, M.x86.R_DX, M.x86.R_AX); + handle = 0xdeadbeef; + function = 1; /* pmmFind */ + push_long(handle); + push_word(function); + push_long(0); /* This is the return address for the ABI, unused in this implementation */ + pmm_handleInt(); + addr = ((u32) M.x86.R_DX << 16) | M.x86.R_AX; + function = 2; /* pmmDeallocate */ + push_long(addr); + push_word(function); + push_long(0); /* This is the return address for the ABI, unused in this implementation */ + pmm_handleInt(); + DEBUG_PRINTF_PMM + ("%s: freed memory rval: %04x:%04x (expected: 0000:0000)\n", + __FUNCTION__, M.x86.R_DX, M.x86.R_AX); + /*-------------------- Test out of memory allocation ----------------------------- */ + function = 0; /* pmmAllocate */ + handle = 0xdeadbeef; + length = 0; /* length zero means, give me the largest possible block */ + flags = 0x1; /* conventional memory, unaligned */ + /* setup stack for call to pmm_handleInt() */ + push_word(flags); + push_long(handle); + push_long(length); + push_word(function); + push_long(0); /* This is the return address for the ABI, unused in this implementation */ + pmm_handleInt(); + length = ((u32) M.x86.R_DX << 16) | M.x86.R_AX; + length /= 16; /* length in paragraphs */ + DEBUG_PRINTF_PMM("%s: largest possible length: %08x\n", __FUNCTION__, + length); + function = 0; /* pmmAllocate */ + flags = 0x1; /* conventional memory, aligned */ + /* setup stack for call to pmm_handleInt() */ + push_word(flags); + push_long(handle); + push_long(length); + push_word(function); + push_long(0); /* This is the return address for the ABI, unused in this implementation */ + pmm_handleInt(); + addr = ((u32) M.x86.R_DX << 16) | M.x86.R_AX; + DEBUG_PRINTF_PMM("%s: allocated memory at: %04x:%04x\n", __FUNCTION__, + M.x86.R_DX, M.x86.R_AX); + function = 0; /* pmmAllocate */ + length = 1; + handle = 0xf00d4b0b; + flags = 0x1; /* conventional memory, aligned */ + /* setup stack for call to pmm_handleInt() */ + push_word(flags); + push_long(handle); + push_long(length); + push_word(function); + push_long(0); /* This is the return address for the ABI, unused in this implementation */ + pmm_handleInt(); + /* this should fail, so 0x0 should be returned */ + addr = ((u32) M.x86.R_DX << 16) | M.x86.R_AX; + DEBUG_PRINTF_PMM + ("%s: allocated memory at: %04x:%04x expected: 0000:0000\n", + __FUNCTION__, M.x86.R_DX, M.x86.R_AX); + handle = 0xdeadbeef; + function = 1; /* pmmFind */ + push_long(handle); + push_word(function); + push_long(0); /* This is the return address for the ABI, unused in this implementation */ + pmm_handleInt(); + addr = ((u32) M.x86.R_DX << 16) | M.x86.R_AX; + function = 2; /* pmmDeallocate */ + push_long(addr); + push_word(function); + push_long(0); /* This is the return address for the ABI, unused in this implementation */ + pmm_handleInt(); + DEBUG_PRINTF_PMM + ("%s: freed memory rval: %04x:%04x (expected: 0000:0000)\n", + __FUNCTION__, M.x86.R_DX, M.x86.R_AX); +} diff --git a/util/x86emu/yabel/pmm.h b/util/x86emu/yabel/pmm.h new file mode 100644 index 000000000000..56298adacc9f --- /dev/null +++ b/util/x86emu/yabel/pmm.h @@ -0,0 +1,41 @@ +/**************************************************************************** + * YABEL BIOS Emulator + * + * Copyright 2008 Pattrick Hueper + ****************************************************************************/ + +#ifndef _YABEL_PMM_H_ +#define _YABEL_PMM_H_ + +#include + +/* PMM Structure see PMM Spec Version 1.01 Chapter 3.1.1 + * (search web for specspmm101.pdf) + */ +typedef struct { + u8 signature[4]; + u8 struct_rev; + u8 length; + u8 checksum; + u32 entry_point_offset; + u8 reserved[5]; + /* Code is not part of the speced PMM struct, however, since I cannot + * put the handling of PMM in the virtual memory (I dont want to hack it + * together in x86 assembly ;-)) this code array is pointed to by + * entry_point_offset, in code there is only a INT call and a RETF, + * thus every PMM call will issue a PMM INT (only defined in YABEL, + * see interrupt.c) and the INT Handler will do the actual PMM work. + */ + u8 code[3]; +} __attribute__ ((__packed__)) pmm_information_t; + +/* This function is used to setup the PMM struct in virtual memory + * at a certain offset */ +u8 pmm_setup(u16 segment, u16 offset); + +/* This is the INT Handler mentioned above, called by my special PMM INT. */ +void pmm_handleInt(void); + +void pmm_test(void); + +#endif // _YABEL_PMM_H -- cgit v1.2.3