402 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			402 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
|  | /*
 | ||
|  |  * Copyright (c) 2006-2023, RT-Thread Development Team | ||
|  |  * | ||
|  |  * SPDX-License-Identifier: Apache-2.0 | ||
|  |  * | ||
|  |  * Change Logs: | ||
|  |  * Date           Author       Notes | ||
|  |  * 2023-09-07     zmshahaha    the first version | ||
|  |  */ | ||
|  | 
 | ||
|  | #include "mm_memblock.h"
 | ||
|  | #include "mm_page.h"
 | ||
|  | #include "mm_aspace.h"
 | ||
|  | #include <mmu.h>
 | ||
|  | 
 | ||
|  | #define DBG_TAG "mm.memblock"
 | ||
|  | #define DBG_LVL DBG_INFO
 | ||
|  | #include <rtdbg.h>
 | ||
|  | 
 | ||
|  | #define PHYS_ADDR_MAX (~((rt_size_t)0))
 | ||
|  | 
 | ||
|  | #define MAX(a, b) ((a) > (b) ? (a) : (b))
 | ||
|  | #define MIN(a, b) ((a) < (b) ? (a) : (b))
 | ||
|  | 
 | ||
|  | #ifndef RT_INIT_MEMORY_REGIONS
 | ||
|  | #define RT_INIT_MEMORY_REGIONS 128
 | ||
|  | #endif
 | ||
|  | 
 | ||
|  | static struct rt_mmblk_reg _regions[RT_INIT_MEMORY_REGIONS]; | ||
|  | static int _hint_idx; | ||
|  | 
 | ||
|  | static struct rt_memblock mmblk_memory; | ||
|  | static struct rt_memblock mmblk_reserved; | ||
|  | 
 | ||
|  | struct rt_memblock *rt_memblock_get_memory(void) | ||
|  | { | ||
|  |     return &mmblk_memory; | ||
|  | } | ||
|  | 
 | ||
|  | struct rt_memblock *rt_memblock_get_reserved(void) | ||
|  | { | ||
|  |     return &mmblk_reserved; | ||
|  | } | ||
|  | 
 | ||
