1 /* 2 * This work is licensed under the terms of the GNU LGPL, version 2. 3 * 4 * This is a simple allocator that provides contiguous physical addresses 5 * with page granularity. 6 */ 7 #include "libcflat.h" 8 #include <asm/page.h> 9 #include <asm/io.h> 10 #include <asm/spinlock.h> 11 12 static struct spinlock lock; 13 static void *freelist = 0; 14 15 void free_pages(void *mem, unsigned long size) 16 { 17 void *old_freelist; 18 void *end; 19 20 assert_msg((unsigned long) mem % PAGE_SIZE == 0, 21 "mem not page aligned: %p", mem); 22 23 assert_msg(size % PAGE_SIZE == 0, "size not page aligned: %#lx", size); 24 25 assert_msg(size == 0 || mem + size > mem, 26 "mem + size overflow: %p + %#lx", mem, size); 27 28 if (size == 0) { 29 freelist = NULL; 30 return; 31 } 32 33 spin_lock(&lock); 34 old_freelist = freelist; 35 freelist = mem; 36 end = mem + size; 37 while (mem + PAGE_SIZE != end) { 38 *(void **)mem = (mem + PAGE_SIZE); 39 mem += PAGE_SIZE; 40 } 41 42 *(void **)mem = old_freelist; 43 spin_unlock(&lock); 44 } 45 46 void *alloc_page() 47 { 48 void *p; 49 50 if (!freelist) 51 return 0; 52 53 spin_lock(&lock); 54 p = freelist; 55 freelist = *(void **)freelist; 56 spin_unlock(&lock); 57 58 return p; 59 } 60 61 /* 62 * Allocates (1 << order) physically contiguous and naturally aligned pages. 63 * Returns NULL if there's no memory left. 64 */ 65 void *alloc_pages(unsigned long order) 66 { 67 /* Generic list traversal. */ 68 void *prev; 69 void *curr = NULL; 70 void *next = freelist; 71 72 /* Looking for a run of length (1 << order). */ 73 unsigned long run = 0; 74 const unsigned long n = 1ul << order; 75 const unsigned long align_mask = (n << PAGE_SHIFT) - 1; 76 void *run_start = NULL; 77 void *run_prev = NULL; 78 unsigned long run_next_pa = 0; 79 unsigned long pa; 80 81 assert(order < sizeof(unsigned long) * 8); 82 83 spin_lock(&lock); 84 for (;;) { 85 prev = curr; 86 curr = next; 87 88 if (!curr) { 89 run_start = NULL; 90 break; 91 } 92 93 next = *((void **) curr); 94 pa = virt_to_phys(curr); 95 96 if (run == 0) { 97 if (!(pa & align_mask)) { 98 run_start = curr; 99 run_prev = prev; 100 run_next_pa = pa + PAGE_SIZE; 101 run = 1; 102 } 103 } else if (pa == run_next_pa) { 104 run_next_pa += PAGE_SIZE; 105 run += 1; 106 } else { 107 run = 0; 108 } 109 110 if (run == n) { 111 if (run_prev) 112 *((void **) run_prev) = next; 113 else 114 freelist = next; 115 break; 116 } 117 } 118 spin_unlock(&lock); 119 return run_start; 120 } 121 122 123 void free_page(void *page) 124 { 125 spin_lock(&lock); 126 *(void **)page = freelist; 127 freelist = page; 128 spin_unlock(&lock); 129 } 130 131