Skip to content

Commit

Permalink
qdl: Add support for <read> operations
Browse files Browse the repository at this point in the history
Being able to read the content of the flash is useful for e.g. making
backups or for testing purposes, add support for a new type of XML
containing <read> tags and execute these operations after
flashing and patching.

[bjorn: Rebased on master, updated commit message, moved read_op_exec after patching]
Signed-off-by: Bjorn Andersson <[email protected]>
  • Loading branch information
cpfair authored and andersson committed Jun 10, 2024
1 parent a60d45c commit a1cd535
Show file tree
Hide file tree
Showing 6 changed files with 267 additions and 1 deletion.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
102 changes: 102 additions & 0 deletions firehose.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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");
Expand Down
10 changes: 10 additions & 0 deletions qdl.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ enum {
QDL_FILE_UNKNOWN,
QDL_FILE_PATCH,
QDL_FILE_PROGRAM,
QDL_FILE_READ,
QDL_FILE_UFS,
QDL_FILE_CONTENTS,
};
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions qdl.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include "patch.h"
#include "program.h"
#include "read.h"
#include <libxml/tree.h>

#define MAPPING_SZ 64
Expand Down
131 changes: 131 additions & 0 deletions read.c
Original file line number Diff line number Diff line change
@@ -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 <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <libxml/parser.h>
#include <libxml/tree.h>

#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;
}
22 changes: 22 additions & 0 deletions read.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#ifndef __READ_H__
#define __READ_H__

#include <stdbool.h>

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

0 comments on commit a1cd535

Please sign in to comment.