| /* |
| * 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-media.h> |
| #include <mtd_swab.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 (be32_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_be32(data_size); |
| u->v->data_crc = cpu_to_be32(crc); |
| |
| if (action & BROKEN_DATA_CRC) { |
| u->v->data_crc = |
| cpu_to_be32(be32_to_cpu(u->v->data_crc) + 1); |
| } |
| if (action & BROKEN_DATA_SIZE) { |
| u->v->data_size = |
| cpu_to_be32(be32_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_be32(crc); |
| if (action & BROKEN_HDR_CRC) { |
| u->v->hdr_crc = cpu_to_be32(be32_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_be32(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 ubi_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", be32_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", |
| be32_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", |
| be32_to_cpu(u->v->data_pad)); |
| fprintf(stderr, "leb_total : %8d\n", u->leb_total); |
| fprintf(stderr, "header offs : 0x%08x\n", |
| be32_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_be32(UBI_VID_HDR_MAGIC); |
| res->v->version = version ? version : UBI_VERSION; |
| res->v->vol_type = vol_type; |
| res->v->vol_id = cpu_to_be32(vol_id); |
| res->v->compat = compat_flag; |
| res->v->data_pad = cpu_to_be32(res->data_pad); |
| |
| /* static only: used_ebs */ |
| if (res->v->vol_type == UBI_VID_STATIC) { |
| res->v->used_ebs = cpu_to_be32(byte_to_blk |
| (res->bytes_total, |
| res->leb_size)); |
| } |
| |
| /* ec hdr (fixed, doesn't change) */ |
| res->ec->magic = cpu_to_be32(UBI_EC_HDR_MAGIC); |
| res->ec->version = version ? version : UBI_VERSION; |
| res->ec->ec = cpu_to_be64(ec); |
| res->ec->vid_hdr_offset = cpu_to_be32(vid_hdr_offset); |
| |
| res->ec->data_offset = cpu_to_be32(data_offset); |
| |
| crc = clc_crc32(crc32_table, UBI_CRC32_INIT, res->ec, |
| UBI_EC_HDR_SIZE_CRC); |
| res->ec->hdr_crc = cpu_to_be32(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 + be32_to_cpu(res->ec->vid_hdr_offset); |
| res->ptr_data = res->buf + be32_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_vtbl_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_be32(byte_to_blk(reserved_bytes, u->leb_size)); |
| lvol_rec->alignment = cpu_to_be32(u->alignment); |
| lvol_rec->data_pad = u->v->data_pad; |
| lvol_rec->vol_type = u->v->vol_type; |
| |
| lvol_rec->name_len = |
| cpu_to_be16((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_be32(crc); |
| |
| return 0; |
| } |