mirror of
https://github.com/linux-msm/qdl.git
synced 2026-02-25 13:12:25 -08:00
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:
committed by
Bjorn Andersson
parent
10d16aabc8
commit
dff2ed7a72
39
program.c
39
program.c
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
35
qdl.c
@@ -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
3
qdl.h
@@ -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
38
read.c
@@ -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
1
read.h
@@ -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
74
util.c
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user