Skip to content

Commit c059327

Browse files
Maximilian BlenkMaximilian Blenk
Maximilian Blenk
authored and
Maximilian Blenk
committed
qdl: Implement sparse image flashing using libsparse
Extend XML with the optional "sparse" tag. If sparse is true the file is interpreted as sparse file and only non "Don't Care" Sections are flashed. Signed-off-by: Maximilian Blenk <[email protected]>
1 parent 4f237c2 commit c059327

File tree

5 files changed

+228
-31
lines changed

5 files changed

+228
-31
lines changed

Makefile

+8-4
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,21 @@
11
OUT := qdl
22

3-
CFLAGS := -O2 -Wall -g `xml2-config --cflags`
4-
LDFLAGS := `xml2-config --libs` -ludev
3+
CFLAGS := -O2 -Wall -g `xml2-config --cflags` -I libsparse/include
4+
LDFLAGS := `xml2-config --libs` -ludev -lz
55
prefix := /usr/local
66

77
SRCS := firehose.c qdl.c sahara.c util.c patch.c program.c ufs.c
88
OBJS := $(SRCS:.c=.o)
99

10-
$(OUT): $(OBJS)
11-
$(CC) -o $@ $^ $(LDFLAGS)
10+
$(OUT): $(OBJS) libsparse/libsparse.a
11+
$(CXX) -o $@ $^ $(LDFLAGS)
12+
13+
libsparse/libsparse.a:
14+
$(MAKE) -C libsparse
1215

1316
clean:
1417
rm -f $(OUT) $(OBJS)
18+
$(MAKE) -C libsparse clean
1519

1620
install: $(OUT)
1721
install -D -m 755 $< $(DESTDIR)$(prefix)/bin/$<

firehose.c

+211-24
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,12 @@
4848
#include <unistd.h>
4949
#include <libxml/parser.h>
5050
#include <libxml/tree.h>
51+
#include <sparse/sparse.h>
5152
#include "qdl.h"
5253
#include "ufs.h"
5354

