Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <address specifier>
```

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
Expand Down
58 changes: 52 additions & 6 deletions firehose.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand All @@ -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) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fact that this function is a big if/else makes me feel that it would be cleaner to split this into a separate FIREHOST_OP_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++) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FIREHOSE_MAX_PHYSICAL_PARTITIONS = 8 is UFS-specific but applied to every storage type

How does this behave on eMMC? does physical_partition_number map to the hardware partitions (UDA=0, boot0=1, boot1=2, RPMB=3, GPP=4…), "erase all" would attempt to wipe boot0/boot1 and hit RPMB?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you get if you "qdl firehose.elf read 1/0+100" ?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like the programmer does honor the physical partition id on eMMC storages (tested on Uno Q), which confirms that erase all loop really does walk distinct physical partitions

Dump boot0 (id 1):

$ qdl --storage=emmc prog_firehose_lite.elf read 1/0+100 test_boot0.bin
Flashing device (PID 0x9008, serial: E0171CC8)
Sahara: sending prog_firehose_lite.elf (602112 bytes)
waiting for Firehose programmer...                                             
read "test.bin" successfully 


$ hexdump -C test_boot0.bin 
00000000  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
000001c0  01 00 ee ff ff ff 01 00  00 00 ff ff ff ff 00 00  |................|
000001d0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
000001f0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 55 aa  |..............U.|
00000200  45 46 49 20 50 41 52 54  00 00 01 00 5c 00 00 00  |EFI PART....\...|
00000210  55 b3 3a 7e 00 00 00 00  01 00 00 00 00 00 00 00  |U.:~............|
00000220  ff 1f 00 00 00 00 00 00  22 00 00 00 00 00 00 00  |........".......|
00000230  de 1f 00 00 00 00 00 00  61 38 6a c0 36 ea d9 c8  |........a8j.6...|
00000240  7a f6 b7 09 05 a6 e6 66  02 00 00 00 00 00 00 00  |z......f........|
00000250  04 00 00 00 80 00 00 00  ee a5 5e 8b 00 00 00 00  |..........^.....|
00000260  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00000400  6d e1 1a b9 88 dc 20 40  be 70 e2 14 1c 11 f9 eb  |m..... @.p......|
00000410  46 1d 22 1c 94 7f 97 12  d6 e2 4f 60 08 7e f6 f6  |F.".......O`.~..|
00000420  22 00 00 00 00 00 00 00  29 00 00 00 00 00 00 00  |".......).......|
00000430  00 00 00 00 00 00 00 10  62 00 64 00 61 00 64 00  |........b.d.a.d.|
00000440  64 00 72 00 00 00 00 00  00 00 00 00 00 00 00 00  |d.r.............|
00000450  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00000480  69 ec a5 c4 63 56 b0 43  9e e7 7b c4 b5 cd c6 86  |i...cV.C..{.....|
00000490  02 43 28 23 64 73 48 b1  c7 14 a2 6d 5b b8 d8 1b  |.C(#dsH....m[...|
000004a0  2a 00 00 00 00 00 00 00  31 00 00 00 00 00 00 00  |*.......1.......|
000004b0  00 00 00 00 00 00 00 10  77 00 6c 00 61 00 6e 00  |........w.l.a.n.|
000004c0  61 00 64 00 64 00 72 00  00 00 00 00 00 00 00 00  |a.d.d.r.........|
000004d0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00000500  1b fa 44 aa 62 8e 16 4f  a3 1f 24 4e 35 15 5f be  |..D.b..O..$N5._.|
00000510  6c 50 51 4f a6 f5 ee e1  c9 cb ce 94 0e f1 c3 27  |lPQO...........'|
00000520  32 00 00 00 00 00 00 00  de 1f 00 00 00 00 00 00  |2...............|
00000530  00 00 00 00 00 00 00 10  70 00 65 00 72 00 73 00  |........p.e.r.s.|
00000540  69 00 73 00 74 00 00 00  00 00 00 00 00 00 00 00  |i.s.t...........|
00000550  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00004400  c0 35 32 c4 3f 7b 00 00  00 00 00 00 00 00 00 00  |.52.?{..........|
00004410  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00005400  c0 35 32 c4 3f 7b 00 00  00 00 00 00 00 00 00 00  |.52.?{..........|
00005410  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00006400  76 6f 69 64 0a 00 00 00  00 00 00 00 00 00 00 00  |void............|
00006410  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
0000c800

Dump UDA (id 0):

$ qdl --storage=emmc prog_firehose_lite.elf read 0/0+100 test_uda.bin
...

$ hexdump -C test_uda.bin 
00000000  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
000001c0  01 00 ee ff ff ff 01 00  00 00 ff ff ff ff 00 00  |................|
000001d0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
000001f0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 55 aa  |..............U.|
00000200  45 46 49 20 50 41 52 54  00 00 01 00 5c 00 00 00  |EFI PART....\...|
00000210  16 6d 4e 56 00 00 00 00  01 00 00 00 00 00 00 00  |.mNV............|
00000220  ff 27 d3 01 00 00 00 00  22 00 00 00 00 00 00 00  |.'......".......|
00000230  de 27 d3 01 00 00 00 00  11 2c 87 c7 ed ac 6f 20  |.'.......,....o |
00000240  fd ca e5 54 a1 67 4a 08  02 00 00 00 00 00 00 00  |...T.gJ.........|
00000250  44 00 00 00 80 00 00 00  3b dd ef 46 00 00 00 00  |D.......;..F....|
00000260  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00000400  2c ba a0 de dd cb 05 48  b4 f9 f4 28 25 1c 3e 98  |,......H...(%.>.|
00000410  df b6 c4 3f ca 40 ce d0  02 ee 0b a3 5a 20 d6 0b  |...?.@......Z ..|
00000420  00 00 02 00 00 00 00 00  ff 1b 02 00 00 00 00 00  |................|
00000430  00 00 00 00 00 00 00 10  78 00 62 00 6c 00 5f 00  |........x.b.l._.|
00000440  61 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |a...............|
00000450  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00000480  a3 f1 3d 7a 1a a3 4d 45  bd 78 df 25 9e d4 86 be  |..=z..ME.x.%....|
00000490  d8 d2 c7 69 02 fb ad d9  78 46 e1 56 37 95 9c a5  |...i....xF.V7...|
000004a0  00 1c 02 00 00 00 00 00  ff 37 02 00 00 00 00 00  |.........7......|
000004b0  00 00 00 00 00 00 00 10  78 00 62 00 6c 00 5f 00  |........x.b.l._.|
000004c0  62 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |b...............|
000004d0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00000500  e4 5a 32 5a 76 42 6d b6  0a dd 34 94 df 27 70 6a  |.Z2ZvBm...4..'pj|
00000510  45 d5 ad 47 06 99 09 a6  ad 81 cc d1 ec c4 92 a2  |E..G............|
00000520  00 38 02 00 00 00 00 00  ff 38 02 00 00 00 00 00  |.8.......8......|
00000530  00 00 00 00 00 00 00 10  78 00 62 00 6c 00 5f 00  |........x.b.l._.|
00000540  63 00 6f 00 6e 00 66 00  69 00 67 00 5f 00 61 00  |c.o.n.f.i.g._.a.|
00000550  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00000580  ea e0 62 f4 0e a2 10 4b  86 7a 2d 44 55 36 65 48  |..b....K.z-DU6eH|
00000590  de 41 f7 07 49 d8 cb 32  e4 ff 76 75 87 1a 80 df  |.A..I..2..vu....|
000005a0  00 39 02 00 00 00 00 00  ff 39 02 00 00 00 00 00  |.9.......9......|
000005b0  00 00 00 00 00 00 00 10  78 00 62 00 6c 00 5f 00  |........x.b.l._.|
000005c0  63 00 6f 00 6e 00 66 00  69 00 67 00 5f 00 62 00  |c.o.n.f.i.g._.b.|
000005d0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00000600  7f aa 53 a0 b8 40 1c 4b  ba 08 2f 68 ac 71 a4 f4  |..S..@.K../h.q..|
00000610  9b bd 44 ac d7 77 ad 3c  b3 c8 58 1c fa 42 82 b1  |..D..w.<..X..B..|
00000620  00 3a 02 00 00 00 00 00  ff 59 02 00 00 00 00 00  |.:.......Y......|
00000630  00 00 00 00 00 00 00 10  74 00 7a 00 5f 00 61 00  |........t.z._.a.|
00000640  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
........

and compare both:

$ cmp test_boot0.bin test_uda.bin 
test_boot0.bin test_uda.bin differ: byte 529, line 1

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);

/*
Expand Down
3 changes: 3 additions & 0 deletions firehose.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
19 changes: 19 additions & 0 deletions program.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion qdl.c
Original file line number Diff line number Diff line change
Expand Up @@ -468,7 +468,7 @@ static void print_usage(FILE *out)

fprintf(out, "Usage: %s [options] <prog.mbn> (<program-xml> | <patch-xml> | <read-xml>)...\n", __progname);
fprintf(out, " %s [options] <prog.mbn> ((read | write) <address> <binary>)...\n", __progname);
fprintf(out, " %s [options] <prog.mbn> (erase <address>)...\n", __progname);
fprintf(out, " %s [options] <prog.mbn> (erase (<address> | all))...\n", __progname);
fprintf(out, " %s [options] <prog.mbn> (sha256 <address>)...\n", __progname);
fprintf(out, " %s list\n", __progname);
fprintf(out, " %s ramdump [--debug] [-o <ramdump-path>] [<segment-filter>,...]\n", __progname);
Expand Down
Loading