/* * 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; }