| /* |
| * 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; |
| } |