| /******************************************************************************** |
| * Marvell GPL License Option |
| * |
| * If you received this File from Marvell, you may opt to use, redistribute and/or |
| * modify this File in accordance with the terms and conditions of the General |
| * Public License Version 2, June 1991 (the "GPL License"), a copy of which is |
| * available along with the File in the license.txt file or by writing to the Free |
| * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or |
| * on the worldwide web at http://www.gnu.org/licenses/gpl.txt. |
| * |
| * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED |
| * WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY |
| * DISCLAIMED. The GPL License provides additional details about this warranty |
| * disclaimer. |
| ******************************************************************************/ |
| |
| /** Randomize the data in NAND. |
| * |
| * At present, it only supports SAMSUNG NAND randomization way at present. |
| * |
| * Some key points. |
| * 1. Don't randomize bad block marker. |
| * 2. Seed can use randomized data. |
| * |
| * Important things for modifications, for the follow things would change |
| * the random data, and it would make the data won't be binary compatible. |
| * 1. Initial Seed. |
| * 2. Page start positions. |
| * 3. Randomize or unrandomized some special bytes, such as bad block marker. |
| * |
| * @warning: This file is used in other places, so don't inlcude any |
| * kernel related files to it. |
| * |
| * Author: Yongsen Chen, YongsenChen@gmail.com |
| */ |
| |
| /* |
| * CONFIGURATIONS |
| */ |
| #define xUNIT_TEST |
| |
| #ifndef ASSERT |
| # define ASSERT(x) /* {if (!(x)) while (1);} */ |
| #endif /* ASSERT */ |
| |
| #define DO_TRACE_LOG (0) |
| |
| #if DO_TRACE_LOG |
| int printf(const char *fmt, ...); |
| #define TRACE_LOG printf |
| #else |
| #define TRACE_LOG(...) |
| #endif |
| |
| /* We use random seed to make it work for all chip that requires randomization. |
| */ |
| #define RANDOM_SEED |
| |
| /* |
| * INCLUDES |
| */ |
| #include "prbs.h" |
| #include "nand_randomizer.h" |
| |
| /* |
| * CONSTANTS |
| */ |
| #ifndef TRUE |
| #define TRUE (1) |
| #endif |
| |
| #ifndef FALSE |
| #define FALSE (0) |
| #endif |
| |
| #ifndef NULL |
| #define NULL (0) |
| #endif |
| |
| |
| #define NAND_ID_MAX_SIZE (8) |
| #define NAND_DEFAULT_OOB_SIZE (32) |
| |
| #define IS_POWER_OF_2(x) ( !( (x) & ((x)-1) ) ) |
| #define ARRAYSIZE(a) (sizeof(a)/sizeof(*a)) |
| |
| #define PAGE_START_POS_ALIGNED_BYTES (4) |
| #define PAGE_START_POS_MASK(cycle_len) \ |
| ((cycle_len) - 1) & (~(PAGE_START_POS_ALIGNED_BYTES - 1)) |
| |
| /** NAND randomizer types. |
| * |
| * We only support SAMSUNG PRBS-15 at present. |
| * @sa SAMSUNG Recommendation for a randomizer v0.1 |
| */ |
| enum mv_nand_randomizer_type_e { |
| MV_NAND_RANDOMIZER_FROM_MEMORY, /** Use the input buffer directly */ |
| MV_NAND_RANDOMIZER_SAMSUNG_PRBS15, /** Generate by PRBS-15 with seeds |
| * defined by SAMSUNG. |
| */ |
| MV_NAND_RANDOMIZER_TYPE_MAX |
| }; |
| |
| /* |
| * TYPES |
| */ |
| |
| /** NAND Randomizer chip information. |
| */ |
| struct nand_randomized_chip_info_s { |
| int chip_id_len; |
| unsigned char chip_id[NAND_ID_MAX_SIZE]; |
| unsigned int block_size; |
| unsigned int page_size; |
| unsigned int spare_size; |
| enum mv_nand_randomizer_type_e randomizer_type; |
| unsigned int randomizer_buffer_length; |
| }; |
| |
| |
| /** NAND Randomizer. |
| */ |
| struct mv_nand_randomizer_s { |
| int chip_randomized; |
| struct nand_randomized_chip_info_s *chip_info; |
| |
| unsigned int block_size; |
| unsigned int page_size; |
| unsigned int oob_size; |
| |
| unsigned int page_per_block; |
| unsigned int block_shift; |
| unsigned int page_mask; |
| |
| unsigned int random_data_buffer_length; |
| unsigned char *p_random_data_buffer; |
| }; |
| |
| /* |
| * VARIABLES |
| */ |
| static const struct nand_randomized_chip_info_s g_nand_randomized_chip_list[] = { |
| /* SAMSUNG K9GBG08U0A, 32Gb */ |
| { |
| 6, |
| {0xEC, 0xD7, 0x94, 0x7A, 0x54, 0x43}, |
| 8192 * 128, |
| 8192, |
| 640, |
| MV_NAND_RANDOMIZER_SAMSUNG_PRBS15, |
| 4096 |
| } |
| /* SAMSUNG K9GBG08U0B, 32Gb */ |
| ,{ |
| 6, |
| {0xEC, 0xD7, 0x94, 0x7E, 0x64, 0x44}, |
| 8192 * 128, |
| 8192, |
| 1024, |
| MV_NAND_RANDOMIZER_SAMSUNG_PRBS15, |
| 4096 |
| } |
| /* TOSHIBA TC58NVG4D2HTA00, 16Gb */ |
| ,{ |
| 6, |
| {0x98, 0xD5, 0x84, 0x32, 0x72, 0x56}, |
| 8192 * 128, |
| 8192, |
| 640, |
| MV_NAND_RANDOMIZER_SAMSUNG_PRBS15, |
| 4096 |
| } |
| /* SAMSUNG K9LCG08U0A/K9HDG08U1A, 64Gb/128Gb |
| * K9HDG08U1A consist 2 pieces of K9LCG08U0A, 1 CE(Chip Enable) pin |
| * for 1 piece. |
| */ |
| ,{ |
| 6, |
| {0xEC, 0xDE, 0xD5, 0x7A, 0x58, 0x43}, |
| 8192 * 128, |
| 8192, |
| 640, |
| MV_NAND_RANDOMIZER_SAMSUNG_PRBS15, |
| 4096 |
| } |
| /* MICRON MT29F32G08CBxBx/MT29F64G08CxxBx, 32Gb/64Gb */ |
| ,{ |
| 5, |
| {0x2C, 0x68, 0x04, 0x46, 0x89}, |
| 4096 * 256, |
| 4096, |
| 224, |
| MV_NAND_RANDOMIZER_SAMSUNG_PRBS15, |
| 4096 |
| } |
| /* MICRON MT29F16G08CBACA, 16Gb */ |
| ,{ |
| 5, |
| {0x2C, 0x48, 0x04, 0x4A, 0xA5}, |
| 4096 * 256, |
| 4096, |
| 224, |
| MV_NAND_RANDOMIZER_SAMSUNG_PRBS15, |
| 4096 |
| } |
| /* HYNIX H27UBG8T2BTR, 32Gb */ |
| ,{ |
| 6, |
| {0xAD, 0xDE, 0x94, 0xD2, 0x04, 0x43}, |
| 8192 * 256, |
| 8192, |
| 448, |
| MV_NAND_RANDOMIZER_SAMSUNG_PRBS15, |
| 4096 |
| } |
| /* HYNIX H27UCG8T2ATR, 64Gb */ |
| ,{ |
| 6, |
| {0xAD, 0xDE, 0x94, 0xDA, 0x74, 0xC4}, |
| 8192 * 256, |
| 8192, |
| 640, |
| MV_NAND_RANDOMIZER_SAMSUNG_PRBS15, |
| 4096 |
| } |
| /* HYNIX H27UAG8T2CTR, 16Gb */ |
| ,{ |
| 6, |
| {0xAD, 0xD5, 0x94, 0xDA, 0x74, 0xC4}, |
| 8192 * 256, |
| 8192, |
| 640, |
| MV_NAND_RANDOMIZER_SAMSUNG_PRBS15, |
| 4096 |
| } |
| |
| }; |
| |
| /* Samsung randomizer. |
| */ |
| #ifndef RANDOM_SEED |
| static const unsigned short g_nand_randomozer_seed_start_pos_table_samsung [] = { |
| 0, 32, 1020, 2564, 2664, 3408, 1300, 3048, |
| 848, 332, 860, 804, 800, 3816, 3064, 3096, |
| 1136, 3208, 304, 3264, 500, 3944, 2348, 1260, |
| 3884, 516, 1280, 3980, 176, 2552, 2584, 648, |
| 1832, 1928, 656, 224, 1848, 340, 2544, 988, |
| 2468, 292, 224, 2968, 3000, 1168, 2000, 36, |
| 792, 1972, 3684, 928, 4052, 2752, 1016, 440, |
| 2412, 176, 2184, 2216, 1824, 2536, 2456, 2992, |
| 1848, 2120, 184, 2484, 2220, 1324, 2056, 3472, |
| 3544, 3576, 3600, 556, 3428, 2936, 336, 2284, |
| 804, 3620, 3140, 1660, 2888, 3476, 44, 1816, |
| 1848, 408, 3864, 1592, 2896, 452, 3564, 1916, |
| 3300, 1852, 172, 964, 804, 296, 328, 1052, |
| 2168, 2180, 356, 4064, 2096, 1228, 2484, 2204, |
| 1996, 1192, 984, 1052, 3356, 3388, 2368, 3260, |
| 2308, 1920, 1304, 2732, 3744, 3112, 3256, 732 |
| }; |
| |
| #define NAND_RANDOMIZER_SEED_COUNT_SAMSUNG \ |
| ARRAYSIZE(g_nand_randomozer_seed_start_pos_table_samsung) |
| |
| #endif /* RANDOM_SEED */ |
| |
| #define NAND_RANDOMIZER_SEED_SAMSUNG (0x576A) |
| |
| /* Global randomizer. */ |
| static struct mv_nand_randomizer_s g_nand_randomizer; |
| |
| |
| /* |
| * PRIVATE FUNCTIONS |
| */ |
| |
| #define DUMP_8_BYTES(name, b) \ |
| if (b) { \ |
| TRACE_LOG("%s(0x%08X): %02X %02X %02X %02X %02X %02X %02X %02X\n", \ |
| name, (int)(b), \ |
| (b)[0], (b)[1], (b)[2], (b)[3], (b)[4], (b)[5], (b)[6], (b)[7]);\ |
| } |
| |
| |
| /** Get shift of x. |
| */ |
| static unsigned int get_shift(unsigned int x) |
| { |
| unsigned int s = 0; |
| ASSERT(x > 0 && IS_POWER_OF_2(x)); |
| |
| for (s=0; s<sizeof(x)*8; s++) { |
| if ((1 << s) == x) { |
| break; |
| } |
| } |
| return s; |
| } |
| |
| /** Get the random data by PRBS generator |
| * |
| */ |
| static unsigned int gen_prbs15_random_data_samsung(unsigned char *p_random_data_buffer, |
| unsigned int random_data_length) |
| { |
| unsigned short seed = NAND_RANDOMIZER_SEED_SAMSUNG; |
| |
| ASSERT(p_random_data_buffer); |
| ASSERT(random_data_length); |
| |
| prbs15_gen(PRBS_POLYNOMIAL_DEFAULT, |
| seed, |
| p_random_data_buffer, |
| random_data_length, |
| FALSE); |
| return random_data_length; |
| } |
| |
| /** Randomize data by XOR way. |
| * |
| * For each byte, *p_dest = *p_random_data ^ *p_src. |
| * |
| * @param p_random_data Random data. |
| * @param p_src Source data. |
| * @param p_dst Buffer to save the randomized data. |
| * @param length Length of the data to randomize. |
| * |
| * @return void |
| * |
| * @sa mv_nand_randomizer_init(), mv_nand_randomizer_get_page(). |
| */ |
| static void randomize_by_xor(const unsigned char *p_random_data, |
| const unsigned char *p_src, |
| unsigned char *p_dst, |
| int length) |
| { |
| int i; |
| const int aligned_mask = sizeof(unsigned int) - 1; |
| ASSERT(p_random_data); |
| ASSERT(p_src); |
| ASSERT(p_dst); |
| ASSERT(length > 0); |
| |
| /* if it's 4-byte aligned, then we use a faster way to do so */ |
| if ((0 == ((int)p_random_data & aligned_mask)) && |
| (0 == ((int)p_src & aligned_mask)) && |
| (0 == ((int)p_dst & aligned_mask)) && |
| (0 == (length & aligned_mask))) { |
| const unsigned int *p_random_data_32 = (const unsigned int *)p_random_data; |
| const unsigned int *p_src_32 = (const unsigned int *)p_src; |
| unsigned int *p_dst_32 = (unsigned int *)p_dst; |
| int length_32 = length/sizeof(*p_random_data_32); |
| |
| for (i=0; i<length_32; i++) { |
| p_dst_32[i] = p_src_32[i] ^ p_random_data_32[i]; |
| } |
| } else { |
| for (i=0; i<length; i++) { |
| p_dst[i] = p_src[i] ^ p_random_data[i]; |
| } |
| } |
| } |
| |
| /** It will randomize the data in ring. |
| * |
| * @param p_random_data Random data. |
| * @param random_data_length Length of the random data. |
| * @param p_src Source data. |
| * @param p_dst Buffer to save the randomized data. |
| * @param length Length of the data to randomize, must not |
| * be larger than a page_random_data_length. |
| * @param start Start position in the page to randomize. |
| * |
| * @return void |
| * |
| * @sa randomize_by_xor() |
| */ |
| static void randomize_by_xor_ring(const unsigned char *p_random_data, |
| unsigned int random_data_length, |
| const unsigned char *p_src, |
| unsigned char *p_dst, |
| unsigned int length, |
| unsigned int start) |
| { |
| unsigned int randomized_length = 0; |
| unsigned int offset = start; |
| |
| /* make sure offset is in a page */ |
| while (offset >= random_data_length) { |
| offset -= random_data_length; |
| } |
| |
| while (randomized_length < length) { |
| unsigned int processing_length = random_data_length - offset; |
| if (processing_length > length - randomized_length) { |
| processing_length = length - randomized_length; |
| } |
| |
| if (offset != 0) { |
| DUMP_8_BYTES("RANDOM ", p_random_data + offset); |
| } |
| |
| randomize_by_xor(p_random_data + offset, |
| p_src + randomized_length, |
| p_dst + randomized_length, |
| processing_length); |
| offset = 0; |
| randomized_length += processing_length; |
| } |
| } |
| |
| /** Get the randomized chip info. |
| * |
| * @param p_chip_id Chip ID data. |
| * @param chip_id_len Chip ID length. |
| * |
| * @retval NULL Chip is unrandomized. |
| * @retval !NULL Chip is randomized, return the chip info. |
| * |
| * @sa |
| */ |
| static const struct nand_randomized_chip_info_s* |
| mv_nand_get_chip_randomized_info(const unsigned char *p_chip_id, |
| int chip_id_len) |
| { |
| const struct nand_randomized_chip_info_s *p_found_chip_info = NULL; |
| int chip_index; |
| |
| /* If no chip_id input, then default is enabled */ |
| if (NULL == p_chip_id || chip_id_len <= 0) { |
| TRACE_LOG("no chip_id input, use default randomizer.......\n"); |
| return &g_nand_randomized_chip_list[0]; |
| } |
| |
| for (chip_index=0; chip_index < ARRAYSIZE(g_nand_randomized_chip_list); chip_index++) { |
| const struct nand_randomized_chip_info_s * p_chip_info = |
| &g_nand_randomized_chip_list[chip_index]; |
| int compare_len = chip_id_len; |
| int found = TRUE; |
| int i; |
| |
| if ((0 == compare_len) || |
| (compare_len > p_chip_info->chip_id_len)) { |
| compare_len = p_chip_info->chip_id_len; |
| } |
| |
| /* we don't use memcmp() or strncmp() to make the function get better |
| * compatibility. |
| */ |
| for (i=0; i<compare_len; i++) { |
| if (p_chip_id[i] != p_chip_info->chip_id[i]) { |
| found = FALSE; |
| break; |
| } |
| } |
| |
| if (found) { |
| p_found_chip_info = p_chip_info; |
| break; |
| } |
| } |
| |
| TRACE_LOG("mv_nand_chip_randomized(%02X %02X %02X %02X %02X %02X): !!!%s!!!\n", |
| p_chip_id[0], p_chip_id[1], p_chip_id[2], p_chip_id[3], |
| p_chip_id[4], p_chip_id[5], |
| p_found_chip_info ? "RANDOMIZED" : "UNRANDOMIZED"); |
| |
| return p_found_chip_info; |
| } |
| |
| /** Check whether a block is randomized. |
| * |
| * @param block_index Block index to check. |
| * |
| * @retval 0 Chip is not randomized. |
| * @retval !0 Chip is randomized. |
| * |
| * @sa |
| */ |
| static int mv_nand_block_randomized(unsigned int block_index) |
| { |
| /* Modify it to support other blocks */ |
| const unsigned int unrandomized_block_table[] = { |
| /* block 0 */0, |
| /* bootloader */1, 2, 3, 4, 5, 6, 7, 8 |
| }; |
| int i; |
| int randomized = TRUE; |
| |
| for (i=0; i<ARRAYSIZE(unrandomized_block_table); i++) { |
| if (block_index == unrandomized_block_table[i]) { |
| randomized = FALSE; |
| break; |
| } |
| } |
| return randomized; |
| } |
| |
| /** Get the start random data pos for a page. |
| * |
| * @param page_addr Address of a page. |
| * |
| * @return unsigned int Start pos in random data for the page. |
| * |
| * @sa |
| */ |
| static unsigned int mv_nand_get_page_start(unsigned int page_addr) |
| { |
| struct mv_nand_randomizer_s * p_nr = &g_nand_randomizer; |
| unsigned int page_index_in_block = page_addr & p_nr->page_mask; |
| |
| #ifdef RANDOM_SEED |
| unsigned int start; |
| /* 2 for each pos need 2 bytes |
| * we don't use pos = page_addr * 2; because LEAPimg need repeat the seed |
| * in each block, for the which block is bad is unexpected. |
| */ |
| unsigned int pos = page_index_in_block * 2; |
| pos = pos & (p_nr->random_data_buffer_length - 1); |
| |
| start = (p_nr->p_random_data_buffer[pos + 1] << 8) + |
| p_nr->p_random_data_buffer[pos]; |
| |
| start &= PAGE_START_POS_MASK(p_nr->random_data_buffer_length); |
| /* & p_nr->random_data_buffer_length is to make sure it won't exceed random |
| * data length. |
| * & (~0x3) is to make sure it's 32-bit aligned |
| */ |
| |
| return start; |
| #else /* !RANDOM_SEED */ |
| while (page_index_in_block >= NAND_RANDOMIZER_SEED_COUNT_SAMSUNG) { |
| page_index_in_block -= NAND_RANDOMIZER_SEED_COUNT_SAMSUNG; |
| } |
| |
| return g_nand_randomozer_seed_start_pos_table_samsung[page_index_in_block]; |
| #endif /* RANDOM_SEED */ |
| } |
| |
| /** The function to do real randomization. |
| * |
| * It will be called by all randomization functions. |
| * |
| * @param page_addr Address of the page (start from 0). |
| * @param p_src Source data. |
| * @param p_dst Buffer to save the randomized data. |
| * @param length Length of the data to randomize, must not |
| * be larger than a page_random_data_length. |
| * @param offset_in_page Start position in the page to randomize. |
| * |
| * @return void |
| * |
| * @sa |
| */ |
| static void mv_nand_do_randomize(unsigned int page_addr, |
| const unsigned char *p_src, |
| unsigned char *p_dst, |
| unsigned int length, |
| unsigned int offset_in_page) |
| { |
| struct mv_nand_randomizer_s * p_nr = &g_nand_randomizer; |
| |
| int start = mv_nand_get_page_start(page_addr); |
| randomize_by_xor_ring(p_nr->p_random_data_buffer, |
| p_nr->random_data_buffer_length, |
| p_src, |
| p_dst, |
| length, |
| start + offset_in_page); |
| } |
| /* |
| * PUBLIC FUNCTIONS |
| */ |
| |
| int mv_nand_chip_randomized(const unsigned char *p_chip_id, |
| int chip_id_len, |
| unsigned int *p_randomizer_buffer_length) |
| { |
| const struct nand_randomized_chip_info_s *p_randomized_chip_info = NULL; |
| p_randomized_chip_info = mv_nand_get_chip_randomized_info(p_chip_id, chip_id_len); |
| |
| if (p_randomizer_buffer_length) { |
| *p_randomizer_buffer_length = 0; |
| } |
| |
| if (!p_randomized_chip_info) { |
| return FALSE; |
| } |
| |
| if (p_randomizer_buffer_length) { |
| *p_randomizer_buffer_length = p_randomized_chip_info->randomizer_buffer_length; |
| } |
| return TRUE; |
| } |
| |
| int mv_nand_randomizer_init(const unsigned char *p_chip_id, |
| int chip_id_len, |
| unsigned int block_size, |
| unsigned int page_size, |
| unsigned int oob_size, |
| unsigned char *p_randomizer_buffer, |
| unsigned int randomizer_buffer_length) |
| { |
| struct mv_nand_randomizer_s *p_nr = &g_nand_randomizer; |
| const struct nand_randomized_chip_info_s *p_randomized_chip_info = NULL; |
| |
| ASSERT(IS_POWER_OF_2(page_size)); |
| ASSERT(IS_POWER_OF_2(block_size)); |
| |
| TRACE_LOG("mv_nand_randomizer_init(block_size=%d, page_size=%d, oob_size=%d, buf_len=%d)\n", |
| block_size, page_size, oob_size, randomizer_buffer_length); |
| |
| if (NULL == p_randomizer_buffer || 0 == randomizer_buffer_length) { |
| mv_nand_chip_randomized(p_chip_id, chip_id_len, &randomizer_buffer_length); |
| return randomizer_buffer_length; |
| } |
| |
| p_nr->p_random_data_buffer = p_randomizer_buffer; |
| |
| /* we initialize randomizer ASAP, to make sure the the randomizer can be initialized |
| * as unrandomized first. |
| */ |
| /*memset(p_nr, 0, sizeof(*p_nr));*/ |
| p_nr->chip_randomized = FALSE; |
| p_nr->chip_info = NULL; |
| |
| p_randomized_chip_info = mv_nand_get_chip_randomized_info(p_chip_id, chip_id_len); |
| |
| if (NULL == p_randomized_chip_info) { |
| return 0; |
| } |
| |
| if (0 == block_size) { |
| block_size = p_randomized_chip_info->block_size; |
| } |
| |
| if (0 == page_size) { |
| page_size = p_randomized_chip_info->page_size; |
| } |
| |
| if (0 == oob_size) { |
| oob_size = NAND_DEFAULT_OOB_SIZE; |
| } |
| |
| p_nr->block_size = block_size; |
| p_nr->page_size = page_size; |
| p_nr->oob_size = oob_size; |
| |
| /* calculate the masks and shifts */ |
| p_nr->page_per_block = block_size / page_size; |
| p_nr->page_mask = p_nr->page_per_block - 1; |
| p_nr->block_shift = get_shift(p_nr->page_per_block); |
| |
| if (randomizer_buffer_length < p_randomized_chip_info->randomizer_buffer_length) { |
| return -1; |
| } else { |
| randomizer_buffer_length = p_randomized_chip_info->randomizer_buffer_length; |
| } |
| |
| if (MV_NAND_RANDOMIZER_SAMSUNG_PRBS15 == p_randomized_chip_info->randomizer_type) { |
| gen_prbs15_random_data_samsung(p_randomizer_buffer, |
| randomizer_buffer_length); |
| } |
| |
| ASSERT(IS_POWER_OF_2(randomizer_buffer_length)); |
| p_nr->chip_randomized = TRUE; |
| p_nr->random_data_buffer_length = randomizer_buffer_length; |
| return randomizer_buffer_length; |
| } |
| |
| unsigned int mv_nand_randomizer_randomize(unsigned int page_addr, |
| const unsigned char *p_src, |
| unsigned char *p_dst, |
| unsigned int length, |
| unsigned int offset_in_page) |
| { |
| const struct mv_nand_randomizer_s *p_nr = &g_nand_randomizer; |
| unsigned int block_index = page_addr >> p_nr->block_shift; |
| /*unsigned int page_index = page_addr & p_nr->page_mask;*/ |
| |
| int need_set_bad_marker = FALSE; |
| unsigned char bad_marker = 0; |
| unsigned int bad_marker_pos = 0; |
| ASSERT(p_src); |
| ASSERT(p_dst); |
| ASSERT(length > 0); |
| |
| if (!p_nr->chip_randomized || !mv_nand_block_randomized(block_index)) { |
| return 0; |
| } |
| |
| if ((offset_in_page <= p_nr->page_size) && |
| ((offset_in_page + length) > p_nr->page_size)) { |
| need_set_bad_marker = TRUE; |
| bad_marker_pos = p_nr->page_size - offset_in_page; |
| bad_marker = p_src[bad_marker_pos]; |
| } |
| |
| mv_nand_do_randomize(page_addr, p_src, p_dst, length, offset_in_page); |
| |
| if (need_set_bad_marker) { |
| p_dst[bad_marker_pos] = bad_marker; |
| } |
| |
| return length; |
| } |
| |
| unsigned int mv_nand_randomizer_randomize_page(unsigned int page_addr, |
| const unsigned char *p_data_src, |
| const unsigned char *p_oob_src, |
| unsigned char *p_data_dst, |
| unsigned char *p_oob_dst) |
| { |
| const struct mv_nand_randomizer_s *p_nr = &g_nand_randomizer; |
| unsigned int block_index = page_addr >> p_nr->block_shift; |
| #if DO_TRACE_LOG |
| unsigned int page_index = page_addr & p_nr->page_mask; |
| #endif /* DO_TRACE_LOG */ |
| unsigned int randomized_length = 0; |
| |
| if (!p_nr->chip_randomized || !mv_nand_block_randomized(block_index)) { |
| TRACE_LOG("nand_randomize_page(0x%08X): UNRONDOMIZED\n", page_addr); |
| return 0; |
| } |
| |
| TRACE_LOG("mv_nand_randomizer_randomize_page(0x%08X): block %d, page %d, !!! RANDOMIZED !!!.\n", |
| page_addr, block_index, page_index); |
| |
| if (p_data_src) { |
| /* randomize page data */ |
| DUMP_8_BYTES("DATA_SRC", p_data_src); |
| mv_nand_do_randomize(page_addr, p_data_src, p_data_dst, p_nr->page_size, 0); |
| randomized_length += p_nr->page_size; |
| DUMP_8_BYTES("DATA_DST", p_data_dst); |
| } |
| |
| if (p_oob_src) { |
| unsigned char bad_marker = p_oob_src[0]; |
| /* randomize oob data */ |
| DUMP_8_BYTES("OOB_SRC", p_oob_src); |
| |
| mv_nand_do_randomize(page_addr, p_oob_src, p_oob_dst, p_nr->oob_size, p_nr->page_size); |
| /* we don't randomize the bad block marker */ |
| p_oob_dst[0] = bad_marker; |
| randomized_length += p_nr->oob_size; |
| DUMP_8_BYTES("OOB_DST", p_oob_dst); |
| } |
| |
| return randomized_length; |
| } |
| |
| /* |
| * UNIT TEST |
| */ |
| |
| #ifdef UNIT_TEST |
| |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <string.h> |
| |
| #include <assert.h> |
| |
| #define BLOCK_SIZE (1024*1024) |
| #define PAGE_SIZE (8192) |
| #define OOB_SIZE (32) |
| #define TOTAL_RANDOM_DATA_LENGTH (4096) |
| |
| static unsigned char g_random_buffer[TOTAL_RANDOM_DATA_LENGTH]; |
| |
| void mv_nand_randomizer_ut(void) |
| { |
| #define TEST_DATA_LENGTH (8) |
| const char chip_id[] = {0xEC, 0xD7, 0x94, 0x7A, 0x54, 0x43}; |
| unsigned int block_size = BLOCK_SIZE; |
| unsigned int page_size = PAGE_SIZE; |
| unsigned int oob_size = OOB_SIZE; |
| |
| unsigned int random_data_size; |
| const unsigned char *p_page0_random_data = NULL; |
| const unsigned char randomizer_buffer_date_expect[] = { |
| 0xCF, 0x7E, 0xD4, 0x20, 0x5F, 0x58, 0x38, 0x3A |
| }; |
| const unsigned char page0_random_data_expect[PAGE_SIZE] = { |
| #ifdef RANDOM_SEED |
| 0xD4, 0x3F, 0x1F, 0x50, 0x08, 0x3C, 0x06, 0x91 |
| #else /* !RANDOM_SEED */ |
| 0xCF, 0x7E, 0xD4, 0x20, 0x5F, 0x58, 0x38, 0x3A |
| #endif /* RANDOM_SEED */ |
| }; |
| unsigned char randomized_data[PAGE_SIZE]; |
| const unsigned char randomized_data_expect[PAGE_SIZE] = {0}; |
| const unsigned char unrandomized_data_expect[PAGE_SIZE] = { |
| 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF |
| }; |
| unsigned int unrandomized_page_addr = 0; |
| unsigned int randomized_page_addr = 0x1000; |
| int randomized = FALSE; |
| |
| random_data_size = mv_nand_randomizer_init( |
| chip_id, |
| sizeof(chip_id), |
| 0, |
| 0, |
| oob_size, |
| g_random_buffer, |
| TOTAL_RANDOM_DATA_LENGTH |
| ); |
| assert(TOTAL_RANDOM_DATA_LENGTH == random_data_size); |
| |
| assert(0 == memcmp(g_random_buffer, randomizer_buffer_date_expect, |
| TEST_DATA_LENGTH)); |
| |
| p_page0_random_data = page0_random_data_expect; |
| memcpy(randomized_data, unrandomized_data_expect, TEST_DATA_LENGTH); |
| randomize_by_xor(p_page0_random_data, |
| page0_random_data_expect, randomized_data, TEST_DATA_LENGTH); |
| assert(0 == memcmp(randomized_data, randomized_data_expect, TEST_DATA_LENGTH)); |
| |
| /* unrandomized page test */ |
| memcpy(randomized_data, unrandomized_data_expect, TEST_DATA_LENGTH); |
| randomized = mv_nand_randomizer_randomize(unrandomized_page_addr, |
| page0_random_data_expect, randomized_data, TEST_DATA_LENGTH, 0); |
| assert(!randomized && 0 == memcmp(randomized_data, unrandomized_data_expect, TEST_DATA_LENGTH)); |
| |
| memcpy(randomized_data, unrandomized_data_expect, TEST_DATA_LENGTH); |
| randomized = mv_nand_randomizer_randomize_page(unrandomized_page_addr, |
| page0_random_data_expect, NULL, randomized_data, NULL); |
| assert(!randomized && 0 == memcmp(randomized_data, unrandomized_data_expect, TEST_DATA_LENGTH)); |
| |
| /* randomized page test */ |
| memcpy(randomized_data, unrandomized_data_expect, TEST_DATA_LENGTH); |
| randomized = mv_nand_randomizer_randomize(randomized_page_addr, |
| page0_random_data_expect, randomized_data, TEST_DATA_LENGTH, 0); |
| assert(randomized && 0 == memcmp(randomized_data, randomized_data_expect, TEST_DATA_LENGTH)); |
| |
| memcpy(randomized_data, randomized_data_expect, TEST_DATA_LENGTH); |
| randomized = mv_nand_randomizer_randomize_page(randomized_page_addr, |
| page0_random_data_expect, NULL, randomized_data, NULL); |
| assert(randomized && 0 == memcmp(randomized_data, randomized_data_expect, TEST_DATA_LENGTH)); |
| |
| } |
| |
| |
| #define FILL_BYTE (0xAA) |
| static unsigned char g_data_src [PAGE_SIZE + OOB_SIZE]; |
| static unsigned char g_data_dst [PAGE_SIZE + OOB_SIZE]; |
| static unsigned char g_data_dst_prev [PAGE_SIZE + OOB_SIZE]; |
| void mv_nand_randomizer_page_ut(unsigned int page_addr) |
| { |
| /* randomized start from block 9 */ |
| #define RANDOMIZED_START_PAGE_ADDR (9 * BLOCK_SIZE / PAGE_SIZE) |
| int randomized = FALSE; |
| |
| memset(g_data_src, FILL_BYTE, sizeof(g_data_src)); |
| |
| randomized = mv_nand_randomizer_randomize_page(page_addr, |
| g_data_src, g_data_src + PAGE_SIZE, g_data_dst, g_data_dst + PAGE_SIZE); |
| |
| if (page_addr < RANDOMIZED_START_PAGE_ADDR) { |
| assert(!randomized); |
| } else { |
| assert(randomized |
| /* If the source fill with same data, then the randomized data would |
| * repeat in cycle of 4096 bytes, for the random data repeats in that |
| * cycle. |
| */ |
| && 0 == memcmp(g_data_dst, g_data_dst + 4096, 4096) |
| /* The bad block marker should not be randomized |
| */ |
| && FILL_BYTE == g_data_dst[PAGE_SIZE] |
| /* OOB data should repeat too (except the bad block marker). |
| */ |
| && 0 == memcmp(g_data_dst + 1, g_data_dst + PAGE_SIZE + 1, OOB_SIZE - 1) |
| /* The random start position should change every time, so the randomized |
| * data should change too. |
| */ |
| && 0 != memcmp(g_data_dst, g_data_dst_prev, PAGE_SIZE + OOB_SIZE) |
| ); |
| } |
| } |
| |
| void mv_nand_randomizer_full_page_ut(void) |
| { |
| unsigned int page_addr = 0; |
| for (page_addr = 0; page_addr < 512*4096; page_addr++) { |
| mv_nand_randomizer_page_ut(page_addr); |
| } |
| } |
| |
| void mv_nand_randomizer_dump_seed_pos_ut(void) |
| { |
| unsigned int page_addr = 0; |
| unsigned short *random_pos = (unsigned short *)g_random_buffer; |
| |
| for (page_addr = 0; page_addr < 8192; page_addr++) { |
| unsigned int random_start = mv_nand_get_page_start(page_addr); |
| unsigned int except_random_start = random_pos[page_addr % 128] & PAGE_START_POS_MASK(4096); |
| printf("0x%08X: %d \t %d\n", page_addr, random_start, except_random_start); |
| assert(random_start == except_random_start); |
| } |
| } |
| |
| int main(int argc, const char* argv[]) |
| { |
| mv_nand_randomizer_ut(); |
| mv_nand_randomizer_full_page_ut(); |
| mv_nand_randomizer_dump_seed_pos_ut(); |
| |
| return 0; |
| } |
| |
| #endif /* UNIT_TEST */ |