diff --git a/Makefile b/Makefile index 4724858..e4ab4c6 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ CFLAGS += -O2 -Wall -g `pkg-config --cflags libxml-2.0 libusb-1.0` LDFLAGS += `pkg-config --libs libxml-2.0 libusb-1.0` prefix := /usr/local -QDL_SRCS := firehose.c qdl.c sahara.c util.c patch.c program.c ufs.c usb.c +QDL_SRCS := firehose.c qdl.c sahara.c util.c patch.c program.c read.c ufs.c usb.c QDL_OBJS := $(QDL_SRCS:.c=.o) RAMDUMP_SRCS := ramdump.c sahara.c usb.c util.c diff --git a/firehose.c b/firehose.c index cd802b4..01ded57 100644 --- a/firehose.c +++ b/firehose.c @@ -440,6 +440,104 @@ static int firehose_program(struct qdl_device *qdl, struct program *program, int return ret == FIREHOSE_ACK ? 0 : -1; } +static int firehose_read_op(struct qdl_device *qdl, struct read_op *read_op, int fd) +{ + size_t chunk_size; + xmlNode *root; + xmlNode *node; + xmlDoc *doc; + void *buf; + time_t t0; + time_t t; + int left; + int ret; + int n; + bool expect_empty; + + buf = malloc(max_payload_size); + if (!buf) + err(1, "failed to allocate sector buffer"); + + doc = xmlNewDoc((xmlChar*)"1.0"); + root = xmlNewNode(NULL, (xmlChar*)"data"); + xmlDocSetRootElement(doc, root); + + node = xmlNewChild(root, NULL, (xmlChar*)"read", NULL); + xml_setpropf(node, "SECTOR_SIZE_IN_BYTES", "%d", read_op->sector_size); + xml_setpropf(node, "num_partition_sectors", "%d", read_op->num_sectors); + xml_setpropf(node, "physical_partition_number", "%d", read_op->partition); + xml_setpropf(node, "start_sector", "%s", read_op->start_sector); + if (read_op->filename) + xml_setpropf(node, "filename", "%s", read_op->filename); + + ret = firehose_write(qdl, doc); + if (ret < 0) { + fprintf(stderr, "[READ] failed to write read command\n"); + goto out; + } + + ret = firehose_read(qdl, 10000, firehose_generic_parser, NULL); + if (ret) { + fprintf(stderr, "[READ] failed to setup reading\n"); + goto out; + } + + t0 = time(NULL); + + left = read_op->num_sectors; + expect_empty = false; + while (left > 0 || expect_empty) { + chunk_size = MIN(max_payload_size / read_op->sector_size, left); + + n = qdl_read(qdl, buf, chunk_size * read_op->sector_size, 30000); + if (n < 0) { + err(1, "failed to read"); + } + + if (n == 0 && expect_empty) { + // Every second transfer is empty during this stage. + expect_empty = false; + continue; + } else if (expect_empty) { + err(1, "expected empty transfer but received non-empty transfer during read"); + } else if (n != chunk_size * read_op->sector_size){ + err(1, "failed to read full sector"); + } + + n = write(fd, buf, n); + + if (n != chunk_size * read_op->sector_size) { + err(1, "failed to write"); + } + + left -= chunk_size; + expect_empty = true; + } + + ret = firehose_read(qdl, 10000, firehose_generic_parser, NULL); + if (ret) { + fprintf(stderr, "[READ] failed to complete reading\n"); + goto out; + } + + t = time(NULL) - t0; + + if (t) { + fprintf(stderr, + "[READ] read \"%s\" successfully at %ldkB/s\n", + read_op->filename, + (unsigned long)read_op->sector_size * read_op->num_sectors / t / 1024); + } else { + fprintf(stderr, "[READ] read \"%s\" successfully\n", + read_op->filename); + } + +out: + xmlFreeDoc(doc); + free(buf); + return ret; +} + static int firehose_apply_patch(struct qdl_device *qdl, struct patch *patch) { xmlNode *root; @@ -666,6 +764,10 @@ int firehose_run(struct qdl_device *qdl, const char *incdir, const char *storage if (ret) return ret; + ret = read_op_execute(qdl, firehose_read_op, incdir); + if (ret) + return ret; + bootable = program_find_bootable_partition(); if (bootable < 0) fprintf(stderr, "no boot partition found\n"); diff --git a/qdl.c b/qdl.c index e15dcbd..7ba1991 100644 --- a/qdl.c +++ b/qdl.c @@ -48,6 +48,7 @@ enum { QDL_FILE_UNKNOWN, QDL_FILE_PATCH, QDL_FILE_PROGRAM, + QDL_FILE_READ, QDL_FILE_UFS, QDL_FILE_CONTENTS, }; @@ -79,6 +80,10 @@ static int detect_type(const char *xml_file) type = QDL_FILE_PROGRAM; break; } + if (!xmlStrcmp(node->name, (xmlChar*)"read")) { + type = QDL_FILE_READ; + break; + } if (!xmlStrcmp(node->name, (xmlChar*)"ufs")) { type = QDL_FILE_UFS; break; @@ -168,6 +173,11 @@ int main(int argc, char **argv) if (ret < 0) errx(1, "program_load %s failed", argv[optind]); break; + case QDL_FILE_READ: + ret = read_op_load(argv[optind]); + if (ret < 0) + errx(1, "read_op_load %s failed", argv[optind]); + break; case QDL_FILE_UFS: ret = ufs_load(argv[optind],qdl_finalize_provisioning); if (ret < 0) diff --git a/qdl.h b/qdl.h index 0bb310a..9d6d58d 100644 --- a/qdl.h +++ b/qdl.h @@ -5,6 +5,7 @@ #include "patch.h" #include "program.h" +#include "read.h" #include #define MAPPING_SZ 64 diff --git a/read.c b/read.c new file mode 100644 index 0000000..4eda8c2 --- /dev/null +++ b/read.c @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2016-2017, Linaro Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include +#include +#include +#include +#include +#include +#include + +#include "read.h" +#include "qdl.h" + +static struct read_op *read_ops; +static struct read_op *read_ops_last; + +int read_op_load(const char *read_op_file) +{ + struct read_op *read_op; + xmlNode *node; + xmlNode *root; + xmlDoc *doc; + int errors; + + doc = xmlReadFile(read_op_file, NULL, 0); + if (!doc) { + fprintf(stderr, "[READ] failed to parse %s\n", read_op_file); + return -EINVAL; + } + + root = xmlDocGetRootElement(doc); + for (node = root->children; node ; node = node->next) { + if (node->type != XML_ELEMENT_NODE) + continue; + + if (xmlStrcmp(node->name, (xmlChar*)"read")) { + fprintf(stderr, "[READ] unrecognized tag \"%s\", ignoring\n", node->name); + continue; + } + + errors = 0; + + read_op = calloc(1, sizeof(struct read_op)); + + read_op->sector_size = attr_as_unsigned(node, "SECTOR_SIZE_IN_BYTES", &errors); + read_op->filename = attr_as_string(node, "filename", &errors); + read_op->partition = attr_as_unsigned(node, "physical_partition_number", &errors); + read_op->num_sectors = attr_as_unsigned(node, "num_partition_sectors", &errors); + read_op->start_sector = attr_as_string(node, "start_sector", &errors); + + if (errors) { + fprintf(stderr, "[READ] errors while parsing read\n"); + free(read_op); + continue; + } + + if (read_ops) { + read_ops_last->next = read_op; + read_ops_last = read_op; + } else { + read_ops = read_op; + read_ops_last = read_op; + } + } + + xmlFreeDoc(doc); + + return 0; +} + +int read_op_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, struct read_op *read_op, int fd), + const char *incdir) +{ + struct read_op *read_op; + const char *filename; + char tmp[PATH_MAX]; + int ret; + int fd; + + for (read_op = read_ops; read_op; read_op = read_op->next) { + filename = read_op->filename; + if (incdir) { + snprintf(tmp, PATH_MAX, "%s/%s", incdir, filename); + if (access(tmp, F_OK) != -1) + filename = tmp; + } + + fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0644); + + if (fd < 0) { + printf("Unable to open %s...\n", read_op->filename); + return ret; + } + + ret = apply(qdl, read_op, fd); + + close(fd); + if (ret) + return ret; + } + + return 0; +} diff --git a/read.h b/read.h new file mode 100644 index 0000000..34d4bc8 --- /dev/null +++ b/read.h @@ -0,0 +1,22 @@ +#ifndef __READ_H__ +#define __READ_H__ + +#include + +struct qdl_device; + +struct read_op { + unsigned sector_size; + const char *filename; + unsigned partition; + unsigned num_sectors; + const char *start_sector; + struct read_op *next; +}; + +int read_op_load(const char *read_op_file); +int read_op_execute(struct qdl_device *qdl, + int (*apply)(struct qdl_device *qdl, struct read_op *read_op, int fd), + const char *incdir); + +#endif