[PATCH 5/5] ubinize: Add fastmap support

From: Richard Weinberger
Date: Mon May 14 2018 - 07:26:13 EST


Using the "-F" parameter, ubinize can now create ubinized images
with a preseeded fastmap on it.
You need a recent kernel with UBI_FM_SB_PRESEEDED_FLG support to
attach from such a fastmap.

Signed-off-by: Richard Weinberger <richard@xxxxxx>
---
include/libubigen.h | 17 +++-
lib/libubigen.c | 224 ++++++++++++++++++++++++++++++++++++++++++++++----
ubi-utils/ubiformat.c | 6 +-
ubi-utils/ubinize.c | 76 +++++++++++++----
4 files changed, 283 insertions(+), 40 deletions(-)

diff --git a/include/libubigen.h b/include/libubigen.h
index 6073a2d72e05..7b50d903300c 100644
--- a/include/libubigen.h
+++ b/include/libubigen.h
@@ -110,6 +110,10 @@ void ubigen_info_init(struct ubigen_info *ui, int peb_size, int min_io_size,
int subpage_size, int vid_hdr_offs, int ubi_ver,
uint32_t image_seq);

+int ubigen_fastmap_size(struct ubigen_info *ui,
+ struct ubigen_vol_info *vols[], int vol_count,
+ struct ubigen_vol_info *layout_vol);
+
/**
* ubigen_create_empty_vtbl - creates empty volume table.
* @ui: libubigen information
@@ -171,13 +175,18 @@ int ubigen_add_volume(const struct ubigen_info *ui,
* writes the UBI volume to the output file @out. Returns zero on success and
* %-1 on failure.
*/
-int ubigen_write_volume(const struct ubigen_info *ui,
+int ubigen_write_volume(struct ubigen_info *ui,
const struct ubigen_vol_info *vi, long long ec,
int in, int out);

+int ubigen_write_fastmap(struct ubigen_info *ui,
+ struct ubigen_vol_info *layout_vi,
+ int vol_count, struct ubigen_vol_info *vols[],
+ int ec, int out_fd);
/**
* ubigen_write_layout_vol - write UBI layout volume
* @ui: libubigen information
+ * @vi: volume information
* @peb1: physical eraseblock number to write the first volume table copy
* @peb2: physical eraseblock number to write the second volume table copy
* @ec1: erase counter value for @peb1
@@ -188,10 +197,12 @@ int ubigen_write_volume(const struct ubigen_info *ui,
* This function creates the UBI layout volume which contains 2 copies of the
* volume table. Returns zero in case of success and %-1 in case of failure.
*/
-int ubigen_write_layout_vol(const struct ubigen_info *ui, int peb1, int peb2,
- long long ec1, long long ec2,
+int ubigen_write_layout_vol(struct ubigen_info *ui, struct ubigen_vol_info *vi,
+ int peb1, int peb2, long long ec1, long long ec2,
struct ubi_vtbl_record *vtbl, int fd);

+struct ubigen_vol_info *ubigen_init_layout_vi(const struct ubigen_info *ui);
+
#ifdef __cplusplus
}
#endif
diff --git a/lib/libubigen.c b/lib/libubigen.c
index f509d4d072bd..05a27e639a49 100644
--- a/lib/libubigen.c
+++ b/lib/libubigen.c
@@ -30,13 +30,31 @@
#include <stdint.h>
#include <unistd.h>
#include <string.h>
+#include <sys/param.h> /* roundup() */

#include <mtd/ubi-media.h>
+#include <mtd/ubi-user.h>
#include <mtd_swab.h>
#include <libubigen.h>
#include <crc32.h>
#include "common.h"

+static size_t calc_fastmap_size(struct ubigen_info *ui, int peb_count)
+{
+ size_t size;
+
+ size = sizeof(struct ubi_fm_sb) +
+ sizeof(struct ubi_fm_hdr) +
+ sizeof(struct ubi_fm_scan_pool) +
+ sizeof(struct ubi_fm_scan_pool) +
+ (peb_count * sizeof(struct ubi_fm_ec)) +
+ (sizeof(struct ubi_fm_eba) +
+ (peb_count * sizeof(__be32))) +
+ sizeof(struct ubi_fm_volhdr) * UBI_MAX_VOLUMES;
+
+ return roundup(size, ui->leb_size);
+}
+
void ubigen_info_init(struct ubigen_info *ui, int peb_size, int min_io_size,
int subpage_size, int vid_hdr_offs, int ubi_ver,
uint32_t image_seq)
@@ -63,6 +81,19 @@ void ubigen_info_init(struct ubigen_info *ui, int peb_size, int min_io_size,
ui->vtbl_size = ui->max_volumes * UBI_VTBL_RECORD_SIZE;
}

+int ubigen_fastmap_size(struct ubigen_info *ui,
+ struct ubigen_vol_info *vols[], int vol_count,
+ struct ubigen_vol_info *layout_vol)
+{
+ int i;
+ int vol_pebs = layout_vol->used_ebs;
+
+ for (i = 0; i < vol_count; i++)
+ vol_pebs += vols[i]->used_ebs;
+
+ return calc_fastmap_size(ui, vol_pebs + UBI_FM_MAX_BLOCKS);
+}
+
struct ubi_vtbl_record *ubigen_create_empty_vtbl(const struct ubigen_info *ui)
{
struct ubi_vtbl_record *vtbl;
@@ -168,11 +199,31 @@ void ubigen_init_vid_hdr(const struct ubigen_info *ui,
hdr->hdr_crc = cpu_to_be32(crc);
}

-int ubigen_write_volume(const struct ubigen_info *ui,
+static void ubigen_init_fm_vid_hdr(const struct ubigen_info *ui,
+ struct ubi_vid_hdr *hdr, int vol_id,
+ int lnum)
+{
+ uint32_t crc;
+
+ memset(hdr, 0, sizeof(struct ubi_vid_hdr));
+
+ hdr->magic = cpu_to_be32(UBI_VID_HDR_MAGIC);
+ hdr->version = ui->ubi_ver;
+ hdr->vol_type = UBI_VID_DYNAMIC;
+ hdr->vol_id = cpu_to_be32(vol_id);
+ hdr->lnum = cpu_to_be32(lnum);
+ hdr->compat = UBI_COMPAT_DELETE;
+ hdr->sqnum = cpu_to_be64(1);
+
+ crc = mtd_crc32(UBI_CRC32_INIT, hdr, UBI_VID_HDR_SIZE_CRC);
+ hdr->hdr_crc = cpu_to_be32(crc);
+}
+
+int ubigen_write_volume(struct ubigen_info *ui,
const struct ubigen_vol_info *vi, long long ec,
int in, int out)
{
- int len = vi->usable_leb_size, rd, lnum = 0;
+ int len = vi->usable_leb_size, rd, lnum;
long long bytes = vi->image_file_len;
char *inbuf, *outbuf;

@@ -259,27 +310,144 @@ out_free:
return -1;
}

-int ubigen_write_layout_vol(const struct ubigen_info *ui, int peb1, int peb2,
- long long ec1, long long ec2,
+int ubigen_write_fastmap(struct ubigen_info *ui,
+ struct ubigen_vol_info *layout_vi,
+ int vol_count, struct ubigen_vol_info *vols[],
+ int ec, int out_fd)
+{
+ int i, j, ret;
+ struct ubi_fm_sb *fmsb;
+ struct ubi_fm_hdr *fmh;
+ struct ubi_fm_scan_pool *fmpl, *fmpl_wl;
+ struct ubi_fm_ec *fec;
+ struct ubi_fm_volhdr *fvh;
+ struct ubi_fm_eba *feba;
+ struct ubigen_vol_info *vi;
+ size_t fm_pos = 0;
+ size_t fm_size = ubigen_fastmap_size(ui, vols, vol_count, layout_vi);
+ char *fm_raw = xcalloc(1, fm_size);
+ char *peb_buf = xmalloc(ui->peb_size);
+ int fm_pebs = fm_size / ui->leb_size;
+ int pebs_used = 0;
+
+
+ fmsb = (struct ubi_fm_sb *)fm_raw;
+ fm_pos += sizeof(*fmsb);
+
+ fmsb->magic = cpu_to_be32(UBI_FM_SB_MAGIC);
+ fmsb->version = 2;
+ fmsb->used_blocks = cpu_to_be32(fm_pebs);
+ fmsb->sqnum = 0;
+ fmsb->data_crc = 0;
+ fmsb->flags = cpu_to_be32(UBI_FM_SB_PRESEEDED_FLG);
+
+ for (i = 0; i < fm_pebs; i++) {
+ fmsb->block_loc[i] = cpu_to_be32(layout_vi->used_ebs + i);
+ fmsb->block_ec[i] = cpu_to_be32(ec);
+ }
+
+ fmh = (struct ubi_fm_hdr *)(fm_raw + fm_pos);
+ fm_pos += sizeof(*fmh);
+
+ fmh->magic = cpu_to_be32(UBI_FM_HDR_MAGIC);
+ fmh->vol_count = cpu_to_be32(vol_count + 1);
+ fmh->free_peb_count = 0;
+ fmh->scrub_peb_count = 0;
+ fmh->erase_peb_count = 0;
+ fmh->bad_peb_count = 0;
+
+ fmpl = (struct ubi_fm_scan_pool *)(fm_raw + fm_pos);
+ fm_pos += sizeof(*fmpl);
+
+ fmpl->magic = cpu_to_be32(UBI_FM_POOL_MAGIC);
+ fmpl->size = 0;
+ fmpl->max_size = 0;
+
+ fmpl_wl = (struct ubi_fm_scan_pool *)(fm_raw + fm_pos);
+ fm_pos += sizeof(*fmpl_wl);
+
+ fmpl_wl->magic = cpu_to_be32(UBI_FM_POOL_MAGIC);
+ fmpl_wl->size = 0;
+ fmpl_wl->max_size = 0;
+
+ for (i = 0; i < vol_count + 1; i++) {
+ if (i == vol_count)
+ vi = layout_vi;
+ else
+ vi = vols[i];
+
+ for (j = 0; j < vi->used_ebs; j++) {
+ fec = (struct ubi_fm_ec *)(fm_raw + fm_pos);
+
+ if (vi->eba[j] > -1) {
+ fec->pnum = cpu_to_be32(vi->eba[j]);
+ fec->ec = cpu_to_be32(ec);
+ pebs_used++;
+ fm_pos += sizeof(*fec);
+ }
+ }
+ }
+
+ fmh->used_peb_count = cpu_to_be32(pebs_used);
+
+ for (i = 0; i < vol_count + 1; i++) {
+ if (i == vol_count)
+ vi = layout_vi;
+ else
+ vi = vols[i];
+
+ fvh = (struct ubi_fm_volhdr *)(fm_raw + fm_pos);
+ fm_pos += sizeof(*fvh);
+
+ fvh->magic = cpu_to_be32(UBI_FM_VHDR_MAGIC);
+ fvh->vol_id = cpu_to_be32(vi->id);
+ fvh->vol_type = vi->type == UBI_VID_DYNAMIC ? UBI_DYNAMIC_VOLUME : UBI_STATIC_VOLUME;
+ fvh->used_ebs = cpu_to_be32(vi->used_ebs);
+ fvh->data_pad = cpu_to_be32(vi->data_pad);
+ fvh->last_eb_bytes = cpu_to_be32(vi->bytes % vi->usable_leb_size);
+
+ feba = (struct ubi_fm_eba *)(fm_raw + fm_pos);
+ fm_pos += sizeof(*feba) + (sizeof(__be32) * vi->used_ebs);
+
+ feba->magic = cpu_to_be32(UBI_FM_EBA_MAGIC);
+ feba->reserved_pebs = cpu_to_be32(vi->used_ebs);
+ for (j = 0; j < vi->used_ebs; j++)
+ feba->pnum[j] = cpu_to_be32(vi->eba[j]);
+ }
+
+ fmsb->data_crc = cpu_to_be32(mtd_crc32(UBI_CRC32_INIT, fm_raw, fm_size));
+
+ for (i = 0; i < fm_pebs; i++) {
+ int vol_id = i == 0 ? UBI_FM_SB_VOLUME_ID : UBI_FM_DATA_VOLUME_ID;
+
+ memset(peb_buf, 0xff, ui->peb_size);
+
+ ubigen_init_ec_hdr(ui, (struct ubi_ec_hdr *)peb_buf, ec);
+ ubigen_init_fm_vid_hdr(ui, (struct ubi_vid_hdr *)(peb_buf + ui->vid_hdr_offs), vol_id, i);
+ memcpy(peb_buf + ui->data_offs, fm_raw + ui->leb_size * i, ui->leb_size);
+ ret = write(out_fd, peb_buf, ui->peb_size);
+ if (ret != ui->peb_size) {
+ sys_errmsg("cannot write %d bytes", ui->peb_size);
+ goto out;
+ }
+ }
+
+ ret = 0;
+out:
+ free(fm_raw);
+ free(peb_buf);
+ return ret;
+}
+
+int ubigen_write_layout_vol(struct ubigen_info *ui, struct ubigen_vol_info *vi,
+ int peb1, int peb2, long long ec1, long long ec2,
struct ubi_vtbl_record *vtbl, int fd)
{
int ret;
- struct ubigen_vol_info vi;
char *outbuf;
struct ubi_vid_hdr *vid_hdr;
off_t seek;

- vi.bytes = ui->leb_size * UBI_LAYOUT_VOLUME_EBS;
- vi.id = UBI_LAYOUT_VOLUME_ID;
- vi.alignment = UBI_LAYOUT_VOLUME_ALIGN;
- vi.data_pad = ui->leb_size % UBI_LAYOUT_VOLUME_ALIGN;
- vi.usable_leb_size = ui->leb_size - vi.data_pad;
- vi.data_pad = ui->leb_size - vi.usable_leb_size;
- vi.type = UBI_LAYOUT_VOLUME_TYPE;
- vi.name = UBI_LAYOUT_VOLUME_NAME;
- vi.name_len = strlen(UBI_LAYOUT_VOLUME_NAME);
- vi.compat = UBI_LAYOUT_VOLUME_COMPAT;
-
outbuf = malloc(ui->peb_size);
if (!outbuf)
return sys_errmsg("failed to allocate %d bytes",
@@ -299,7 +467,7 @@ int ubigen_write_layout_vol(const struct ubigen_info *ui, int peb1, int peb2,
vi->eba[0] = peb1;

ubigen_init_ec_hdr(ui, (struct ubi_ec_hdr *)outbuf, ec1);
- ubigen_init_vid_hdr(ui, &vi, vid_hdr, 0, NULL, 0);
+ ubigen_init_vid_hdr(ui, vi, vid_hdr, 0, NULL, 0);
ret = write(fd, outbuf, ui->peb_size);
if (ret != ui->peb_size) {
sys_errmsg("cannot write %d bytes", ui->peb_size);
@@ -314,7 +482,7 @@ int ubigen_write_layout_vol(const struct ubigen_info *ui, int peb1, int peb2,
vi->eba[1] = peb2;

ubigen_init_ec_hdr(ui, (struct ubi_ec_hdr *)outbuf, ec2);
- ubigen_init_vid_hdr(ui, &vi, vid_hdr, 1, NULL, 0);
+ ubigen_init_vid_hdr(ui, vi, vid_hdr, 1, NULL, 0);
ret = write(fd, outbuf, ui->peb_size);
if (ret != ui->peb_size) {
sys_errmsg("cannot write %d bytes", ui->peb_size);
@@ -328,3 +496,23 @@ out_free:
free(outbuf);
return -1;
}
+
+struct ubigen_vol_info *ubigen_init_layout_vi(const struct ubigen_info *ui)
+{
+ struct ubigen_vol_info *vi = xmalloc(sizeof(*vi));
+
+ vi->bytes = ui->leb_size * UBI_LAYOUT_VOLUME_EBS;
+ vi->id = UBI_LAYOUT_VOLUME_ID;
+ vi->alignment = UBI_LAYOUT_VOLUME_ALIGN;
+ vi->data_pad = ui->leb_size % UBI_LAYOUT_VOLUME_ALIGN;
+ vi->usable_leb_size = ui->leb_size - vi->data_pad;
+ vi->data_pad = ui->leb_size - vi->usable_leb_size;
+ vi->type = UBI_LAYOUT_VOLUME_TYPE;
+ vi->name = UBI_LAYOUT_VOLUME_NAME;
+ vi->name_len = strlen(UBI_LAYOUT_VOLUME_NAME);
+ vi->compat = UBI_LAYOUT_VOLUME_COMPAT;
+ vi->used_ebs = 2;
+ vi->eba = xcalloc(vi->used_ebs, sizeof(int));
+
+ return vi;
+}
diff --git a/ubi-utils/ubiformat.c b/ubi-utils/ubiformat.c
index c38b9b4abb16..a333246ecf77 100644
--- a/ubi-utils/ubiformat.c
+++ b/ubi-utils/ubiformat.c
@@ -553,7 +553,7 @@ out_close:
}

static int format(libmtd_t libmtd, const struct mtd_dev_info *mtd,
- const struct ubigen_info *ui, struct ubi_scan_info *si,
+ struct ubigen_info *ui, struct ubi_scan_info *si,
int start_eb, int novtbl)
{
int eb, err, write_size;
@@ -655,6 +655,8 @@ static int format(libmtd_t libmtd, const struct mtd_dev_info *mtd,
printf("\n");

if (!novtbl) {
+ struct ubigen_vol_info *layout_vi = ubigen_init_layout_vi(ui);
+
if (eb1 == -1 || eb2 == -1) {
errmsg("no eraseblocks for volume table");
goto out_free;
@@ -665,7 +667,7 @@ static int format(libmtd_t libmtd, const struct mtd_dev_info *mtd,
if (!vtbl)
goto out_free;

- err = ubigen_write_layout_vol(ui, eb1, eb2, ec1, ec2, vtbl,
+ err = ubigen_write_layout_vol(ui, layout_vi, eb1, eb2, ec1, ec2, vtbl,
args.node_fd);
free(vtbl);
if (err) {
diff --git a/ubi-utils/ubinize.c b/ubi-utils/ubinize.c
index 62ddd7b85296..08048eceb33a 100644
--- a/ubi-utils/ubinize.c
+++ b/ubi-utils/ubinize.c
@@ -82,6 +82,7 @@ static const struct option long_options[] = {
{ .name = "verbose", .has_arg = 0, .flag = NULL, .val = 'v' },
{ .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' },
{ .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' },
+ { .name = "fastmap", .has_arg = 0, .flag = NULL, .val = 'F' },
{ NULL, 0, NULL, 0}
};

@@ -97,6 +98,7 @@ struct args {
int ubi_ver;
uint32_t image_seq;
int verbose;
+ int fastmap;
dictionary *dict;
};

@@ -116,7 +118,7 @@ static int parse_opt(int argc, char * const argv[])
int key, error = 0;
unsigned long int image_seq;

- key = getopt_long(argc, argv, "o:p:m:s:O:e:x:Q:vhV", long_options, NULL);
+ key = getopt_long(argc, argv, "o:p:m:s:O:e:x:Q:vhVF", long_options, NULL);
if (key == -1)
break;

@@ -180,6 +182,10 @@ static int parse_opt(int argc, char * const argv[])
args.verbose = 1;
break;

+ case 'F':
+ args.fastmap = 1;
+ break;
+
case 'h':
fputs(usage, stdout);
fputs(optionsstr, stdout);
@@ -408,6 +414,7 @@ int main(int argc, char * const argv[])
struct ubigen_info ui;
struct ubi_vtbl_record *vtbl;
struct ubigen_vol_info *vi;
+ struct ubigen_vol_info *layout_vi;
off_t seek;

err = parse_opt(argc, argv);
@@ -465,20 +472,11 @@ int main(int argc, char * const argv[])
goto out_dict;
}

- /*
- * Skip 2 PEBs at the beginning of the file for the volume table which
- * will be written later.
- */
- seek = ui.peb_size * 2;
- if (lseek(args.out_fd, seek, SEEK_SET) != seek) {
- err = -1;
- sys_errmsg("cannot seek file \"%s\"", args.f_out);
- goto out_free;
- }
+ layout_vi = ubigen_init_layout_vi(&ui);

for (i = 0; i < sects; i++) {
const char *sname = iniparser_getsecname(args.dict, i);
- int fd, j;
+ int j;

if (!sname) {
err = -1;
@@ -531,8 +529,33 @@ int main(int argc, char * const argv[])
goto out_free;
}

+ if (args.verbose)
+ printf("\n");
+ }
+
+ /*
+ * Skip 2 PEBs at the beginning of the file for the volume table which
+ * will be written later.
+ */
+ seek = 2;
+
+ /*
+ * If Fastmap is enabled we need also some PEBs for it at the beginning.
+ */
+ if (args.fastmap)
+ seek += ubigen_fastmap_size(&ui, &vi, sects, layout_vi) / ui.leb_size;
+
+ seek *= ui.peb_size;
+
+ if (lseek(args.out_fd, seek, SEEK_SET) != seek) {
+ err = -1;
+ sys_errmsg("cannot seek file \"%s\"", args.f_out);
+ goto out_free;
+ }
+
+ for (i = 0; i < sects; i++) {
if (vi[i].image_file) {
- fd = open(vi[i].image_file, O_RDONLY);
+ int fd = open(vi[i].image_file, O_RDONLY);
if (fd == -1) {
err = fd;
sys_errmsg("cannot open \"%s\"", vi[i].image_file);
@@ -545,27 +568,44 @@ int main(int argc, char * const argv[])
err = ubigen_write_volume(&ui, &vi[i], args.ec, fd, args.out_fd);
close(fd);
if (err) {
- errmsg("cannot write volume for section \"%s\"", sname);
+ errmsg("cannot write volume \"%s\"", vi[i].name);
goto out_free;
}
}

- if (args.verbose)
- printf("\n");
}

verbose(args.verbose, "writing layout volume");

- err = ubigen_write_layout_vol(&ui, 0, 1, args.ec, args.ec, vtbl, args.out_fd);
+ err = ubigen_write_layout_vol(&ui, layout_vi, 0, 1, args.ec, args.ec, vtbl, args.out_fd);
if (err) {
errmsg("cannot write layout volume");
goto out_free;
}

+ if (args.fastmap) {
+ verbose(args.verbose, "writing fastmap");
+
+ if (lseek(args.out_fd, 2 * ui.peb_size, SEEK_SET) != 2 * ui.peb_size) {
+ err = -1;
+ sys_errmsg("cannot seek file \"%s\"", args.f_out);
+ goto out_free;
+ }
+
+ err = ubigen_write_fastmap(&ui, layout_vi, sects, &vi, args.ec, args.out_fd);
+ if (err) {
+ errmsg("cannot write fastmap");
+ goto out_free;
+ }
+ }
+
+
verbose(args.verbose, "done");

free(vi->eba);
free(vi);
+ free(layout_vi->eba);
+ free(layout_vi);
iniparser_freedict(args.dict);
free(vtbl);
close(args.out_fd);
@@ -573,6 +613,8 @@ int main(int argc, char * const argv[])

out_free:
free(vi);
+ free(layout_vi);
+ free(layout_vi->eba);
out_dict:
iniparser_freedict(args.dict);
out_vtbl:
--
2.13.6