qemu/util/qemu-nand-creator.c
<<
>>
Prefs
   1/*
   2 * QEMU NAND flash image creator.
   3 *
   4 * QEMU NAND flash backing files have a non-trivial data layout due to OOB data.
   5 * This program takes an input image and lays it out in the in-band sections and
   6 * sets the OOB to 0xFF spam.
   7 *
   8 * Copyright (C) 2013 - 2014 Xilinx, Inc.  All rights reserved.
   9 * Written by Peter Crosthwaite <peter.crosthwaite@xilinx.com>
  10 *
  11 * Permission is hereby granted, free of charge, to any person obtaining a copy
  12 * of this software and associated documentation files (the "Software"), to deal
  13 * in the Software without restriction, including without limitation the rights
  14 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  15 * copies of the Software, and to permit persons to whom the Software is
  16 * furnished to do so, subject to the following conditions:
  17 *
  18 * The above copyright notice and this permission notice shall be included in
  19 * all copies or substantial portions of the Software.
  20 *
  21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  22 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  23 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  24 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  25 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  26 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  27 * THE SOFTWARE.
  28 */
  29
  30#define _FILE_OFFSET_BITS 64
  31
  32#include <stdio.h>
  33#include <stdlib.h>
  34#include <stdint.h>
  35#include <stdbool.h>
  36#include <unistd.h>
  37#include <sys/types.h>
  38#include <sys/stat.h>
  39#include <fcntl.h>
  40#include <string.h>
  41#include <assert.h>
  42#include <errno.h>
  43
  44#define ECC_CODEWORD_SIZE 512
  45
  46#ifdef DEBUG
  47    #define DPRINT(...) do { fprintf(stdout, __VA_ARGS__); } while (0)
  48#else
  49    #define DPRINT(...) do { } while (0)
  50#endif
  51
  52uint32_t ecc_size;
  53uint32_t ecc_pos;
  54uint32_t ecc_subpage_offset;
  55
  56static void ecc_digest(uint8_t *data , uint8_t * oob, uint32_t bytes_read,
  57                       uint32_t page_size);
  58
  59/* Command line positional inputs
  60 * 1. page size
  61 * 2. oob size
  62 * 3. num of pages per block
  63 * 5. num of blocks per lun
  64 * 4. ecc size
  65ex: for 32gb micron nand inputs are as below.
  66./qemu-nand-creator 16384 1216 256 1048 672 < BOOT.BIN
  67
  68*/
  69
  70int main(int argc, char *argv[])
  71{
  72    uint32_t page_size;
  73    uint32_t oob_size;
  74    uint32_t num_pages_per_block;
  75    uint32_t num_pages = 0;
  76    uint32_t block = 0;
  77    uint32_t num_of_blocks = 0;
  78    uint32_t bb_pat = 0;
  79    uint8_t *oob_pbuf;
  80    uint8_t *buf;
  81    uint8_t *ecc_data;
  82    int i, j;
  83    bool Image_done = false;
  84    bool page_empty = false;
  85    bool Image_wip = true;
  86    int nand_flash;
  87
  88    const char *exe_name = argv[0];
  89    argc--;
  90    argv++;
  91
  92    if (argc != 5) {
  93        fprintf(stderr, "Usage: %s <page size> <oob size> " \
  94                "<num of pages per block> <num_blocks> "\
  95                "<ecc size>\n", exe_name);
  96        return 1;
  97    }
  98
  99    page_size = strtol(argv[0], NULL, 0);
 100    argc--;
 101    if (errno) {
 102        perror(argv[0]);
 103        exit(1);
 104    }
 105
 106    oob_size = strtol(argv[1], NULL, 0);
 107
 108    if (argc) {
 109        num_pages_per_block = strtol(argv[2], NULL, 0);
 110    }
 111    argc--;
 112    num_of_blocks = strtol(argv[3], NULL, 0);
 113    ecc_size = strtol(argv[4], NULL, 0);
 114    ecc_data = (uint8_t *)malloc(ecc_size);
 115
 116    uint8_t *oob_buf = (uint8_t *)malloc(oob_size);
 117    memset(oob_buf, 0xFF, oob_size);
 118
 119    nand_flash = open("./qemu_nand.bin", O_CREAT | O_WRONLY, S_IRWXU);
 120
 121    /* bad block marker oob data */
 122    uint8_t oob_buf_bad[oob_size];
 123    memset(oob_buf_bad, 0xFF, oob_size);
 124    oob_buf_bad[0] = 0x00;
 125
 126#ifdef CREATE_BB
 127    /* create random bad block pattern with first block as good */
 128    bb_pat = rand();
 129
 130    /* First block is always good */
 131    bb_pat &= ~(1 << 0);
 132#endif
 133
 134    /* Create Page Refernce buffer */
 135    buf = (uint8_t *)malloc(page_size);
 136
 137    fprintf(stderr, "Creating Nand Flash Image:\n");
 138    while (true) {
 139        /* check if block is bad for first 32 blocks */
 140        bool block_is_bad = (block < 32) ? ((bb_pat >> block) & 0x1) : 0;
 141
 142        if (block_is_bad) {
 143            /* Block is bad, make an empty page */
 144            page_empty = true;
 145            DPRINT("Bad Block %d\n", block);
 146        } else if (!Image_done) {
 147            /* Image Write in progress.. */
 148            page_empty = false;
 149            /* Clear oob & ecc area */
 150            memset(oob_buf, 0xFF, oob_size);
 151            memset(ecc_data, 0xFF, ecc_size);
 152            ecc_pos = 0;
 153            ecc_subpage_offset = 0;
 154
 155            for (i = 0; i < page_size; ++i) {
 156                /* Read Input file, 1 page at a time */
 157                ssize_t bytes_read = read(STDIN_FILENO, &buf[i], page_size - i);
 158                DPRINT("Block %d page %d bytes_read %d\n", block,
 159                       num_pages, bytes_read);
 160
 161                /* Calcualte Ecc digest based on read bytes */
 162                if (ecc_size && bytes_read) {
 163                    ecc_digest(&buf[i], ecc_data, bytes_read, page_size);
 164                    /* Copy ecc_data to OOB */
 165                    memcpy(&oob_buf[oob_size - ecc_size], ecc_data, ecc_size);
 166                    /* ECC debug Logging */
 167                    DPRINT("\tECC pos %d\nECC Digest:\n", ecc_pos);
 168                    for (j = oob_size - ecc_size; j < oob_size; j++) {
 169                        DPRINT("%d:%x ", j, oob_buf[j]);
 170                        if (!(j % 5)) {
 171                            DPRINT("\n");
 172                        }
 173                    }
 174                    DPRINT("\n");
 175                }
 176
 177                switch (bytes_read) {
 178                case 0:
 179                    Image_done = true;
 180                    memset(&buf[i], 0xFF, page_size - i);
 181                    break;
 182                case -1:
 183                    perror("stdin");
 184                    return 1;
 185                default:
 186                    if (bytes_read < page_size) {
 187                        Image_done = true;
 188                        /* Clear Rest of the page and update ECC */
 189                        memset(&buf[bytes_read], 0xFF, page_size - bytes_read);
 190                        ecc_digest(&buf[bytes_read], ecc_data,
 191                                   page_size - bytes_read, page_size);
 192                        /* Refresh ECC data */
 193                        memcpy(&oob_buf[oob_size - ecc_size], ecc_data,
 194                               ecc_size);
 195                        i = page_size;
 196                    } else {
 197                        i += bytes_read;
 198                    }
 199                    break;
 200                }
 201            }
 202        } else {
 203            /* Empty Page */
 204            page_empty = true;
 205        }
 206
 207        for (i = 0; i < page_size; ++i) {
 208            ssize_t bytes_written;
 209            if (page_empty == false) {
 210                bytes_written = write(nand_flash, &buf[i], page_size - i);
 211            } else {
 212                lseek64(nand_flash, page_size - i, SEEK_CUR);
 213                bytes_written = page_size;
 214            }
 215
 216            switch (bytes_written) {
 217            case -1:
 218                perror("stdin");
 219                return 1;
 220            default:
 221                i += bytes_written;
 222            }
 223        }
 224
 225        /* write bad block markers in spare area */
 226        oob_pbuf = (num_pages <= 1) && block_is_bad ? &oob_buf_bad[0]
 227                                                    : &oob_buf[0];
 228        for (i = 0; i < oob_size; ++i) {
 229            ssize_t bytes_written = write(nand_flash, &oob_pbuf[i],
 230                                          oob_size - i);
 231            switch (bytes_written) {
 232            case -1:
 233                perror("stdin");
 234                return 1;
 235            default:
 236                i += bytes_written;
 237            }
 238        }
 239
 240        if (Image_wip == Image_done) {
 241            /* Image is written. So one final time clean the oob,
 242             * so it can be written every time
 243             */
 244            memset(oob_buf, 0xFF, oob_size);
 245            Image_wip = false;
 246        }
 247        num_pages++;
 248        /* check block boundary */
 249        if (num_pages_per_block && !(num_pages % num_pages_per_block)) {
 250            block++;
 251            fprintf(stderr, "\r. . .");
 252            if (block > num_of_blocks) {
 253                goto done;
 254            }
 255            num_pages = 0;
 256        }
 257
 258    }
 259
 260done:
 261    fprintf(stderr, "Done!\n");
 262    sync();
 263    close(nand_flash);
 264    return 0;
 265}
 266
 267static void ecc_digest(uint8_t *data , uint8_t * oob,
 268                       uint32_t bytes_read, uint32_t page_size)
 269{
 270    int ecc_bytes_per_subpage =  ecc_size /
 271                                    (page_size / ECC_CODEWORD_SIZE);
 272    uint32_t head = 0;
 273    while (head < bytes_read) {
 274        oob[ecc_pos++] ^= ~data[head];
 275        if (!(ecc_pos % ecc_bytes_per_subpage)) {
 276            ecc_pos -= ecc_bytes_per_subpage;
 277        }
 278
 279        ecc_subpage_offset++;
 280        if (ecc_subpage_offset == ECC_CODEWORD_SIZE) {
 281            ecc_subpage_offset = 0;
 282            do {
 283                ecc_pos++;
 284            } while (ecc_pos % ecc_bytes_per_subpage);
 285        }
 286        head++;
 287    }
 288}
 289