|  | rt_inline struct rt_mmblk_reg *_next_region(struct rt_mmblk_reg *prev) | ||
|  | { | ||
|  |     if (prev && prev->node.next) | ||
|  |     { | ||
|  |         return rt_slist_entry(prev->node.next, struct rt_mmblk_reg, node); | ||
|  |     } | ||
|  |     else | ||
|  |     { | ||
|  |         return RT_NULL; | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | static struct rt_mmblk_reg *_alloc_memreg(struct rt_mmblk_reg *prev) | ||
|  | { | ||
|  |     for (int i =_hint_idx; i < RT_INIT_MEMORY_REGIONS; i++) | ||
|  |     { | ||
|  |         if (_regions[i].alloc == RT_FALSE) | ||
|  |         { | ||
|  |             rt_slist_insert(&(prev->node), &(_regions[i].node)); | ||
|  |             _regions[i].alloc = RT_TRUE; | ||
|  |             _hint_idx = i + 1; | ||
|  |             return &_regions[i]; | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     for (int i = 0; i < _hint_idx; i++) | ||
|  |     { | ||
|  |         if (_regions[i].alloc == RT_FALSE) | ||
|  |         { | ||
|  |             rt_slist_insert(&(prev->node), &(_regions[i].node)); | ||
|  |             _regions[i].alloc = RT_TRUE; | ||
|  |             _hint_idx = i + 1; | ||
|  |             return &_regions[i]; | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     return RT_NULL; | ||
|  | } | ||
|  | 
 | ||
|  | static void _free_memreg(struct rt_mmblk_reg *prev) | ||
|  | { | ||
|  |     struct rt_mmblk_reg *next = _next_region(prev); | ||
|  | 
 | ||
|  |     next->alloc = RT_FALSE; | ||
|  |     rt_slist_remove(&(prev->node), prev->node.next); | ||
|  | } | ||
|  | 
 | ||
|  | static rt_err_t _reg_insert_after(struct rt_mmblk_reg *prev, rt_region_t *reg, | ||
|  |                                 mmblk_flag_t flags) | ||
|  | { | ||
|  |     struct rt_mmblk_reg *new_reg = _alloc_memreg(prev); | ||
|  | 
 | ||
|  |     if (!new_reg) | ||
|  |     { | ||
|  |         LOG_E("No enough space"); | ||
|  |         return -RT_ENOMEM; | ||
|  |     } | ||
|  | 
 | ||
|  |     rt_memcpy(&(new_reg->memreg), reg, sizeof(*reg)); | ||
|  |     new_reg->flags = flags; | ||
|  |     return RT_EOK; | ||
|  | } | ||
|  | 
 | ||
|  | rt_inline void _reg_remove_after(struct rt_mmblk_reg *prev) | ||
|  | { | ||
|  |     _free_memreg(prev); | ||
|  | } | ||
|  | 
 | ||
|  | /* adding overlapped regions is banned */ | ||
|  | static rt_err_t _memblock_add_range(struct rt_memblock *memblock, | ||
|  |                     char *name, rt_size_t start, rt_size_t end, mm_flag_t flag) | ||
|  | { | ||
|  |     struct rt_mmblk_reg *reg, *reg_next; | ||
|  |     rt_slist_t sentinel; | ||
|  |     rt_region_t new_region; | ||
|  | 
 | ||
|  |     if (start >= end) | ||
|  |         return -RT_EINVAL; | ||
|  | 
 | ||
|  |     sentinel.next = &(memblock->reg_list); | ||
|  | 
 | ||
|  |     /* find suitable place */ | ||
|  |     rt_slist_for_each_entry(reg, &sentinel, node) | ||
|  |     { | ||
|  |         reg_next = _next_region(reg); | ||
|  | 
 | ||
|  |         if (reg_next == RT_NULL) | ||
|  |             break; | ||
|  | 
 | ||
|  |         rt_size_t rstart = reg_next->memreg.start; | ||
|  |         rt_size_t rend = reg_next->memreg.end; | ||
|  | 
 | ||
|  |         /* not overlap */ | ||
|  |         if (rstart >= end) | ||
|  |             break; | ||
|  |         if (rend <= start) | ||
|  |             continue; | ||
|  | 
 | ||
|  |         /* overlap */ | ||
|  |         LOG_E("region to add %s: [%p-%p) overlap with existing region %s: [%p-%p)",\ | ||
|  |                 name, start, end, reg_next->memreg.name, rstart, rend); | ||
|  |         return -RT_EINVAL; | ||
|  |     } | ||
|  | 
 | ||
|  |     /* insert the region */ | ||
|  |     new_region.name = name; | ||
|  |     new_region.start = start; | ||
|  |     new_region.end = end; | ||
|  |     return _reg_insert_after(reg, &new_region, flag); | ||
|  | } | ||
|  | 
 | ||
|  | rt_err_t rt_memblock_add_memory(char *name, rt_size_t start, rt_size_t end, mmblk_flag_t flags) | ||
|  | { | ||
|  |     LOG_D("add physical address range [%p-%p) with flag 0x%x" \ | ||
|  |             " to overall memory regions\n", base, base + size, flag); | ||
|  | 
 | ||
|  |     return _memblock_add_range(&mmblk_memory, name, start, end, flags); | ||
|  | } | ||
|  | 
 | ||
|  | rt_err_t rt_memblock_reserve_memory(char *name, rt_size_t start, rt_size_t end, mmblk_flag_t flags) | ||
|  | { | ||
|  |     LOG_D("add physical address range [%p-%p) to reserved memory regions\n",\ | ||
|  |                                         base, base + size); | ||
|  | 
 | ||
|  |     return _memblock_add_range(&mmblk_reserved, name, start, end, flags); | ||
|  | } | ||
|  | 
 | ||
|  | /* [*start_reg, *end_reg) is the isolated range */ | ||
|  | static rt_err_t _memblock_separate_range(struct rt_memblock *memblock, | ||
|  |                     rt_size_t start, rt_size_t end, | ||
|  |                     struct rt_mmblk_reg **start_reg, struct rt_mmblk_reg **end_reg) | ||
|  | { | ||
|  |     struct rt_mmblk_reg *reg = RT_NULL; | ||
|  |     rt_region_t new_region; | ||
|  |     rt_err_t err = RT_EOK; | ||
|  | 
 | ||
|  |     *start_reg = *end_reg = RT_NULL; | ||
|  | 
 | ||
|  |     rt_slist_for_each_entry(reg, &(memblock->reg_list), node) | ||
|  |     { | ||
|  |         rt_size_t rstart = reg->memreg.start; | ||
|  |         rt_size_t rend = reg->memreg.end; | ||
|  | 
 | ||
|  |         if (rstart >= end) | ||
|  |             break; | ||
|  |         if (rend <= start) | ||
|  |             continue; | ||
|  | 
 | ||
|  |         /* the beginning of the range separates its respective region */ | ||
|  |         if (rstart < start) | ||
|  |         { | ||
|  |             new_region.start = start; | ||
|  |             new_region.end = rend; | ||
|  |             new_region.name = reg->memreg.name; | ||
|  |             err = _reg_insert_after(reg, &new_region, reg->flags); | ||
|  | 
 | ||
|  |             if (err != RT_EOK) | ||
|  |                 return err; | ||
|  | 
 | ||
|  |             reg->memreg.end = start; | ||
|  | 
 | ||
|  |             *start_reg = _next_region(reg); | ||
|  |             *end_reg = _next_region(*start_reg); | ||
|  |         } | ||
|  |         /* the endpoint of the range separates its respective region */ | ||
|  |         else if (rend > end) | ||
|  |         { | ||
|  |             new_region.start = end; | ||
|  |             new_region.end = rend; | ||
|  |             new_region.name = reg->memreg.name; | ||
|  |             err = _reg_insert_after(reg, &new_region, reg->flags); | ||
|  | 
 | ||
|  |             if (err != RT_EOK) | ||
|  |                 return err; | ||
|  | 
 | ||
|  |             reg->memreg.end = end; | ||
|  | 
 | ||
|  |             *end_reg = _next_region(reg); | ||
|  |             break; | ||
|  |         } | ||
|  |         /* reg->next is fully contained in range */ | ||
|  |         else | ||
|  |         { | ||
|  |             if (!*end_reg) | ||
|  |                 *start_reg = reg; | ||
|  |             *end_reg = _next_region(reg); | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     return err; | ||
|  | } | ||
|  | 
 | ||
|  | static void _memblock_set_flag(struct rt_mmblk_reg *start_reg, struct rt_mmblk_reg *end_reg, \ | ||
|  |                         mmblk_flag_t flags) | ||
|  | { | ||
|  |     if (start_reg == RT_NULL) | ||
|  |         return; | ||
|  | 
 | ||
|  |     for (struct rt_mmblk_reg *iter = start_reg; iter != end_reg; iter = _next_region(iter)) { | ||
|  |         iter->flags |= flags; | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | static void _next_free_region(struct rt_mmblk_reg **m, struct rt_mmblk_reg **r, mmblk_flag_t flags, | ||
|  |                       rt_size_t *out_start, rt_size_t *out_end) | ||
|  | { | ||
|  |     /* memory related data */ | ||
|  |     rt_size_t m_start = 0; | ||
|  |     rt_size_t m_end = 0; | ||
|  | 
 | ||
|  |     /* reserved related data */ | ||
|  |     rt_size_t r_start = 0; | ||
|  |     rt_size_t r_end = 0; | ||
|  |     struct rt_mmblk_reg *r_sentinel = rt_slist_entry(&(mmblk_reserved.reg_list), struct rt_mmblk_reg, node); | ||
|  | 
 | ||
|  |     for (; *m != RT_NULL; *m = _next_region(*m)) | ||
|  |     { | ||
|  |         if ((*m)->flags != flags) | ||
|  |             continue; | ||
|  | 
 | ||
|  |         m_start = (*m)->memreg.start; | ||
|  |         m_end = (*m)->memreg.end; | ||
|  | 
 | ||
|  |         for (; *r != RT_NULL; *r = _next_region(*r)) | ||
|  |         { | ||
|  |             /*
 | ||
|  |              * r started with _resreg_guard | ||
|  |              * Find the complement of reserved memblock. | ||
|  |              * For example, if reserved memblock is following: | ||
|  |              * | ||
|  |              *  0:[8-16), 1:[32-48), 2:[128-130) | ||
|  |              * | ||
|  |              * The upper 32bit indexes the following regions. | ||
|  |              * | ||
|  |              *  0:[0-8), 1:[16-32), 2:[48-128), 3:[130-MAX) | ||
|  |              * | ||
|  |              * So we can find intersecting region other than excluding. | ||
|  |              */ | ||
|  |             r_start = (*r == r_sentinel) ? 0 : (*r)->memreg.end; | ||
|  |             r_end = (_next_region(*r)) ? _next_region(*r)->memreg.start : PHYS_ADDR_MAX; | ||
|  | 
 | ||
|  |             /* two reserved region are adjacent */ | ||
|  |             if (r_start == r_end) | ||
|  |                 continue; | ||
|  | 
 | ||
|  |             if (r_start >= m_end) | ||
|  |                 break; | ||
|  | 
 | ||
|  |             if (m_start < r_end) | ||
|  |             { | ||
|  |                 *out_start = MAX(m_start, r_start); | ||
|  |                 *out_end = MIN(m_end, r_end); | ||
|  | 
 | ||
|  |                 if (m_end <= r_end) | ||
|  |                     *m = _next_region(*m); | ||
|  |                 else | ||
|  |                     *r = _next_region(*r); | ||
|  |                 return; | ||
|  |             } | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     /* all regions found */ | ||
|  |     *m = rt_slist_entry(&(mmblk_memory.reg_list), struct rt_mmblk_reg, node); | ||
|  | } | ||
|  | 
 | ||
|  | /* for each region in memory with flags and not reserved */ | ||
|  | #define for_each_free_region(m, r, flags, p_start, p_end)                               \
 | ||
|  |     m = rt_slist_entry(&(mmblk_memory.reg_list.next), struct rt_mmblk_reg, node);       \ | ||
|  |     r = rt_slist_entry(&(mmblk_reserved.reg_list), struct rt_mmblk_reg, node);          \ | ||
|  |     for (_next_free_region(&m, &r, flags, p_start, p_end);                              \ | ||
|  |          m != rt_slist_entry(&(mmblk_memory.reg_list), struct rt_mmblk_reg, node);      \ | ||
|  |          _next_free_region(&m, &r, flags, p_start, p_end)) | ||
|  | 
 | ||
|  | /* merge normal memory regions */ | ||
|  | static void _memblock_merge_memory(void) | ||
|  | { | ||
|  |     struct rt_mmblk_reg *reg; | ||
|  | 
 | ||
|  |     rt_slist_for_each_entry(reg, &(mmblk_memory.reg_list), node) | ||
|  |     { | ||
|  |         while (_next_region(reg) && | ||
|  |             reg->flags == _next_region(reg)->flags && | ||
|  |             reg->memreg.end == _next_region(reg)->memreg.start) | ||
|  |         { | ||
|  |             reg->memreg.end = _next_region(reg)->memreg.end; | ||
|  |             _reg_remove_after(reg); | ||
|  |         } | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | /* free all available memory to buddy system */ | ||
|  | static void _memblock_free_all(void) | ||
|  | { | ||
|  |     rt_region_t reg; | ||
|  |     rt_size_t mem = 0; | ||
|  |     struct rt_mmblk_reg *m, *r; | ||
|  | 
 | ||
|  |     for_each_free_region(m, r, MEMBLOCK_NONE, ®.start, ®.end) | ||
|  |     { | ||
|  |         reg.start -= PV_OFFSET; | ||
|  |         reg.end -= PV_OFFSET; | ||
|  |         rt_page_install(reg); | ||
|  | 
 | ||
|  |         LOG_D("region [%p-%p) added to buddy system\n", reg.start, reg.end); | ||
|  |         mem += reg.end - reg.start; | ||
|  |     } | ||
|  | 
 | ||
|  |     LOG_D("0x%lx(%ld) bytes memory added to buddy system\n", mem, mem); | ||
|  | } | ||
|  | 
 | ||
|  | void rt_memblock_setup_memory_environment(void) | ||
|  | { | ||
|  |     struct rt_mmblk_reg *reg, *start_reg, *end_reg; | ||
|  |     rt_err_t err = RT_EOK; | ||
|  | 
 | ||
|  |     _memblock_merge_memory(); | ||
|  |     rt_slist_for_each_entry(reg, &(mmblk_reserved.reg_list), node) | ||
|  |     { | ||
|  |         if (reg->flags != MEMBLOCK_NONE) | ||
|  |         { | ||
|  |             err = _memblock_separate_range(&mmblk_memory, reg->memreg.start, reg->memreg.end, &start_reg, &end_reg); | ||
|  |             RT_ASSERT(err == RT_EOK); | ||
|  | 
 | ||
|  |             _memblock_set_flag(start_reg, end_reg, reg->flags); | ||
|  |         } | ||
|  |     } | ||
|  |     _memblock_free_all(); | ||
|  | } | ||
|  | 
 | ||
|  | #ifdef UTEST_MM_API_TC
 | ||
|  | /* functions below are only used for utest */ | ||
|  | void rt_memblock_merge(void) | ||
|  | { | ||
|  |     _memblock_merge_memory(); | ||
|  | } | ||
|  | 
 | ||
|  | static struct rt_mmblk_reg *mem; | ||
|  | static struct rt_mmblk_reg *res; | ||
|  | 
 | ||
|  | void rt_memblock_next_free_region_init(void) | ||
|  | { | ||
|  |     mem = rt_slist_entry(&(mmblk_memory.reg_list.next), struct rt_mmblk_reg, node); | ||
|  |     res = rt_slist_entry(&(mmblk_reserved.reg_list), struct rt_mmblk_reg, node); | ||
|  | } | ||
|  | 
 | ||
|  | void rt_memblock_next_free_region(mmblk_flag_t flags, rt_size_t *out_start, rt_size_t *out_end) | ||
|  | { | ||
|  |     _next_free_region(&mem, &res, flags, out_start, out_end); | ||
|  | } | ||
|  | 
 | ||
|  | rt_bool_t rt_memblock_is_last_free(void) | ||
|  | { | ||
|  |     return mem == rt_slist_entry(&(mmblk_memory.reg_list), struct rt_mmblk_reg, node); | ||
|  | } | ||
|  | 
 | ||
|  | #endif /* UTEST_MM_API_TC */
 |