summaryrefslogtreecommitdiff
path: root/util/x86emu/yabel/pmm.c
diff options
context:
space:
mode:
authorStefan Reinauer <stepan@coresystems.de>2009-08-11 21:28:25 +0000
committerStefan Reinauer <stepan@openbios.org>2009-08-11 21:28:25 +0000
commit38cd29ebd7282333650cf11ed50c7f2fd4031e80 (patch)
tree61008ca7d9a8e4ad5e5237e5f7cbc982a8206c55 /util/x86emu/yabel/pmm.c
parentb339e10f04869a3d8da31e7d52831c32c57302a2 (diff)
downloadcoreboot-38cd29ebd7282333650cf11ed50c7f2fd4031e80.tar.xz
Don't pull in x86emu from a foreign directory anymore. This
produced numerous problems in the past, including the fact that x86emu doesn't work in v3 anymore even though it lives in the v3 repository. Since this is a cross-repository move, keeping the history in the v2 tree would make life hard for everone. So check the v3 repository for x86emu history since the merger. The his commit is based on an svn export of r1175 of the coreboot-v3 repository. Signed-off-by: Stefan Reinauer <stepan@coresystems.de> Acked-by: Patrick Georgi <patrick.georgi@coresystems.de> git-svn-id: svn://svn.coreboot.org/coreboot/trunk@4532 2b7e53f0-3cfb-0310-b3e9-8179ed1497e1
Diffstat (limited to 'util/x86emu/yabel/pmm.c')
-rw-r--r--util/x86emu/yabel/pmm.c446
1 files changed, 446 insertions, 0 deletions
diff --git a/util/x86emu/yabel/pmm.c b/util/x86emu/yabel/pmm.c
new file mode 100644
index 0000000000..0b78ca5330
--- /dev/null
+++ b/util/x86emu/yabel/pmm.c
@@ -0,0 +1,446 @@
+/****************************************************************************
+ * YABEL BIOS Emulator
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Copyright (c) 2008 Pattrick Hueper <phueper@hueper.net>
+ ****************************************************************************/
+
+#include <x86emu/x86emu.h>
+#ifdef CONFIG_COREBOOT_V2
+#include "../x86emu/prim_ops.h"
+#else
+#include <x86emu/prim_ops.h>
+#endif
+#include <string.h>
+
+#include "biosemu.h"
+#include "pmm.h"
+#include "debug.h"
+#include "device.h"
+
+/* this struct is used to remember which PMM spaces
+ * have been assigned. MAX_PMM_AREAS defines how many
+ * PMM areas we can assign.
+ * All areas are assigned in PMM_CONV_SEGMENT
+ */
+typedef struct {
+ u32 handle; /* handle that is returned to PMM caller */
+ u32 offset; /* in PMM_CONV_SEGMENT */
+ u32 length; /* length of this area */
+} pmm_allocation_t;
+
+#define MAX_PMM_AREAS 10
+
+/* array to store the above structs */
+static pmm_allocation_t pmm_allocation_array[MAX_PMM_AREAS];
+
+/* index into pmm_allocation_array */
+static u32 curr_pmm_allocation_index = 0;
+
+/* This function is used to setup the PMM struct in virtual memory
+ * at a certain offset, the length of the PMM struct is returned */
+u8 pmm_setup(u16 segment, u16 offset)
+{
+ /* setup the PMM structure */
+ pmm_information_t *pis =
+ (pmm_information_t *) (M.mem_base + (((u32) segment) << 4) +
+ offset);
+ memset(pis, 0, sizeof(pmm_information_t));
+ /* set signature to $PMM */
+ pis->signature[0] = '$';
+ pis->signature[1] = 'P';
+ pis->signature[2] = 'M';
+ pis->signature[3] = 'M';
+ /* revision as specified */
+ pis->struct_rev = 0x01;
+ /* internal length, excluding code */
+ pis->length = ((void *)&(pis->code) - (void *)&(pis->signature));
+ /* the code to be executed, pointed to by entry_point_offset */
+ pis->code[0] = 0xCD; /* INT */
+ pis->code[1] = PMM_INT_NUM; /* my selfdefined PMM INT number */
+ pis->code[2] = 0xCB; /* RETF */
+ /* set the entry_point_offset, it should point to pis->code, segment is the segment of
+ * this struct. Since pis->length is the length of the struct excluding code, offset+pis->length
+ * points to the code... it's that simple ;-)
+ */
+ out32le(&(pis->entry_point_offset),
+ (u32) segment << 16 | (u32) (offset + pis->length));
+ /* checksum calculation */
+ u8 i;
+ u8 checksum = 0;
+ for (i = 0; i < pis->length; i++) {
+ checksum += *(((u8 *) pis) + i);
+ }
+ pis->checksum = ((u8) 0) - checksum;
+ CHECK_DBG(DEBUG_PMM) {
+ DEBUG_PRINTF_PMM("PMM Structure:\n");
+ dump((void *)pis, sizeof(pmm_information_t));
+ }
+ return sizeof(pmm_information_t);
+}
+
+/* handle the selfdefined interrupt, this is executed, when the PMM Entry Point
+ * is executed, it must handle all PMM requests
+ */
+void pmm_handleInt()
+{
+ u32 rval = 0;
+ u16 function, flags;
+ u32 handle, length;
+ u32 i, j;
+ u32 buffer;
+ /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ * according to the PMM Spec "the flags and all registers, except DX and AX
+ * are preserved across calls to PMM"
+ * so we save M.x86 and in :exit label we restore it, however, this means that no
+ * returns must be used in this function, any exit must use goto exit!
+ * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ */
+ X86EMU_regs backup_regs = M.x86;
+ pop_long(); /* pop the return address, this is already saved in INT handler, we don't need
+ to remember this. */
+ function = pop_word();
+ switch (function) {
+ case 0:
+ /* function pmmAllocate */
+ length = pop_long();
+ length *= 16; /* length is passed in "paragraphs" of 16 bytes each */
+ handle = pop_long();
+ flags = pop_word();
+ DEBUG_PRINTF_PMM
+ ("%s: pmmAllocate: Length: %x, Handle: %x, Flags: %x\n",
+ __func__, length, handle, flags);
+ if ((flags & 0x1) != 0) {
+ /* request to allocate in conventional memory */
+ if (curr_pmm_allocation_index >= MAX_PMM_AREAS) {
+ printf
+ ("%s: pmmAllocate: Maximum Number of allocatable areas reached (%d), cannot allocate more memory!\n",
+ __func__, MAX_PMM_AREAS);
+ rval = 0;
+ goto exit;
+ }
+ /* some ROMs seem to be confused by offset 0, so lets start at 0x100 */
+ u32 next_offset = 0x100;
+ pmm_allocation_t *pmm_alloc =
+ &(pmm_allocation_array[curr_pmm_allocation_index]);
+ if (curr_pmm_allocation_index != 0) {
+ /* we have already allocated... get the new next_offset
+ * from the previous pmm_allocation_t */
+ next_offset =
+ pmm_allocation_array
+ [curr_pmm_allocation_index - 1].offset +
+ pmm_allocation_array
+ [curr_pmm_allocation_index - 1].length;
+ }
+ DEBUG_PRINTF_PMM("%s: next_offset: 0x%x\n",
+ __func__, next_offset);
+ if (length == 0) {
+ /* largest possible block size requested, we have on segment
+ * to allocate, so largest possible is segment size (0xFFFF)
+ * minus next_offset
+ */
+ rval = 0xFFFF - next_offset;
+ goto exit;
+ }
+ u32 align = 0;
+ if (((flags & 0x4) != 0) && (length > 0)) {
+ /* align to least significant bit set in length param */
+ u8 lsb = 0;
+ while (((length >> lsb) & 0x1) == 0) {
+ lsb++;
+ }
+ align = 1 << lsb;
+ }
+ /* always align at least to paragraph (16byte) boundary
+ * hm... since the length is always in paragraphs, we cannot
+ * align outside of paragraphs anyway... so this check might
+ * be unnecessary...*/
+ if (align < 0x10) {
+ align = 0x10;
+ }
+ DEBUG_PRINTF_PMM("%s: align: 0x%x\n", __func__,
+ align);
+ if ((next_offset & (align - 1)) != 0) {
+ /* not yet aligned... align! */
+ next_offset += align;
+ next_offset &= ~(align - 1);
+ }
+ if ((next_offset + length) > 0xFFFF) {
+ rval = 0;
+ printf
+ ("%s: pmmAllocate: Not enough memory available for allocation!\n",
+ __func__);
+ goto exit;
+ }
+ curr_pmm_allocation_index++;
+ /* remember the values in pmm_allocation_array */
+ pmm_alloc->handle = handle;
+ pmm_alloc->offset = next_offset;
+ pmm_alloc->length = length;
+ /* return the 32bit "physical" address, i.e. combination of segment and offset */
+ rval = ((u32) (PMM_CONV_SEGMENT << 16)) | next_offset;
+ DEBUG_PRINTF_PMM
+ ("%s: pmmAllocate: allocated memory at %x\n",
+ __func__, rval);
+ } else {
+ rval = 0;
+ printf
+ ("%s: pmmAllocate: allocation in extended memory not supported!\n",
+ __func__);
+ }
+ goto exit;
+ case 1:
+ /* function pmmFind */
+ handle = pop_long(); /* the handle to lookup */
+ DEBUG_PRINTF_PMM("%s: pmmFind: Handle: %x\n", __func__,
+ handle);
+ i = 0;
+ for (i = 0; i < curr_pmm_allocation_index; i++) {
+ if (pmm_allocation_array[i].handle == handle) {
+ DEBUG_PRINTF_PMM
+ ("%s: pmmFind: found allocated memory at %x\n",
+ __func__, rval);
+ /* return the 32bit "physical" address, i.e. combination of segment and offset */
+ rval =
+ ((u32) (PMM_CONV_SEGMENT << 16)) |
+ pmm_allocation_array[i].offset;
+ }
+ }
+ if (rval == 0) {
+ DEBUG_PRINTF_PMM
+ ("%s: pmmFind: handle (%x) not found!\n",
+ __func__, handle);
+ }
+ goto exit;
+ case 2:
+ /* function pmmDeallocate */
+ buffer = pop_long();
+ /* since argument is the address of the PMM block (including the segment,
+ * we need to remove the segment to get the offset
+ */
+ buffer = buffer ^ ((u32) PMM_CONV_SEGMENT << 16);
+ DEBUG_PRINTF_PMM("%s: pmmDeallocate: PMM segment offset: %x\n",
+ __func__, buffer);
+ i = 0;
+ /* rval = 0 means we deallocated the buffer, so set it to 1 in case we dont find it and
+ * thus cannot deallocate
+ */
+ rval = 1;
+ for (i = 0; i < curr_pmm_allocation_index; i++) {
+ DEBUG_PRINTF_PMM("%d: %x\n", i,
+ pmm_allocation_array[i].handle);
+ if (pmm_allocation_array[i].offset == buffer) {
+ /* we found the requested buffer, rval = 0 */
+ rval = 0;
+ DEBUG_PRINTF_PMM
+ ("%s: pmmDeallocate: found allocated memory at index: %d\n",
+ __func__, i);
+ /* copy the remaining elements in pmm_allocation_array one position up */
+ j = i;
+ for (; j < curr_pmm_allocation_index; j++) {
+ pmm_allocation_array[j] =
+ pmm_allocation_array[j + 1];
+ }
+ /* move curr_pmm_allocation_index one up, too */
+ curr_pmm_allocation_index--;
+ /* finally clean last element */
+ pmm_allocation_array[curr_pmm_allocation_index].
+ handle = 0;
+ pmm_allocation_array[curr_pmm_allocation_index].
+ offset = 0;
+ pmm_allocation_array[curr_pmm_allocation_index].
+ length = 0;
+ break;
+ }
+ }
+ if (rval != 0) {
+ DEBUG_PRINTF_PMM
+ ("%s: pmmDeallocate: offset (%x) not found, cannot deallocate!\n",
+ __func__, buffer);
+ }
+ goto exit;
+ default:
+ /* invalid/unimplemented function */
+ printf("%s: invalid PMM function (0x%04x) called!\n",
+ __func__, function);
+ /* PMM spec says if function is invalid, return 0xFFFFFFFF */
+ rval = 0xFFFFFFFF;
+ goto exit;
+ }
+ exit:
+ /* exit handler of this function, restore registers, put return value in DX:AX */
+ M.x86 = backup_regs;
+ M.x86.R_DX = (u16) ((rval >> 16) & 0xFFFF);
+ M.x86.R_AX = (u16) (rval & 0xFFFF);
+ CHECK_DBG(DEBUG_PMM) {
+ DEBUG_PRINTF_PMM("%s: dump of pmm_allocation_array:\n",
+ __func__);
+ for (i = 0; i < MAX_PMM_AREAS; i++) {
+ DEBUG_PRINTF_PMM
+ ("%d:\n\thandle: %x\n\toffset: %x\n\tlength: %x\n",
+ i, pmm_allocation_array[i].handle,
+ pmm_allocation_array[i].offset,
+ pmm_allocation_array[i].length);
+ }
+ }
+ return;
+}
+
+/* This function tests the pmm_handleInt() function above. */
+void pmm_test(void)
+{
+ u32 handle, length, addr;
+ u16 function, flags;
+ /*-------------------- Test simple allocation/find/deallocation ----------------------------- */
+ function = 0; /* pmmAllocate */
+ handle = 0xdeadbeef;
+ length = 16; /* in 16byte paragraphs, so we allocate 256 bytes... */
+ flags = 0x1; /* conventional memory, unaligned */
+ /* setup stack for call to pmm_handleInt() */
+ push_word(flags);
+ push_long(handle);
+ push_long(length);
+ push_word(function);
+ push_long(0); /* This is the return address for the ABI, unused in this implementation */
+ pmm_handleInt();
+ addr = ((u32) M.x86.R_DX << 16) | M.x86.R_AX;
+ DEBUG_PRINTF_PMM("%s: allocated memory at: %04x:%04x\n", __func__,
+ M.x86.R_DX, M.x86.R_AX);
+ function = 1; /* pmmFind */
+ push_long(handle);
+ push_word(function);
+ push_long(0); /* This is the return address for the ABI, unused in this implementation */
+ pmm_handleInt();
+ DEBUG_PRINTF_PMM("%s: found memory at: %04x:%04x (expected: %08x)\n",
+ __func__, M.x86.R_DX, M.x86.R_AX, addr);
+ function = 2; /* pmmDeallocate */
+ push_long(addr);
+ push_word(function);
+ push_long(0); /* This is the return address for the ABI, unused in this implementation */
+ pmm_handleInt();
+ DEBUG_PRINTF_PMM
+ ("%s: freed memory rval: %04x:%04x (expected: 0000:0000)\n",
+ __func__, M.x86.R_DX, M.x86.R_AX);
+ /*-------------------- Test aligned allocation/deallocation ----------------------------- */
+ function = 0; /* pmmAllocate */
+ handle = 0xdeadbeef;
+ length = 257; /* in 16byte paragraphs, so we allocate 4KB + 16 bytes... */
+ flags = 0x1; /* conventional memory, unaligned */
+ /* setup stack for call to pmm_handleInt() */
+ push_word(flags);
+ push_long(handle);
+ push_long(length);
+ push_word(function);
+ push_long(0); /* This is the return address for the ABI, unused in this implementation */
+ pmm_handleInt();
+ addr = ((u32) M.x86.R_DX << 16) | M.x86.R_AX;
+ DEBUG_PRINTF_PMM("%s: allocated memory at: %04x:%04x\n", __func__,
+ M.x86.R_DX, M.x86.R_AX);
+ function = 0; /* pmmAllocate */
+ handle = 0xf00d4b0b;
+ length = 128; /* in 16byte paragraphs, so we allocate 2KB... */
+ flags = 0x5; /* conventional memory, aligned */
+ /* setup stack for call to pmm_handleInt() */
+ push_word(flags);
+ push_long(handle);
+ push_long(length);
+ push_word(function);
+ push_long(0); /* This is the return address for the ABI, unused in this implementation */
+ pmm_handleInt();
+ /* the address should be aligned to 0x800, so probably it is at offset 0x1800... */
+ addr = ((u32) M.x86.R_DX << 16) | M.x86.R_AX;
+ DEBUG_PRINTF_PMM("%s: allocated memory at: %04x:%04x\n", __func__,
+ M.x86.R_DX, M.x86.R_AX);
+ function = 1; /* pmmFind */
+ push_long(handle);
+ push_word(function);
+ push_long(0); /* This is the return address for the ABI, unused in this implementation */
+ pmm_handleInt();
+ addr = ((u32) M.x86.R_DX << 16) | M.x86.R_AX;
+ function = 2; /* pmmDeallocate */
+ push_long(addr);
+ push_word(function);
+ push_long(0); /* This is the return address for the ABI, unused in this implementation */
+ pmm_handleInt();
+ DEBUG_PRINTF_PMM
+ ("%s: freed memory rval: %04x:%04x (expected: 0000:0000)\n",
+ __func__, M.x86.R_DX, M.x86.R_AX);
+ handle = 0xdeadbeef;
+ function = 1; /* pmmFind */
+ push_long(handle);
+ push_word(function);
+ push_long(0); /* This is the return address for the ABI, unused in this implementation */
+ pmm_handleInt();
+ addr = ((u32) M.x86.R_DX << 16) | M.x86.R_AX;
+ function = 2; /* pmmDeallocate */
+ push_long(addr);
+ push_word(function);
+ push_long(0); /* This is the return address for the ABI, unused in this implementation */
+ pmm_handleInt();
+ DEBUG_PRINTF_PMM
+ ("%s: freed memory rval: %04x:%04x (expected: 0000:0000)\n",
+ __func__, M.x86.R_DX, M.x86.R_AX);
+ /*-------------------- Test out of memory allocation ----------------------------- */
+ function = 0; /* pmmAllocate */
+ handle = 0xdeadbeef;
+ length = 0; /* length zero means, give me the largest possible block */
+ flags = 0x1; /* conventional memory, unaligned */
+ /* setup stack for call to pmm_handleInt() */
+ push_word(flags);
+ push_long(handle);
+ push_long(length);
+ push_word(function);
+ push_long(0); /* This is the return address for the ABI, unused in this implementation */
+ pmm_handleInt();
+ length = ((u32) M.x86.R_DX << 16) | M.x86.R_AX;
+ length /= 16; /* length in paragraphs */
+ DEBUG_PRINTF_PMM("%s: largest possible length: %08x\n", __func__,
+ length);
+ function = 0; /* pmmAllocate */
+ flags = 0x1; /* conventional memory, aligned */
+ /* setup stack for call to pmm_handleInt() */
+ push_word(flags);
+ push_long(handle);
+ push_long(length);
+ push_word(function);
+ push_long(0); /* This is the return address for the ABI, unused in this implementation */
+ pmm_handleInt();
+ addr = ((u32) M.x86.R_DX << 16) | M.x86.R_AX;
+ DEBUG_PRINTF_PMM("%s: allocated memory at: %04x:%04x\n", __func__,
+ M.x86.R_DX, M.x86.R_AX);
+ function = 0; /* pmmAllocate */
+ length = 1;
+ handle = 0xf00d4b0b;
+ flags = 0x1; /* conventional memory, aligned */
+ /* setup stack for call to pmm_handleInt() */
+ push_word(flags);
+ push_long(handle);
+ push_long(length);
+ push_word(function);
+ push_long(0); /* This is the return address for the ABI, unused in this implementation */
+ pmm_handleInt();
+ /* this should fail, so 0x0 should be returned */
+ addr = ((u32) M.x86.R_DX << 16) | M.x86.R_AX;
+ DEBUG_PRINTF_PMM
+ ("%s: allocated memory at: %04x:%04x expected: 0000:0000\n",
+ __func__, M.x86.R_DX, M.x86.R_AX);
+ handle = 0xdeadbeef;
+ function = 1; /* pmmFind */
+ push_long(handle);
+ push_word(function);
+ push_long(0); /* This is the return address for the ABI, unused in this implementation */
+ pmm_handleInt();
+ addr = ((u32) M.x86.R_DX << 16) | M.x86.R_AX;
+ function = 2; /* pmmDeallocate */
+ push_long(addr);
+ push_word(function);
+ push_long(0); /* This is the return address for the ABI, unused in this implementation */
+ pmm_handleInt();
+ DEBUG_PRINTF_PMM
+ ("%s: freed memory rval: %04x:%04x (expected: 0000:0000)\n",
+ __func__, M.x86.R_DX, M.x86.R_AX);
+}