]> git.corax.cc Git - corax/commitdiff
Implement page-directory-level functions to handle heaps
authorMatthias Kruk <m@m10k.eu>
Mon, 2 Dec 2019 08:09:00 +0000 (17:09 +0900)
committerMatthias Kruk <m@m10k.eu>
Mon, 2 Dec 2019 08:09:00 +0000 (17:09 +0900)
 - Add pg_dir_sbrk() function to handle heap allocation and size adjustments
 - Add pg_dir_get_heap() function to locate the base of the heap
 - Add documentation for pg_dir_map() function

kernel/arch/paging.c
kernel/include/arch.h

index 6fb27b8d0c53f570081eca7173ecedda72396324..978cf541a351b3f1a59e673e39b0a9941899cd4f 100644 (file)
@@ -62,6 +62,7 @@ static int _pg_dir_add_region(pg_dir_t*, void*, u32_t, u32_t, u32_t, u32_t);
 int _pg_dir_vpxlate(pg_dir_t*, u32_t, u32_t*);
 int _pg_dir_pagesize(pg_dir_t*, u32_t, u32_t*);
 int _pg_dir_xfer(pg_dir_t*, void*, pg_dir_t*, void*, u32_t);
+static int _pg_dir_heap_map(pg_dir_t*, u32_t);
 
 static void* _phys_alloc(u32_t size, u32_t align)
 {
@@ -769,6 +770,114 @@ cleanup:
        return(ret_val);
 }
 
