Merge with master
[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 #include <ros/common.h>
14 #include <ros/error.h>
15 #include <pmap.h>
16 #include <kmalloc.h>
17 #include <stdio.h>
18
19 #define kmallocdebug(args...)  //printk(args)
20
21 char *RO BND(end, maxaddrpa_ptr + IVY_KERNBASE) boot_freemem;
22
23 //List of physical pages used by kmalloc
24 static spinlock_t pages_list_lock = 0;
25 static page_list_t LCKD(&pages_list_lock)pages_list;
26
27 /*
28  * Allocate n bytes of physical memory aligned on an 
29  * align-byte boundary.  Align must be a power of two.
30  * Return kernel virtual address.  Returned memory is uninitialized.
31  *
32  * If we're out of memory, boot_alloc should panic.
33  * This function may ONLY be used during initialization,
34  * before the page_free_list has been set up.
35  */
36 void* boot_alloc(uint32_t n, uint32_t align)
37 {
38         extern char (SNT RO end)[];
39         void *v;
40
41         // Initialize boot_freemem if this is the first time.
42         // 'end' is a magic symbol automatically generated by the linker,
43         // which points to the end of the kernel's bss segment -
44         // i.e., the first virtual address that the linker
45         // did _not_ assign to any kernel code or global variables.
46         if (boot_freemem == 0) {
47                 boot_freemem = SINIT(TC(end));
48         }
49
50         //      Step 1: round boot_freemem up to be aligned properly
51         char RO*tmp = PTRROUNDUP(boot_freemem, align);
52         boot_freemem = SINIT(tmp);
53
54         //      Step 2: save current value of boot_freemem as allocated chunk
55         v = boot_freemem;
56         //  Step 2.5: check if we can alloc
57         if (PADDR(boot_freemem + n) > maxaddrpa)
58                 panic("Out of memory in boot alloc, you fool!\n");
59         //      Step 3: increase boot_freemem to record allocation
60         boot_freemem = SINIT(boot_freemem + n);
61         //      Step 4: return allocated chunk
62         return v;
63 }
64
65 void* boot_calloc(uint32_t _n, size_t sz, uint32_t align)
66 {
67         extern char (SNT RO end)[];
68         uint32_t n = _n *sz;
69         void *v;
70
71         // Initialize boot_freemem if this is the first time.
72         // 'end' is a magic symbol automatically generated by the linker,
73         // which points to the end of the kernel's bss segment -
74         // i.e., the first virtual address that the linker
75         // did _not_ assign to any kernel code or global variables.
76         if (boot_freemem == 0)
77                 boot_freemem = SINIT(TC(end));
78
79         //      Step 1: round boot_freemem up to be aligned properly
80         char RO*tmp = PTRROUNDUP(boot_freemem, align);
81         boot_freemem = SINIT(tmp);
82
83         //      Step 2: save current value of boot_freemem as allocated chunk
84         v = boot_freemem;
85         //  Step 2.5: check if we can alloc
86         if (PADDR(boot_freemem + n) > maxaddrpa)
87                 panic("Out of memory in boot alloc, you fool!\n");
88         //      Step 3: increase boot_freemem to record allocation
89         boot_freemem = SINIT(boot_freemem + n);
90         //  Step 4: zero allocated chunk
91         memset(v,0,n);
92         //      Step 5: return allocated chunk
93         return v;
94 }
95
96 void kmalloc_init() 
97 {
98         LIST_INIT(&pages_list);
99 }
100
101 void* kmalloc(size_t size, int flags) 
102 {
103         if (size == 0)
104                 return NULL;
105
106         int npages = ROUNDUP(size, PGSIZE) / PGSIZE;
107         
108         // Find 'npages' free consecutive pages
109         int first = -1;
110         kmallocdebug("naddrpages: %u\n", naddrpages);
111         kmallocdebug("npages: %u\n", npages);
112         for(int i=(naddrpages-1); i>=(npages-1); i--) {
113                 int j;
114                 for(j=i; j>=(i-(npages-1)); j--) {
115                         if( !page_is_free(j) )
116                                 break;
117                 }
118                 if( j == (i-(npages-1)-1)) {
119                         first = j+1;
120                         break;
121                 }
122         }
123         //If we couldn't find them, return NULL
124         if( first == -1 )
125                 return NULL;
126                 
127         //Otherwise go ahead and allocate them to ourselves now
128         for(int i=0; i<npages; i++) {
129                 page_t* page;
130
131                 page_alloc_specific(&page, first+i);
132                 // Kevin doesn't like this next line 
133                 page_incref(page); 
134                 page->num_cons_links = npages-i;
135
136                 spin_lock_irqsave(&pages_list_lock);
137                 LIST_INSERT_HEAD(&pages_list, page, page_link);
138                 spin_unlock_irqsave(&pages_list_lock);
139
140                 kmallocdebug("mallocing page: %u\n", first+i);
141                 kmallocdebug("at addr: %p\n", ppn2kva(first+i));
142         }
143         //And return the address of the first one
144         return ppn2kva(first);
145 }
146
147 void kfree(void *addr)
148 {
149         kmallocdebug("incoming address: %p\n", addr);
150         page_t* page = kva2page(addr);
151         int num_links = page->num_cons_links;
152         kmallocdebug("getting page: %u\n", page2ppn(page));
153         for(int i=0; i<num_links; i++) {
154                 page_t* p = ppn2page((page2ppn(page) + i));
155                 LIST_REMOVE(p, page_link);
156                 page_free(p);
157                 kmallocdebug("freeing page: %d\n", page2ppn(p));
158         }
159 }
160