diff --git a/utils/calvaria/Makefile b/utils/calvaria/Makefile new file mode 100644 index 000000000..01e69a299 --- /dev/null +++ b/utils/calvaria/Makefile @@ -0,0 +1,39 @@ +# +# Copyright (C) 2011 OpenWrt.org +# +# This is free software, licensed under the GNU General Public License v2. +# See /LICENSE for more information. + +include $(TOPDIR)/rules.mk + +PKG_NAME:=calvaria +PKG_RELEASE:=1 + +PKG_INSTALL:=1 +PKG_BUILD_PARALLEL:=1 + +include $(INCLUDE_DIR)/package.mk + +define Package/calvaria + SECTION:=utils + CATEGORY:=Utilities + TITLE:=Maemo CAL partition access tool + MAINTAINER:=Michael Buesch +endef + +define Package/calvaria/description + Calvaria - Maemo CAL partition variable access tool + The CAL partition (/dev/mtdblock1) is used to store configuration + and calibration data. +endef + +define Build/Prepare + $(CP) ./files/src/* $(PKG_BUILD_DIR)/ +endef + +define Package/calvaria/install + $(INSTALL_DIR) $(1)/usr/bin + $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/calvaria $(1)/usr/bin/ +endef + +$(eval $(call BuildPackage,calvaria)) diff --git a/utils/calvaria/files/src/Makefile b/utils/calvaria/files/src/Makefile new file mode 100644 index 000000000..f977e9ba7 --- /dev/null +++ b/utils/calvaria/files/src/Makefile @@ -0,0 +1,26 @@ +DESTDIR ?= +PREFIX ?= /usr +CFLAGS ?= -O2 -Wall +LDFLAGS ?= +LDLIBS ?= + +STRIP ?= strip +INSTALL ?= install + +BIN := calvaria + +all: $(BIN) + +$(BIN): calvaria.o + $(CC) $(CFLAGS) $(LDFLAGS) $^ $(LDLIBS) -o $@ + $(STRIP) $@ + +%.o: %.c + $(CC) -c $(CFLAGS) $^ -o $@ + +install: $(BIN) + $(INSTALL) -d $(DESTDIR)/$(PREFIX)/bin + $(INSTALL) -m 0755 $(BIN) $(DESTDIR)/$(PREFIX)/bin/ + +clean: + rm -f $(BIN) *.o diff --git a/utils/calvaria/files/src/calvaria.c b/utils/calvaria/files/src/calvaria.c new file mode 100644 index 000000000..3f61dca63 --- /dev/null +++ b/utils/calvaria/files/src/calvaria.c @@ -0,0 +1,444 @@ +/* + * Calvaria - Maemo CAL partition variable access tool. + * + * Copyright (c) 2011 Michael Buesch + * + * Licensed under the GNU General Public License + * version 2 or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define _packed __attribute__((__packed__)) + +typedef uint16_t le16_t; +typedef uint32_t le32_t; + +struct header { + char magic[4]; /* Magic sequence */ + uint8_t type; /* Type number */ + uint8_t index; /* Index number */ + le16_t flags; /* Flags */ + char name[16]; /* Human readable section name */ + le32_t length; /* Payload length */ + le32_t datasum; /* Data CRC32 checksum */ + le32_t hdrsum; /* Header CRC32 checksum */ +} _packed; + +#define HDR_MAGIC "ConF" + + +static char toAscii(char c) +{ + if (c >= 32 && c <= 126) + return c; + return '.'; +} + +static void dump(FILE *outstream, const char *buf, size_t size) +{ + size_t i, ascii_cnt = 0; + char ascii[17] = { 0, }; + + for (i = 0; i < size; i++) { + if (i % 16 == 0) { + if (i != 0) { + fprintf(outstream, " |%s|\n", ascii); + ascii[0] = 0; + ascii_cnt = 0; + } + fprintf(outstream, "[%04X]: ", (unsigned int)i); + } + fprintf(outstream, " %02X", buf[i]); + ascii[ascii_cnt] = toAscii(buf[i]); + ascii[ascii_cnt + 1] = 0; + ascii_cnt++; + } + if (ascii[0]) { + if (size % 16) { + for (i = 0; i < 16 - (size % 16); i++) + fprintf(outstream, " "); + } + fprintf(outstream, " |%s|\n", ascii); + } + fprintf(outstream, "\n"); +} + +static uint32_t crc32(uint32_t crc, const void *_data, size_t size) +{ + const uint8_t *data = _data; + uint8_t value; + unsigned int bit; + size_t i; + const uint32_t poly = 0xEDB88320; + + for (i = 0; i < size; i++) { + value = data[i]; + for (bit = 8; bit; bit--) { + if ((crc & 1) != (value & 1)) + crc = (crc >> 1) ^ poly; + else + crc >>= 1; + value >>= 1; + } + } + + return crc; +} + +static inline uint16_t le16_to_cpu(le16_t x) +{ + uint8_t *bytes = (uint8_t *)&x; + uint16_t ret; + + ret = bytes[0]; + ret |= (uint16_t)(bytes[1]) << 8; + + return ret; +} + +static inline uint32_t le32_to_cpu(le32_t x) +{ + uint8_t *bytes = (uint8_t *)&x; + uint32_t ret; + + ret = bytes[0]; + ret |= (uint32_t)(bytes[1]) << 8; + ret |= (uint32_t)(bytes[2]) << 16; + ret |= (uint32_t)(bytes[3]) << 24; + + return ret; +} + +static int is_header(void *data, size_t size) +{ + struct header *hdr = data; + + if (size < sizeof(struct header)) + return 0; + if (memcmp(hdr->magic, HDR_MAGIC, sizeof(hdr->magic)) != 0) + return 0; + return 1; +} + +static int dump_section(const struct header *hdr, + const void *payload, size_t payload_len, + int dump_payload, + FILE *outstream) +{ + char name[sizeof(hdr->name) + 1] = { 0, }; + int hdrsum_ok, datasum_ok; + + memcpy(name, hdr->name, sizeof(hdr->name)); + hdrsum_ok = (crc32(0, hdr, sizeof(*hdr) - 4) == le32_to_cpu(hdr->hdrsum)); + datasum_ok = (crc32(0, payload, payload_len) == le32_to_cpu(hdr->datasum)); + + fprintf(outstream, "Section: %s\n", name); + fprintf(outstream, "Type: %u (0x%X)\n", hdr->type, hdr->type); + fprintf(outstream, "Index: %u (0x%X)\n", hdr->index, hdr->index); + fprintf(outstream, "Flags: 0x%04X\n", le16_to_cpu(hdr->flags)); + fprintf(outstream, "Length: %u (0x%X)\n", + le32_to_cpu(hdr->length), le32_to_cpu(hdr->length)); + fprintf(outstream, "Data CRC32: 0x%08X (%s)\n", le32_to_cpu(hdr->datasum), + datasum_ok ? "Ok" : "MISMATCH"); + fprintf(outstream, "Header CRC32: 0x%08X (%s)\n", le32_to_cpu(hdr->hdrsum), + hdrsum_ok ? "Ok" : "MISMATCH"); + if (dump_payload) + dump(outstream, payload, payload_len); + fprintf(outstream, "\n"); + + return 0; +} + +static void * map_file(const char *filepath, int readonly, + uint64_t *filelen) +{ + int fd; + off_t len; + void *data; + + fd = open(filepath, readonly ? O_RDONLY : O_RDWR); + if (fd < 0) { + fprintf(stderr, "Failed to open file %s: %s\n", + filepath, strerror(errno)); + return NULL; + } + len = lseek(fd, 0, SEEK_END); + if (len < 0 || lseek(fd, 0, SEEK_SET)) { + fprintf(stderr, "Failed to calculate file length of %s: %s\n", + filepath, strerror(errno)); + close(fd); + return NULL; + } + + data = mmap(NULL, len, + readonly ? PROT_READ : (PROT_READ | PROT_WRITE), + readonly ? MAP_PRIVATE : 0, + fd, 0); + close(fd); + if (data == MAP_FAILED) { + fprintf(stderr, "Failed to MMAP file %s: %s\n", + filepath, strerror(errno)); + return NULL; + } + madvise(data, len, MADV_SEQUENTIAL); + + *filelen = len; + + return data; +} + +static void unmap_file(void *mapping, uint64_t len) +{ + munmap(mapping, len); +} + +static int64_t find_section(void *start, uint64_t count, + int want_index, const char *want_name) +{ + uint64_t offset = 0; + uint8_t *data = start; + struct header *hdr; + char sectname[sizeof(hdr->name) + 1] = { 0, }; + uint32_t payload_len; + + while (1) { + /* Find header start */ + if (count < sizeof(struct header)) + break; + if (!is_header(data + offset, count)) { + count--; + offset++; + continue; + } + hdr = (struct header *)(data + offset); + payload_len = le32_to_cpu(hdr->length); + if (count - sizeof(struct header) < payload_len) { + fprintf(stderr, "Premature EOF\n"); + return -1; + } + memcpy(sectname, hdr->name, sizeof(hdr->name)); + + if (want_index >= 0 && want_index != hdr->index) + goto next; + if (want_name && strcmp(sectname, want_name) != 0) + goto next; + + /* Found it */ + return offset; + +next: + count -= sizeof(struct header) + payload_len; + offset += sizeof(struct header) + payload_len; + } + + return -1; +} + +static int dump_image(const char *filepath, + int want_section_index, const char *want_section_name, + int want_headers_only, + FILE *outstream) +{ + int err, ret = 0; + uint64_t filelen; + uint64_t count, offset; + int64_t find_offset; + uint8_t *data, *section; + struct header *hdr; + uint32_t payload_len; + + data = map_file(filepath, 1, &filelen); + if (!data) + return -EIO; + + count = filelen; + offset = 0; + while (1) { + find_offset = find_section(data + offset, count, + want_section_index, want_section_name); + if (find_offset < 0) + break; + offset += find_offset; + count -= find_offset; + + section = data + offset; + hdr = (struct header *)section; + payload_len = le32_to_cpu(hdr->length); + + err = dump_section(hdr, section + sizeof(struct header), + payload_len, + !want_headers_only, + outstream); + if (err) { + ret = err; + goto out; + } + + count -= sizeof(struct header) + payload_len; + offset += sizeof(struct header) + payload_len; + } +out: + unmap_file(data, filelen); + + return ret; +} + +static int write_payload(const char *filepath, + int want_section_index, const char *want_section_name, + FILE *outstream) +{ + int64_t find_offset; + uint64_t filelen; + uint8_t *data; + struct header *hdr; + + data = map_file(filepath, 1, &filelen); + if (!data) + return -EIO; + + find_offset = find_section(data, filelen, + want_section_index, want_section_name); + if (find_offset < 0) { + fprintf(stderr, "Section %s, index %d not found\n", + want_section_name, want_section_index); + unmap_file(data, filelen); + return -ESRCH; + } + + hdr = (struct header *)(data + find_offset); + if (fwrite(data + find_offset + sizeof(struct header), + le32_to_cpu(hdr->length), 1, outstream) != 1) { + fprintf(stderr, "Could not write output data\n"); + unmap_file(data, filelen); + return -EIO; + } + + unmap_file(data, filelen); + + return 0; +} + +static void usage(FILE *fd) +{ + fprintf(fd, "Calvaria - Maemo CAL partition variable access tool\n"); + fprintf(fd, "\n"); + fprintf(fd, "Usage: calvaria [OPTIONS] FILE\n"); + fprintf(fd, "\n"); + fprintf(fd, "Actions:\n"); + fprintf(fd, " -d|--dump Dump the contents of the image\n"); + fprintf(fd, " -H|--headers Dump the headers of the image, only\n"); + fprintf(fd, " -p|--payload Write the binary payload to stdout.\n"); + fprintf(fd, " Requires -i and -n to be set, too\n"); + fprintf(fd, "\n"); + fprintf(fd, "Options:\n"); + fprintf(fd, " -i|--index NUMBER Use this section index number\n"); + fprintf(fd, " -n|--name STRING Use this section name\n"); + fprintf(fd, "\n"); + fprintf(fd, " -h|--help Print this help text\n"); +} + +enum action { + ACTION_NONE, + ACTION_DUMP, + ACTION_DUMPHDRS, + ACTION_GETPAYLOAD, +}; + +int main(int argc, char **argv) +{ + int err, c, idx = 0; + const char *filepath; + enum action action = ACTION_NONE; + int opt_index = -1; + const char *opt_name = NULL; + + static struct option long_options[] = { + { "dump", no_argument, 0, 'd', }, + { "headers", no_argument, 0, 'H', }, + { "payload", no_argument, 0, 'p', }, + { "index", required_argument, 0, 'i', }, + { "name", required_argument, 0, 'n', }, + { "help", no_argument, 0, 'h', }, + { }, + }; + + while (1) { + c = getopt_long(argc, argv, "dHphi:n:", + long_options, &idx); + if (c == -1) + break; + switch (c) { + case 'h': + usage(stdout); + return 0; + case 'd': + action = ACTION_DUMP; + break; + case 'H': + action = ACTION_DUMPHDRS; + break; + case 'p': + action = ACTION_GETPAYLOAD; + break; + case 'i': + if (sscanf(optarg, "%d", &opt_index) != 1 || opt_index < 0) { + fprintf(stderr, "-i|--index is not a positive integer\n"); + return 1; + } + break; + case 'n': + opt_name = optarg; + break; + default: + return 1; + } + } + argc -= optind; + argv += optind; + if (action == ACTION_NONE) { + fprintf(stderr, "No action specified.\n"); + return 1; + } + if (action == ACTION_GETPAYLOAD) { + if (opt_index < 0 || !opt_name) { + fprintf(stderr, "Required options -i|--index or -n|--name " + "not specified for action -p|--payload\n"); + return 1; + } + } + if (argc != 1) { + usage(stderr); + return 1; + } + filepath = argv[0]; + + switch (action) { + case ACTION_NONE: + break; + case ACTION_DUMP: + case ACTION_DUMPHDRS: + err = dump_image(filepath, opt_index, opt_name, + (action == ACTION_DUMPHDRS), + stdout); + if (err) + return 1; + break; + case ACTION_GETPAYLOAD: + err = write_payload(filepath, opt_index, opt_name, stdout); + if (err) + return 1; + break; + } + + return 0; +}