Skip to content
Snippets Groups Projects
nand-image-builder.c 11.1 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*
     *  Nand-image-builder for broadcom chipset.
     *
     * Copyright (C) 2019 iopsys Software Solutions AB. All rights reserved.
     *
     * This program is free software; you can redistribute it and/or
     * modify it under the terms of the GNU General Public License
     * version 2 as published by the Free Software Foundation.
     *
     * 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., 51 Franklin St, Fifth Floor, Boston, MA
     * 02110-1301 USA
    
     *
     * This code is referenced from https://github.com/ak-hard/brcm-nand-bch/.
     *
    
     */
    
    // BCH 4 algorithm is used for ECC level. In schematic bootstrap sets to 4 bit ECC level for NAND.
    #define BCH_4 4
    
    //Field order for above 7.0 broadcom NAND Controller is 14, older is 13
    #define FIELD_ORDER 14
    #define MIN_FIELD 13
    #define MAX_FIELD 14
    #define SECTORS_PER_PAGE 4
    
    // The parameters are for subpages (2048/4)
    #define SECTOR_SZ (PAGE_SIZE / SECTORS_PER_PAGE)
    #define OOB_SZ (SPARE_AREA / SECTORS_PER_PAGE)
    #define OOB_ECC_OFS 9
    #define OOB_ECC_LEN 7
    
    // Default NAND Confgiuration for 2k paged with 64bytes spare size.
    
    #define PAGE_SIZE 2048
    #define BLOCK_SIZE 131072
    #define SPARE_AREA 64
    #define SUB_PAGE_SIZE 512
    
    #define MAX_BLOCK_SIZE 524288
    #define MAX_OOB_BLOCK_SIZE 540672
    
    #define DEFAULT_POLY 0x201b
    
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <stdint.h>
    #include <getopt.h>
    #include <sys/stat.h>
    #include <sys/types.h>
    #include <inttypes.h>
    
    #include "bch.h"
    
    /**
     *
     * This program reads raw NAND image from standard input and updates ECC bytes in the OOB block for each sector.
     * Data layout is as following:
     *
     * 2 KB page, consisting of 4 x 512 B sectors
     * 64 bytes OOB, consisting of 4 x 16 B OOB regions, one for each sector
     *
     * In each OOB region, the first 9 1/2 bytes are user defined and the remaining 6 1/2 bytes are ECC.
     *
     */
    
    uint8_t page_buffer[(SECTOR_SZ + OOB_SZ) * SECTORS_PER_PAGE];
    
    // Wide right shift by 4 bits. Preserves the very first 4 bits of the output.
    static void shift_half_byte(const uint8_t *src, uint8_t *dest, size_t sz)
    {
        // go right to left since input and output may overlap
        unsigned j;
        dest[sz] = src[sz - 1] << 4;
        for (j = sz; j != 0; --j)
            dest[j] = src[j] >> 4 | src[j - 1] << 4;
        dest[0] |= src[0] >> 4;
    }
    
    
    Pitchaiah Murugan's avatar
    Pitchaiah Murugan committed
    /* swap buffer for another */
    static void swap(uint8_t *first, uint8_t *second, int amount)
    {
        uint8_t temp;
    
    
    Pitchaiah Murugan's avatar
    Pitchaiah Murugan committed
        {
            temp = *first;
            *first++ = *second;
            *second++ = temp;
        }
    }
    
    
    static void clear_pagebuffer()
    {
        uint32_t index = 0U;
        /* Clears the buffers before programming the Page. */
        for (index = 0; index < ((SECTOR_SZ + OOB_SZ) * SECTORS_PER_PAGE); index++)
        {
    
            page_buffer[index] = 0xFF;
    
        }
    }
    
    void print_usage(void)
    {
        printf("nand image builder version 1.0\n");
        printf("Usage: nand-image-builder [OPTION]...\n");
        printf("\t -i input file\n");
        printf("\t -o output file\n");
        printf("\t -b block size\n");
        printf("\t -p page size\n");
        printf("\t -s spare area/oob area size\n");
        printf("\t -e ecc mode\n");
        printf("\t -m field order\n");
        printf("Default command is:\n");
        printf("./nand-image-builder -i infile -o outfile -b 131072 -p 2048 -s 64 -e 4 -m 14\n");
        return;
    }
    
    int main(int argc, char **argv)
    {
        int option = 0;
        uint32_t blocks_app, blocks_num = 0;
        uint32_t code_length, parity, msg_length, data_bytes = 0;
        uint32_t countMask, count = 0;
        uint32_t numBytesRead, totalBytesRead = 0;
        uint32_t block_size, page_size, ecc_level, spare_area, field_order = 0;
        uint32_t sector_sz, oob_sz, oob_ecc_len, oob_ecc_ofs = 0;
    
        int pages_in_block, i, j = 0;
    
        const char *input_file = NULL;
        const char *output_file = NULL;
        FILE *in_file = NULL, *out_file = NULL;
        struct stat st;
        uint8_t *buffer;
        uint8_t cmp_buffer[SECTOR_SZ * SECTORS_PER_PAGE];
        uint8_t ecc[OOB_ECC_LEN];
    
        /* Initialize BCH lib data and parameters */
    
        while ((option = getopt(argc, argv, "e:i:o:b:p:s:m:")) != -1)
        {
            switch (option)
            {
            case 'i':
                input_file = optarg;
                break;
            case 'o':
                output_file = optarg;
                break;
            case 'b':
                block_size = atoi(optarg);
                break;
            case 'p':
                page_size = atoi(optarg);
                break;
            case 'e':
                ecc_level = atoi(optarg);
                break;
            case 's':
                spare_area = atoi(optarg);
                break;
            case 'm':
                field_order = atoi(optarg);
                break;
            default:
                print_usage();
                goto exit;
            }
        }
        if (argc < 4)
        {
            print_usage();
            goto exit;
        }
    
        /* Validate input */
        if (block_size == 0)
        {
            printf("Block size can not be 0\n");
            goto exit;
        }
        if (block_size > MAX_BLOCK_SIZE)
        {
            printf("Block size to large\n");
        }
    
        if (page_size == 0)
        {
            printf("Page size can not be 0\n");
            goto exit;
        }
        if (page_size % 32)
        {
            printf("Page size not even multiple of 32\n");
            goto exit;
        }
    
        if (field_order < MIN_FIELD || field_order > MAX_FIELD)
        {
            printf("Field order %d and %d are supported.\n", MIN_FIELD, MAX_FIELD);
            goto exit;
        }
    
        if (ecc_level != BCH_4)
        {
            printf("-e unsupported ecc level %d.\n", ecc_level);
            goto exit;
        }
    
        if (input_file == NULL)
        {
            printf("-i input file name is missing\n");
            goto exit;
        }
        if (output_file == NULL)
        {
            printf("-o output file name is missing\n");
            goto exit;
        }
    
        in_file = fopen(input_file, "r");
        if (in_file == NULL)
        {
            printf("In file open error\n");
            goto exit;
        }
        out_file = fopen(output_file, "wb");
        if (out_file == NULL)
        {
            printf("Out file open error\n");
            goto exit;
        }
    
        fseek(in_file, 0, SEEK_SET);
        fseek(out_file, 0, SEEK_SET);
    
        /* Validate that the input file size is a multiple of the block size */
        if (stat(input_file, &st) != 0)
        {
            printf("File size error\n");
            goto exit;
        }
    
    
        /* if ((st.st_size % block_size) != 0)
    
        {
            printf("File size not multiple of block size\n");
            goto exit;
    
    
        blocks_app = st.st_size / block_size; //calculate total blocks needed for application.
        pages_in_block = block_size / page_size;
    
        sector_sz = page_size / SECTORS_PER_PAGE;
        oob_sz = spare_area / SECTORS_PER_PAGE;
    
        /* Codeword length (in bits) */
        code_length = (sector_sz + oob_sz) * 8;
    
        /* Parity length (in bits). */
        parity = field_order * ecc_level;
    
        /* Message length (in bits). */
        msg_length = code_length - parity;
    
        /* Number of data bytes = floor(k/8) */
        data_bytes = msg_length / 8;
    
        /* Number of ecc_code bytes, v = ceil(p/8) = ceil(m*t/8) = p_partial_page_size+s-u */
        oob_ecc_len = (sector_sz + oob_sz) - data_bytes;
    
        if (oob_ecc_len > oob_sz)
        {
            printf(" Number of spare bytes must be at least %d\n", oob_ecc_len);
            goto exit;
        }
    
        oob_ecc_ofs = oob_sz - oob_ecc_len;
    
        countMask = pages_in_block - 1;
    
        buffer = malloc(sector_sz + oob_ecc_ofs + 1);
        if (!buffer)
        {
            printf("Failed to allocate the  buffer\n");
            goto exit;
        }
    
        /* Initialize BCH lib data and parameters */
    
        struct bch_control *bch = init_bch(field_order, ecc_level, 0);
    
        if (!bch)
            goto exit;
    
        printf("Input file %s\n", input_file);
        printf("Size of input file: %d\n", (int)st.st_size);
        printf("Block size: %d\n", block_size);
        printf("Input file blocks %d\n", blocks_app);
        printf("Page size: %d\n", page_size);
        printf("Pages in block: %d\n", pages_in_block);
        printf("ECC mode: %d\n", ecc_level);
        printf("OOB area size %d\n", spare_area);
        printf("Sub Page size %d\n", sector_sz);
        printf("OOB size per subpage %d\n", oob_sz);
        printf("OOB_ECC_LEN %d\n", oob_ecc_len);
        printf("OOB_ECC_OFS %d\n", oob_ecc_ofs);
    
    
            numBytesRead = fread(page_buffer, (size_t)1, (sector_sz)*SECTORS_PER_PAGE, in_file);
    
            totalBytesRead += numBytesRead;
    
            /* printf("\r\nRead %d bytes [%d%%] from file...\r\n",
                           totalBytesRead,
                           ((totalBytesRead * 100U) / st.st_size)); */
    
            if (numBytesRead == 0)
            {
                printf("Fread failed or file size is zero\r\n");
                goto exit;
            }
    
    
            if (numBytesRead != (sector_sz)*SECTORS_PER_PAGE)
                break; // skip if byte is less than bytes read from fread api.
    
            memset(cmp_buffer, 0, sector_sz * SECTORS_PER_PAGE);
    
            memcpy(cmp_buffer, page_buffer, (sector_sz)*SECTORS_PER_PAGE);
    
            /*printf("Page %d memory dump before ECC data\n", count);
    		  for(i=0; i < 2048; i++)
    			printf("0x%X\t", cmp_buffer[i]); */
    
    
            for (i = 0; i < SECTOR_SZ * SECTORS_PER_PAGE; i += 4)
    
                if (*(uint32_t *)(page_buffer + i) != 0xffffffff)
                {
    
                    for (i = 0; i != SECTORS_PER_PAGE; ++i)
                    {
    
                        swap(&page_buffer[(i + 1) * sector_sz], &page_buffer[sector_sz * SECTORS_PER_PAGE + (i * oob_sz)], oob_sz);
    
                        memset(ecc, 0, OOB_ECC_LEN);
    
                        encode_bch(bch, &page_buffer[i * sector_sz], sector_sz + oob_ecc_ofs, ecc);
    
                        swap(&page_buffer[(i + 1) * sector_sz], &page_buffer[sector_sz * SECTORS_PER_PAGE + (i * oob_sz)], oob_sz);
    
                        for (j = 0; j < oob_ecc_len; j++)
                        {
                            page_buffer[sector_sz * SECTORS_PER_PAGE + ((i + 1) * oob_sz) - oob_ecc_len + j] = ecc[j];
                        }
    
                        //page_buffer[sector_sz * SECTORS_PER_PAGE + ((i + 1) * oob_sz) - oob_ecc_len +] |= ecc[oob_ecc_len - 1] >> 4;
                        //fwrite(page_buffer, (sector_sz + oob_sz) * SECTORS_PER_PAGE, 1, out_file);
                        //goto exit;
                        //sector_oob[oob_ecc_ofs + oob_ecc_len - 1] |= ecc[oob_ecc_len - 1] >> 4;
                    }
                }
                break;
    
            }
    
            fwrite(page_buffer, (sector_sz + oob_sz) * SECTORS_PER_PAGE, 1, out_file);
    
            i = memcmp(cmp_buffer, page_buffer, (sector_sz)*SECTORS_PER_PAGE); // compare the page buffer with cmp_buffer to verify page has not modified except oob spare size.
    
            if (i != 0)
                printf("\r\npage no %d and block no %d compare failed\n", count, blocks_num);
    
            /*	printf("\r\nPage %d memory dump after adding ECC data\n", count);
    		for(i=0; i < 2048; i++)
    			printf("0x%X\t", page_buffer[i]); */
    
            count++;
    
            if (!(count & countMask))
            {
    
                /* printf("block No  %d\r\n", blocks_num); */
    
                blocks_num++;
            }
        }
    
    exit:
        if (in_file)
            fclose(in_file);
        if (out_file)
            fclose(out_file);
        exit(0);
    }