[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Xen-devel] [PATCH] hvm bios: add PMM (a memory manager during POST)



Hi Keir,

Thanks for your review.
Here is an updated patch using new 32-bit gateway interface.

Thanks,
Kouya

Keir Fraser writes:
> On 22/01/2009 11:12, "Kouya Shimura" <kouya@xxxxxxxxxxxxxx> wrote:
> 
> > The PMM (POST Memory Manager) offers malloc/free functionality
> > for PCI option ROMs during POST (Power On Self Test).
> > 
> > This patch adds a PMM functionality to the guest BIOS.
> 
> Hardcoding the entry point and setting up $PMM in hvmloader is not very
> nice. I think you should define the $PMM struct as data in rombios.c, and
> then you can directly reference pmm_entry_point without needing a .org
> directive. The only difficulty then is the checksum, which I think you
> should calculate in rombios.c rather than doing it in hvmloader. It's a tiny
> bit of C or pretty trivial asm to do it that way. Overall this avoids
> further tying together hvmloader and rombios unnecessarily.
> 
> Beyond that, the allocator looks pretty complicated, but as long as it works
> I suppose that is fine. It would be nice if you could add a top-of-file
> comment explaining the allocator algorithm, where it allocates memory from
> (scavenging from rombios's e820 perhaps), and other details like that.
> Otherwise it's rather unnecessarily opaque new code. A few more comments
> scattered through (especially at tops of functions) would be nice too!

Signed-off-by: Kouya Shimura <kouya@xxxxxxxxxxxxxx>
Signed-off-by: Akio Takebe <takebe_akio@xxxxxxxxxxxxxx>

diff -r 9b0289a165eb tools/firmware/rombios/32bit/Makefile
--- a/tools/firmware/rombios/32bit/Makefile     Thu Jan 22 18:00:48 2009 +0000
+++ b/tools/firmware/rombios/32bit/Makefile     Fri Jan 23 19:16:05 2009 +0900
@@ -18,7 +18,7 @@
 clean: subdirs-clean
        rm -rf *.o $(TARGET) $(DEPS)
 
-$(TARGET): 32bitbios.o $(MODULES) util.o
+$(TARGET): 32bitbios.o $(MODULES) util.o pmm.o
        $(LD) $(LDFLAGS_DIRECT) -s -r $^ -o 32bitbios_all.o
        @nm 32bitbios_all.o |                                \
          egrep '^ +U ' >/dev/null && {                      \
diff -r 9b0289a165eb tools/firmware/rombios/32bit/pmm.c
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/firmware/rombios/32bit/pmm.c        Fri Jan 23 19:16:05 2009 +0900
@@ -0,0 +1,530 @@
+/*
+ *  pmm.c - POST(Power On Self Test) Memory Manager
+ *  according to the specification described in
+ *  
http://www.phoenix.com/NR/rdonlyres/873A00CF-33AC-4775-B77E-08E7B9754993/0/specspmm101.pdf
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ *  Copyright (C) 2009 FUJITSU LIMITED
+ *
+ *  Author: Kouya Shimura <kouya@xxxxxxxxxxxxxx>
+ *
+ */
+
+/*
+ * Algorithm:
+ *
+ * This is not a fast storage allocator but simple one.  There is no
+ * segregated management by block size and it does nothing special for
+ * avoiding the fragmentation.
+ *
+ * The allocation algorithm is a first-fit. All memory blocks are
+ * managed by linear single linked list in order of the address.
+ * (i.e. There is no backward pointer) It searches the first available
+ * equal or larger block from the head (lowest address) of memory
+ * heap. The larger block is splitted into two blocks unless one side
+ * becomes too small.
+ * 
+ * For de-allocation, the specified block is just marked as available
+ * and it does nothing else. Thus, the fragmentation will occur. The
+ * collection of continuous available blocks are done on the search
+ * phase of another block allocation.
+ *
+ * The following is an abstract of this algorithm. The actual code
+ * looks complicated on account of alignment and checking the handle.
+ *
+ *     static memblk_t *
+ *     alloc(heap_t *heap, uint32_t size)
+ *     {
+ *         static memblk_t *mb;
+ *         for_each_memblk(heap, mb) // search memory blocks
+ *             if (memblk_is_avail(mb))
+ *             {
+ *                 collect_avail_memblks(heap, mb);
+ *                 if (size <= memblk_bufsize(mb))
+ *                 {
+ *                     split_memblk(mb, size);
+ *                     set_inuse(mb);
+ *                     return mb;
+ *                 }
+ *             }
+ *         return NULL;
+ *     }
+ */
+
+#include <stdint.h>
+#include <stddef.h>
+#include <../hvmloader/config.h>
+#include <../hvmloader/e820.h>
+#include "util.h"
+
+#define DEBUG_PMM 0
+
+#define ASSERT(_expr, _action)                                  \
+    if (!(_expr)) {                                             \
+        printf("ASSERTION FAIL: %s %s:%d %s()\n",               \
+               __STRING(_expr), __FILE__, __LINE__, __func__);  \
+        _action;                                                \
+    } else
+
+#if DEBUG_PMM
+# define PMM_DEBUG(format, p...) printf("PMM " format, ##p)
+#else
+# define PMM_DEBUG(format, p...)
+#endif
+
+struct pmmAllocArgs {
+    uint16_t function;
+    uint32_t length;
+    uint32_t handle;
+    uint16_t flags;
+} __attribute__ ((packed));
+
+struct pmmFindArgs {
+    uint16_t function;
+    uint32_t handle;
+} __attribute__ ((packed));
+
+struct pmmDeallocateArgs {
+    uint16_t function;
+    uint32_t buffer;
+} __attribute__ ((packed));
+
+#define PMM_FUNCTION_ALLOCATE   0
+#define PMM_FUNCTION_FIND       1         
+#define PMM_FUNCTION_DEALLOC    2
+
+#define PARAGRAPH_LENGTH        16  // unit of length
+
+#define PMM_HANDLE_ANONYMOUS    0xffffffff
+
+#define PMM_FLAGS_MEMORY_TYPE_MASK      0x0003
+#define PMM_FLAGS_MEMORY_INVALID        0
+#define PMM_FLAGS_MEMORY_CONVENTIONAL   1  // 0 to 1MB
+#define PMM_FLAGS_MEMORY_EXTENDED       2  // 1MB to 4GB
+#define PMM_FLAGS_MEMORY_ANY            3  // whichever is available
+#define PMM_FLAGS_ALIGINMENT            0x0004
+
+/* Error code */
+#define PMM_ENOMEM      (0)     // Out of memory, duplicate handle
+#define PMM_EINVAL      (-1)    // Invalid argument
+
+#define ALIGN_UP(addr, size)    (((addr)+((size)-1))&(~((size)-1)))
+#define ALIGN_DOWN(addr, size)  ((addr)&(~((size)-1)))
+
+typedef struct memblk {
+    uint32_t magic;      // inuse or available
+    struct memblk *next; // points the very next of this memblk
+    uint32_t handle;     // identifier of this block
+    uint32_t __fill;     // for 16byte alignment, not used
+    uint8_t buffer[0];
+} memblk_t;
+
+typedef struct heap {
+    memblk_t *head;     // start address of heap
+    memblk_t *end;      // end address of heap
+} heap_t;
+
+#define HEAP_NOT_INITIALIZED    (memblk_t *)-1
+#define HEAP_ALIGNMENT          16
+
+/*
+ * PMM handles two memory heaps, the caller chooses either.
+ *
+ * - conventional memroy (below 1MB)
+ *    In HVM, the area is fixed. 0x00010000-0x0007FFFF
+ *    (from SCRATCH_PHYSICAL_ADDRESS to HYPERCALL_PHYSICAL_ADDRESS)
+ *
+ * - extended memory (start at 1MB, below 4GB)
+ *    In HVM, the area starts at memory address 0x00100000.
+ *    The end address is variable. We read low RAM address from e820 table.
+ *
+ * The following struct must be located in the data segment since bss
+ * in 32bitbios doesn't be relocated.
+ */
+static struct {
+    heap_t heap;     // conventional memory
+    heap_t ext_heap; // extended memory
+} pmm_data = { {HEAP_NOT_INITIALIZED, NULL}, {NULL, NULL} };
+
+/* These values are private use, not a spec in PMM */
+#define MEMBLK_MAGIC_INUSE   0x2A4D4D50  // 'PMM*'
+#define MEMBLK_MAGIC_AVAIL   0x5F4D4D50  // 'PMM_'
+
+#define memblk_is_inuse(_mb)  ((_mb)->magic == MEMBLK_MAGIC_INUSE)
+#define memblk_is_avail(_mb)  ((_mb)->magic == MEMBLK_MAGIC_AVAIL)
+
+static void set_inuse(memblk_t *mb, uint32_t handle)
+{
+    mb->magic = MEMBLK_MAGIC_INUSE;
+    mb->handle = handle;
+}
+
+static void set_avail(memblk_t *mb)
+{
+    mb->magic = MEMBLK_MAGIC_AVAIL;
+    mb->handle = PMM_HANDLE_ANONYMOUS;
+}
+
+#define MEMBLK_HEADER_SIZE   ((int)(&((memblk_t *)0)->buffer))
+#define MIN_MEMBLK_SIZE      (MEMBLK_HEADER_SIZE + PARAGRAPH_LENGTH)
+
+#define memblk_size(_mb)     ((void *)((_mb)->next) - (void *)(_mb))
+#define memblk_buffer(_mb)   ((uint32_t)(&(_mb)->buffer))
+#define memblk_bufsize(_mb)  (memblk_size(_mb) - MEMBLK_HEADER_SIZE)
+
+#define buffer_memblk(_buf)  (memblk_t *)((_buf) - MEMBLK_HEADER_SIZE)
+
+#define memblk_loop_mbondition(_h, _mb) \
+    (((_mb) < (_h)->end) && (/* avoid infinite loop */ (_mb) < (_mb)->next))
+
+#define for_each_memblk(_h, _mb)        \
+    for ((_mb) = (_h)->head;            \
+         memblk_loop_mbondition(_h, _mb);   \
+         (_mb) = (_mb)->next)
+
+#define for_remain_memblk(_h, _mb)      \
+    for (;                                      \
+         memblk_loop_mbondition(_h, _mb);   \
+         (_mb) = (_mb)->next)
+
+/*
+ *                                       <-size->
+ *    +==================+======+       +========+========+======+
+ *    |      avail       |      |       | avail  | avail  |      |
+ *    |      memblk      |memblk|...    | memblk | memblk |memblk|...
+ *    +==================+======+   =>  +========+========+======+
+ *    ^ |                ^ |    ^         |      ^ |      ^ |    ^
+ *    | |next            | |next|         |next  | |next  | |next|
+ *    | \________________/ \____/         \______/ \______/ \____/
+ *    |                                          ^
+ *    |                                          |
+ *    mb                                         +- sb(return value)
+ */
+static memblk_t *
+split_memblk(memblk_t *mb, uint32_t size)
+{
+    memblk_t *sb = (void *)memblk_buffer(mb) + size;
+
+    if (memblk_bufsize(mb) - size < MIN_MEMBLK_SIZE) // one side is too small
+        return mb;
+
+    sb->next = mb->next;
+    set_avail(sb);
+
+    mb->next = sb;
+    return sb;
+}
+
+/*
+ *    +======+======+======+======+       +=================+======+
+ *    |avail |avail |avail |inuse |       |      avail      |inuse |   
+ *    |memblk|memblk|memblk|memblk|...    |      memblk     |memblk|...
+ *    +======+======+======+======+   =>  +=================+======+
+ *    ^ |    ^ |    ^ |    ^ |    ^         |               ^ |    ^
+ *    | |next| |next| |next| |next|         |next           | |next|
+ *    | \____/ \____/ \____/ \____/         \_______________/ \____/
+ *    |
+ *    mb
+ */
+static void
+collect_avail_memblks(heap_t *heap, memblk_t *mb)
+{
+    memblk_t *nb = mb->next;
+
+    for_remain_memblk(heap, nb)
+        if (memblk_is_inuse(nb))
+            break;
+    mb->next = nb;
+}
+
+static void
+pmm_init_heap(heap_t *heap, uint32_t from_addr, uint32_t to_addr)
+{
+    memblk_t *mb = (memblk_t *)ALIGN_UP(from_addr, HEAP_ALIGNMENT);
+
+    mb->next = (memblk_t *)ALIGN_DOWN(to_addr, HEAP_ALIGNMENT);
+    set_avail(mb);
+
+    heap->head = mb;
+    heap->end = mb->next;
+}
+
+static void
+pmm_initalize(void)
+{
+    int i, e820_nr = *E820_NR;
+    struct e820entry *e820 = E820;
+
+    /* extended memory: RAM below 4GB, 0x100000-0xXXXXXXXX */
+    for (i = 0; i < e820_nr; i++)
+    {
+        if (e820[i].type == E820_RAM && e820[i].addr == 0x00100000)
+        {
+            pmm_init_heap(&pmm_data.ext_heap, e820[i].addr, 
+                          e820[i].addr + e820[i].size);
+            break;
+        }
+    }
+
+    /* convectional memory: RAM below 1MB, 0x10000-0x7FFFF */
+    pmm_init_heap(&pmm_data.heap, SCRATCH_PHYSICAL_ADDRESS,
+                  HYPERCALL_PHYSICAL_ADDRESS);
+}
+
+static uint32_t
+pmm_max_avail_length(heap_t *heap)
+{
+    memblk_t *mb;
+    uint32_t size, max = 0;
+
+    for_each_memblk(heap, mb)
+    {
+        if (memblk_is_avail(mb))
+        {
+            collect_avail_memblks(heap, mb);
+            size = memblk_bufsize(mb);
+            if (size > max)
+                max = size;
+        }
+    }
+    return (max / PARAGRAPH_LENGTH);
+}
+
+static memblk_t *
+first_fit(heap_t *heap, uint32_t size, uint32_t handle, uint32_t flags)
+{
+    memblk_t *mb;
+    int32_t align = 0;
+
+    if (flags & PMM_FLAGS_ALIGINMENT)
+        align = ((size ^ (size - 1)) >> 1) + 1;
+
+    for_each_memblk(heap, mb)
+    {
+        if (memblk_is_avail(mb))
+        {
+            collect_avail_memblks(heap, mb);
+
+            if (align)
+            {
+                uint32_t addr = memblk_buffer(mb);
+                uint32_t offset = ALIGN_UP(addr, align) - addr;
+
+                if (offset > 0)
+                {
+                    ASSERT(offset >= MEMBLK_HEADER_SIZE, continue);
+
+                    if (offset + size > memblk_bufsize(mb)) // not enough
+                        continue;
+
+                    mb = split_memblk(mb, offset - MEMBLK_HEADER_SIZE);
+                    return mb;
+                }
+            }
+
+            if (size <= memblk_bufsize(mb)) // large enough ?
+                return mb;
+        }
+        else
+        {
+            ASSERT(memblk_is_inuse(mb), return NULL);
+
+            /* duplication check for handle */
+            if (handle != PMM_HANDLE_ANONYMOUS && mb->handle == handle)
+                return NULL;
+        }
+    }
+
+    return NULL;
+}
+
+static memblk_t *
+pmm_find_handle(heap_t *heap, uint32_t handle)
+{
+    memblk_t *mb;
+
+    if (handle == PMM_HANDLE_ANONYMOUS)
+        return NULL;
+
+    for_each_memblk(heap, mb)
+        if (mb->handle == handle)
+            return mb;
+    return NULL;
+}
+
+/*
+ * allocate a memory block of the specified type and size, and returns
+ * the address of the memory block.
+ *
+ * A client-specified identifier to be associated with the allocated
+ * memory block. A handle of 0xFFFFFFFF indicates that no identifier
+ * should be associated with the block. Such a memory block is known
+ * as an "anonymous" memory block and cannot be found using the
+ * pmmFind function. If a specified handle for a requested memory
+ * block is already used in a currently allocated memory block, the
+ * error value of 0x00000000 is returned
+ *
+ * If length is 0x00000000, no memory is allocated and the value
+ * returned is the size of the largest memory block available for the
+ * memory type specified in the flags parameter. The alignment bit in
+ * the flags register is ignored when calculating the largest memory
+ * block available.
+ *
+ * If a specified handle for a requested memory block is already used
+ * in a currently allocated memory block, the error value of
+ * 0x00000000 is returned.
+ * 
+ * A return value of 0x00000000 indicates that an error occurred and
+ * no memory has been allocated. 
+ */
+static uint32_t
+pmmAllocate(uint32_t length, uint32_t handle, uint16_t flags)
+{
+    heap_t *heap;
+    memblk_t *mb;
+    uint32_t size;
+
+    switch(flags & PMM_FLAGS_MEMORY_TYPE_MASK)
+    {
+    case PMM_FLAGS_MEMORY_CONVENTIONAL:
+        heap = &pmm_data.heap;
+        break;
+
+    case PMM_FLAGS_MEMORY_EXTENDED:
+    case PMM_FLAGS_MEMORY_ANY: // XXX: ignore conventional memory for now
+        heap = &pmm_data.ext_heap;
+        break;
+
+    default:
+        return PMM_EINVAL;
+    }
+
+    /* return the largest memory block available */
+    if (length == 0)
+        return pmm_max_avail_length(heap);
+
+    size = length * PARAGRAPH_LENGTH;
+    mb = first_fit(heap, size, handle, flags);
+
+    if (mb == NULL)
+        return PMM_ENOMEM;
+
+    /* duplication check for handle */
+    if (handle != PMM_HANDLE_ANONYMOUS)
+    {
+        memblk_t *nb = mb->next;
+
+        for_remain_memblk(heap, nb)
+            if (nb->handle == handle)
+                return PMM_ENOMEM;
+    }
+
+    split_memblk(mb, size);
+    set_inuse(mb, handle);
+
+    return memblk_buffer(mb);
+}
+
+/*
+ * returns the address of the memory block associated with the
+ * specified handle.  
+ *
+ * A return value of 0x00000000 indicates that the handle does not
+ * correspond to a currently allocated memory block.
+ */
+static uint32_t
+pmmFind(uint32_t handle)
+{
+    memblk_t *mb;
+
+    if (handle == PMM_HANDLE_ANONYMOUS)
+        return 0;
+
+    mb = pmm_find_handle(&pmm_data.heap, handle);
+    if (mb != NULL)
+        return memblk_buffer(mb);
+    mb = pmm_find_handle(&pmm_data.ext_heap, handle);
+    if (mb != NULL)
+        return memblk_buffer(mb);
+    return 0;
+}
+
+/* 
+ * frees the specified memory block that was previously allocated by
+ * pmmAllocate.
+ *
+ * If the memory block was deallocated correctly, the return value is
+ * 0x00000000. If there was an error, the return value is non-zero.
+ */
+static uint32_t
+pmmDeallocate(uint32_t buffer)
+{
+    memblk_t *mb = buffer_memblk(buffer);
+
+    if (!memblk_is_inuse(mb))
+        return PMM_EINVAL;
+
+    set_avail(mb);
+    return 0;
+}
+
+
+union pmm_args {
+    uint16_t function;
+    struct pmmAllocArgs alloc;
+    struct pmmFindArgs find;
+    struct pmmDeallocateArgs dealloc;
+} __attribute__ ((packed));
+
+/*
+ * entry function of all PMM services.
+ *
+ * Values returned to the caller are placed in the DX:AX register
+ * pair. The flags and all registers, other than DX and AX, are
+ * preserved across calls to PMM services.
+ */
+uint32_t
+pmm(void *argp)
+{
+    union pmm_args *ap = argp;
+    uint32_t ret = PMM_EINVAL;
+
+    if (pmm_data.heap.head == HEAP_NOT_INITIALIZED)
+        pmm_initalize();
+
+    switch(ap->function) {
+    case PMM_FUNCTION_ALLOCATE:
+        ret = pmmAllocate(ap->alloc.length, ap->alloc.handle, ap->alloc.flags);
+        PMM_DEBUG("Alloc length=%x handle=%x flags=%x ret=%x\n", 
+               ap->alloc.length, ap->alloc.handle, ap->alloc.flags, ret);
+        break;
+
+    case PMM_FUNCTION_FIND:
+        ret = pmmFind(ap->find.handle);
+        PMM_DEBUG("Find handle=%x ret=%x\n", ap->find.handle, ret);
+        break;
+
+    case PMM_FUNCTION_DEALLOC:
+        ret = pmmDeallocate(ap->dealloc.buffer);
+        PMM_DEBUG("Dealloc buffer=%x ret=%x\n", ap->dealloc.buffer, ret);
+        break;
+
+    default:
+        PMM_DEBUG("Invalid function:%d\n", ap->function);
+    }
+
+    return ret;
+}
diff -r 9b0289a165eb tools/firmware/rombios/32bitprotos.h
--- a/tools/firmware/rombios/32bitprotos.h      Thu Jan 22 18:00:48 2009 +0000
+++ b/tools/firmware/rombios/32bitprotos.h      Fri Jan 23 19:16:05 2009 +0900
@@ -13,3 +13,4 @@
 X(11, void,   tcpa_measure_post, Bit32u from, Bit32u to)
 X(12, Bit32u, tcpa_initialize_tpm, Bit32u physpres)
 X(13, Bit32u, get_s3_waking_vector, void)
+X(14, Bit32u, pmm, void *argp)
diff -r 9b0289a165eb tools/firmware/rombios/rombios.c
--- a/tools/firmware/rombios/rombios.c  Thu Jan 22 18:00:48 2009 +0000
+++ b/tools/firmware/rombios/rombios.c  Fri Jan 23 19:16:05 2009 +0900
@@ -160,6 +160,8 @@
 #define BX_ELTORITO_BOOT 1
 
 #define BX_TCGBIOS       0   /* main switch for TCG BIOS ext. */
+
+#define BX_PMM           1   /* POST Memory Manager */
 
 #define BX_MAX_ATA_INTERFACES   4
 #define BX_MAX_ATA_DEVICES      (BX_MAX_ATA_INTERFACES*2)
@@ -2054,7 +2056,10 @@
   "rombios32 "
 #endif
 #if BX_TCGBIOS
-  "TCG-enabled"
+  "TCG-enabled "
+#endif
+#if BX_PMM
+  "PMM "
 #endif
   "\n\n");
 }
