diff --git a/firehose.c b/firehose.c index c0c0682..e3afb28 100644 --- a/firehose.c +++ b/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) { diff --git a/qdl.c b/qdl.c index b8bafcb..2840de7 100644 --- a/qdl.c +++ b/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 ] [--finalize-provisioning] [--include ] [--serial ] [--out-chunk-size ] [--create-digests ] [ ...]\n", + "%s [--debug] [--dry-run] [--version] [--allow-missing] [--storage ] [--finalize-provisioning] [--include ] [--serial ] [--out-chunk-size ] [--create-digests ] [--vip_table_path ] [ ...]\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; diff --git a/qdl.h b/qdl.h index 7d58385..93203b9 100644 --- a/qdl.h +++ b/qdl.h @@ -7,6 +7,7 @@ #include "program.h" #include "read.h" #include +#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); diff --git a/sim.c b/sim.c index 72673f6..29e9a77 100644 --- a/sim.c +++ b/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; } diff --git a/usb.c b/usb.c index 8fbfa6a..73e77bc 100644 --- a/usb.c +++ b/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; } diff --git a/vip.c b/vip.c index 5b39286..291656b 100644 --- a/vip.c +++ b/vip.c @@ -2,10 +2,13 @@ /* * Copyright (c) 2025, Qualcomm Innovation Center, Inc. All rights reserved. */ +#include #include +#include #include #include #include +#include #include #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; +} diff --git a/vip.h b/vip.h index 60993cb..b916c79 100644 --- a/vip.h +++ b/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);