// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2020 MediaTek Inc. */ #include #include #include #include #include #include #include #include #include #include #include #include #include struct module_sect_attr { struct bin_attribute battr; unsigned long address; }; struct module_sect_attrs { struct attribute_group grp; unsigned int nsections; struct module_sect_attr attrs[0]; }; #define KV kimage_vaddr #define S_MAX SZ_32M #define SM_SIZE 28 #define TT_SIZE 256 #define NAME_LEN 128 static unsigned long *mb_ka; static int *mb_ko; static unsigned long *mb_krb; static unsigned int *mb_kns; static u8 *mb_kn; static unsigned int *mb_km; static u8 *mb_ktt; static u16 *mb_kti; static void *mb_abt_addr(void) { void *ssa = (void *)(KV); void *pos; u8 abt[SM_SIZE]; int i; u32 s_left; for (i = 0; i < SM_SIZE; i++) { if (i & 0x1) abt[i] = 0; else abt[i] = 0x61 + i / 2; } pos = ssa; s_left = S_MAX; while ((u64)pos < (u64)(ssa + S_MAX)) { pos = memchr(pos, 'a', s_left); if (!pos) { pr_info("fail at: 0x%lx @ 0x%x\n", ssa, s_left); return NULL; } s_left = ssa + S_MAX - pos; if (!memcmp(pos, (const void *)abt, sizeof(abt))) return pos; pos += 1; } pr_info("fail at end: 0x%lx @ 0x%x\n", ssa, s_left); return NULL; } static unsigned long *mb_krb_addr(void) { void *abt_addr = mb_abt_addr(); unsigned long *ssa; int i; if (!abt_addr) return NULL; ssa = (unsigned long *)round_up((unsigned long)abt_addr, 8); for (i = 0; i < SZ_1M ; i++) { if (*ssa == KV) return ssa; ssa--; } pr_info("krb not found: 0x%lx\n", ssa); return NULL; } static unsigned int *mb_km_addr(void) { const u8 *name = mb_kn; unsigned int loop = *mb_kns; while (loop--) name = name + (*name) + 1; return (unsigned int *)round_up((unsigned long)name, 8); } static u16 *mb_kti_addr(void) { const u8 *pch = mb_ktt; int loop = TT_SIZE; while (loop--) { for (; *pch; pch++) ; pch++; } return (u16 *)round_up((unsigned long)pch, 8); } int mb_ka_init(void) { unsigned int kns; mb_krb = mb_krb_addr(); if (!mb_krb) return -EINVAL; mb_kns = (unsigned int *)(mb_krb + 1); mb_kn = (u8 *)(mb_krb + 2); kns = *mb_kns; mb_ko = (int *)(mb_krb - (round_up(kns, 2) / 2)); mb_km = mb_km_addr(); mb_ktt = (u8 *)round_up((unsigned long)(mb_km + (round_up(kns, 256) / 256)), 8); mb_kti = mb_kti_addr(); return 0; } static unsigned int mb_checking_names(unsigned int off, char *namebuf, size_t buflen) { int len, skipped_first = 0; const u8 *tptr, *data; data = mb_kn + off; len = *data; data++; off += len + 1; while (len) { tptr = mb_ktt + *(mb_kti + *data); data++; len--; while (*tptr) { if (skipped_first) { if (buflen <= 1) goto tail; *namebuf = *tptr; namebuf++; buflen--; } else skipped_first = 1; tptr++; } } tail: if (buflen) *namebuf = '\0'; return off; } static unsigned long mb_idx2addr(int idx) { if (!IS_ENABLED(CONFIG_KALLSYMS_BASE_RELATIVE)) return *(mb_ka + idx); if (!IS_ENABLED(CONFIG_KALLSYMS_ABSOLUTE_PERCPU)) return *mb_krb + (u32)(*(mb_ko + idx)); if (*(mb_ko + idx) >= 0) return *(mb_ko + idx); return *mb_krb - 1 - *(mb_ko + idx); } static unsigned long mb_addr_find(const char *name) { char strbuf[NAME_LEN]; unsigned long i; unsigned int off; for (i = 0, off = 0; i < *mb_kns; i++) { off = mb_checking_names(off, strbuf, ARRAY_SIZE(strbuf)); if (strcmp(strbuf, name) == 0) return mb_idx2addr(i); } return 0; } static struct memblock *p_memblock; static struct memblock *mb_get_memblock(void) { if (p_memblock) return p_memblock; p_memblock = (void *)(mb_addr_find("memblock")); if (!p_memblock) { pr_info("%s failed", __func__); return NULL; } return p_memblock; } phys_addr_t mb_start_of_DRAM(void) { struct memblock *memblockp = mb_get_memblock(); if (!memblockp) { pr_info("%s failed", __func__); return 0; } return memblockp->memory.regions[0].base; } EXPORT_SYMBOL_GPL(mb_start_of_DRAM); phys_addr_t mb_end_of_DRAM(void) { struct memblock *memblockp = mb_get_memblock(); int idx; if (!memblockp) { pr_info("%s failed", __func__); return 0; } idx = memblockp->memory.cnt - 1; return (memblockp->memory.regions[idx].base + memblockp->memory.regions[idx].size); } EXPORT_SYMBOL_GPL(mb_end_of_DRAM); static int __init mb_init(void) { int ret; return 0; ret = mb_ka_init(); if (ret) { pr_info("mb_ka_init failed: %d", ret); return ret; } mb_get_memblock(); return 0; } arch_initcall(mb_init); MODULE_AUTHOR("Zhiyong Wang "); MODULE_AUTHOR("Stanley Chu "); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("Memory Block");