page_attrs_t, region_opts_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);
/*
* _phys_alloc() - Dynamic memory allocator for linear memory
return(ret_val);
}
-/*
- * _pg_dir_heap_start() - Get the start address of page directory's heap
- *
- * SYNOPSIS
- * static void *_pg_dir_heap_start(pg_dir_t *pd);
- *
- * DESCRIPTION
- * The _pg_dir_heap_start() function returns the start address of the heap within the page
- * directory pointed to by `pd'. If there is no heap mapped into the page directory, the end of
- * the last memory section that is not a stack is returned instead (which is the address where
- * the heap will be, once it has been mapped).
- *
- * RETURN VALUE
- * Upon success, the virtual address of the heap will be returned (the heap may in fact not be
- * mapped yet, though). If the position of the heap could not be determined, (void*)-1 will be
- * returned instead.
- *
- * ERRORS
- * This function does not return further information in case of an error.
- */
-static void* _pg_dir_heap_start(pg_dir_t *pd)
-{
- u32_t ret_val;
- int i;
-
- for(i = 0, ret_val = (u32_t)-1; i < CONFIG_PAGING_DIR_MAXREGIONS; i++) {
- struct region *reg;
- u32_t limit;
-
- reg = pd->pd_regions[i];
-
- switch(reg->reg_type) {
- case REGION_TYPE_HEAP:
- ret_val = (u32_t)reg->reg_base;
- i = CONFIG_PAGING_DIR_MAXREGIONS;
-
- break;
-
- case REGION_TYPE_MMAP:
- case REGION_TYPE_TEXT:
- case REGION_TYPE_BSS:
- case REGION_TYPE_DATA:
- case REGION_TYPE_RODATA:
- limit = ALIGN((u32_t)reg->reg_base + reg->reg_size,
- reg->reg_pgsize);
-
- if(limit > ret_val) {
- ret_val = limit;
- }
-
- break;
-
- default:
- case REGION_TYPE_STACK:
- case REGION_TYPE_KSTACK:
- break;
- }
- }
-
- return((void*)ret_val);
-}
-
-/*
- * _pg_dir_heap_map() - Map a heap into a page directory
- *
- * SYNOPSIS
- * static int _pg_dir_heap_map(pg_dir_t *pd, u32_t heap_size);
- *
- * DESCRIPTION
- * The _pg_dir_heap_map() function will map a heap of size `heap_size' into the page directory
- * pointed to by `pd'. The heap will be mapped non-executable if supported by the processor and
- * configured to do so. The heap will be placed after the last memory region that is not a stack.
- *
- * RETURN VALUE
- * Upon success, zero is returned. Otherwise, a negative error number is returned and the page
- * directory is returned to its previous state.
- *
- * ERRORS
- * -EINVAL `pd' does not reference a valid page directory
- * -EALREADY The page directory already has a heap
- * -ENOMEM Ran out of memory trying to allocate the heap
- * -EBADFD The page directory is in an unusable state
- */
-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];
-
- dbg_printf("Found a region at 0x%08x:%08x\n",
- reg->reg_base, reg->reg_size);
-
- switch(reg->reg_type) {
- case REGION_TYPE_HEAP:
- /* the page directory apparently already contains a heap */
- ret_val = -EALREADY;
- break;
-
- case REGION_TYPE_STACK:
- case REGION_TYPE_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_TYPE_MMAP:
- case REGION_TYPE_TEXT:
- case REGION_TYPE_BSS:
- case REGION_TYPE_DATA:
- case REGION_TYPE_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) {
- page_attrs_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_TYPE_HEAP, attrs, REGION_OPT_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);
-}
-
/*
* _clone_kernel_region() - Clone a memory region from the kernel page directory
*
return(ret_val);
}
-/*
- * pg_dir_sbrk() - Change the size of a page directory's heap
- *
- * SYNOPSIS
- * void *pg_dir_sbrk(pg_dir_t *pd, u32_t increment);
- *
- * DESCRIPTION
- * The pg_dir_sbrk() function manipulates the size of the heap that is mapped into the page
- * directory pointed to by `pd'. If `increment' is positive, the size of the heap will be
- * increased by `increment' bytes by increasing the limit of the heap. If `increment' is
- * negative, the limit of the size will be decreased by `-increment', shrinking the heap.
- * If the value of `increment' is zero, The end of the heap will be returned without modifying
- * its position.
- * If there is no heap mapped into the page directory, this function will return the address
- * where the heap will be mapped if this function is called with a positive `increment' value.
- *
- * RETURN VALUE
- * Upon success, the address of the end of the heap (that is, the address right behind the heap)
- * is returned.
- *
- * ERRORS
- * This function does not signal information about errors.
- */
-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_TYPE_HEAP) {
- ret_val = pd->pd_regions[i]->reg_base +
- pd->pd_regions[i]->reg_size;
- break;
- }
- }
-
- /*
- * If we're still here, we should return the address where
- * the program break would have been if there was a heap.
- */
- ret_val = _pg_dir_heap_start(pd);
-
- return(ret_val);
- }
-
- for(heap = NULL, i = 0; i < CONFIG_PAGING_DIR_MAXREGIONS; i++) {
- if(pd->pd_regions[i]->reg_type == REGION_TYPE_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? */
- dbg_printf("Kernel says no. (_pg_dir_heap_map() says %u)\n", -err);
- 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);
-}
-
/*
* pg_fault_debug() - Print page table entries for an address
*