summaryrefslogtreecommitdiff
path: root/components
diff options
context:
space:
mode:
authorzms123456 <[email protected]>2023-10-14 14:10:57 +0800
committerGitHub <[email protected]>2023-10-14 14:10:57 +0800
commita39da9cb596b9dc0b0d0265fa67d5db8a2f66f09 (patch)
tree1225c904c74778ccaed41e9ea6b5fee330703720 /components
parent1e7add54d44146a0245fc89f0a55ce27e772f2d3 (diff)
[components][mm]添加预留内存支持 (#8025)
Diffstat (limited to 'components')
-rw-r--r--components/Kconfig1
-rw-r--r--components/mm/Kconfig19
-rw-r--r--components/mm/SConscript7
-rw-r--r--components/mm/mm_memblock.c401
-rw-r--r--components/mm/mm_memblock.h95
5 files changed, 521 insertions, 2 deletions
diff --git a/components/Kconfig b/components/Kconfig
index d9b9bc60df..45cd7e1f7c 100644
--- a/components/Kconfig
+++ b/components/Kconfig
@@ -34,5 +34,6 @@ source "$RTT_DIR/components/libc/Kconfig"
source "$RTT_DIR/components/net/Kconfig"
source "$RTT_DIR/components/utilities/Kconfig"
source "$RTT_DIR/components/vbus/Kconfig"
+source "$RTT_DIR/components/mm/Kconfig"
endmenu
diff --git a/components/mm/Kconfig b/components/mm/Kconfig
new file mode 100644
index 0000000000..59ba75886e
--- /dev/null
+++ b/components/mm/Kconfig
@@ -0,0 +1,19 @@
+menu "Memory management"
+
+config RT_USING_MEMBLOCK
+ bool "Using memblock"
+ default n
+ help
+ Using memblock to record memory infomation in init time
+
+config RT_INIT_MEMORY_REGIONS
+ int "The max number of memory block regions in init time"
+ depends on RT_USING_MEMBLOCK
+ range 1 1024
+ default 128
+ help
+ During the system initialization phase, the kernel divides
+ memory into different types of regions. This variable specifies
+ the maximum number of regions supported by the system.
+
+endmenu
diff --git a/components/mm/SConscript b/components/mm/SConscript
index 02273a34b0..e42fa9f0c8 100644
--- a/components/mm/SConscript
+++ b/components/mm/SConscript
@@ -1,11 +1,14 @@
import os
from building import *
-objs = []
+objs = []
+src = []
if GetDepend('ARCH_ARM_CORTEX_A') or GetDepend('ARCH_ARMV8') or GetDepend('ARCH_RISCV64'):
cwd = GetCurrentDir()
- src = Glob('*.c') + Glob('*_gcc.S')
+ src += ['avl_adpt.c', 'ioremap.c', 'mm_aspace.c', 'mm_fault.c', 'mm_kmem.c', 'mm_object.c', 'mm_page.c']
+ if GetDepend('RT_USING_MEMBLOCK'):
+ src += ['mm_memblock.c']
CPPPATH = [cwd]
group = DefineGroup('mm', src, depend = ['ARCH_MM_MMU'], CPPPATH = CPPPATH)
diff --git a/components/mm/mm_memblock.c b/components/mm/mm_memblock.c
new file mode 100644
index 0000000000..8ec0867942
--- /dev/null
+++ b/components/mm/mm_memblock.c
@@ -0,0 +1,401 @@
+/*
+ * 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, &reg.start, &reg.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 */
diff --git a/components/mm/mm_memblock.h b/components/mm/mm_memblock.h
new file mode 100644
index 0000000000..da9c7a679b
--- /dev/null
+++ b/components/mm/mm_memblock.h
@@ -0,0 +1,95 @@
+/*
+ * 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
+ */
+
+#ifndef __MM_MEMBLOCK_H__
+#define __MM_MEMBLOCK_H__
+
+#include "mm_page.h"
+#include <rtdef.h>
+
+enum mmblk_flag
+{
+ MEMBLOCK_NONE = 0x0, /**< normal region */
+ MEMBLOCK_HOTPLUG = 0x1, /**< hotpluggable region */
+ MEMBLOCK_NOMAP = 0x2, /**< don't add to kernel direct mapping */
+};
+
+typedef rt_uint32_t mmblk_flag_t;
+
+/**
+ * @struct rt_mmblk_reg
+ *
+ * @brief A structure representing a region in rt_memblock
+ */
+struct rt_mmblk_reg
+{
+ rt_region_t memreg; /**< used to indicate the extent of this area */
+ mmblk_flag_t flags; /**< the flag of the region */
+ rt_bool_t alloc; /**< is the node allocated */
+ rt_slist_t node; /**< hook on rt_memblock */
+};
+
+/**
+ * @struct rt_memblock
+ *
+ * @brief A structure representing physical memory block
+ */
+struct rt_memblock
+{
+ rt_slist_t reg_list; /**< store the regions of the memory block */
+};
+
+/**
+ * @brief Add a physical address range to the overall memory region
+ *
+ * @note The newly added region is strictly prohibited from overlapping with existing regions.
+ *
+ * @param name the name of the region
+ * @param start the beginning of the physical address range
+ * @param end the size of the physical address range
+ * @param flags the flags of the region
+ */
+rt_err_t rt_memblock_add_memory(char *name, rt_size_t start, rt_size_t end, mmblk_flag_t flags);
+
+/**
+ * @brief Add a physical address range to the reserved memory region
+ *
+ * @note The newly added region is strictly prohibited from overlapping with existing regions.
+ *
+ * @param name the name of the reseved region
+ * @param start the beginning of the physical address range
+ * @param end the size of the physical address range
+ * @param flags the flags of the region
+ */
+rt_err_t rt_memblock_reserve_memory(char *name, rt_size_t start, rt_size_t end, mmblk_flag_t flags);
+
+/**
+ * @brief To conclude the management of memory by the memblock.
+ *
+ * @note This function will free all usable to buddy system and map all memory without
+ * specified MEMBLOCK_NOMAP.
+ */
+void rt_memblock_setup_memory_environment(void);
+
+/**
+ * @brief Get the memory memblock.
+ *
+ * @return Overall memory memblock.
+ */
+struct rt_memblock *rt_memblock_get_memory(void);
+
+/**
+ * @brief Get the reserved memory memblock.
+ *
+ * @return Reserved memory memblock.
+ */
+struct rt_memblock *rt_memblock_get_reserved(void);
+
+#endif /* __MM_MEMBLOCK_H__ */