[U-Boot-Users] [PATCH 5/8] Adds support for RedBoot

Pantelis Antoniou pantelis at embeddedalley.com
Sat Dec 2 23:16:25 CET 2006


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 at embeddedalley.com>
---

 README             |   70 ++++
 common/Makefile    |    1 
 common/cmd_bootm.c |   20 +
 common/redboot.c   |  978 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 include/redboot.h  |   98 +++++
 5 files changed, 1166 insertions(+), 1 deletions(-)

diff --git a/README b/README
index e28f935..c3055c0 100644
--- a/README
+++ b/README
@@ -2324,6 +2324,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 79d11a5..13dc6ae 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -51,6 +51,7 @@ COBJS	= main.o ACEX1K.o altera.o bedbug.o circbuf.o \
 	  lattice.o lattice_ec.o lattice_ivm_core.o lattice_ivm_supp.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..578d05b 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,13 @@ 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..22c4373
--- /dev/null
+++ b/common/redboot.c
@@ -0,0 +1,978 @@
+/*
+ * (C) Copyright 2006 - Embedded Alley Solutions Inc.
+ * by Pantelis Antoniou, pantelis at embeddedalley.com
+ *
+ * Based on Linux & RedBoot code fragments 
+ *   by David Woodhouse, dwmw2 at infradead.org
+ *   by Mark Sattler, msalter at redhat.com
+ *   by Gary Thomas, gthomas at 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; 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; 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; 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..620fe86
--- /dev/null
+++ b/include/redboot.h
@@ -0,0 +1,98 @@
+/*
+ * (C) Copyright 2006 - Embedded Alley Solutions Inc.
+ * by Pantelis Antoniou, pantelis at embeddedalley.com
+ *
+ * Based on Linux & RedBoot code fragments 
+ *   by David Woodhouse, dwmw2 at infradead.org
+ *   by Mark Sattler, msalter at redhat.com
+ *   by Gary Thomas, gthomas at 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(u32))];
+	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 */
+#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




More information about the U-Boot mailing list