278 lines
4.9 KiB
C
278 lines
4.9 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (C) 2020 MediaTek Inc.
|
|
*/
|
|
|
|
#include <linux/init.h>
|
|
#include <linux/memblock.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/module.h>
|
|
#include <linux/of_fdt.h>
|
|
#include <linux/percpu.h>
|
|
#include <linux/string.h>
|
|
|
|
#include <asm/memory.h>
|
|
#include <asm/pgtable.h>
|
|
#include <asm/sections.h>
|
|
#include <asm/smp_plat.h>
|
|
#include <asm/stacktrace.h>
|
|
#include <asm/system_misc.h>
|
|
|
|
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 <zhiyong.wang@mediatek.com>");
|
|
MODULE_AUTHOR("Stanley Chu <stanley chu@mediatek.com>");
|
|
MODULE_LICENSE("GPL v2");
|
|
MODULE_DESCRIPTION("Memory Block");
|