6db4831e98
Android 14
764 lines
20 KiB
C
764 lines
20 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (C) 2016 MediaTek Inc.
|
|
*/
|
|
|
|
#include <linux/bug.h>
|
|
#include <linux/compiler.h>
|
|
#include <linux/elf.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/highmem.h>
|
|
#include <linux/init.h>
|
|
#include <linux/kdebug.h>
|
|
#include <linux/memblock.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/module.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_fdt.h>
|
|
#include <linux/of_reserved_mem.h>
|
|
#include <linux/printk.h>
|
|
#include <linux/sizes.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/spinlock.h>
|
|
#include <linux/stacktrace.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/vmalloc.h>
|
|
#include "../../../../kernel/sched/sched.h"
|
|
|
|
#include <asm/irq.h>
|
|
#include <asm/kexec.h>
|
|
#include <asm/page.h>
|
|
#include <asm/pgtable.h>
|
|
#include <asm/setup.h>
|
|
#include <asm/stacktrace.h>
|
|
#include <asm-generic/percpu.h>
|
|
#include <asm-generic/sections.h>
|
|
|
|
#include <mrdump.h>
|
|
#include <mt-plat/aee.h>
|
|
#include "mrdump_mini.h"
|
|
#include "mrdump_private.h"
|
|
|
|
static struct mrdump_mini_elf_header *mrdump_mini_ehdr;
|
|
#ifdef CONFIG_MODULES
|
|
static char *modules_info_buf;
|
|
#endif
|
|
|
|
__weak void get_gz_log_buffer(unsigned long *addr, unsigned long *paddr,
|
|
unsigned long *size, unsigned long *start)
|
|
{
|
|
*addr = *paddr = *size = *start = 0;
|
|
}
|
|
|
|
__weak void get_disp_err_buffer(unsigned long *addr, unsigned long *size,
|
|
unsigned long *start)
|
|
{
|
|
}
|
|
|
|
|
|
__weak void get_disp_fence_buffer(unsigned long *addr, unsigned long *size,
|
|
unsigned long *start)
|
|
{
|
|
}
|
|
|
|
__weak void get_disp_dbg_buffer(unsigned long *addr, unsigned long *size,
|
|
unsigned long *start)
|
|
{
|
|
}
|
|
|
|
__weak void get_disp_dump_buffer(unsigned long *addr, unsigned long *size,
|
|
unsigned long *start)
|
|
{
|
|
}
|
|
|
|
__weak void get_pidmap_aee_buffer(unsigned long *addr, unsigned long *size)
|
|
{
|
|
}
|
|
|
|
#ifdef __aarch64__
|
|
#define MIN_MARGIN VA_START
|
|
#else
|
|
#define MIN_MARGIN MODULES_VADDR
|
|
#endif
|
|
|
|
#ifdef __aarch64__
|
|
static unsigned long virt_2_pfn(unsigned long addr)
|
|
{
|
|
pgd_t *pgd = aee_pgd_offset_k(addr), _pgd_val = {0};
|
|
pud_t *pud, _pud_val = {0};
|
|
pmd_t *pmd, _pmd_val = {0};
|
|
pte_t *ptep, _pte_val = {0};
|
|
unsigned long pfn = ~0UL;
|
|
|
|
if (addr < VA_START)
|
|
goto OUT;
|
|
|
|
if (probe_kernel_address(pgd, _pgd_val) || pgd_none(_pgd_val))
|
|
goto OUT;
|
|
pud = pud_offset(pgd, addr);
|
|
if (probe_kernel_address(pud, _pud_val) || pud_none(_pud_val))
|
|
goto OUT;
|
|
if (pud_sect(_pud_val)) {
|
|
pfn = pud_pfn(_pud_val) + ((addr&~PUD_MASK) >> PAGE_SHIFT);
|
|
} else if (pud_table(_pud_val)) {
|
|
pmd = pmd_offset(pud, addr);
|
|
if (probe_kernel_address(pmd, _pmd_val) || pmd_none(_pmd_val))
|
|
goto OUT;
|
|
if (pmd_sect(_pmd_val)) {
|
|
pfn = pmd_pfn(_pmd_val) +
|
|
((addr&~PMD_MASK) >> PAGE_SHIFT);
|
|
} else if (pmd_table(_pmd_val)) {
|
|
ptep = pte_offset_map(pmd, addr);
|
|
if (probe_kernel_address(ptep, _pte_val)
|
|
|| !pte_present(_pte_val)) {
|
|
pte_unmap(ptep);
|
|
goto OUT;
|
|
}
|
|
pfn = pte_pfn(_pte_val);
|
|
pte_unmap(ptep);
|
|
}
|
|
}
|
|
OUT:
|
|
return pfn;
|
|
|
|
}
|
|
#else
|
|
#ifndef pmd_sect
|
|
#define pmd_sect(pmd) (pmd & PMD_TYPE_SECT)
|
|
#endif
|
|
#ifndef pmd_table
|
|
#define pmd_table(pmd) (pmd & PMD_TYPE_TABLE)
|
|
#endif
|
|
#ifndef pmd_pfn
|
|
#define pmd_pfn(pmd) (((pmd_val(pmd) & PMD_MASK) & PHYS_MASK) >> PAGE_SHIFT)
|
|
#endif
|
|
static unsigned long virt_2_pfn(unsigned long addr)
|
|
{
|
|
pgd_t *pgd = aee_pgd_offset_k(addr), _pgd_val = {0};
|
|
#ifdef CONFIG_ARM_LPAE
|
|
pud_t *pud, _pud_val = {0};
|
|
#else
|
|
pud_t *pud, _pud_val = {{0} };
|
|
#endif
|
|
pmd_t *pmd, _pmd_val = 0;
|
|
pte_t *ptep, _pte_val = 0;
|
|
unsigned long pfn = ~0UL;
|
|
|
|
if (probe_kernel_address(pgd, _pgd_val) || pgd_none(_pgd_val))
|
|
goto OUT;
|
|
pud = pud_offset(pgd, addr);
|
|
if (probe_kernel_address(pud, _pud_val) || pud_none(_pud_val))
|
|
goto OUT;
|
|
pmd = pmd_offset(pud, addr);
|
|
if (probe_kernel_address(pmd, _pmd_val) || pmd_none(_pmd_val))
|
|
goto OUT;
|
|
if (pmd_sect(_pmd_val)) {
|
|
pfn = pmd_pfn(_pmd_val) + ((addr&~PMD_MASK) >> PAGE_SHIFT);
|
|
} else if (pmd_table(_pmd_val)) {
|
|
ptep = pte_offset_map(pmd, addr);
|
|
if (probe_kernel_address(ptep, _pte_val)
|
|
|| !pte_present(_pte_val)) {
|
|
pte_unmap(ptep);
|
|
goto OUT;
|
|
}
|
|
pfn = pte_pfn(_pte_val);
|
|
pte_unmap(ptep);
|
|
}
|
|
OUT:
|
|
return pfn;
|
|
}
|
|
#endif
|
|
|
|
/* copy from fs/binfmt_elf.c */
|
|
static void fill_elf_header(struct elfhdr *elf, int segs)
|
|
{
|
|
memcpy(elf->e_ident, ELFMAG, SELFMAG);
|
|
elf->e_ident[EI_CLASS] = ELF_CLASS;
|
|
elf->e_ident[EI_DATA] = ELF_DATA;
|
|
elf->e_ident[EI_VERSION] = EV_CURRENT;
|
|
elf->e_ident[EI_OSABI] = ELF_OSABI;
|
|
|
|
elf->e_type = ET_CORE;
|
|
elf->e_machine = ELF_ARCH;
|
|
elf->e_version = EV_CURRENT;
|
|
elf->e_phoff = sizeof(struct elfhdr);
|
|
#ifndef ELF_CORE_EFLAGS
|
|
#define ELF_CORE_EFLAGS 0
|
|
#endif
|
|
elf->e_flags = ELF_CORE_EFLAGS;
|
|
elf->e_ehsize = sizeof(struct elfhdr);
|
|
elf->e_phentsize = sizeof(struct elf_phdr);
|
|
elf->e_phnum = segs;
|
|
|
|
}
|
|
|
|
static void fill_elf_note_phdr(struct elf_phdr *phdr, int sz, loff_t offset)
|
|
{
|
|
phdr->p_type = PT_NOTE;
|
|
phdr->p_offset = offset;
|
|
phdr->p_vaddr = 0;
|
|
phdr->p_paddr = 0;
|
|
phdr->p_filesz = sz;
|
|
phdr->p_memsz = 0;
|
|
phdr->p_flags = 0;
|
|
phdr->p_align = 0;
|
|
}
|
|
|
|
static noinline void fill_note(struct elf_note *note, const char *name,
|
|
int type, unsigned int sz, unsigned int namesz)
|
|
{
|
|
char *n_name = (char *)note + sizeof(struct elf_note);
|
|
|
|
note->n_namesz = namesz;
|
|
note->n_type = type;
|
|
note->n_descsz = sz;
|
|
strncpy(n_name, name, note->n_namesz);
|
|
}
|
|
|
|
static void fill_note_L(struct elf_note *note, const char *name, int type,
|
|
unsigned int sz)
|
|
{
|
|
fill_note(note, name, type, sz, NOTE_NAME_LONG);
|
|
}
|
|
|
|
static void fill_note_S(struct elf_note *note, const char *name, int type,
|
|
unsigned int sz)
|
|
{
|
|
fill_note(note, name, type, sz, NOTE_NAME_SHORT);
|
|
}
|
|
|
|
/*
|
|
* fill up all the fields in prstatus from the given task struct, except
|
|
* registers which need to be filled up separately.
|
|
*/
|
|
static void fill_prstatus(struct elf_prstatus *prstatus, struct pt_regs *regs,
|
|
struct task_struct *p, unsigned long pid)
|
|
{
|
|
elf_core_copy_regs(&prstatus->pr_reg, regs);
|
|
prstatus->pr_pid = pid;
|
|
prstatus->pr_ppid = nr_cpu_ids;
|
|
prstatus->pr_sigpend = (uintptr_t)p;
|
|
}
|
|
|
|
static int fill_psinfo(struct elf_prpsinfo *psinfo)
|
|
{
|
|
unsigned int i;
|
|
|
|
strncpy(psinfo->pr_psargs, "vmlinux", ELF_PRARGSZ - 1);
|
|
for (i = 0; i < ELF_PRARGSZ - 1; i++)
|
|
if (psinfo->pr_psargs[i] == 0)
|
|
psinfo->pr_psargs[i] = ' ';
|
|
psinfo->pr_psargs[ELF_PRARGSZ - 1] = 0;
|
|
strncpy(psinfo->pr_fname, "vmlinux", sizeof(psinfo->pr_fname));
|
|
return 0;
|
|
}
|
|
|
|
#ifndef __pa_nodebug
|
|
#ifdef __pa_symbol_nodebug
|
|
#define __pa_nodebug __pa_symbol_nodebug
|
|
#else
|
|
#define __pa_nodebug __pa
|
|
#endif
|
|
#endif
|
|
void mrdump_mini_add_misc_pa(unsigned long va, unsigned long pa,
|
|
unsigned long size, unsigned long start, char *name)
|
|
{
|
|
int i;
|
|
struct elf_note *note;
|
|
|
|
if (!mrdump_mini_ehdr)
|
|
return;
|
|
|
|
for (i = 0; i < MRDUMP_MINI_NR_MISC; i++) {
|
|
note = &mrdump_mini_ehdr->misc[i].note;
|
|
if (note->n_type == NT_IPANIC_MISC) {
|
|
if (strncmp(name, MRDUMP_MINI_MISC_LOAD, 4) == 0)
|
|
continue;
|
|
if (strncmp(mrdump_mini_ehdr->misc[i].name, name, 16)
|
|
!= 0)
|
|
continue;
|
|
}
|
|
mrdump_mini_ehdr->misc[i].data.vaddr = va;
|
|
mrdump_mini_ehdr->misc[i].data.paddr = pa;
|
|
mrdump_mini_ehdr->misc[i].data.size = size;
|
|
mrdump_mini_ehdr->misc[i].data.start =
|
|
mrdump_virt_addr_valid((void *)start) ?
|
|
__pa_nodebug(start) : 0;
|
|
fill_note_L(note, name, NT_IPANIC_MISC,
|
|
sizeof(struct mrdump_mini_elf_misc));
|
|
break;
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(mrdump_mini_add_misc_pa);
|
|
|
|
void mrdump_mini_add_misc(unsigned long addr, unsigned long size,
|
|
unsigned long start, char *name)
|
|
{
|
|
if (!mrdump_virt_addr_valid((void *)addr))
|
|
return;
|
|
mrdump_mini_add_misc_pa(addr, __pa_nodebug(addr), size, start, name);
|
|
}
|
|
|
|
int kernel_addr_valid(unsigned long addr)
|
|
{
|
|
if (addr < MIN_MARGIN)
|
|
return 0;
|
|
|
|
return pfn_valid(virt_2_pfn(addr));
|
|
}
|
|
|
|
static void mrdump_mini_build_task_info(struct pt_regs *regs)
|
|
{
|
|
#define MAX_STACK_TRACE_DEPTH 64
|
|
unsigned long ipanic_stack_entries[MAX_STACK_TRACE_DEPTH];
|
|
char symbol[96] = {'\0'};
|
|
int sz;
|
|
#ifdef CONFIG_STACKTRACE
|
|
int off, plen;
|
|
struct stack_trace trace;
|
|
int i;
|
|
#endif
|
|
struct task_struct *tsk, *cur;
|
|
struct task_struct *previous;
|
|
struct aee_process_info *cur_proc;
|
|
|
|
if (!mrdump_mini_ehdr) {
|
|
pr_notice("mrdump: ehder invalid\n");
|
|
return;
|
|
}
|
|
|
|
if (!mrdump_virt_addr_valid(current_thread_info())) {
|
|
pr_notice("current thread info invalid\n");
|
|
return;
|
|
}
|
|
cur = current;
|
|
tsk = cur;
|
|
if (!mrdump_virt_addr_valid(tsk)) {
|
|
pr_notice("tsk invalid\n");
|
|
return;
|
|
}
|
|
cur_proc = (struct aee_process_info *)((void *)mrdump_mini_ehdr +
|
|
MRDUMP_MINI_HEADER_SIZE);
|
|
/* Current panic user tasks */
|
|
sz = 0;
|
|
do {
|
|
if (!tsk) {
|
|
pr_notice("No tsk info\n");
|
|
memset_io(cur_proc, 0x0,
|
|
sizeof(struct aee_process_info));
|
|
break;
|
|
}
|
|
/* FIXME: Check overflow ? */
|
|
sz += snprintf(symbol + sz, 96 - sz, "[%s, %d]", tsk->comm,
|
|
tsk->pid);
|
|
previous = tsk;
|
|
tsk = tsk->real_parent;
|
|
if (!mrdump_virt_addr_valid(tsk)) {
|
|
pr_notice("tsk(%p) invalid (previous: [%s, %d])\n", tsk,
|
|
previous->comm, previous->pid);
|
|
break;
|
|
}
|
|
} while (tsk && (tsk->pid != 0) && (tsk->pid != 1));
|
|
if (!strncmp(cur_proc->process_path, symbol, sz)) {
|
|
pr_notice("same process path\n");
|
|
return;
|
|
}
|
|
|
|
memset_io(cur_proc, 0, sizeof(struct aee_process_info));
|
|
memcpy(cur_proc->process_path, symbol, sz);
|
|
|
|
if (regs) {
|
|
cur_proc->ke_frame.pc = (__u64) regs->reg_pc;
|
|
cur_proc->ke_frame.lr = (__u64) regs->reg_lr;
|
|
}
|
|
#ifdef CONFIG_STACKTRACE
|
|
/* Grab kernel task stack trace */
|
|
trace.nr_entries = 0;
|
|
trace.max_entries = MAX_STACK_TRACE_DEPTH;
|
|
trace.entries = ipanic_stack_entries;
|
|
/* the value is only from experience and without strict rules
|
|
* need to pay attention to the value
|
|
*/
|
|
trace.skip = 4;
|
|
save_stack_trace_tsk(cur, &trace);
|
|
if (!regs) {
|
|
/* in case panic() is called without die */
|
|
/* Todo: a UT for this */
|
|
cur_proc->ke_frame.pc = ipanic_stack_entries[0];
|
|
cur_proc->ke_frame.lr = ipanic_stack_entries[1];
|
|
}
|
|
/* Skip the entries -
|
|
* ipanic_save_current_tsk_info/save_stack_trace_tsk
|
|
*/
|
|
for (i = 0; i < trace.nr_entries; i++) {
|
|
off = strlen(cur_proc->backtrace);
|
|
plen = AEE_BACKTRACE_LENGTH - ALIGN(off, 8);
|
|
if (plen > 16) {
|
|
if (ipanic_stack_entries[i] != cur_proc->ke_frame.pc)
|
|
ipanic_stack_entries[i] -= 4;
|
|
sz = snprintf(symbol, 96, "[<%px>] %pS\n",
|
|
(void *)ipanic_stack_entries[i],
|
|
(void *)ipanic_stack_entries[i]);
|
|
if (ALIGN(sz, 8) - sz) {
|
|
memset_io(symbol + sz - 1, ' ',
|
|
ALIGN(sz, 8) - sz);
|
|
memset_io(symbol + ALIGN(sz, 8) - 1, '\n', 1);
|
|
}
|
|
if (ALIGN(sz, 8) <= plen)
|
|
memcpy(cur_proc->backtrace + ALIGN(off, 8),
|
|
symbol, ALIGN(sz, 8));
|
|
}
|
|
}
|
|
#endif
|
|
if (mrdump_virt_addr_valid(cur_proc->ke_frame.pc))
|
|
snprintf(cur_proc->ke_frame.pc_symbol, AEE_SZ_SYMBOL_S,
|
|
"[<%px>] %pS",
|
|
(void *)(unsigned long)cur_proc->ke_frame.pc,
|
|
(void *)(unsigned long)cur_proc->ke_frame.pc);
|
|
else
|
|
pr_info("[<%llu>] invalid pc", cur_proc->ke_frame.pc);
|
|
if (mrdump_virt_addr_valid(cur_proc->ke_frame.lr))
|
|
snprintf(cur_proc->ke_frame.lr_symbol, AEE_SZ_SYMBOL_L,
|
|
"[<%px>] %pS",
|
|
(void *)(unsigned long)cur_proc->ke_frame.lr,
|
|
(void *)(unsigned long)cur_proc->ke_frame.lr);
|
|
else
|
|
pr_info("[<%llu>] invalid lr", cur_proc->ke_frame.lr);
|
|
|
|
}
|
|
|
|
|
|
int mrdump_modules_info(unsigned char *buffer, size_t sz_buf)
|
|
{
|
|
#ifdef CONFIG_MODULES
|
|
int sz;
|
|
|
|
sz = aee_save_modules(modules_info_buf, MODULES_INFO_BUF_SIZE);
|
|
if (sz <= 0 || sz_buf < sz || !buffer)
|
|
return -1;
|
|
memcpy(buffer, modules_info_buf, sz);
|
|
return sz;
|
|
#else
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
#define EXTRA_MISC(func, name, max_size) \
|
|
__weak void func(unsigned long *vaddr, unsigned long *size) \
|
|
{ \
|
|
if (size) \
|
|
*size = 0; \
|
|
}
|
|
#include "mrdump_mini_extra_misc.h"
|
|
|
|
#undef EXTRA_MISC
|
|
#define EXTRA_MISC(func, name, max_size) \
|
|
{func, name, max_size},
|
|
|
|
static struct mrdump_mini_extra_misc extra_members[] = {
|
|
#include "mrdump_mini_extra_misc.h"
|
|
};
|
|
|
|
#define EXTRA_TOTAL_NUM ((sizeof(extra_members)) / (sizeof(extra_members[0])))
|
|
static size_t __maybe_unused dummy_check(void)
|
|
{
|
|
size_t dummy;
|
|
|
|
dummy = BUILD_BUG_ON_ZERO(EXTRA_TOTAL_NUM > 10);
|
|
return dummy;
|
|
}
|
|
|
|
static int _mrdump_mini_add_extra_misc(unsigned long vaddr, unsigned long size,
|
|
const char *name)
|
|
{
|
|
char name_buf[SZ_128];
|
|
|
|
if (!mrdump_mini_ehdr ||
|
|
!size ||
|
|
size > SZ_512K ||
|
|
!name)
|
|
return -1;
|
|
snprintf(name_buf, SZ_128, "_EXTRA_%s_", name);
|
|
mrdump_mini_add_misc(vaddr, size, 0, name_buf);
|
|
return 0;
|
|
}
|
|
|
|
void mrdump_mini_add_extra_misc(void)
|
|
{
|
|
static int once;
|
|
int i;
|
|
unsigned long vaddr = 0;
|
|
unsigned long size = 0;
|
|
int ret;
|
|
|
|
if (!once) {
|
|
once = 1;
|
|
for (i = 0; i < EXTRA_TOTAL_NUM; i++) {
|
|
extra_members[i].dump_func(&vaddr, &size);
|
|
if (size > extra_members[i].max_size)
|
|
continue;
|
|
ret = _mrdump_mini_add_extra_misc(vaddr, size,
|
|
extra_members[i].dump_name);
|
|
if (ret < 0)
|
|
pr_notice("mrdump: add %s:0x%lx sz:0x%lx failed\n",
|
|
extra_members[i].dump_name,
|
|
vaddr, size);
|
|
}
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(mrdump_mini_add_extra_misc);
|
|
|
|
static void mrdump_mini_fatal(const char *str)
|
|
{
|
|
pr_notice("minirdump: FATAL:%s\n", str);
|
|
}
|
|
|
|
static unsigned int mrdump_mini_addr;
|
|
static unsigned int mrdump_mini_size;
|
|
void mrdump_mini_set_addr_size(unsigned int addr, unsigned int size)
|
|
{
|
|
mrdump_mini_addr = addr;
|
|
mrdump_mini_size = size;
|
|
}
|
|
EXPORT_SYMBOL(mrdump_mini_set_addr_size);
|
|
|
|
static void mrdump_mini_build_elf_misc(void)
|
|
{
|
|
struct mrdump_mini_elf_misc misc;
|
|
unsigned long task_info_va =
|
|
(unsigned long)((void *)mrdump_mini_ehdr + MRDUMP_MINI_HEADER_SIZE);
|
|
unsigned long task_info_pa = 0;
|
|
|
|
if (mrdump_mini_addr
|
|
&& mrdump_mini_size
|
|
&& MRDUMP_MINI_HEADER_SIZE < mrdump_mini_size) {
|
|
task_info_pa = (unsigned long)(mrdump_mini_addr +
|
|
MRDUMP_MINI_HEADER_SIZE);
|
|
} else {
|
|
pr_notice("minirdump: unexpected addr:0x%x, size:0x%x(0x%x)\n",
|
|
mrdump_mini_addr, mrdump_mini_size,
|
|
(unsigned int)MRDUMP_MINI_HEADER_SIZE);
|
|
mrdump_mini_fatal("illegal addr size");
|
|
}
|
|
mrdump_mini_add_misc_pa(task_info_va, task_info_pa,
|
|
sizeof(struct aee_process_info), 0, "PROC_CUR_TSK");
|
|
memset_io(&misc, 0, sizeof(struct mrdump_mini_elf_misc));
|
|
/* could also use the kernel log in pstore for LKM case */
|
|
misc.vaddr = (unsigned long)aee_log_buf_addr_get();
|
|
misc.size = (unsigned long)aee_log_buf_len_get();
|
|
mrdump_mini_add_misc(misc.vaddr, misc.size, misc.start, "_KERNEL_LOG_");
|
|
memset_io(&misc, 0, sizeof(struct mrdump_mini_elf_misc));
|
|
get_mbootlog_buffer(&misc.vaddr, &misc.size, &misc.start);
|
|
mrdump_mini_add_misc(misc.vaddr, misc.size, misc.start, "_LAST_KMSG");
|
|
memset_io(&misc, 0, sizeof(struct mrdump_mini_elf_misc));
|
|
aee_rr_get_desc_info(&misc.vaddr, &misc.size, &misc.start);
|
|
mrdump_mini_add_misc(misc.vaddr, misc.size, misc.start, "_RR_DESC_");
|
|
#if IS_ENABLED(CONFIG_HAVE_MTK_GZ_LOG)
|
|
memset_io(&misc, 0, sizeof(struct mrdump_mini_elf_misc));
|
|
get_gz_log_buffer(&misc.vaddr, &misc.paddr, &misc.size, &misc.start);
|
|
if (misc.paddr)
|
|
mrdump_mini_add_misc_pa(misc.vaddr, misc.paddr, misc.size,
|
|
misc.start, "_GZ_LOG_");
|
|
#endif
|
|
memset_io(&misc, 0, sizeof(struct mrdump_mini_elf_misc));
|
|
get_disp_err_buffer(&misc.vaddr, &misc.size, &misc.start);
|
|
mrdump_mini_add_misc(misc.vaddr, misc.size, misc.start, "_DISP_ERR_");
|
|
memset_io(&misc, 0, sizeof(struct mrdump_mini_elf_misc));
|
|
get_disp_dump_buffer(&misc.vaddr, &misc.size, &misc.start);
|
|
mrdump_mini_add_misc(misc.vaddr, misc.size, misc.start, "_DISP_DUMP_");
|
|
memset_io(&misc, 0, sizeof(struct mrdump_mini_elf_misc));
|
|
get_disp_fence_buffer(&misc.vaddr, &misc.size, &misc.start);
|
|
mrdump_mini_add_misc(misc.vaddr, misc.size, misc.start, "_DISP_FENCE_");
|
|
memset_io(&misc, 0, sizeof(struct mrdump_mini_elf_misc));
|
|
get_disp_dbg_buffer(&misc.vaddr, &misc.size, &misc.start);
|
|
mrdump_mini_add_misc(misc.vaddr, misc.size, misc.start, "_DISP_DBG_");
|
|
#ifdef CONFIG_MODULES
|
|
modules_info_buf = kzalloc(MODULES_INFO_BUF_SIZE, GFP_KERNEL);
|
|
if (modules_info_buf)
|
|
mrdump_mini_add_misc_pa((unsigned long)modules_info_buf,
|
|
(unsigned long)__pa_nodebug(
|
|
(unsigned long)modules_info_buf),
|
|
MODULES_INFO_BUF_SIZE, 0, "SYS_MODULES_INFO");
|
|
#endif
|
|
|
|
memset_io(&misc, 0, sizeof(struct mrdump_mini_elf_misc));
|
|
get_pidmap_aee_buffer(&misc.vaddr, &misc.size);
|
|
misc.start = 0;
|
|
mrdump_mini_add_misc(misc.vaddr, misc.size, misc.start, "_PIDMAP_");
|
|
|
|
#ifndef MODULE
|
|
memset_io(&misc, 0, sizeof(struct mrdump_mini_elf_misc));
|
|
misc.vaddr = (unsigned long)(void *)linux_banner;
|
|
misc.size = strlen(linux_banner);
|
|
misc.start = 0;
|
|
mrdump_mini_add_misc(misc.vaddr, misc.size, misc.start, "_VERSION_BR");
|
|
#endif
|
|
}
|
|
|
|
static void mrdump_mini_clear_loads(void)
|
|
{
|
|
struct elf_phdr *phdr;
|
|
int i;
|
|
|
|
for (i = 0; i < MRDUMP_MINI_NR_SECTION; i++) {
|
|
phdr = &mrdump_mini_ehdr->phdrs[i];
|
|
if (phdr->p_type == PT_NULL)
|
|
continue;
|
|
if (phdr->p_type == PT_LOAD)
|
|
phdr->p_type = PT_NULL;
|
|
}
|
|
}
|
|
|
|
void mrdump_mini_add_hang_raw(unsigned long vaddr, unsigned long size)
|
|
{
|
|
pr_notice("mrdump: hang data 0x%lx size:0x%lx\n", vaddr, size);
|
|
if (!mrdump_mini_ehdr) {
|
|
pr_notice("mrdump: ehdr invalid");
|
|
return;
|
|
}
|
|
mrdump_mini_add_misc(vaddr, size, 0, "_HANG_DETECT_");
|
|
/* hang only remove mini rdump loads info to save storage space */
|
|
mrdump_mini_clear_loads();
|
|
}
|
|
EXPORT_SYMBOL(mrdump_mini_add_hang_raw);
|
|
|
|
void mrdump_mini_ke_cpu_regs(struct pt_regs *regs)
|
|
{
|
|
mrdump_mini_build_task_info(regs);
|
|
}
|
|
|
|
static void *remap_lowmem(phys_addr_t start, phys_addr_t size)
|
|
{
|
|
struct page **pages;
|
|
phys_addr_t page_start;
|
|
unsigned int page_count;
|
|
unsigned int i;
|
|
void *vaddr;
|
|
|
|
page_start = start - offset_in_page(start);
|
|
page_count = DIV_ROUND_UP(size + offset_in_page(start), PAGE_SIZE);
|
|
|
|
pages = kmalloc_array(page_count, sizeof(struct page *), GFP_KERNEL);
|
|
if (!pages)
|
|
return NULL;
|
|
|
|
for (i = 0; i < page_count; i++) {
|
|
phys_addr_t addr = page_start + i * PAGE_SIZE;
|
|
|
|
pages[i] = pfn_to_page(addr >> PAGE_SHIFT);
|
|
}
|
|
vaddr = vmap(pages, page_count, VM_MAP, PAGE_KERNEL);
|
|
kfree(pages);
|
|
if (!vaddr) {
|
|
pr_notice("%s: Failed to map %u pages\n", __func__, page_count);
|
|
return NULL;
|
|
}
|
|
|
|
return vaddr + offset_in_page(start);
|
|
}
|
|
|
|
static void __init mrdump_mini_elf_header_init(void)
|
|
{
|
|
if (mrdump_mini_addr && mrdump_mini_size) {
|
|
mrdump_mini_ehdr =
|
|
remap_lowmem(mrdump_mini_addr,
|
|
mrdump_mini_size);
|
|
pr_notice("minirdump: [DT] reserved 0x%x+0x%lx->%p\n",
|
|
mrdump_mini_addr,
|
|
(unsigned long)mrdump_mini_size,
|
|
mrdump_mini_ehdr);
|
|
} else {
|
|
pr_notice("minirdump: [DT] illegal value 0x%x(0x%x)\n",
|
|
mrdump_mini_addr,
|
|
mrdump_mini_size);
|
|
mrdump_mini_fatal("illegal addr size");
|
|
return;
|
|
}
|
|
if (!mrdump_mini_ehdr) {
|
|
pr_notice("mrdump mini reserve buffer fail");
|
|
mrdump_mini_fatal("header null pointer");
|
|
return;
|
|
}
|
|
memset_io(mrdump_mini_ehdr, 0, MRDUMP_MINI_HEADER_SIZE +
|
|
sizeof(struct aee_process_info));
|
|
fill_elf_header(&mrdump_mini_ehdr->ehdr, MRDUMP_MINI_NR_SECTION);
|
|
}
|
|
|
|
int __init mrdump_mini_init(const struct mrdump_params *mparams)
|
|
{
|
|
int i, cpu;
|
|
unsigned long size, offset, vaddr;
|
|
struct pt_regs regs;
|
|
|
|
mrdump_mini_elf_header_init();
|
|
if (!mrdump_mini_ehdr) {
|
|
pr_notice("mrdump: mini init fail\n");
|
|
return -1;
|
|
}
|
|
fill_psinfo(&mrdump_mini_ehdr->psinfo.data);
|
|
fill_note_S(&mrdump_mini_ehdr->psinfo.note, "vmlinux", NT_PRPSINFO,
|
|
sizeof(struct elf_prpsinfo));
|
|
|
|
memset_io(®s, 0, sizeof(struct pt_regs));
|
|
for (i = 0; i < AEE_MTK_CPU_NUMS; i++) {
|
|
fill_prstatus(&mrdump_mini_ehdr->prstatus[i].data, ®s,
|
|
NULL, i);
|
|
fill_note_S(&mrdump_mini_ehdr->prstatus[i].note, "NA",
|
|
NT_PRSTATUS, sizeof(struct elf_prstatus));
|
|
}
|
|
|
|
offset = offsetof(struct mrdump_mini_elf_header, psinfo);
|
|
size = sizeof(mrdump_mini_ehdr->psinfo) +
|
|
sizeof(mrdump_mini_ehdr->prstatus);
|
|
fill_elf_note_phdr(&mrdump_mini_ehdr->phdrs[0], size, offset);
|
|
|
|
for (i = 0; i < MRDUMP_MINI_NR_MISC; i++)
|
|
fill_note_L(&mrdump_mini_ehdr->misc[i].note, "NA", 0,
|
|
sizeof(struct mrdump_mini_elf_misc));
|
|
mrdump_mini_build_elf_misc();
|
|
fill_elf_note_phdr(&mrdump_mini_ehdr->phdrs[1],
|
|
sizeof(mrdump_mini_ehdr->misc),
|
|
offsetof(struct mrdump_mini_elf_header, misc));
|
|
|
|
if (mrdump_cblock) {
|
|
mrdump_mini_add_misc_pa((unsigned long)mrdump_cblock,
|
|
mparams->cb_addr, mparams->cb_size,
|
|
0, MRDUMP_MINI_MISC_LOAD);
|
|
|
|
vaddr = aee_get_kallsyms_addresses();
|
|
vaddr = round_down(vaddr, PAGE_SIZE);
|
|
size = mrdump_cblock->machdesc.kallsyms.size;
|
|
size = round_up(size, PAGE_SIZE);
|
|
if (vaddr)
|
|
mrdump_mini_add_misc_pa(vaddr, __pa_nodebug(vaddr),
|
|
size, 0, MRDUMP_MINI_MISC_LOAD);
|
|
}
|
|
|
|
vaddr = round_down((unsigned long)__per_cpu_offset, PAGE_SIZE);
|
|
mrdump_mini_add_misc_pa(vaddr, __pa_nodebug(vaddr),
|
|
PAGE_SIZE * 2, 0, MRDUMP_MINI_MISC_LOAD);
|
|
|
|
for (cpu = 0; cpu < nr_cpu_ids; cpu++) {
|
|
vaddr = (unsigned long)aee_cpu_rq(cpu);
|
|
vaddr = round_down(vaddr, PAGE_SIZE);
|
|
mrdump_mini_add_misc(vaddr, MRDUMP_MINI_SECTION_SIZE,
|
|
0, MRDUMP_MINI_MISC_LOAD);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int mini_rdump_reserve_memory(struct reserved_mem *rmem)
|
|
{
|
|
pr_info("[memblock]%s: 0x%llx - 0x%llx (0x%llx)\n",
|
|
"mediatek,minirdump",
|
|
(unsigned long long)rmem->base,
|
|
(unsigned long long)rmem->base +
|
|
(unsigned long long)rmem->size,
|
|
(unsigned long long)rmem->size);
|
|
return 0;
|
|
}
|
|
|
|
RESERVEDMEM_OF_DECLARE(reserve_memory_minirdump, "mediatek,minirdump",
|
|
mini_rdump_reserve_memory);
|