diff --git a/README.md b/README.md index 0af1b1df..cf0e4c59 100644 --- a/README.md +++ b/README.md @@ -171,6 +171,24 @@ specifier*** can take the forms: partition names of the GPT partition table in the specified physical partition N. +### Erasing storage + +Storage can be erased using the `erase` command, taking an ***address +specifier*** as described above. When given a bare physical partition number, +the entire physical partition is erased: + +```bash +qdl prog_firehose_ddr.elf erase
+``` + +To wipe the whole storage device, mirroring the "erase all" operation of QFIL +and PCAT, pass `all` instead of an address. Every physical partition on the +device is erased in turn: + +```bash +qdl prog_firehose_ddr.elf erase all +``` + ### Validated Image Programming (VIP) QDL supports **Validated Image Programming (VIP)** mode, which is activated diff --git a/firehose.c b/firehose.c index d37880fd..5d650039 100644 --- a/firehose.c +++ b/firehose.c @@ -581,7 +581,15 @@ static int firehose_try_configure(struct qdl_device *qdl, bool skip_storage_init return 0; } -static int firehose_erase(struct qdl_device *qdl, struct firehose_op *program) +/* + * UFS supports up to 8 logical units, bounding the "erase all" scan. The loop + * stops at the first physical partition that can't be erased, so devices with + * fewer physical partitions only have their existing ones touched. + */ +#define FIREHOSE_MAX_PHYSICAL_PARTITIONS 8 + +static int firehose_erase_one(struct qdl_device *qdl, struct firehose_op *program, + int partition) { unsigned int sector_size; xmlNode *root; @@ -597,7 +605,7 @@ static int firehose_erase(struct qdl_device *qdl, struct firehose_op *program) node = xmlNewChild(root, NULL, (xmlChar *)"erase", NULL); xml_setpropf(node, "SECTOR_SIZE_IN_BYTES", "%d", sector_size); - xml_setpropf(node, "physical_partition_number", "%d", program->partition); + xml_setpropf(node, "physical_partition_number", "%d", partition); /* * Omitting num_sectors and start_sector attributes tells the programmer @@ -621,16 +629,54 @@ static int firehose_erase(struct qdl_device *qdl, struct firehose_op *program) } ret = firehose_read(qdl, 30000, firehose_generic_parser, NULL); - if (ret) - ux_err("failed to erase %s+0x%x\n", program->start_sector, program->num_sectors); - else - ux_info("successfully erased %s+0x%x\n", program->start_sector, program->num_sectors); out: xmlFreeDoc(doc); return ret == FIREHOSE_ACK ? 0 : -1; } +static int firehose_erase(struct qdl_device *qdl, struct firehose_op *program) +{ + int ret; + int i; + + if (!program->erase_all) { + ret = firehose_erase_one(qdl, program, program->partition); + if (ret) + ux_err("failed to erase %s+0x%x\n", + program->start_sector, program->num_sectors); + else if (program->num_sectors > 0) + ux_info("successfully erased %s+0x%x\n", + program->start_sector, program->num_sectors); + else + ux_info("successfully erased physical partition %d\n", + program->partition); + return ret; + } + + /* + * "erase all" mirrors the behavior of QFIL/PCAT and wipes every physical + * partition on the storage device. The programmer provides no way to + * enumerate the physical partitions, so erase them in order and stop at + * the first one that can't be erased, treating that as the end of the + * device. Failing to erase physical partition 0 is a genuine error. + */ + for (i = 0; i < FIREHOSE_MAX_PHYSICAL_PARTITIONS; i++) { + ret = firehose_erase_one(qdl, program, i); + if (ret) { + if (i == 0) { + ux_err("failed to erase physical partition 0\n"); + return ret; + } + ux_debug("erase all stopped after physical partition %d\n", i - 1); + break; + } + ux_info("successfully erased physical partition %d\n", i); + } + + return 0; +} + static int firehose_getsha256digest(struct qdl_device *qdl, struct firehose_op *op); /* diff --git a/firehose.h b/firehose.h index 1c664a4d..a980867b 100644 --- a/firehose.h +++ b/firehose.h @@ -47,6 +47,9 @@ struct firehose_op { unsigned int last_sector; bool is_nand; + /* erase */ + bool erase_all; + unsigned int sparse_chunk_type; uint32_t sparse_fill_value; off_t sparse_offset; diff --git a/program.c b/program.c index 1143e219..637c78b4 100644 --- a/program.c +++ b/program.c @@ -500,6 +500,25 @@ int erase_cmd_add(struct list_head *ops, const char *address) char buf[20]; int ret; + /* + * "erase all" wipes every physical partition on the storage device, as + * done by QFIL/PCAT. It carries no address, so handle it before parsing. + */ + if (!strcmp(address, "all")) { + program = firehose_alloc_op(FIREHOSE_OP_ERASE); + if (!program) { + ux_err("failed to allocate erase command\n"); + return -1; + } + + program->erase_all = true; + program->start_sector = strdup("0"); + + list_append(ops, &program->node); + + return 0; + } + ret = parse_storage_address(address, &partition, &start_sector, &num_sectors, &gpt_partition); if (ret < 0) return ret; diff --git a/qdl.c b/qdl.c index 5a212de0..f218fb29 100644 --- a/qdl.c +++ b/qdl.c @@ -468,7 +468,7 @@ static void print_usage(FILE *out) fprintf(out, "Usage: %s [options]