Adds support for sysupgrade on ixp4xx-based platforms.
Tested on ADI Engineering Pronghorn.

Signed-off-by: Frank Laub <[email protected]>
---
 package/fis/Makefile                               |   36 +
 package/fis/src/Makefile                           |    6 +
 package/fis/src/crc.c                              |   88 +++
 package/fis/src/crc.h                              |   25 +
 package/fis/src/fis.c                              |  705 ++++++++++++++++++++
 scripts/ixp4xximage.sh                             |   68 ++
 target/linux/ixp4xx/Makefile                       |    2 +-
 .../ixp4xx/base-files/lib/upgrade/platform.sh      |   55 ++
 target/linux/ixp4xx/image/Makefile                 |   10 +
 .../701-redboot-fis-directory-readable.patch       |   26 +
 tools/firmware-utils/Makefile                      |    1 +
 tools/firmware-utils/src/redboot_crc32.c           |   43 ++
 12 files changed, 1064 insertions(+), 1 deletions(-)
 create mode 100644 package/fis/Makefile
 create mode 100644 package/fis/src/Makefile
 create mode 100644 package/fis/src/crc.c
 create mode 100644 package/fis/src/crc.h
 create mode 100644 package/fis/src/fis.c
 create mode 100755 scripts/ixp4xximage.sh
 create mode 100644 target/linux/ixp4xx/base-files/lib/upgrade/platform.sh
 create mode 100644 
target/linux/ixp4xx/patches-2.6.37/701-redboot-fis-directory-readable.patch
 create mode 100644 tools/firmware-utils/src/redboot_crc32.c

