From: Alan Cox Date: Fri, 30 Jan 2015 00:30:19 +0000 (+0000) Subject: malloc: We need a heap allocator for flat model systems X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=154c318fc5c51acc888cbfd96001694e987fdaf5;p=FUZIX.git malloc: We need a heap allocator for flat model systems This isn't intended as a general purpose kernel malloc. --- diff --git a/Kernel/malloc.c b/Kernel/malloc.c new file mode 100644 index 00000000..8422e821 --- /dev/null +++ b/Kernel/malloc.c @@ -0,0 +1,217 @@ +#include +#include + +/* + * From Dale Schumacher's public domain dlibs. It's a very simple, very + * compact and surprisingly efficient malloc/free/memavail + * + * These functions should *not* be used by core kernel code. They are + * used to support flat address space machines. They can be used by 32bit + * specific code and drivers but care should be taken to avoid fragmentation + * and leaks. + */ + +/* + * MAX_POOLS is the number of discontiguous memory pools we have. + */ + +#if defined(CONFIG_32BIT) + +#ifndef MAX_POOLS +#define MAX_POOLS 1 +#endif + +#define FREE 0x00 +#define USED 0x80 +#define NULL_BLOCK 0x80000000L +static char *_mblk[MAX_POOLS]; /* system memory heaps */ +static long _msiz[MAX_POOLS]; /* allocated heap sizes */ +static unsigned long memtotal; + +/* + * Up to MAX_POOLS heaps are allocated as the operating system boots. + * These heaps are them divided into blocks for parcelling out by the + * user-callable memory allocation routines. Each heap beings with a + * pointer to the first free block, or NULL if there are no free blocks + * in this heap. Each block begins with a 4-byte header which defines + * the number of bytes in the block, including the header. Since blocks + * in a heap are known to be contiguous, this value also defines the + * beginning of the next block in the heap. The high bit of the header + * is set if the block is used and clear if it is free. The heaps ends + * with a block header which indicates a used block containing 0 bytes. + * The is the constant value NULL_BLOCK. Free blocks contain an additional + * pointer field, immediatly following the header, which is a pointer to + * the header of the next free block, or NULL. + */ + +/* + * Split block at * into a used block containing bytes + * and a free block containing the remainder. + */ +static long *splitblk(register long **addr, long size) +{ + register long n, *p, *q; + n = *(p = *addr); /* get actual block size */ + if (n > (size + 8L)) { /* is it worth splitting? */ + n -= size; + + /* calculate "break" point */ + q = ((long *) (((char *) p) + size)); + p[0] = size; + q[0] = n; + q[1] = p[1]; + *addr = q; + } + + else /* not worth splitting */ + *addr = ((long *) p[1]); /* remove from free list */ + *((char *) p) = USED; /* mark block "used" */ + return (p); +} + + +/* + * Find the smallest unused block containing at least bytes. + */ +static long *findblk(register long size) +{ + register int i; + register long n, tsiz = 0x7FFFFFFFL, **p, *q, *tptr = NULL; + for (i = 0; i < MAX_POOLS; ++i) { + if ((p = ((long **) _mblk[i])) == NULL) + continue; /* skip unavailable heaps */ + while ((q = *p) != NULL) { + n = *q; + if ((n >= size) && (n < tsiz)) { /* it fits */ + tsiz = n; + tptr = ((long *) p); + } + p = ((long **) (q + 1)); + } + } + return (tptr); +} + + +/* + * Merge adjacent "free" blocks in heap . Links in the free chain + * are guarenteed to be in forward order. + */ +static void mergeblk(int i) +{ + register long n, *p, *q; + p = (long *) _mblk[i]; + if ((p = ((long *) *p)) == NULL) /* empty chain */ + return; + while ((q = ((long *) p[1])) != NULL) { + n = *p; + if (((char *) p) + n == ((char *) q)) { /* adjacent free block */ + p[1] = q[1]; /* re-link free chain */ + *p += *q; /* adjust block size */ + } else + p = q; + } +} + + +/*--------------------- Documented Functions ---------------------------*/ +void *kmalloc(size_t size) +{ + register long *p; + if (size <= 4L) + size = 8L; /* minimum allocation */ + else + size = (size + 5L) & ~1L; /* header & alignment */ + if ((p = findblk(size)) == NULL) + return (NULL); + /* FIXME: check cast */ + p = splitblk((long **) p, size); + return (p + 4); /* skip over header */ +} + +void *kzalloc(size_t size) +{ + void *p = kmalloc(size); + if (p) + memset(p, 0, size); + return p; +} + +void kfree(void *ptr) +{ + register long *addr = ptr; + register int i; + register long *p, *q; + if (addr == NULL) + return; + --addr; /* point to block header */ + for (i = 0; i < MAX_POOLS; ++i) { + if ((p = ((long *) _mblk[i])) == NULL) + continue; /* skip unavailable blocks */ + if ((addr < p) + || (addr > ((long *) (((char *) p) + _msiz[i])))) + continue; /* block range check */ + while ((q = ((long *) *p)) != NULL) { + ++q; + if ((addr < q) && (addr > p)) + break; + p = q; + } + *((char *) addr) = FREE; /* link into free chain */ + addr[1] = *p; + *p = ((long) addr); + mergeblk(i); + return; + } + panic("bad free"); +} + + +/* FIXME: We ought to keep this as a running total */ +unsigned long kmemavail(void) +{ + register int i; + register unsigned long n = 0L; + register long **p, *q; + for (i = 0; i < MAX_POOLS; ++i) { + if ((p = ((long **) _mblk[i])) == NULL) + continue; /* skip unavailable heaps */ + while ((q = *p) != NULL) { + n += *q; + p = ((long **) (q + 1)); + } + } return (n); +} + +unsigned long kmemused(void) +{ + return memtotal - kmemavail(); +} + +/* + * Add a memory block to the pool + */ +void kmemaddblk(void *base, size_t size) +{ + register int i; + register char *p; + register long *q; + for (i = 0; i < MAX_POOLS; ++i) { + if (_mblk[i] != NULL) + continue; /* skip used heaps */ + _mblk[i] = base; + _msiz[i] = size; + q = ((long *) (p + size)); /* thread starting blocks */ + q[-1] = NULL_BLOCK; + q = ((long *) (p + 4L)); + q[-1] = (long) q; + q[0] = (size - 8L); + q[1] = 0; + p[4] = FREE; + memtotal += size; + return; + } + kputs("Too many memory pools\n"); +} + +#endif