+static int _pg_dir_heap_map(pg_dir_t *pd, u32_t heap_size)
+{
+       /*
+        * The pg_dir_heap_map() function is used to add a heap segment
+        * to a page dir that does not already have one.
+        *
+        * It should work as follows:
+        *  1. Make sure the page dir doesn't already have a heap
+        *  2. Find the highest segment that is not a stack segment
+        *  3. Add the heap segment right after the segement located in 2
+        */
+
+       u32_t heap_start;
+       int ret_val;
+       int i;
+
+       /* FIXME: Make sure heap_size is aligned to the page directory's page size */
+       /* FIXME: Set heap_size to the page directory's page size if set to zero */
+
+       if(!pd) {
+               ret_val = -EINVAL;
+               goto gtfo;
+       }
+
+       /* FIXME: Lock the page directory */
+
+       for(i = 0, heap_start = 0, ret_val = 0;
+               i < CONFIG_PAGING_DIR_MAXREGIONS && ret_val > 0;
+               i++) {
+               struct region *reg;
+               u32_t limit;
+
+               reg = pd->pd_regions[i];
+
+               switch(reg->reg_type) {
+               case REGION_HEAP:
+                       /* the page directory apparently already contains a heap */
+                       ret_val = -EALREADY;
+                       break;
+
+               case REGION_STACK:
+               case REGION_KSTACK:
+                       /*
+                        * Ignore stack segments in the calculation because they are supposed to
+                        * be at the end of the address space, and not in front of the heap.
+                        */
+                       break;
+
+               case REGION_TEXT:
+               case REGION_BSS:
+               case REGION_DATA:
+               case REGION_RODATA:
+                       /*
+                        * Calculate the address of the first byte after this region. It *should*
+                        * already be aligned, but nevertheless align it again. 念の為.
+                        */
+                       limit = ALIGN((u32_t)reg->reg_base + reg->reg_size,
+                                                 reg->reg_pgsize);
+
+                       if(limit > heap_start) {
+                               heap_start = limit;
+                       }
+                       break;
+
+               default:
+                       /* this should absolutely not have happened */
+                       ret_val = -EBADFD;
+                       break;
+               }
+       }
+
+       if(!ret_val) {
+               /* we found a place to allocate the heap segment */
+
+               if(heap_start) {
+                       u32_t attrs;
+
+                       /* the heap shouldn't be executable, right? */
+                       attrs = PAGE_ATTR_PRESENT | PAGE_ATTR_WRITABLE |
+                               PAGE_ATTR_USER | PAGE_ATTR_NO_EXEC;
+
+                       /* allocate page frames from wherever */
+                       ret_val = pg_dir_map(pd, NULL, (void*)heap_start,
+                                                                heap_size, attrs);
+
+                       if(!ret_val) {
+                               /* add a struct region so the memory is accounted for */
+                               ret_val = _pg_dir_add_region(pd, (void*)heap_start, heap_size,
+                                                                                        REGION_HEAP, attrs, REGION_PRIV);
+
+                               if(ret_val < 0) {
+                                       /* oops - ran out of kernel heap space */
+                                       pg_dir_unmap(pd, (void*)heap_start, heap_size);
+                               }
+                       } else {
+                               /* Failed to map the heap. Wat do? */
+                               ret_val = -ENOMEM;
+                       }
+               } else {
+                       /* the page directory doesn't appear to contain any regions? */
+                       ret_val = -EBADFD;
+               }
+       }
+
+gtfo:
+       return(ret_val);
+}
+
 int _clone_kernel_region(pg_dir_t *kdir, region_t *reg, void *data)
 {
        pg_dir_t *dir;
@@ -1306,3 +1415,97 @@ void* pg_dir_get_pdbr(pg_dir_t *pd)
 {
        return(pd->pd_base);
 }
+
+void* pg_dir_get_heap(pg_dir_t *pd)
+{
+       void *ret_val;
+       int i;
+
+       for(ret_val = NULL, i = 0; i < CONFIG_PAGING_DIR_MAXREGIONS; i++) {
+               if(pd->pd_regions[i]->reg_type == REGION_HEAP) {
+                       ret_val = pd->pd_regions[i]->reg_base;
+                       break;
+               }
+       }
+
+       return(ret_val);
+}
+
+void* pg_dir_sbrk(pg_dir_t *pd, i32_t increment)
+{
+       struct region *heap;
+       void *ret_val;
+       int i;
+
+       /* special case where the heap is not touched */
+       if(!increment) {
+               for(i = 0, ret_val = NULL; i < CONFIG_PAGING_DIR_MAXREGIONS; i++) {
+                       if(pd->pd_regions[i]->reg_type == REGION_HEAP) {
+                               ret_val = pd->pd_regions[i]->reg_base +
+                                       pd->pd_regions[i]->reg_size;
+                               break;
+                       }
+               }
+
+               return(ret_val);
+       }
+
+       for(heap = NULL, i = 0; i < CONFIG_PAGING_DIR_MAXREGIONS; i++) {
+               if(pd->pd_regions[i]->reg_type == REGION_HEAP) {
+                       heap = pd->pd_regions[i];
+                       break;
+               }
+       }
+
+       if(!heap) {
+               u32_t heap_size;
+               int err;
+
+               heap_size = ALIGN(increment, PAGE_SIZE);
+
+               err = _pg_dir_heap_map(pd, heap_size);
+
+               if(!err) {
+                       /* the new heap has been created with the correct size */
+                       return(pg_dir_sbrk(pd, 0));
+               } else {
+                       /* could not map a heap - how do we get the error to the caller? */
+                       return(NULL);
+               }
+       } else {
+               u32_t nbytes;
+               int err;
+
+               if(increment > 0) {
+                       /* round up to the page size */
+                       nbytes = ALIGN(increment, heap->reg_pgsize);
+
+                       err = pg_dir_map(pd, 0, heap->reg_base + heap->reg_size, nbytes, heap->reg_attrs);
+
+                       if(!err) {
+                               /* successfully mapped pages - keep track of the increased heap size */
+                               heap->reg_size += nbytes;
+                       }
+               } else { /* increment < 0 */
+                       /* request to reduce the heap size, round down */
+                       nbytes = (u32_t)-increment & ~(heap->reg_pgsize - 1);
+
+                       /* value may be zero, e.g. if the caller wants to free less than a page */
+                       if(nbytes) {
+                               /* remove nbytes of space at the end of the region */
+                               err = pg_dir_unmap(pd, heap->reg_base + heap->reg_size - nbytes, nbytes);
+
+                               if(!err) {
+                                       heap->reg_size -= nbytes;
+                               }
+                       }
+               }
+
+               /* FIXME: We need to somehow set errno if the mapping failed */
+       }
+
+       /* either way return the new/current sbrk value */
+       ret_val = heap->reg_base + heap->reg_size;
+
+       return(ret_val);
+}
index 9ae6dfa72ecf296ea3077d66f3f2922c86876fe3..f275d4f05396cd87cb3b863f9d79eecbcdcecc1c 100644 (file)
@@ -144,15 +144,45 @@ typedef struct pagedir pg_dir_t;
 
 int     pg_dir_create(pg_dir_t**);
 
+/*
+ * pg_dir_map() - Map page frames into a page directory
+ *
+ * SYNOPSIS
+ *  int pg_dir_map(pg_dir_t* pd, const void *paddr, const void *vaddr,
+ *                 const u32_t bytes, const u32_t attrs);
+ *
+ * DESCRIPTION
+ *  The pg_dir_map() function attempts to map page frames (physical pages) into
+ *  the page directory pointed to by `pd'. It will attempt to allocate the page
+ *  frames pointed to by `paddr', or if `paddr' is NULL it will use whatever is
+ *  available. The virtual address where the frames will be mapped has to be
+ *  provided by the caller in `vaddr'. Unlike `paddr', `vaddr' may not be NULL.
+ *  The number of pages that will be mapped depends on the page directory. If
+ *  PAE-mode paging is employed, pg_dir_map() will attempt to map pages of the
+ *  largest-possible size, meaning that it will attempt to map 4M pages if the
+ *  alignment of the addresses and the page directory's page size permitts.
+ *  Any pages that have been mapped into the page directory will have their
+ *  page attributes set according to the value passed in `attrs'.
+ *  The PAGE_ATTR_PRESENT will be set regardless of the value in `attrs'.
+ *
+ * RETURN VALUE
+ *          0 The frames have been successfully mapped
+ *  -EALREADY The virtual addresses are already mapped
+ *    -ENOMEM Not enough unused page frames to be mapped
+ *    -ENOSYS The function is not implemented for this paging mode
+ *    -EFAULT Could not determine the paging mode used by the page directory
+ */
 int     pg_dir_map(pg_dir_t*, const void*, const void*, const u32_t, const u32_t);
 int     pg_dir_map_region(pg_dir_t*, pg_dir_t*, region_t*);
 int     pg_dir_clone_region(pg_dir_t*, pg_dir_t*, region_t*);
 int     pg_dir_unmap(pg_dir_t*, const void*, const u32_t);
 
+void*   pg_dir_get_heap(pg_dir_t*);
 void*   pg_dir_get_pdbr(pg_dir_t*);
 int     _pg_dir_vpxlate(pg_dir_t*, u32_t, u32_t*);
 void*   pg_dir_get_kstack(pg_dir_t*);
 void*   pg_dir_get_ustack(pg_dir_t*);
+void*   pg_dir_sbrk(pg_dir_t*, i32_t);
 int     pg_dir_foreach_region(pg_dir_t*, int(*)(pg_dir_t*, struct region*, void*), void*);
 int     pg_dir_memcpy(pg_dir_t*, void*, pg_dir_t*, void*, u32_t);