Overview
Comment: | finish moving heap allocation/free functions to the posix syscall apparatus and deprecate the direct assembly implementations of platform_mmap; update the kmem docs to match new function signatures (and remove typos) |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA3-256: |
709ffb094d0a399ac98957496b58a8ac |
User & Date: | lexi on 2019-08-22 04:31:43 |
Other Links: | manifest | tags |
Context
2019-08-22
| ||
08:44 | add kmlini() and kmlina() functions; restructure allocation functions to work more reasonably (returning a tuple struct instead of making a user pass in a void**); update docs accordingly check-in: acb4a9944e user: lexi tags: trunk | |
04:31 | finish moving heap allocation/free functions to the posix syscall apparatus and deprecate the direct assembly implementations of platform_mmap; update the kmem docs to match new function signatures (and remove typos) check-in: 709ffb094d user: lexi tags: trunk | |
02:52 | removed sneaky segfault in x86-64 syscall fn where %r8 (the register that contains the pointer to the syscall arguments from the C syscall wrapper, which need to be copied into the correct registers before the kernel is invoked) gets overwritten if the syscall valency > 5, because of overlapping ccall and syscall ABI argument registers - r8 is clobbered by argument 5 and any further attempts to use it as a ptr segfault at best. also modified the report function so that it immediate cancels compilation if a sub-process reports failure. changed allocator function signatures so they can return a condition code if the kernel reports an error; updated example code so it compiles and runs without fault. check-in: e50a476efe user: lexi tags: trunk | |
Changes
Name change from mod/kmem/platform.mmap.fn.x86.lin.64.s to legacy/platform.mmap.fn.x86.lin.64.s.
Name change from mod/kmem/platform.munmap.fn.x86.lin.64.s to legacy/platform.munmap.fn.x86.lin.64.s.
Modified mod/kmem/heapa.fn.c from [e093792a2c] to [c8ccec879e].
15 15 16 16 #include <error_table.h> 17 17 18 18 /* we define all our platform functions here, whether or not 19 19 * they're for the correct platform - only the ones that are 20 20 * called by the preprocessed form of the code will actually 21 21 * be linked, linker errors are our friend here! */ 22 -extern void* kmem_platform_mmap(void* addr, 23 - unsigned long sz, unsigned long prot, unsigned long flags, 24 - unsigned long fd, unsigned long off); 25 22 26 23 kmcond kmheapa(void** where, sz len) { 27 24 /* allocate an object on the heap and return 28 25 * a pointer, or NULL if the allocation failed. */ 29 26 union { 30 27 void* raw; 31 28 ubyte* byte; ................................................................................ 57 54 58 55 k_platform_syscall_arg args[] = { 59 56 null, region_size, 60 57 posix_prot_read | posix_prot_write, 61 58 posix_flag_anonymous | posix_map_shared, 62 59 -1, 0 63 60 }; 61 + 62 + /* impl note: while per manpage fd is "ignored" 63 + * for MAP_ANONYMOUS, "some implementations" require 64 + * a value of -1 */ 64 65 65 66 struct k_platform_syscall_answer r = k_platform_syscall 66 67 (k_platform_syscall_mmap, Kmsz(args), args); 67 68 68 69 if (r.error == 0) region.byte = (ubyte*)r.ret; else { 69 70 switch (r.error) { 70 71 case k_platform_error_EAGAIN: return kmcond_bad_lock; 71 72 case k_platform_error_EINVAL: return kmcond_bad_size; 72 73 case k_platform_error_EMFILE: return kmcond_too_many; 73 74 case k_platform_error_ENOMEM: return kmcond_no_room; 74 75 default: return kmcond_fail_assert; 75 76 } 76 77 } 77 - 78 - /* region.byte = kmem_platform_mmap(null, region_size, */ 79 - /* posix_prot_read | posix_prot_write, */ 80 - /* posix_flag_anonymous | posix_map_shared, -1, 0); */ 81 - 82 - /* impl note: while per manpage fd is "ignored" 83 - * for MAP_ANONYMOUS, "some implementations" require 84 - * a value of -1 */ 85 - 86 78 # else 87 79 Knoimpl(kmheapa,KVos); 88 80 # error missing implementation 89 81 # endif 90 82 91 83 void* const object = (region.byte + sizeof (kmbox)); 92 84 93 85 region.header -> kind = kmkind_heap; 94 86 region.header -> size = len; 95 87 96 88 *where = object; 97 89 return kmcond_ok; 98 90 }
Modified mod/kmem/heapf.fn.c from [3d67513f54] to [fd2ee0e61b].
29 29 30 30 if (header -> kind != kmkind_heap) return kmcond_mismatch; 31 31 # ifdef KFenv_posix 32 32 /* currently allocation is handled on posix by naive use 33 33 * of MAP_ANONYMOUS. munmap needs to be told the size of 34 34 * the region to unmap (free), which kmheapa() stores at 35 35 * (ptr - sizeof sz). see kmheap.c for details. */ 36 + k_platform_syscall_arg args[] = { (sz)header, total }; 37 + struct k_platform_syscall_answer r = k_platform_syscall 38 + (k_platform_syscall_munmap, Kmsz(args), args); 36 39 37 - if(kmem_platform_munmap(header, total) == -1) { 38 - /* we don't need to bother recovering errno; 39 - * there's only one possible munmap error */ 40 + if(r.error==0) { 41 + /* we don't need to bother recovering the error 42 + * code, there's only one possible munmap error */ 40 43 return kmcond_bad_address; 41 44 } 42 45 43 46 # else 44 47 Knoimpl(kmheapf,KVos); 45 48 # error missing implementation 46 49 # endif 47 50 return kmcond_ok; 48 51 }
Modified mod/kmem/heapo.fn.c from [e98ef79662] to [70c3679214].
1 1 #include <k/core.h> 2 2 #include <k/mem.h> 3 -/* heapao.fn.c - kmheapao() "allocate heap object" 3 +/* heapo.fn.c - kmheapo() "allocate heap object" 4 4 * ~ lexi hale <lexi@hale.su> 5 - * kmheapao() allocates a region in heap memory 5 + * kmheapo() allocates a region in heap memory 6 6 * and returns a kmptr struct referencing that 7 7 * newly allocated region. 8 8 */ 9 9 10 -kmcond kmheapao(kmptr* where, sz size) { 10 +kmcond kmheapo(kmptr* where, sz size) { 11 11 void* ptr; 12 12 kmcond e = kmheapa(&ptr, size); 13 13 if (e != kmcond_ok) return e; 14 14 kmptr p = { 15 15 .kind = (ptr != null ? kmkind_heap : kmkind_fail), 16 16 .ref = ptr, 17 17 .shred = false, 18 18 }; 19 19 *where = p; 20 20 return kmcond_ok; 21 21 }
Modified mod/kmem/kmem.md from [73d0a96333] to [0e44a94bf4].
1 1 # kmem 2 2 3 3 **kmem** is a libk module that contains various functions for memory allocation and deallocation. it uses the **short** naming convention with the glyph `m`. 4 + 5 +# description 4 6 5 7 kmem allocators can work in several different ways. they can allocate memory directly from the heap (like `kmheapa()` and `kmlina()`), use a header that has already been allocated by another function, or allocate memory only from a pre-allocated pool. linear allocation with pool allocation is particularly useful, as it permits the very rapid allocation and deallocation of lots of objects with only a few adjustments to the heap, and no possibility of fragmentation or need for expensive algorithms like `malloc()` or `kmheapa()` 6 8 7 -## module functions 9 +# module functions 8 10 9 11 kmem supplies two module-level functions, used to interact with the `kmptr` container type. 10 12 11 - * `kmfree(kmptr) → void` - free, downref, or ignore the pasted object as appropriate 12 - * `kmshred(kmptr) → void` - free, downref, or ignore the pasted object as appropriate. if deallocating, zero its contents 13 + * `kmfree(kmptr) → kmcond` - free, downref, or ignore the passed object as appropriate 14 + * `kmshred(kmptr) → void` - free, downref, or zero the passed object as appropriate. if downref'ing, mark underlying object to be shredded. otherwise, zero its contents, then deallocate if appropriate. 13 15 * `kmstat(void*) → kmptr` - convenience function to wrap a pointer to a non-managed object in a `kmptr` struct, so it can be passed to functions that accept arbitrary objects. `kmptr p = kmstat(raw)` is equivalent to `kmptr p = { kmkind_none, raw, NULL }`. 14 16 * `kmtaint(&kmptr) → void` - "taints" a `kmptr` object by setting it to be shredded when freed. this may be desirable if the object pointed to contains privileged information. 15 17 * `kmzero(void*,sz) → void` - zeroes a region of memory 16 18 * `kmozero(kmptr) → void` - zeroes an object in memory 17 19 * `kmcopy(void* dest, void* src, sz) → void` - copies one region of memory to another 18 20 * `kmdup(kmptr) → kmptr` - duplicates an object in memory, allocating it as sibling of the original 19 21 20 -## types 22 +# types 21 23 22 24 kmem defines the following types: 23 25 24 26 * `enum kmkind` - enumerates allocation strategies 25 27 * `struct kmptr` - abstract pointer object 26 28 * `struct kmcell` - abstract memory cell 27 29 * `struct kmref` - a reference-counted cell 28 30 * `struct kmnode` - a node in an allocation tree 29 31 * `struct kmpool` - a memory pool 30 32 31 33 `kmptr` and `kmcell` are both very similar. the difference is that a kmptr points to a region in memory and can be passed around freely. a `kmcell` is the actual in-memory representation of an allocation cell. a `kmcell` cannot be usefully instantiated; rather, it is downcast from an actual cell type (e.g. `kmnode n; kmcell* s = (kmcell*)(&n)`) 32 34 33 35 34 -### kmkind 36 +## kmkind 35 37 36 38 `kmkind` is an enum that specifies an allocation function. 37 39 38 40 * `kmkind_none` - no allocation 39 41 * `kmkind_lin` - linear heap allocation 40 42 * `kmkind_heap` - random heap allocation 41 43 * `kmkind_pool` - pool allocation 42 44 * `kmkind_ref` - reference-counting allocation 43 45 * `kmkind_tree` - tree allocation 44 46 45 -### kmptr 47 +## kmptr 46 48 47 49 kmem functions can operate on both raw pointers and the `kmptr` struct type. `kmptr` is a generic struct that can contain any kind of pointer. this is useful if you wish to allocate different objects in different manners, but pass them on into a single interface. 48 50 49 51 memory pointed at by `kmptr` pointers can be freed either with the usual specialized function, or by passing the `kmptr` structure itself to the generic function `kmfree`, which will handle it appropriately, even if it's a pointer to a garbage-collected object or to a static region of memory. 50 52 51 53 a `kmptr` has the following layout: 52 54 ................................................................................ 53 55 * `kmkind kind` - codes the type of pointer; `kmkind_none` indicates a non-allocated pointer to a static (global or on-stack) object. 54 56 * `kmshred shred` - an enum. if `kmshred_yes`, the value will be zeroed or otherwise made unreadable on free. if no, `kmfree` will consult `src` for shred policy if it is not NULL. 55 57 * `void* ref` - the raw pointer enclosed by `cell` 56 58 * `kmcell* cell` - a pointer to an object enclosure, typically either a memory pool or a referencing-counting object. NULL if not needed. 57 59 58 60 the convenience function `kmstat(void*) → kmptr` wraps a pointer to a static object in a `kmptr` struct. 59 61 60 -### struct kmcell 62 +## struct kmcell 61 63 62 64 `kmcell` is a stub struct used to disambiguate between source types. a "source" is an object that can hold an allocated object, such as the heap, a memory pool, a fixed-length array on stack, or a fixed-length global array. all values produced by a kmem allocation function can be cast to `kmcell*`, and have an intial field `id` that contains a `kmcell`. 63 65 64 66 * `kmkind kind` - kind of cell 65 67 * `size_t size` - size of cell (data plus all fields) 66 68 * `kmshred shred` - shredding flag 67 69 68 -### struct kmref 70 +## struct kmref 69 71 70 72 `kmref` is a struct that constitutes the in-memory representation of a reference-counted cell. 71 73 72 74 * `kmcell id = { .kind = kmkind_ref, … } ` - kind of cell 73 75 * `size_t refs` - number of active references 74 76 * `kmcell* src` - source, if any 75 77 * `char data[]` - content of cell 76 78 77 -### struct kmnode 79 +## struct kmnode 78 80 79 81 `kmnode` is the header struct for tree nodes. all tree nodes pointers can yield a `kmnode` structure by subtracting `sizeof (kmnode)` from the pointer. a utility function and macro are made available to automate this safely. 80 82 81 83 * `kmcell id = { .kind = kmkind_tree, … } ` - kind of cell 82 84 * `kmnode* parent` - parent node 83 85 * `kmnode* child` - first child node 84 86 * `kmnode* lastchild` - last child node 85 87 * `kmnode* prev` - previous sibling, NULL if first 86 88 * `kmnode* next` - next sibling, NULL if last 87 89 88 -### struct kmpool 90 +## struct kmpool 89 91 90 92 * `kmcell id = { .kind = kmkind_pool, … } ` - kind of cell 91 93 * `size_t cellsz` - size of individual pool cells 92 94 * `kmpoolcell* top` - pointer to most recently allocated pool cell 93 95 * `kmpoolcell* bottom` - pointer to most recently freed pool cell 94 96 * `kmpoolcell data[]` - content of cell 95 97 96 -#### struct kmpoolcell 98 +### struct kmpoolcell 97 99 98 100 * `kmpoolcell* last` - pointer to last element allocated before this one 99 101 * `char data[]` - pool data 100 102 101 -### enum kmshred 103 +## enum kmshred 102 104 103 105 `kmshred` is an enum used to indicate whether an object should be "shredded" (written over) in memory when it's deleted. this is a useful means to ensure that privileged information is not accidentally left in memory after use. if the shredding mechanism is not useful, compile libk with the flag `KFmem_noshred` to exclude its functions and fields. 104 106 105 107 * `kmshred_no = 0` - marks an object not to shred on free 106 108 * `kmshred_yes = 1` - marks an object to shred on free 107 109 108 -## naming convention 110 +# naming convention 109 111 110 112 kmem function names are based on the **method** of allocation and the **action** being performed. methods are listed in the section below. kmem defines a number of standardized actions, though not every method uses every action. the character listed in brackets is suffixed to the name of the method to produce a function name: for instance, `kmheapa` will allocate memory on the heap, while `kmrefd` will decrement the reference count of its argument. 111 113 112 114 * initialize [i] - initializes a memory store on the heap 113 115 * initialize fixed [if] - initialize a memory store on the stack or in a fixed-size global 114 - * allocate [a] - return a raw pointer to a new region of memory of the given size, ready to write, or NULL if not possible. contents of that region undefined. takes parameter (size_t sz). 115 - * allocate pointer object [ao] - like *allocate*, but returns a `kmptr` instead of a raw `void*`. 116 + * allocate [a] -a llocate a new region of memory of the given size, ready to write, and write a pointer to it into argument `where`. returns a value of `kmcond`; always check this to ensure allocation succeeded. contents of that region undefined. takes parameters `void** where, size_t sz`. 117 + * allocate pointer object [o] - like *allocate*, but fills in a `kmptr` instead of a raw `void*`. takes parameters `kmptr* where, size_t sz`. 116 118 * zero [z] - allocate a new region of memory and zero it before returning it for writing. 117 119 * zero pointer object [zo] - like *zero*, but returns a `kmptr` instead of a raw `void*`. 118 120 * free [f] - free a section of memory, either decrementing a reference count or returning it to whatever pool it came from. 119 121 * shred [s] - destroy whatever was in the segment of memory, then return it to the pool it came from. 120 122 * destroy [x] - tears down a memory store 121 123 * upref [u] - increments a reference counter 122 124