From dcee2e678c4c6e43cd8f271471a5bbb98ea5e0d4 Mon Sep 17 00:00:00 2001 From: Matthias Kruk Date: Sat, 8 Aug 2020 17:07:26 +0900 Subject: [PATCH] kernel/arch: Add missing manpage-style comments to paging functions --- kernel/arch/paging.c | 598 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 581 insertions(+), 17 deletions(-) diff --git a/kernel/arch/paging.c b/kernel/arch/paging.c index 1974e1c..08fafef 100644 --- a/kernel/arch/paging.c +++ b/kernel/arch/paging.c @@ -1073,6 +1073,29 @@ int pg_dir_memcpy(struct pagedir *ddir, void *dvaddr, return(ret_val); } +/* + * _pg_dir_memset_page() - Call memset on a page in a different address space + * + * SYNOPSIS + * int _pg_dir_memset_page(struct pagedior *ddir, void *dvaddr, int c, u32_t n); + * + * DESCRIPTION + * The _pg_dir_memset_page() function sets `n' bytes from the address passed in `dvaddr' within + * the page directory pointed to by `ddir' to the value passed in `c', and returns the number of + * bytes that have been set. This function does not write over page boundaries, which means it + * will write from `dvaddr' only to the end of the page. This means that this function may, in + * the worst case, write no more than one byte. For that reason, this function should be called + * in a loop, in order to make sure partial writes are accommodated for. In almost all cases, + * this function should not be used directly, and pg_dir_memset() should be used in its stead. + * + * RETURN VALUE + * On success, the number of bytes that have been set will be returned. Otherwise, a negative + * error number will be returned. + * + * ERRORS + * -EADDRNOTAVAIL The page at `dvattr' is not mapped in `ddir' + * -ENOSYS The current paging mode is not supported + */ int _pg_dir_memset_page(struct pagedir *ddir, void *dvaddr, int c, u32_t n) { int ret_val; @@ -1104,15 +1127,40 @@ gtfo: return(ret_val); } -int pg_dir_memset(struct pagedir *ddir, void *dvaddr, - int c, u32_t n) +/* + * pg_dir_memset() - Call memset in a different address space + * + * SYNOPSIS + * int pg_dir_memset(struct pagedir *ddir, void *dvaddr, int c, u32_t n); + * + * DESCRIPTION + * The pg_dir_memset() function sets `n' bytes of memory starting at `dvaddr' within the page + * directory pointed to by `ddir' to the value specified in `c' and returns the number of bytes + * that have been written. The write operation is performed page-wise, as the virtual memory + * region might not be continguous in physical memory. + * This function does not map pages into the destination page directory, meaning that it will + * stop if it encounters a non-present page. For that reason, the caller must check the return + * value of this function if there is even a slight chance that the destination memory is not + * mapped into the page directory. + * + * RETURN VALUE + * The number of bytes written is returned. + * + * ERRORS + * This function does not explicitly signal error conditions. However, if a partial write has + * occured, the memory region was not (completely) mapped into the page directory. + */ +int pg_dir_memset(struct pagedir *ddir, void *dvaddr, int c, u32_t n) { int ret_val; ret_val = 0; if(!ddir) { - /* kernel pagedir - just call memset() */ + /* + * Memory in the kernel page directory is continguous, which means that we can + * fulfill the entire request in a single swoop. + */ memset(dvaddr, c, n); return((int)n); } @@ -1135,6 +1183,30 @@ int pg_dir_memset(struct pagedir *ddir, void *dvaddr, return(ret_val); } +/* + * _pg_dir_xfer() - Copy a page from one address space to another one + * + * SYNOPSIS + * int _pg_dir_xfer(struct pagedir *ddir, void *dvaddr, + * struct pagedir *sdir, void *svaddr, u32_t bytes); + * + * DESCRIPTION + * The _pg_dir_xfer() function copies the `bytes' of memory starting at `svaddr' in the page + * directory pointed to by `sdir' to the memory pointed to by `dvaddr' within the page directory + * pointed to by `ddir', and returns the number of bytes that have been copied. + * This function does not read or write beyond page boundaries, which means that it will copy at + * most the content of a single page, and may copy as little as a single byte. + * This function should in most cases not be used directly, and the pg_dir_memcpy() function + * should be used in its stead. + * + * RETURN VALUE + * On success, the number of bytes that have been copied will be returned. Otherwise, a negative + * error number will be returned. + * + * ERRORS + * -EADDRNOTAVAIL Either the source or the destination addresses point to non-present pages + * -ENOSYS The current paging mode is not supported + */ int _pg_dir_xfer(struct pagedir *ddir, void *dvaddr, struct pagedir *sdir, void *svaddr, u32_t bytes) { @@ -1221,6 +1293,27 @@ gtfo: return(ret_val); } +/* + * _pg_dir_pagesize() - Get the size of a specific page in a page directory + * + * SYNOPSIS + * int _pg_dir_pagesize(struct pagedir *pgdir, u32_t virt, u32_t *pagesize); + * + * DESCRIPTION + * The _pg_dir_pagesize() function determines the size of the page pointed to by `virt' within + * the page directory pointed to by `pgdir' and writes the result to the dword pointed to by + * `pagesize'. This function is used within the kernel to determine the amount of data that can + * be written to a specific address without crossing page boundaries, as the page management in + * Corax allows pages of different sizes to be used within the same page directory. + * + * RETURN VALUE + * On success, zero is returned. Otherwise, a negative error number is returned and the value + * of `*pagesize' is left untouched. + * + * ERRORS + * -EADDRNOTAVAIL The page pointed to by `virt' is not present in the page directory + * -ENOSYS The current paging mode is not supported + */ int _pg_dir_pagesize(struct pagedir *pgdir, u32_t virt, u32_t *pagesize) { int ret_val; @@ -1269,6 +1362,26 @@ int _pg_dir_pagesize(struct pagedir *pgdir, u32_t virt, u32_t *pagesize) return(ret_val); } +/* + * _pg_dir_vpxlate() - Look up the corresponding physical address of a virtual address + * + * SYNOPSIS + * int _pg_dir_vpxlate(struct pagedir *pgdir, u32_t virt, u32_t *phys); + * + * DESCRIPTION + * The _pg_dir_vpxlate() performs a translation of the virtual address specified by `virt' within + * the page directory pointed to by `pgdir' and writes the result to the dword pointed to by + * `phys'. The translation works essentially the same way as the MMU would have done it, except + * in software. This operation is potentially costly and should be used sparingly. + * + * RETURN VALUE + * On success, zero is returned. Otherwise, a negative error number is returned and the value of + * `*phys' is left untouched. + * + * ERRORS + * -EFAULT The page pointed to by `virt' is non-present in the page directory + * -ENOSYS The current paging mode is not supported + */ int _pg_dir_vpxlate(struct pagedir *pgdir, u32_t virt, u32_t *phys) { int ret_val; @@ -1320,6 +1433,28 @@ int _pg_dir_vpxlate(struct pagedir *pgdir, u32_t virt, u32_t *phys) return(ret_val); } +/* + * _pg_dir_kstack_map() - Map a kernel-mode stack into a page directory + * + * SYNOPSIS + * int _pg_dir_kstack_map(struct pagedir *pgdir); + * + * DESCRIPTION + * The _pg_dir_kstack_map() function attempts to map a stack for kernel-mode execution into the + * page directory pointed to by `pgdir'. This function also updates the state of the page + * directory so that the newly mapped stack is used when an interrupt occurs during execution + * within the page directory. + * The kernel-mode stack will be mapped at the virtual address CONFIG_KERNEL_STACK_BASE within + * the page directory. The stack will be mapped non-executable if supported by the processor and + * configured to do so. + * + * RETURN VALUE + * If successful, this function returns zero. Otherwise, a negative error number is returned and + * the page directory is returned to its previous state. + * + * ERRORS + * -ENOMEM The system ran out of memory while trying to map the stack + */ int _pg_dir_kstack_map(struct pagedir *pgdir) { int ret_val; @@ -1365,6 +1500,27 @@ cleanup: return(ret_val); } +/* + * _pg_dir_ustack_map() - Map a stack for user-mode execution into a page directory + * + * SYNOPSIS + * int _pg_dir_ustack_map(struct pagedir *pgdir); + * + * DESCRIPTION + * The _pg_dir_ustack_map() function maps a stack for user-mode execution into the page directory + * pointed to by `pgdir' and adjusts the internal state of the page directory so that the newly + * allocated stack will be used upon a context switch to the page directory. + * The user-mode stack will be mapped at the virtual address CONFIG_KERNEL_STACK_BASE - PAGE_SIZE + * within the page directory. The stack will be made non-executable if supported by the processor + * and configured to do so. + * + * RETURN VALUE + * Opon success, zero is returned. Otherwise, a negative error number will be returned and the + * page directory will be returned to its previous state. + * + * ERRORS + * -ENOMEM The system ran out of memory trying to allocate the stack + */ int _pg_dir_ustack_map(struct pagedir *pgdir) { int ret_val; @@ -1378,7 +1534,8 @@ int _pg_dir_ustack_map(struct pagedir *pgdir) void* vaddr; vaddr = (void*)((u32_t)CONFIG_KERNEL_STACK_BASE - PAGE_SIZE); - attrs = PAGE_ATTR_PRESENT | PAGE_ATTR_WRITABLE | PAGE_ATTR_USER | PAGE_ATTR_NO_EXEC; + attrs = PAGE_ATTR_PRESENT | PAGE_ATTR_WRITABLE | + PAGE_ATTR_USER | PAGE_ATTR_NO_EXEC; ret_val = pg_dir_map(pgdir, frame, vaddr, PAGE_SIZE, attrs); @@ -1409,6 +1566,26 @@ cleanup: 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; @@ -1451,6 +1628,27 @@ static void* _pg_dir_heap_start(pg_dir_t *pd) 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) { /* @@ -1485,6 +1683,9 @@ static int _pg_dir_heap_map(pg_dir_t *pd, u32_t heap_size) 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 */ @@ -1560,6 +1761,28 @@ gtfo: return(ret_val); } +/* + * _clone_kernel_region() - Clone a memory region from the kernel page directory + * + * SYNOPSIS + * int _clone_kernel_region(pg_dir_t *kdir, region_t *reg, void *data); + * + * DESCRIPTION + * The _clone_kernel_region() function clones the memory region pointed to by `reg' from the + * page directory pointed to by `kdir' into the page directory pointed to by `data'. This + * function is called from pg_dir_create() by means of the pg_dir_foreach_region() function. As + * such, it is not meant to be called directly. + * If `reg' represents a .text, .bss, .data, or .rodata region, the region will be mapped + * directly. This function does not actually clone the pages belonging to those memory regions, + * as these pages cannot be modified outside of the kernel, and thus there is no need for copies + * that are private to a process. + * + * RETURN VALUE + * This function zero upon success, or a negative value if an error has occured. + * + * ERRORS + * This function passes on errors returned by pg_dir_map_region(). + */ int _clone_kernel_region(pg_dir_t *kdir, region_t *reg, void *data) { pg_dir_t *dir; @@ -1667,6 +1890,28 @@ int _clone_kernel_region(pg_dir_t *kdir, region_t *reg, void *data) return(ret_val); } +/* + * pg_dir_create() - Create a new page directory + * + * SYNOPSIS + * int pg_dir_create(pg_dir_t **dst); + * + * DESCRIPTION + * The pg_dir_create() function allocates a new page directory and populates it with those memory + * regions from the kernel that are necessary to make it usable. This function also maps stacks + * for execution in user-mode and kernel-mode into the newly created page directory. + * + * RETURN VALUE + * Upon success, this function returns zero, and the pointer pointed to by `dst' is adjusted to + * point to the newly allocated page directory. Otherwise, this function returns a negative error + * number, and the value of `*dst' is undefined. + * + * ERRORS + * -ENOMEM There were no empty page frames to allocate the page directory + * -EFAULT The system's paging mode is in an invalid state + * + * This function also passes on errors from _pg_dir_kstack_map() and _pg_dir_ustack_map(). + */ int pg_dir_create(pg_dir_t **dst) { int ret_val; @@ -1735,8 +1980,30 @@ int pg_dir_create(pg_dir_t **dst) return(ret_val); } +/* + * _pg_dir_map_legacy() - Map memory into a legacy page directory + * + * SYNOPSIS + * int _pg_dir_map_legacy(page_table_t *pd, u32_t paddr, u32_t vaddr, + * u32_t size, const page_attrs_t attrs); + * + * DESCRIPTION + * The _pg_dir_map_legacy() function maps the `size' bytes of physical memory starting at `paddr' + * to the virtual address `vaddr' in the page directory pointed to by `pd', applying the page + * attributes specified in `attrs'. + * If `paddr' is NULL, the physical location of the mapped memory depends on the available of + * allocatable page frames in the system. In this case, the allocated memory may not be + * continguous in physical memory. Unlike `paddr', `vaddr' may not be NULL. + * + * RETURN VALUE + * This function returns zero upon success, or a negative error number in an error has occured. + * + * ERRORS + * -ENOMEM Not enough free page frames available to satisfy the allocation + * -EALREADY The specified virtual memory is already (partially) mapped + */ static int _pg_dir_map_legacy(page_table_t *pd, u32_t paddr, u32_t vaddr, - u32_t size, const u32_t flags) + u32_t size, const page_attrs_t attrs) { int ret_val; @@ -1759,13 +2026,17 @@ static int _pg_dir_map_legacy(page_table_t *pd, u32_t paddr, u32_t vaddr, */ if(size >= PAGE_SIZE_LARGE && ALIGNED(vaddr, PAGE_SIZE_LARGE) && paddr && ALIGNED(paddr, PAGE_SIZE_LARGE)) { - pd->pt_entries[pde] = paddr | PAGE_ATTR_SIZE | PAGE_ATTR_PRESENT | flags; + pd->pt_entries[pde] = paddr | PAGE_ATTR_SIZE | + PAGE_ATTR_PRESENT | attrs; paddr += PAGE_SIZE_LARGE; vaddr += PAGE_SIZE_LARGE; size -= PAGE_SIZE_LARGE; - /* skip the rest of the loop, which would attempt to populate the page table */ + /* + * Skip the rest of the loop, which would attempt + * to populate the page table + */ continue; } else { pd->pt_entries[pde] = (u32_t)pg_frame_alloc_end(); @@ -1778,19 +2049,19 @@ static int _pg_dir_map_legacy(page_table_t *pd, u32_t paddr, u32_t vaddr, memset((void*)pd->pt_entries[pde], 0, PAGE_SIZE); } - pd->pt_entries[pde] |= PAGE_ATTR_PRESENT | flags; + pd->pt_entries[pde] |= PAGE_ATTR_PRESENT | attrs; } } pt = (page_table_t*)(pd->pt_entries[pde] & 0xfffff000); /* make sure PAGE_ATTR_USER is set on the page table, if necessary */ - if(flags & PAGE_ATTR_USER) { + if(attrs & PAGE_ATTR_USER) { pd->pt_entries[pde] |= PAGE_ATTR_USER; } /* also make sure the page table is writable, if necessary */ - if(flags & PAGE_ATTR_WRITABLE) { + if(attrs & PAGE_ATTR_WRITABLE) { pd->pt_entries[pde] |= PAGE_ATTR_WRITABLE; } @@ -1813,8 +2084,7 @@ static int _pg_dir_map_legacy(page_table_t *pd, u32_t paddr, u32_t vaddr, break; } - /* set the flags */ - pt->pt_entries[pte] |= PAGE_ATTR_PRESENT | flags; + pt->pt_entries[pte] |= PAGE_ATTR_PRESENT | attrs; vaddr += PAGE_SIZE; size -= PAGE_SIZE; @@ -1823,8 +2093,31 @@ static int _pg_dir_map_legacy(page_table_t *pd, u32_t paddr, u32_t vaddr, return(ret_val); } +/* + * _pg_dir_map_pae() - Map pages into a PAE page directory + * + * SYNOPSIS + * int _pg_dir_map_pae(pdpt_t *pd, u64_t paddr, u64_t vaddr, + * u32_t size, const page_attr_t attrs); + * + * DESCRIPTION + * The _pg_dir_map_pae() function maps the `size' bytes of physical memory starting at `paddr' + * to the virtual address `vaddr' in the page directory pointed to by `pd' and applies the page + * attributes in `attrs' to the newly mapped pages. If `paddr' is NULL, the physical addresses of + * the mapping depend on the availability of page frames in the system. In that case, the mapped + * memory is not guaranteed to be continguous in physical memory. + * This function will attempt to map the desired memory region using the largest possible page + * size that suits the passed parameters. + * + * RETURN VALUE + * This function returns zero upon success, or a negative error number if an error has occured. + * + * ERRORS + * -ENOMEM Not enough free page frames available to satisfy the allocation + * -EALREADY The specified virtual memory is already (partially) mapped + */ static int _pg_dir_map_pae(pdpt_t *pd, u64_t paddr, u64_t vaddr, - u32_t size, const u32_t flags) + u32_t size, const page_attrs_t attrs) { int ret_val; @@ -1867,7 +2160,7 @@ static int _pg_dir_map_pae(pdpt_t *pd, u64_t paddr, u64_t vaddr, } /* set the page attributes on the entry */ - pt->ppt_entries[pte] |= PAGE_ATTR_PRESENT | flags; + pt->ppt_entries[pte] |= PAGE_ATTR_PRESENT | attrs; vaddr += PAGE_SIZE; paddr += PAGE_SIZE; @@ -1877,8 +2170,32 @@ static int _pg_dir_map_pae(pdpt_t *pd, u64_t paddr, u64_t vaddr, return(ret_val); } +/* + * pg_dir_map() - Map memory into a page directory + * + * SYNOPSIS + * int pg_dir_map(pg_dir_t *pd, const void *phys, const void *virt, + * const u32_t size, const page_attrs_t attrs); + * + * DESCRIPTION + * The pg_dir_map() function maps the `size' bytes of physical memory pointed to by `phys' to + * the virtual address `virt' from the page directory pointed to by `pd', setting the page + * attributes according to `attrs'. If `phys' is NULL, this function will use whatever page + * frames are available for allocation, but they need not be continguous in memory. The value + * passed in `virt' may not be NULL. + * Depending on the paging mode configured in the kernel, this function will call either of the + * _pg_dir_map_legacy() or _pg_dir_map_pae() functions to perform the actual mapping of the + * pages. + * + * RETURN VALUE + * This function returns zero upon success. Otherwise, a negative error number is returned. + * + * ERRORS + * -ENOSYS The configured paging mode is not supported + * -EFAULT The configured paging mode is invalid + */ int pg_dir_map(pg_dir_t *pd, const void *phys, const void *virt, - const u32_t size, const u32_t flags) + const u32_t size, const page_attrs_t attrs) { int ret_val; u32_t asize; @@ -1893,13 +2210,13 @@ int pg_dir_map(pg_dir_t *pd, const void *phys, const void *virt, case PG_MODE_LEGACY: ret_val = _pg_dir_map_legacy((struct page_table*)pd->pd_base, (u32_t)phys, (u32_t)virt, - asize, flags); + asize, attrs); break; case PG_MODE_PAE: ret_val = _pg_dir_map_pae((struct pdpt*)pd->pd_base, (unsigned)phys, (unsigned)virt, - asize, flags); + asize, attrs); break; case PG_MODE_IA32E: @@ -1915,6 +2232,29 @@ int pg_dir_map(pg_dir_t *pd, const void *phys, const void *virt, return(ret_val); } +/* + * pg_dir_map_region() - Map a memory region from one page directory into another one + * + * SYNOPSIS + * int pg_dir_map_region(pg_dir_t *dpd, pg_dir_t *spd, region_t *reg); + * + * DESCRIPTION + * The pg_dir_map_region() function maps the memory region pointed to by `reg' from the source + * page directory `spd' to the destination page directory `dpd'. + * The region will be mapped into the destination page directory with the same page flags as used + * in the source page directory, unless the source page directory is the kernel page directory. + * In the latter case, the pages will be mapped read-only into the destination page directory. + * + * RETURN VALUE + * This function returns zero upon success, or a negative error number if an error has occured. + * + * ERRORS + * -ERANGE The maximum number of regions has been mapped into the destination page directory + * -EFAULT An unknown error (kernel bug) has occured + * + * This function also passes on errors from the _pg_dir_vpxlate(), pg_dir_map(), and + * _pg_dir_add_region() functions. + */ int pg_dir_map_region(pg_dir_t *dpd, pg_dir_t *spd, region_t *reg) { int ret_val; @@ -1991,6 +2331,25 @@ gtfo: return(ret_val); } +/* + * pg_dir_clone_region() - Clone a memory region from one page directory into another one + * + * SYNOPSIS + * int pg_dir_clone_region(pg_dir_t *dpd, pg_dir_t *spd, region_t *reg); + * + * DESCRIPTION + * The pg_dir_clone_region() function creates a copy of the memory region pointed to by `reg' + * from the source page directory `spd' and maps the copy into the destination page directory + * `dpd'. The memory region will be mapped with the same page attributes into the destination + * page directory, with the exception that PAGE_ATTR_USER will be added where necessary. + * + * RETURN VALUE + * -ERANGE The maximum number of regions has been mapped into the destination page directory + * -EFAULT An unknown error (kernel bug) has occured + * + * This function also passes on errors from the pg_dir_map(), pg_dir_memcpy(), and + * _pg_dir_add_region() functions. + */ int pg_dir_clone_region(pg_dir_t *dpd, pg_dir_t *spd, region_t *reg) { int ret_val; @@ -2039,11 +2398,49 @@ gtfo: return(ret_val); } +/* + * pg_dir_unmap() - Release a memory mapping from a page directory + * + * SYNOPSIS + * int pg_dir_unmap(pg_dir_t *pd, const void *base, const u32_t size); + * + * DESCRIPTION + * The pg_dir_unmap() function releases `size' bytes of memory starting at the virtual address + * `base' from the page directory pointed to by `pd'. This function is currently not implemented. + * + * RETURN VALUE + * This function returns zero upon success, otherwise a negative error number is returned. + * + * ERRORS + * -ENOSYS The function is not implemented + */ int pg_dir_unmap(pg_dir_t *pd, const void *base, const u32_t size) { return(-ENOSYS); } +/* + * pg_dir_foreach_region() - Execute a function on all memory regions in a page directory + * + * SYNOPSIS + * int pg_dir_foreach_region(pg_dir_t *pd, int (*func)(pg_dir_t*, region_t*, void*), void *data); + * + * DESCRIPTION + * The pg_dir_foreach_region() iterates over the memory regions in the page directory pointed to + * by `pd' and calls the function pointed to by `func' on each one of them. The function pointed + * to by `func' must accept three arguments; the first one is a pointer to the page directory, + * the second one is a pointer to the memory region, the third value passed to the function is + * the value that was passed to pg_dir_foreach_region() in the `data' parameter. + * If the function pointed to by `func' returns a negative value, pg_dir_foreach_region() will + * interrupt the iteration early. + * + * RETURN VALUE + * This function will return the value that was returned by the last call to `func'. If an error + * has occured, a negative error number will be returned. + * + * ERRORS + * -EINVAL `pd' or `func' point to an invalid value + */ int pg_dir_foreach_region(pg_dir_t *pd, int (*func)(pg_dir_t*, region_t*, void*), void *data) { int ret_val; @@ -2071,6 +2468,31 @@ int pg_dir_foreach_region(pg_dir_t *pd, int (*func)(pg_dir_t*, region_t*, void*) return(ret_val); } +/* + * _pg_dir_add_region() - Add a region to a page directory + * + * SYNOPSIS + * int _pg_dir_add_region(pg_dir_t *pd, void *base, u32_t size, const region_type_t type, + * const page_attrs_t attrs, const region_opts_t opts); + * + * DESCRIPTION + * The _pg_dir_add_region() function adds a region with the specified parameters to the page + * directory pointed to by `pd'. A region is a data structure used purely for accounting + * purposes. This call will not modify the paging structures referenced by the page directory. + * The values passed in `base' and `size' represent the base address of the memory region, and + * the regions size in bytes, respectively. The value of `type' determines the type of region + * that the memory represents. The `attrs' argument determines the page attributes that are used + * for pages within this region. The value in `opts' is used to determine if this region is + * private to this page directory, or if it may be shared. + * + * RETURN VALUE + * On success, this function returns zero. Otherwise, a negative error number is returned. + * + * ERRORS + * -EFAULT An unknown error (kernel bug) has occured + * -ERANGE The maximum number of regions has been mapped into the destination page directory + * -ENOMEM The kernel ran out of heap space + */ static int _pg_dir_add_region(pg_dir_t *pd, void *base, u32_t size, region_type_t type, page_attrs_t attrs, region_opts_t opts) { @@ -2115,11 +2537,49 @@ gtfo: return(ret_val); } +/* + * pg_dir_get_pdbr() - Get the base address of a page directory + * + * SYNOPSIS + * void *pg_dir_get_pdbr(pg_dir_t *pd); + * + * DESCRIPTION + * The pg_dir_get_pdbr() function returns the base address of the page directory pointed to by + * `pd'. The return value may be used in assigments to the page directory base register (also + * known as CR3 on IA-32/Intel64). + * + * RETURN VALUE + * The value of the variable keeping track of the PDBR within the page directory structure is + * returned by this function. This function does not perform any checks to ensure the validity + * of that value. + * + * ERRORS + * This function does not signal any error conditions. + */ void* pg_dir_get_pdbr(pg_dir_t *pd) { return(pd->pd_base); } +/* + * pg_dir_get_heap() - Get the virtual address of a page directory's heap + * + * SYNOPSIS + * void *pg_dir_get_heap(pg_dir_t *pd); + * + * DESCRIPTION + * The pg_dir_get_heap() function returns the virtual address of the heap that is mapped into the + * page directory pointed to by `pd'. This is done by iterating over all memory regions that are + * associated with the page directory, returning the base address of the region with type + * REGION_TYPE_HEAP. + * + * RETURN VALUE + * Upon success, the virtual base address of the heap is returned. If there is no heap mapped + * into the page directory, NULL will be returned instead. + * + * ERRORS + * This function does not convey any additional information about errors. + */ void* pg_dir_get_heap(pg_dir_t *pd) { void *ret_val; @@ -2135,6 +2595,29 @@ void* pg_dir_get_heap(pg_dir_t *pd) 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; @@ -2180,6 +2663,7 @@ void* pg_dir_sbrk(pg_dir_t *pd, i32_t increment) 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 { @@ -2222,6 +2706,25 @@ void* pg_dir_sbrk(pg_dir_t *pd, i32_t increment) return(ret_val); } +/* + * pg_fault_debug() - Print page table entries for an address + * + * SYNOPSIS + * void pg_fault_debug(pg_dir_t *pdir, void *addr); + * + * DESCRIPTION + * The pg_fault_debug() function prints the page table entries for the virtual address `addr' + * within the page directory pointed to by `pdir'. This function will perform the page table + * lookup as it would be done by the MMU and print the encountered entries to the kernel's debug + * output. + * This function is currently only implemented for legacy IA-32 paging. + * + * RETURN VALUE + * void + * + * ERRORS + * This function does not return errors. + */ void pg_fault_debug(pg_dir_t *pdir, void *addr) { u64_t pde; @@ -2256,6 +2759,47 @@ void pg_fault_debug(pg_dir_t *pdir, void *addr) return; } +/* + * pg_dir_mmap() - Map various kinds of memory into a page directory + * + * SYNOPSIS + * int pg_dir_mmap(pg_dir_t *pgdir, void *addr, u32_t size, int prot, int flags, void **dst); + * + * DESCRITION + * The pg_dir_mmap() function maps memory into the page directory pointed to by `pgdir' according + * to the values specified in the remaining parameters. The function will attempt to create the + * mapping at the virtual address `addr'. The mapping will be created from arbitrary free page + * frames, meaning that they may not be continguous in physical memory. + * If the MAP_PHYS flag is set in `flags', this function will attempt to create an identity + * mapping to physical memory, thus allowing the calling process to map device memory into their + * address space. + * + * The attributes of the pages in the region will be set according to `prot': + * - If the PROT_EXEC bit is set, the pages will be made executable + * - If the PROT_WRITE bit is set, the pages will be made writable + * - If the PROT_READ bit is set, the pages will be made readable + * + * Note that, in absense of the PROT_READ bit, the pages will not be accessible from non-kernel + * contexts at all, so this bit should almost always be set. + * + * The visibility of the mapping is further influenced by the value of `opts': + * - If `opts' is REGION_OPT_SHARED, the physical memory may be shared with other processes, + * - otherwise the memory region will be private and not shareable with other processes. + * - However, mappings made with the MAP_PHYS flag cannot be made private. + * + * If the mapping has been successful, the pointer pointed to by `dst' will be made to point to + * the virtual address of the mapping. + * + * RETURN VALUE + * This function will return zero upon success, or a negative error number in the case that an + * error has occured. In the latter case, the value of `*dst' is undefined. + * + * ERRORS + * -EINVAL Either `pgdir' or `dst' are invalid pointers + * -EFAULT An unknown error (kernel bug) has occured + * + * This function also passes on error values returned by pg_dir_map() and _pg_dir_add_region(). + */ int pg_dir_mmap(pg_dir_t *pgdir, void *addr, u32_t size, int prot, int flags, void **dst) { @@ -2316,6 +2860,7 @@ int pg_dir_mmap(pg_dir_t *pgdir, void *addr, u32_t size, /* or unmap if it can't be accounted for */ if(ret_val < 0) { + dbg_printf("...and unmapping again\n"); pg_dir_unmap(pgdir, addr, size); } } @@ -2331,6 +2876,25 @@ int pg_dir_mmap(pg_dir_t *pgdir, void *addr, u32_t size, return(ret_val); } +/* + * pg_dir_munmap() - Release a mapping made by pg_dir_mmap() + * + * SYNOPSIS + * int pg_dir_munmap(pg_dir_t *pgdir, void *addr, u32_t size); + * + * DESCRIPTION + * The pg_dir_munmap() function releases the `size' bytes of memory mapped at the virtual address + * `addr' inside the page directory pointed to by `pgdir'. This function is the inverse function + * of pg_dir_mmap(). + * + * This function is currently not implemented. + * + * RETURN VALUE + * This function returns zero upon success, or a negative error number if an error has occured. + * + * ERRORS + * -ENOSYS The function is not implemented + */ int pg_dir_munmap(pg_dir_t *pgdir, void *addr, u32_t size) { return(-ENOSYS); -- 2.47.3