summaryrefslogtreecommitdiff
path: root/packages/fis/files/fis.cc
diff options
context:
space:
mode:
Diffstat (limited to 'packages/fis/files/fis.cc')
-rw-r--r--packages/fis/files/fis.cc372
1 files changed, 372 insertions, 0 deletions
diff --git a/packages/fis/files/fis.cc b/packages/fis/files/fis.cc
new file mode 100644
index 0000000000..8c43eca811
--- /dev/null
+++ b/packages/fis/files/fis.cc
@@ -0,0 +1,372 @@
+// fis.cc
+// see http://svn.chezphil.org/utils
+// (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 <string>
+#include <iostream>
+#include <map>
+
+#include <boost/lexical_cast.hpp>
+#include <boost/format.hpp>
+
+#include "Exception.hh"
+#include "FileDescriptor.hh"
+#include "utils.hh"
+#include "endian.hh"
+
+#include <stdint.h>
+
+using namespace std;
+using namespace boost;
+using namespace pbe;
+
+
+static void check_dev(string device)
+{
+ if (device=="") {
+ throw "You must specify a device using -d";
+ }
+}
+
+
+// This is taken from drivers/mtd/redboot.c in the Linux source
+struct fis_image_desc {
+ 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
+};
+
+ostream& operator<<(ostream& strm, const fis_image_desc& d)
+{
+ strm << format("%16s: addr = 0x%08x, size = 0x%08x\n")
+ % (d.name) % (d.flash_base) % (d.size);
+ 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];
+ strm << format(" skip: %08x + %08x\n")
+ % (offset) % (length);
+ i+=2;
+ }
+ }
+ return strm;
+}
+
+
+void check_checksum(const 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(fis_image_desc& d)
+{
+ // ditto
+}
+
+
+typedef map<uint32_t,fis_image_desc> dir_t;
+
+
+static void swap_entry_endianness(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);
+ for (unsigned int i=0; i<(sizeof(d.skips)/4); ++i) {
+ d.skips[i] = swap_end_32(d.skips[i]);
+ }
+}
+
+
+static void load_dir(FileDescriptor& fd, int offset, int size, bool swap_endianness,
+ dir_t& dir)
+{
+ fd.seek(offset);
+ int num_entries = size/sizeof(fis_image_desc);
+ for (int i=0; i<num_entries; ++i) {
+ fis_image_desc d = fd.binread<fis_image_desc>();
+ if (d.name[0]!=static_cast<char>(0xff)) {
+ check_checksum(d);
+ if (swap_endianness) {
+ swap_entry_endianness(d);
+ }
+ dir[d.flash_base] = d;
+ }
+ }
+}
+
+
+static void write_blank_entries(FileDescriptor& fd, int n)
+{
+ char dummy[sizeof(fis_image_desc)];
+ for (unsigned int i=0; i<sizeof(fis_image_desc); ++i) {
+ dummy[i] = 0xff;
+ }
+ for (int i=0; i<n; ++i) {
+ fd.writeall(dummy,sizeof(fis_image_desc));
+ }
+}
+
+
+static void save_dir(FileDescriptor& fd, int offset, int size, bool swap_endianness,
+ const dir_t& dir)
+{
+ fd.seek(offset);
+ unsigned int num_entries = size/sizeof(fis_image_desc);
+ if (num_entries<dir.size()) {
+ throw "Too many entries for directory";
+ }
+ for (dir_t::const_iterator i=dir.begin();
+ i!=dir.end(); ++i) {
+ fis_image_desc d = i->second;
+ compute_checksum(d);
+ if (swap_endianness) {
+ swap_entry_endianness(d);
+ }
+ fd.binwrite<fis_image_desc>(d);
+ }
+ write_blank_entries(fd,num_entries-dir.size());
+}
+
+
+static void fis_list(string device, int offset, int size, bool swap_endianness)
+{
+ FileDescriptor fd(device,FileDescriptor::read_only);
+ dir_t dir;
+ load_dir(fd,offset,size,swap_endianness,dir);
+ for (dir_t::const_iterator i = dir.begin();
+ i != dir.end(); ++i) {
+ cout << i->second;
+ }
+}
+
+
+static void fis_init(string device, int offset, int size)
+{
+ FileDescriptor fd(device,FileDescriptor::create);
+ fd.seek(offset);
+ int num_entries = size/sizeof(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 (dir_t::const_iterator i = dir.begin();
+ i != dir.end(); ++i) {
+ if (addr<(i->second.flash_base+i->second.size)
+ && end_addr>i->second.flash_base) {
+ throw "New partition overlaps existing partitions";
+ }
+ }
+}
+
+
+static void fis_create(string device, int offset, int size, bool swap_endianness,
+ int argc, char* argv[])
+{
+ fis_image_desc d;
+ 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) {
+ string arg=argv[i];
+ if (arg=="-l") {
+ if (i==argc-1) {
+ throw "argumnet missing for -l";
+ }
+ ++i;
+ d.size = maybe_hex_string_to_int(argv[i]);
+ } else if (arg=="-f") {
+ if (i==argc-1) {
+ throw "argumnet missing for -f";
+ }
+ ++i;
+ d.flash_base = maybe_hex_string_to_int(argv[i]);
+ } else if (arg=="-n") {
+ if (i==argc-1) {
+ throw "argumnet missing for -n";
+ }
+ ++i;
+ string name = argv[i];
+ if (name.length()>=16) {
+ throw "name too long, max 16 chars including terminating null";
+ }
+ for (int j=0; j<16; j++) {
+ char c = name.c_str()[j];
+ d.name[j] = c;
+ if (!c) {
+ for (; j<16; ++j) {
+ d.name[j]=0;
+ }
+ break;
+ }
+ }
+ } else {
+ cerr << "Unrecognised option '" << arg << "'\n";
+ exit(1);
+ }
+ }
+
+ FileDescriptor fd(device,FileDescriptor::read_write);
+ dir_t dir;
+ load_dir(fd,offset,size,swap_endianness,dir);
+ check_overlap(dir,d.flash_base,d.size);
+ dir[d.flash_base] = d;
+ save_dir(fd,offset,size,swap_endianness,dir);
+}
+
+
+static void fis_delete(string device, int offset, int size, bool swap_endianness,
+ string name)
+{
+ FileDescriptor fd(device,FileDescriptor::read_write);
+ dir_t dir;
+ load_dir(fd,offset,size,swap_endianness,dir);
+
+ for (dir_t::iterator i = dir.begin();
+ i != dir.end(); ++i) {
+ string this_name(i->second.name);
+ if (this_name == name) {
+ dir.erase(i);
+ save_dir(fd,offset,size,swap_endianness,dir);
+ return;
+ }
+ }
+
+ throw "No partition found with specified name";
+}
+
+
+static void usage()
+{
+ cerr << "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"
+ << "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";
+}
+
+
+int main(int argc, char* argv[])
+{
+ try { try {
+
+ if (argc==1) {
+ usage();
+ exit(1);
+ }
+
+ string device="";
+ int offset=0;
+ int size=0;
+ bool swap_endianness=false;
+
+ for (int i=1; i<argc; ++i) {
+ string arg = argv[i];
+ if (arg=="-d") {
+ if (device!="") {
+ throw "-d option used more than once";
+ }
+ if (i==argc-1) {
+ throw "-d option is missing its parameter";
+ }
+ ++i;
+ device = argv[i];
+ } else if (arg=="-o") {
+ if (offset!=0) {
+ throw "-o option used more than once";
+ }
+ if (i==argc-1) {
+ throw "-o option is missing its parameter";
+ }
+ ++i;
+ offset = maybe_hex_string_to_int(argv[i]);
+ } else if (arg=="-s") {
+ if (size!=0) {
+ throw "-s option used more than once";
+ }
+ if (i==argc-1) {
+ throw "-s option is missing its parameter";
+ }
+ ++i;
+ size = maybe_hex_string_to_int(argv[i]);
+ } else if (arg=="-e") {
+ swap_endianness = true;
+ } else if (arg=="list") {
+ if (i!=argc-1) {
+ throw "Extra arguments after 'list'";
+ }
+ check_dev(device);
+ fis_list(device,offset,size,swap_endianness);
+ } else if (arg=="init") {
+ if (i!=argc-1) {
+ throw "Extra arguments after 'init'";
+ }
+ check_dev(device);
+ fis_init(device,offset,size);
+ } else if (arg=="create") {
+ check_dev(device);
+ fis_create(device,offset,size,swap_endianness,
+ argc-i-1,&argv[i+1]);
+ break;
+ } else if (arg=="delete") {
+ if (i!=argc-2) {
+ throw "Exactly one argumnet required after 'delete'";
+ }
+ ++i;
+ string name = argv[i];
+ check_dev(device);
+ fis_delete(device,offset,size,swap_endianness,name);
+ } else {
+ cerr << "unrecognised argument '" << arg << "'\n";
+ usage();
+ exit(1);
+ }
+ }
+ } RETHROW_MISC_EXCEPTIONS }
+ catch (Exception& E) {
+ cerr << "Error: ";
+ E.report(cerr);
+ exit(1);
+ }
+}
+