mirror of
https://github.com/linux-msm/qdl.git
synced 2026-02-25 13:12:25 -08:00
firehose: support VIP extension
This extends the Firehose protocol implementation to support the VIP extension. It implements a state machine that counts the number of packets sent, then injects the VIP table as the next RAW packet when the Firehose programmer runs out of provided digests and needs a new table of digests to validate the next packets. For example: Packet 0: DigestsTableToSign.bin.mbn (53 digest + 1 digest of next table) Packet 1: <configure> Packet 2: <program> Packet 3: ... ... Packet 54: ChainedTableOfDigests0.bin (255 digests + digest of next table) Packet 55: <program> ... Packet 309: ChainedTableOfDigests1.bin To enable VIP extension provide a path where previously generated VIP tables are stored using "--vip-table-path" param: $ qdl --vip-table-path "<vip-table-path>" prog_firehose_ddr.elf \ rawprogram*.xml patch*.xml Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
This commit is contained in:
committed by
Konrad Dybcio
parent
6454ab46f2
commit
8818b98e85
60
firehose.c
60
firehose.c
@@ -159,6 +159,23 @@ static int firehose_write(struct qdl_device *qdl, xmlDoc *doc)
|
||||
|
||||
xmlDocDumpMemory(doc, &s, &len);
|
||||
|
||||
ret = vip_transfer_handle_tables(qdl);
|
||||
if (ret) {
|
||||
ux_err("VIP: error occurred during VIP table transmission\n");
|
||||
return -1;
|
||||
}
|
||||
if (vip_transfer_status_check_needed(qdl)) {
|
||||
ret = firehose_read(qdl, 30000, firehose_generic_parser, NULL);
|
||||
if (ret) {
|
||||
ux_err("VIP: sending of digest table failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ux_info("VIP: digest table has been sent successfully\n");
|
||||
|
||||
vip_transfer_clear_status(qdl);
|
||||
}
|
||||
|
||||
vip_gen_chunk_init(qdl);
|
||||
|
||||
for (;;) {
|
||||
@@ -184,8 +201,6 @@ static int firehose_write(struct qdl_device *qdl, xmlDoc *doc)
|
||||
return ret < 0 ? -saved_errno : 0;
|
||||
}
|
||||
|
||||
static size_t max_payload_size = 1048576;
|
||||
|
||||
/**
|
||||
* firehose_configure_response_parser() - parse a configure response
|
||||
* @node: response xmlNode
|
||||
@@ -270,7 +285,8 @@ static int firehose_configure(struct qdl_device *qdl, bool skip_storage_init,
|
||||
size_t size = 0;
|
||||
int ret;
|
||||
|
||||
ret = firehose_send_configure(qdl, max_payload_size, skip_storage_init, storage, &size);
|
||||
ret = firehose_send_configure(qdl, qdl->max_payload_size, skip_storage_init,
|
||||
storage, &size);
|
||||
if (ret < 0) {
|
||||
ux_err("configure request failed\n");
|
||||
return -1;
|
||||
@@ -284,24 +300,21 @@ static int firehose_configure(struct qdl_device *qdl, bool skip_storage_init,
|
||||
return 0;
|
||||
|
||||
/* Retry if remote proposed different size */
|
||||
if (size != max_payload_size) {
|
||||
if (size != qdl->max_payload_size) {
|
||||
ret = firehose_send_configure(qdl, size, skip_storage_init, storage, &size);
|
||||
if (ret != FIREHOSE_ACK) {
|
||||
ux_err("configure request with updated payload size failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
max_payload_size = size;
|
||||
qdl->max_payload_size = size;
|
||||
}
|
||||
|
||||
ux_debug("accepted max payload size: %zu\n", max_payload_size);
|
||||
ux_debug("accepted max payload size: %zu\n", qdl->max_payload_size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define MIN(x, y) ((x) < (y) ? (x) : (y))
|
||||
#define ROUND_UP(x, a) (((x) + (a) - 1) & ~((a) - 1))
|
||||
|
||||
static int firehose_erase(struct qdl_device *qdl, struct program *program)
|
||||
{
|
||||
xmlNode *root;
|
||||
@@ -370,7 +383,7 @@ static int firehose_program(struct qdl_device *qdl, struct program *program, int
|
||||
num_sectors = program->num_sectors;
|
||||
}
|
||||
|
||||
buf = malloc(max_payload_size);
|
||||
buf = malloc(qdl->max_payload_size);
|
||||
if (!buf)
|
||||
err(1, "failed to allocate sector buffer");
|
||||
|
||||
@@ -417,7 +430,7 @@ static int firehose_program(struct qdl_device *qdl, struct program *program, int
|
||||
* not for the whole binary.
|
||||
*/
|
||||
vip_gen_chunk_init(qdl);
|
||||
chunk_size = MIN(max_payload_size / program->sector_size, left);
|
||||
chunk_size = MIN(qdl->max_payload_size / program->sector_size, left);
|
||||
|
||||
n = read(fd, buf, chunk_size * program->sector_size);
|
||||
if (n < 0) {
|
||||
@@ -425,10 +438,27 @@ static int firehose_program(struct qdl_device *qdl, struct program *program, int
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (n < max_payload_size)
|
||||
memset(buf + n, 0, max_payload_size - n);
|
||||
if (n < qdl->max_payload_size)
|
||||
memset(buf + n, 0, qdl->max_payload_size - n);
|
||||
|
||||
vip_gen_chunk_update(qdl, buf, chunk_size * program->sector_size);
|
||||
|
||||
ret = vip_transfer_handle_tables(qdl);
|
||||
if (ret) {
|
||||
ux_err("VIP: error occurred during VIP table transmission\n");
|
||||
return -1;
|
||||
}
|
||||
if (vip_transfer_status_check_needed(qdl)) {
|
||||
ret = firehose_read(qdl, 30000, firehose_generic_parser, NULL);
|
||||
if (ret) {
|
||||
ux_err("VIP: sending of digest table failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ux_info("VIP: digest table has been sent successfully\n");
|
||||
|
||||
vip_transfer_clear_status(qdl);
|
||||
}
|
||||
n = qdl_write(qdl, buf, chunk_size * program->sector_size);
|
||||
if (n < 0) {
|
||||
ux_err("USB write failed for data chunk\n");
|
||||
@@ -486,7 +516,7 @@ static int firehose_read_op(struct qdl_device *qdl, struct read_op *read_op, int
|
||||
int n;
|
||||
bool expect_empty;
|
||||
|
||||
buf = malloc(max_payload_size);
|
||||
buf = malloc(qdl->max_payload_size);
|
||||
if (!buf)
|
||||
err(1, "failed to allocate sector buffer");
|
||||
|
||||
@@ -519,7 +549,7 @@ static int firehose_read_op(struct qdl_device *qdl, struct read_op *read_op, int
|
||||
left = read_op->num_sectors;
|
||||
expect_empty = false;
|
||||
while (left > 0 || expect_empty) {
|
||||
chunk_size = MIN(max_payload_size / read_op->sector_size, left);
|
||||
chunk_size = MIN(qdl->max_payload_size / read_op->sector_size, left);
|
||||
|
||||
n = qdl_read(qdl, buf, chunk_size * read_op->sector_size, 30000);
|
||||
if (n < 0) {
|
||||
|
||||
18
qdl.c
18
qdl.c
@@ -84,7 +84,7 @@ static void print_usage(void)
|
||||
extern const char *__progname;
|
||||
|
||||
fprintf(stderr,
|
||||
"%s [--debug] [--dry-run] [--version] [--allow-missing] [--storage <emmc|nand|ufs>] [--finalize-provisioning] [--include <PATH>] [--serial <NUM>] [--out-chunk-size <SIZE>] [--create-digests <PATH>] <prog.mbn> [<program> <patch> ...]\n",
|
||||
"%s [--debug] [--dry-run] [--version] [--allow-missing] [--storage <emmc|nand|ufs>] [--finalize-provisioning] [--include <PATH>] [--serial <NUM>] [--out-chunk-size <SIZE>] [--create-digests <PATH>] [--vip_table_path <PATH>] <prog.mbn> [<program> <patch> ...]\n",
|
||||
__progname);
|
||||
}
|
||||
|
||||
@@ -98,6 +98,7 @@ int main(int argc, char **argv)
|
||||
char *incdir = NULL;
|
||||
char *serial = NULL;
|
||||
const char *vip_generate_dir = NULL;
|
||||
const char *vip_table_path = NULL;
|
||||
int type;
|
||||
int ret;
|
||||
int opt;
|
||||
@@ -115,6 +116,7 @@ int main(int argc, char **argv)
|
||||
{"finalize-provisioning", no_argument, 0, 'l'},
|
||||
{"out-chunk-size", required_argument, 0, OPT_OUT_CHUNK_SIZE },
|
||||
{"serial", required_argument, 0, 'S'},
|
||||
{"vip-table-path", required_argument, 0, 'D'},
|
||||
{"storage", required_argument, 0, 's'},
|
||||
{"allow-missing", no_argument, 0, 'f'},
|
||||
{"allow-fusing", no_argument, 0, 'c'},
|
||||
@@ -160,6 +162,9 @@ int main(int argc, char **argv)
|
||||
case 'S':
|
||||
serial = optarg;
|
||||
break;
|
||||
case 'D':
|
||||
vip_table_path = optarg;
|
||||
break;
|
||||
default:
|
||||
print_usage();
|
||||
return 1;
|
||||
@@ -178,6 +183,14 @@ int main(int argc, char **argv)
|
||||
goto out_cleanup;
|
||||
}
|
||||
|
||||
if (vip_table_path) {
|
||||
if (vip_generate_dir)
|
||||
errx(1, "VIP mode and VIP table generation can't be enabled together\n");
|
||||
ret = vip_transfer_init(qdl, vip_table_path);
|
||||
if (ret)
|
||||
errx(1, "VIP initialization failed\n");
|
||||
}
|
||||
|
||||
if (out_chunk_size)
|
||||
qdl_set_out_chunk_size(qdl, out_chunk_size);
|
||||
|
||||
@@ -251,6 +264,9 @@ out_cleanup:
|
||||
free_programs();
|
||||
free_patches();
|
||||
|
||||
if (qdl->vip_data.state != VIP_DISABLED)
|
||||
vip_transfer_deinit(qdl);
|
||||
|
||||
qdl_deinit(qdl);
|
||||
|
||||
return !!ret;
|
||||
|
||||
10
qdl.h
10
qdl.h
@@ -7,6 +7,7 @@
|
||||
#include "program.h"
|
||||
#include "read.h"
|
||||
#include <libxml/tree.h>
|
||||
#include "vip.h"
|
||||
|
||||
#define container_of(ptr, typecast, member) ({ \
|
||||
void *_ptr = (void *)(ptr); \
|
||||
@@ -14,6 +15,9 @@
|
||||
|
||||
#define MAPPING_SZ 64
|
||||
|
||||
#define MIN(x, y) ((x) < (y) ? (x) : (y))
|
||||
#define ROUND_UP(x, a) (((x) + (a) - 1) & ~((a) - 1))
|
||||
|
||||
enum QDL_DEVICE_TYPE {
|
||||
QDL_DEVICE_USB,
|
||||
QDL_DEVICE_SIM,
|
||||
@@ -22,14 +26,19 @@ enum QDL_DEVICE_TYPE {
|
||||
struct qdl_device {
|
||||
enum QDL_DEVICE_TYPE dev_type;
|
||||
int fd;
|
||||
size_t max_payload_size;
|
||||
|
||||
int (*open)(struct qdl_device *qdl, const char *serial);
|
||||
int (*read)(struct qdl_device *qdl, void *buf, size_t len, unsigned int timeout);
|
||||
int (*write)(struct qdl_device *qdl, const void *buf, size_t nbytes);
|
||||
void (*close)(struct qdl_device *qdl);
|
||||
void (*set_out_chunk_size)(struct qdl_device *qdl, long size);
|
||||
void (*set_vip_transfer)(struct qdl_device *qdl, const char *signed_table,
|
||||
const char *chained_table);
|
||||
|
||||
char *mappings[MAPPING_SZ]; // array index is the id from the device
|
||||
|
||||
struct vip_transfer_data vip_data;
|
||||
};
|
||||
|
||||
struct libusb_device_handle;
|
||||
@@ -41,6 +50,7 @@ void qdl_close(struct qdl_device *qdl);
|
||||
int qdl_read(struct qdl_device *qdl, void *buf, size_t len, unsigned int timeout);
|
||||
int qdl_write(struct qdl_device *qdl, const void *buf, size_t len);
|
||||
void qdl_set_out_chunk_size(struct qdl_device *qdl, long size);
|
||||
int qdl_vip_transfer_enable(struct qdl_device *qdl, const char *vip_table_path);
|
||||
|
||||
struct qdl_device *usb_init(void);
|
||||
struct qdl_device *sim_init(void);
|
||||
|
||||
1
sim.c
1
sim.c
@@ -55,6 +55,7 @@ struct qdl_device *sim_init(void)
|
||||
qdl->write = sim_write;
|
||||
qdl->close = sim_close;
|
||||
qdl->set_out_chunk_size = sim_set_out_chunk_size;
|
||||
qdl->max_payload_size = 1048576;
|
||||
|
||||
return qdl;
|
||||
}
|
||||
|
||||
3
usb.c
3
usb.c
@@ -293,12 +293,15 @@ struct qdl_device *usb_init(void)
|
||||
if (!qdl)
|
||||
return NULL;
|
||||
|
||||
memset(qdl, 0, sizeof(struct qdl_device_usb));
|
||||
|
||||
qdl->dev_type = QDL_DEVICE_USB;
|
||||
qdl->open = usb_open;
|
||||
qdl->read = usb_read;
|
||||
qdl->write = usb_write;
|
||||
qdl->close = usb_close;
|
||||
qdl->set_out_chunk_size = usb_set_out_chunk_size;
|
||||
qdl->max_payload_size = 1048576;
|
||||
|
||||
return qdl;
|
||||
}
|
||||
|
||||
153
vip.c
153
vip.c
@@ -2,10 +2,13 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "sim.h"
|
||||
@@ -14,6 +17,7 @@
|
||||
#define CHAINED_TABLE_FILE_PREF "ChainedTableOfDigests"
|
||||
#define CHAINED_TABLE_FILE_MAX_NAME 64
|
||||
#define DIGEST_TABLE_TO_SIGN_FILE "DigestsToSign.bin"
|
||||
#define DIGEST_TABLE_TO_SIGN_FILE_MBN (DIGEST_TABLE_TO_SIGN_FILE ".mbn")
|
||||
#define MAX_DIGESTS_PER_SIGNED_FILE 54
|
||||
#define MAX_DIGESTS_PER_SIGNED_TABLE (MAX_DIGESTS_PER_SIGNED_FILE - 1)
|
||||
#define MAX_DIGESTS_PER_CHAINED_FILE 256
|
||||
@@ -346,3 +350,152 @@ void vip_gen_finalize(struct qdl_device *qdl)
|
||||
free(vip_gen);
|
||||
sim_set_digest_generation(false, qdl, NULL);
|
||||
}
|
||||
|
||||
int vip_transfer_init(struct qdl_device *qdl, const char *vip_table_path)
|
||||
{
|
||||
char fullpath[PATH_MAX];
|
||||
|
||||
snprintf(fullpath, sizeof(fullpath), "%s/%s",
|
||||
vip_table_path, DIGEST_TABLE_TO_SIGN_FILE_MBN);
|
||||
qdl->vip_data.signed_table_fd = open(fullpath, O_RDONLY);
|
||||
if (!qdl->vip_data.signed_table_fd) {
|
||||
ux_err("Can't open signed table %s\n", fullpath);
|
||||
return -1;
|
||||
}
|
||||
|
||||
qdl->vip_data.chained_num = 0;
|
||||
|
||||
for (int i = 0; i < MAX_CHAINED_FILES; ++i) {
|
||||
snprintf(fullpath, sizeof(fullpath), "%s/%s%d%s",
|
||||
vip_table_path, CHAINED_TABLE_FILE_PREF, i, ".bin");
|
||||
|
||||
int fd = open(fullpath, O_RDONLY);
|
||||
|
||||
if (fd == -1) {
|
||||
if (errno == ENOENT)
|
||||
break;
|
||||
|
||||
ux_err("Can't open signed table %s\n", fullpath);
|
||||
goto out_cleanup;
|
||||
}
|
||||
|
||||
qdl->vip_data.chained_fds[qdl->vip_data.chained_num++] = fd;
|
||||
}
|
||||
|
||||
qdl->vip_data.state = VIP_INIT;
|
||||
qdl->vip_data.chained_cur = 0;
|
||||
|
||||
return 0;
|
||||
|
||||
out_cleanup:
|
||||
close(qdl->vip_data.signed_table_fd);
|
||||
for (int i = 0; i < qdl->vip_data.chained_num - 1; ++i)
|
||||
close(qdl->vip_data.chained_fds[i]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
void vip_transfer_deinit(struct qdl_device *qdl)
|
||||
{
|
||||
close(qdl->vip_data.signed_table_fd);
|
||||
for (int i = 0; i < qdl->vip_data.chained_num - 1; ++i)
|
||||
close(qdl->vip_data.chained_fds[i]);
|
||||
}
|
||||
|
||||
static int vip_transfer_send_raw(struct qdl_device *qdl, int table_fd)
|
||||
{
|
||||
struct stat sb;
|
||||
int ret;
|
||||
void *buf;
|
||||
size_t n;
|
||||
|
||||
ret = fstat(table_fd, &sb);
|
||||
if (ret < 0) {
|
||||
ux_err("Failed to stat digest table file\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
buf = malloc(sb.st_size);
|
||||
if (!buf) {
|
||||
ux_err("Failed to allocate transfer buffer\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
n = read(table_fd, buf, sb.st_size);
|
||||
if (n < 0) {
|
||||
ux_err("failed to read binary\n");
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
n = qdl_write(qdl, buf, sb.st_size);
|
||||
if (n < 0) {
|
||||
ux_err("USB write failed for data chunk\n");
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
free(buf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int vip_transfer_handle_tables(struct qdl_device *qdl)
|
||||
{
|
||||
struct vip_transfer_data *vip_data = &qdl->vip_data;
|
||||
int ret = 0;
|
||||
|
||||
if (vip_data->state == VIP_DISABLED)
|
||||
return 0;
|
||||
|
||||
if (vip_data->state == VIP_INIT) {
|
||||
/* Send initial signed table */
|
||||
ret = vip_transfer_send_raw(qdl, vip_data->signed_table_fd);
|
||||
if (ret) {
|
||||
ux_err("VIP: failed to send the Signed VIP table\n");
|
||||
return ret;
|
||||
}
|
||||
ux_debug("VIP: successfully sent the Initial VIP table\n");
|
||||
|
||||
vip_data->state = VIP_SEND_DATA;
|
||||
vip_data->frames_sent = 0;
|
||||
vip_data->frames_left = MAX_DIGESTS_PER_SIGNED_TABLE;
|
||||
vip_data->fh_parse_status = true;
|
||||
}
|
||||
if (vip_data->state == VIP_SEND_NEXT_TABLE) {
|
||||
if (vip_data->chained_cur >= vip_data->chained_num) {
|
||||
ux_err("VIP: the required quantity of chained tables is missing\n");
|
||||
return -1;
|
||||
}
|
||||
ret = vip_transfer_send_raw(qdl, vip_data->chained_fds[vip_data->chained_cur]);
|
||||
if (ret) {
|
||||
ux_err("VIP: failed to send the chained VIP table\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ux_debug("VIP: successfully sent " CHAINED_TABLE_FILE_PREF "%lu.bin\n",
|
||||
vip_data->chained_cur);
|
||||
|
||||
vip_data->state = VIP_SEND_DATA;
|
||||
vip_data->frames_sent = 0;
|
||||
vip_data->frames_left = MAX_DIGESTS_PER_CHAINED_TABLE;
|
||||
vip_data->fh_parse_status = true;
|
||||
vip_data->chained_cur++;
|
||||
}
|
||||
|
||||
vip_data->frames_sent++;
|
||||
if (vip_data->frames_sent >= vip_data->frames_left)
|
||||
vip_data->state = VIP_SEND_NEXT_TABLE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool vip_transfer_status_check_needed(struct qdl_device *qdl)
|
||||
{
|
||||
return qdl->vip_data.fh_parse_status;
|
||||
}
|
||||
|
||||
void vip_transfer_clear_status(struct qdl_device *qdl)
|
||||
{
|
||||
qdl->vip_data.fh_parse_status = false;
|
||||
}
|
||||
|
||||
28
vip.h
28
vip.h
@@ -9,6 +9,34 @@
|
||||
|
||||
struct vip_table_generator;
|
||||
|
||||
enum vip_state {
|
||||
VIP_DISABLED,
|
||||
VIP_INIT,
|
||||
VIP_SEND_NEXT_TABLE,
|
||||
VIP_SEND_DATA,
|
||||
VIP_MAX,
|
||||
};
|
||||
#define MAX_CHAINED_FILES 32
|
||||
|
||||
struct vip_transfer_data {
|
||||
enum vip_state state;
|
||||
int signed_table_fd;
|
||||
int chained_fds[MAX_CHAINED_FILES];
|
||||
size_t chained_num;
|
||||
size_t chained_cur;
|
||||
size_t digests;
|
||||
size_t frames_sent;
|
||||
size_t frames_left;
|
||||
size_t chained_table_size;
|
||||
bool fh_parse_status;
|
||||
};
|
||||
|
||||
int vip_transfer_init(struct qdl_device *qdl, const char *vip_table_path);
|
||||
void vip_transfer_deinit(struct qdl_device *qdl);
|
||||
int vip_transfer_handle_tables(struct qdl_device *qdl);
|
||||
bool vip_transfer_status_check_needed(struct qdl_device *qdl);
|
||||
void vip_transfer_clear_status(struct qdl_device *qdl);
|
||||
|
||||
int vip_gen_init(struct qdl_device *qdl, const char *path);
|
||||
void vip_gen_chunk_init(struct qdl_device *qdl);
|
||||
void vip_gen_chunk_update(struct qdl_device *qdl, const void *buf, size_t len);
|
||||
|
||||
Reference in New Issue
Block a user