qdl: Add "read" and "write" commands

There are a number of scenarios where one just want to write a binary
file directly to some specific location on the target, such as writing a
full disk image to LUN 0. Similarily (although not as frequent) one just
want to dump a few sectors of data for inspection.

So far the recommended way to do this has been to craft a program.xml
or a read.xml file and feed to QDL, but this is annoying.

Add support for writing/reading binary data straight to/from the device
by the means of just specifying the "write" or "read" commands on the
QDL commandline, e.g.:

  qdl prog_firehose.elf write 0 debian.img

Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
This commit is contained in:
Bjorn Andersson
2025-09-07 20:56:44 -05:00
committed by Bjorn Andersson
parent 10d16aabc8
commit dff2ed7a72
7 changed files with 188 additions and 3 deletions

View File

@@ -422,3 +422,42 @@ void free_programs(void)
programes = NULL;
}
int program_cmd_add(const char *address, const char *filename)
{
unsigned int start_sector;
unsigned int num_sectors;
struct program *program;
int partition;
char buf[20];
int ret;
ret = parse_storage_address(address, &partition, &start_sector, &num_sectors);
if (ret < 0)
return ret;
program = calloc(1, sizeof(struct program));
program->sector_size = 0;
program->file_offset = 0;
program->filename = filename ? strdup(filename) : NULL;
program->label = filename ? strdup(filename) : NULL;
program->num_sectors = num_sectors;
program->partition = partition;
program->sparse = false;
sprintf(buf, "%u", start_sector);
program->start_sector = strdup(buf);
program->last_sector = 0;
program->is_nand = false;
program->is_erase = false;
if (programes) {
programes_last->next = program;
programes_last = program;
} else {
programes = program;
programes_last = program;
}
return 0;
}

View File

@@ -35,6 +35,7 @@ int program_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl,
int erase_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, struct program *program));
int program_find_bootable_partition(bool *multiple_found);
int program_is_sec_partition_flashed(void);
int program_cmd_add(const char *address, const char *filename);
void free_programs(void);

35
qdl.c
View File

@@ -12,6 +12,7 @@
#include <string.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <unistd.h>
#include "qdl.h"
#include "patch.h"
@@ -33,20 +34,32 @@ enum {
QDL_FILE_READ,
QDL_FILE_UFS,
QDL_FILE_CONTENTS,
QDL_CMD_READ,
QDL_CMD_WRITE,
};
bool qdl_debug;
static int detect_type(const char *xml_file)
static int detect_type(const char *verb)
{
xmlNode *root;
xmlDoc *doc;
xmlNode *node;
int type = QDL_FILE_UNKNOWN;
doc = xmlReadFile(xml_file, NULL, 0);
if (!strcmp(verb, "read"))
return QDL_CMD_READ;
if (!strcmp(verb, "write"))
return QDL_CMD_WRITE;
if (access(verb, F_OK)) {
ux_err("%s is not a verb and not a XML file\n", verb);
return -EINVAL;
}
doc = xmlReadFile(verb, NULL, 0);
if (!doc) {
ux_err("failed to parse XML file \"%s\"\n", xml_file);
ux_err("failed to parse XML file \"%s\"\n", verb);
return -EINVAL;
}
@@ -249,6 +262,22 @@ int main(int argc, char **argv)
if (ret < 0)
errx(1, "ufs_load %s failed", argv[optind]);
break;
case QDL_CMD_READ:
if (optind + 2 >= argc)
errx(1, "read command missing arguments");
ret = read_cmd_add(argv[optind + 1], argv[optind + 2]);
if (ret < 0)
errx(1, "failed to add read command");
optind += 2;
break;
case QDL_CMD_WRITE:
if (optind + 2 >= argc)
errx(1, "write command missing arguments");
ret = program_cmd_add(argv[optind + 1], argv[optind + 2]);
if (ret < 0)
errx(1, "failed to add write command");
optind += 2;
break;
default:
errx(1, "%s type not yet supported", argv[optind]);
break;

3
qdl.h
View File

@@ -85,6 +85,9 @@ void ux_progress(const char *fmt, unsigned int value, unsigned int size, ...);
void print_version(void);
int parse_storage_address(const char *address, int *_partition,
unsigned int *_sector, unsigned int *_length);
extern bool qdl_debug;
#endif

38
read.c
View File

@@ -106,3 +106,41 @@ int read_op_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl,
return 0;
}
int read_cmd_add(const char *address, const char *filename)
{
unsigned int start_sector;
unsigned int num_sectors;
struct read_op *read_op;
int partition;
char buf[20];
int ret;
ret = parse_storage_address(address, &partition, &start_sector, &num_sectors);
if (ret < 0)
return ret;
if (num_sectors == 0) {
ux_err("read command without length specifier not supported\n");
return -1;
}
read_op = calloc(1, sizeof(struct read_op));
read_op->sector_size = 0;
read_op->filename = strdup(filename);
read_op->partition = partition;
read_op->num_sectors = num_sectors;
sprintf(buf, "%u", start_sector);
read_op->start_sector = strdup(buf);
if (read_ops) {
read_ops_last->next = read_op;
read_ops_last = read_op;
} else {
read_ops = read_op;
read_ops_last = read_op;
}
return 0;
}

1
read.h
View File

@@ -19,5 +19,6 @@ 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);
int read_cmd_add(const char *source, const char *filename);
#endif

74
util.c
View File

@@ -4,6 +4,7 @@
* All rights reserved.
*/
#include <ctype.h>
#include <errno.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
@@ -115,3 +116,76 @@ bool attr_as_bool(xmlNode *node, const char *attr, int *errors)
return xmlStrcmp(value, (xmlChar *)"true") == 0;
}
/***
* parse_storage_address() - parse a storage address specifier
* @address: specifier to be parsed
* @physical_partition: physical partition
* @start_sector: start_sector
* @num_sectors: number of sectors
*
* This function parses the provided address specifier and detects the
* following patterns:
*
* N => physical partition N, sector 0
* N/S => physical partition N, sector S
* N/S+L => physical partition N, L sectors at sector S
*
* Returns: 0 on success, -1 on failure
*/
int parse_storage_address(const char *address, int *physical_partition,
unsigned int *start_sector, unsigned int *num_sectors)
{
unsigned long length = 0;
const char *ptr = address;
unsigned long sector = 0;
long partition;
char *end;
errno = 0;
partition = strtol(ptr, &end, 10);
if (end == ptr)
return -1;
if ((errno == ERANGE && partition == LONG_MAX) || partition < 0)
return -1;
if (end[0] == '\0')
goto done;
if (end[0] != '/')
return -1;
ptr = end + 1;
errno = 0;
sector = strtoul(ptr, &end, 10);
if (end == ptr)
return -1;
if (errno == ERANGE && sector == ULONG_MAX)
return -1;
if (end[0] == '\0')
goto done;
if (end[0] != '+')
return -1;
ptr = end + 1;
errno = 0;
length = strtoul(ptr, &end, 10);
if (end == ptr)
return -1;
if (errno == ERANGE && length == ULONG_MAX)
return -1;
if (length == 0)
return -1;
if (end[0] != '\0')
return -1;
done:
*physical_partition = partition;
*start_sector = sector;
*num_sectors = length;
return 0;
}