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
