Files

313 lines
7.9 KiB
C

/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */
/*
* common/ramdump.c
*
* Copyright (C) 2020 Amlogic, Inc. All rights reserved.
*
*/
#include <common.h>
#include <asm/arch/bl31_apis.h>
#include <asm/reboot.h>
#include <asm/arch/secure_apb.h>
#include <ramdump.h>
#include <emmc_partitions.h>
#include <asm/cpu_id.h>
#include <cli.h>
#define DEBUG_RAMDUMP 0
#define _AML_MISC_INTERRUPT_KEY 0x09
unsigned long ramdump_base = 0;
unsigned long ramdump_size = 0;
unsigned int get_reboot_mode(void)
{
uint32_t reboot_mode_val = ((readl(AO_SEC_SD_CFG15) >> 12) & 0xf);
return reboot_mode_val;
}
void ramdump_init(void)
{
unsigned int data, reboot_mode;
ramdump_base = readl(P_AO_SEC_GP_CFG12);
ramdump_size = readl(P_AO_SEC_GP_CFG13);
#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(PREG_STICKY_REG8);
writel(data & ~RAMDUMP_STICKY_DATA_MASK, PREG_STICKY_REG8);
printf("%s, add:%lx, size:%lx\n", __func__, ramdump_base, ramdump_size);
reboot_mode = get_reboot_mode();
if ((reboot_mode == AMLOGIC_WATCHDOG_REBOOT ||
reboot_mode == AMLOGIC_KERNEL_PANIC)) {
if (ramdump_base && ramdump_size)
ramdump_save_compress_data();
}
}
#ifdef CONFIG_USB_STORAGE
static int wait_for_interrupt_key(const int tm/*timeout in ms*/)
{
int i;
unsigned long ts;
char str_input[5];
ts = get_timer(0);
for (i = 0; i < sizeof(str_input) - 1;) {
int c = 0;
while (!tstc()) {
if (get_timer(ts) >= tm)
return 0;
}
c = getc();
if (i == 0 && c == _AML_MISC_INTERRUPT_KEY) {//drop first duplicated ctrlI
printf("Wait y/yes input\n");
continue;
}
putc(c);
str_input[i++] = c;
if (c == '\r')
break;
}
str_input[i] = '\0';
putc('\n');
if (strcasecmp(str_input, "y\r") == 0 ||
strcasecmp(str_input, "yes\r") == 0) {
cli_init();
cli_loop();
panic("No CLI available");
}
return 0;
}
static void wait_usb_dev(void)
{
block_dev_desc_t *usb_dev;
int print_cnt = 0;
const char *env = getenv("ramdump_usb_timeout");
int timeout_minutes = env ? simple_strtoul(env, NULL, 10) : 0;
int max_attempts = (timeout_minutes > 0) ? (timeout_minutes * 60 / 20) : 0;
while (1) {
run_command("usb start", 1);
mdelay(2000);
run_command("usb reset", 1);
usb_dev = usb_stor_get_dev(0);
if (!usb_dev) {
print_cnt++;
if (max_attempts > 0 && print_cnt > max_attempts) {
printf("ramdump: Udisk not found after %d minutes. Exiting...\n",
timeout_minutes);
return;
}
printf("ramdump: can't find USB device, attempt %d.", print_cnt);
printf(" Please insert FAT32 Udisk!\n");
printf("Or you can input 'Ctrl I' to force stopped anyway ...\n\n\n");
wait_for_interrupt_key(18000);
} 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 *env;
int ret = 0;
#ifdef CONFIG_USB_STORAGE
char cmd[128] = {0};
char path[64] = "/";
int cnt = 0;
#endif
env = getenv("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();
env = getenv("ramdump_usb_path");
if (env) {
printf("ramdump_usb_path: %s\n", env);
snprintf(cmd, sizeof(cmd), "fatls usb 0 %s/", env);
ret = run_command(cmd, 0);
if (ret != 0)
printf("USB path '%s' does not exist, using default '/'.\n", env);
else
snprintf(path, sizeof(path), "%s/", env);
}
printf("\nPrepare to save crash file: base=0x%08lx, size=0x%08lx (%ld MB)\n",
ramdump_base, ramdump_size, ramdump_size / 1024 / 1024);
snprintf(cmd, sizeof(cmd), "fatwrite usb 0 %lx %scrashdump-1.bin %lx",
ramdump_base, path, ramdump_size);
printf("\nCMD: %s\n\n", cmd);
printf("It may take about 3 minutes, please wait...\n");
while (cnt < 3) {
ret = run_command(cmd, 0);
if (ret == 0) {
run_command("usb stop", 0);
printf("run fatwrite usb OK! (%scrashdump-1.bin)\n\n", path);
break;
}
printf("run fatwrite usb ERROR! Try again...\n");
cnt++;
run_command("usb stop", 0);
mdelay(1000);
run_command("usb start", 1);
mdelay(2000);
run_command("usb reset", 1);
run_command("usb dev", 1);
mdelay(1000);
}
if (cnt >= 3) {
printf("\n======================= fatwrite ERROR! (try 3 times) =============\n");
printf("= 1. Please check Udisk! (FAT32 type, and enough space.)\n");
printf("= 2. Try another Udisk! (Press Ctrl+i, then manually run fatwrite.)\n");
printf("====================================================================\n\n");
}
env = getenv("ramdump_usb_reboot");
if (env && strcmp(env, "1") == 0) {
printf("ramdump_usb_reboot: %s\n", env);
printf("Rebooting in 5 seconds ...\n\n\n");
mdelay(5000);
run_command("reboot", 1);
}
printf("\nRun fatwrite finished. Wait here forever!\n");
printf("\nAnd You can input 'Ctrl I' to force stopped anyway ...\n\n\n");
while (1) {
mdelay(1000);
wait_for_interrupt_key(100000);
}
#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
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)
*/
//setenv("initrd_high", "0x04400000");
//setenv("fdt_high", "0x04E00000");
line = getenv("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);
setenv("bootargs", o);
free(o);
line = NULL;
#if DEBUG_RAMDUMP
run_command("printenv bootargs", 1);
printf("\n");
#endif
}
void check_ramdump(void)
{
unsigned long size = 0;
unsigned long addr = 0;
char *env;
int reboot_mode;
env = getenv("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);
printf("Crash file will save to Android /data.\n");
} 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
}
}
}