/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */ /* * common/ramdump.c * * Copyright (C) 2020 Amlogic, Inc. All rights reserved. * */ #include #include #include #include #include #include #include #include #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 } } }