
CHANGELOG entry:
Provide support for RedBoot's FIS & config variables.
For PPC provide also a way to boot a redboot compiled Linux kernel. The bd_t is different in that case.
--- Signed-off-by: Pantelis Antoniou pantelis@embeddedalley.com ---
README | 70 ++++ common/Makefile | 1 common/cmd_bootm.c | 19 + common/redboot.c | 941 ++++++++++++++++++++++++++++++++++++++++++++++++++++ include/redboot.h | 91 +++++ 5 files changed, 1121 insertions(+), 1 deletions(-)
diff --git a/README b/README index 0aab04c..502f8ec 100644 --- a/README +++ b/README @@ -2265,6 +2265,76 @@ Low Level (hardware related) configuration options: some other boot loader or by a debugger which performs these intializations itself.
+- CONFIG_REDBOOT + Add support for read-only parsing of the FIS & config areas. + For PPC also add an option to boot a kernel compiled for RedBoot. + + The redboot command is also enabled conditional on CFG_CMD_FLASH. + + - Select a different FIS + > redboot fis select ([bank] [sector] | reset) + + - Display FIS + > redboot fis list + + - Set an environment variable from a member variable of a FIS entry + + > redboot fis setenv var type name + + Where type: flash_base, mem_base, size, entry_point, data_length. + + An example given a FIS entry of + + Name FLASH addr Mem addr Datalen Length Entry point + Linux 0xF0470000 0x00100000 0x000FA000 0x00100000 0x00100000 + + > redboot fis setenv linux_base flash_base Linux + + Would set the environment variable linux_base to 0xf0470000 + + - Print either all the RedBoot config variables or a specific one + + > redboot config printenv [config-var] [member] + + member is one of value, type, enable-sense, enable-key with value + being the default one. + + - Set an environment variable from a RedBoot config variable + + > redboot config setenv var config-var [member] + + For example when given a config variable of + + tsec1_esa=00:08:e5:11:32:33 + type=esa enable-sense=true + + > redboot config setenv ethaddr tsec1_esa + + would set the ethaddr environment variable to 00:08:e5:11:32:33 + + - Boot a RedBoot kernel [PPC specific] + + > redboot exec address + + The PPC bd_t differs for kernels compiled for redboot; this command + lets you boot such a kernel. + + RedBoot config defaults (can be overriden on board config): + + - Sector containing the FIS area (negative values for counting from the end) + CONFIG_REDBOOT_FIS_DIRECTORY_BLOCK -1 + + - FIS directory entry size + CONFIG_REDBOOT_DIRECTORY_ENTRY_SIZE 0x100 + + - Size of the config area + CONFIG_REDBOOT_FLASH_CONFIG_SIZE 4096 + + - Size of the script config variables + CONFIG_REDBOOT_FLASH_SCRIPT_SIZE 256 + + - Size of the string config variables + CONFIG_REDBOOT_FLASH_STRING_SIZE 128
Building the Software: ====================== diff --git a/common/Makefile b/common/Makefile index 8baeab3..b6db7f4 100644 --- a/common/Makefile +++ b/common/Makefile @@ -49,6 +49,7 @@ COBJS = main.o ACEX1K.o altera.o bedbug.o circbuf.o \ flash.o fpga.o ft_build.o \ hush.o kgdb.o lattice.o lattice_ivm_core.o lcd.o lists.o lynxkdi.o \ memsize.o miiphybb.o miiphyutil.o \ + redboot.o \ s_record.o serial.o soft_i2c.o soft_spi.o spartan2.o spartan3.o \ usb.o usb_kbd.o usb_storage.o \ virtex2.o xilinx.o crc16.o xyzModem.o cmd_mac.o diff --git a/common/cmd_bootm.c b/common/cmd_bootm.c index 7aae8a6..fd02257 100644 --- a/common/cmd_bootm.c +++ b/common/cmd_bootm.c @@ -146,6 +146,12 @@ extern void lynxkdi_boot( image_header_t * ); #define CFG_BOOTM_LEN 0x800000 /* use 8MByte as default max gunzip size */ #endif
+#ifdef CONFIG_REDBOOT_BD_T_SIZE +extern void board_redboot_bd_t_adapt(void *kbd, + ulong cmd_start, ulong cmd_end, + ulong initrd_start, ulong initrd_end); +#endif + image_header_t header;
ulong load_addr = CFG_LOAD_ADDR; /* Default Load Address */ @@ -533,6 +539,11 @@ do_bootm_linux (cmd_tbl_t *cmdtp, int flag, char *of_flat_tree = NULL; ulong of_data = 0; #endif +#ifdef CONFIG_REDBOOT_BD_T_SIZE +#define BD_T_SIZE max(sizeof(bd_t), CONFIG_REDBOOT_BD_T_SIZE) +#else +#define BD_T_SIZE sizeof(bd_t) +#endif
if ((s = getenv ("initrd_high")) != NULL) { /* a value of "no" or a similar string will act like 0, @@ -575,7 +586,7 @@ do_bootm_linux (cmd_tbl_t *cmdtp, int flag, debug ("=> set upper limit to 0x%08lX\n", sp);
cmdline = (char *)((sp - CFG_BARGSIZE) & ~0xF); - kbd = (bd_t *)(((ulong)cmdline - sizeof(bd_t)) & ~0xF); + kbd = (bd_t *)(((ulong)cmdline - BD_T_SIZE) & ~0xF);
if ((s = getenv("bootargs")) == NULL) s = ""; @@ -904,6 +915,12 @@ do_bootm_linux (cmd_tbl_t *cmdtp, int flag, initrd_end = 0; }
+ /* redboot bd_t modify */ +#ifdef CONFIG_REDBOOT_BD_T_SIZE + if ((s = getenv ("redboot_bd_t")) != NULL) + board_redboot_bd_t_adapt(kbd, cmd_start, cmd_end, initrd_start, initrd_end); +#endif + debug ("## Transferring control to Linux (at address %08lx) ...\n", (ulong)kernel);
diff --git a/common/redboot.c b/common/redboot.c new file mode 100644 index 0000000..eb19a58 --- /dev/null +++ b/common/redboot.c @@ -0,0 +1,941 @@ +/* + * (C) Copyright 2006 - Embedded Alley Solutions Inc. + * by Pantelis Antoniou, pantelis@embeddedalley.com + * + * Based on Linux & RedBoot code fragments + * by David Woodhouse, dwmw2@infradead.org + * by Mark Sattler, msalter@redhat.com + * by Gary Thomas, gthomas@redhat.com + * + * RedBoot flash images/configuration & kernel startup + * + * 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 + * + */ + +#include <common.h> +#include <malloc.h> +#include <flash.h> +#include <command.h> + +#include <redboot.h> + +#ifdef CONFIG_SHOW_BOOT_PROGRESS +# include <status_led.h> +# define SHOW_BOOT_PROGRESS(arg) show_boot_progress(arg) +#else +# define SHOW_BOOT_PROGRESS(arg) +#endif + +#ifdef CFG_INIT_RAM_LOCK +#include <asm/cache.h> +#endif + +#if (CONFIG_COMMANDS & CFG_CMD_FLASH) && defined(CONFIG_REDBOOT) + +DECLARE_GLOBAL_DATA_PTR; + +extern void board_redboot_bd_t_adapt(void *kbd, + ulong cmd_start, ulong cmd_end, + ulong initrd_start, ulong initrd_end); + +extern flash_info_t flash_info[]; /* info for FLASH chips */ + +static inline int redboot_checksum(struct fis_image_desc *img) +{ + /* RedBoot doesn't actually write the desc_cksum field yet AFAICT */ + return 1; +} + +static int get_sector_size(flash_info_t *info, int sector) +{ + ulong s, e; + + if (info->flash_id == FLASH_UNKNOWN) + return 0; + + if ((unsigned int)sector >= info->sector_count) + return 0; + + s = info->start[sector]; + if (sector < info->sector_count - 1) + e = info->start[sector + 1]; + else + e = info->start[0] + info->size; + + return e - s; +} + +/* get the sector which corresponds to this address */ +static int get_sector(flash_info_t *info, void *ptr) +{ + int i; + ulong s, e; + + if (info->flash_id == FLASH_UNKNOWN) + return -1; + + if (info->start[0] > (ulong)ptr || + info->start[info->sector_count - 1] < (ulong)ptr) + return -1; + + for (i = 0; i < info->sector_count; i++) { + s = info->start[i]; + if (i < info->sector_count - 1) + e = info->start[i + 1]; + else + e = info->start[0] + info->size; + + if (s <= (ulong)ptr && e > (ulong)ptr) + return i; + } + + /* should not happen */ + return -1; +} + +static void *get_fis(flash_info_t *info, void *fis) +{ + int dir_block, sector; + + if (info->flash_id == FLASH_UNKNOWN) + return NULL; + + /* already given a fis address (verify it) */ + if (fis != NULL) { + if (info->start[0] > (ulong)fis || + info->start[info->sector_count - 1] < (ulong)fis) + return NULL; + return fis; + } + + /* go with defaults */ + dir_block = CONFIG_REDBOOT_FIS_DIRECTORY_BLOCK; + if (dir_block < 0) + sector = info->sector_count + dir_block; + else + sector = dir_block; + + if ((unsigned int)sector >= info->sector_count) + return NULL; + + return (void *)info->start[sector]; +} + +static int get_fis_slots(flash_info_t *info, void *fis, int *swapped) +{ + int i, sector, sector_size, slots; + struct fis_image_desc *ptr; + + *swapped = -1; + + fis = get_fis(info, fis); + if (fis == NULL) + return -1; + + sector = get_sector(info, fis); + sector_size = get_sector_size(info, sector); + + slots = sector_size / sizeof(struct fis_image_desc); + + /* first pass; detect swap status */ + for (i = 0, ptr = fis; i < slots; i++, ptr++) { + if (ptr->name[0] == 0xFF) + break; + + if (!memcmp(ptr->name, "FIS directory", 14)) + *swapped = swab32(ptr->size) == sector_size; + } + + if (*swapped == -1) + return -1; + + return i; +} + +void *redboot_fis_get(int bank, int dir_block, flash_info_t **infop) +{ + flash_info_t *info; + int block; + + if (bank < 1 || bank > CFG_MAX_FLASH_BANKS) { + printf ("Only FLASH Banks # 1 ... # %d supported\n", + CFG_MAX_FLASH_BANKS); + return NULL; + } + info = &flash_info[bank-1]; + if (info->flash_id == FLASH_UNKNOWN) { + printf ("FLASH Bank #%d, not present\n", bank); + return NULL; + } + + if (dir_block < 0) + block = info->sector_count + dir_block; + else + block = dir_block; + + if ((unsigned int)block >= info->sector_count) { + printf ("FLASH dir_block %d out of range in Bank #%d\n", dir_block, bank); + return NULL; + } + + if (infop) + *infop = info; + return (void *)info->start[block]; +} + + +int redboot_fis_entry_lookup(flash_info_t *info, void *fis, struct fis_image_desc *fis_buf, const char *name) +{ + int i, slots, swapped, name_len; + struct fis_image_desc *ptr; + + fis = get_fis(info, fis); + if (fis == NULL) + return -1; + + slots = get_fis_slots(info, fis, &swapped); + if (slots == -1) { + printf("fis list: FIS not found\n"); + return -1; + } + + name_len = strlen(name); + + /* now do the proper read (with swapping if needed */ + for (i = 0, ptr = fis; i < slots; i++, ptr++) { + + if (ptr->name[0] == 0xFF) + break; + + /* copy to buffer */ + memcpy(fis_buf, ptr, sizeof(struct fis_image_desc)); + if (swapped) { + /* The unsigned long fields were written with the + * wrong byte sex, name and pad have no byte sex. + */ + swab32s(&fis_buf->flash_base); + swab32s(&fis_buf->mem_base); + swab32s(&fis_buf->size); + swab32s(&fis_buf->entry_point); + swab32s(&fis_buf->data_length); + swab32s(&fis_buf->desc_cksum); + swab32s(&fis_buf->file_cksum); + } + + /* found */ + if (strcmp(name, fis_buf->name) == 0) + return 0; + } + + return -1; +} + +int redboot_fis_list(flash_info_t *info, void *fis) +{ + int i, slots, swapped, found; + static struct fis_image_desc fis_buf; /* static; to save stack space */ + struct fis_image_desc *ptr; + u32 last, lowest, flash_base; + + fis = get_fis(info, fis); + if (fis == NULL) { + printf("fis list: could not get FIS\n"); + return -1; + } + + slots = get_fis_slots(info, fis, &swapped); + if (slots == -1) { + printf("fis list: FIS not found\n"); + return -1; + } + + printf("%-16s %-10s %-10s %-10s %-10s %-s\n", + "Name", "FLASH addr", "Mem addr", + "Datalen", "Length", "Entry point" ); + + last = 0; + do { + found = -1; + lowest = 0xFFFFFFFF; + + /* now do the proper read (with swapping if needed */ + for (i = 0, ptr = fis; i < slots; i++, ptr++) { + + if (ptr->name[0] == 0xFF) + break; + + if (swapped) + flash_base = swab32(ptr->flash_base); + else + flash_base = ptr->flash_base; + + if (flash_base > last && flash_base < lowest) { + lowest = flash_base; + found = i; + } + } + + if (found >= 0) { + ptr = fis; + memcpy(&fis_buf, &ptr[found], sizeof(struct fis_image_desc)); + if (swapped) { + /* The unsigned long fields were written with the + * wrong byte sex, name and pad have no byte sex. + */ + swab32s(&fis_buf.flash_base); + swab32s(&fis_buf.mem_base); + swab32s(&fis_buf.size); + swab32s(&fis_buf.entry_point); + swab32s(&fis_buf.data_length); + swab32s(&fis_buf.desc_cksum); + swab32s(&fis_buf.file_cksum); + } + + printf("%-16s 0x%08lX 0x%08lX 0x%08lX 0x%08lX 0x%08lX\n", + fis_buf.name, + (unsigned long)fis_buf.flash_base, fis_buf.mem_base, + fis_buf.data_length, fis_buf.size, + (unsigned long)fis_buf.entry_point); + } + last = lowest; + } while (found >= 0); + + return 0; +} + +int redboot_fis_setenv(flash_info_t *info, void *fis, const char *name, const char *what, char *var) +{ + static struct fis_image_desc fis_buf; /* static; to save stack space */ + static char varbuf[12]; + int i; + + i = redboot_fis_entry_lookup(info, fis, &fis_buf, name); + if (i != 0) + return -1; + + if (strcmp(what, "flash_base") == 0) + sprintf(varbuf, "0x%x", fis_buf.flash_base); + else if (strcmp(what, "mem_base") == 0) + sprintf(varbuf, "0x%x", fis_buf.mem_base); + else if (strcmp(what, "size") == 0) + sprintf(varbuf, "0x%x", fis_buf.size); + else if (strcmp(what, "entry_point") == 0) + sprintf(varbuf, "0x%x", fis_buf.entry_point); + else if (strcmp(what, "data_length") == 0) + sprintf(varbuf, "0x%x", fis_buf.data_length); + else + return -1; + + setenv(var, varbuf); + + return 0; +} + +/*************************************************************************/ + +#define CONFIG_KEY1 0x0badface +#define CONFIG_KEY2 0xdeaddead + +// +// Layout of config data +// Each data item is variable length, with the name, type and dependencies +// encoded into the object. +// offset contents +// 0 data type +// 1 length of name (N) +// 2 enable sense +// 3 length of enable key (M) +// 4 key name +// N+4 enable key +// M+N+4 data value +// + +#define TYPE(dp) ((dp)[0]) +#define KEYLEN(dp) ((dp)[1]) +#define ENABLE_SENSE(dp) ((dp)[2]) +#define ENABLE_KEYLEN(dp) ((dp)[3]) +#define KEY(dp) ((dp)+4) +#define ENABLE_KEY(dp) ((dp)+4+KEYLEN(dp)) +#define VALUE(dp) ((dp)+4+KEYLEN(dp)+ENABLE_KEYLEN(dp)) + +void *get_config(flash_info_t *info, void *fis, int *sizep, int *swappedp) +{ + static struct fis_image_desc fis_buf; /* static; to save stack space */ + int i; + ulong base, size; + void *config; + u32 *ps, *pe; + + /* lookup the RedBoot config */ + i = redboot_fis_entry_lookup(info, fis, &fis_buf, "RedBoot config"); + if (i != 0) + return NULL; + + base = fis_buf.flash_base; + size = fis_buf.data_length; + + config = (void *)base; + + /* verify */ + ps = (u32 *)base; + if (ps[1] != CONFIG_KEY1 && ps[1] != swab32(CONFIG_KEY1)) + return NULL; + + if (ps[1] == CONFIG_KEY1) { + *swappedp = 0; + if (ps[0] != size) + return NULL; + pe = (u32 *)(base + ps[0]); + if (pe[-2] != CONFIG_KEY2) + return NULL; + } else { + *swappedp = 1; + if (swab32(ps[0]) != size) + return NULL; + pe = (u32 *)(base + swab32(ps[0])); + if (pe[-2] != swab32(CONFIG_KEY2)) + return NULL; + } + + *sizep = size; + return config; +} + +#define CONFIG_EMPTY 0 +#define CONFIG_BOOL 1 +#define CONFIG_INT 2 +#define CONFIG_STRING 3 +#define CONFIG_SCRIPT 4 +#define CONFIG_IP 5 +#define CONFIG_ESA 6 +#define CONFIG_NETPORT 7 + +static const int config_len_tab[] = { + [CONFIG_EMPTY] = 0, + [CONFIG_BOOL] = 4, + [CONFIG_INT] = 4, + [CONFIG_STRING] = CONFIG_REDBOOT_FLASH_STRING_SIZE, + [CONFIG_SCRIPT] = CONFIG_REDBOOT_FLASH_SCRIPT_SIZE, + [CONFIG_IP] = 4, + [CONFIG_ESA] = 8, + [CONFIG_NETPORT]= CONFIG_REDBOOT_FLASH_STRING_SIZE, +}; + +static const char *config_txt_tab[] = { + [CONFIG_EMPTY] = "", + [CONFIG_BOOL] = "bool", + [CONFIG_INT] = "int", + [CONFIG_STRING] = "string", + [CONFIG_SCRIPT] = "script", + [CONFIG_IP] = "ip", + [CONFIG_ESA] = "esa", + [CONFIG_NETPORT]= "netport", +}; + +static inline int config_length(int type) +{ + if ((unsigned int)type >= sizeof(config_len_tab)/sizeof(config_len_tab[0])) + return -1; + return config_len_tab[type]; +} + +static const char *config_format_value(int type, const char *value, char *small_buf, int swapped) +{ + int val; + unsigned int uval; + const char *fmt; + + switch (type) { + case CONFIG_BOOL: + val = *(int *)value; + if (swapped) + val = swab32(val); + fmt = val ? "true" : "false"; + break; + case CONFIG_INT: + val = *(int *)value; + if (swapped) + val = swab32(val); + sprintf(small_buf, "%d", val); + fmt = small_buf; + break; + case CONFIG_STRING: + fmt = value; + break; + case CONFIG_SCRIPT: + fmt = value; + break; + case CONFIG_IP: + uval = *(unsigned int *)value; + if (swapped) + uval = swab32(uval); + sprintf(small_buf, "%u.%u.%u.%u", + (uval >> 24) & 0xff, (uval >> 16) & 0xff, + (uval >> 8) & 0xff, uval & 0xff); + fmt = small_buf; + break; + case CONFIG_ESA: + sprintf(small_buf, "%02x:%02x:%02x:%02x:%02x:%02x", + value[0] & 0xff, value[1] & 0xff, + value[2] & 0xff, value[3] & 0xff, + value[4] & 0xff, value[5] & 0xff); + fmt = small_buf; + break; + case CONFIG_NETPORT: + fmt = value; + break; + + default: + fmt = ""; /* empty */ + break; + } + + return fmt; +} + +int redboot_config_printenv(flash_info_t *info, void *fis, char *small_buf, const char *what) +{ + int size, cfglen, len, printit, swapped; + void *config; + char *dp, *dpe; + + if (small_buf == NULL) + return -1; + + config = get_config(info, fis, &size, &swapped); + if (config == NULL) + return -1; + + /* point to the config data */ + dp = config + 8; + dpe = config + size - 8; + + for (; dp < dpe; dp += len) { + + cfglen = config_length(TYPE(dp)); + if (cfglen < 0) /* illegal type */ + return -1; + + len = 4 + KEYLEN(dp) + ENABLE_KEYLEN(dp) + cfglen; + + /* don't bother with the empty */ + if (TYPE(dp) == CONFIG_EMPTY) + continue; + + /* printf("%p: %02x %02x %02x %02x\n", + dp, (int)dp[0] & 0xff, (int)dp[1] & 0xff, + (int)dp[2] & 0xff, (int)dp[3] & 0xff); */ + + printit = what == NULL || (what != NULL && strcmp(what, KEY(dp)) == 0); + + if (printit) { + printf("%s=%s\n", KEY(dp), config_format_value(TYPE(dp), VALUE(dp), small_buf, swapped)); + printf("\ttype=%s", config_txt_tab[(int)TYPE(dp)]); + printf(" enable-sense=%s", ENABLE_SENSE(dp) ? "true" : "false"); + if (ENABLE_KEYLEN(dp) > 0) + printf(" enable-key=%s", ENABLE_KEY(dp)); + printf("\n"); + + if (what) + break; + } + } + + return 0; +} + +const char *redboot_config_getenv(flash_info_t *info, void *fis, char *small_buf, const char *what, const char *special) +{ + int size, cfglen, len, swapped; + void *config; + char *dp, *dpe; + + /* this time we need that */ + if (what == NULL || small_buf == NULL) + return NULL; + + config = get_config(info, fis, &size, &swapped); + if (config == NULL) + return NULL; + + /* point to the config data */ + dp = config + 8; + dpe = config + size - 8; + + for (; dp < dpe; dp += len) { + + cfglen = config_length(TYPE(dp)); + if (cfglen < 0) /* illegal type */ + return NULL; + + len = 4 + KEYLEN(dp) + ENABLE_KEYLEN(dp) + cfglen; + + /* don't bother with the empty */ + if (TYPE(dp) == CONFIG_EMPTY) + continue; + + if (strcmp(what, KEY(dp)) == 0) { + + /* return value */ + if (special == NULL || strcmp(special, "value") == 0) + return config_format_value(TYPE(dp), VALUE(dp), small_buf, swapped); + + if (strcmp(special, "type") == 0) + return config_txt_tab[(int)TYPE(dp)]; + + if (strcmp(special, "enable-sense") == 0) + return ENABLE_SENSE(dp) ? "true" : "false"; + + if (strcmp(special, "enable-key") == 0) + return ENABLE_KEY(dp); + } + } + + return NULL; +} + +int redboot_config_setenv(flash_info_t *info, void *fis, + const char *var, const char *what, const char *special) +{ + static char small_buf[18]; + const char *value; + + value = redboot_config_getenv(info, fis, small_buf, what, special); + if (!value) + return -1; + + setenv((char *)var, (char *)value); + + return 0; +} + +/*************************************************************/ + +#ifdef CONFIG_PPC + +/* Note: Only tested on a PPC board - other arches + * should be made to work with not much trouble + * + * The way this works is: we do pretty much what + * we do for a bootm case of a uImage (even creating + * the u-boot's idea of bd_t). And then we call + * board specific code to fix the mess. + */ + +static void __attribute__((noinline)) +redboot_linux_exec(ulong addr, ulong ramdisk_address, ulong ramdisk_length) +{ + ulong sp; + ulong initrd_start, initrd_end; + ulong cmd_start, cmd_end; + char *cmdline; + char *s; + bd_t *kbd; + + initrd_start = ramdisk_address; + initrd_end = ramdisk_address + ramdisk_length; + + /* + * Booting a (Linux) kernel image + * + * Allocate space for command line and board info - the + * address should be as high as possible within the reach of + * the kernel (see CFG_BOOTMAPSZ settings), but in unused + * memory, which means far enough below the current stack + * pointer. + */ + + /* point stack at the end of memory */ + /* redboot is different that u-boot in that regard */ + sp = (gd->bd->bi_memstart + gd->bd->bi_memsize) - 16; + sp &= ~0x0f; + + debug ("## Current stack ends at 0x%08lX ", sp); + + sp -= 2048; /* just to be sure */ + + debug ("=> set upper limit to 0x%08lX\n", sp); + + cmdline = (char *)((sp - CFG_BARGSIZE) & ~0xF); + kbd = (bd_t *)(((ulong)cmdline - CONFIG_REDBOOT_BD_T_SIZE) & ~0xF); + + if ((s = getenv("bootargs")) != NULL) { + + strcpy (cmdline, s); + + cmd_start = (ulong)&cmdline[0]; + cmd_end = cmd_start + strlen(cmdline); + } else { + cmd_start = 0; + cmd_end = 0; + } + + *kbd = *(gd->bd); + + if ((s = getenv("clocks_in_mhz")) != NULL) { + /* convert all clock information to MHz */ + kbd->bi_intfreq /= 1000000L; + kbd->bi_busfreq /= 1000000L; +#if defined(CONFIG_MPC8220) + kbd->bi_inpfreq /= 1000000L; + kbd->bi_pcifreq /= 1000000L; + kbd->bi_pevfreq /= 1000000L; + kbd->bi_flbfreq /= 1000000L; + kbd->bi_vcofreq /= 1000000L; +#endif +#if defined(CONFIG_CPM2) + kbd->bi_cpmfreq /= 1000000L; + kbd->bi_brgfreq /= 1000000L; + kbd->bi_sccfreq /= 1000000L; + kbd->bi_vco /= 1000000L; +#endif +#if defined(CONFIG_MPC5xxx) + kbd->bi_ipbfreq /= 1000000L; + kbd->bi_pcifreq /= 1000000L; +#endif /* CONFIG_MPC5xxx */ + } + + /* convert u-boot bd_t to redboot_bd_t in place */ + board_redboot_bd_t_adapt(kbd, cmd_start, cmd_end, initrd_start, initrd_end); + + debug ("## Transferring control to Linux (at address %08lx) ...\n", + (ulong)kernel); + + SHOW_BOOT_PROGRESS (15); + +#if defined(CFG_INIT_RAM_LOCK) && !defined(CONFIG_E500) + unlock_ram_in_cache(); +#endif + dcache_disable(); + icache_disable(); + disable_interrupts(); + + /* Call into Linux */ + __asm__ volatile ( + /* Start by disabling MMU - the mappings are */ + /* 1-1 so this should not cause any problems */ + "mfmsr 3\n" + "li 4,0xFFFFFFCF\n" + "and 3,3,4\n" + "sync\n" + "mtmsr 3\n" + "sync\n" + + /* Now set up parameters to jump into linux */ + + "mtlr %0\n" /* set entry address in LR */ + "mr 1,%1\n" /* set stack pointer */ + "mr 3,%2\n" /* set board info in R3 */ + "mr 4,%3\n" /* set command line in R4 */ + "blr \n" /* jump into linux */ + : + : "r"(addr), "r"(sp), "r"(kbd), "r"(cmd_start) + : "r3", "r4"); + + /* never reached */ +} +#endif /* CONFIG_PPC */ + +extern flash_info_t flash_info[]; /* info for FLASH chips */ + +int do_redboot (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + int i, j; + char *name, *var; + static int bank = 1; + static int dir_block = -1; /* defaults */ + void *fis; + static char small_buf[18]; + const char *value; + + if (argc < 2) + goto usage; + + if (strcmp(argv[1], "fis") == 0) { + + if (argc < 3) + goto usage; + + if (strcmp(argv[2], "select") == 0) { + + i = bank; + j = dir_block; + + if (argc == 3 || strcmp(argv[3], "show") == 0) { + ; /* nothing */ + } else if (argc >= 4 && strcmp(argv[3], "reset") == 0) { + i = 1; + j = -1; + } else { + if (argc >= 4) + i = simple_strtoul(argv[3], NULL, 16); + if (argc >= 5) + j = simple_strtol(argv[4], NULL, 16); + } + + fis = redboot_fis_get(i, j, NULL); + if (fis == NULL) { + printf ("Illegal selection: bank %ld, dir_block # %ld\n", i, j); + return 1; + } + + printf ("bank #%ld, dir-block #%ld: RedBoot FIS @%p\n", bank, dir_block, fis); + bank = i; + dir_block = j; + return 0; + } + + if (strcmp(argv[2], "list") == 0) { + + fis = redboot_fis_get(bank, dir_block, NULL); + if (fis == NULL) { + printf ("Illegal selection: bank %ld, dir_block # %ld\n", bank, dir_block); + return 1; + } + + printf ("\nbank #%ld, dir-block #%ld: RedBoot FIS @%p\n", bank, dir_block, fis); + + redboot_fis_list(&flash_info[bank-1], fis); + + return 0; + } + + if (strcmp(argv[2], "setenv") == 0) { + + if (argc < 6) + goto usage; + + var = argv[3]; + name = argv[5]; + + fis = redboot_fis_get(bank, dir_block, NULL); + if (fis == NULL) { + printf ("Illegal selection: bank %ld, dir_block # %ld\n", bank, dir_block); + return 1; + } + + i = redboot_fis_setenv(&flash_info[bank-1], fis, name, argv[4], var); + if (i != 0) { + printf ("Unable to set var '%s' from '%s'\n", argv[4], name); + return 1; + } + + return 0; + } + } + + if (strcmp(argv[1], "config") == 0) { + + if (argc < 3) + goto usage; + + fis = redboot_fis_get(bank, dir_block, NULL); + if (fis == NULL) { + printf ("Illegal selection: bank %ld, dir_block # %ld\n", bank, dir_block); + return 1; + } + + if (strcmp(argv[2], "printenv") == 0) { + + if (argc >= 5) { + value = redboot_config_getenv(&flash_info[bank-1], fis, small_buf, argv[3], argv[4]); + if (value) + printf("%s=%s\n", argv[3], value); + } else + redboot_config_printenv(&flash_info[bank-1], fis, small_buf, argc >= 4 ? argv[3] : NULL); + + return 0; + } + + if (strcmp(argv[2], "setenv") == 0) { + + if (argc < 5) + goto usage; + + i = redboot_config_setenv(&flash_info[bank-1], fis, + argv[3], argv[4], argc >= 6 ? argv[5] : NULL); + if (i != 0) { + printf("Failed to set variable\n"); + return 1; + } + + return 0; + } + } + +#ifdef CONFIG_PPC + if (strcmp(argv[1], "exec") == 0) { + ulong addr, ramdisk_start, ramdisk_length; + + addr = 0; + ramdisk_start = 0; + ramdisk_length = 0; + + if (argc >= 3) + addr = simple_strtoul(argv[2], NULL, 16); + else if ((value = getenv("loadaddr")) != NULL) + addr = simple_strtoul(value, NULL, 16); + else if ((value = getenv("redboot_kernel_address")) != NULL) + addr = simple_strtoul(value, NULL, 16); + else { + printf("Kernel address not found\n"); + goto usage; + } + + if ((value = getenv("redboot_ramdisk_address")) != NULL) + ramdisk_start = simple_strtoul(value, NULL, 16); + + if ((value = getenv("redboot_ramdisk_length")) != NULL) + ramdisk_length = simple_strtoul(value, NULL, 16); + + redboot_linux_exec(addr, ramdisk_start, ramdisk_length); + } + +#endif + +usage: + printf ("Usage:\n%s\n", cmdtp->usage); + return 1; +} + + +U_BOOT_CMD( + redboot, 6, 1, do_redboot, + "redboot - redboot support commands\n", + "select ([bank] [sector] | reset)\n" + " - Select FIS\n" + "redboot fis list\n" + " - List FIS\n" + "redboot fis setenv var type name\n" + " - Set variable var to the type variable of the image\n" + " type: flash_base, mem_base, size, entry_point, data_length\n" + "redboot config printenv [cfgvar] [member]\n" + " - Print Redboot config key(s) of [member] of [cfgvar]\n" + "redboot config setenv var cfgvar [member]\n" + " - Print Redboot config key(s) of [member] of cfgvar\n" +#ifdef CONFIG_PPC + "redboot exec [address]\n" + " - Boot a Linux kernel compiled for redboot\n" +#endif + "" /* last empty string */ +); + +#endif + diff --git a/include/redboot.h b/include/redboot.h new file mode 100644 index 0000000..4fc6315 --- /dev/null +++ b/include/redboot.h @@ -0,0 +1,91 @@ +/* + * (C) Copyright 2006 - Embedded Alley Solutions Inc. + * by Pantelis Antoniou, pantelis@embeddedalley.com + * + * Based on Linux & RedBoot code fragments + * by David Woodhouse, dwmw2@infradead.org + * by Mark Sattler, msalter@redhat.com + * by Gary Thomas, gthomas@redhat.com + * + * 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 + * + */ + +#ifndef REDBOOT_H +#define REDBOOT_H + +#include <common.h> +#include <flash.h> + +struct fis_image_desc { + u8 name[16]; /* Null terminated name */ + u32 flash_base; /* Address within FLASH of image */ + u32 mem_base; /* Address in memory where it executes */ + u32 size; /* Length of image */ + u32 entry_point; /* Execution entry point */ + u32 data_length; /* Length of actual data */ + u8 _pad[256-(16+7*sizeof(unsigned long))]; + u32 desc_cksum; /* Checksum over image descriptor */ + u32 file_cksum; /* Checksum over image data */ +}; + +/* redboot config is stored like this + note that we don't define it, since we can work + without having a fixed size config + +struct redboot_config { + u32 len; + u32 key1; + char config_data[MAX_CONFIG_DATA-(4*4)]; + u32 key2; + u32 long cksum; +}; +*/ + +/* defaults */ +#ifndef CONFIG_REDBOOT_FIS_DIRECTORY_BLOCK +#define CONFIG_REDBOOT_FIS_DIRECTORY_BLOCK -1 /* last block of flash */ +#endif + +#ifndef CONFIG_REDBOOT_DIRECTORY_ENTRY_SIZE +#define CONFIG_REDBOOT_DIRECTORY_ENTRY_SIZE 0x100 /* default */ +#endif + +#ifndef CONFIG_REDBOOT_FLASH_SCRIPT_SIZE +#define CONFIG_REDBOOT_FLASH_SCRIPT_SIZE 256 +#endif + +#ifndef CONFIG_REDBOOT_FLASH_STRING_SIZE +#define CONFIG_REDBOOT_FLASH_STRING_SIZE 128 +#endif + +#ifndef CONFIG_REDBOOT_FLASH_CONFIG_SIZE +#define CONFIG_REDBOOT_FLASH_CONFIG_SIZE 4096 +#endif + +void *redboot_fis_get(int bank, int dir_block, flash_info_t **infop); +int redboot_fis_list(flash_info_t *info, void *fis); +int redboot_fis_entry_lookup(flash_info_t *info, void *fis, struct fis_image_desc *fis_buf, const char *name); +int redboot_fis_setenv(flash_info_t *info, void *fis, const char *name, const char *what, char *var); + +int redboot_config_printenv(flash_info_t *info, void *fis, char *small_buf, const char *what); +const char *redboot_config_getenv(flash_info_t *info, void *fis, char *small_buf, const char *what, const char *special); +int redboot_config_setenv(flash_info_t *info, void *fis, const char *var, const char *what, const char *special); + +#endif