aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2009-04-01 13:20:24 -0700
committerH. Peter Anvin <hpa@zytor.com>2009-04-01 13:20:24 -0700
commit175d813554b69e2883f5bd5eec5eb86a8a0118b7 (patch)
treee32553008cb2ab07698918cc8af9276098c8b5d2
parent62d83458e5dc64c3c52372953ebbf79a61054cd2 (diff)
downloadsyslinux.git-175d813554b69e2883f5bd5eec5eb86a8a0118b7.tar.gz
syslinux.git-175d813554b69e2883f5bd5eec5eb86a8a0118b7.tar.xz
syslinux.git-175d813554b69e2883f5bd5eec5eb86a8a0118b7.zip
com32: make memory beyond the core HighMem available to malloc
Impact: should deal with memory holes more gracefully Right now, if we find a memory hole, we simply don't use the memory beyond that point. This makes it possible for com32 modules to use that memory. After this, we should be able to add relocation support to allow loading when the memory at 1 MB is unavailable. Signed-off-by: H. Peter Anvin <hpa@zytor.com>
-rw-r--r--com32/include/com32.h1
-rw-r--r--com32/lib/free.c36
-rw-r--r--com32/lib/malloc.c79
-rw-r--r--com32/lib/malloc.h2
-rw-r--r--com32/lib/sys/entry.S2
-rw-r--r--com32/lib/syslinux/memmap.c1
-rw-r--r--core/com32.inc3
-rw-r--r--doc/comboot.txt3
8 files changed, 117 insertions, 10 deletions
diff --git a/com32/include/com32.h b/com32/include/com32.h
index 8be87211..5c031ffb 100644
--- a/com32/include/com32.h
+++ b/com32/include/com32.h
@@ -75,6 +75,7 @@ extern struct com32_sys_args {
uint32_t cs_bounce_size;
void __cdecl (*cs_farcall)(uint32_t, const com32sys_t *, com32sys_t *);
int __cdecl (*cs_cfarcall)(uint32_t, const void *, uint32_t);
+ uint32_t cs_memsize;
} __com32;
/*
diff --git a/com32/lib/free.c b/com32/lib/free.c
index aa17080d..20044078 100644
--- a/com32/lib/free.c
+++ b/com32/lib/free.c
@@ -58,6 +58,42 @@ __free_block(struct free_arena_header *ah)
return ah;
}
+/*
+ * This is used to insert a block which is not previously on the
+ * free list. Only the a.size field of the arena header is assumed
+ * to be valid.
+ */
+void __inject_free_block(struct free_arena_header *ah)
+{
+ struct free_arena_header *nah;
+ size_t a_end = (size_t)ah + ah->a.size;
+ size_t n_end;
+
+ for (nah = __malloc_head.a.next; nah->a.type != ARENA_TYPE_HEAD;
+ nah = nah->a.next) {
+ n_end = (size_t)nah + nah->a.size;
+
+ /* Is nah entirely beyond this block? */
+ if ((size_t)nah >= a_end)
+ break;
+
+ /* Is this block entirely beyond nah? */
+ if ((size_t)ah >= n_end)
+ continue;
+
+ /* Otherwise we have some sort of overlap - reject this block */
+ return;
+ }
+
+ /* Now, nah should point to the successor block */
+ ah->a.next = nah;
+ ah->a.prev = nah->a.prev;
+ nah->a.prev = ah;
+ ah->a.prev->a.next = ah;
+
+ __free_block(ah);
+}
+
void free(void *ptr)
{
struct free_arena_header *ah;
diff --git a/com32/lib/malloc.c b/com32/lib/malloc.c
index 2f8362b4..6ba80672 100644
--- a/com32/lib/malloc.c
+++ b/com32/lib/malloc.c
@@ -5,6 +5,8 @@
*/
#include <stdlib.h>
+#include <string.h>
+#include <com32.h>
#include "init.h"
#include "malloc.h"
@@ -31,10 +33,22 @@ static inline size_t sp(void)
return sp;
}
+struct e820_entry {
+ uint64_t start;
+ uint64_t len;
+ uint32_t type;
+ uint32_t extattr;
+};
+
+#define E820_MEM_MAX 0xfff00000 /* 4 GB - 1 MB */
+
static void __constructor init_memory_arena(void)
{
- struct free_arena_header *fp;
+ struct free_arena_header *fp, *fx;
size_t start, total_space;
+ static com32sys_t ireg;
+ com32sys_t oreg;
+ struct e820_entry *e820buf;
start = (size_t)ARENA_ALIGN_UP(__mem_end);
total_space = sp() - start;
@@ -46,14 +60,65 @@ static void __constructor init_memory_arena(void)
__stack_size = total_space - 4*sizeof(struct arena_header);
fp = (struct free_arena_header *)start;
- fp->a.type = ARENA_TYPE_FREE;
fp->a.size = total_space - __stack_size;
- /* Insert into chains */
- fp->a.next = fp->a.prev = &__malloc_head;
- fp->next_free = fp->prev_free = &__malloc_head;
- __malloc_head.a.next = __malloc_head.a.prev = fp;
- __malloc_head.next_free = __malloc_head.prev_free = fp;
+ __inject_free_block(fp);
+
+ /* Scan E820 to see if there are any other suitable blocks */
+ if (!__com32.cs_memsize)
+ return; /* Old Syslinux core, can't do this... */
+
+ e820buf = __com32.cs_bounce;
+ ireg.eax.w[0] = 0xe820;
+ ireg.edx.l = 0x534d4150;
+ /* ireg.ebx.l = 0; */
+ ireg.ecx.b[0] = sizeof(*e820buf);
+ ireg.es = SEG(e820buf);
+ ireg.edi.w[0] = OFFS(e820buf);
+ memset(e820buf, 0, sizeof *e820buf);
+ /* Set this in case the BIOS doesn't, but doesn't change %ecx to match. */
+ e820buf->extattr = 1;
+
+ do {
+ size_t start, end, len;
+
+ __intcall(0x15, &ireg, &oreg);
+
+ if ((oreg.eflags.l & EFLAGS_CF) ||
+ (oreg.eax.l != 0x534d4150) ||
+ (oreg.ecx.l < 20))
+ break;
+
+ if (oreg.ecx.l > 20 && !(e820buf->extattr & 1))
+ continue;
+ if (e820buf->type != 1)
+ continue;
+
+ /* Careful... these may be truncated values */
+ start = e820buf->start;
+ len = e820buf->len;
+
+ if (e820buf->start >= E820_MEM_MAX)
+ continue;
+ if (e820buf->len > E820_MEM_MAX - start)
+ len = E820_MEM_MAX - start;
+
+ /* Now all the values should be within range */
+ end = start + len;
+
+ if (end <= __com32.cs_memsize)
+ continue;
+ if (start <= __com32.cs_memsize) {
+ start = __com32.cs_memsize;
+ len = end - start;
+ }
+
+ if (len >= 2*sizeof(struct arena_header)) {
+ fp = (struct free_arena_header *)start;
+ fp->a.size = len;
+ __inject_free_block(fp);
+ }
+ } while ((ireg.ebx.l = oreg.ebx.l) != 0);
}
static void *__malloc_from_block(struct free_arena_header *fp, size_t size)
diff --git a/com32/lib/malloc.h b/com32/lib/malloc.h
index 830377d9..983a3c52 100644
--- a/com32/lib/malloc.h
+++ b/com32/lib/malloc.h
@@ -52,3 +52,5 @@ struct free_arena_header {
};
extern struct free_arena_header __malloc_head;
+void __inject_free_block(struct free_arena_header *ah);
+
diff --git a/com32/lib/sys/entry.S b/com32/lib/sys/entry.S
index ed5da95f..53bf2ecb 100644
--- a/com32/lib/sys/entry.S
+++ b/com32/lib/sys/entry.S
@@ -30,7 +30,7 @@
*/
/* Number of arguments in our version of the entry structure */
-#define COM32_ARGS 6
+#define COM32_ARGS 7
.section ".init","ax"
.globl _start
diff --git a/com32/lib/syslinux/memmap.c b/com32/lib/syslinux/memmap.c
index 105c9473..4ccddd27 100644
--- a/com32/lib/syslinux/memmap.c
+++ b/com32/lib/syslinux/memmap.c
@@ -79,6 +79,7 @@ struct syslinux_memmap *syslinux_memory_map(void)
ireg.es = SEG(e820buf);
ireg.edi.w[0] = OFFS(e820buf);
memset(e820buf, 0, sizeof *e820buf);
+ /* Set this in case the BIOS doesn't, but doesn't change %ecx to match. */
e820buf->extattr = 1;
do {
diff --git a/core/com32.inc b/core/com32.inc
index bd6d727a..477d657f 100644
--- a/core/com32.inc
+++ b/core/com32.inc
@@ -178,7 +178,8 @@ com32_call_start:
push dword (comboot_seg << 4) ; Bounce buffer address
push dword com32_intcall ; Intcall entry point
push dword command_line ; Command line pointer
- push dword 6 ; Argument count
+ push dword [HighMemSize] ; Memory managed by Syslinux
+ push dword 7 ; Argument count
sti ; Interrupts OK now
call pm_entry ; Run the program...
; ... on return, fall through to com32_exit ...
diff --git a/doc/comboot.txt b/doc/comboot.txt
index 5f57da7b..d7890d44 100644
--- a/doc/comboot.txt
+++ b/doc/comboot.txt
@@ -79,13 +79,14 @@ The following arguments are passed to the program on the stack:
Address Size Meaning
[ESP] dword Return (termination) address
- [ESP+4] dword Number of additional arguments (currently 5)
+ [ESP+4] dword Number of additional arguments (currently 7)
[ESP+8] dword Pointer to the command line arguments (null-terminated string)
[ESP+12] dword Pointer to INT call helper function
[ESP+16] dword Pointer to low memory bounce buffer
[ESP+20] dword Size of low memory bounce buffer
[ESP+24] dword Pointer to FAR call helper function (new in 2.05)
[ESP+28] dword Pointer to CDECL helper function (new in 3.54)
+ [ESP+32] dword Amount of memory controlled by the Syslinux core (new in 3.74)
This corresponds to the following C prototype, available in the file
com32/include/com32.h: