Kernel uses user_contexts, instead of TFs
[akaros.git] / kern / src / kmalloc.c
1 /* Copyright (c) 2009 The Regents of the University of California. 
2  * See the COPYRIGHT files at the top of this source tree for full 
3  * license information.
4  * 
5  * Kevin Klues <klueska@cs.berkeley.edu>    
6  */
7
8 #ifdef __SHARC__
9 #pragma nosharc
10 #define SINIT(x) x
11 #endif
12
13 #ifdef __DEPUTY__
14 #pragma nodeputy
15 #endif
16
17 #include <ros/common.h>
18 #include <error.h>
19 #include <pmap.h>
20 #include <kmalloc.h>
21 #include <stdio.h>
22 #include <slab.h>
23 #include <assert.h>
24
25 #define kmallocdebug(args...)  //printk(args)
26
27 char *RO BND(end, maxaddrpa_ptr + IVY_KERNBASE) boot_freemem;
28
29 //List of physical pages used by kmalloc
30 static spinlock_t pages_list_lock = SPINLOCK_INITIALIZER;
31 static page_list_t LCKD(&pages_list_lock)pages_list;
32
33 /*
34  * Allocate n bytes of physical memory aligned on an 
35  * align-byte boundary.  Align must be a power of two.
36  * Return kernel virtual address.  Returned memory is uninitialized.
37  *
38  * If we're out of memory, boot_alloc should panic.
39  * This function may ONLY be used during initialization,
40  * before the page_free_list has been set up.
41  */
42 void* boot_alloc(uint32_t n, uint32_t align)
43 {
44         extern char (SNT RO end)[];
45         void *v;
46
47         // Initialize boot_freemem if this is the first time.
48         // 'end' is a magic symbol automatically generated by the linker,
49         // which points to the end of the kernel's bss segment -
50         // i.e., the first virtual address that the linker
51         // did _not_ assign to any kernel code or global variables.
52         if (boot_freemem == 0) {
53                 boot_freemem = SINIT(TC(end));
54         }
55
56         //      Step 1: round boot_freemem up to be aligned properly
57         char RO*tmp = PTRROUNDUP(boot_freemem, align);
58         boot_freemem = SINIT(tmp);
59
60         //      Step 2: save current value of boot_freemem as allocated chunk
61         v = boot_freemem;
62         //  Step 2.5: check if we can alloc
63         if (PADDR(boot_freemem + n) > maxaddrpa)
64                 panic("Out of memory in boot alloc, you fool!\n");
65         //      Step 3: increase boot_freemem to record allocation
66         boot_freemem = SINIT(boot_freemem + n);
67         //      Step 4: return allocated chunk
68         return v;
69 }
70
71 void *boot_calloc(uint32_t n, size_t sz, uint32_t align)
72 {
73         void *v = boot_alloc(n * sz, align);    
74         memset(v, 0, n * sz);
75         return v;
76 }
77
78 struct kmem_cache *kmalloc_caches[NUM_KMALLOC_CACHES];
79 void kmalloc_init(void)
80 {
81         // i want to know if we ever make the tag bigger (should be below 16 bytes)
82         static_assert(sizeof(struct kmalloc_tag) <= KMALLOC_ALIGNMENT);
83         // build caches of common sizes
84         size_t ksize = KMALLOC_SMALLEST;
85         for (int i = 0; i < NUM_KMALLOC_CACHES; i++) {
86                 kmalloc_caches[i] = kmem_cache_create("kmalloc_cache", ksize,
87                                                       KMALLOC_ALIGNMENT, 0, 0, 0);
88                 ksize <<= 1;
89         }
90 }
91
92 void *kmalloc(size_t size, int flags) 
93 {
94         // reserve space for bookkeeping and preserve alignment
95         size_t ksize = size + KMALLOC_OFFSET;
96         void *buf;
97         int cache_id;
98         // determine cache to pull from
99         if (ksize <= KMALLOC_SMALLEST)
100                 cache_id = 0;
101         else
102                 cache_id = LOG2_UP(ksize) - LOG2_UP(KMALLOC_SMALLEST);
103         // if we don't have a cache to handle it, alloc cont pages
104         if (cache_id >= NUM_KMALLOC_CACHES) {
105                 size_t num_pgs = ROUNDUP(size + sizeof(struct kmalloc_tag), PGSIZE) /
106                                            PGSIZE;
107                 buf = get_cont_pages(LOG2_UP(num_pgs), flags);
108                 if (!buf)
109                         panic("Kmalloc failed!  Handle me!");
110                 // fill in the kmalloc tag
111                 struct kmalloc_tag *tag = buf;
112                 tag->flags = KMALLOC_TAG_PAGES;
113                 tag->num_pages = num_pgs;
114                 tag->canary = KMALLOC_CANARY;
115                 return buf + KMALLOC_OFFSET;
116         }
117         // else, alloc from the appropriate cache
118         buf = kmem_cache_alloc(kmalloc_caches[cache_id], flags);
119         if (!buf)
120                 panic("Kmalloc failed!  Handle me!");
121         // store a pointer to the buffers kmem_cache in it's bookkeeping space
122         struct kmalloc_tag *tag = buf;
123         tag->flags = KMALLOC_TAG_CACHE;
124         tag->my_cache = kmalloc_caches[cache_id];
125         tag->canary = KMALLOC_CANARY;
126         return buf + KMALLOC_OFFSET;
127 }
128
129 void *krealloc(void* buf, size_t size, int flags) {
130         struct kmalloc_tag *tag = (struct kmalloc_tag*)(buf - KMALLOC_OFFSET);
131         if (tag->my_cache->obj_size >= size)
132                 return buf;
133         kfree(buf);
134         return kmalloc(size, flags);
135 }
136
137 void kfree(void *addr)
138 {
139         if(addr == NULL)
140                 return;
141         struct kmalloc_tag *tag = (struct kmalloc_tag*)(addr - KMALLOC_OFFSET);
142         assert(tag->canary == KMALLOC_CANARY);
143         if (tag->flags & KMALLOC_TAG_CACHE)
144                 kmem_cache_free(tag->my_cache, addr - KMALLOC_OFFSET);
145         else if (tag->flags & KMALLOC_TAG_PAGES) {
146                 free_cont_pages(addr - KMALLOC_OFFSET, LOG2_UP(tag->num_pages));
147         } else 
148                 panic("[Italian Accent]: Che Cazzo! BO! Flag in kmalloc!!!");
149 }
150