summaryrefslogtreecommitdiffstats
path: root/src/core/heap.c
diff options
context:
space:
mode:
authorMichael Brown2005-03-08 19:53:11 +0100
committerMichael Brown2005-03-08 19:53:11 +0100
commit3d6123e69ab879c72ff489afc5bf93ef0b7a94ce (patch)
tree9f3277569153a550fa8d81ebd61bd88f266eb8da /src/core/heap.c
downloadipxe-3d6123e69ab879c72ff489afc5bf93ef0b7a94ce.tar.gz
ipxe-3d6123e69ab879c72ff489afc5bf93ef0b7a94ce.tar.xz
ipxe-3d6123e69ab879c72ff489afc5bf93ef0b7a94ce.zip
Initial revision
Diffstat (limited to 'src/core/heap.c')
-rw-r--r--src/core/heap.c168
1 files changed, 168 insertions, 0 deletions
diff --git a/src/core/heap.c b/src/core/heap.c
new file mode 100644
index 000000000..51ce47d75
--- /dev/null
+++ b/src/core/heap.c
@@ -0,0 +1,168 @@
+#include "etherboot.h"
+
+size_t heap_ptr, heap_top, heap_bot;
+
+void init_heap(void)
+{
+ size_t size;
+ size_t start, end;
+ unsigned i;
+ /* Find the largest contiguous area of memory that
+ * I can use for the heap, which is organized as
+ * a stack that grows backwards through memory.
+ */
+
+ /* If I have virtual address that do not equal physical addresses
+ * there is a change I will try to use memory from both sides of
+ * the virtual address space simultaneously, which can cause all kinds
+ * of interesting problems.
+ * Avoid it by logically extending etherboot. Once I know that relocation
+ * works I can just start the virtual address space at 0, and this problem goes
+ * away so that is probably a better solution.
+ */
+#if 0
+ start = virt_to_phys(_text);
+#else
+ /* segment wrap around is nasty don't chance it. */
+ start = virt_to_phys(_virt_start);
+#endif
+ end = virt_to_phys(_end);
+ size = 0;
+ for(i = 0; i < meminfo.map_count; i++) {
+ unsigned long r_start, r_end;
+ if (meminfo.map[i].type != E820_RAM)
+ continue;
+ if (meminfo.map[i].addr > ULONG_MAX)
+ continue;
+ if (meminfo.map[i].size > ULONG_MAX)
+ continue;
+
+ r_start = meminfo.map[i].addr;
+ r_end = r_start + meminfo.map[i].size;
+ if (r_end < r_start) {
+ r_end = ULONG_MAX;
+ }
+ /* Handle areas that overlap etherboot */
+ if ((end > r_start) && (start < r_end)) {
+ /* Etherboot completely covers the region */
+ if ((start <= r_start) && (end >= r_end))
+ continue;
+ /* Etherboot is completely contained in the region */
+ if ((start > r_start) && (end < r_end)) {
+ /* keep the larger piece */
+ if ((r_end - end) >= (r_start - start)) {
+ r_start = end;
+ }
+ else {
+ r_end = start;
+ }
+ }
+ /* Etherboot covers one end of the region.
+ * Shrink the region.
+ */
+ else if (end >= r_end) {
+ r_end = start;
+ }
+ else if (start <= r_start) {
+ r_start = end;
+ }
+ }
+ /* If two areas are the size prefer the greater address */
+ if (((r_end - r_start) > size) ||
+ (((r_end - r_start) == size) && (r_start > heap_top))) {
+ size = r_end - r_start;
+ heap_top = r_start;
+ heap_bot = r_end;
+ }
+ }
+ if (size == 0) {
+ printf("init_heap: No heap found.\n");
+ exit(1);
+ }
+ heap_ptr = heap_bot;
+}
+
+void *allot(size_t size)
+{
+ void *ptr;
+ size_t *mark, addr;
+ /* Get an 16 byte aligned chunk of memory off of the heap
+ * An extra sizeof(size_t) bytes is allocated to track
+ * the size of the object allocated on the heap.
+ */
+ addr = (heap_ptr - (size + sizeof(size_t))) & ~15;
+ if (addr < heap_top) {
+ ptr = 0;
+ } else {
+ mark = phys_to_virt(addr);
+ *mark = size;
+ heap_ptr = addr;
+ ptr = phys_to_virt(addr + sizeof(size_t));
+ }
+ return ptr;
+}
+
+//if mask = 0xf, it will be 16 byte aligned
+//if mask = 0xff, it will be 256 byte aligned
+//For DMA memory allocation, because it has more reqiurement on alignment
+void *allot2(size_t size, uint32_t mask)
+{
+ void *ptr;
+ size_t *mark, addr;
+ uint32_t *mark1;
+
+ addr = ((heap_ptr - size ) & ~mask) - sizeof(size_t) - sizeof(uint32_t);
+ if (addr < heap_top) {
+ ptr = 0;
+ } else {
+ mark = phys_to_virt(addr);
+ *mark = size;
+ mark1 = phys_to_virt(addr+sizeof(size_t));
+ *mark1 = mask;
+ heap_ptr = addr;
+ ptr = phys_to_virt(addr + sizeof(size_t) + sizeof(uint32_t));
+ }
+ return ptr;
+}
+
+void forget(void *ptr)
+{
+ size_t *mark, addr;
+ size_t size;
+
+ if (!ptr) {
+ return;
+ }
+ addr = virt_to_phys(ptr);
+ mark = phys_to_virt(addr - sizeof(size_t));
+ size = *mark;
+ addr += (size + 15) & ~15;
+
+ if (addr > heap_bot) {
+ addr = heap_bot;
+ }
+ heap_ptr = addr;
+}
+
+void forget2(void *ptr)
+{
+ size_t *mark, addr;
+ size_t size;
+ uint32_t mask;
+ uint32_t *mark1;
+
+ if (!ptr) {
+ return;
+ }
+ addr = virt_to_phys(ptr);
+ mark = phys_to_virt(addr - sizeof(size_t) - sizeof(uint32_t));
+ size = *mark;
+ mark1 = phys_to_virt(addr - sizeof(uint32_t));
+ mask = *mark1;
+ addr += (size + mask) & ~mask;
+
+ if (addr > heap_bot) {
+ addr = heap_bot;
+ }
+ heap_ptr = addr;
+}