diff --git a/include/sysmem.h b/include/sysmem.h new file mode 100644 index 0000000000..308b8e2653 --- /dev/null +++ b/include/sysmem.h @@ -0,0 +1,148 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2019 Rockchip Electronics Co., Ltd + */ + +#ifndef _SYSMEM_H +#define _SYSMEM_H + +#include + +#define MAX_SYSMEM_REGIONS 64 + +#undef MAX_LMB_REGIONS +#define MAX_LMB_REGIONS MAX_SYSMEM_REGIONS + +struct sysmem_property { + const char *name; + phys_addr_t base; + phys_size_t size; + struct list_head node; +}; + +struct sysmem { + struct lmb lmb; + struct list_head allocated_head; + struct list_head reserved_head; + ulong allocated_cnt; + bool has_init; +}; + +/** + * sysmem_init() - Sysmem initialization + * + * @return 0 on success, otherwise error + */ +int sysmem_init(void); + +/** + * sysmem_add() - Add sysmem region + * + * @base: region base address + * @size: region size + * + * @return 0 on success, otherwise error + */ +int sysmem_add(phys_addr_t base, phys_size_t size); + +/** + * sysmem_reserve() - Reserve sysmem region + * + * @name: region name + * @base: region base address + * @size: region size + * + * @return 0 on success, otherwise error + */ +int sysmem_reserve(const char *name, phys_addr_t base, phys_size_t size); + +/** + * sysmem_alloc() - Alloc sysmem region at anywhere + * + * @name: region name + * @size: region size + * + * @return NULL on error, otherwise the allocated region address ptr + */ +void *sysmem_alloc(const char *name, phys_size_t size); + +/** + * sysmem_alloc_align() - Alloc sysmem region at anywhere with addr align down + * + * @name: region name + * @size: region size + * @align: region base address align (down) + * + * @return NULL on error, otherwise the allocated region address ptr + */ +void *sysmem_alloc_align(const char *name, phys_size_t size, ulong align); + +/** + * sysmem_alloc_base() - Alloc sysmem region at the expect addr + * + * @name: region name + * @base: region base + * @size: region size + * + * @return NULL on error, otherwise the allocated region address ptr + */ +void *sysmem_alloc_base(const char *name, phys_addr_t base, phys_size_t size); + +/** + * sysmem_alloc_align_base() - Alloc sysmem region at the expect addr with align down + * + * @name: region name + * @base: region base + * @size: region size + * @align: region base address align (down) + * + * @return NULL on error, otherwise the allocated region address ptr + */ +void *sysmem_alloc_align_base(const char *name, phys_addr_t base, + phys_size_t size, ulong align); + +/** + * sysmem_free() - Free sysmem region + * + * @base: region base + * + * @return 0 on success, otherwise error + */ +int sysmem_free(phys_addr_t base); + +/** + * sysmem_check() - Check sysmem regions + * + * @return 0 on okay, otherwise something wrong + */ +int sysmem_check(void); + +/** + * sysmem_dump_all() - Dump all sysmem stat + */ +void sysmem_dump(void); + +/** + * sysmem_dump_check() - Dump all sysmem stat and check overflow + */ +int sysmem_dump_check(void); + +/** + * board_sysmem_reserve() - Weak function for board to implement + * + * @sysmem: global sysmem point, ignored + * + * @return 0 on success, otherwise error + */ +int board_sysmem_reserve(struct sysmem *sysmem); + +/** + * arch_sysmem_reserve() - Weak function for arch to implement + * + * @sysmem: global sysmem point, ignored + * + * @return 0 on success, otherwise error + */ +int arch_sysmem_reserve(struct sysmem *sysmem); + +#endif /* _SYSMEM_H */ diff --git a/lib/Kconfig b/lib/Kconfig index 0743d91360..12b597d104 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -93,6 +93,12 @@ config RBTREE config BITREVERSE bool +config SYSMEM + bool "System memory management" + default y + help + This enables support for system permanent memory management. + source lib/dhry/Kconfig menu "Security support" diff --git a/lib/Makefile b/lib/Makefile index fdb9f8e043..e417c794df 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -29,6 +29,9 @@ obj-$(CONFIG_GZIP_COMPRESSED) += gzip.o obj-$(CONFIG_GENERATE_SMBIOS_TABLE) += smbios.o obj-y += initcall.o obj-$(CONFIG_LMB) += lmb.o +ifdef CONFIG_LMB +obj-$(CONFIG_SYSMEM) += sysmem.o +endif obj-y += ldiv.o obj-$(CONFIG_LZ4) += lz4_wrapper.o obj-$(CONFIG_MD5) += md5.o diff --git a/lib/sysmem.c b/lib/sysmem.c new file mode 100644 index 0000000000..705ba34771 --- /dev/null +++ b/lib/sysmem.c @@ -0,0 +1,467 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019 Fuzhou Rockchip Electronics Co., Ltd + */ + +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +#define SYSMEM_MAGIC 0x4D454D53 /* "SMEM" */ +#define SYSMEM_ALLOC_ANYWHERE 0 +#define SYSMEM_ALLOC_NO_ALIGN 1 + +#ifndef CONFIG_SYS_STACK_SIZE +#define CONFIG_SYS_STACK_SIZE SZ_2M +#endif + +#define SIZE_MB(len) ((len) >> 20) +#define SIZE_KB(len) (((len) % (1 << 20)) >> 10) + +#define SYSMEM_I(fmt, args...) printf("Sysmem: "fmt, ##args) +#define SYSMEM_W(fmt, args...) printf("Sysmem Warn: "fmt, ##args) +#define SYSMEM_E(fmt, args...) printf("Sysmem Error: "fmt, ##args) +#define SYSMEM_D(fmt, args...) debug("Sysmem Debug: "fmt, ##args) + +static struct sysmem plat_sysmem; /* Global for platform */ + +struct sysmem_check { + uint32_t magic; +}; + +static int sysmem_has_init(void) +{ + if (!plat_sysmem.has_init) { + SYSMEM_E("Framework is not initialized\n"); + return 0; + } + + return 1; +} + +void sysmem_dump(void) +{ +#ifdef DEBUG + struct sysmem *sysmem = &plat_sysmem; + struct lmb *lmb = &sysmem->lmb; + struct sysmem_property *prop; + struct sysmem_check *check; + struct list_head *node; + ulong memory_size = 0; + ulong reserved_size = 0; + ulong allocated_size = 0; + ulong i; + + if (!sysmem_has_init()) + return; + + printf("\nsysmem_dump_all:\n"); + + /* Memory pool */ + printf(" ------------------------------------------------------\n"); + for (i = 0; i < lmb->memory.cnt; i++) { + memory_size += lmb->memory.region[i].size; + printf(" memory.rgn[%ld].base = 0x%08lx\n", i, + (ulong)lmb->memory.region[i].base); + printf(" .size = 0x%08lx\n", + (ulong)lmb->memory.region[i].size); + } + printf("\n memory.total = 0x%08lx (%ld MiB. %ld KiB)\n", + (ulong)memory_size, + SIZE_MB((ulong)memory_size), + SIZE_KB((ulong)memory_size)); + + /* Reserved */ + i = 0; + printf(" ------------------------------------------------------\n"); + list_for_each(node, &sysmem->reserved_head) { + prop = list_entry(node, struct sysmem_property, node); + reserved_size += prop->size; + printf(" reserved.rgn[%ld].name = \"%s\"\n", i, prop->name); + printf(" .base = 0x%08lx\n", + (ulong)prop->base); + printf(" .size = 0x%08lx\n", + (ulong)prop->size); + i++; + } + printf("\n reserved.total = 0x%08lx (%ld MiB. %ld KiB)\n", + (ulong)reserved_size, + SIZE_MB((ulong)reserved_size), + SIZE_KB((ulong)reserved_size)); + + /* Allocated */ + i = 0; + printf(" ------------------------------------------------------\n"); + list_for_each(node, &sysmem->allocated_head) { + prop = list_entry(node, struct sysmem_property, node); + allocated_size += prop->size; + check = (struct sysmem_check *) + (prop->base + prop->size - sizeof(*check)); + printf(" allocated.rgn[%ld].name = \"%s\"%s\n", + i, prop->name, + check->magic != SYSMEM_MAGIC ? " (Overflow)" : ""); + printf(" .base = 0x%08lx\n", + (ulong)prop->base); + printf(" .size = 0x%08lx\n", + (ulong)prop->size); + i++; + } + printf("\n allocated.total = 0x%08lx (%ld MiB. %ld KiB)\n", + (ulong)allocated_size, + SIZE_MB((ulong)allocated_size), + SIZE_KB((ulong)allocated_size)); + + /* LMB core reserved */ + printf(" ------------------------------------------------------\n"); + reserved_size = 0; + for (i = 0; i < lmb->reserved.cnt; i++) { + reserved_size += lmb->reserved.region[i].size; + printf(" LMB.reserved[%ld].base = 0x%08lx\n", i, + (ulong)lmb->reserved.region[i].base); + printf(" .size = 0x%08lx\n", + (ulong)lmb->reserved.region[i].size); + } + + printf("\n reserved.core.total = 0x%08lx (%ld MiB. %ld KiB)\n", + (ulong)reserved_size, + SIZE_MB((ulong)reserved_size), + SIZE_KB((ulong)reserved_size)); + printf(" ------------------------------------------------------\n\n"); +#endif +} + +int sysmem_check(void) +{ + struct sysmem *sysmem = &plat_sysmem; + struct sysmem_property *prop; + struct sysmem_check *check; + struct list_head *node; + int ret = 0; + + if (!sysmem_has_init()) + return -ENOSYS; + + /* Check allocated */ + list_for_each(node, &sysmem->allocated_head) { + prop = list_entry(node, struct sysmem_property, node); + check = (struct sysmem_check *) + (prop->base + prop->size - sizeof(*check)); + if (check->magic != SYSMEM_MAGIC) { + ret = -EOVERFLOW; + SYSMEM_E("\"%s\" (base=0x%08lx, size=0x%lx) is Overflow!\n", + prop->name, (ulong)prop->base, (ulong)prop->size); + } + } + + /* Check stack */ + check = (struct sysmem_check *)(gd->start_addr_sp - CONFIG_SYS_STACK_SIZE); + if (check->magic != SYSMEM_MAGIC) { + ret = -EOVERFLOW; + SYSMEM_E("Runtime stack is Overflow!\n"); + } + + return ret; +} + +int sysmem_dump_check(void) +{ + sysmem_dump(); + + return sysmem_check(); +} + +static int sysmem_is_overlap(phys_addr_t base1, phys_size_t size1, + phys_addr_t base2, phys_size_t size2) +{ + return ((base1 < (base2 + size2)) && (base2 < (base1 + size1))); +} + +int sysmem_add(phys_addr_t base, phys_size_t size) +{ + struct sysmem *sysmem = &plat_sysmem; + int ret; + + if (!sysmem_has_init()) + return -ENOSYS; + + ret = lmb_add(&sysmem->lmb, base, size); + if (ret < 0) + SYSMEM_E("Failed to add sysmem at 0x%lx for 0x%lx size\n", + (ulong)base, (ulong)size); + + return (ret >= 0) ? 0 : ret; +} + +int sysmem_reserve(const char *name, phys_addr_t base, phys_size_t size) +{ + struct sysmem *sysmem = &plat_sysmem; + struct sysmem_property *prop; + struct list_head *node; + int ret = 0; + + if (!sysmem_has_init()) + return -ENOSYS; + + if (!name) { + SYSMEM_E("NULL name for reserved sysmem\n"); + return -EINVAL; + } + + /* Check overlap */ + list_for_each(node, &sysmem->reserved_head) { + prop = list_entry(node, struct sysmem_property, node); + if (!strcmp(prop->name, name)) { + SYSMEM_E("Failed to double reserve for existence \"%s\"\n", name); + return -EEXIST; + } else if (sysmem_is_overlap(prop->base, prop->size, base, size)) { + SYSMEM_W("\"%s\" (base=0x%08lx, size=0x%lx) reserve is " + "overlap with existence \"%s\" (base=0x%08lx, size=0x%lx)\n", + name, (ulong)base, (ulong)size, prop->name, + (ulong)prop->base, (ulong)prop->size); + } + } + + ret = lmb_reserve(&sysmem->lmb, base, size); + if (ret >= 0) { + prop = malloc(sizeof(*prop)); + if (!prop) { + SYSMEM_E("No memory for \"%s\" reserve sysmem\n", name); + return -ENOMEM; + } + + prop->name = name; + prop->base = base; + prop->size = size; + list_add_tail(&prop->node, &sysmem->reserved_head); + } else { + SYSMEM_E("Failed to reserve \"%s\" at 0x%lx\n", name, (ulong)base); + return -EINVAL; + } + + return 0; +} + +void *sysmem_alloc_align_base(const char *name, + phys_addr_t base, + phys_size_t size, + ulong align) +{ + struct sysmem *sysmem = &plat_sysmem; + struct sysmem_property *prop; + struct sysmem_check *check; + struct list_head *node; + phys_addr_t paddr; + phys_addr_t alloc_base; + phys_size_t alloc_size; + + if (!sysmem_has_init()) + return NULL; + + if (!name) { + SYSMEM_E("NULL name for alloc sysmem\n"); + return NULL; + } + + /* Already allocated ? */ + list_for_each(node, &sysmem->allocated_head) { + prop = list_entry(node, struct sysmem_property, node); + if (!strcmp(prop->name, name)) { + SYSMEM_E("Failed to double alloc for existence \"%s\"\n", name); + return NULL; + } else if (sysmem_is_overlap(prop->base, prop->size, base, size)) { + SYSMEM_E("\"%s\" (base=0x%08lx, size=0x%lx) alloc is " + "overlap with existence \"%s\" (base=0x%08lx, size=0x%lx)\n", + name, (ulong)base, (ulong)size, + prop->name, (ulong)prop->base, + (ulong)prop->size); + return NULL; + } + } + + alloc_size = size + sizeof(*check); + if (base == SYSMEM_ALLOC_ANYWHERE) + alloc_base = base; + else + alloc_base = base + alloc_size; /* LMB is align down alloc mechanism */ + + paddr = lmb_alloc_base(&sysmem->lmb, alloc_size, align, alloc_base); + if (paddr) { + if ((paddr == base) || (base == SYSMEM_ALLOC_ANYWHERE)) { + prop = malloc(sizeof(*prop)); + if (!prop) { + SYSMEM_E("No memory for \"%s\" alloc sysmem\n", name); + return NULL; + } + + prop->name = name; + prop->base = paddr; + prop->size = alloc_size; + sysmem->allocated_cnt++; + + check = (struct sysmem_check *)(paddr + size); + check->magic = SYSMEM_MAGIC; + + list_add_tail(&prop->node, &sysmem->allocated_head); + } else { + SYSMEM_E("Failed to alloc \"%s\" at expect 0x%lx but " + "alloc at 0x%lx\n", + name, (ulong)base, (ulong)paddr); + return NULL; + } + } else { + SYSMEM_E("Failed to alloc \"%s\" at 0x%lx\n", name, (ulong)base); + } + + SYSMEM_D("Alloc: \"%s\", paddr=0x%lx, size=0x%lx, align=0x%x, anywhere=%d\n", + name, (ulong)paddr, (ulong)size, (u32)align, !base); + + return (void *)paddr; +} + +void *sysmem_alloc_align(const char *name, phys_size_t size, ulong align) +{ + return sysmem_alloc_align_base(name, + SYSMEM_ALLOC_ANYWHERE, + size, + align); +} + +void *sysmem_alloc_base(const char *name, phys_addr_t base, phys_size_t size) +{ + return sysmem_alloc_align_base(name, + base, + size, + SYSMEM_ALLOC_NO_ALIGN); +} + +void *sysmem_alloc(const char *name, phys_size_t size) +{ + return sysmem_alloc_align_base(name, + SYSMEM_ALLOC_ANYWHERE, + size, + SYSMEM_ALLOC_NO_ALIGN); +} + +int sysmem_free(phys_addr_t base) +{ + struct sysmem *sysmem = &plat_sysmem; + struct sysmem_property *prop; + struct list_head *node; + int found = 0; + int ret; + + if (!sysmem_has_init()) + return -ENOSYS; + + /* Find existence */ + list_for_each(node, &sysmem->allocated_head) { + prop = list_entry(node, struct sysmem_property, node); + if (prop->base == base) { + found = 1; + break; + } + } + + if (!found) { + SYSMEM_E("Failed to free no allocated sysmem at 0x%lx\n", (ulong)base); + return -EINVAL; + } + + ret = lmb_free(&sysmem->lmb, prop->base, prop->size); + if (ret >= 0) { + SYSMEM_I("Free: \"%s\", paddr=0x%lx, size=0x%lx\n", + prop->name, (ulong)prop->base, (ulong)prop->size); + sysmem->allocated_cnt--; + list_del(&prop->node); + free(prop); + } else { + SYSMEM_E("Failed to free \"%s\" at 0x%lx\n", prop->name, (ulong)base); + } + + return (ret >= 0) ? 0 : ret; +} + +int sysmem_init(void) +{ + struct sysmem *sysmem = &plat_sysmem; + struct sysmem_check *check; + phys_addr_t mem_start; + phys_size_t mem_size; + int ret; + + SYSMEM_I("init\n"); + + lmb_init(&sysmem->lmb); + INIT_LIST_HEAD(&sysmem->allocated_head); + INIT_LIST_HEAD(&sysmem->reserved_head); + sysmem->allocated_cnt = 0; + sysmem->has_init = true; + + /* Add all available system memory */ +#ifdef CONFIG_NR_DRAM_BANKS + int i; + + for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { + ret = sysmem_add(gd->bd->bi_dram[i].start, + gd->bd->bi_dram[i].size); + if (ret) { + SYSMEM_E("Failed to add sysmem from bi_dram[%d]\n", i); + return ret; + } + } +#else + mem_start = env_get_bootm_low(); + mem_size = env_get_bootm_size(); + ret = sysmem_add(mem_start, mem_size); + if (ret) { + SYSMEM_E("Failed to add sysmem from bootm_low/size\n"); + return ret; + } +#endif + + /* Reserved for arch */ + ret = arch_sysmem_reserve(sysmem); + if (ret) { + SYSMEM_E("Failed to reserve sysmem for arch\n"); + return ret; + } + + /* Reserved for board */ + ret = board_sysmem_reserve(sysmem); + if (ret) { + SYSMEM_E("Failed to reserve sysmem for board\n"); + return ret; + } + + /* Reserved for U-boot framework 'reserve_xxx()' */ + mem_start = gd->start_addr_sp - CONFIG_SYS_STACK_SIZE; + mem_size = gd->ram_top - mem_start; + check = (struct sysmem_check *)mem_start; + check->magic = SYSMEM_MAGIC; + + ret = sysmem_reserve("U-Boot", mem_start, mem_size); + if (ret) { + SYSMEM_E("Failed to reserve sysmem for U-Boot framework\n"); + return ret; + } + + sysmem_dump(); + + return 0; +} + +__weak int board_sysmem_reserve(struct sysmem *sysmem) +{ + /* please define platform specific board_sysmem_reserve() */ + return 0; +} + +__weak int arch_sysmem_reserve(struct sysmem *sysmem) +{ + /* please define platform specific arch_sysmem_reserve() */ + return 0; +}