blob: 1f1fa609a2e9ae2db3aa7f5aa5b8c6d0410f34d6 [file] [log] [blame]
/*
* Copyright (c) 2018 MediaTek Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <arch/mmu.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <lib/bio.h>
#include <lib/cksum.h>
#include <lib/mempool.h>
#include <lib/partition.h>
#include <platform/mt_reg_base.h>
#include <platform/mtk_bio_ioctl.h>
#include <platform/mtk_wdt.h>
#include <boot_args.h>
#include "KEHeader.h"
#include "elf.h"
#include "ram_console.h"
#ifdef MTK_3LEVEL_PAGETABLE
#include <target.h>
#endif
#ifdef MTK_MRDUMP_SRAM_CB
#include <platform/mtk_mrdump.h>
#endif
#include <err.h>
#include <boot_mode.h>
extern BOOT_ARGUMENT *g_boot_arg;
extern void arch_clean_cache_range(addr_t start, size_t len);
enum {
AEE_LKDUMP_CLEAR = 0,
AEE_LKDUMP_RAMCONSOLE_RAW,
AEE_LKDUMP_PSTORE_RAW,
AEE_LKDUMP_KEDUMP_CRC,
AEE_LKDUMP_MINI_RDUMP,
AEE_LKDUMP_PROC_CUR_TSK, //5
AEE_LKDUMP_KERNEL_LOG_RAW,
AEE_LKDUMP_DISP_DEBUG_RAW,
AEE_LKDUMP_DFD20,
AEE_LKDUMP_LAST_DRAM,
AEE_LKDUMP_LAST_CPU_BUS, //10
AEE_LKDUMP_LAST_SPM_DATA,
AEE_LKDUMP_LAST_SPM_SRAM_DATA,
AEE_LKDUMP_ATF_LAST,
AEE_LKDUMP_ATF_CRASH,
AEE_LKDUMP_ATF_RAW,
AEE_LKDUMP_ATF_RDUMP, //16
AEE_LKDUMP_CPU_HVFS_RAW,
AEE_LKDUMP_SSPM_COREDUMP,
AEE_LKDUMP_SSPM_DATA,
AEE_LKDUMP_SSPM_XFILE,
AEE_LKDUMP_SSPM_LAST_LOG, //21
AEE_LKDUMP_PLLK_LAST_LOG,
AEE_LKDUMP_MCDI_DATA,
AEE_LKDUMP_SCP_COREDUMP,
AEE_LKDUMP_ZAEE_LOG,
AEE_LKDUMP_HEADER, //26
AEE_LKDUMP_UNKNOWN
};
enum {
AEE_PLAT_DFD20,
AEE_PLAT_DRAM,
AEE_PLAT_CPU_BUS,
AEE_PLAT_SPM_DATA,
AEE_PLAT_SPM_SRAM_DATA,
AEE_PLAT_ATF_LAST_LOG,
AEE_PLAT_ATF_CRASH_REPORT,
AEE_PLAT_ATF_RAW_LOG,
AEE_PLAT_ATF_RDUMP_LOG,
AEE_PLAT_HVFS,
#ifdef MTK_TINYSYS_SSPM_SUPPORT
AEE_PLAT_SSPM_COREDUMP,
AEE_PLAT_SSPM_DATA,
AEE_PLAT_SSPM_XFILE,
AEE_PLAT_SSPM_LAST_LOG,
#endif
AEE_PLAT_PLLK_LAST_LOG,
AEE_PLAT_LOG_DUR_LKDUMP,
AEE_PLAT_MCDI_DATA,
AEE_PLAT_SCP_COREDUMP,
AEE_PLAT_DEBUG_NUM
};
/* db filename and max size */
struct aee_db_file_info {
char filename[32];
unsigned int filesize;
unsigned int step;
};
static struct aee_db_file_info adfi[AEE_PLAT_DEBUG_NUM] = {
[AEE_PLAT_DFD20] = { "DFD20.dfd", 0x40000, AEE_LKDUMP_DFD20}, /* 256 KB */
[AEE_PLAT_DRAM] = { "SYS_LAST_DRAM", 0x2400, AEE_LKDUMP_LAST_DRAM}, /* 9 KB */
[AEE_PLAT_CPU_BUS] = { "SYS_LAST_CPU_BUS", 0x10000, AEE_LKDUMP_LAST_CPU_BUS}, /* 64 KB */
[AEE_PLAT_SPM_DATA] = { "SYS_LAST_SPM_DATA", 0x1000, AEE_LKDUMP_LAST_SPM_DATA}, /* 4 KB */
[AEE_PLAT_SPM_SRAM_DATA] = { "SYS_LAST_SPM_SRAM_DATA", 0x1000, AEE_LKDUMP_LAST_SPM_SRAM_DATA}, /* 4 KB */
[AEE_PLAT_ATF_LAST_LOG] = { "SYS_ATF_LAST", 0x20000, AEE_LKDUMP_ATF_LAST}, /* 128KB */
[AEE_PLAT_ATF_CRASH_REPORT] = { "SYS_ATF_CRASH", 0x30000, AEE_LKDUMP_ATF_CRASH}, /* 64KB+128KB */
[AEE_PLAT_ATF_RAW_LOG] = { "SYS_ATF_RAW_LOG", 0x60000, AEE_LKDUMP_ATF_RAW}, /* 384 KB */
[AEE_PLAT_ATF_RDUMP_LOG] = { "SYS_ATF_RDUMP", 0x80000, AEE_LKDUMP_ATF_RDUMP }, /* 512KB */
[AEE_PLAT_HVFS] = { "SYS_CPUHVFS_RAW", 0x3000, AEE_LKDUMP_CPU_HVFS_RAW}, /* 12 KB */
#ifdef MTK_TINYSYS_SSPM_SUPPORT
[AEE_PLAT_SSPM_COREDUMP] = { "SYS_SSPM_COREDUMP", 0x40080, AEE_LKDUMP_SSPM_COREDUMP}, /* 256KB + 128Byte */
[AEE_PLAT_SSPM_DATA] = { "SYS_SSPM_DATA", 0x400, AEE_LKDUMP_SSPM_DATA}, /* 1KB */
[AEE_PLAT_SSPM_XFILE] = { "SYS_SSPM_XFILE", 0xA0000, AEE_LKDUMP_SSPM_XFILE}, /* 640KB */
[AEE_PLAT_SSPM_LAST_LOG] = { "SYS_SSPM_LAST_LOG", 0x400, AEE_LKDUMP_SSPM_LAST_LOG}, /* 1KB */
#endif
[AEE_PLAT_PLLK_LAST_LOG] = { "SYS_PLLK_LAST_LOG", 0x40000, AEE_LKDUMP_PLLK_LAST_LOG}, /* 256KB */
[AEE_PLAT_LOG_DUR_LKDUMP] = { "SYS_LOG_DUR_LKDUMP", 0x40000, AEE_PLAT_LOG_DUR_LKDUMP}, /* 256KB */
[AEE_PLAT_MCDI_DATA] = { "SYS_MCDI_DATA", 0x800, AEE_LKDUMP_MCDI_DATA}, /* 2KB, size will modified by plat. */
#ifdef MTK_TINYSYS_SCP_SUPPORT
[AEE_PLAT_SCP_COREDUMP] = { "SYS_SCP_DUMP.gz", 0xA0000, AEE_LKDUMP_SCP_COREDUMP} /* 640KB */
#endif
};
struct aee_db_file_info *get_file_info(void)
{
return adfi;
}
#define EXPDB_RESERVED_OTHER (3 * 1024 * 1024) //reserved expdb for control block and pl/lk log
#define BIO_ERASE_ALIGN 0x40000
#define MEM_EXPDB_SIZE 0x300000
static char *mem_expdb;
static bdev_t *bdev;
struct elfhdr {
void *start;
unsigned int e_machine;
unsigned int e_phoff;
unsigned int e_phnum;
};
struct kedump_crc {
unsigned int ram_console_crc;
unsigned int pstore_crc;
};
static struct kedump_crc kc;
int check_ram_console_is_abnormal_boot(void)
{
return ram_console_is_abnormal_boot();
}
static unsigned int last_dump_step;
#define elf_note elf32_note
#define PHDR_PTR(ehdr, phdr, mem) \
(ehdr->e_machine == EM_ARM ? ((struct elf32_phdr*)phdr)->mem : ((struct elf64_phdr*)phdr)->mem)
#define PHDR_TYPE(ehdr, phdr) PHDR_PTR(ehdr, phdr, p_type)
#define PHDR_VADDR(ehdr, phdr) PHDR_PTR(ehdr, phdr, p_vaddr)
#define PHDR_ADDR(ehdr, phdr) PHDR_PTR(ehdr, phdr, p_paddr)
#define PHDR_SIZE(ehdr, phdr) PHDR_PTR(ehdr, phdr, p_filesz)
#define PHDR_OFF(ehdr, phdr) PHDR_PTR(ehdr, phdr, p_offset)
#define PHDR_INDEX(ehdr, i) \
(ehdr->e_machine == EM_ARM ? ehdr->start + ehdr->e_phoff + sizeof(struct elf32_phdr) * i : \
ehdr->start + ehdr->e_phoff + sizeof(struct elf64_phdr) *i)
#ifndef ALIGN
#define ALIGN(x, a) (((x) + ((a) -1)) & ~((a) -1))
#endif
static unsigned int calculate_crc32(void *data, unsigned int len)
{
unsigned int crc_value = 0;
crc_value = crc32(0L, data, len);
LOG("kedump: crc = 0x%x\n", crc_value);
return crc_value;
}
static struct elfhdr *kedump_elf_hdr(void)
{
char *ei;
static struct elfhdr kehdr;
static struct elfhdr *ehdr = (void *)-1;
if (ehdr != (void *)-1)
return ehdr;
ehdr = NULL;
kehdr.start = PA_TO_VA((paddr_t)KE_RESERVED_MEM_ADDR);
LOG("kedump: KEHeader %p\n", kehdr.start);
if (kehdr.start) {
ei = (char *)kehdr.start; //elf_hdr.e_ident
LOG("kedump: read header 0x%p[0x%x%x%x%x]\n", ei, ei[0], ei[1], ei[2], ei[3]);
/* valid elf header */
if (ei[0] == 0x7f && ei[1] == 'E' && ei[2] == 'L' && ei[3] == 'F') {
kehdr.e_machine = ((struct elf32_hdr *)(kehdr.start))->e_machine;
if (kehdr.e_machine == EM_ARM) {
kehdr.e_phnum = ((struct elf32_hdr *)(kehdr.start))->e_phnum;
kehdr.e_phoff = ((struct elf32_hdr *)(kehdr.start))->e_phoff;
ehdr = &kehdr;
} else if (kehdr.e_machine == EM_AARCH64) {
kehdr.e_phnum = ((struct elf64_hdr *)(kehdr.start))->e_phnum;
kehdr.e_phoff = ((struct elf64_hdr *)(kehdr.start))->e_phoff;
ehdr = &kehdr;
}
}
if (ehdr == NULL)
LOG("kedump: invalid header[0x%x%x%x%x]\n", ei[0], ei[1], ei[2], ei[3]);
}
LOG("kedump: mach[0x%x], phnum[0x%x], phoff[0x%x]\n", kehdr.e_machine, kehdr.e_phnum,
kehdr.e_phoff);
return ehdr;
}
static int kedump_dev_open(void)
{
bdev = bio_open_by_label(AEE_IPANIC_PLABLE);
if (!bdev) {
bdev = bio_open(AEE_IPANIC_PLABLE);
if (!bdev) {
LOG("kedump: no %s partition\n", AEE_IPANIC_PLABLE);
return -1;
}
}
if (bdev->total_size < EXPDB_RESERVED_OTHER) {
LOG("kedump: partition size(%llx) is lesser then reserved!(%llx)\n", bdev->total_size,
(unsigned long long)EXPDB_RESERVED_OTHER);
return -1;
}
closebdev:
bio_close(bdev);
return 0;
}
static unsigned long long mem_expdb_write(void *data, unsigned long long offset, unsigned long sz)
{
if ((offset + sz) > MEM_EXPDB_SIZE) {
LOG("kedump: mem_expdb_write overflow!\n");
return 0;
}
memcpy(mem_expdb + offset, data, sz);
return sz;
}
#define TRUNK 0x8000
static unsigned long long kedump_dev_write(unsigned long long offset, uint64_t data, unsigned long sz)
{
unsigned long long size_wrote = 0;
uint8_t *memsrc = (uint8_t *)data;
unsigned long rest = sz;
unsigned int ret = 0;
unsigned long long mini_size = bdev->total_size - EXPDB_RESERVED_OTHER;
LOG("kedump: offset:0x%llx, data:0x%llx, size:0x%lx\n", offset, data, sz);
if (offset >= mini_size || sz > (mini_size - offset)) {
LOG("kedump: kedump_dev_write overflow\n");
return 0;
}
while (rest > 0) {
unsigned long write_sz;
if (rest <= TRUNK) {
write_sz = rest;
} else {
write_sz = TRUNK;
}
LOG("kedump: offset:0x%llx, memsrc:0x%lx, write_sz:0x%lx\n", offset, (size_t)memsrc, write_sz);
ret = mem_expdb_write(memsrc, offset, write_sz);
if (ret <= 0) {
LOG("kedump: mem_expdb_write fail\n");
break;
}
size_wrote += ret;
offset += write_sz;
rest -= write_sz;
memsrc += write_sz;
}
if ((long long)size_wrote <= 0) {
LOG("kedump: write failed(%llx), %lx@%llx -> %llx\n", size_wrote, sz, data, offset);
size_wrote = 0;
}
LOG("kedump: kedump_dev_write done\n");
return size_wrote;
}
static unsigned long long offset_plat_debug = 0;
static unsigned long length_plat_debug = 0;
static unsigned long long kedump_plat_write (void *data, unsigned long sz)
{
unsigned long long datasize = 0;
datasize = kedump_dev_write(offset_plat_debug, (uint64_t)data, sz);
offset_plat_debug += datasize;
length_plat_debug += sz;
return datasize;
}
static void kedump_dev_close(void)
{
return;
}
/* the min offset reserved for the header's size. */
static unsigned long kedump_mrdump_header_size (struct elfhdr *ehdr)
{
void *phdr = PHDR_INDEX(ehdr, 1);
return ALIGN(PHDR_OFF(ehdr, phdr) + PHDR_SIZE(ehdr, phdr), PAGE_SIZE);
}
static unsigned int kedump_mini_rdump(struct elfhdr *ehdr, unsigned long long offset)
{
void *phdr;
unsigned long long addr;
void *vaddr;
unsigned long size;
unsigned int i;
unsigned int total = 0;
unsigned long elfoff = kedump_mrdump_header_size(ehdr);
unsigned long sz_header = elfoff;
for (i = 0; i < ehdr->e_phnum; i++) {
phdr = PHDR_INDEX(ehdr, i);
if (PHDR_SIZE(ehdr, phdr) != 0 || PHDR_TYPE(ehdr, phdr) != 0)
LOGD("kedump: PT[%d] %x@%llx -> %x(%x)\n", PHDR_TYPE(ehdr, phdr), PHDR_SIZE(ehdr, phdr),
PHDR_ADDR(ehdr, phdr), elfoff, (unsigned int)PHDR_OFF(ehdr, phdr));
if (PHDR_TYPE(ehdr, phdr) != PT_LOAD)
continue;
addr = PHDR_ADDR(ehdr, phdr);
#ifdef MTK_MRDUMP_SRAM_CB
if ((addr < DRAM_BASE_PHY) && ((addr < MRDUMP_CB_ADDR) ||
(addr > (MRDUMP_CB_ADDR + MRDUMP_CB_SIZE)))) {
LOG("kedump: skip dump non-allow PA:%llx, VA:%llx\n", addr, PHDR_VADDR(ehdr, phdr));
continue;
}
#else
if (addr < DRAM_BASE_PHY) {
LOG("kedump: skip dump non-dram PA:%llx, VA:%llx\n", addr, PHDR_VADDR(ehdr, phdr));
continue;
}
#endif
size = PHDR_SIZE(ehdr, phdr);
if (size == 0 || elfoff == 0)
LOG("kedump: dump addr 0x%llx, size 0x%lx\n", addr, size);
if (ehdr->e_machine == EM_ARM)
((struct elf32_phdr *)phdr)->p_offset = elfoff;
else
((struct elf64_phdr *)phdr)->p_offset = elfoff;
if (size != 0 && elfoff != 0) {
vaddr = PA_TO_VA((paddr_t)addr);
total += kedump_dev_write(offset + elfoff, (uint64_t)vaddr, size);
}
elfoff += size;
}
total += kedump_dev_write(offset, (uint64_t)(ehdr->start), sz_header);
return total;
}
static unsigned int kedump_misc(unsigned long long addr, unsigned int start, unsigned int size,
unsigned long long offset)
{
unsigned int total;
LOG("kedump: misc data %x@%llx+%x\n", size, addr, start);
if (start >= size)
start = start % size;
total = kedump_dev_write(offset, (uint64_t)(addr + start), size - start);
if (start)
total += kedump_dev_write(offset + total, (uint64_t)addr, start);
return total;
}
static unsigned int kedump_misc32(struct mrdump_mini_misc_data32 *data, unsigned long long offset)
{
if (data != NULL) {
unsigned int addr = data->paddr;
unsigned int start = 0;
void *vaddr;
vaddr = PA_TO_VA((paddr_t)addr);
start = data->start ? *(unsigned int *)(unsigned long)(PA_TO_VA(data->start)) : 0;
unsigned int size = data->size;
return kedump_misc((uint64_t)vaddr, start, size, offset);
} else
return 0;
}
static unsigned int kedump_misc64(struct mrdump_mini_misc_data64 *data, unsigned long long offset)
{
if (data != NULL) {
unsigned long long addr = (unsigned long long)data->paddr;
unsigned int start = 0;
void *vaddr;
vaddr = PA_TO_VA((paddr_t)addr);
start = data->start ? *(unsigned int *)(unsigned long)(PA_TO_VA((paddr_t)data->start)) : 0;
unsigned int size = (unsigned int)data->size;
return kedump_misc((uint64_t)vaddr, start, size, offset);
} else
return 0;
}
struct ipanic_header panic_header;
static unsigned long long header_off;
static void kedump_add2hdr(unsigned int offset, unsigned int size, unsigned datasize,
const char *name)
{
struct ipanic_data_header *pdata;
int i;
for (i = 0; i < IPANIC_NR_SECTIONS; i++) {
pdata = &panic_header.data_hdr[i];
if (pdata->valid == 0)
break;
}
LOG("kedump add: %s[%d] %x/%x@%x\n", name, i, datasize, size, offset);
if (i < IPANIC_NR_SECTIONS) {
pdata->offset = offset;
pdata->total = size;
pdata->used = datasize;
strlcpy((char *)pdata->name, name, sizeof(pdata->name));
pdata->valid = 1;
}
header_off += kedump_dev_write(header_off, (uint64_t)(pdata), sizeof(struct ipanic_data_header));
}
static int kedump_kernel_info(unsigned long long *offset)
{
struct elfhdr *ehdr;
unsigned long sz_misc;
void *phdr_misc;
struct elf_note *misc, *miscs;
char *m_name;
void *m_data;
char name[32];
unsigned int size, datasize;
unsigned int i;
unsigned int start_tmp;
ehdr = kedump_elf_hdr();
if (0 == ehdr)
return -1;
ram_console_set_dump_step(AEE_LKDUMP_MINI_RDUMP);
datasize = kedump_mini_rdump(ehdr, *offset);
size = datasize;
kedump_add2hdr(*offset, size, datasize, "SYS_MINI_RDUMP");
*offset += datasize;
phdr_misc = PHDR_INDEX(ehdr, 1);
miscs = (struct elf_note *)(ehdr->start + PHDR_OFF(ehdr, phdr_misc));
LOGD("kedump: misc[%p] %llx@%llx\n", phdr_misc, PHDR_SIZE(ehdr, phdr_misc),
PHDR_OFF(ehdr, phdr_misc));
sz_misc = sizeof(struct elf_note) + miscs->n_namesz + miscs->n_descsz;
LOGD("kedump: miscs[%p], size %x\n", miscs, sz_misc);
for (i = 0; i < (PHDR_SIZE(ehdr, phdr_misc)) / sz_misc; i++) {
char klog_first[16];
memset(klog_first, 0x0, sizeof(klog_first));
misc = (struct elf_note *)((void *)miscs + sz_misc * i);
m_name = (char *)misc + sizeof(struct elf_note);
if (m_name[0] == 'N' && m_name[1] == 'A' && m_name[2] == '\0')
break;
m_data = (void *)misc + sizeof(struct elf_note) + misc->n_namesz;
if (misc->n_descsz == sizeof(struct mrdump_mini_misc_data32)) {
if (strcmp(m_name, "_KERNEL_LOG_") == 0) {
start_tmp = ((struct mrdump_mini_misc_data32 *)m_data)->start;
sprintf(klog_first, "_%u", start_tmp ? *(unsigned int *)
(PA_TO_VA((paddr_t)start_tmp)) : 0);
((struct mrdump_mini_misc_data32 *)m_data)->start = 0;
}
datasize = kedump_misc32((struct mrdump_mini_misc_data32 *)m_data, *offset);
size = ((struct mrdump_mini_misc_data32 *)m_data)->size;
} else {
if (strcmp(m_name, "_KERNEL_LOG_") == 0) {
start_tmp = ((struct mrdump_mini_misc_data64 *)m_data)->start;
sprintf(klog_first, "_%u", start_tmp ? *(unsigned int *)
(PA_TO_VA((paddr_t)start_tmp)) : 0);
((struct mrdump_mini_misc_data64 *)m_data)->start = 0;
}
datasize = kedump_misc64((struct mrdump_mini_misc_data64 *)m_data, *offset);
size = ((struct mrdump_mini_misc_data64 *)m_data)->size;
}
/* [SYS_]MISC[_RAW] */
if (m_name[0] == '_')
strlcpy (name, "SYS", sizeof(name));
else
name[0] = 0;
strlcat (name, m_name, sizeof(name));
if (m_name[strlen(m_name)-1] == '_')
strlcat (name, "RAW", sizeof(name));
if (klog_first[0] != 0)
strlcat(name, klog_first, sizeof(name));
kedump_add2hdr(*offset, size, datasize, name);
*offset += datasize;
}
return 0;
}
static int kedump_ram_console(unsigned long long *offset)
{
unsigned long sz_misc, addr_misc;
unsigned int datasize;
/* ram_console raw log */
ram_console_set_dump_step(AEE_LKDUMP_RAMCONSOLE_RAW);
ram_console_addr_size(&addr_misc, &sz_misc);
if (addr_misc && sz_misc) {
datasize = kedump_misc((unsigned long long)addr_misc, 0, (unsigned int)sz_misc, *offset);
kc.ram_console_crc = calculate_crc32((void *)addr_misc, sz_misc);
kedump_add2hdr(*offset, (unsigned long long)sz_misc, datasize, "SYS_RAMCONSOLE_RAW");
*offset += datasize;
}
#ifdef MTK_PMIC_FULL_RESET
/* pstore raw log*/
ram_console_set_dump_step(AEE_LKDUMP_PSTORE_RAW);
datasize = kedump_misc((unsigned long long)PA_TO_VA((paddr_t)PSTORE_ADDR), 0,
(unsigned int)PSTORE_SIZE, *offset);
kc.pstore_crc = calculate_crc32((void *)PA_TO_VA((paddr_t)PSTORE_ADDR), PSTORE_SIZE);
kedump_add2hdr(*offset, (unsigned int)PSTORE_SIZE, datasize, "SYS_PSTORE_RAW");
*offset += datasize;
#endif
/* save crc data*/
ram_console_set_dump_step(AEE_LKDUMP_KEDUMP_CRC);
datasize = kedump_dev_write(*offset, (uint64_t)(&kc), sizeof(struct kedump_crc));
kedump_add2hdr(*offset, sizeof(struct kedump_crc), datasize, "KEDUMP_CRC");
*offset += datasize;
return 0;
}
#if 0
static int kedump_platform_debug(unsigned long long *offset)
{
/* platform debug */
int len = 0;
unsigned int datasize;
unsigned int i;
for (i=0; i<AEE_PLAT_DEBUG_NUM; i++) {
offset_plat_debug = *offset;
length_plat_debug = 0;
ram_console_set_dump_step(adfi[i].step);
datasize = kedump_plat_savelog(i, offset_plat_debug, &len, kedump_plat_write);
if ((datasize > 0) && (datasize <= adfi[i].filesize)) {
kedump_add2hdr(*offset, length_plat_debug, datasize, adfi[i].filename);
*offset += datasize;
}
}
return 0;
}
#endif
static int kedump_to_expdb(void)
{
unsigned long long offset;
unsigned int datasize;
ssize_t ret;
bool f_rewrite;
if (kedump_dev_open() != 0)
return -1;
last_dump_step = ram_console_get_dump_step();
if (last_dump_step != AEE_LKDUMP_CLEAR) {
LOG("kedump: last lk dump is not finished at step %u\n", last_dump_step);
//return 0;
}
mem_expdb = mempool_alloc(MEM_EXPDB_SIZE, MEMPOOL_ANY);
if (mem_expdb == NULL) {
LOG("kedump: mem_expdb malloc fail!\n");
return -1;
}
LOG("kedump: mem_expdb malloc success %p size is 0x%x\n", mem_expdb, MEM_EXPDB_SIZE);
memset(mem_expdb, 0x0, MEM_EXPDB_SIZE);
//write header firstly
panic_header.magic = AEE_IPANIC_MAGIC;
panic_header.version = AEE_IPANIC_PHDR_VERSION;
panic_header.size = sizeof(panic_header);
panic_header.blksize = bdev->block_size;
panic_header.partsize = bdev->total_size - EXPDB_RESERVED_OTHER;
LOG("kedump: expdb write panic header panic_header addr:0x%llx\n", (uint64_t)(&panic_header));
kedump_dev_write(0, (uint64_t)(&panic_header), sizeof(panic_header));
header_off = sizeof(panic_header) - sizeof(struct ipanic_data_header) * IPANIC_NR_SECTIONS;
LOG("kedump: block size:0x%zx\n", bdev->block_size);
/* reserve space in expdb for panic header */
offset = ALIGN(sizeof(panic_header), bdev->block_size);
kedump_ram_console(&offset);
kedump_kernel_info(&offset);
// kedump_platform_debug(&offset);
/* save KEdump flow logs */
datasize = kedump_dev_write(offset, (uint64_t)logbuf, SZLOG);
kedump_add2hdr(offset, SZLOG, datasize, "ZAEE_LOG");
offset += datasize;
offset = ALIGN(offset, BIO_ERASE_ALIGN);
LOG("kedump: total offset is:0x%llx after align:0x%x\n", offset, BIO_ERASE_ALIGN);
if (offset > MEM_EXPDB_SIZE) {
LOG("kedump: total offset 0x%llx over MEM_EXPDB_SIZE\n", offset);
mempool_free((void *)mem_expdb);
return -1;
}
if (bio_ioctl(bdev, BIO_IOCTL_QUERY_CAP_REWRITABLE, &f_rewrite) != NO_ERROR) {
f_rewrite = false;
}
if (!f_rewrite) {
ret = bio_erase(bdev, 0, offset);
if (ret < 0) {
LOG("kedump: bio_erase mem_expdb 0x%llx fail\n", offset);
mempool_free((void *)mem_expdb);
return -1;
}
}
ret = bio_write(bdev, mem_expdb, 0, offset);
if (ret != offset) {
LOG("kedump: bio_write mem_expdb 0x%llx but only write 0x%zd\n", offset, ret);
}
mempool_free((void *)mem_expdb);
ram_console_set_dump_step(AEE_LKDUMP_CLEAR);
return 0;
}
/*
* kedump_restore_mem() - read ram console data from expdb
*
* this function is used to store ram console data from
* expdb to PSTORE reserved memory.
*
* returns:
* -1: device not supported
*/
int kedump_restore_mem(void)
{
int i;
unsigned int crc;
struct ipanic_header iheader;
struct kedump_crc saved_crc = {0, 0};
unsigned long sz_misc = 0, addr_misc = 0;
unsigned char *temp_ram_console = NULL;
#ifdef MTK_PMIC_FULL_RESET
unsigned char *temp_pstore = NULL;
#endif
#ifdef MTK_PMIC_FULL_RESET
if (ram_console_reboot_by_cold_reset()) {
LOG("kedump: last is full pmic reset!\n");
} else {
LOG("kedump: last is not full pmic reset!\n");
}
#endif
if (kedump_dev_open() != 0)
return -1;
bio_read(bdev, (unsigned char *)&iheader, 0, sizeof(struct ipanic_header));
if (iheader.magic == AEE_IPANIC_MAGIC && iheader.version >= AEE_IPANIC_PHDR_VERSION) {
LOG("kedump: found content in expdb\n");
for (i = IPANIC_NR_SECTIONS - 1; i >= 0; i--) {
if (strncmp((const char *)iheader.data_hdr[i].name, "KEDUMP_CRC",
sizeof("KEDUMP_CRC") - 1) == 0) {
LOG("kedump: read %s from offset 0x%x size 0x%x\n", iheader.data_hdr[i].name,
iheader.data_hdr[i].offset, iheader.data_hdr[i].used);
bio_read(bdev, (uchar *)(&saved_crc), iheader.data_hdr[i].offset,
iheader.data_hdr[i].used);
}
if (strncmp((const char *)iheader.data_hdr[i].name, "SYS_RAMCONSOLE_RAW",
sizeof("SYS_RAMCONSOLE_RAW") - 1) == 0) {
ram_console_addr_size(&addr_misc, &sz_misc);
if (addr_misc && sz_misc) {
temp_ram_console = mempool_alloc(sz_misc, MEMPOOL_ANY);
if (!temp_ram_console) {
LOG("kedump: temp_ram_console alloc fail\n");
} else {
LOG("kedump: read %s from offset 0x%x size 0x%x\n", iheader.data_hdr[i].name,
iheader.data_hdr[i].offset, iheader.data_hdr[i].used);
memset(temp_ram_console, 0x0, sz_misc);
bio_read(bdev, temp_ram_console, iheader.data_hdr[i].offset,
iheader.data_hdr[i].used);
crc = calculate_crc32(temp_ram_console, sz_misc);
if (crc != saved_crc.ram_console_crc) {
LOG("kedump: temp ram_console crc fail\n");
mempool_free(temp_ram_console);
temp_ram_console = NULL;
}
}
} else {
LOG("kedump: ram_console not init\n");
}
}
#ifdef MTK_PMIC_FULL_RESET
else if (strncmp((const char *)iheader.data_hdr[i].name, "SYS_PSTORE_RAW",
sizeof("SYS_PSTORE_RAW") - 1) == 0) {
temp_pstore = mempool_alloc(PSTORE_SIZE, MEMPOOL_ANY);
if (!temp_pstore) {
LOG("kedump: temp_pstore alloc fail\n");
} else {
LOG("kedump: read %s from offset 0x%x size 0x%x\n", iheader.data_hdr[i].name,
iheader.data_hdr[i].offset, iheader.data_hdr[i].used);
memset(temp_pstore, 0x0, PSTORE_SIZE);
bio_read(bdev, temp_pstore, iheader.data_hdr[i].offset,
iheader.data_hdr[i].used);
crc = calculate_crc32(temp_pstore, PSTORE_SIZE);
if (crc != saved_crc.pstore_crc) {
LOG("kedump: pstore crc fail\n");
mempool_free(temp_pstore);
temp_pstore = NULL;
}
}
}
#endif
}
if (ram_console_should_restore(temp_ram_console)) {
LOG("kedump: ram_console_should_restore\n");
memcpy((uchar *)addr_misc, temp_ram_console, sz_misc);
#ifdef MTK_PMIC_FULL_RESET
if (temp_pstore)
memcpy((uchar *)paddr_to_kvaddr((paddr_t)PSTORE_ADDR), temp_pstore, PSTORE_SIZE);
#endif
ram_console_set_dump_step(AEE_LKDUMP_CLEAR);
ram_console_is_abnormal_boot();
}
if (temp_ram_console)
mempool_free(temp_ram_console);
#ifdef MTK_PMIC_FULL_RESET
if (temp_pstore)
mempool_free(temp_pstore);
#endif
}
return 0;
}
static int kedump_avail(void)
{
void *flag = PA_TO_VA((paddr_t)KE_RESERVED_MEM_ADDR);
if (((char *)flag)[0] == 0x81 && ((char *)flag)[1] == 'E' &&
((char *)flag)[2] == 'L' && ((char *)flag)[3] == 'F') {
LOG("kedump: already dumped in lk\n");
return -1;
}
if (((char *)flag)[0] == 0x0 && ((char *)flag)[1] == 'E' &&
((char *)flag)[2] == 'L' && ((char *)flag)[3] == 'F') {
LOG("kedump: already dumped in kernel\n");
return -1;
}
return 0;
}
int kedump_get_data_info(int index, char **name, u32 *offset, u32 *size)
{
struct ipanic_header iheader;
struct ipanic_data_header *pdata;
if (bdev == NULL ) {
if (kedump_dev_open() != 0)
return -1;
}
bio_read(bdev, (unsigned char *)&iheader, 0, sizeof(struct ipanic_header));
if (iheader.magic != AEE_IPANIC_MAGIC || iheader.version < AEE_IPANIC_PHDR_VERSION)
return -2;
if (index < 0 || index >= IPANIC_NR_SECTIONS) {
LOG("kedump: invalid index number:%d\n", index);
return -3;
}
if (name == NULL || offset == NULL || size == NULL) {
LOG("kedump: invalid argument number:%d\n", index);
return -4;
}
pdata = &iheader.data_hdr[index];
if (pdata->valid == 0)
return -5;
*name = (char *)pdata->name;
*offset = pdata->offset;
*size = pdata->used;
return 0;
}
static int kedump_done(void)
{
void *flag = PA_TO_VA((paddr_t)KE_RESERVED_MEM_ADDR);
((char *)flag)[0] = 0x81;
((char *)flag)[1] = 'E';
((char *)flag)[2] = 'L';
((char *)flag)[3] = 'F';
arch_clean_cache_range((vaddr_t)flag, sizeof(struct elfhdr));
return 0;
}
/* in case that platform didn't support smart_reset_check() */
const char *smart_reset_check(void) __attribute__((weak));
const char *smart_reset_check(void)
{
return NULL;
}
/* in case that platform didn't support mtk_wdt_get_last_stage() */
const char *mtk_wdt_get_last_stage(void) __attribute__((weak));
const char *mtk_wdt_get_last_stage(void)
{
return NULL;
}
/* Dump KE infomation to expdb */
/* 1: has expception, 0: has no exception */
int kedump_mini(void)
{
const char *status;
status = smart_reset_check();
if (status != NULL)
LOG("kedump: smart_reset_check %s\n", status);
status = mtk_wdt_get_last_stage();
if (status != NULL)
LOG("kedump: mtk_wdt_get_last_stage %s\n", status);
LOG("kedump mini start\n");
#if 0
if (lkdump_debug_init())
LOG("kedump: lkdump debug init ok\n");
else
LOG("kedump: lkdump debug not ready\n");
#endif
if (g_boot_arg) {
if (!g_boot_arg->ddr_reserve_enable)
LOG("kedump: ddr reserve mode disabled\n");
else
LOG("kedump: ddr reserve mode enabled\n");
if (!g_boot_arg->ddr_reserve_success)
LOG("kedump: ddr reserve mode failed\n");
} else {
LOG("kedump: skip due to null boot arg pointer error\n");
return 0;
}
LOG("kedump avail\n");
if (kedump_avail())
return 0;
LOG("kedump to expdb\n");
kedump_to_expdb();
kedump_done();
LOG("kedump mini done\n");
return 1;
}