@@ -10356,6 +10361,32 @@
   dw 0xffff, 0, 0x9300, 0x0000 ; 16 bit data segment base=0x0 limit=0xffff
 #endif // BX_ROMBIOS32
 
+#if BX_PMM
+; according to POST Memory Manager Specification Version 1.01
+.align 16
+pmm_structure:
+  db 0x24,0x50,0x4d,0x4d ;; "$PMM" signature
+  db 0x01 ;; revision
+  db 16 ;; length
+  db (-((pmm_entry_point>>8)+pmm_entry_point+0x20f))&0xff;; checksum
+  dw pmm_entry_point,0xf000 ;; far call entrypoint
+  db 0,0,0,0,0 ;; reserved
+
+pmm_entry_point:
+  pushad
+  mov   eax, esp
+  add   eax, #(8*4+2+2) ;; skip regs of pushad, ip, cs
+  push  eax ;; pointer to PMM function args
+  call _pmm
+  mov   bx, sp
+SEG SS
+  mov   [bx+(4+7*4)], ax
+SEG SS
+  mov   [bx+(4+5*4)], dx
+  pop   eax
+  popad
+  db 0xcb ;; lret
+#endif // BX_PMM
 
 ; parallel port detection: base address in DX, index in BX, timeout in CL
 detect_parport:
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.