diff --git a/package/fis/Makefile b/package/fis/Makefile
new file mode 100644
index 0000000..b86a359
--- /dev/null
+++ b/package/fis/Makefile
@@ -0,0 +1,36 @@
+#
+# Copyright (C) 2006-2009 OpenWrt.org
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+
+include $(TOPDIR)/rules.mk
+include $(INCLUDE_DIR)/package.mk
+
+PKG_NAME:=fis
+PKG_VERSION:=0.0.1
+PKG_RELEASE:=1
+
+define Package/fis
+  SECTION:=utils
+  CATEGORY:=Base system
+  TITLE:=Update utility for fis directory
+endef
+
+define Package/fis/description
+ This package contains an utility useful during upgrade from other firmware or 
+ older OpenWrt releases.
+endef
+
+define Build/Prepare
+       mkdir -p $(PKG_BUILD_DIR)
+       $(CP) ./src/* $(PKG_BUILD_DIR)/
+endef
+
+define Package/fis/install
+       $(INSTALL_DIR) $(1)/sbin
+       $(INSTALL_BIN) $(PKG_BUILD_DIR)/fis $(1)/sbin/
+endef
+
+$(eval $(call BuildPackage,fis))
diff --git a/package/fis/src/Makefile b/package/fis/src/Makefile
new file mode 100644
index 0000000..a097692
--- /dev/null
+++ b/package/fis/src/Makefile
@@ -0,0 +1,6 @@
+CFLAGS += -std=c99
+
+fis: crc.o fis.o
+
+clean:
+       -rm -f fis *.o
diff --git a/package/fis/src/crc.c b/package/fis/src/crc.c
new file mode 100644
index 0000000..a9a7181
--- /dev/null
+++ b/package/fis/src/crc.c
@@ -0,0 +1,88 @@
+/*
+ * crc.c
+ *
+ * $Id: crc.c,v 1.1 2006/02/13 09:58:08 andrzej Exp $
+ *
+ * Gary S. Brown's CRC
+ * Code based on Gary S. Brown CRC (1986). 
+ * Generation polynomial is:
+ *   X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 
+ *
+ * Copyright (C) 2006 Ekiert sp z o.o.
+ * Author: Andrzej Ekiert <[email protected]>
+ *
+ * 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. 
+ */
+
+#include <stdint.h>
+
+#include "crc.h"
+
+static const uint32_t crc32_tab[] = {
+      0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
+      0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
+      0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
+      0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
+      0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
+      0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
+      0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
+      0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
+      0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
+      0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
+      0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
+      0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
+      0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
+      0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
+      0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
+      0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
+      0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
+      0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
+      0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
+      0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
+      0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
+      0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
+      0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
+      0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
+      0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
+      0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
+      0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
+      0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
+      0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
+      0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
+      0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
+      0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
+      0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
+      0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
+      0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
+      0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
+      0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
+      0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
+      0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
+      0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
+      0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
+      0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
+      0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
+      0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
+      0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
+      0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
+      0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
+      0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
+      0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
+      0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
+      0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
+      0x2d02ef8dL
+   };
+
+uint32_t crc32(uint8_t *s, uint32_t len)
+{
+       uint32_t i, val = 0;
+
+       for (i = 0;  i < len;  i++) {
+               val = crc32_tab[(val^s[i]) & 0xff] ^ (val >> 8);
+       }
+       return val;
+}
+
diff --git a/package/fis/src/crc.h b/package/fis/src/crc.h
new file mode 100644
index 0000000..481da82
--- /dev/null
+++ b/package/fis/src/crc.h
@@ -0,0 +1,25 @@
+/*
+ * crc.h
+ *
+ * $Id: crc.h,v 1.1 2006/02/13 09:58:08 andrzej Exp $
+ *
+ * Gary S. Brown's CRC - header. 
+ *
+ * Copyright (C) 2006 Ekiert sp z o.o.
+ * Author: Andrzej Ekiert <[email protected]>
+ *
+ * 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. 
+ */
+
+#ifndef CRC_H
+#define CRC_H
+
+#include <stdint.h>
+
+uint32_t crc32(uint8_t *s, uint32_t len);
+
+#endif //CRC_H
+
diff --git a/package/fis/src/fis.c b/package/fis/src/fis.c
new file mode 100644
index 0000000..c558c0b
--- /dev/null
+++ b/package/fis/src/fis.c
@@ -0,0 +1,705 @@
+// fis.c
+// see http://svn.chezphil.org/utils
+// Based on the C++ version in fis.cc
+// To compile, use --std=c99
+
+// (C) 2007 Philip Endecott
+
+// 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
+// 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 <stdint.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "crc.h"
+
+// Report an error and terminate:
+
+static void fatal(const char* msg)
+{
+  fputs(msg,stderr);
+  fputc('\n',stderr);
+  exit(1);
+}
+
+
+// Macro to call a library function and check the result, printing an 
appropriate error 
+// message if it fails.  E.g. open() returns -1 if it fails, so you can write:
+// CHECK(fd=open(fn),-1);
+// (But you can't write CHECK(int fd=open...), even in c99 mode.)
+
+#define CHECK(what,errcode) if ((what)==errcode) { perror(#what); exit(1); }
+
+
+// Wrapper for malloc() that checks for out-of-memory and other errors:
+
+static void* chk_malloc(size_t size)
+{
+  void* ptr = malloc(size);
+  if (!ptr) {
+    perror("malloc");
+    exit(1);
+  }
+  return ptr;
+}
+
+
+// Wrappers for read() and write() that check for error and unexpected 
conditions.
+
+static void chk_read(int fd, void* buf, size_t count)
+{
+  size_t rc = read(fd,buf,count);
+  if (rc!=count) {
+    if ((int)rc==-1) {
+      perror("read");
+      exit(1);
+    } else {
+      fatal("short read");
+    }
+  }
+}
+
+static void chk_write(int fd, const void* buf, size_t count)
+{
+  size_t rc = write(fd,buf,count);
+  if (rc!=count) {
+    if ((int)rc==-1) {
+      perror("write");
+      exit(1);
+    } else {
+      fatal("short write");
+    }
+  }
+}
+
+
+// Get the size of a file
+
+unsigned int filesize(int fd)
+{
+  return lseek(fd,0,SEEK_END);
+}
+
+
+
+static uint32_t swap_end_32(uint32_t data)
+{
+  uint32_t r = data>>24;
+  r |= (data>>8)&0x0000ff00;
+  r |= (data<<8)&0x00ff0000;
+  r |= data<<24;
+  return r;
+}
+
+
+
+// Parse a string containing a number.  If it starts with 0x, parse the rest 
as hex.
+// !!! NOTE: also parses octal constants that start with '0' !!!
+
+static unsigned int str_to_int_maybe_hex(const char* s)
+{
+  char* endptr;
+  unsigned int i = strtoul(s,&endptr,0);
+  if (*s!='\0' && *endptr=='\0') {
+    return i;
+  }
+  fatal("junk after number");
+}
+
+
+
+// This is taken from drivers/mtd/redboot.c in the Linux source
+struct fis_image_desc {
+  unsigned char name[16];  // Null terminated name
+  uint32_t  flash_base;    // Address within FLASH of image
+  uint32_t  mem_base;      // Address in memory where it executes
+  uint32_t  size;          // Length of image
+  uint32_t  entry_point;   // Execution entry point
+  uint32_t  data_length;   // Length of actual data
+  uint32_t  skips[53];
+  uint32_t  desc_cksum;    // Checksum over image descriptor
+  uint32_t  file_cksum;    // Checksum over image data
+};
+
+static void dump_desc(FILE* f, const struct fis_image_desc* d)
+{
+  fprintf(f,"%16s: addr = 0x%08x, size = 0x%08x, checksum = 0x%08x\n",
+             d->name,     d->flash_base, d->size, d->file_cksum);
+  for (unsigned int i=0; i<(sizeof(d->skips)/4); ++i) {
+    if (d->skips[i]==0x736b6970 || d->skips[i]==0x70696b73) { // "skip"
+      uint32_t offset = d->skips[i+1];
+      uint32_t length = d->skips[i+2];
+      fprintf(stderr,"                    skip: %08x + %08x\n",
+                                                offset,length);
+      i+=2;
+    }
+  }
+}
+
+
+// Use a non-invasive list to represent the entire directory.
+// Yes, this probably does look a bit over-the-top, but it's a close match to 
what the 
+// C++ version does, making the other code simpler.
+
+// Each element of the list has one of the following handle structures:
+
+struct dirnode {
+  struct fis_image_desc* entry;
+  struct dirnode* prev;
+  struct dirnode* next;
+};
+
+// The list is circularly linked, i.e. the last node's next pointer points to 
the first 
+// node, and the first node's prev pointer points to the last node.  The start 
of the 
+// list is distinguished by the fact that the list as a whole is represented 
by a 
+// pointer to the first node.
+
+// Functions that do read-only operations on a list take a dir_t parameter.  
Functions 
+// that do read-write operations take a dir_t* since they may have to modify 
this 
+// pointer if the first element changes.  An empty list is represented by a 
NULL 
+// pointer.
+
+// Functions that operate on particular elements of a list use an iter_t 
parameter.  This 
+// is just a pointer to a node handle, but can be treated as opaque.  The 
for-each macro, 
+// below, supplies iter_t iterators which the caller dereferences using get() 
to get the 
+// struct fis_image_desc.
+
+typedef struct dirnode* dir_t;
+typedef struct dirnode* iter_t;
+
+
+static unsigned int dir_size_tail(dir_t d, struct dirnode* n)
+{
+  if (n==d) {
+    return 0;
+  } else {
+    return 1 + dir_size_tail(d,n->next);
+  }
+}
+
+
+// Return the number of entries in a directory.
+
+static unsigned int dir_size(dir_t d)
+{
+  if (!d) {
+    return 0;
+  } else {
+    return 1 + dir_size_tail(d,d->next);
+  }
+}
+
+
+// Create a new empty directory.
+
+static void dir_create(dir_t* dir)
+{
+  *dir = NULL;
+}
+
+
+// Append an entry to a directory.
+// The list takes ownership of the new entry.
+
+static void dir_append(dir_t* dir, struct fis_image_desc* d)
+{
+  struct dirnode* n = chk_malloc(sizeof(struct dirnode));
+  n->entry = d;
+  
+  if (*dir) {
+    n->next = *dir;
+    n->prev = (*dir)->prev;
+    (*dir)->prev->next = n;
+    (*dir)->prev = n;
+  } else {
+    n->next = n;
+    n->prev = n;
+    (*dir) = n;
+  }
+}
+
+
+// Insert an entry into a directory after the entry referred to by the 
iterator 'after'.
+// If 'after' is NULL, insert at the beginning.
+// The list takes ownership of the new entry.
+
+static void dir_insert(dir_t* dir, iter_t after, struct fis_image_desc* d)
+{
+  // Special case, directory is empty.
+  if (!(*dir)) {
+    dir_append(dir,d);
+    return;
+  }
+
+  struct dirnode* n = chk_malloc(sizeof(struct dirnode));
+  n->entry = d;
+
+  if (!after) {
+    after = (*dir)->prev;
+    *dir = n;
+  }
+  
+  n->prev = after;
+  n->next = after->next;
+  after->next->prev = n;
+  after->next = n;
+}
+
+
+// Remove an entry from a directory.
+// The entry is free()d.
+
+static void dir_erase(dir_t* dir, iter_t i)
+{
+  // Erasing the first element:
+  if (i==(*dir)) {
+    // Erasing the first and only element:
+    if (i->next==i) {
+      *dir = NULL;
+    } else {
+      *dir = i->next;
+    }
+  }
+
+  i->next->prev = i->prev;
+  i->prev->next = i->next;
+
+  free(i->entry);
+  free(i);
+}
+
+
+// This macro can be used to iterate through a directory.
+// It takes the directory and an iterator, which it will declare, as 
parameters.
+// Example:
+// FOR_EACH_DIR_ENTRY(dir,i) {
+//   dump_desc(stdout,get(i));
+// }
+
+#define FOR_EACH_DIR_ENTRY(dir,iterator) \
+for (iter_t iterator = dir;  \
+     iterator; \
+     iterator = (iterator->next==dir) ? NULL : iterator->next)
+
+// Use this to get the struct fis_image_desc from the iterator:
+#define get(iterator) (iterator->entry)
+
+
+
+static void check_dev(const char* device)
+{
+  if (!device[0]) {
+    fatal("You must specify a device using -d");
+  }
+}
+
+
+void check_checksum(const struct fis_image_desc* d)
+{
+  // This isn't checked by the kernel mtd driver, which has this 
+  // comment: "RedBoot doesn't actually write the desc_cksum field yet 
+  // AFAICT".  I don't know what checksum is supposed to be used here.
+}
+
+void compute_checksum(struct fis_image_desc* d)
+{
+  // ditto
+}
+
+
+static void swap_entry_endianness(struct fis_image_desc* d)
+{
+  d->flash_base  = swap_end_32(d->flash_base);
+  d->mem_base    = swap_end_32(d->mem_base);
+  d->size        = swap_end_32(d->size);
+  d->entry_point = swap_end_32(d->entry_point);
+  d->data_length = swap_end_32(d->data_length);
+  d->file_cksum  = swap_end_32(d->file_cksum);
+  for (unsigned int i=0; i<(sizeof(d->skips)/4); ++i) {
+    d->skips[i] = swap_end_32(d->skips[i]);
+  }
+}
+
+
+static void load_dir(int fd, int offset, int* size_p, bool swap_endianness,
+                     dir_t* dir)
+{
+  dir_create(dir);
+  if ((*size_p)==-1) {
+    (*size_p) = filesize(fd)-offset;
+  }
+  CHECK(lseek(fd,offset,SEEK_SET),-1);
+  int num_entries = (*size_p)/sizeof(struct fis_image_desc);
+  for (int i=0; i<num_entries; ++i) {
+    struct fis_image_desc* d = chk_malloc(sizeof(struct fis_image_desc));
+    chk_read(fd,d,sizeof(struct fis_image_desc));
+    if (d->name[0]!=0xff) {
+      check_checksum(d);
+      if (swap_endianness) {
+        swap_entry_endianness(d);
+      }
+      dir_append(dir,d);
+    }
+  }
+}
+
+
+static void write_blank_entries(int fd, int n)
+{
+  char dummy[sizeof(struct fis_image_desc)];
+  for (unsigned int i=0; i<sizeof(struct fis_image_desc); ++i) {
+    dummy[i] = 0xff;
+  }
+  for (int i=0; i<n; ++i) {
+    chk_write(fd,dummy,sizeof(struct fis_image_desc));
+  }  
+}
+
+
+static void save_dir(int fd, int offset, int size, bool swap_endianness,
+                     const dir_t dir)
+{
+  CHECK(lseek(fd,offset,SEEK_SET),-1);
+  unsigned int num_entries = size/sizeof(struct fis_image_desc);
+  if (num_entries<dir_size(dir)) {
+    fatal("Too many entries for directory");
+  }
+  FOR_EACH_DIR_ENTRY(dir,i) {
+    compute_checksum(get(i));
+    if (swap_endianness) {
+      swap_entry_endianness(get(i));
+    }
+    chk_write(fd,get(i),sizeof(struct fis_image_desc));
+  }
+  write_blank_entries(fd,num_entries-dir_size(dir));
+}
+
+
+static void fis_list(const char* device, int offset, int size, bool 
swap_endianness)
+{
+  int fd;
+  CHECK(fd=open(device,O_RDONLY),-1);
+  dir_t dir;
+  load_dir(fd,offset,&size,swap_endianness,&dir);
+  FOR_EACH_DIR_ENTRY(dir,i) {
+    dump_desc(stdout,get(i));
+  }
+}
+
+
+static void fis_init(const char* device, int offset, int size)
+{
+  if (size==-1) {
+    fatal("size must be specified using -s");
+  }
+  int fd;
+  CHECK(fd=open(device,O_CREAT|O_RDWR,0666),-1);
+  CHECK(lseek(fd,offset,SEEK_SET),-1);
+  int num_entries = size/sizeof(struct fis_image_desc);
+  write_blank_entries(fd,num_entries);
+}
+
+
+static void check_overlap(const dir_t dir, uint32_t addr, uint32_t size)
+{
+  uint32_t end_addr = addr+size;
+  FOR_EACH_DIR_ENTRY(dir,i) {
+    if (addr<(get(i)->flash_base+get(i)->size)
+        && end_addr>get(i)->flash_base) {
+      fatal("New partition overlaps existing partitions");
+    }
+  }
+}
+
+
+static void fis_create(const char* device, int offset, int size, bool 
swap_endianness,
+                       int argc, char* argv[])
+{
+  struct fis_image_desc* d = chk_malloc(sizeof(struct fis_image_desc));
+  d->mem_base = 0;
+  d->entry_point = 0;
+  d->data_length = 0;
+  for (unsigned int i=0; i<(sizeof(d->skips)/4); ++i) {
+    d->skips[i] = 0;
+  }
+  d->desc_cksum = 0;
+  d->file_cksum = 0;
+  
+  for (int i=0; i<argc; ++i) {
+    char* arg=argv[i];
+    if (strcmp(arg,"-l")==0) {
+      if (i==argc-1) {
+        fatal("argumnet missing for -l");
+      }
+      ++i;
+      d->size = str_to_int_maybe_hex(argv[i]);
+    } else if (strcmp(arg,"-f")==0) {
+      if (i==argc-1) {
+        fatal("argumnet missing for -f");
+      }
+      ++i;
+      d->flash_base = str_to_int_maybe_hex(argv[i]);
+    } else if (strcmp(arg,"-n")==0) {
+      if (i==argc-1) {
+        fatal("argumnet missing for -n");
+      }
+      ++i;
+      char* name = argv[i];
+      if (strlen(name)>=16) {
+        fatal("name too long, max 16 chars including terminating null");
+      }
+      for (int j=0; j<16; j++) {
+        char c = name[j];
+        d->name[j] = c;
+        if (!c) {
+          for (; j<16; ++j) {
+            d->name[j]=0;
+          }
+          break;
+        }
+      }
+    } else {
+      fputs("Unrecognised option '",stderr);
+      fputs(arg,stderr);
+      fputs("'\n",stderr);
+      exit(1);
+    }
+  }
+
+  int fd;
+  CHECK(fd=open(device,O_RDWR),-1);
+  dir_t dir;
+  load_dir(fd,offset,&size,swap_endianness,&dir);
+  check_overlap(dir,d->flash_base,d->size);
+  iter_t after = NULL;
+  FOR_EACH_DIR_ENTRY(dir,i) {
+    if (get(i)->flash_base > d->flash_base) {
+      break;
+    }
+    after = i;
+  }
+  dir_insert(&dir,after,d);
+  save_dir(fd,offset,size,swap_endianness,dir);
+}
+
+static void fis_checksum(const char* device, int offset, int size, bool 
swap_endianness,
+                       int argc, char* argv[])
+{
+
+  uint32_t crc=0;
+  uint32_t length=0;
+       char* name = NULL;
+       char* filename = NULL;
+  int fd;
+  char *buf;
+  struct stat stat_buf;
+
+  
+       for (int i=0; i<argc; ++i) {
+    char* arg=argv[i];
+    if (strcmp(arg,"-f")==0) {
+      if (i==argc-1) {
+        fatal("argumnet missing for -f");
+      }
+      ++i;
+      filename = argv[i];
+    } else if (strcmp(arg,"-n")==0) {
+      if (i==argc-1) {
+        fatal("argumnet missing for -n");
+      }
+      ++i;
+      name = argv[i];
+      if (strlen(name)>=16) {
+        fatal("name too long, max 16 chars including terminating null");
+      }
+    } else if (strcmp(arg, "-c")==0) {
+       if (i==argc-1) {
+               fatal("argument missing for -c");
+       }
+       ++i;
+       crc=str_to_int_maybe_hex(argv[i]);
+    } else if (strcmp(arg, "-l")==0) {
+       if (i==argc-1) {
+               fatal("argument missing for -l");
+       }
+       ++i;
+       length=str_to_int_maybe_hex(argv[i]);
+    } else {
+      fputs("Unrecognised option '",stderr);
+      fputs(arg,stderr);
+      fputs("'\n",stderr);
+      exit(1);
+    }
+  }
+       if (crc==0)
+       {
+
+  if (name == NULL || filename == NULL)
+    fatal("filename and partition name must be specified");
+
+  CHECK(fd=open(filename,O_RDONLY),-1);
+  CHECK(stat(filename, &stat_buf),-1);
+  buf = (char*)chk_malloc(stat_buf.st_size);
+  CHECK(read(fd,buf,stat_buf.st_size),-1);
+  crc = crc32(buf,stat_buf.st_size);
+  close(fd);
+  free(buf);
+}
+
+  CHECK(fd=open(device,O_RDWR),-1);
+  dir_t dir;
+  load_dir(fd,offset,&size,swap_endianness,&dir);
+  iter_t after = NULL;
+  FOR_EACH_DIR_ENTRY(dir,i) {
+    if (strcmp(get(i)->name, name) == 0) {
+      get(i)->file_cksum = crc;
+       if (length != 0)
+               get(i)->data_length = length;
+       else
+               get(i)->data_length = stat_buf.st_size;
+      break;
+    }
+  }
+  save_dir(fd,offset,size,swap_endianness,dir);
+}
+
+static void fis_delete(const char* device, int offset, int size, bool 
swap_endianness,
+                       char* name)
+{
+  int fd;
+  CHECK(fd=open(device,O_RDWR),-1);
+  dir_t dir;
+  load_dir(fd,offset,&size,swap_endianness,&dir);
+
+  FOR_EACH_DIR_ENTRY(dir,i) {
+    char* this_name = get(i)->name;
+    if (strcmp(this_name,name)==0) {
+      dir_erase(&dir,i);
+      save_dir(fd,offset,size,swap_endianness,dir);
+      return;
+    }
+  }
+
+  fatal("No partition found with specified name");
+}
+
+
+static void usage()
+{
+  fputs("Usage:\n"
+        "  fis [options] list\n"
+        "  fis [options] init\n"
+        "  fis [options] create -f address -l size -n name\n"
+        "  fis [options] delete name\n"
+        "  fis [options] checksum -f filename -n name -c cooked_crc -l 
cooked_len\n"
+        "Options:\n"
+        "  -d device    specify /dev/mtd* device containing directory\n"
+        "  -o offset    specify offset into device of start of directory\n"
+        "               (in decimal; prefix with 0x for hex)\n"
+        "  -s size      specify size of directory in bytes\n"
+        "  -e           swap endianness\n", stderr);
+}
+
+
+int main(int argc, char* argv[])
+{
+    if (argc==1) {
+      usage();
+      exit(1);
+    }
+
+    char* device="";
+    int offset=0;
+    int size=-1;
+    bool swap_endianness=false;
+
+    for (int i=1; i<argc; ++i) {
+      char* arg = argv[i];
+      if (strcmp(arg,"-d")==0) {
+        if (device[0]) {
+          fatal("-d option used more than once");
+        }
+        if (i==argc-1) {
+          fatal("-d option is missing its parameter");
+        }
+        ++i;
+        device = argv[i];
+      } else if (strcmp(arg,"-o")==0) {
+        if (offset!=0) {
+          fatal("-o option used more than once");
+        }
+        if (i==argc-1) {
+          fatal("-o option is missing its parameter");
+        }
+        ++i;
+        offset = str_to_int_maybe_hex(argv[i]);
+      } else if (strcmp(arg,"-s")==0) {
+        if (size!=-1) {
+          fatal("-s option used more than once");
+        }
+        if (i==argc-1) {
+          fatal("-s option is missing its parameter");
+        }
+        ++i;
+        size = str_to_int_maybe_hex(argv[i]);
+      } else if (strcmp(arg,"-e")==0) {
+        swap_endianness = true;
+      } else if (strcmp(arg,"list")==0) {
+        if (i!=argc-1) {
+          fatal("Extra arguments after 'list'");
+        }
+        check_dev(device);
+        fis_list(device,offset,size,swap_endianness);
+      } else if (strcmp(arg,"init")==0) {
+        if (i!=argc-1) {
+          fatal("Extra arguments after 'init'");
+        }
+        check_dev(device);
+        fis_init(device,offset,size);
+      } else if (strcmp(arg,"create")==0) {
+        check_dev(device);
+        fis_create(device,offset,size,swap_endianness,
+                   argc-i-1,&argv[i+1]);
+        break;
+      } else if (strcmp(arg,"checksum")==0) {
+        check_dev(device);
+        fis_checksum(device,offset,size,swap_endianness,
+                   argc-i-1,&argv[i+1]);
+        break;
+      } else if (strcmp(arg,"delete")==0) {
+        if (i!=argc-2) {
+          fatal("Exactly one argumnet required after 'delete'");
+        }
+        ++i;
+        char* name = argv[i];
+        check_dev(device);
+        fis_delete(device,offset,size,swap_endianness,name);
+      } else {
+        fputs("unrecognised argument '",stderr);
+        fputs(arg,stderr);
+        fputs("'\n",stderr);
+        usage();
+        exit(1);
+      }
+    }
+  exit(0);
+}
+
diff --git a/scripts/ixp4xximage.sh b/scripts/ixp4xximage.sh
new file mode 100755
index 0000000..81cab12
--- /dev/null
+++ b/scripts/ixp4xximage.sh
@@ -0,0 +1,68 @@
+#!/bin/sh
+
+if [ $# != 4 ] ; then
+       echo "Invalid number of parameters."
+       exit 1
+fi
+
+KERNEL_IMAGE=$1
+ROOTFS_IMAGE=$2
+COMBINED_IMAGE=$3
+BOARD=$4
+
+PARAMS_SH=$BIN_DIR/params.sh
+PARAMS_TAR=$BIN_DIR/params.tar
+
+echo "Building image for $BOARD"
+
+case $BOARD in
+cambria)
+       LINUX_START=0x50080000
+       ROOT_START=0x50200000
+       FIS_PART=mtd4
+       break
+       ;;
+pronghorn)
+       LINUX_START=0x50060000
+       ROOT_START=0x501E0000
+       FIS_PART=mtd5
+       break
+       ;;
+*)
+       echo "Unsupported board"
+       ;;
+esac
+
+LINUX_LENGTH_HEX=0x180000
+LINUX_LENGTH_DEC=1572864
+LINUX_LENGTH_BLOCKS=12
+
+dd if=$KERNEL_IMAGE bs=$LINUX_LENGTH_DEC conv=sync of=${KERNEL_IMAGE}.padded
+
+LINUX_CRC=$( $STAGING_DIR_HOST/bin/redboot_crc32 ${KERNEL_IMAGE}.padded )
+ROOT_LENGTH=$( dd if=$ROOTFS_IMAGE bs=128k conv=sync of=/dev/null 2>&1 | grep 
bytes | cut -d" " -f1 | awk '{printf "0x%x\n", $1}')
+ROOT_LENGTH_BLOCKS=$( dd if=$ROOTFS_IMAGE bs=128k conv=sync of=/dev/null 2>&1 
| grep out | cut -d"+" -f1 )
+MD5SUM=$( (dd if=${KERNEL_IMAGE}.padded bs=128k conv=sync 2>/dev/null && dd 
if=$ROOTFS_IMAGE bs=128k conv=sync 2>/dev/null ) | md5sum -) ; 
MD5SUM=${MD5SUM%% *}
+
+echo "LINUX_START=$LINUX_START" > $PARAMS_SH
+echo "LINUX_LENGTH=$LINUX_LENGTH_HEX" >> $PARAMS_SH
+echo "LINUX_LENGTH_BLOCKS=$LINUX_LENGTH_BLOCKS" >> $PARAMS_SH
+echo "LINUX_CRC=$LINUX_CRC" >> $PARAMS_SH
+echo "ROOT_START=$ROOT_START" >> $PARAMS_SH
+echo "ROOT_LENGTH=$ROOT_LENGTH" >> $PARAMS_SH
+echo "ROOT_LENGTH_BLOCKS=$ROOT_LENGTH_BLOCKS" >> $PARAMS_SH
+echo "BOARD=$BOARD" >> $PARAMS_SH
+echo "FIS_PART=$FIS_PART" >> $PARAMS_SH
+echo "MD5SUM=$MD5SUM" >> $PARAMS_SH
+echo "FIS_OFFSET=0x400" >> $PARAMS_SH
+echo "FIS_SIZE=0x100" >> $PARAMS_SH
+echo "PART_COUNT=5" >> $PARAMS_SH
+echo "BLOCK_SIZE=256" >> $PARAMS_SH
+
+
+tar --create --file=$PARAMS_TAR --directory=$BIN_DIR params.sh
+dd if=$PARAMS_TAR bs=128k conv=sync of=$COMBINED_IMAGE
+cat ${KERNEL_IMAGE}.padded >> $COMBINED_IMAGE
+dd if=$ROOTFS_IMAGE bs=128k conv=sync,notrunc oflag=append of=$COMBINED_IMAGE
+
+rm $PARAMS_TAR $PARAMS_SH ${KERNEL_IMAGE}.padded
diff --git a/target/linux/ixp4xx/Makefile b/target/linux/ixp4xx/Makefile
index 87e1e06..2abcb95 100644
--- a/target/linux/ixp4xx/Makefile
+++ b/target/linux/ixp4xx/Makefile
@@ -17,6 +17,6 @@ LINUX_VERSION:=2.6.37.6
 
 include $(INCLUDE_DIR)/target.mk
 
