4 * Open Hack'Ware BIOS: memory management
6 * Copyright (c) 2004-2005 Jocelyn Mayer
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License V2
10 * as published by the Free Software Foundation
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 /* functions prototypes are here */
23 /* NULL is declared here */
25 /* memcpy is defined here */
27 /* set_errno is defined here */
30 //#define DEBUG_MEMOPS
31 #if defined (DEBUG_MEMOPS)
32 #define MEMOPS_PRINTF(fmt, args...) do { dprintf(fmt , ##args); } while (0)
34 #define MEMOPS_PRINTF(fmt, args...) do { } while (0)
37 #define unused __attribute__ (( unused ))
39 /* XXX: TODO: put this elsewhere */
40 void *page_get (int nb_pages);
41 void page_put (void *addr, int nb_pages);
43 /* XXX: TOTO: put this elsewhere */
44 #if defined (__i386__)
45 #define NATURAL_ALIGN_BITS 2
48 #elif defined (__powerpc__) || defined (_ARCH_PPC)
49 #define NATURAL_ALIGN_BITS 2
52 #elif defined (__x86_64__)
53 #define NATURAL_ALIGN_BITS 3
56 #error "Unsupported architecture"
58 #define PAGE_SIZE (1 << PAGE_BITS)
59 #define PAGE(addr) ((void *)((unsigned long)(addr) & ~(PAGE_SIZE - 1)))
62 #define MIN_ELEM_BITS (NATURAL_ALIGN_BITS + 1)
63 #define MIN_ELEM_SIZE (1 << (MIN_ELEM_BITS))
64 #define MAX_ELEM_BITS (PAGE_BITS)
65 #define MAX_ELEM_SIZE (1 << (MAX_ELEM_BITS - 1))
66 #define POOL_MAX ((MAX_ELEM_BITS) - (MIN_ELEM_BITS))
69 typedef struct free_slot_t free_slot_t;
71 struct free_slot_t *next;
74 typedef struct page_descr_t page_descr_t;
76 struct page_descr_t *next;
77 struct page_descr_t *prev;
83 * This points to the first descriptor page where stand:
84 * - 1 descriptor for this page.
85 * - 1 decriptor pages list head.
86 * - POOL_MAX pool descriptors list heads.
87 * - CACHE_MAX page cache entries list heads.
89 static void *malloc_base;
91 /* Shared functions */
92 static inline page_descr_t *main_descr_get (void)
94 return (page_descr_t *)malloc_base + 1;
97 static inline page_descr_t *pool_head_get (int pool_idx)
99 return main_descr_get() + 1 + pool_idx;
102 static inline page_descr_t *cache_head_get (int cache_idx)
104 return pool_head_get(POOL_MAX) + 1 + cache_idx;
107 static void free_slots_init (void *page, int incr, int size)
109 free_slot_t *slot, *next;
112 slot < (free_slot_t *)((char *)page + size); slot = next) {
113 next = (void *)((char *)slot + incr);
116 slot = (void *)((char *)slot - incr);
120 static page_descr_t *descr_find_free (page_descr_t *head_descr,
121 page_descr_t *skip_descr)
123 page_descr_t *cur_descr, *best;
124 unsigned long max_used;
126 /* Try to always return the page with the less free slots to reduce
127 * memory fragmentation.
131 for (cur_descr = head_descr->next;
132 cur_descr != head_descr; cur_descr = cur_descr->next) {
133 if (cur_descr != skip_descr && cur_descr->addr != NULL &&
134 cur_descr->nb >= max_used) {
135 max_used = cur_descr->nb;
143 /* Page descriptors management */
144 static void page_descr_free (page_descr_t *head_descr)
146 head_descr->next->prev = head_descr->prev;
147 head_descr->prev->next = head_descr->next;
148 page_put(head_descr, 1);
151 static page_descr_t *page_descr_get (void)
153 page_descr_t *main_descr, *head_descr, *page_descr;
154 free_slot_t *first_free;
156 main_descr = main_descr_get();
157 head_descr = main_descr->addr;
158 first_free = head_descr->addr;
159 if (first_free == NULL) {
160 /* Find a page with free descriptors */
161 head_descr = descr_find_free(main_descr, NULL);
162 if (head_descr != NULL) {
163 /* Get the first free slot */
164 first_free = head_descr->addr;
166 /* Allocate a new page */
167 head_descr = page_get(1);
168 if (head_descr == NULL) {
169 MEMOPS_PRINTF("%s: cannot get new head descriptor\n",
173 /* Initialise free slots */
174 free_slots_init(head_descr, sizeof(page_descr_t), PAGE_SIZE);
175 /* Initialise page head descriptor */
176 head_descr->addr = head_descr + 1;
178 head_descr->next = main_descr;
179 head_descr->prev = main_descr->prev;
180 /* Update main descriptor */
181 main_descr->prev->next = head_descr;
182 main_descr->prev = head_descr;
184 first_free = head_descr->addr;
186 main_descr->addr = head_descr;
188 head_descr->addr = first_free->next;
189 if (head_descr->nb == 0)
192 page_descr = (page_descr_t *)first_free;
193 page_descr->prev = NULL;
194 page_descr->next = NULL;
195 page_descr->addr = NULL;
201 static void page_descr_put (page_descr_t *page_descr)
203 page_descr_t *main_descr, *head_descr, *next_descr, *free_descr;
204 free_slot_t *first_free, *next_free;
206 head_descr = PAGE(page_descr);
207 /* Mark this descriptor as free */
208 next_free = head_descr->addr;
209 first_free = (free_slot_t *)page_descr;
210 first_free->next = next_free;
211 /* Update page descriptor */
212 head_descr->addr = first_free;
214 main_descr = main_descr_get();
215 if (head_descr->nb == 0) {
216 /* Try to free this page */
217 if (main_descr->addr == head_descr ||
218 main_descr->addr == NULL ||
220 free_descr = descr_find_free(main_descr, head_descr);
222 free_descr = main_descr->addr;
223 if (free_descr != NULL) {
224 /* Update main descriptor */
225 page_descr_free(head_descr);
226 main_descr->addr = free_descr;
228 main_descr->addr = head_descr;
231 } else if (next_free == NULL) {
232 free_descr = head_descr;
233 for (head_descr = main_descr->next;
234 main_descr->nb > 0 && head_descr != main_descr;
235 head_descr = next_descr) {
236 next_descr = head_descr->next;
237 if (head_descr->nb == 0) {
238 if (main_descr->addr == head_descr)
239 main_descr->addr = NULL;
240 page_descr_free(head_descr);
244 if (main_descr->addr == NULL)
245 main_descr->addr = free_descr;
249 /* Page cache management */
250 static inline unsigned long cache_idx (void *addr)
252 return ((unsigned long)addr >> PAGE_BITS) & (CACHE_MAX - 1);
255 static inline unsigned long page_cache_pool_idx (page_descr_t *cache_descr)
257 return (cache_descr->nb & 0xF);
260 static inline page_descr_t *page_cache_page_descr (page_descr_t *cache_descr)
262 return (page_descr_t *)(cache_descr->nb & ~0xF);
265 static int page_cache_add_page (page_descr_t *page_descr, int pool_idx)
267 page_descr_t *main_descr, *cache_head, *cache_descr;
269 main_descr = main_descr_get();
270 cache_head = cache_head_get(cache_idx(page_descr->addr));
271 cache_descr = page_descr_get();
272 if (cache_descr == NULL) {
273 MEMOPS_PRINTF("%s: cannot get cache page\n", __func__);
276 cache_descr->nb = pool_idx | (unsigned long)page_descr;
277 cache_descr->prev = cache_head;
278 cache_descr->next = cache_head->next;
279 cache_descr->addr = page_descr->addr;
280 cache_head->next->prev = cache_descr;
281 cache_head->next = cache_descr;
286 static page_descr_t *page_cache_get_descr (void *addr)
288 page_descr_t *main_descr, *cache_head, *cache_descr;
290 main_descr = main_descr_get();
291 cache_head = cache_head_get(cache_idx(addr));
292 for (cache_descr = cache_head->next;
293 cache_descr != cache_head; cache_descr = cache_descr->next) {
294 if (cache_descr->addr == addr) {
298 MEMOPS_PRINTF("%s: cannot get cache page descr\n", __func__);
303 static void page_cache_remove_descr (page_descr_t *cache_descr)
305 cache_descr->next->prev = cache_descr->prev;
306 cache_descr->prev->next = cache_descr->next;
307 page_descr_put(cache_descr);
310 /* Allocation by pool (size <= PAGE_SIZE / 2) */
311 static void pool_descr_free (page_descr_t *cache_descr,
312 page_descr_t *pool_descr)
314 page_put(PAGE(pool_descr->addr), 1);
315 page_cache_remove_descr(cache_descr);
316 pool_descr->next->prev = pool_descr->prev;
317 pool_descr->prev->next = pool_descr->next;
318 page_descr_put(pool_descr);
321 static void *pool_malloc (int pool_idx)
323 page_descr_t *main_descr, *pool_head, *pool_descr;
324 free_slot_t *first_free, *next_free;
326 main_descr = main_descr_get();
327 pool_head = pool_head_get(pool_idx);
328 pool_descr = pool_head->addr;
329 if (pool_descr == NULL || pool_descr->addr == NULL) {
330 pool_descr = descr_find_free(pool_head, NULL);
331 if (pool_descr == NULL) {
332 pool_descr = page_descr_get();
333 if (pool_descr == NULL) {
334 MEMOPS_PRINTF("%s: cannot get pool descr\n", __func__);
337 pool_descr->addr = page_get(1);
338 if (pool_descr->addr == NULL) {
339 MEMOPS_PRINTF("%s: cannot allocate new page\n", __func__);
340 page_descr_put(pool_descr);
343 if (page_cache_add_page(pool_descr, pool_idx) < 0) {
344 MEMOPS_PRINTF("%s: cannot add new page to cache\n", __func__);
345 page_put(pool_descr->addr, 1);
346 page_descr_put(pool_descr);
349 free_slots_init(pool_descr->addr,
350 1 << (MIN_ELEM_BITS + pool_idx), PAGE_SIZE);
352 pool_descr->prev = pool_head->prev;
353 pool_descr->next = pool_head;
354 pool_head->prev->next = pool_descr;
355 pool_head->prev = pool_descr;
358 pool_head->addr = pool_descr;
360 first_free = pool_descr->addr;
361 next_free = first_free->next;
362 // memset(first_free, 0, 1 << (MIN_ELEM_BITS + pool_idx));
363 pool_descr->addr = next_free;
364 if (pool_descr->nb == 0)
371 unused static void pool_free (page_descr_t *cache_descr, void *area)
373 page_descr_t *pool_head, *pool_descr, *pool_next, *free_pool;
374 free_slot_t *first_free, *next_free;
375 unsigned long size, pool_idx;
377 pool_descr = page_cache_page_descr(cache_descr);
379 next_free = pool_descr->addr;
380 pool_idx = page_cache_pool_idx(cache_descr);
381 size = 1 << (MIN_ELEM_BITS + pool_idx);
382 first_free->next = next_free;
383 pool_descr->addr = first_free;
385 pool_head = pool_head_get(pool_idx);
386 if (pool_descr->nb == 0) {
387 if (pool_head->addr == pool_descr ||
388 pool_head->addr == NULL ||
390 free_pool = descr_find_free(pool_head, pool_descr);
392 free_pool = pool_head->addr;
393 if (free_pool != NULL) {
394 /* Free page & descriptor */
395 pool_descr_free(cache_descr, pool_descr);
396 pool_head->addr = free_pool;
398 pool_head->addr = pool_descr;
401 } else if (next_free == NULL) {
402 free_pool = pool_descr;
403 for (pool_descr = pool_head->next;
404 pool_head->nb > 0 && pool_descr != pool_head;
405 pool_descr = pool_next) {
406 pool_next = pool_descr->next;
407 if (pool_descr->nb == 0) {
408 if (pool_head->addr == pool_descr)
409 pool_head->addr = NULL;
410 cache_descr = page_cache_get_descr(PAGE(pool_descr->addr));
411 if (cache_descr != NULL) {
412 pool_descr_free(cache_descr, pool_descr);
415 /* Incoherency: what to do ? */
419 if (pool_head->addr == NULL)
420 pool_head->addr = free_pool;
424 /* Big area management (size > PAGE_SIZE / 2) */
425 static void *big_malloc (int nb_pages)
427 page_descr_t *main_descr, *pool_head, *pool_descr;
429 main_descr = main_descr_get();
430 pool_head = pool_head_get(POOL_MAX);
431 pool_descr = page_descr_get();
432 if (pool_descr == NULL) {
433 MEMOPS_PRINTF("%s: cannot get pool descr\n", __func__);
436 pool_descr->addr = page_get(nb_pages);
437 if (pool_descr->addr == NULL) {
438 page_descr_put(pool_descr);
439 MEMOPS_PRINTF("%s: cannot get page\n", __func__);
442 if (page_cache_add_page(pool_descr, POOL_MAX) < 0) {
443 page_put(pool_descr->addr, nb_pages);
444 page_descr_put(pool_descr);
445 MEMOPS_PRINTF("%s: cannot get add page to cache\n", __func__);
448 pool_descr->prev = pool_head->prev;
449 pool_descr->next = pool_head;
450 pool_descr->nb = nb_pages;
451 pool_head->prev->next = pool_descr;
452 pool_head->prev = pool_descr;
454 return pool_descr->addr;
457 static void big_free (page_descr_t *cache_descr)
459 page_descr_t *pool_descr;
461 pool_descr = page_cache_page_descr(cache_descr);
462 if (pool_descr->addr != NULL && pool_descr->nb != 0) {
463 page_put(pool_descr->addr, pool_descr->nb);
464 pool_descr->next->prev = pool_descr->prev;
465 pool_descr->prev->next = pool_descr->next;
466 page_descr_put(pool_descr);
467 page_cache_remove_descr(cache_descr);
469 MEMOPS_PRINTF("%s: ERROR %p %d\n",
470 __func__, pool_descr->addr, (int)pool_descr->nb);
474 unused static void *big_realloc (page_descr_t *cache_descr, int new_size)
477 page_descr_t *pool_descr;
478 unsigned long new_nb;
480 pool_descr = page_cache_page_descr(cache_descr);
481 new_nb = (new_size + PAGE_SIZE - 1) / PAGE_SIZE;
482 if (new_nb == pool_descr->nb) {
483 new_area = cache_descr->addr;
485 new_area = big_malloc(new_size);
486 memcpy(new_area, cache_descr->addr, pool_descr->nb * PAGE_SIZE);
487 big_free(cache_descr);
493 /* Global entry points */
494 int page_descrs_init (void)
496 page_descr_t *main_descr, *page_descr, *pool_head, *cache_head;
499 /* Allocate first descriptor page */
500 malloc_base = page_get(1);
501 if (malloc_base == NULL) {
503 MEMOPS_PRINTF("%s: cannot get main descriptor\n", __func__);
506 /* Init free slots in this page */
507 free_slots_init(malloc_base, sizeof(page_descr_t), PAGE_SIZE);
508 /* Init main descriptor */
509 page_descr = malloc_base;
510 main_descr = main_descr_get();
511 main_descr->addr = page_descr;
513 main_descr->next = page_descr;
514 main_descr->prev = page_descr;
516 page_descr->addr = page_descr + 2;
517 page_descr->next = main_descr;
518 page_descr->prev = main_descr;
519 /* Init pool lists heads */
520 for (i = 0; i <= POOL_MAX; i++) {
521 pool_head = page_descr_get();
522 if (pool_head == NULL) {
523 page_put(malloc_base, 1);
525 MEMOPS_PRINTF("%s: cannot get pool descriptor %d\n", __func__, i);
528 pool_head->prev = pool_head;
529 pool_head->next = pool_head;
530 pool_head->addr = NULL;
532 /* Init page caches lists heads */
533 for (i = 0; i < CACHE_MAX; i++) {
534 cache_head = page_descr_get();
535 if (cache_head == NULL) {
536 page_put(malloc_base, 1);
538 MEMOPS_PRINTF("%s: cannot get page cache descriptor %d\n",
542 cache_head->prev = cache_head;
543 cache_head->next = cache_head;
549 static inline int get_pool_idx (size_t size)
554 for (size /= MIN_ELEM_SIZE; size != 0; size = size / 2)
561 void *malloc (size_t size)
566 if (malloc_base == NULL || size == 0) {
568 } else if (size >= MAX_ELEM_SIZE) {
569 ret = big_malloc((size + PAGE_SIZE - 1) / PAGE_SIZE);
571 if (size <= MIN_ELEM_SIZE)
574 pool_idx = get_pool_idx(size);
576 ret = pool_malloc(pool_idx);
579 memset(ret, 0, size);
582 printf("%s(%d) => %p\n", __func__, size, ret);
590 void free (void *area)
592 page_descr_t *cache_descr;
595 if (malloc_base == NULL || area == NULL)
597 cache_descr = page_cache_get_descr(PAGE(area));
598 if (cache_descr != NULL) {
599 pool_idx = page_cache_pool_idx(cache_descr);
600 if (pool_idx == POOL_MAX) {
601 big_free(cache_descr);
603 pool_free(cache_descr, area);
606 /* Given area is not valid */
607 MEMOPS_PRINTF("ERROR: area to free not found: %p\n", area);
613 void *realloc (void *area, size_t new_size)
616 page_descr_t *pool_descr, *cache_descr;
618 int pool_idx, new_pool_idx;
620 if (malloc_base == NULL || new_size == 0) {
625 return malloc(new_size);
626 cache_descr = page_cache_get_descr(PAGE(area));
627 if (cache_descr == NULL) {
628 /* Given area is not valid */
631 pool_idx = page_cache_pool_idx(cache_descr);
632 if (new_size >= MAX_ELEM_SIZE) {
633 new_pool_idx = POOL_MAX;
634 if (pool_idx == POOL_MAX)
635 return big_realloc(cache_descr, new_size);
637 if (new_size <= MIN_ELEM_SIZE)
640 new_pool_idx = get_pool_idx(size);
641 if (pool_idx == new_pool_idx)
644 /* Common case: alloc, copy & free */
645 if (new_pool_idx == POOL_MAX)
646 new_area = big_malloc((new_size + PAGE_SIZE - 1) / PAGE_SIZE);
648 new_area = pool_malloc(new_pool_idx);
649 if (new_area == NULL)
651 if (pool_idx == POOL_MAX) {
652 pool_descr = page_cache_page_descr(cache_descr);
653 size = pool_descr->nb * PAGE_SIZE;
655 size = MIN_ELEM_SIZE << pool_idx;
657 memcpy(new_area, area, size);
658 if (pool_idx == POOL_MAX)
659 big_free(cache_descr);
661 pool_free(cache_descr, area);
667 void memory_dump (void)
669 #if defined (DEBUG_MEMOPS)
670 page_descr_t *main_descr, *page_descr;
671 page_descr_t *pool_head, *pool_descr, *cache_head, *cache_descr;
674 main_descr = main_descr_get();
675 /* Dump descriptor pages */
676 printf("Descriptor pages dump: %p max=%d %d pages with no alloc descrs\n",
677 main_descr, (int)(PAGE_SIZE / sizeof(page_descr_t)), (int)main_descr->nb);
679 for (page_descr = main_descr->next;
680 page_descr != main_descr; page_descr = page_descr->next) {
681 printf("Descr %d : %p %p used: %d\n",
682 n, page_descr, page_descr->addr, (int)page_descr->nb);
685 /* Dump pool areas pages */
686 for (i = 0; i < POOL_MAX; i++) {
688 pool_head = pool_head_get(i);
689 printf("Pool %d %p\n", i, pool_head);
690 for (pool_descr = pool_head->next;
691 pool_descr != pool_head; pool_descr = pool_descr->next) {
692 printf("Pool %d descr %d : %p %p used: %d size %d free: %p %p\n",
693 i, n, pool_descr, PAGE(pool_descr->addr), (int)pool_descr->nb,
694 1 << (MIN_ELEM_BITS + i), pool_descr->addr,
695 ((free_slot_t *)pool_descr->addr)->next);
698 printf(" => %d pages allocated\n", n);
701 /* Dump big area pages */
702 printf("Pool %d\n", POOL_MAX);
704 pool_head = pool_head_get(POOL_MAX);
705 for (pool_descr = pool_head->next;
706 pool_descr != pool_head; pool_descr = pool_descr->next) {
707 printf("Pool %d descr %d : %p nb pages: %d\n",
708 POOL_MAX, n, pool_descr->addr, (int)pool_descr->nb);
711 printf(" => %d pages allocated\n", n);
712 /* Dump page cache */
713 for (i = 0; i < CACHE_MAX; i++) {
714 printf("Page cache 0x____%x___\n", i);
716 cache_head = cache_head_get(i);
717 for (cache_descr = cache_head->next;
718 cache_descr != cache_head; cache_descr = cache_descr->next) {
719 pool_descr = page_cache_page_descr(cache_descr);
720 printf("Cache %d descr %d : %p pool: %d descr: %p %p %d\n",
721 i, n, cache_descr->addr,
722 (int)page_cache_pool_idx(cache_descr),
723 pool_descr, pool_descr->addr, (int)pool_descr->nb);
726 printf(" => %d pages allocated\n", n);