blob: 49aeb01cdf003f8ab483384ca3496d6ed7bceca4 [file] [log] [blame] [edit]
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*/
#include <common.h>
#include <asm/amlogic/arch/bl31_apis.h>
#include <asm/amlogic/arch/secure_apb.h>
#include <amlogic/ramdump.h>
#include <amlogic/emmc_partitions.h>
#include <usb.h>
#include <asm/amlogic/arch/regs.h>
#include <asm/global_data.h>
#include <syscon.h>
#define DEBUG_RAMDUMP 0
#define AMLOGIC_KERNEL_PANIC 0x0c
#define AMLOGIC_WATCHDOG_REBOOT 0x0d
#ifdef CONFIG_USB_STORAGE
static void wait_usb_dev(void);
#endif
unsigned long ramdump_base = 0;
unsigned long ramdump_size = 0;
#undef CONFIG_DUMP_COMPRESS_HEAD
#ifdef CONFIG_DUMP_COMPRESS_HEAD
static void dump_info(unsigned int addr, unsigned int size, const char *info)
{
int i = 0;
printf("\nDUMP %s 0X%08x :", info, addr);
for (i = 0; i < size; i += 4) {
if (0 == (i % 32))
printf("\n[0x%08x] ", addr + i);
printf("%08x ", readl(addr + i));
}
printf("\n\n");
}
int parse_dump_file_v2(const char *base, unsigned long size)
{
struct ram_compress_file_header_v2 *fhead;
struct ram_compress_section_header_v2 csh;
struct section_info *seg_tbl;
unsigned long seg_offset;
unsigned int idx;
int cnt = 0, csh_size = 0;
// check ramdump version >= v2.0
if ((!strncmp(base, COMPRESS_TAG_HEAD, COMPRESS_TAG_SIZE - 1)) &&
(!strncmp(base, COMPRESS_TAG_BL3Z, COMPRESS_TAG_SIZE - 1))) {
printf("# Check the crash file head error, exit.\n");
return -1;
}
// dump crash section head
fhead = (struct ram_compress_file_header_v2 *)base;
printf("# total number of segment is %d\n", fhead->section_count);
if (fhead->section_count > 10) {
printf("# crash file head error. exit.\n");
return -1;
}
seg_tbl = malloc(sizeof(struct section_info) * fhead->section_count);
printf("# total compress file size is 0x%08lx\n", fhead->file_size);
csh_size = sizeof(struct ram_compress_section_header_v2);
seg_offset = (unsigned long)fhead + sizeof(struct ram_compress_file_header_v2);
printf("# crash file fhead offset is 0x%08lx\n", seg_offset);
for (cnt = 0; cnt < fhead->section_count; cnt++) {
memset(&csh, 0, sizeof(csh));
if (seg_offset > ((unsigned long)base + size))
continue;
memcpy((void *)&csh, (char *)seg_offset, sizeof(csh));
if (csh.section_index > fhead->section_count) {
printf("csh.section_index(%d) > fhead->section_count(%d), error, exit.\n",
csh.section_index, fhead->section_count);
return -1;
}
if (csh.compress_type == RAM_COMPRESS_ERROR ||
csh.compress_type > RAM_COMPRESS_MAX)
continue;
idx = csh.section_index - 1;
seg_tbl[idx].offset = seg_offset + sizeof(csh);
seg_tbl[idx].type = csh.compress_type;
seg_tbl[idx].raw_size = csh.raw_size;
seg_tbl[idx].zip_size = csh.zip_size;
seg_tbl[idx].val = csh.set_value;
seg_offset += csh.zip_size;
}
// dump section table
printf("\n-----------------------------------------------------------\n");
printf("idx offset type orig size zip size val\n");
printf("-----------------------------------------------------------\n");
for (cnt = 0; cnt < fhead->section_count; cnt++) {
if (seg_tbl[cnt].type == RAM_COMPRESS_ERROR ||
seg_tbl[cnt].type > RAM_COMPRESS_MAX)
continue;
printf(" %2d, 0x%08lx, %2u, 0x%08lx, 0x%08lx, 0x%02x\n",
cnt + 1, seg_tbl[cnt].offset,
seg_tbl[cnt].type, seg_tbl[cnt].raw_size,
seg_tbl[cnt].zip_size, seg_tbl[cnt].val);
}
printf("-----------------------------------------------------------\n");
// printf section table
for (cnt = 0; cnt < fhead->section_count; cnt++) {
if (seg_tbl[cnt].type == RAM_COMPRESS_ERROR ||
seg_tbl[cnt].type > RAM_COMPRESS_MAX)
continue;
dump_info((unsigned int)seg_tbl[cnt].offset - csh_size, csh_size, "crash section head");
}
return 0;
}
#endif
static int compare_memory_regions(unsigned int *addr1,
unsigned int *addr2,
unsigned int total_size,
unsigned int block_size)
{
unsigned int row_index, column_index;
unsigned int row_max = MD5_PER_ROW_NUM;
unsigned int column_max, ddr_size, offset;
unsigned int err_addr[5] = {0};
unsigned int err_count = 0, ret = 0;
unsigned int skip1_index_s, skip1_index_e;
unsigned int skip2_index_s, skip2_index_e;
unsigned int skip3_index_s;
struct rammd5_info_t *md5_info1 = (struct rammd5_info_t *)addr1;
struct rammd5_info_t *md5_info2 = (struct rammd5_info_t *)addr2;
if ((memcmp((char *)addr1, MD5_MAGIC, strlen(MD5_MAGIC)) != 0) ||
(memcmp((char *)addr2, MD5_MAGIC, strlen(MD5_MAGIC)) != 0))
return 0;
addr1 += sizeof(struct rammd5_info_t) / sizeof(unsigned int);
addr2 += sizeof(struct rammd5_info_t) / sizeof(unsigned int);
ddr_size = total_size > 0xe0000000 ? 0xe0000000 : total_size;
column_max = ddr_size / block_size / row_max;
/* fix up area1: bl33z reserved */
skip1_index_s = min(md5_info1->area1_start, md5_info2->area1_start) / block_size;
skip1_index_e = max(md5_info1->area1_end, md5_info2->area1_end) / block_size;
/* fix up area2: bl31 and bl32 reserved */
skip2_index_s = min(md5_info1->area2_start, md5_info2->area2_start) / block_size;
skip2_index_e = max(md5_info1->area2_end, md5_info2->area2_end) / block_size + 1;
/* fix up area3: ramdump compress end ~ ddr_end */
skip3_index_s = (ramdump_base + ramdump_size) / block_size + 1;
printf("ramdump MD5sum check %-7s vs %-7s, skip: %d~%d, %d~%d, %d~ ...",
md5_info1->stage, md5_info2->stage, skip1_index_s, skip1_index_e,
skip2_index_s, skip2_index_e, skip3_index_s);
for (column_index = 0; column_index < column_max; column_index++) {
for (row_index = 0; row_index < row_max; row_index++) {
offset = column_index * row_max + row_index;
if ((offset >= skip1_index_s && offset < skip1_index_e) ||
(offset >= skip2_index_s && offset < skip2_index_e) ||
offset >= skip3_index_s) {
continue;
} else {
if (addr1[offset] != addr2[offset]) {
if (err_count < 5)
err_addr[err_count++] = offset * block_size;
}
}
}
}
if (err_count == 0) {
ret = 0;
printf(" PASS.\n");
} else {
ret = -1;
printf(" ERROR !!\n");
printf("ramdump Polluted addr: ");
for (unsigned int i = 0; i < err_count; i++) {
if (err_addr[i] != 0)
printf("0x%08x, ", err_addr[i]);
}
printf(" ...\n");
for (column_index = 0; column_index < column_max; column_index++) {
printf("[0x%8x]:", column_index * row_max * block_size);
for (row_index = 0; row_index < row_max; row_index++) {
offset = column_index * row_max + row_index;
if ((row_index % 8) == 0)
printf(" ");
if (addr1[offset] == addr2[offset])
printf("- ");
else
printf("* ");
}
printf("\n");
}
}
return ret;
}
static int ramdump_save_ddr_md5_info(unsigned long ddr_size,
unsigned int block_size,
unsigned int *store_addr,
char *stage_info)
{
unsigned int i, j;
unsigned int total_size, blk_uint_num;
unsigned int *src_addr = (unsigned int *)0x0;
unsigned long sum = 0;
uintptr_t addr_ptr;
unsigned int skip1_start, skip2_start;
unsigned int skip1_end, skip2_end;
struct rammd5_info_t *md5_info;
/* Max check size is 3.5GB */
total_size = ddr_size > 0xe0000000 ? 0xe0000000 : ddr_size;
/* set ddr md5 skip area */
#ifdef MD5_SKIP_UBOOT_END
skip1_start = MD5_SKIP_UBOOT_START;
skip1_end = MD5_SKIP_UBOOT_END;
#else
skip1_start = 0;
skip1_end = 0x01800000;
#endif
#ifdef REG_RSVMEM_SIZE
unsigned int data = 0;
/* bl31/32 rsvmem start */
skip2_start = readl(REG_MDUMP_RSVMEM_BL31_START);
skip2_end = readl(REG_MDUMP_RSVMEM_BL32_START);
/* bl32_start + bl32_rsvmem_size = skip2_end */
data = readl(REG_RSVMEM_SIZE);
if ((data >> 16) & 0xff)
skip2_end += (data & 0x0000ffff) << 16;
else
skip2_end += (data & 0x0000ffff) << 10;
/* + bl32 stack reserved 1MB */
skip2_end += (1 << 20);
#else
printf("ramdump md5 use default bl32 size.\n");
skip2_start = 0x05000000;
skip2_end = 0x08400000;
#endif
printf("ramdump md5sum skip: 0x%08x~0x%08x, 0x%08x~0x%08x\n",
skip1_start, skip1_end, skip2_start, skip2_end);
md5_info = (struct rammd5_info_t *)store_addr;
memcpy(md5_info->magic, MD5_MAGIC, sizeof(md5_info->magic));
memcpy(md5_info->stage, stage_info, sizeof(md5_info->stage));
md5_info->block_size = block_size;
md5_info->ddr_size = ddr_size;
md5_info->area1_start = skip1_start;
md5_info->area1_end = skip1_end;
md5_info->area2_start = skip2_start;
md5_info->area2_end = skip2_end;
store_addr += sizeof(struct rammd5_info_t) / sizeof(unsigned int);
printf("ramdump md5sum total=0x%x, block=%x, store_addr=0x%lx\n",
total_size, block_size, (unsigned long)store_addr);
blk_uint_num = block_size / sizeof(unsigned int);
for (i = 0; i < (total_size / block_size - 1); i++) {
for (j = 0, sum = 0; j < blk_uint_num; j++) {
addr_ptr = (uintptr_t)&src_addr[i * blk_uint_num + j];
if ((addr_ptr >= skip1_start && addr_ptr < skip1_end) ||
(addr_ptr >= skip2_start && addr_ptr < skip2_end))
sum += 0;
else
sum += src_addr[i * blk_uint_num + j];
}
*store_addr = (unsigned int)(sum & 0xFFFFFFFF);
store_addr++;
}
return 0;
}
static int ramdump_check_ddr_md5_info(void)
{
int ret1 = 0, ret2 = 0, ret3 = 0;
ulong ddr_size = ((readl(SYSCTRL_SEC_STATUS_REG4) & ~0xfffffUL) << 4) >
0xe0000000 ? 0xe0000000 : ((readl(SYSCTRL_SEC_STATUS_REG4) & ~0xfffffUL) << 4);
ramdump_save_ddr_md5_info(ddr_size, MD5_BLOCK_SIZE,
(unsigned int *)MD5_BL33X_1_BASE_ADDR, "BL33X");
/* compare_memory_regions BL2E1 vs BL2E2 */
ret1 = compare_memory_regions((unsigned int *)MD5_BL2E_1_BASE_ADDR,
(unsigned int *)MD5_BL2E_2_BASE_ADDR, ddr_size, MD5_BLOCK_SIZE);
/* compare_memory_regions BL2E2 vs BL33Z-1 */
ret2 = compare_memory_regions((unsigned int *)MD5_BL2E_2_BASE_ADDR,
(unsigned int *)MD5_BL33Z_1_BASE_ADDR, ddr_size, MD5_BLOCK_SIZE);
/* compare_memory_regions BL33Z-2 vs BL33x */
ret3 = compare_memory_regions((unsigned int *)MD5_BL33Z_2_BASE_ADDR,
(unsigned int *)MD5_BL33X_1_BASE_ADDR, ddr_size, MD5_BLOCK_SIZE);
if (ret1 < 0 || ret2 < 0 || ret3 < 0)
return -1;
else
return 0;
}
unsigned int get_reboot_mode(void)
{
uint32_t reboot_mode_val = ((readl(REG_MDUMP_REBOOT_MODE) >> 12) & 0xf);
return reboot_mode_val;
}
void ramdump_init(void)
{
unsigned int data, reboot_mode;
printf("%s, base reg:0x%08x, size reg:0x%08x\n", __func__,
REG_MDUMP_COMPRESS_BASE, REG_MDUMP_COMPRESS_SIZE);
ramdump_base = readl(REG_MDUMP_COMPRESS_BASE);
ramdump_size = readl(REG_MDUMP_COMPRESS_SIZE);
#if defined(CONFIG_MDUMP_COMPRESS) && defined(CONFIG_COMPRESSED_ADDR)
printf("%s, ramdump_base(0x%08lx) is overwritten as 0x%08x\n",
__func__, ramdump_base, CONFIG_COMPRESSED_ADDR);
ramdump_base = (unsigned long)CONFIG_COMPRESSED_ADDR;
#endif
if (ramdump_base & 0x80) {
/* 0x80: The flag indicates that the addr exceeds 4G. */
/* real size = size[31:0] + addr[6:0]<<32 */
ramdump_size += (ramdump_base & 0x7f) << 32;
/* real addr = addr[31:8] << 8 */
ramdump_base = (ramdump_base & 0xffffff00) << 8;
}
data = readl(REG_MDUMP_CPUBOOT_STATUS);
writel(data & ~RAMDUMP_STICKY_DATA_MASK, REG_MDUMP_CPUBOOT_STATUS);
printf("%s, add:%lx, size:%lx\n", __func__, ramdump_base, ramdump_size);
#ifdef CONFIG_DUMP_COMPRESS_HEAD
dump_info((unsigned int)ramdump_base, 0x80, "bl33 check COMPRESS DATA 1");
#endif
reboot_mode = get_reboot_mode();
if ((reboot_mode == AMLOGIC_WATCHDOG_REBOOT ||
reboot_mode == AMLOGIC_KERNEL_PANIC)) {
if (ramdump_base && ramdump_size) {
#ifdef CONFIG_DUMP_COMPRESS_HEAD
parse_dump_file_v2((char *)ramdump_base, ramdump_size);
#endif
if (ramdump_check_ddr_md5_info() < 0)
printf("%s, The core-dump is polluted !!!\n\n", __func__);
ramdump_save_compress_data();
}
}
}
#ifdef CONFIG_USB_STORAGE
static void wait_usb_dev(void)
{
int print_cnt = 0, ret;
while (1) {
run_command("usb start", 1);
mdelay(2000);
run_command("usb reset", 1);
ret = usb_stor_scan(1);
if (ret) {
print_cnt++;
if (print_cnt > 100) {
printf("ramdump: USB device not found after 100 attempts.");
printf(" Exiting...\n\n");
return;
}
printf("ramdump: can't find USB device, attempt %d.", print_cnt);
printf(" Please insert FAT32 Udisk!\n\n");
mdelay(15000);
} else {
run_command("usb dev", 1);
break;
}
}
}
#endif
/*
* NOTE: this is a default implementation for writing compressed ramdump data
* to /data/ partition for Android platform. You can read out dumpfile in
* path /data/crashdump-1.bin when enter Android for crash analyze.
* by default, /data/ partition for android is EXT4 fs.
*
* TODO:
* If you are using different fs or OS on your platform, implement compress
* data save command for your fs and OS in your board.c with same function
* name "ramdump_save_compress_data".
*/
__weak int ramdump_save_compress_data(void)
{
char cmd[128] = {0};
char *env;
int ret = 0;
env = env_get("ramdump_enable");
if (!env || strcmp(env, "1") != 0)
return 0;
env = env_get("ramdump_location");
if (!env)
return 0;
printf("ramdump_location:%s\n", env);
/* currently we only support write to usb disk */
if (strncmp(env, "usb", 3)) {
printf("not supported location\n");
return 0;
}
#ifdef CONFIG_USB_STORAGE
wait_usb_dev();
#endif
printf("\nPrepare to save crash file: base=0x%08lx, size=0x%08lx (%ld MB)\n",
ramdump_base, ramdump_size, ramdump_size / 1024 / 1024);
sprintf(cmd, "fatwrite usb 0 %lx crashdump-1.bin %lx\n",
ramdump_base, ramdump_size);
printf("CMD:%s\n", cmd);
#ifdef CONFIG_USB_STORAGE
printf("It may take about 3 minutes, please wait...\n");
ret = run_command(cmd, 0);
if (ret != 0) {
printf("run fatwrite usb ERROR!\n");
return -1;
}
printf("run fatwrite usb OK!\n");
#else
printf("CONFIG_USB_STORAGE is not defined! Could it be in PRODUCT mode?\n");
printf("ERROR: The usb drv is not available!\n");
#endif
printf("Rebooting in 5 seconds ...\n\n\n");
mdelay(5000);
run_command("reboot", 1);
return ret;
}
static void ramdump_env_setup(unsigned long addr, unsigned long size)
{
unsigned int data[10] = {
0x8E9C929F, 0x9E9C9791,
0xD28C9191, 0x97949B8D,
0x888B92, 0xCEBB97,
0x938E9B90, 0x978D8D97,
0xC8009B8A, 0xB99CDB
};
char *line, *o;
unsigned char *p;
int i;
p = (unsigned char *)data;
for (i = 0; i < 40; i++)
p[i] = ~(p[i] - 1);
/*
* TODO: Make sure address for fdt_high and initrd_high
* are suitable for all boards
*
* usually kernel load address is 0x010800000
* Make sure:
* (kernel image size + ramdisk size) <
* (initrd_high - 0x010800000)
* dts file size < (fdt_high - initrd_high)
*/
//env_set("initrd_high", "0x0BB00000");
//env_set("fdt_high", "0x0BE00000");
line = env_get("bootargs");
if (!line)
return;
i = strlen(line);
o = malloc(i + 128);
if (!o)
return;
memset(o, 0, i + 128);
sprintf(o, "%s=%s ramdump=%lx,%lx %s\n",
(char *)data, (char *)(data + 6), addr, size, line);
env_set("bootargs", o);
free(o);
line = NULL;
#if DEBUG_RAMDUMP
run_command("printenv bootargs", 1);
printf("\n");
#endif
}
static int overwrite_bl33z_rsvmem_info(unsigned long addr, unsigned long size)
{
int address_cells = 0;
unsigned long align_size = PAGE_ALIGN(size);
char cmd[0x80];
address_cells = fdt_address_cells(gd->fdt_blob, 0);
if (address_cells < 1) {
printf("%s, bad #address-cells !\n", __func__);
return -1;
}
printf("%s, get fdt #address-cells = %d\n", __func__, address_cells);
memset(cmd, 0, sizeof(cmd));
if (address_cells == 2)
sprintf(cmd,
"fdt set /reserved-memory/ramdump_bl33z reg '<0x0 0x%08lx 0x0 0x%08lx>'",
addr, align_size);
else
sprintf(cmd, "fdt set /reserved-memory/ramdump_bl33z reg '<0x%08lx 0x%08lx>'",
addr, align_size);
printf("%s\n", cmd);
run_command(cmd, 0);
printf("fdt set /reserved-memory/ramdump_bl33z status okay\n");
run_command("fdt set /reserved-memory/ramdump_bl33z status okay", 0);
return 0;
}
static int reduce_dts_reserved_memory(void)
{
int address_cells = 0;
address_cells = fdt_address_cells(gd->fdt_blob, 0);
if (address_cells < 1) {
printf("%s, bad #address-cells !\n", __func__);
return -1;
}
printf("%s, get fdt #address-cells = %d\n", __func__, address_cells);
if (address_cells == 2) {
printf("\nReduce dts reserved memory:\n");
printf(" * disabled logo_reserved\n");
run_command("fdt set /reserved-memory/linux,meson-fb reg '<0x0 0x0 0x0 0x0>'", 0);
printf(" * disabled vdin1_cma_reserved\n");
run_command("fdt set /reserved-memory/linux,vdin1_cma reg '<0x0 0x0 0x0 0x0>'", 0);
printf(" * disabled ion_cma_reserved\n");
run_command("fdt set /reserved-memory/linux,ion-dev size '<0 0>'", 0);
printf(" * disabled dsp_fw_reserved\n");
run_command("fdt set /reserved-memory/linux,dsp_fw size '<0 0>'", 0);
printf(" * disabled lcd_tcon_reserved\n");
run_command("fdt set /reserved-memory/linux,lcd_tcon reg '<0x0 0x0 0x0 0x0>'", 0);
printf(" * reduce codec_mm_cma_reserved\n");
run_command("fdt set /reserved-memory/linux,codec_mm_cma size '<0x0 0x5000000>'", 0);
printf(" * reduce ion_fb_reserved, display\n");
run_command("fdt set /reserved-memory/linux,ion-fb size '<0x0 0x1000000>'", 0);
} else {
printf("\nReduce dts reserved memory:\n");
printf(" * disabled logo_reserved\n");
run_command("fdt set /reserved-memory/linux,meson-fb reg '<0x0 0x0>'", 0);
printf(" * disabled vdin1_cma_reserved\n");
run_command("fdt set /reserved-memory/linux,vdin1_cma reg '<0x0 0x0>'", 0);
printf(" * disabled ion_cma_reserved\n");
run_command("fdt set /reserved-memory/linux,ion-dev size '<0>'", 0);
printf(" * disabled dsp_fw_reserved\n");
run_command("fdt set /reserved-memory/linux,dsp_fw size '<0>'", 0);
printf(" * disabled lcd_tcon_reserved\n");
run_command("fdt set /reserved-memory/linux,lcd_tcon reg '<0x0 0x0>'", 0);
printf(" * reduce codec_mm_cma_reserved\n");
run_command("fdt set /reserved-memory/linux,codec_mm_cma size '<0x5000000>'", 0);
printf(" * reduce ion_fb_reserved, display\n");
run_command("fdt set /reserved-memory/linux,ion-fb size '<0x1000000>'", 0);
}
//run_command("fdt print /reserved-memory", 0);
return 0;
}
void check_ramdump(void)
{
unsigned long size = 0;
unsigned long addr = 0;
char *env;
int reboot_mode;
char str[128] = "";
unsigned int ddr_scramble_reg = 0xfe02e030;
env = env_get("ramdump_enable");
printf("%s, ramdump_enable = %s\n", __func__, env);
if (env) {
if (!strcmp(env, "1")) {
reboot_mode = get_reboot_mode();
if ((reboot_mode == AMLOGIC_WATCHDOG_REBOOT ||
reboot_mode == AMLOGIC_KERNEL_PANIC)) {
addr = ramdump_base;
size = ramdump_size;
printf("%s, addr:%lx, size:%lx\n",
__func__, addr, size);
if (addr && size) {
ramdump_env_setup(addr, size);
#ifdef CONFIG_DUMP_COMPRESS_HEAD
dump_info((unsigned int)ramdump_base, 0x80, "bl33 check COMPRESS DATA 2");
#endif
env = env_get("ramdump_location");
if (!strncmp(env, "data", 4)) {
printf("Crash file will save to Android /data.\n");
reduce_dts_reserved_memory();
overwrite_bl33z_rsvmem_info(addr, size);
env_set("initrd_high", "0x0BB00000");
env_set("fdt_high", "0x0BE00000");
}
} else {
ramdump_env_setup(0, 0);
}
} else {
ramdump_env_setup(0, 0);
}
#ifndef CONFIG_MDUMP_COMPRESS
//ramdump bl33z
printf("%s, fdt: rsvmem ramdump_bl33z enable.\n", __func__);
run_command("fdt set /reserved-memory/ramdump_bl33z status okay", 0);
#endif
//ddr scramble
sprintf(str, "setenv initargs ${initargs} scramble_reg=0x%08x",
ddr_scramble_reg);
printf("set_scramble: %s\n", str);
run_command(str, 0);
}
}
}