55+
#define SPARSE_BUFFER_SIZE 8
56+
5457
static void xml_setpropf(xmlNode *node, const char *attr, const char *fmt, ...)
5558
{
5659
xmlChar buf[128];
@@ -288,14 +291,47 @@ static int firehose_configure(struct qdl_device *qdl, bool skip_storage_init, co
288291
#define MIN(x, y) ((x) < (y) ? (x) : (y))
289292
#define ROUND_UP(x, a) (((x) + (a) - 1) & ~((a) - 1))
290293

294+
static int firehose_program_init(struct qdl_device *qdl, unsigned int sector_size,
295+
unsigned int num_sectors, unsigned int partition_number,
296+
const char *start_sector, const char *filename) {
297+
xmlNode *root;
298+
xmlNode *node;
299+
xmlDoc *doc;
300+
int ret;
301+
302+
doc = xmlNewDoc((xmlChar*)"1.0");
303+
root = xmlNewNode(NULL, (xmlChar*)"data");
304+
xmlDocSetRootElement(doc, root);
305+
306+
node = xmlNewChild(root, NULL, (xmlChar*)"program", NULL);
307+
xml_setpropf(node, "SECTOR_SIZE_IN_BYTES", "%d", sector_size);
308+
xml_setpropf(node, "num_partition_sectors", "%d", num_sectors);
309+
xml_setpropf(node, "physical_partition_number", "%d", partition_number);
310+
xml_setpropf(node, "start_sector", "%s", start_sector);
311+
if (filename)
312+
xml_setpropf(node, "filename", "%s", filename);
313+
314+
ret = firehose_write(qdl, doc);
315+
if (ret < 0) {
316+
fprintf(stderr, "[PROGRAM] failed to write program command\n");
317+
goto out;
318+
}
319+
320+
ret = firehose_read(qdl, -1, firehose_nop_parser);
321+
if (ret) {
322+
fprintf(stderr, "[PROGRAM] failed to setup programming\n");
323+
goto out;
324+
}
325+
out:
326+
xmlFreeDoc(doc);
327+
return ret;
328+
}
329+
291330
static int firehose_program(struct qdl_device *qdl, struct program *program, int fd)
292331
{
293332
unsigned num_sectors;
294333
struct stat sb;
295334
size_t chunk_size;
296-
xmlNode *root;
297-
xmlNode *node;
298-
xmlDoc *doc;
299335
void *buf;
300336
time_t t0;
301337
time_t t;
@@ -322,27 +358,10 @@ static int firehose_program(struct qdl_device *qdl, struct program *program, int
322358
if (!buf)
323359
err(1, "failed to allocate sector buffer");
324360

325-
doc = xmlNewDoc((xmlChar*)"1.0");
326-
root = xmlNewNode(NULL, (xmlChar*)"data");
327-
xmlDocSetRootElement(doc, root);
328-
329-
node = xmlNewChild(root, NULL, (xmlChar*)"program", NULL);
330-
xml_setpropf(node, "SECTOR_SIZE_IN_BYTES", "%d", program->sector_size);
331-
xml_setpropf(node, "num_partition_sectors", "%d", num_sectors);
332-
xml_setpropf(node, "physical_partition_number", "%d", program->partition);
333-
xml_setpropf(node, "start_sector", "%s", program->start_sector);
334-
if (program->filename)
335-
xml_setpropf(node, "filename", "%s", program->filename);
336-
337-
ret = firehose_write(qdl, doc);
338-
if (ret < 0) {
339-
fprintf(stderr, "[PROGRAM] failed to write program command\n");
340-
goto out;
341-
}
361+
ret = firehose_program_init(qdl, program->sector_size, num_sectors, program->partition,
362+
program->start_sector, program->filename);
342363

343-
ret = firehose_read(qdl, -1, firehose_nop_parser);
344364
if (ret) {
345-
fprintf(stderr, "[PROGRAM] failed to setup programming\n");
346365
goto out;
347366
}
348367

@@ -386,10 +405,178 @@ static int firehose_program(struct qdl_device *qdl, struct program *program, int
386405
}
387406

388407
out:
389-
xmlFreeDoc(doc);
408+
free(buf);
390409
return ret;
391410
}
392411

412+
struct Data {
413+
struct program* program;
414+
long unsigned int offset;
415+
struct qdl_device *qdl;
416+
void *data_blob;
417+
int data_blob_size;
418+
int data_blob_count;
419+
};
420+
421+
int firehose_program_sparse_do_flash(struct qdl_device *qdl, struct program* program, const void *data, long unsigned int num_sectors, long unsigned int start_sector_offset) {
422+
int ret;
423+
int chunk_size;
424+
int n;
425+
int left;
426+
int offset;
427+
long long start_sector;
428+
// 2^64 requires 20 chars at max (+ an additional one for '\0')
429+
char start_sector_str[21];
430+
431+
// Calculate new start sector
432+
start_sector = atoll(program->start_sector);
433+
start_sector += start_sector_offset;
434+
snprintf(start_sector_str, sizeof(start_sector_str), "%lld", start_sector);
435+
436+
ret = firehose_program_init(qdl, program->sector_size, num_sectors, program->partition,
437+
start_sector_str, program->filename);
438+
if (ret) {
439+
return ret;
440+
}
441+
442+
// Send given data to the target
443+
left = num_sectors;
444+
offset = 0;
445+
while (left > 0) {
446+
chunk_size = MIN(max_payload_size / program->sector_size, left);
447+
448+
n = qdl_write(qdl, data + offset, chunk_size * program->sector_size, true);
449+
if (n < 0)
450+
err(1, "failed to write");
451+
452+
if (n != chunk_size * program->sector_size)
453+
err(1, "failed to write full sector");
454+
455+
left -= chunk_size;
456+
offset += n;
457+
}
458+
459+
ret = firehose_read(qdl, -1, firehose_nop_parser);
460+
if (ret) {
461+
fprintf(stderr, "[PROGRAM] failed\n");
462+
}
463+
return ret;
464+
}
465+
466+
int firehose_program_sparse_callback(void *priv, const void *data, long unsigned int len) {
467+
int ret = 0;
468+
long unsigned int already_flashed = 0;
469+
struct Data *d = priv;
470+
unsigned num_sectors = 0;
471+
472+
if (data == NULL) {
473+
// Reached "Don't care" part --> Flash what is currently buffered
474+
if (d->data_blob) {
475+
num_sectors = (d->data_blob_count + d->program->sector_size - 1) / d->program->sector_size;
476+
477+
memset(d->data_blob + d->data_blob_count, 0, d->data_blob_size - d->data_blob_count);
478+
479+
ret = firehose_program_sparse_do_flash(d->qdl, d->program, d->data_blob, num_sectors, d->offset);
480+
if (ret) {
481+
goto out;
482+
}
483+
484+
d->data_blob_count = 0;
485+
}
486+
487+
d->offset += num_sectors;
488+
d->offset += (len + d->program->sector_size - 1) / d->program->sector_size;
489+
goto out;
490+
}
491+
else if ((d->data_blob_size - d->data_blob_count) < len) {
492+
// Reach part that contains data --> fill up buffer and flash if neccessary
493+
// Fill up current buffer and flash it
494+
already_flashed = d->data_blob_size - d->data_blob_count;
495+
memcpy(d->data_blob + d->data_blob_count, data, already_flashed);
496+
d->data_blob_count += already_flashed;
497+
ret = firehose_program_sparse_callback(priv, NULL, 0);
498+
499+
// Flash the rest of the given data
500+
if ((len - already_flashed) > d->data_blob_size) {
501+
num_sectors = ((len - already_flashed) / d->program->sector_size);
502+
503+
ret = firehose_program_sparse_do_flash(d->qdl, d->program, data + already_flashed, num_sectors, d->offset);
504+
if (ret) {
505+
goto out;
506+
}
507+
already_flashed += num_sectors * d->program->sector_size;
508+
d->data_blob_count = 0;
509+
d->offset += num_sectors;
510+
}
511+
}
512+
513+
memcpy(d->data_blob + d->data_blob_count, data + already_flashed, len - already_flashed);
514+
d->data_blob_count += len - already_flashed;
515+
516+
out:
517+
return ret;
518+
}
519+
520+
static int firehose_program_sparse(struct qdl_device *qdl, struct program *program, struct sparse_file *sparse)
521+
{
522+
unsigned num_sectors;
523+
time_t t0;
524+
time_t t;
525+
int ret;
526+
struct Data d;
527+
528+
num_sectors = program->num_sectors;
529+
t0 = time(NULL);
530+
531+
memset(&d, 0, sizeof(struct Data));
532+
d.qdl = qdl;
533+
d.program = program;
534+
d.data_blob_size = SPARSE_BUFFER_SIZE * max_payload_size;
535+
d.data_blob = malloc(d.data_blob_size);
536+
if (!d.data_blob) {
537+
return -1;
538+
}
539+
540+
// Process the sparse file
541+
ret = sparse_file_callback(sparse, false, false, firehose_program_sparse_callback, &d);
542+
if (!ret) {
543+
// Call once more (pretending a "Don't care" block) to flash current buffer
544+
ret = firehose_program_sparse_callback(&d, NULL, 0);
545+
}
546+
free(d.data_blob);
547+
548+
t = time(NULL) - t0;
549+
if (t) {
550+
fprintf(stderr,
551+
"[PROGRAM] flashed \"%s\" successfully at %ldkB/s\n",
552+
program->label,
553+
program->sector_size * num_sectors / t / 1024);
554+
} else {
555+
fprintf(stderr, "[PROGRAM] flashed \"%s\" successfully\n",
556+
program->label);
557+
}
558+
559+
return ret;
560+
}
561+
562+
static int firehose_program_maybe_sparse(struct qdl_device *qdl, struct program *program, int fd)
563+
{
564+
int ret;
565+
struct sparse_file *sparse;
566+
567+
if (!program->sparse) {
568+
return firehose_program(qdl, program, fd);
569+
} else {
570+
sparse = sparse_file_import(fd, false, false);
571+
if (!sparse) {
572+
return -1;
573+
}
574+
ret = firehose_program_sparse(qdl, program, sparse);
575+
sparse_file_destroy(sparse);
576+
return ret;
577+
}
578+
}
579+
393580
static int firehose_apply_patch(struct qdl_device *qdl, struct patch *patch)
394581
{
395582
xmlNode *root;
@@ -597,7 +784,7 @@ int firehose_run(struct qdl_device *qdl, const char *incdir, const char *storage
597784
if (ret)
598785
return ret;
599786

600-
ret = program_execute(qdl, firehose_program, incdir);
787+
ret = program_execute(qdl, firehose_program_maybe_sparse, incdir);
601788
if (ret)
602789
return ret;
603790

libsparse/Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
OUT := libsparse.a
2-
CXXFLAGS := -O2 -Wall -I include
2+
CXXFLAGS := -O2 -Wall -I include -std=c++11
33

44
SRCS := backed_block.cpp output_file.cpp sparse.cpp sparse_crc32.cpp sparse_err.cpp sparse_read.cpp stringprintf.cpp
55
OBJS := $(SRCS:.cpp=.o)

program.c

+7-2
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737

3838
#include "program.h"
3939
#include "qdl.h"
40-
40+
4141
static struct program *programes;
4242
static struct program *programes_last;
4343

@@ -76,6 +76,11 @@ int program_load(const char *program_file)
7676
program->num_sectors = attr_as_unsigned(node, "num_partition_sectors", &errors);
7777
program->partition = attr_as_unsigned(node, "physical_partition_number", &errors);
7878
program->start_sector = attr_as_string(node, "start_sector", &errors);
79+
int ignore_errors = 0;
80+
const char *sparse = attr_as_string(node, "sparse", &ignore_errors);
81+
if (sparse) {
82+
program->sparse = strcmp(sparse, "true") == 0;
83+
}
7984

8085
if (errors) {
8186
fprintf(stderr, "[PROGRAM] errors while parsing program\n");
@@ -96,7 +101,7 @@ int program_load(const char *program_file)
96101

97102
return 0;
98103
}
99-
104+
100105
int program_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, struct program *program, int fd),
101106
const char *incdir)
102107
{

program.h

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ struct program {
1212
unsigned num_sectors;
1313
unsigned partition;
1414
const char *start_sector;
15+
bool sparse;
1516

1617
struct program *next;
1718
};

0 commit comments

Comments
 (0)