[coreboot] [PATCH] YABEL: add PMM functionality
Pattrick Hueper
phueper at hueper.net
Mon Dec 22 17:16:37 CET 2008
Hi,
i am trying to reimplement the functionality, that IBM did not
opensource of YABEL.
This patch adds PMM (POST Memory Manager) functionality
Since i dont have a board for coreboot available at the moment, i
cannot test this code, it is written to follow the PMM Spec 1.01
I have included a pmm_test function that tests the code, so it should
be functional, but i cannot give any guarantees... i just would like
to add the code, as long as i still remember the pitfalls i found
already.
Cheers, Patty
Signed-off-by: Pattrick Hueper <phueper at hueper.net>
---
util/x86emu/Makefile | 1 +
util/x86emu/yabel/biosemu.c | 15 ++
util/x86emu/yabel/pmm.c | 384 +++++++++++++++++++++++++++++++++++++++++++
util/x86emu/yabel/pmm.h | 39 +++++
4 files changed, 439 insertions(+), 0 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 b50b3b4..02f9f98 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 cc11c0f..ebeb9af 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 <rtas.h>
@@ -209,6 +210,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;
diff --git a/util/x86emu/yabel/pmm.c b/util/x86emu/yabel/pmm.c
new file mode 100644
index 0000000..771482e
--- /dev/null
+++ b/util/x86emu/yabel/pmm.c
@@ -0,0 +1,384 @@
+/****************************************************************************
+ * YABEL BIOS Emulator
+ *
+ * Copyright 2008 Pattrick Hueper <phueper at hueper.net>
+ ****************************************************************************/
+
+#include <x86emu/x86emu.h>
+#include <x86emu/prim_ops.h>
+#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", __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 0000000..b3d34ad
--- /dev/null
+++ b/util/x86emu/yabel/pmm.h
@@ -0,0 +1,39 @@
+/****************************************************************************
+ * YABEL BIOS Emulator
+ *
+ * Copyright 2008 Pattrick Hueper <phueper at hueper.net>
+ ****************************************************************************/
+
+#ifndef _YABEL_PMM_H_
+#define _YABEL_PMM_H_
+
+#include <types.h>
+
+/* PMM Structure see PMM Spec Version 1.01 Chapter 3.1.1 (search web
for specspmm101.pdf) */
+typedef struct {
+ u8 signature[4];
+ u8 struct_rev;
+ u8 length;
+ u8 checksum;
+ u32 entry_point_offset;
+ u8 reserved[5];
+ /* code is not part of the speced PMM struct, however, since i cannot put the
+ * handling in 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, it will be called by my
special PMM INT */
+void pmm_handleInt(void);
+
+void pmm_test(void);
+
+#endif // _YABEL_PMM_H
--
1.6.0.4
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0001-YABEL-add-PMM-functionality.patch
Type: text/x-diff
Size: 19213 bytes
Desc: not available
URL: <http://www.coreboot.org/pipermail/coreboot/attachments/20081222/c15874c2/attachment.bin>
More information about the coreboot
mailing list