Stefan Reinauer has submitted this change. ( https://review.coreboot.org/c/em100/+/36943 )
Change subject: Use binaries from Windows installer directly ......................................................................
Use binaries from Windows installer directly
Find configs.tar.xz in $HOME/.em100/ or $EM100_HOME and load chip descriptions from there instead of bringing the static array of all chips along with the binary. This allows updating the chip database from the Windows installer without recompiling the em100 tool.
Change-Id: I8c360e96ce42fd78141a1564976454711e20f527 Signed-off-by: Stefan Reinauer stefan.reinauer@coreboot.org Reviewed-on: https://review.coreboot.org/c/em100/+/36943 Tested-by: build bot (Jenkins) no-reply@coreboot.org Reviewed-by: Angel Pons th3fanbus@gmail.com --- M Makefile A chips.c M em100.c M em100.h A tar.c 5 files changed, 775 insertions(+), 46 deletions(-)
Approvals: build bot (Jenkins): Verified Angel Pons: Looks good to me, approved
diff --git a/Makefile b/Makefile index 698920b..bd20b47 100644 --- a/Makefile +++ b/Makefile @@ -29,6 +29,7 @@ CFLAGS += -Wall -Wundef -Wstrict-prototypes -Wmissing-prototypes CFLAGS += -Wwrite-strings -Wredundant-decls -Wstrict-aliasing -Wshadow -Wextra CFLAGS += -Wno-unused-but-set-variable +CFLAGS += -DXZ_USE_CRC64 -DXZ_DEC_ANY_CHECK -Ixz # Remove after fixing CFLAGS += -Wno-sign-compare -Wno-discarded-qualifiers
@@ -38,7 +39,9 @@ CC ?= gcc PKG_CONFIG ?= pkg-config
+XZ = xz/xz_crc32.c xz/xz_crc64.c xz/xz_dec_bcj.c xz/xz_dec_lzma2.c xz/xz_dec_stream.c SOURCES = em100.c firmware.c fpga.c hexdump.c sdram.c spi.c system.c trace.c usb.c +SOURCES += chips.c tar.c $(XZ) OBJECTS = $(SOURCES:.c=.o)
all: dep em100 diff --git a/chips.c b/chips.c new file mode 100644 index 0000000..acd90e1 --- /dev/null +++ b/chips.c @@ -0,0 +1,329 @@ +/* + * Copyright 2012 Google Inc. + * + * 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; version 2 of the License. + * + * 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. + */ + +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <stdint.h> +#ifdef __APPLE__ +#include <libkern/OSByteOrder.h> +#define bswap_16(x) OSSwapInt16(x) +#define bswap_32(x) OSSwapInt32(x) +#include <architecture/byte_order.h> + #if BYTE_ORDER == LITTLE_ENDIAN + #define le32toh(x) (x) + #define le16toh(x) (x) + #define htobe16(x) bswap_16(x) + #else + #define le32toh(x) bswap_32(x) + #define le16toh(x) bswap_16(x) + #define htobe16(x) (x) + #endif +#else +#include <endian.h> +#endif + +#include "em100.h" + +#define DEDIPROG_CFG_PRO_MAX_ENTRIES 212 + +#define DEDIPROG_CFG_PRO_SIZE 176 +#define DEDIPROG_CFG_PRO_SIZE_SFDP 256 +#define DEDIPROG_CFG_PRO_SIZE_SRST 144 + +#define DEDIPROG_CFG_MAGIC 0x67666344 /* 'Dcfg' */ +#define DEDIPROG_SFDP_MAGIC 0x50444653 /* 'SFDP' */ +#define DEDIPROG_SRST_MAGIC 0x54535253 /* 'SRST' */ +#define DEDIPROG_PROT_MAGIC 0x544f5250 /* 'PROT' */ + +#define INIT_SEQUENCE_REIGSTER_OFFSET_0 0x2300 +#define INIT_SEQUENCE_REIGSTER_OFFSET_1 0x1100 + +/* Init sequence and file format: + * + * All v1.1 dediprog configuration files are 176 bytes and all values are + * encoded as little endian format. + + * At offset init_offset the init sequence consists of init entries that are + * sent to endpoint 1. There are 2 sets of entries separated by a 32-bit + * terminator of 0xffffffff. Each entry consists of a value and register offset. + * The first set of entries have a base register address of 0x2300 while the + * the second set of entries have a base register address of 0x1100. Each entry + * is sent to the device as <register> <value>, however the data is sent in + * big endian format. Additionally, each entry is sent as a 16-byte transfer + * with the remaining bytes all 0's. + * + * Configuration files that are >= 436 bytes contain SFDP data, separated by + * the magic value 'SFDP' while those that are 584 bytes contain SRST data + * separated by the magic value 'SRST' and containing 0 or 3 entries followed + * by PROT data in the rest of the buffer. Unfortunately there does not seem + * to be any change in the version fields and these are still reported as 1.1. + */ + + +struct dediprog_cfg_hdr { + uint32_t magic; + uint16_t ver_min; + uint16_t ver_maj; + uint32_t init_offset; + uint32_t chip_size; + uint32_t vendor_name_offset; + uint32_t chip_name_offset; + uint32_t unknown_offset[2]; +} __attribute__((packed)); + +struct dediprog_cfg_pro { + struct dediprog_cfg_hdr hdr; + uint8_t payload[DEDIPROG_CFG_PRO_SIZE-sizeof(struct dediprog_cfg_hdr)]; +} __attribute__((packed)); + + +struct dediprog_cfg_init_entry { + uint16_t value; + uint16_t reg; +} __attribute__((packed)); + +unsigned char cfg_buffer[DEDIPROG_CFG_PRO_SIZE]; + +static int parse_and_output_config(struct dediprog_cfg_pro *cfg, chipdesc *chip) +{ + struct dediprog_cfg_init_entry *entry, *end; + struct dediprog_cfg_hdr *hdr; + const char *vendor, *chip_name; + uint16_t reg_offset; + int entries = 0; + + hdr = &cfg->hdr; + + /* The magic number is actually string, but it can be converted to + * a host ordered 32-bit number. */ + hdr->magic= le32toh(hdr->magic); + + if (hdr->magic != DEDIPROG_CFG_MAGIC) { + fprintf(stderr, "Invalid magic number: 0x%x\n", hdr->magic); + return -1; + } + + /* Convert all header values from little endian to host byte order. */ + hdr->ver_min = le16toh(hdr->ver_min); + hdr->ver_maj = le16toh(hdr->ver_maj); + hdr->init_offset = le32toh(hdr->init_offset); + hdr->chip_size = le32toh(hdr->chip_size); + hdr->vendor_name_offset = le32toh(hdr->vendor_name_offset); + hdr->chip_name_offset = le32toh(hdr->chip_name_offset); + + if (hdr->ver_maj != 1 && hdr->ver_min != 1) { + fprintf(stderr, "Invalid version number: %d.%d\n", hdr->ver_maj, + hdr->ver_min); + return -1; + } + + /* Adjust the offsets to be into the payload of the config file. */ + hdr->init_offset -= sizeof(*hdr); + hdr->vendor_name_offset -= sizeof(*hdr); + hdr->chip_name_offset -= sizeof(*hdr); + + vendor = (void *)&cfg->payload[hdr->vendor_name_offset]; + chip_name = (void *)&cfg->payload[hdr->chip_name_offset]; + + /* Since configs.tar is resident, we don't have to malloc */ + chip->vendor = (const char *)vendor; + chip->name = (const char *)chip_name; + chip->size = hdr->chip_size; + +#ifdef DEBUG + printf("%s %s (%d kB)\n", + vendor, chip_name, hdr->chip_size/1024); +#endif + + entry = (void *)&cfg->payload[hdr->init_offset]; + end = (void *)&cfg[1]; /* 1 past the last entry */ + + reg_offset = INIT_SEQUENCE_REIGSTER_OFFSET_0; + + for (; entry != end; entry++) { + uint8_t *reg, *value; + + /* Convert from little endian to host format. */ + entry->value = le16toh(entry->value); + entry->reg = le16toh(entry->reg); + + if (entry->value == 0xffff && entry->reg == 0xffff) { + reg_offset = INIT_SEQUENCE_REIGSTER_OFFSET_1; + continue; + } + + entry->reg += reg_offset; + + /* Convert from host to big endian format. */ + entry->value = htobe16(entry->value); + entry->reg = htobe16(entry->reg); + + value = (void *)&entry->value; + reg = (void *)&entry->reg; + + chip->init[entries][0] = reg[0]; + chip->init[entries][1] = reg[1]; + chip->init[entries][2] = value[0]; + chip->init[entries][3] = value[1]; + ++entries; + } + + return entries; +} + +static int parse_and_output_sfdp(chipdesc *chip, void **ptr, int *length, int entries) +{ + int i, len = 0; + unsigned char *sfdp_buffer = (unsigned char *)*ptr; + + if (*length < DEDIPROG_CFG_PRO_SIZE_SFDP) { + fprintf(stderr, "Error reading SFDP\n"); + return -1; + } + + chip->init[entries][0] = 0x23; + chip->init[entries][1] = 0xc9; + chip->init[entries][2] = 0x00; + chip->init[entries][3] = 0x01; + + len++; + + for (i = 0; i < DEDIPROG_CFG_PRO_SIZE_SFDP; i+=2) { + chip->init[entries+len][0] = 0x23; + chip->init[entries+len][1] = 0xc1; + chip->init[entries+len][2] = sfdp_buffer[i+1]; + chip->init[entries+len][3] = sfdp_buffer[i]; + len++; + } + + *ptr += DEDIPROG_CFG_PRO_SIZE_SFDP; + *length -= DEDIPROG_CFG_PRO_SIZE_SFDP; + + return len; +} + +static int parse_and_output_srst(chipdesc *chip, void **ptr, int *length, int entries) +{ + int i, len = 0; + uint32_t magic; + unsigned char *srst_buffer = (unsigned char *)*ptr; + + if (*length < DEDIPROG_CFG_PRO_SIZE_SRST) { + fprintf(stderr, "Error reading SRST\n"); + return -1; + } + + /* SRST has 0 or 3 entries before PROT */ + memcpy(&magic, &srst_buffer[0], sizeof(magic)); + + if (magic != DEDIPROG_PROT_MAGIC) { + int j; + + for (j = 0; j < 3; j++) { + chip->init[entries+len][0] = 0x23; + chip->init[entries+len][1] = srst_buffer[j*4+2]; + chip->init[entries+len][2] = srst_buffer[j*4+1]; + chip->init[entries+len][3] = srst_buffer[j*4]; + len++; + } + + /* Start after SFDP data and PROT magic */ + i = 16; + } else { + /* Start after PROT magic */ + i = 4; + } + + chip->init[entries+len][0] = 0x23; + chip->init[entries+len][1] = 0xc4; + chip->init[entries+len][2] = 0x00; + chip->init[entries+len][3] = 0x01; + len++; + + for (; i < DEDIPROG_CFG_PRO_SIZE_SRST; i+=2) { + chip->init[entries+len][0] = 0x23; + chip->init[entries+len][1] = 0xc5; + chip->init[entries+len][2] = srst_buffer[i+1]; + chip->init[entries+len][3] = srst_buffer[i]; + len++; + } + + *ptr += DEDIPROG_CFG_PRO_SIZE_SRST; + *length -= DEDIPROG_CFG_PRO_SIZE_SRST; + + return len; +} + +int parse_dcfg(chipdesc *chip, TFILE *dcfg) +{ + struct dediprog_cfg_pro *cfg; + int init_len = 0; + uint32_t magic; + + void *ptr = (void *)dcfg->address; + int length = dcfg->length; + + if (length < sizeof(cfg_buffer)) { + /* Not a config file */ + return 1; + } + cfg = (struct dediprog_cfg_pro *)ptr; + + init_len = parse_and_output_config(cfg, chip); + if (init_len < 0) { + fprintf(stderr, "Error parsing Dcfg\n"); + return 1; + } + + ptr += DEDIPROG_CFG_PRO_SIZE; + length -= DEDIPROG_CFG_PRO_SIZE; + + /* Handle any extra data */ + while (length) { + int ret = 0; + + magic = *(uint32_t *)ptr; + ptr+=sizeof(uint32_t); + length-=sizeof(uint32_t); + + switch (magic) { + case DEDIPROG_SFDP_MAGIC: + ret = parse_and_output_sfdp(chip, &ptr, &length, init_len); + break; + case DEDIPROG_SRST_MAGIC: + ret = parse_and_output_srst(chip, &ptr, &length, init_len); + break; + default: + fprintf(stderr, "Unknown magic: 0x%08x\n", magic); + break; + } + + if (ret < 0) { + fprintf(stderr, "FAILED.\n"); + return 1; + } + init_len += ret; + } + + chip->init_len = init_len; + + return 0; +} diff --git a/em100.c b/em100.c index b389dc8..8596edb 100644 --- a/em100.c +++ b/em100.c @@ -22,10 +22,15 @@ #include <signal.h> #include <getopt.h> #include <unistd.h> +#include <dirent.h> +#include <errno.h> +#include <sys/stat.h> +#include <wordexp.h> + #include "em100.h"
-/* SPI flash chips parameters definition */ -#include "em100pro_chips.h" +TFILE *configs; +char *database_version;
volatile int do_exit_flag = 0;
@@ -517,7 +522,7 @@ int fpga_voltage, chip_voltage = 0; int req_voltage = 0;
- printf("Sending flash chip configuration\n"); + printf("Configuring SPI flash chip emulation.\n");
memset(cmd, 0, 16);
@@ -597,6 +602,31 @@ return 1; }
+typedef struct { + uint16_t venid; + uint16_t devid; + int found; + chipdesc chip; +} vendev_t; + +static int get_chip_type_entry(char *name __unused, TFILE *dcfg, void *data, int ok __unused) +{ + uint16_t comp; + chipdesc chip; + + vendev_t *v = (vendev_t *)data; + + parse_dcfg(&chip, dcfg); + + if (get_chip_init_val(&chip, 0x23, FPGA_REG_DEVID, &comp) || v->devid != comp) + return 0; + if (get_chip_init_val(&chip, 0x23, FPGA_REG_VENDID, &comp) || v->venid != comp) + return 0; + v->found = 1; + v->chip = chip; + return 1; +} + /** * Tries to identify the currently emulated SPI flash by looking at * known registers in the FPGA and matches those bits with the @@ -604,62 +634,109 @@ * * Returns 0 on success. */ -static int get_chip_type(struct em100 *em100, const chipdesc **out) +static int get_chip_type(struct em100 *em100, chipdesc *out) { - const chipdesc *chip; - uint16_t venid; - uint16_t devid; + vendev_t v;
/* Read manufacturer and vendor id from FPGA */ - if (!read_fpga_register(em100, FPGA_REG_VENDID, &venid)) + if (!read_fpga_register(em100, FPGA_REG_VENDID, &v.venid)) return 1; - if (!read_fpga_register(em100, FPGA_REG_DEVID, &devid)) + if (!read_fpga_register(em100, FPGA_REG_DEVID, &v.devid)) return 1;
- for (chip = chips; chip->name != NULL; chip++) { - uint16_t comp; - - if (get_chip_init_val(chip, 0x23, FPGA_REG_DEVID, &comp) || devid != comp) - continue; - if (get_chip_init_val(chip, 0x23, FPGA_REG_VENDID, &comp) || venid != comp) - continue; - - break; - } - - if (!chip->name) + tar_for_each(configs, get_chip_type_entry, (void *)&v); + if (!v.found) return 1;
- *out = chip; + *out = v.chip; + + return 0; +} + +static int list_chips_entry(char *name __unused, TFILE *file, void *data __unused, int ok __unused) +{ + static chipdesc chip; + /* Is the file a dcfg file? Then print the name, otherwise skip. */ + + if (!parse_dcfg(&chip, file)) + printf(" • %s %s\n", chip.vendor, chip.name);
return 0; }
static chipdesc *setup_chips(const char *desiredchip) { - const chipdesc *chip = chips; + static chipdesc chip; + configs = tar_load_compressed(get_em100_file("configs.tar.xz")); + if (!configs) { + printf("Can't find chip configs in $EM100_HOME/configs.tar.xz.\n"); + return NULL; + } + + TFILE *version = tar_find(configs,"configs/VERSION", 1); + if (!version) { + printf("Can't find VERSION of chip configs.\n"); + return NULL; + } + database_version = (char *)version->address; + tar_close(version); + if (desiredchip) { - do { - if (strcasecmp(desiredchip, chip->name) == 0) { - printf("will emulate '%s'\n", chip->name); - break; - } - } while ((++chip)->name); - - if (chip->name == NULL) { - printf("Supported chips:\n"); - chip = chips; - do { - printf("%s ", chip->name); - } while ((++chip)->name); - printf("\n\nCould not find emulation for '%s'.\n", + char chipname[256]; + sprintf(chipname, "configs/%s.cfg", desiredchip); + TFILE *dcfg = tar_find(configs, chipname, 0); + if (!dcfg) { + printf("Supported chips:\n\n"); + tar_for_each(configs, list_chips_entry, NULL); + printf("\nCould not find a chip matching '%s' to be emulated.\n", desiredchip); - return NULL; } - + parse_dcfg(&chip, dcfg); + tar_close(dcfg); + return &chip; } - return chip; + return NULL; +} + +static char *get_em100_home(void) +{ + static char directory[FILENAME_BUFFER_SIZE] = "\0"; + + if (directory[0] != 0) + return directory; + + /* find out file */ + wordexp_t p; + char *em100_home = getenv("EM100_HOME"); + if (em100_home) + wordexp("$EM100_HOME/", &p, 0); + else + wordexp("$HOME/.em100/", &p, 0); + + strncpy(directory, p.we_wordv[0], FILENAME_BUFFER_SIZE - 1); + wordfree(&p); + + DIR *dir = opendir(directory); + if (dir) { + // success + } else if (errno == ENOENT) { + mkdir(directory, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); + } else { + perror("EM100_HOME inaccessible"); + directory[0]=0; + return NULL; + } + + return directory; +} + +char *get_em100_file(char *name) +{ + char file[FILENAME_BUFFER_SIZE]; + strncpy(file, get_em100_home(), FILENAME_BUFFER_SIZE); + strncat(file, name, FILENAME_BUFFER_SIZE - strlen(file) - 1); + return strdup(file); }
static const struct option longopts[] = { @@ -813,7 +890,7 @@ }
const chipdesc *chip = setup_chips(desiredchip); - if (!chip) + if (desiredchip && !chip) return 1;
printf("MCU version: %d.%02d\n", em100.mcu >> 8, em100.mcu & 0xff); @@ -842,7 +919,7 @@ printf("Serial number: EM%06d\n", em100.serialno); else printf("Serial number: N.A.\n"); - printf("SPI flash database: %s\n", VERSION); + printf("SPI flash database: %s\n", database_version); get_current_state(&em100); get_current_pin_state(&em100); printf("\n"); @@ -890,7 +967,7 @@ printf("Failed configuring chip type.\n"); return 0; } - printf("Chip set to %s\n", desiredchip); + printf("Chip set to %s %s.\n", chip->vendor, chip->name); }
if (voltage) { @@ -912,11 +989,11 @@
if (!desiredchip) { /* Read configured SPI emulation from EM100 */ - const chipdesc *emulated_chip; + chipdesc emulated_chip;
if (!get_chip_type(&em100, &emulated_chip)) { - printf("Configured to emulate %s\n", emulated_chip->name); - maxlen = emulated_chip->size; + printf("Configured to emulate %dkB chip\n", emulated_chip.size / 1024); + maxlen = emulated_chip.size; } } else { maxlen = chip->size; diff --git a/em100.h b/em100.h index 72518d1..f126b7d 100644 --- a/em100.h +++ b/em100.h @@ -32,6 +32,16 @@ uint8_t hwversion; };
+#define NUM_INIT_ENTRIES 212 +#define BYTES_PER_INIT_ENTRY 4 +typedef struct { + const char *vendor; + const char *name; + unsigned int size; + uint8_t init[NUM_INIT_ENTRIES][BYTES_PER_INIT_ENTRY]; + int init_len; +} chipdesc; + /* Hardware versions */ #define HWVERSION_EM100PRO 4 #define HWVERSION_EM100PRO_G2 6 @@ -165,8 +175,26 @@ int read_spi_terminal(struct em100 *em100, int print_counter); int init_spi_terminal(struct em100 *em100);
+/* Archive handling */ +typedef struct { + unsigned char *address; + size_t length; + int alloc; +} TFILE; +TFILE *tar_find(TFILE *tfile, char *name, int casesensitive); +TFILE *tar_load_compressed(char *filename); +int tar_for_each(TFILE *tfile, int (*run)(char *, TFILE *, void *, int), void *data); +int tar_close(TFILE *tfile); +int tar_ls(TFILE *tfile); +int test_tar(void); + /* Misc. */
#define MB * 1024 * 1024 +#define FILENAME_BUFFER_SIZE 1024 +char *get_em100_file(char *name); + +/* Chips */ +int parse_dcfg(chipdesc *chip, TFILE *dcfg);
#endif diff --git a/tar.c b/tar.c new file mode 100644 index 0000000..e8d3664 --- /dev/null +++ b/tar.c @@ -0,0 +1,292 @@ +/* + * Copyright 2019 Google LLC + * + * 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; version 2 of the License. + * + * 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. + */ + +#define _GNU_SOURCE +#include <stddef.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "em100.h" +#include "xz.h" + +#define ROUND_UP(n, inc) (n + (inc - n % inc) % inc) + +typedef struct { + char name[100]; + char mode[8]; + char owner[8]; + char group[8]; + char size[12]; + char mtime[12]; + char checksum[8]; + char type; + char linkname[100]; + char magic[6]; + char version[2]; + char uname[32]; + char gname[32]; + char devmajor[8]; + char devminor[8]; + char prefix[155]; + char padding[12]; +} __packed tar_header_t; + +static unsigned int checksum(tar_header_t *file) +{ + int i, chk_off = offsetof(tar_header_t, checksum); + unsigned char *raw = (unsigned char *)file; + unsigned int chksum = 256; + + for (i = 0; i < sizeof(tar_header_t); i++) { + if (i >= chk_off && i < chk_off + 8) + continue; + chksum += raw[i]; + } + return chksum; +} + +int tar_for_each(TFILE *tfile, int (*run)(char *, TFILE *, void *, int), void *data) +{ + size_t i = 0; + + while (i < tfile->length) { + tar_header_t *f = (tar_header_t *)(tfile->address + i); + /* null header at end of tar */ + if (f->name[0] == 0) + break; + + unsigned int size = strtol(f->size, NULL, 8); + unsigned int cksum = strtol(f->checksum, NULL, 8); + unsigned int ok = (checksum(f) == cksum); + + if (f->type == '0') { + TFILE s; + s.address = tfile->address + i + sizeof(tar_header_t); + s.length = size; + s.alloc = 0; + + if ((*run)(f->name, &s, data, ok)) + break; + } + + if (!ok) + break; + i += sizeof(tar_header_t) + ROUND_UP(size, 512); + } + + return 0; +} + +static int tar_ls_entry(char *name, TFILE *file __unused, void *data __unused, int ok) +{ + printf("%s %s\n", name, ok?"✔":"✘"); + return 0; +} + +int tar_ls(TFILE *tfile) +{ + tar_for_each(tfile, tar_ls_entry, NULL); + return 0; +} + +TFILE *tar_find(TFILE *tfile, char *name, int casesensitive) +{ + size_t i = 0; + TFILE *ret; + + while (i < tfile->length) { + tar_header_t *f = (tar_header_t *)(tfile->address + i); + if (f->name[0] == 0) /* null header at end of tar */ + break; + + unsigned int size = strtol(f->size, NULL, 8); + unsigned int cksum = strtol(f->checksum, NULL, 8); + unsigned int ok = (checksum(f) == cksum); + + if (!ok) + break; + + int compare = casesensitive ? strncmp(name, f->name, 100) : + strncasecmp(name, f->name, 100); + if (!compare && f->type == '0') { + ret = (TFILE *)malloc(sizeof(TFILE)); + if (!ret) { + perror("Out of memory.\n"); + return NULL; + } + + ret->address = tfile->address + i + sizeof(tar_header_t); + ret->length = size; + ret->alloc = 0; + + return ret; + } + i += sizeof(tar_header_t) + ROUND_UP(size, 512); + } + + return NULL; +} + +/* Finding the uncompressed size of an archive should really be part + * of the xz API. + */ +static uint32_t decode_vli(unsigned char **streamptr) +{ + unsigned char *stream = *streamptr; + uint32_t val = 0; + int pos = 0; + + val = *stream & 0x7f; + do { + pos += 7; + stream++; + val |= ((uint32_t)*stream & 0x7f) << pos; + } while (stream[0] & 0x80); + *streamptr = stream+1; + + return val; +} + +static uint32_t uncompressed_size(unsigned char *stream, size_t length) +{ + if (stream[length-2] != 0x59 || stream[length-1] != 0x5a) { + printf("Bad stream footer.\n"); + return 0; + } + unsigned char *bytes = stream + length - 8; + uint32_t backward_size = + bytes[0] | (bytes[1]<<8) | (bytes[2]<<16) | (bytes[3]<<24); + backward_size = (backward_size + 1) << 2; + bytes = stream + length - 12 /* stream footer */ - backward_size; + if (bytes[0] != 0x00) { + printf("Bad index indicator.\n"); + return 0; + } + if (bytes[1] != 0x01) { + printf("More than one index. I'm confused.\n"); + return 0; + } + bytes += 2; + + /* skip unpadded size */ + decode_vli(&bytes); + return decode_vli(&bytes); + + return 0; +} + +TFILE *tar_load_compressed(char *filename) +{ + FILE *f; + long cfsize, fsize; + unsigned char *cfw, *fw; + + /* Load our file into memory */ + + f = fopen(filename, "rb"); + if (!f) { + perror(filename); + return NULL; + } + + fseek(f, 0, SEEK_END); + cfsize = ftell(f); + if (cfsize < 0) { + perror(filename); + fclose(f); + return NULL; + } + fseek(f, 0, SEEK_SET); + + cfw = malloc(cfsize); + if (!cfw) { + printf("Out of memory.\n"); + fclose(f); + return NULL; + } + if (fread(cfw, cfsize, 1, f) != 1) { + perror(filename); + fclose(f); + free(cfw); + return NULL; + } + fclose(f); + + fsize = uncompressed_size(cfw, cfsize); + fw = malloc(fsize); + if (!fw) { + printf("Out of memory.\n"); + free(cfw); + return NULL; + } + + /* Decompress xz */ + struct xz_buf b; + struct xz_dec *s; + enum xz_ret ret; + + xz_crc32_init(); +#ifdef XZ_USE_CRC64 + xz_crc64_init(); +#endif + s = xz_dec_init(XZ_SINGLE, 0); + if (s == NULL) { + printf("Decompression init failed.\n"); + free(cfw); + free(fw); + return NULL; + } + + b.in = cfw; + b.in_pos = 0; + b.in_size = cfsize; + b.out = fw; + b.out_pos = 0; + b.out_size = fsize; + + ret = xz_dec_run(s, &b); + if (ret != XZ_STREAM_END) { + printf("Decompression failed.\n"); + free(cfw); + free(fw); + return NULL; + } + free(cfw); + + /* Prepare answer */ + TFILE *tfile = malloc(sizeof(TFILE)); + if (tfile == NULL) { + printf("Out of memory.\n"); + free(fw); + return NULL; + } + tfile->address = fw; + tfile->length = fsize; + tfile->alloc = 1; + + return tfile; +} + +int tar_close(TFILE *tfile) +{ + if (tfile->alloc) { + free(tfile->address); + } + free(tfile); + return 0; +}