-DEFAULT_PACKAGES += ixp4xx-microcode fconfig
+DEFAULT_PACKAGES += ixp4xx-microcode fconfig fis
 
 $(eval $(call BuildTarget))
diff --git a/target/linux/ixp4xx/base-files/lib/upgrade/platform.sh 
b/target/linux/ixp4xx/base-files/lib/upgrade/platform.sh
new file mode 100644
index 0000000..eec3602
--- /dev/null
+++ b/target/linux/ixp4xx/base-files/lib/upgrade/platform.sh
@@ -0,0 +1,55 @@
+RAMFS_COPY_BIN=/sbin/fis
+IMAGE_BLOCK_SIZE=128k
+
+platform_check_image() {
+       [ "$ARGC" -gt 1 ] && return 1
+
+       HARDWARE=$(cat /proc/cpuinfo  | grep Hardware)
+       if echo $HARDWARE | grep -q Cambria ; then
+               MY_BOARD=cambria
+       elif echo $HARDWARE | grep -q Pronghorn; then
+               MY_BOARD=pronghorn
+       else
+               return 1
+       fi
+
+       cd /tmp
+       dd if=$1 bs=$IMAGE_BLOCK_SIZE count=1 2>/dev/null| tar x
+       . /tmp/params.sh
+       if [ "$MY_BOARD" != "$BOARD" ] ; then
+               echo "Invalid image: hardware mismatch - expected ${MY_BOARD}, 
extracted ${BOARD}"
+               return 1
+       fi
+       IMG_MD5=$(dd if="$1" bs=$IMAGE_BLOCK_SIZE skip=1 2>/dev/null | md5sum 
-); IMG_MD5="${IMG_MD5%% *}"
+       if [ "$IMG_MD5" != "$MD5SUM" ] ; then
+               echo "Invalid image: md5sum mismatch - expected ${MD5SUM}, 
extracted ${IMG_MD5}"
+               return 1
+       fi
+
+       return 0
+}
+
+platform_do_upgrade()
+{
+       PARTITIONS=/tmp/partitions
+       LEFT_OVER=/tmp/leftover
+
+       cd /tmp
+       dd if=$1 bs=$IMAGE_BLOCK_SIZE count=1 2>/dev/null| tar x
+       . /tmp/params.sh
+       dd if=/dev/$FIS_PART bs=$BLOCK_SIZE count=$PART_COUNT of=$PARTITIONS 
2>/dev/null
+       dd if=/dev/$FIS_PART bs=$BLOCK_SIZE skip=$PART_COUNT of=$LEFT_OVER 
2>/dev/null
+
+       fis -d $PARTITIONS -o $FIS_OFFSET -s $FIS_SIZE init
+       fis -d $PARTITIONS -o $FIS_OFFSET create -f $ROOT_START -l $ROOT_LENGTH 
-n rootfs
+       fis -d $PARTITIONS checksum -c $LINUX_CRC -l $LINUX_LENGTH -n linux
+
+       cat $LEFT_OVER >> $PARTITIONS
+       mtd write $PARTITIONS $FIS_PART
+
+       append=""
+       [ -f "$CONF_TAR" -a "$SAVE_CONFIG" -eq 1 ] && append="-j $CONF_TAR"
+
+       dd if="$1" bs=$IMAGE_BLOCK_SIZE skip=1 count=$LINUX_LENGTH_BLOCKS 
2>/dev/null | mtd write - linux
+       dd if="$1" bs=$IMAGE_BLOCK_SIZE skip=$((1+$LINUX_LENGTH_BLOCKS)) 
count=$ROOT_LENGTH_BLOCKS 2>/dev/null | mtd $append write - rootfs
+}
diff --git a/target/linux/ixp4xx/image/Makefile 
b/target/linux/ixp4xx/image/Makefile
index 7a69998..f90e50d 100644
--- a/target/linux/ixp4xx/image/Makefile
+++ b/target/linux/ixp4xx/image/Makefile
@@ -31,6 +31,14 @@ define Image/Build/Freecom
        rm -f $(TARGET_DIR)/zImage
 endef
 
