summaryrefslogtreecommitdiff
path: root/ubi-utils/src/libubigen.c
diff options
context:
space:
mode:
Diffstat (limited to 'ubi-utils/src/libubigen.c')
-rw-r--r--ubi-utils/src/libubigen.c486
1 files changed, 486 insertions, 0 deletions
diff --git a/ubi-utils/src/libubigen.c b/ubi-utils/src/libubigen.c
new file mode 100644
index 0000000..258e555
--- /dev/null
+++ b/ubi-utils/src/libubigen.c
@@ -0,0 +1,486 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Oliver Lohmann
+ *
+ * Add UBI headers to binary data.
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <mtd/ubi-header.h>
+
+#include "config.h"
+#include "ubigen.h"
+#include "crc32.h"
+
+#define UBI_NAME_SIZE 256
+#define DEFAULT_VID_OFFSET ((DEFAULT_PAGESIZE) - (UBI_VID_HDR_SIZE))
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+
+static uint32_t crc32_table[256];
+
+struct ubi_info {
+ struct ubi_vid_hdr* v; /* Volume ID header */
+ struct ubi_ec_hdr* ec; /* Erase count header */
+
+ FILE* fp_in; /* Input Stream */
+ FILE* fp_out; /* Output stream */
+
+ size_t eb_size; /* Physical EB size in bytes */
+ size_t leb_size; /* Size of a logical EB in a physical EB */
+ size_t leb_total; /* Total input size in logical EB */
+ size_t alignment; /* Block alignment */
+ size_t data_pad; /* Size of padding in each physical EB */
+
+ size_t bytes_total; /* Total input size in bytes */
+ size_t bytes_read; /* Nymber of read bytes (total) */
+
+ uint32_t blks_written; /* Number of written logical EB */
+
+ uint8_t* buf; /* Allocated buffer */
+ uint8_t* ptr_ec_hdr; /* Pointer to EC hdr in buf */
+ uint8_t* ptr_vid_hdr; /* Pointer to VID hdr in buf */
+ uint8_t* ptr_data; /* Pointer to data region in buf */
+};
+
+
+static uint32_t
+byte_to_blk(uint64_t byte, uint32_t eb_size)
+{
+ return (byte % eb_size) == 0
+ ? (byte / eb_size)
+ : (byte / eb_size) + 1;
+}
+
+static int
+validate_ubi_info(ubi_info_t u)
+{
+ if ((u->v->vol_type != UBI_VID_DYNAMIC) &&
+ (u->v->vol_type != UBI_VID_STATIC)) {
+ return EUBIGEN_INVALID_TYPE;
+ }
+
+ if (ubi32_to_cpu(u->ec->vid_hdr_offset) < UBI_VID_HDR_SIZE) {
+ return EUBIGEN_INVALID_HDR_OFFSET;
+ }
+
+ return 0;
+}
+
+static int
+skip_blks(ubi_info_t u, uint32_t blks)
+{
+ uint32_t i;
+ size_t read = 0, to_read = 0;
+
+ /* Step to a maximum of leb_total - 1 to keep the
+ restrictions. */
+ for (i = 0; i < MIN(blks, u->leb_total-1); i++) {
+ /* Read in data */
+ to_read = MIN(u->leb_size,
+ (u->bytes_total - u->bytes_read));
+ read = fread(u->ptr_data, 1, to_read, u->fp_in);
+ if (read != to_read) {
+ return -EIO;
+ }
+ u->bytes_read += read;
+ u->blks_written++;
+ }
+
+ return 0;
+}
+
+static void
+clear_buf(ubi_info_t u)
+{
+ memset(u->buf, 0xff, u->eb_size);
+}
+
+static void
+write_ec_hdr(ubi_info_t u)
+{
+ memcpy(u->ptr_ec_hdr, u->ec, UBI_EC_HDR_SIZE);
+}
+
+static int
+fill_data_buffer_from_file(ubi_info_t u, size_t* read)
+{
+ size_t to_read = 0;
+
+ if (u-> fp_in == NULL)
+ return -EIO;
+
+ to_read = MIN(u->leb_size, (u->bytes_total - u->bytes_read));
+ *read = fread(u->ptr_data, 1, to_read, u->fp_in);
+ if (*read != to_read) {
+ return -EIO;
+ }
+ return 0;
+}
+
+static void
+add_static_info(ubi_info_t u, size_t data_size, ubigen_action_t action)
+{
+ uint32_t crc = clc_crc32(crc32_table, UBI_CRC32_INIT,
+ u->ptr_data, data_size);
+
+ u->v->data_size = cpu_to_ubi32(data_size);
+ u->v->data_crc = cpu_to_ubi32(crc);
+
+ if (action & BROKEN_DATA_CRC) {
+ u->v->data_crc =
+ cpu_to_ubi32(ubi32_to_cpu(u->v->data_crc) + 1);
+ }
+ if (action & BROKEN_DATA_SIZE) {
+ u->v->data_size =
+ cpu_to_ubi32(ubi32_to_cpu(u->v->data_size) + 1);
+ }
+}
+
+static void
+write_vid_hdr(ubi_info_t u, ubigen_action_t action)
+{
+ uint32_t crc = clc_crc32(crc32_table, UBI_CRC32_INIT,
+ u->v, UBI_VID_HDR_SIZE_CRC);
+ /* Write VID header */
+ u->v->hdr_crc = cpu_to_ubi32(crc);
+ if (action & BROKEN_HDR_CRC) {
+ u->v->hdr_crc = cpu_to_ubi32(ubi32_to_cpu(u->v->hdr_crc) + 1);
+ }
+ memcpy(u->ptr_vid_hdr, u->v, UBI_VID_HDR_SIZE);
+}
+
+static int
+write_to_output_stream(ubi_info_t u)
+{
+ size_t written;
+
+ written = fwrite(u->buf, 1, u->eb_size, u->fp_out);
+ if (written != u->eb_size) {
+ return -EIO;
+ }
+ return 0;
+}
+
+int
+ubigen_write_leb(ubi_info_t u, ubigen_action_t action)
+{
+ int rc = 0;
+ size_t read = 0;
+
+ clear_buf(u);
+ write_ec_hdr(u);
+
+ rc = fill_data_buffer_from_file(u, &read);
+ if (rc != 0)
+ return rc;
+
+ if (u->v->vol_type == UBI_VID_STATIC) {
+ add_static_info(u, read, action);
+ }
+
+ u->v->lnum = cpu_to_ubi32(u->blks_written);
+
+ if (action & MARK_AS_UPDATE) {
+ u->v->copy_flag = (u->v->copy_flag)++;
+ }
+
+ write_vid_hdr(u, action);
+ rc = write_to_output_stream(u);
+ if (rc != 0)
+ return rc;
+
+ /* Update current handle */
+ u->bytes_read += read;
+ u->blks_written++;
+ return 0;
+}
+
+int
+ubigen_write_complete(ubi_info_t u)
+{
+ size_t i;
+ int rc = 0;
+
+ for (i = 0; i < u->leb_total; i++) {
+ rc = ubigen_write_leb(u, NO_ERROR);
+ if (rc != 0)
+ return rc;
+ }
+
+ return 0;
+}
+
+int
+ubigen_write_broken_update(ubi_info_t u, uint32_t blk)
+{
+ int rc = 0;
+
+ rc = skip_blks(u, blk);
+ if (rc != 0)
+ return rc;
+
+ rc = ubigen_write_leb(u, MARK_AS_UPDATE | BROKEN_DATA_CRC);
+ if (rc != 0)
+ return rc;
+
+
+ return 0;
+}
+
+void
+dump_info(ubi_info_t u __unused)
+{
+#ifdef DEBUG
+ int err = 0;
+ if (!u) {
+ fprintf(stderr, "<empty>");
+ return;
+ }
+ if (!u->ec) {
+ fprintf(stderr, "<ec-empty>");
+ err = 1;
+ }
+ if (!u->v) {
+ fprintf(stderr, "<v-empty>");
+ err = 1;
+ }
+ if (err) return;
+
+ fprintf(stderr, "ubi volume\n");
+ fprintf(stderr, "version : %8d\n", u->v->version);
+ fprintf(stderr, "vol_id : %8d\n", ubi32_to_cpu(u->v->vol_id));
+ fprintf(stderr, "vol_type : %8s\n",
+ u->v->vol_type == UBI_VID_STATIC ?
+ "static" : "dynamic");
+ fprintf(stderr, "used_ebs : %8d\n",
+ ubi32_to_cpu(u->v->used_ebs));
+ fprintf(stderr, "eb_size : 0x%08x\n", u->eb_size);
+ fprintf(stderr, "leb_size : 0x%08x\n", u->leb_size);
+ fprintf(stderr, "data_pad : 0x%08x\n",
+ ubi32_to_cpu(u->v->data_pad));
+ fprintf(stderr, "leb_total : %8d\n", u->leb_total);
+ fprintf(stderr, "header offs : 0x%08x\n",
+ ubi32_to_cpu(u->ec->vid_hdr_offset));
+ fprintf(stderr, "bytes_total : %8d\n", u->bytes_total);
+ fprintf(stderr, " + in MiB : %8.2f M\n",
+ ((float)(u->bytes_total)) / 1024 / 1024);
+ fprintf(stderr, "-------------------------------\n\n");
+#else
+ return;
+#endif
+}
+
+int
+ubigen_destroy(ubi_info_t *u)
+{
+ if (u == NULL)
+ return -EINVAL;
+
+ ubi_info_t tmp = *u;
+
+ if (tmp) {
+ if (tmp->v)
+ free(tmp->v);
+ if (tmp->ec)
+ free(tmp->ec);
+ if (tmp->buf)
+ free(tmp->buf);
+ free(tmp);
+ }
+ *u = NULL;
+ return 0;
+}
+
+void
+ubigen_init(void)
+{
+ init_crc32_table(crc32_table);
+}
+
+int
+ubigen_create(ubi_info_t* u, uint32_t vol_id, uint8_t vol_type,
+ uint32_t eb_size, uint64_t ec, uint32_t alignment,
+ uint8_t version, uint32_t vid_hdr_offset, uint8_t compat_flag,
+ size_t data_size, FILE* fp_in, FILE* fp_out)
+{
+ int rc = 0;
+ ubi_info_t res = NULL;
+ uint32_t crc;
+ uint32_t data_offset;
+
+ if (alignment == 0) {
+ rc = EUBIGEN_INVALID_ALIGNMENT;
+ goto ubigen_create_err;
+ }
+ if ((fp_in == NULL) || (fp_out == NULL)) {
+ rc = -EINVAL;
+ goto ubigen_create_err;
+ }
+
+ res = (ubi_info_t) calloc(1, sizeof(struct ubi_info));
+ if (res == NULL) {
+ rc = -ENOMEM;
+ goto ubigen_create_err;
+ }
+
+ res->v = (struct ubi_vid_hdr*) calloc(1, sizeof(struct ubi_vid_hdr));
+ if (res->v == NULL) {
+ rc = -ENOMEM;
+ goto ubigen_create_err;
+ }
+
+ res->ec = (struct ubi_ec_hdr*) calloc(1, sizeof(struct ubi_ec_hdr));
+ if (res->ec == NULL) {
+ rc = -ENOMEM;
+ goto ubigen_create_err;
+ }
+
+ /* data which is needed in the general process */
+ vid_hdr_offset = vid_hdr_offset ? vid_hdr_offset : DEFAULT_VID_OFFSET;
+ data_offset = vid_hdr_offset + UBI_VID_HDR_SIZE;
+ res->bytes_total = data_size;
+ res->eb_size = eb_size ? eb_size : DEFAULT_BLOCKSIZE;
+ res->data_pad = (res->eb_size - data_offset) % alignment;
+ res->leb_size = res->eb_size - data_offset - res->data_pad;
+ res->leb_total = byte_to_blk(data_size, res->leb_size);
+ res->alignment = alignment;
+
+ if ((res->eb_size < (vid_hdr_offset + UBI_VID_HDR_SIZE))) {
+ rc = EUBIGEN_TOO_SMALL_EB;
+ goto ubigen_create_err;
+ }
+ res->fp_in = fp_in;
+ res->fp_out = fp_out;
+
+ /* vid hdr data which doesn't change */
+ res->v->magic = cpu_to_ubi32(UBI_VID_HDR_MAGIC);
+ res->v->version = version ? version : UBI_VERSION;
+ res->v->vol_type = vol_type;
+ res->v->vol_id = cpu_to_ubi32(vol_id);
+ res->v->compat = compat_flag;
+ res->v->data_pad = cpu_to_ubi32(res->data_pad);
+
+ /* static only: used_ebs */
+ if (res->v->vol_type == UBI_VID_STATIC) {
+ res->v->used_ebs = cpu_to_ubi32(byte_to_blk
+ (res->bytes_total,
+ res->leb_size));
+ }
+
+ /* ec hdr (fixed, doesn't change) */
+ res->ec->magic = cpu_to_ubi32(UBI_EC_HDR_MAGIC);
+ res->ec->version = version ? version : UBI_VERSION;
+ res->ec->ec = cpu_to_ubi64(ec);
+ res->ec->vid_hdr_offset = cpu_to_ubi32(vid_hdr_offset);
+
+ res->ec->data_offset = cpu_to_ubi32(data_offset);
+
+ crc = clc_crc32(crc32_table, UBI_CRC32_INIT, res->ec,
+ UBI_EC_HDR_SIZE_CRC);
+ res->ec->hdr_crc = cpu_to_ubi32(crc);
+
+ /* prepare a read buffer */
+ res->buf = (uint8_t*) malloc (res->eb_size * sizeof(uint8_t));
+ if (res->buf == NULL) {
+ rc = -ENOMEM;
+ goto ubigen_create_err;
+ }
+
+ /* point to distinct regions within the buffer */
+ res->ptr_ec_hdr = res->buf;
+ res->ptr_vid_hdr = res->buf + ubi32_to_cpu(res->ec->vid_hdr_offset);
+ res->ptr_data = res->buf + ubi32_to_cpu(res->ec->vid_hdr_offset)
+ + UBI_VID_HDR_SIZE;
+
+ rc = validate_ubi_info(res);
+ if (rc != 0) {
+ fprintf(stderr, "Volume validation failed: %d\n", rc);
+ goto ubigen_create_err;
+ }
+
+ dump_info(res);
+ *u = res;
+ return rc;
+
+ ubigen_create_err:
+ if (res) {
+ if (res->v)
+ free(res->v);
+ if (res->ec)
+ free(res->ec);
+ if (res->buf)
+ free(res->buf);
+ free(res);
+ }
+ *u = NULL;
+ return rc;
+}
+
+int
+ubigen_get_leb_size(ubi_info_t u, size_t* size)
+{
+ if (u == NULL)
+ return -EINVAL;
+
+ *size = u->leb_size;
+ return 0;
+}
+
+
+int
+ubigen_get_leb_total(ubi_info_t u, size_t* total)
+{
+ if (u == NULL)
+ return -EINVAL;
+
+ *total = u->leb_total;
+ return 0;
+}
+
+int
+ubigen_set_lvol_rec(ubi_info_t u, size_t reserved_bytes,
+ const char* vol_name, struct ubi_vol_tbl_record *lvol_rec)
+{
+ uint32_t crc;
+
+ if ((u == NULL) || (vol_name == NULL))
+ return -EINVAL;
+
+ memset(lvol_rec, 0x0, UBI_VTBL_RECORD_SIZE);
+
+ lvol_rec->reserved_pebs =
+ cpu_to_ubi32(byte_to_blk(reserved_bytes, u->leb_size));
+ lvol_rec->alignment = cpu_to_ubi32(u->alignment);
+ lvol_rec->data_pad = u->v->data_pad;
+ lvol_rec->vol_type = u->v->vol_type;
+
+ lvol_rec->name_len =
+ cpu_to_ubi16((uint16_t)strlen((const char*)vol_name));
+
+ memcpy(lvol_rec->name, vol_name, UBI_VOL_NAME_MAX + 1);
+
+ crc = clc_crc32(crc32_table, UBI_CRC32_INIT,
+ lvol_rec, UBI_VTBL_RECORD_SIZE_CRC);
+ lvol_rec->crc = cpu_to_ubi32(crc);
+
+ return 0;
+}