123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424 |
- /*
- * Copyright (c) 2006-2019, RT-Thread Development Team
- *
- * SPDX-License-Identifier: Apache-2.0
- *
- * Change Logs:
- * Date Author Notes
- * 2019-11-01 Jesven The first version
- */
- #include <stdint.h>
- #include <rthw.h>
- #include <rtthread.h>
- #ifdef RT_USING_USERSPACE
- #include <page.h>
- #include <mmu.h>
- #define ARCH_PAGE_LIST_SIZE (ARCH_ADDRESS_WIDTH_BITS - ARCH_PAGE_SHIFT)
- #define DBG_TAG "PAGE"
- #define DBG_LVL DBG_WARNING
- #include <rtdbg.h>
- struct page
- {
- struct page *next; /* same level next */
- struct page *pre; /* same level pre */
- uint32_t size_bits; /* if is ARCH_ADDRESS_WIDTH_BITS, means not free */
- int ref_cnt; /* page group ref count */
- };
- static struct page* page_start;
- static void* page_addr;
- static size_t page_nr;
- static struct page *page_list[ARCH_PAGE_LIST_SIZE];
- RT_WEAK int rt_hw_clz(size_t n)
- {
- int bits = sizeof(size_t) * 8;
- n |= (n >> 1);
- n |= (n >> 2);
- n |= (n >> 4);
- n |= (n >> 8);
- n |= (n >> 16);
- #ifdef ARCH_CPU_64BIT
- n |= (n >> 32);
- n = (n & 0x5555555555555555UL) + ((n >> 1) & 0x5555555555555555UL);
- n = (n & 0x3333333333333333UL) + ((n >> 2) & 0x3333333333333333UL);
- n = (n & 0x0707070707070707UL) + ((n >> 4) & 0x0707070707070707UL);
- n = (n & 0x000f000f000f000fUL) + ((n >> 8) & 0x000f000f000f000fUL);
- n = (n & 0x0000001f0000001fUL) + ((n >> 16) & 0x0000001f0000001fUL);
- n = (n & 0x000000000000003fUL) + ((n >> 32) & 0x000000000000003fUL);
- #else
- n = (n & 0x55555555UL) + ((n >> 1) & 0x55555555UL);
- n = (n & 0x33333333UL) + ((n >> 2) & 0x33333333UL);
- n = (n & 0x07070707UL) + ((n >> 4) & 0x07070707UL);
- n = (n & 0x000f000fUL) + ((n >> 8) & 0x000f000fUL);
- n = (n & 0x0000001fUL) + ((n >> 16) & 0x0000001fUL);
- #endif
- return bits - n;
- }
- RT_WEAK int rt_hw_ctz(size_t n)
- {
- int ret = sizeof(size_t) * 8;
- if (n)
- {
- ret -= (rt_hw_clz(n ^ (n - 1)) + 1);
- }
- return ret;
- }
- size_t rt_page_bits(size_t size)
- {
- int bit = sizeof(size_t) * 8 - rt_hw_clz(size) - 1;
- if ((size ^ (1UL << bit)) != 0)
- {
- bit++;
- }
- bit -= ARCH_PAGE_SHIFT;
- if (bit < 0)
- {
- bit = 0;
- }
- return bit;
- }
- static struct page * addr_to_page(void *addr)
- {
- size_t off;
- if (addr < page_addr)
- {
- return 0;
- }
- off = (size_t)((char*)addr - (char*)page_addr);
- off >>= ARCH_PAGE_SHIFT;
- if (off >= page_nr)
- {
- return 0;
- }
- return &page_start[off];
- }
- static void* page_to_addr(struct page* p)
- {
- if (!p)
- {
- return 0;
- }
- return (void*)((char*)page_addr + ((p - page_start) << ARCH_PAGE_SHIFT));
- }
- static inline struct page *buddy_get(struct page *p, uint32_t size_bits)
- {
- size_t addr;
- addr = (size_t)page_to_addr(p);
- addr ^= (1UL << (size_bits + ARCH_PAGE_SHIFT));
- return addr_to_page((void*)addr);
- }
- static void page_remove(struct page *p, uint32_t size_bits)
- {
- if (p->pre)
- {
- p->pre->next = p->next;
- }
- else
- {
- page_list[size_bits] = p->next;
- }
- if (p->next)
- {
- p->next->pre = p->pre;
- }
- p->size_bits = ARCH_ADDRESS_WIDTH_BITS;
- }
- static void page_insert(struct page *p, uint32_t size_bits)
- {
- p->next = page_list[size_bits];
- if (p->next)
- {
- p->next->pre = p;
- }
- p->pre = 0;
- page_list[size_bits] = p;
- p->size_bits = size_bits;
- }
- static void _pages_ref_inc(struct page *p, uint32_t size_bits)
- {
- struct page *page_head;
- int idx;
- /* find page group head */
- idx = p - page_start;
- if (idx < 0 || idx >= page_nr)
- {
- return;
- }
- idx = idx & ~((1UL << size_bits) - 1);
- page_head = page_start + idx;
- page_head->ref_cnt++;
- }
- static int _pages_ref_get(struct page *p, uint32_t size_bits)
- {
- struct page *page_head;
- int idx;
- /* find page group head */
- idx = p - page_start;
- if (idx < 0 || idx >= page_nr)
- {
- return 0;
- }
- idx = idx & ~((1UL << size_bits) - 1);
- page_head = page_start + idx;
- return page_head->ref_cnt;
- }
- static int _pages_free(struct page *p, uint32_t size_bits)
- {
- uint32_t level = size_bits;
- struct page *buddy;
- RT_ASSERT(p->ref_cnt > 0);
- RT_ASSERT(p->size_bits == ARCH_ADDRESS_WIDTH_BITS);
- p->ref_cnt--;
- if (p->ref_cnt != 0)
- {
- return 0;
- }
- while (level < ARCH_PAGE_LIST_SIZE)
- {
- buddy = buddy_get(p, level);
- if (buddy && buddy->size_bits == level)
- {
- page_remove(buddy, level);
- p = (p < buddy) ? p : buddy;
- level++;
- }
- else
- {
- break;
- }
- }
- page_insert(p, level);
- return 1;
- }
- static struct page *_pages_alloc(uint32_t size_bits)
- {
- struct page *p;
- if (page_list[size_bits])
- {
- p = page_list[size_bits];
- page_remove(p, size_bits);
- }
- else
- {
- uint32_t level;
- for (level = size_bits + 1; level < ARCH_PAGE_LIST_SIZE; level++)
- {
- if (page_list[level])
- {
- break;
- }
- }
- if (level == ARCH_PAGE_LIST_SIZE)
- {
- return 0;
- }
- p = page_list[level];
- page_remove(p, level);
- while (level > size_bits)
- {
- page_insert(p, level - 1);
- p = buddy_get(p, level - 1);
- level--;
- }
- }
- p->size_bits = ARCH_ADDRESS_WIDTH_BITS;
- p->ref_cnt = 1;
- return p;
- }
- int rt_page_ref_get(void *addr, uint32_t size_bits)
- {
- struct page *p;
- rt_base_t level;
- int ref;
- p = addr_to_page(addr);
- level = rt_hw_interrupt_disable();
- ref = _pages_ref_get(p, size_bits);
- rt_hw_interrupt_enable(level);
- return ref;
- }
- void rt_page_ref_inc(void *addr, uint32_t size_bits)
- {
- struct page *p;
- rt_base_t level;
- p = addr_to_page(addr);
- level = rt_hw_interrupt_disable();
- _pages_ref_inc(p, size_bits);
- rt_hw_interrupt_enable(level);
- }
- void *rt_pages_alloc(uint32_t size_bits)
- {
- struct page *p;
- rt_base_t level;
- level = rt_hw_interrupt_disable();
- p = _pages_alloc(size_bits);
- rt_hw_interrupt_enable(level);
- return page_to_addr(p);
- }
- int rt_pages_free(void *addr, uint32_t size_bits)
- {
- struct page *p;
- int real_free = 0;
- p = addr_to_page(addr);
- if (p)
- {
- rt_base_t level;
- level = rt_hw_interrupt_disable();
- real_free = _pages_free(p, size_bits);
- rt_hw_interrupt_enable(level);
- }
- return real_free;
- }
- void list_page(void)
- {
- int i;
- size_t total = 0;
- rt_base_t level;
- level = rt_hw_interrupt_disable();
- for (i = 0; i < ARCH_PAGE_LIST_SIZE; i++)
- {
- struct page *p = page_list[i];
- rt_kprintf("level %d ", i);
- while (p)
- {
- total += (1UL << i);
- rt_kprintf("[0x%08p]", page_to_addr(p));
- p = p->next;
- }
- rt_kprintf("\n");
- }
- rt_hw_interrupt_enable(level);
- rt_kprintf("free pages is %08x\n", total);
- rt_kprintf("-------------------------------\n");
- }
- MSH_CMD_EXPORT(list_page, show page info);
- void rt_page_get_info(size_t *total_nr, size_t *free_nr)
- {
- int i;
- size_t total_free = 0;
- rt_base_t level;
- level = rt_hw_interrupt_disable();
- for (i = 0; i < ARCH_PAGE_LIST_SIZE; i++)
- {
- struct page *p = page_list[i];
- while (p)
- {
- total_free += (1UL << i);
- p = p->next;
- }
- }
- rt_hw_interrupt_enable(level);
- *total_nr = page_nr;
- *free_nr = total_free;
- }
- void rt_page_init(rt_region_t reg)
- {
- int i;
- LOG_D("split 0x%08x 0x%08x\n", reg.start, reg.end);
- reg.start += ARCH_PAGE_MASK;
- reg.start &= ~ARCH_PAGE_MASK;
- reg.end &= ~ARCH_PAGE_MASK;
- {
- int nr = ARCH_PAGE_SIZE / sizeof(struct page);
- int total = (reg.end - reg.start) >> ARCH_PAGE_SHIFT;
- int mnr = (total + nr) / (nr + 1);
- LOG_D("nr = 0x%08x\n", nr);
- LOG_D("total = 0x%08x\n", total);
- LOG_D("mnr = 0x%08x\n", mnr);
- RT_ASSERT(mnr < total);
- page_start = (struct page*)reg.start;
- reg.start += (mnr << ARCH_PAGE_SHIFT);
- page_addr = (void*)reg.start;
- page_nr = (reg.end - reg.start) >> ARCH_PAGE_SHIFT;
- }
- LOG_D("align 0x%08x 0x%08x\n", reg.start, reg.end);
- /* init free list */
- for (i = 0; i < ARCH_PAGE_LIST_SIZE; i++)
- {
- page_list[i] = 0;
- }
- /* add pages to free list */
- while (reg.start != reg.end)
- {
- struct page *p;
- int align_bits;
- int size_bits;
- size_bits = ARCH_ADDRESS_WIDTH_BITS - 1 - rt_hw_clz(reg.end - reg.start);
- align_bits = rt_hw_ctz(reg.start);
- if (align_bits < size_bits)
- {
- size_bits = align_bits;
- }
- p = addr_to_page((void*)reg.start);
- p->size_bits = ARCH_ADDRESS_WIDTH_BITS;
- p->ref_cnt = 1;
- _pages_free(p, size_bits - ARCH_PAGE_SHIFT);
- reg.start += (1UL << size_bits);
- }
- }
- #endif
|