diff options
author | Michael Brandt <michael.brandt@stericsson.com> | 2010-08-27 14:21:17 +0200 |
---|---|---|
committer | Michael BRANDT <michael.brandt@stericsson.com> | 2010-08-30 20:07:21 +0200 |
commit | 529adafde26d951fa425029e0b56370c36d29081 (patch) | |
tree | 4c8728938a2f68c54207c36e5fb4638cf22ba5f4 /board/st/u8500/cmd_cdump.c | |
parent | e12e3ecc89a65421b4e26267c5c71badfdf0dbbb (diff) |
Make U-Boot restartable and crash dump support
* Added an u8500 board specific ld script. Reserve space for .data
backup.
* Make a backup copy of initialised data (.data section). If restarted,
copy the backup data back to .data.
* Create crashkernel env variable dynamically, since it depends on
U-Boot start address. For dumping U-Boot itself is used as
crashkernel.
* Set preboot environment variable to checkcrash command, if
data_init_flag > 0, i.e. we were restarted e.g. by kexec.
* Added crashkernel parameter to common bootargs.
Decreased environment and pool sizes.
* Changed link address of U-Boot: moved it 32 K up for kdump info to
0x05608000
* Added "checkcrash" command
This command checks if there is a pending crash dump
and writes it to a file on SD/MMC.
ST-Ericsson ID: WP264488
Change-Id: If545822e424b95532f1128afb0e762b6b73834e9
Signed-off-by: Michael Brandt <michael.brandt@stericsson.com>
Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/3011
Diffstat (limited to 'board/st/u8500/cmd_cdump.c')
-rw-r--r-- | board/st/u8500/cmd_cdump.c | 332 |
1 files changed, 332 insertions, 0 deletions
diff --git a/board/st/u8500/cmd_cdump.c b/board/st/u8500/cmd_cdump.c new file mode 100644 index 000000000..36bdfc4c8 --- /dev/null +++ b/board/st/u8500/cmd_cdump.c @@ -0,0 +1,332 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * Author: Spjalle Joelson for ST-Ericsson + * Licensed under GPLv2. + * + * Cleanups and comments: Michael Brandt <michael.brandt@stericsson.com> + */ + +/* cmd_cdump.c - crash dump commands, require FAT write support */ + +#include <common.h> +#include <command.h> +#include "malloc.h" +#include <mmc.h> + +#include <asm/setup.h> +#include <elf.h> +#include <fat.h> + +/* + * Note: with the Rockbox FAT support, the file path must be an absolute path, + * i.e. with leading /. + */ +static char *crash_filename = "/cdump.elf"; + +/* + * Check ELF header + */ +static int check_elfhdr(u32 elfhdr_addr) +{ + unsigned char *elfhdr = (unsigned char *) elfhdr_addr; + + /* check ELF core image header MAGIC */ + if (memcmp(elfhdr, ELFMAG, SELFMAG) != 0) + return 1; + + /* check that this is ELF32 */ + if (elfhdr[EI_CLASS] == ELFCLASS32) + return 0; + + return 1; +} + +/* + * Write a chunk + */ +static int write_chunk(int fd, void *addr, size_t count) +{ + size_t bytes; + + bytes = write(fd, addr, count); + if (bytes != count) { + printf("write error\n"); + close(fd); + return -1; + } + return 0; +} + +/* + * Write a big chunk with 'progress' indicator '.' for every MiB + */ +static int write_big_chunk(int fd, void *addr, size_t count) +{ + unsigned char *a = addr; + size_t total = 0; + + if (count < 0x100000) + return write_chunk(fd, addr, count); + /* if large chunk then print dot to show progress */ + while (total < count) { + size_t bytes = count - total; + + if (bytes > 0x100000) + bytes = 0x100000; + if (write_chunk(fd, a, bytes)) + return -1; + putc('.'); + total += bytes; + a += bytes; + } + putc('\n'); + return 0; +} + +/* + * Open the dump file for writing. Create if it not exists. + * Note that with the Rockbox FAT support, the file path must be an absolute + * path, i.e. with leading /. + */ +static int open_create(const char *filename) +{ + int fd; + + fd = open(filename, O_WRONLY | O_CREAT); + if (fd < 0) + printf("%s open error\n", filename); + return fd; +} + +/* + * Check program header and segment + * Truncate note segments. + * Return segment size. + */ +static u32 check_proghdr(u32 *proghdr) +{ + u32 i; + u32 *note; + Elf32_Phdr *phdr = (Elf32_Phdr *) proghdr; + + if (phdr->p_type == PT_NOTE) { + /* see Linux kernel/kexec.c:append_elf_note() */ + note = (u32 *)(phdr->p_paddr); + for (i = 0; i < phdr->p_filesz/4;) { + if (note[i] == 0 && note[i+1] == 0 && note[i+2] == 0) + return i*4; + i += 3 + (note[i] + 3) / 4 + (note[i+1] + 3) / 4; + } + } + + return phdr->p_filesz; +} + +/* + * Dump crash to file + */ +static int write_elf(u32 elfhdr_addr, int fd) +{ + u32 i; + u32 len; + u32 offset; + Elf32_Ehdr *ehdr; + Elf32_Phdr *phdr; + + ehdr = (Elf32_Ehdr *)elfhdr_addr; + /* total hdr size = elf header + pgm header size * pgm header entries */ + offset = ehdr->e_ehsize + ehdr->e_phentsize * ehdr->e_phnum; + ehdr = (Elf32_Ehdr *)malloc(offset); + if (ehdr == NULL) { + printf("elf header alloc error\n"); + return -1; + } + memcpy(ehdr, (void *)elfhdr_addr, offset); + + /* check program headers and adjust length of segments */ + debug("elf hdr %lx\n", (ulong) ehdr); + for (i = 0; i < ehdr->e_phnum; i++) { + phdr = (Elf32_Phdr *) + ((char *)ehdr + ehdr->e_ehsize + ehdr->e_phentsize * i); + + len = check_proghdr((u32 *)phdr); + debug("prog hdr %d: %lx at %x len %x adjusted to %x\n", + i, (ulong) phdr, phdr->p_paddr, + phdr->p_filesz, len); + phdr->p_filesz = len; + phdr->p_memsz = len; + /* set (file) offset to segment */ + phdr->p_offset = offset; + offset += len; + } + + /* write elf header and program headers */ + len = ehdr->e_ehsize + ehdr->e_phentsize * ehdr->e_phnum; + if (write_chunk(fd, ehdr, (size_t) len)) { + free(ehdr); + return -1; + } + + /* copy segment contents from SDRAM to file */ + for (i = 0; i < ehdr->e_phnum; i++) { + phdr = (Elf32_Phdr *) + ((char *)ehdr + ehdr->e_ehsize + ehdr->e_phentsize * i); + if (phdr->p_filesz > 0) { + if (write_big_chunk(fd, (void *) phdr->p_paddr, + phdr->p_filesz)) { + free(ehdr); + return -1; + } + } + } + + free(ehdr); + return 0; +} + +/* + * Dump crash to file + */ +static int dump_elf(u32 elfhdr_addr, char *filename) +{ + int fd; + int rc; + + printf("Crash dump to %s\n", filename); + fd = open_create(filename); + if (fd < 0) + return 1; + rc = write_elf(elfhdr_addr, fd); + close(fd); + + return rc; +} + +/* + * Wait for MMC/SD card to be inserted + */ +static int wait_for_mmc(void) +{ + struct mmc *mmc; + + mmc = find_mmc_device(CONFIG_MMC_DEV_NUM); + while (mmc_init(mmc) != 0) { + printf("Insert MMC/SD card or press ctrl-c to abort\n"); + putc('.'); + udelay(500000); + /* check for ctrl-c to abort... */ + if (ctrlc()) { + puts("Abort\n"); + return -1; + } + } + return 0; +} + +/* + * Find kexec/kdump ATAG command line + */ +static char *get_atag_cmdline(void) +{ + ulong atag_offset = 0x1000; /* 4k offset from memory start */ + ulong offset = 0x8000; /* 32k offset from memory start */ + /* + * Get pointer to ATAG area, somewhere below U-boot image. + * Above values are hard coded in the kexec-tools. + */ + u32 * atags = (u32 *)(_armboot_start - offset + atag_offset); + u32 i = 0; + + /* + * ATAG command line: \0 terminated string. + * The list ends with an ATAG_NONE node. + */ + for (i = 0; (atags[i] != 0) && (atags[i+1] != ATAG_NONE); + i += atags[i]) { + + if (atags[i+1] == ATAG_CMDLINE) + return (char *) &atags[i+2]; + /* sanity check before checking next ATAG */ + if (atags[i] > (offset - atag_offset) / sizeof(u32) - i) + return NULL; + if ((atags[i] + i) < i) /* cannot step backwards */ + return NULL; + } + + return NULL; +} + +/* + * Find out where the kdump elf header is. + */ +static u32 get_elfhdr_addr(void) +{ + const char elfcorehdr[] = "elfcorehdr="; + char *cmd; + char *atag_cmdline = get_atag_cmdline(); + + if (atag_cmdline != NULL) { + cmd = strstr(atag_cmdline, elfcorehdr); + if (cmd != NULL) { + cmd += strlen(elfcorehdr); + return simple_strtoul(cmd, NULL, 16); + } + } + return 0; +} + +/* + * Dump crash to file (typically FAT file on SD/MMC). + */ +static int crashdump(u32 elfhdr_addr, char *filename) +{ + int rc; + block_dev_desc_t *dev_desc=NULL; + + rc = wait_for_mmc(); + if (rc == 0) { + dev_desc = get_dev("mmc", 1); /* mmc 1 */ + rc = fat_register_device(dev_desc, 1); /* part 1 */ + if (rc != 0) { + printf("crashdump: fat_register_device failed %d\n", + rc); + return -1; + } + rc = dump_elf(elfhdr_addr, filename); + } + + if (rc != 0) + printf("crashdump: error writing dump to %s\n", filename); + + return rc; +} + +/* + * Dump crash to file (typically FAT file on SD/MMC). + */ +static int do_checkcrash(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + u32 elfhdr_addr; + int rc = -1; + + elfhdr_addr = get_elfhdr_addr(); + if (elfhdr_addr != 0) + rc = check_elfhdr(elfhdr_addr); + if (rc == 0) { + printf("crash dump elf header found. Dumping to card...\n"); + /* stop autoboot in case we were called by preboot */ + setenv("bootdelay", "-1"); + rc = crashdump(elfhdr_addr, crash_filename); + if (rc != 0) + printf("checkcrash: error writing dump from %x to %s\n", + elfhdr_addr, crash_filename); + } + return rc; +} + +U_BOOT_CMD( + checkcrash, 1, 0, do_checkcrash, + "check ATAGS from crash and dump to file", + " - dump crash info to file and stop autoboot\n" +); |