From 6a67b6bd6404927f9b56669fd33f627cd66dbfdc Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Thu, 28 Aug 2025 13:49:24 -0500 Subject: [PATCH] sparse: Parse sparse files at load time The nested loop and the parsing of the sparse files during application isn't very clean, and results in parse errors coming late in the process. It also results in two different lists of program elements, and the sparse entries are currently not cleaned up after application. Instead update the program parser, such that when a sparse entry is found the sparse image is parsed and the chunks are added to the program list. The filename member of the original program entry is dropped, to prevent programming the region, while still tracking it. This could allow us to later implement an "erase before flash" of the program segments. This results in a much cleaner application of the program entries, and any errors are detected up front. Signed-off-by: Bjorn Andersson --- program.c | 125 +++++++++++++++++++++++++----------------------------- 1 file changed, 58 insertions(+), 67 deletions(-) diff --git a/program.c b/program.c index 71b0cc7..edf0f65 100644 --- a/program.c +++ b/program.c @@ -58,11 +58,9 @@ static int load_erase_tag(xmlNode *node, bool is_nand) return 0; } -static struct program *program_load_sparse(struct program *program, int fd) +static int program_load_sparse(struct program *program, int fd) { struct program *program_sparse = NULL; - struct program *programes_sparse = NULL; - struct program *programes_sparse_last = NULL; char tmp[PATH_MAX]; sparse_header_t sparse_header; @@ -78,20 +76,25 @@ static struct program *program_load_sparse(struct program *program, int fd) * for a partition node but lacks a sparse header, * it will be validated against the defined partition size. * If the sizes match, it is likely that the 'sparse="true"' attribute - * was set by mistake. + * was set by mistake, fix the sparse flag and add the + * program entry to the list. */ if ((off_t)program->sector_size * program->num_sectors == lseek(fd, 0, SEEK_END)) { - program_sparse = calloc(1, sizeof(struct program)); - memcpy(program_sparse, program, sizeof(struct program)); - program_sparse->sparse = false; - program_sparse->next = NULL; - return program_sparse; + program->sparse = false; + + if (programes) { + programes_last->next = program; + programes_last = program; + } else { + programes = program; + programes_last = program; + } } ux_err("[PROGRAM] Unable to parse sparse header at %s...failed\n", program->filename); - return NULL; + return -1; } for (uint32_t i = 0; i < sparse_header.total_chunks; ++i) { @@ -102,7 +105,7 @@ static struct program *program_load_sparse(struct program *program, int fd) if (chunk_type < 0) { ux_err("[PROGRAM] Unable to parse sparse chunk %i at %s...failed\n", i, program->filename); - return NULL; + return -1; } if (chunk_size == 0) @@ -111,7 +114,7 @@ static struct program *program_load_sparse(struct program *program, int fd) if (chunk_size % program->sector_size != 0) { ux_err("[SPARSE] File chunk #%u size %" PRIu64 " is not a sector-multiple\n", i, chunk_size); - return NULL; + return -1; } if (chunk_size / program->sector_size >= UINT_MAX) { @@ -121,51 +124,39 @@ static struct program *program_load_sparse(struct program *program, int fd) */ ux_err("[SPARSE] File chunk #%u size %" PRIu64 " is too large\n", i, chunk_size); - return NULL; + return -1; } - switch (chunk_type) { - case CHUNK_TYPE_RAW: + if (chunk_type == CHUNK_TYPE_RAW || chunk_type == CHUNK_TYPE_FILL) { program_sparse = calloc(1, sizeof(struct program)); - memcpy(program_sparse, program, sizeof(struct program)); + program_sparse->pages_per_block = program->pages_per_block; + program_sparse->sector_size = program->sector_size; + program_sparse->file_offset = program->file_offset; + program_sparse->filename = strdup(program->filename); + program_sparse->label = strdup(program->label); + program_sparse->partition = program->partition; + program_sparse->sparse = program->sparse; + program_sparse->start_sector = strdup(program->start_sector); + program_sparse->last_sector = program->last_sector; + program_sparse->is_nand = program->is_nand; + program_sparse->is_erase = false; - program_sparse->next = NULL; + program_sparse->sparse_chunk_type = chunk_type; program_sparse->num_sectors = chunk_size / program->sector_size; - program_sparse->sparse_chunk_type = CHUNK_TYPE_RAW; - program_sparse->sparse_offset = sparse_offset; - - if (programes_sparse) { - programes_sparse_last->next = program_sparse; - programes_sparse_last = program_sparse; - } else { - programes_sparse = program_sparse; - programes_sparse_last = program_sparse; - } - - break; - case CHUNK_TYPE_FILL: - - program_sparse = calloc(1, sizeof(struct program)); - memcpy(program_sparse, program, sizeof(struct program)); + if (chunk_type == CHUNK_TYPE_RAW) + program_sparse->sparse_offset = sparse_offset; + else + program_sparse->sparse_fill_value = sparse_fill_value; program_sparse->next = NULL; - program_sparse->num_sectors = chunk_size / program->sector_size; - - program_sparse->sparse_chunk_type = CHUNK_TYPE_FILL; - program_sparse->sparse_fill_value = sparse_fill_value; - - if (programes_sparse) { - programes_sparse_last->next = program_sparse; - programes_sparse_last = program_sparse; + if (programes) { + programes_last->next = program_sparse; + programes_last = program_sparse; } else { - programes_sparse = program_sparse; - programes_sparse_last = program_sparse; + programes = program_sparse; + programes_last = program_sparse; } - - break; - default: - break; } start_sector = (unsigned int)strtoul(program->start_sector, NULL, 0); @@ -174,7 +165,7 @@ static struct program *program_load_sparse(struct program *program, int fd) program->start_sector = strdup(tmp); } - return programes_sparse; + return 0; } static int load_program_tag(xmlNode *node, bool is_nand, bool allow_missing, const char *incdir) @@ -182,7 +173,8 @@ static int load_program_tag(xmlNode *node, bool is_nand, bool allow_missing, con struct program *program; char tmp[PATH_MAX]; int errors = 0; - int fd; + int ret; + int fd = -1; program = calloc(1, sizeof(struct program)); @@ -232,7 +224,19 @@ static int load_program_tag(xmlNode *node, bool is_nand, bool allow_missing, con free((void *)program->filename); program->filename = NULL; } - close(fd); + } + + if (fd >= 0 && program->sparse) { + ret = program_load_sparse(program, fd); + if (ret < 0) + return -1; + + /* + * Chunks were added to the program list, drop the filename of + * the parent, to prevent this from being written to the device + */ + free((void *)program->filename); + program->filename = NULL; } if (programes) { @@ -243,6 +247,9 @@ static int load_program_tag(xmlNode *node, bool is_nand, bool allow_missing, con programes_last = program; } + if (fd >= 0) + close(fd); + return 0; } @@ -286,7 +293,6 @@ out: int program_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, struct program *program, int fd)) { struct program *program; - struct program *program_sparse; int ret; int fd; @@ -301,22 +307,7 @@ int program_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, return -1; } - if (!program->sparse) { - ret = apply(qdl, program, fd); - } else { - program_sparse = program_load_sparse(program, fd); - if (!program_sparse) { - ux_err("[PROGRAM] load sparse failed\n"); - return -EINVAL; - } - - for (; program_sparse; program_sparse = program_sparse->next) { - ret = apply(qdl, program_sparse, fd); - if (ret) - break; - } - } - + ret = apply(qdl, program, fd); close(fd); if (ret) return ret;