+define Image/Build/ixp4xx
+       BIN_DIR=$(BIN_DIR) STAGING_DIR_HOST=$(STAGING_DIR_HOST) 
$(TOPDIR)/scripts/ixp4xximage.sh \
+               $(BIN_DIR)/openwrt-$(2)-zImage \
+               $(BIN_DIR)/$(IMG_PREFIX)-$(1).img \
+               $(BIN_DIR)/openwrt-$(2)-$(1)-combined.img \
+               $(2)
+endef
+
 define Image/Prepare
        cp $(LINUX_DIR)/arch/arm/boot/zImage $(KDIR)/zImage
 endef
@@ -59,6 +67,8 @@ define Image/Build/squashfs
        dd if=$(KDIR)/root.$(1) of=$(BIN_DIR)/$(IMG_PREFIX)-$(1).img bs=131072 
conv=sync
        $(call Image/Build/Linksys,$(1),nslu2,$(1))
        $(call Image/Build/Freecom,$(1),fsg3,$(1))
+       $(call Image/Build/ixp4xx,$(1),cambria,$(1))
+       $(call Image/Build/ixp4xx,$(1),pronghorn,$(1))
 endef
 
 $(eval $(call BuildImage))
diff --git 
a/target/linux/ixp4xx/patches-2.6.37/701-redboot-fis-directory-readable.patch 
b/target/linux/ixp4xx/patches-2.6.37/701-redboot-fis-directory-readable.patch
new file mode 100644
index 0000000..7641c7d
--- /dev/null
+++ 
b/target/linux/ixp4xx/patches-2.6.37/701-redboot-fis-directory-readable.patch
@@ -0,0 +1,26 @@
+--- a/drivers/mtd/redboot.c
++++ b/drivers/mtd/redboot.c
+@@ -124,6 +124,10 @@ static int parse_redboot_partitions(stru
+ 
+       numslots = (master->erasesize / sizeof(struct fis_image_desc));
+       for (i = 0; i < numslots; i++) {
++              if (buf[i].name[0] == 0xff) {
++                      i = numslots;
++                      break;
++              }
+               if (!memcmp(buf[i].name, "FIS directory", 14)) {
+                       /* This is apparently the FIS directory entry for the
+                        * FIS directory itself.  The FIS directory size is
+@@ -274,6 +278,12 @@ static int parse_redboot_partitions(stru
+                               !memcmp(names, "FIS directory", 14)) {
+                       parts[i].mask_flags = MTD_WRITEABLE;
+               }
++#else
++              if (!memcmp(names, "FIS directory", 14)) {
++                      if ((fl->img->size & (master->erasesize - 1)) != 0) {
++                              parts[i].size = (fl->img->size + 
(master->erasesize - 1)) & ~(master->erasesize - 1);
++                      }
++              }
+ #endif
+               names += strlen(names)+1;
+ 
diff --git a/tools/firmware-utils/Makefile b/tools/firmware-utils/Makefile
index 4364223..cb87d22 100644
--- a/tools/firmware-utils/Makefile
+++ b/tools/firmware-utils/Makefile
@@ -57,6 +57,7 @@ define Host/Compile
        $(call cc,mkedimaximg)
        $(call cc,mkbrncmdline)
        $(call cc,mkbrnimg)
+       $(call cc,redboot_crc32 cyg_crc32)
 endef
 
 define Host/Install
diff --git a/tools/firmware-utils/src/redboot_crc32.c 
b/tools/firmware-utils/src/redboot_crc32.c
new file mode 100644
index 0000000..356e965
--- /dev/null
+++ b/tools/firmware-utils/src/redboot_crc32.c
@@ -0,0 +1,43 @@
+#include <stdint.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+//#include <string.h>
+//#include <stdbool.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "cyg_crc.h"
+
+static void fis_checksum(const char* filename)
+{
+       int fd;
+       char *buf;
+       struct stat stat_buf;
+       uint32_t crc;
+
+       fd=open(filename,O_RDONLY);
+       if (fd == -1)
+       {
+               printf("Unable to open file %s\n", filename);
+               exit(1);
+       }
+       stat(filename, &stat_buf);
+       buf = (char*)malloc(stat_buf.st_size);
+       read(fd,buf,stat_buf.st_size);
+       crc = cyg_crc32(buf,stat_buf.st_size);
+       printf("0x%X\n", crc);
+       close(fd);
+       free(buf);
+}
+
+int main(int argc, char* argv[])
+{
+       if (argc != 2) {
+               printf("Invalid number of arguments!\n");
+               exit(1);
+       }
+        fis_checksum(argv[1]);
+       exit(0);
+}
+
-- 
1.7.5.4

_______________________________________________
openwrt-devel mailing list
[email protected]
https://lists.openwrt.org/mailman/listinfo/openwrt-devel

Reply via email to