summaryrefslogtreecommitdiff
path: root/board
diff options
context:
space:
mode:
authorMichael Brandt <michael.brandt@stericsson.com>2010-08-27 14:21:17 +0200
committerMichael BRANDT <michael.brandt@stericsson.com>2010-08-30 20:07:21 +0200
commit529adafde26d951fa425029e0b56370c36d29081 (patch)
tree4c8728938a2f68c54207c36e5fb4638cf22ba5f4 /board
parente12e3ecc89a65421b4e26267c5c71badfdf0dbbb (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')
-rw-r--r--board/st/u8500/Makefile2
-rw-r--r--board/st/u8500/cmd_cdump.c332
-rw-r--r--board/st/u8500/config.mk2
-rw-r--r--board/st/u8500/u-boot.lds70
-rw-r--r--board/st/u8500/u8500.c50
5 files changed, 454 insertions, 2 deletions
diff --git a/board/st/u8500/Makefile b/board/st/u8500/Makefile
index 3bd3a7a37..e0617f471 100644
--- a/board/st/u8500/Makefile
+++ b/board/st/u8500/Makefile
@@ -24,7 +24,7 @@ include $(TOPDIR)/config.mk
LIB = $(obj)lib$(BOARD).a
-COBJS := u8500.o flash.o gpio.o u8500_i2c.o mmc_host.o mmc_utils.o clock.o prcmu.o mcde_display.o mcde_hw.o ab8500vibra.o
+COBJS := u8500.o flash.o gpio.o u8500_i2c.o mmc_host.o mmc_utils.o clock.o prcmu.o mcde_display.o mcde_hw.o ab8500vibra.o cmd_cdump.o
SOBJS := mmc_fifo.o
SRCS := $(SOBJS:.o=.S) $(COBJS:.o=.c)
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"
+);
diff --git a/board/st/u8500/config.mk b/board/st/u8500/config.mk
index 4d007f96d..02f1643a4 100644
--- a/board/st/u8500/config.mk
+++ b/board/st/u8500/config.mk
@@ -12,7 +12,7 @@
sinclude $(OBJTREE)/board/$(BOARDDIR)/config.tmp
ifndef TEXT_BASE
-TEXT_BASE = 0x05600000
+TEXT_BASE = 0x05608000
endif
PLATFORM_CPPFLAGS += -DTEXT_BASE=$(TEXT_BASE)
diff --git a/board/st/u8500/u-boot.lds b/board/st/u8500/u-boot.lds
new file mode 100644
index 000000000..c2fe5ccec
--- /dev/null
+++ b/board/st/u8500/u-boot.lds
@@ -0,0 +1,70 @@
+/*
+ * January 2004 - Changed to support H4 device
+ * Copyright (c) 2004-2008 Texas Instruments
+ *
+ * (C) Copyright 2002
+ * Gary Jennejohn, DENX Software Engineering, <garyj@denx.de>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
+OUTPUT_ARCH(arm)
+ENTRY(_start)
+SECTIONS
+{
+ . = 0x00000000;
+
+ . = ALIGN(4);
+ .text :
+ {
+ cpu/arm_cortexa9/start.o (.text)
+ *(.text)
+ }
+
+ . = ALIGN(4);
+ .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }
+
+ . = ALIGN(4);
+ _data_start = . ;
+ .data : { *(.data) }
+ _data_end = . ;
+
+ . = ALIGN(4);
+ .got : { *(.got) }
+
+ __u_boot_cmd_start = .;
+ .u_boot_cmd : { *(.u_boot_cmd) }
+ __u_boot_cmd_end = .;
+
+ /*
+ * To make U-Boot restartable, we need to save and copy the initialised
+ * data. Reserve a memory area idata and copy the .data section to it
+ * in u8500.c:board_init().
+ */
+ . = ALIGN(4);
+ _idata_start = .;
+ _idata_end = _idata_start + SIZEOF(.data);
+ . = _idata_end;
+
+ . = ALIGN(4);
+ __bss_start = .;
+ .bss : { *(.bss) }
+ _end = .;
+}
diff --git a/board/st/u8500/u8500.c b/board/st/u8500/u8500.c
index c02d7d9b0..debcc117c 100644
--- a/board/st/u8500/u8500.c
+++ b/board/st/u8500/u8500.c
@@ -69,6 +69,14 @@
int board_id; /* set in board_late_init() */
int errno;
+/*
+ * Flag to indicate from where to where we have to copy the initialised data.
+ * In case we were loaded, its value is -1 and .data must be saved for an
+ * eventual restart. It is 1 if .data was restored, i.e. we were restarted,
+ * e.g. by kexec.
+ */
+static volatile int data_init_flag = -1; /* -1 to get it into .data section */
+
/* PLLs for clock management registers */
enum {
GATED = 0,
@@ -194,6 +202,28 @@ int cpu_is_u8500v2(void)
int board_init(void)
{
+ extern char _idata_start[];
+ extern char _data_start[];
+ extern char _data_end[];
+ unsigned long data_len;
+
+ data_len = _data_end - _data_start;
+ if (++data_init_flag == 0) {
+ /*
+ * first init after reset/loading
+ * save .data section for restart
+ */
+ memcpy(_idata_start, _data_start, data_len);
+ } else {
+ /*
+ * restart, e.g. by kexec
+ * copy back .data section
+ */
+ memcpy(_data_start, _idata_start, data_len);
+ /* memcpy set data_init_flag back to zero */
+ ++data_init_flag;
+ }
+
gd->bd->bi_arch_number = 0x1A4;
gd->bd->bi_boot_params = 0x00000100;
@@ -378,6 +408,7 @@ int board_late_init(void)
#ifdef CONFIG_MMC
uchar byte_array[] = {0x06, 0x06};
#endif
+ char strbuf[80];
/*
* Determine and set board_id environment variable
@@ -436,6 +467,25 @@ int board_late_init(void)
setenv("mem", "512M");
}
+ /*
+ * Create crashkernel env dynamically since it depends on U-Boot start
+ * address. U-Boot itself is used for dumping.
+ * The 32K offset is hardcoded in the kexec-tools.
+ * Parsed by Linux setup.c:reserve_crashkernel() using
+ * lib/cmdline.c:memparse().
+ * crashkernel=ramsize-range:size[,...][@offset]
+ */
+ sprintf(strbuf, "crashkernel=1M@0x%lx", _armboot_start - 0x8000);
+ setenv("crashkernel", strbuf);
+
+ /*
+ * Check for a crashdump, if data_init_flag > 0, i.e. we were
+ * restarted e.g. by kexec. Do not check for crashdump if we were just
+ * loaded from the x-loader.
+ */
+ if (data_init_flag > 0)
+ setenv("preboot", "checkcrash");
+
return (0);
}
#endif /* BOARD_LATE_INIT */