| // SPDX-License-Identifier: (GPL-2.0+ OR MIT) |
| /* |
| * Amlogic reserved information management |
| * |
| * Copyright (C) 2018 Amlogic Corporation |
| * |
| * Licensed under the GPL-2 or later. |
| * |
| */ |
| |
| #include <common.h> |
| #include <malloc.h> |
| #include <linux/errno.h> |
| #include <linux/io.h> |
| #include <linux/mtd/mtd.h> |
| #include <amlogic/aml_rsv.h> |
| #include <amlogic/aml_mtd.h> |
| #include <partition_table.h> |
| #include <amlogic/storage.h> |
| |
| #undef pr_info |
| #define pr_info printf |
| |
| extern int info_disprotect; |
| static struct meson_rsv_handler_t *rsv_handler; |
| |
| static struct free_node_t *get_free_node(struct meson_rsv_info_t *rsv_info) |
| { |
| struct meson_rsv_handler_t *handler = rsv_info->handler; |
| u32 index; |
| |
| index = |
| find_first_zero_bit((void *)&handler->fn_bitmask, |
| NAND_RSV_BLOCK_NUM); |
| if (index > NAND_RSV_BLOCK_NUM) { |
| pr_info("%s %d index :%d is greater than max rsv block num\n", |
| __func__, __LINE__, index); |
| return NULL; |
| } |
| WARN_ON(test_and_set_bit(index, (void *)&handler->fn_bitmask)); |
| |
| return handler->free_node[index]; |
| } |
| |
| static void release_free_node(struct meson_rsv_info_t *rsv_info, |
| struct free_node_t *free_node) |
| { |
| struct meson_rsv_handler_t *handler = rsv_info->handler; |
| u32 index = free_node->index; |
| |
| pr_info("%s %d: bitmask = 0x%llx\n", |
| __func__, __LINE__, handler->fn_bitmask); |
| if (index > NAND_RSV_BLOCK_NUM) { |
| pr_info("%s %d index :%d is greater than max rsv block num\n", |
| __func__, __LINE__, index); |
| return; |
| } |
| WARN_ON(!test_and_clear_bit(index, (void *)&handler->fn_bitmask)); |
| memset(free_node, 0, sizeof(struct free_node_t)); |
| free_node->index = index; |
| pr_info("%s %d: bitmask = 0x%llx\n", |
| __func__, __LINE__, handler->fn_bitmask); |
| } |
| |
| int meson_rsv_erase_protect(struct meson_rsv_handler_t *handler, |
| u32 block_addr) |
| { |
| if (handler->key && handler->key->valid) { |
| if (!(info_disprotect & DISPROTECT_KEY) && |
| block_addr >= handler->key->start && |
| block_addr < handler->key->end) |
| return -1; |
| } |
| if (handler->bbt && handler->bbt->valid) { |
| if ((!(info_disprotect & DISPROTECT_FBBT)) && |
| (block_addr >= handler->bbt->start) && |
| (block_addr < handler->bbt->end)) |
| return -1; |
| } |
| return 0; |
| } |
| |
| int meson_rsv_free(struct meson_rsv_info_t *rsv_info) |
| { |
| struct mtd_info *mtd = rsv_info->mtd; |
| struct free_node_t *tmp_node, *next_node = NULL; |
| int error = 0; |
| loff_t addr = 0; |
| struct erase_info erase_info; |
| |
| pr_info("free %s\n", rsv_info->name); |
| |
| if (rsv_info->valid) { |
| addr = rsv_info->nvalid->blk_addr; |
| addr *= mtd->erasesize; |
| memset(&erase_info, 0, sizeof(struct erase_info)); |
| erase_info.mtd = mtd; |
| erase_info.addr = addr; |
| erase_info.len = mtd->erasesize; |
| error = mtd->_erase(mtd, &erase_info); |
| pr_info("erasing valid info block: %llx\n", addr); |
| rsv_info->nvalid->blk_addr = -1; |
| rsv_info->nvalid->ec = -1; |
| rsv_info->nvalid->page_addr = 0; |
| rsv_info->nvalid->timestamp = 0; |
| rsv_info->nvalid->status = 0; |
| rsv_info->valid = 0; |
| } |
| tmp_node = rsv_info->nfree; |
| while (tmp_node) { |
| next_node = tmp_node->next; |
| release_free_node(rsv_info, tmp_node); |
| tmp_node = next_node; |
| } |
| rsv_info->nfree = NULL; |
| |
| return error; |
| } |
| |
| int meson_rsv_save(struct meson_rsv_info_t *rsv_info, u_char *buf) |
| { |
| struct mtd_info *mtd = rsv_info->mtd; |
| struct free_node_t *free_node, *temp_node; |
| struct erase_info erase_info; |
| int ret = 0, i = 1, pages_per_blk; |
| loff_t offset = 0; |
| |
| pages_per_blk = 1 << (mtd->erasesize_shift - mtd->writesize_shift); |
| if ((rsv_info->nvalid->status & POWER_ABNORMAL_FLAG) || |
| (rsv_info->nvalid->status & ECC_ABNORMAL_FLAG)) |
| rsv_info->nvalid->page_addr = pages_per_blk; |
| if (mtd->writesize < rsv_info->size) |
| i = (rsv_info->size + mtd->writesize - 1) / mtd->writesize; |
| pr_info("%s %d: %s, valid = %d, pages = %d\n", __func__, __LINE__, |
| rsv_info->name, rsv_info->valid, i); |
| RE_SEARCH: |
| if (rsv_info->valid) { |
| rsv_info->nvalid->page_addr += i; |
| if ((rsv_info->nvalid->page_addr + i) > pages_per_blk) { |
| if ((rsv_info->nvalid->page_addr - i) == |
| pages_per_blk) { |
| offset = rsv_info->nvalid->blk_addr; |
| offset *= mtd->erasesize; |
| erase_info.mtd = mtd; |
| erase_info.addr = offset; |
| erase_info.len = mtd->erasesize; |
| ret = mtd_erase(mtd, &erase_info); |
| rsv_info->nvalid->ec++; |
| pr_info("%s %d: erasing bad info block:0x%llx\n", |
| __func__, __LINE__, offset); |
| } |
| free_node = get_free_node(rsv_info); |
| if (!free_node) |
| return -ENOMEM; |
| /* set current valid node to free list */ |
| free_node->blk_addr = rsv_info->nvalid->blk_addr; |
| free_node->ec = rsv_info->nvalid->ec; |
| temp_node = rsv_info->nfree; |
| while (temp_node->next) |
| temp_node = temp_node->next; |
| temp_node->next = free_node; |
| /* get one node from free list and set to current */ |
| temp_node = rsv_info->nfree; |
| rsv_info->nvalid->blk_addr = temp_node->blk_addr; |
| rsv_info->nvalid->page_addr = 0; |
| rsv_info->nvalid->ec = temp_node->ec; |
| rsv_info->nvalid->timestamp++; |
| rsv_info->nfree = temp_node->next; |
| release_free_node(rsv_info, temp_node); |
| } |
| } else { |
| temp_node = rsv_info->nfree; |
| rsv_info->nvalid->blk_addr = temp_node->blk_addr; |
| rsv_info->nvalid->page_addr = 0; |
| rsv_info->nvalid->ec = temp_node->ec; |
| rsv_info->nvalid->timestamp++; |
| rsv_info->nfree = temp_node->next; |
| release_free_node(rsv_info, temp_node); |
| } |
| offset = rsv_info->nvalid->blk_addr; |
| offset *= mtd->erasesize; |
| offset += rsv_info->nvalid->page_addr * mtd->writesize; |
| if (rsv_info->nvalid->page_addr == 0) { |
| ret = mtd_block_isbad(mtd, offset); |
| if (ret) { |
| /** |
| * cause our rsv list includes bad block, |
| * so we need check it here and for fear |
| * of data lost. |
| */ |
| pr_info("%s %d: %s bad block here 0x%llx\n", |
| __func__, __LINE__, rsv_info->name, offset); |
| rsv_info->nvalid->page_addr = pages_per_blk - i; |
| goto RE_SEARCH; |
| } |
| memset(&erase_info, 0, sizeof(struct erase_info)); |
| erase_info.mtd = mtd; |
| erase_info.addr = offset; |
| erase_info.len = mtd->erasesize; |
| ret = mtd_erase(mtd, &erase_info); |
| if (ret) { |
| pr_info("%s %d %s erase failed at 0x%llx ,mark it bad\n", |
| __func__, __LINE__, rsv_info->name, offset); |
| mtd_block_markbad(mtd, offset); |
| return ret; |
| } |
| rsv_info->nvalid->ec++; |
| } |
| ret = meson_rsv_write(rsv_info, buf); |
| if (ret) { |
| pr_info("%s %d rsv info: %s save failed!\n", |
| __func__, __LINE__, rsv_info->name); |
| return ret; |
| } |
| rsv_info->valid = 1; |
| rsv_info->nvalid->status = 0; |
| return ret; |
| } |
| |
| int meson_rsv_write(struct meson_rsv_info_t *rsv_info, u_char *buf) |
| { |
| struct mtd_info *mtd = rsv_info->mtd; |
| struct oobinfo_t oobinfo; |
| struct mtd_oob_ops oob_ops; |
| size_t length = 0; |
| loff_t offset; |
| int ret = 0; |
| |
| offset = rsv_info->nvalid->blk_addr; |
| offset *= mtd->erasesize; |
| offset += rsv_info->nvalid->page_addr * mtd->writesize; |
| pr_info("%s %d write %s to 0x%llx\n", |
| __func__, __LINE__, rsv_info->name, offset); |
| memcpy(oobinfo.name, rsv_info->name, strlen(rsv_info->name)); |
| oobinfo.ec = rsv_info->nvalid->ec; |
| /* TODO: prevent the unrolling situation here */ |
| oobinfo.timestamp = rsv_info->nvalid->timestamp; |
| while (length < rsv_info->size) { |
| oob_ops.mode = MTD_OPS_AUTO_OOB; |
| oob_ops.len = min_t(u32, mtd->writesize, |
| (rsv_info->size - length)); |
| oob_ops.ooblen = sizeof(struct oobinfo_t); |
| oob_ops.ooboffs = 0; |
| oob_ops.datbuf = buf + length; |
| oob_ops.oobbuf = (u8 *)&oobinfo; |
| ret = mtd_write_oob(mtd, offset, &oob_ops); |
| if (ret) { |
| pr_info("fail to write %s to 0x%llx ret:%d\n", |
| rsv_info->name, offset, ret); |
| return -EIO; |
| } |
| offset += mtd->writesize; |
| length += oob_ops.len; |
| } |
| return ret; |
| } |
| |
| int meson_rsv_read(struct meson_rsv_info_t *rsv_info, u_char *buf) |
| { |
| struct mtd_info *mtd = rsv_info->mtd; |
| struct oobinfo_t oobinfo; |
| struct mtd_oob_ops oob_ops; |
| size_t length = 0; |
| loff_t offset; |
| int ret = 0; |
| |
| READ_RSV_AGAIN: |
| offset = rsv_info->nvalid->blk_addr; |
| offset *= mtd->erasesize; |
| offset += rsv_info->nvalid->page_addr * mtd->writesize; |
| pr_info("%s %d read %s from 0x%llx\n", |
| __func__, __LINE__, rsv_info->name, offset); |
| memset(buf, 0, rsv_info->size); |
| while (length < rsv_info->size) { |
| oob_ops.mode = MTD_OPS_AUTO_OOB; |
| oob_ops.len = min_t(u32, mtd->writesize, |
| (rsv_info->size - length)); |
| oob_ops.ooblen = sizeof(struct oobinfo_t); |
| oob_ops.ooboffs = 0; |
| oob_ops.datbuf = buf + length; |
| oob_ops.oobbuf = (u8 *)&oobinfo; |
| |
| memset((u8 *)&oobinfo, 0, oob_ops.ooblen); |
| ret = mtd_read_oob(mtd, offset, &oob_ops); |
| if (ret && (ret != -EUCLEAN)) { |
| pr_info("blk good but read failed: %llx, %d\n", |
| (u64)offset, ret); |
| ret = meson_rsv_scan(rsv_info); |
| if (ret) |
| return -EIO; |
| goto READ_RSV_AGAIN; |
| } |
| if (memcmp(oobinfo.name, rsv_info->name, |
| strlen(oobinfo.name))) |
| pr_info("invalid %s info in %llx:%s\n", |
| rsv_info->name, offset, oobinfo.name); |
| offset += mtd->writesize; |
| length += oob_ops.len; |
| } |
| return ret; |
| } |
| |
| int meson_rsv_erase(struct meson_rsv_info_t *rsv_info) |
| { |
| struct mtd_info *mtd = rsv_info->mtd; |
| struct free_node_t *temp_node = NULL; |
| int ret = 0; |
| struct erase_info erase_info; |
| |
| |
| printf("%s %d rsv erasing %s\n", |
| __func__, __LINE__, rsv_info->name); |
| |
| if (rsv_info->valid) { |
| memset(&erase_info, 0, sizeof(struct erase_info)); |
| erase_info.mtd = mtd; |
| erase_info.addr = rsv_info->nvalid->blk_addr* mtd->erasesize; |
| erase_info.len = mtd->erasesize; |
| ret = mtd_erase(mtd, &erase_info); |
| |
| printk("erasing valid info block: %llx \n", erase_info.addr); |
| rsv_info->nvalid->ec++; |
| rsv_info->nvalid->page_addr = 0; |
| rsv_info->nvalid->timestamp = 1; |
| } |
| |
| temp_node = rsv_info->nfree; |
| while (temp_node) { |
| memset(&erase_info, 0, sizeof(struct erase_info)); |
| erase_info.mtd = mtd; |
| erase_info.addr = temp_node->blk_addr* mtd->erasesize; |
| erase_info.len = mtd->erasesize; |
| ret = mtd_erase(mtd, &erase_info); |
| |
| printk("erasing valid info block: %llx \n", erase_info.addr); |
| rsv_info->nvalid->ec = -1; |
| temp_node->dirty_flag = 0; |
| temp_node = temp_node->next; |
| } |
| return ret; |
| } |
| |
| int meson_rsv_scan(struct meson_rsv_info_t *rsv_info) |
| { |
| struct mtd_info *mtd = rsv_info->mtd; |
| struct mtd_oob_ops oob_ops; |
| struct oobinfo_t oobinfo; |
| struct free_node_t *free_node, *temp_node; |
| loff_t offset; |
| u32 start, end; |
| int ret = 0, error, rsv_status, i, k; |
| |
| u8 scan_status; |
| u8 good_addr[256] = {0}; |
| u32 page_num, pages_per_blk; |
| |
| RE_RSV_INFO_EXT: |
| start = rsv_info->start; |
| end = rsv_info->end; |
| pr_info("%s:info size = 0x%x, start blk = %d, end blk = %d\n", |
| rsv_info->name, rsv_info->size, start, end); |
| do { |
| offset = start; |
| offset *= mtd->erasesize; |
| scan_status = 0; |
| RE_RSV_INFO: |
| oob_ops.mode = MTD_OPS_AUTO_OOB; |
| oob_ops.len = 0; |
| oob_ops.ooblen = sizeof(struct oobinfo_t); |
| oob_ops.ooboffs = 0; |
| oob_ops.datbuf = NULL; |
| oob_ops.oobbuf = (u8 *)&oobinfo; |
| memset((u8 *)&oobinfo, 0, sizeof(struct oobinfo_t)); |
| error = mtd_read_oob(mtd, offset, &oob_ops); |
| if (error && (error != -EUCLEAN)) { |
| pr_info("%s %d blk check good but read failed: %llx, %d\n", |
| __func__, __LINE__, (u64)offset, error); |
| offset += rsv_info->size; |
| if ((scan_status++ > 6) || |
| (!(offset % mtd->erasesize))) { |
| pr_info("ECC error, scan ONE block exit\n"); |
| scan_status = 0; |
| continue; |
| } |
| goto RE_RSV_INFO; |
| } |
| rsv_info->init = 1; |
| rsv_info->nvalid->status = 0; |
| if (!memcmp(oobinfo.name, rsv_info->name, |
| strlen(oobinfo.name))) { |
| rsv_info->valid = 1; |
| if (rsv_info->nvalid->blk_addr >= 0) { |
| free_node = get_free_node(rsv_info); |
| if (!free_node) |
| return -ENOMEM; |
| free_node->dirty_flag = 1; |
| if (oobinfo.timestamp > |
| rsv_info->nvalid->timestamp) { |
| free_node->blk_addr = |
| rsv_info->nvalid->blk_addr; |
| free_node->ec = rsv_info->nvalid->ec; |
| rsv_info->nvalid->blk_addr = start; |
| rsv_info->nvalid->page_addr = 0; |
| rsv_info->nvalid->ec = oobinfo.ec; |
| rsv_info->nvalid->timestamp = |
| oobinfo.timestamp; |
| } else { |
| free_node->blk_addr = start; |
| free_node->ec = oobinfo.ec; |
| } |
| if (!rsv_info->nfree) { |
| rsv_info->nfree = free_node; |
| } else { |
| temp_node = rsv_info->nfree; |
| while (temp_node->next) |
| temp_node = temp_node->next; |
| temp_node->next = free_node; |
| } |
| } else { |
| rsv_info->nvalid->blk_addr = start; |
| rsv_info->nvalid->page_addr = 0; |
| rsv_info->nvalid->ec = oobinfo.ec; |
| rsv_info->nvalid->timestamp = oobinfo.timestamp; |
| } |
| } else { |
| free_node = get_free_node(rsv_info); |
| if (!free_node) |
| return -ENOMEM; |
| free_node->blk_addr = start; |
| free_node->ec = oobinfo.ec; |
| if (!rsv_info->nfree) { |
| rsv_info->nfree = free_node; |
| } else { |
| temp_node = rsv_info->nfree; |
| while (temp_node->next) |
| temp_node = temp_node->next; |
| temp_node->next = free_node; |
| } |
| } |
| } while ((++start) < end); |
| |
| printf("%s blk = %d, ec = %d, page = %d, timestamp = %d\n", |
| rsv_info->name, rsv_info->nvalid->blk_addr, rsv_info->nvalid->ec, |
| rsv_info->nvalid->page_addr, rsv_info->nvalid->timestamp); |
| printf("%s free list: \n", rsv_info->name); |
| temp_node = rsv_info->nfree; |
| while (temp_node) { |
| pr_info("block num = %d, ec = %d, dirty_flag = %d\n", |
| temp_node->blk_addr, |
| temp_node->ec, |
| temp_node->dirty_flag); |
| temp_node = temp_node->next; |
| } |
| /** |
| * step 2, find the newest in the block |
| * watch out here, cause erase size and write size must be |
| * power of 2, and write size must equal page size. |
| */ |
| pages_per_blk = 1 << (mtd->erasesize_shift - mtd->writesize_shift); |
| page_num = rsv_info->size >> mtd->writesize_shift; |
| if (!page_num) |
| page_num++; |
| if (rsv_info->valid == 1) { |
| pr_info("%s %d selecting in block: %d\n", |
| __func__, __LINE__, rsv_info->nvalid->blk_addr); |
| oob_ops.mode = MTD_OPS_AUTO_OOB; |
| oob_ops.len = 0; |
| oob_ops.ooblen = sizeof(struct oobinfo_t); |
| oob_ops.ooboffs = 0; |
| oob_ops.datbuf = NULL; |
| oob_ops.oobbuf = (u8 *)&oobinfo; |
| for (i = 0; i < pages_per_blk; i++) { |
| memset((u8 *)&oobinfo, 0, oob_ops.ooblen); |
| offset = rsv_info->nvalid->blk_addr; |
| offset *= mtd->erasesize; |
| offset += mtd->writesize * i; |
| error = mtd_read_oob(mtd, offset, &oob_ops); |
| if (error && error != -EUCLEAN) { |
| pr_info("%s %d blk good but read failed:%llx, %d\n", |
| __func__, __LINE__, (u64)offset, error); |
| rsv_info->nvalid->status |= ECC_ABNORMAL_FLAG; |
| ret = -1; |
| continue; |
| } |
| if (!memcmp(oobinfo.name, rsv_info->name, |
| strlen(oobinfo.name))) { |
| good_addr[i] = 1; |
| rsv_info->nvalid->page_addr = i; |
| } else { |
| break; |
| } |
| } |
| } |
| if (mtd->writesize < rsv_info->size && |
| rsv_info->valid == 1) { |
| i = rsv_info->nvalid->page_addr; |
| if (((i + 1) % page_num) != 0) { |
| ret = -1; |
| rsv_info->nvalid->status |= POWER_ABNORMAL_FLAG; |
| pr_info("find %s incomplete\n", rsv_info->name); |
| } |
| pr_info("%s %d page_num %d\n", __func__, __LINE__, page_num); |
| if (ret == -1) { |
| for (i = 0; i < (pages_per_blk / page_num); i++) { |
| rsv_status = 0; |
| for (k = 0; k < page_num; k++) { |
| if (!good_addr[k + i * page_num]) { |
| rsv_status = 1; |
| break; |
| } |
| } |
| if (!rsv_status) { |
| pr_info("find %d page ok\n", |
| i * page_num); |
| rsv_info->nvalid->page_addr = |
| k + i * page_num - 1; |
| ret = 0; |
| } |
| } |
| } |
| if (ret == -1) { |
| rsv_info->nvalid->status = 0; |
| meson_rsv_free(rsv_info); |
| goto RE_RSV_INFO_EXT; |
| } |
| i = (rsv_info->size + mtd->writesize - 1) / mtd->writesize; |
| rsv_info->nvalid->page_addr -= (i - 1); |
| } |
| if (rsv_info->valid != 1) |
| ret = -1; |
| offset = rsv_info->nvalid->blk_addr; |
| offset *= mtd->erasesize; |
| offset += rsv_info->nvalid->page_addr * mtd->writesize; |
| pr_info("%s valid address 0x%llx\n", rsv_info->name, offset); |
| return ret; |
| } |
| |
| int meson_rsv_check(struct meson_rsv_info_t *rsv_info) |
| { |
| int ret = 0; |
| |
| ret = meson_rsv_scan(rsv_info); |
| if (ret) |
| pr_info("%s %d %s info check failed ret %d\n", |
| __func__, __LINE__, rsv_info->name, ret); |
| if (!rsv_info->valid) { |
| pr_info("%s %d no %s info exist\n", |
| __func__, __LINE__, rsv_info->name); |
| ret = 1; |
| } |
| return ret; |
| } |
| |
| int meson_rsv_init(struct mtd_info *mtd, |
| struct meson_rsv_handler_t *handler) |
| { |
| int i, ret = 0; |
| u32 pages_per_blk_shift, start, vernier; |
| |
| pages_per_blk_shift = mtd->erasesize_shift - mtd->writesize_shift; |
| start = BOOT_TOTAL_PAGES >> pages_per_blk_shift; |
| start += NAND_GAP_BLOCK_NUM; |
| vernier = start; |
| handler->fn_bitmask = 0; |
| for (i = 0; i < NAND_RSV_BLOCK_NUM; i++) { |
| handler->free_node[i] = |
| kzalloc(sizeof(struct free_node_t), GFP_KERNEL); |
| if (!handler->free_node[i]) { |
| ret = -ENOMEM; |
| goto error; |
| } |
| memset(handler->free_node[i], 0, sizeof(struct free_node_t)); |
| handler->free_node[i]->index = i; |
| } |
| |
| handler->bbt = |
| kzalloc(sizeof(*handler->bbt), GFP_KERNEL); |
| if (!handler->bbt) { |
| ret = -ENOMEM; |
| goto error; |
| } |
| handler->bbt->nvalid = |
| kzalloc(sizeof(*handler->bbt->nvalid), GFP_KERNEL); |
| if (!handler->bbt->nvalid) { |
| ret = -ENOMEM; |
| goto error; |
| } |
| handler->bbt->mtd = mtd; |
| handler->bbt->start = vernier; |
| handler->bbt->end = vernier + NAND_BBT_BLOCK_NUM; |
| handler->bbt->nvalid->blk_addr = -1; |
| handler->bbt->size = mtd->size >> mtd->erasesize_shift; |
| handler->bbt->handler = handler; |
| memcpy(handler->bbt->name, BBT_NAND_MAGIC, 4); |
| vernier += NAND_BBT_BLOCK_NUM; |
| #ifndef CONFIG_ENV_IS_IN_NAND |
| handler->env = |
| kzalloc(sizeof(*handler->env), GFP_KERNEL); |
| if (!handler->env) { |
| ret = -ENOMEM; |
| goto error; |
| } |
| handler->env->nvalid = |
| kzalloc(sizeof(*handler->env->nvalid), GFP_KERNEL); |
| if (!handler->env->nvalid) { |
| ret = -ENOMEM; |
| goto error; |
| } |
| handler->env->mtd = mtd; |
| handler->env->start = vernier; |
| handler->env->end = vernier + NAND_ENV_BLOCK_NUM; |
| handler->env->nvalid->blk_addr = -1; |
| handler->env->size = CONFIG_ENV_SIZE; |
| handler->env->handler = handler; |
| memcpy(handler->env->name, ENV_NAND_MAGIC, 4); |
| vernier += NAND_ENV_BLOCK_NUM; |
| #endif |
| handler->key = |
| kzalloc(sizeof(*handler->key), GFP_KERNEL); |
| if (!handler->key) { |
| ret = -ENOMEM; |
| goto error; |
| } |
| handler->key->nvalid = |
| kzalloc(sizeof(*handler->key->nvalid), GFP_KERNEL); |
| if (!handler->key->nvalid) { |
| ret = -ENOMEM; |
| goto error; |
| } |
| handler->key->mtd = mtd; |
| handler->key->start = vernier; |
| handler->key->end = vernier + NAND_KEY_BLOCK_NUM; |
| handler->key->nvalid->blk_addr = -1; |
| handler->key->size = 0; |
| handler->key->handler = handler; |
| memcpy(handler->key->name, KEY_NAND_MAGIC, 4); |
| vernier += NAND_KEY_BLOCK_NUM; |
| |
| handler->dtb = |
| kzalloc(sizeof(*handler->dtb), GFP_KERNEL); |
| if (!handler->dtb) { |
| ret = -ENOMEM; |
| goto error; |
| } |
| handler->dtb->nvalid = |
| kzalloc(sizeof(*handler->dtb->nvalid), GFP_KERNEL); |
| if (!handler->dtb->nvalid) { |
| ret = -ENOMEM; |
| goto error; |
| } |
| handler->dtb->mtd = mtd; |
| handler->dtb->start = vernier; |
| handler->dtb->end = vernier + NAND_DTB_BLOCK_NUM; |
| handler->dtb->nvalid->blk_addr = -1; |
| handler->dtb->size = 0; |
| handler->dtb->handler = handler; |
| memcpy(handler->dtb->name, DTB_NAND_MAGIC, 4); |
| vernier += NAND_DTB_BLOCK_NUM; |
| |
| if (mtd->erasesize < 0x40000) { |
| handler->key->size = mtd->erasesize >> 2; |
| handler->dtb->size = mtd->erasesize; |
| } else { |
| handler->key->size = 0x40000; |
| handler->dtb->size = 0x40000; |
| } |
| |
| if ((vernier - start) > NAND_RSV_BLOCK_NUM) { |
| pr_info("ERROR: total blk number is over the limit\n"); |
| ret = -ENOMEM; |
| goto error; |
| } |
| rsv_handler = handler; |
| |
| printf("bbt_start=%d, size:0x%x\n", handler->bbt->start,handler->bbt->size); |
| #ifndef CONFIG_ENV_IS_IN_NAND |
| printf("env_start=%d, size:0x%x\n", handler->env->start,handler->env->size); |
| #endif |
| printf("key_start=%d, size:0x%x\n", handler->key->start,handler->key->size); |
| printf("dtb_start=%d, size:0x%x\n", handler->dtb->start,handler->dtb->size); |
| printf("\n"); |
| |
| return ret; |
| error: |
| for (i = 0; i < NAND_RSV_BLOCK_NUM; i++) { |
| free(handler->free_node[i]); |
| handler->free_node[i] = NULL; |
| } |
| free(handler->bbt->nvalid); |
| free(handler->bbt); |
| handler->bbt = NULL; |
| free(handler->env->nvalid); |
| free(handler->env); |
| handler->env = NULL; |
| free(handler->key->nvalid); |
| free(handler->key); |
| handler->key = NULL; |
| free(handler->dtb->nvalid); |
| free(handler->dtb); |
| handler->dtb = NULL; |
| return ret; |
| } |
| |
| int meson_rsv_bbt_read(u_char *dest, size_t size) |
| { |
| u_char *temp; |
| size_t len; |
| int ret; |
| |
| if (!rsv_handler || |
| !rsv_handler->bbt) { |
| pr_info("%s %d rsv info not inited yet!\n", |
| __func__, __LINE__); |
| return 1; |
| } |
| if (!dest || size == 0) { |
| pr_info("%s %d parameter error %p %ld\n", |
| __func__, __LINE__, dest, size); |
| return 1; |
| } |
| len = rsv_handler->bbt->size; |
| temp = kzalloc(len, GFP_KERNEL); |
| memset(temp, 0, len); |
| ret = meson_rsv_read(rsv_handler->bbt, temp); |
| memcpy(dest, temp, len > size ? size : len); |
| pr_info("%s %d read 0x%lx bytes from bbt, ret %d\n", |
| __func__, __LINE__, len > size ? size : len, ret); |
| free(temp); |
| return ret; |
| } |
| |
| int meson_rsv_key_read(u_char *dest, size_t size) |
| { |
| u_char *temp; |
| size_t len; |
| int ret; |
| |
| if (!rsv_handler || |
| !rsv_handler->key) { |
| pr_info("%s %d rsv info not inited yet!\n", |
| __func__, __LINE__); |
| return 1; |
| } |
| if (!dest || size == 0) { |
| pr_info("%s %d parameter error %p %ld\n", |
| __func__, __LINE__, dest, size); |
| return 1; |
| } |
| len = rsv_handler->key->size; |
| temp = kzalloc(len, GFP_KERNEL); |
| memset(temp, 0, len); |
| ret = meson_rsv_read(rsv_handler->key, temp); |
| memcpy(dest, temp, len > size ? size : len); |
| pr_info("%s %d read 0x%lx bytes from key, ret %d\n", |
| __func__, __LINE__, len > size ? size : len, ret); |
| free(temp); |
| return ret; |
| } |
| |
| int meson_rsv_env_read(u_char *dest, size_t size) |
| { |
| u_char *temp; |
| size_t len; |
| int ret; |
| |
| if (!rsv_handler || |
| !rsv_handler->env) { |
| pr_info("%s %d rsv info not inited yet!\n", |
| __func__, __LINE__); |
| return 1; |
| } |
| if (!dest || size == 0) { |
| pr_info("%s %d parameter error %p %ld\n", |
| __func__, __LINE__, dest, size); |
| return 1; |
| } |
| len = rsv_handler->env->size; |
| temp = kzalloc(len, GFP_KERNEL); |
| memset(temp, 0, len); |
| ret = meson_rsv_read(rsv_handler->env, temp); |
| memcpy(dest, temp, len > size ? size : len); |
| pr_info("%s %d read 0x%lx bytes from env, ret %d\n", |
| __func__, __LINE__, len > size ? size : len, ret); |
| free(temp); |
| return ret; |
| } |
| |
| int meson_rsv_dtb_read(u_char *dest, size_t size) |
| { |
| u_char *temp; |
| size_t len; |
| int ret; |
| |
| if (!rsv_handler || |
| !rsv_handler->dtb) { |
| pr_info("%s %d rsv info not inited yet!\n", |
| __func__, __LINE__); |
| return 1; |
| } |
| if (!dest || size == 0) { |
| pr_info("%s %d parameter error %p %ld\n", |
| __func__, __LINE__, dest, size); |
| return 1; |
| } |
| len = rsv_handler->dtb->size; |
| temp = kzalloc(len, GFP_KERNEL); |
| memset(temp, 0, len); |
| ret = meson_rsv_read(rsv_handler->dtb, temp); |
| memcpy(dest, temp, len > size ? size : len); |
| pr_info("%s %d read 0x%lx bytes from dtb, ret %d\n", |
| __func__, __LINE__, len > size ? size : len, ret); |
| free(temp); |
| return ret; |
| } |
| |
| /*update bbt*/ |
| int meson_rsv_bbt_write(u_char *source, size_t size) |
| { |
| u_char *temp; |
| size_t len; |
| int ret; |
| |
| if (!rsv_handler || |
| !rsv_handler->bbt) { |
| pr_info("%s %d rsv info not inited yet!\n", |
| __func__, __LINE__); |
| return 1; |
| } |
| if (!source || size == 0) { |
| pr_info("%s %d parameter error %p %ld\n", |
| __func__, __LINE__, source, size); |
| return 1; |
| } |
| len = rsv_handler->bbt->size; |
| temp = kzalloc(len, GFP_KERNEL); |
| memset(temp, 0, len); |
| memcpy(temp, source, len > size ? size : len); |
| ret = meson_rsv_save(rsv_handler->bbt, temp); |
| pr_info("%s %d write 0x%lx bytes to bbt, ret %d\n", |
| __func__, __LINE__, len > size ? size : len, ret); |
| free(temp); |
| return ret; |
| } |
| |
| int meson_rsv_key_write(u_char *source, size_t size) |
| { |
| u_char *temp; |
| size_t len; |
| int ret; |
| |
| if (!rsv_handler || |
| !rsv_handler->key) { |
| pr_info("%s %d rsv info not inited yet!\n", |
| __func__, __LINE__); |
| return 1; |
| } |
| if (!source || size == 0) { |
| pr_info("%s %d parameter error %p %ld\n", |
| __func__, __LINE__, source, size); |
| return 1; |
| } |
| len = rsv_handler->key->size; |
| temp = kzalloc(len, GFP_KERNEL); |
| memset(temp, 0, len); |
| memcpy(temp, source, len > size ? size : len); |
| ret = meson_rsv_save(rsv_handler->key, temp); |
| pr_info("%s %d write 0x%lx bytes to key, ret %d\n", |
| __func__, __LINE__, len > size ? size : len, ret); |
| free(temp); |
| return ret; |
| } |
| |
| int meson_rsv_env_write(u_char *source, size_t size) |
| { |
| u_char *temp; |
| size_t len; |
| int ret; |
| |
| if (!rsv_handler || |
| !rsv_handler->env) { |
| pr_info("%s %d rsv info has not inited yet!\n", |
| __func__, __LINE__); |
| return 1; |
| } |
| if (!source || size == 0) { |
| pr_info("%s %d parameter error %p %ld\n", |
| __func__, __LINE__, source, size); |
| return 1; |
| } |
| len = rsv_handler->env->size; |
| temp = kzalloc(len, GFP_KERNEL); |
| memset(temp, 0, len); |
| memcpy(temp, source, len > size ? size : len); |
| ret = meson_rsv_save(rsv_handler->env, temp); |
| pr_info("%s %d write 0x%lx bytes to env, ret %d\n", |
| __func__, __LINE__, len > size ? size : len, ret); |
| free(temp); |
| return ret; |
| } |
| |
| int meson_rsv_dtb_write(u_char *source, size_t size) |
| { |
| u_char *temp; |
| size_t len; |
| int ret; |
| |
| if (!rsv_handler || |
| !rsv_handler->dtb) { |
| pr_info("%s %d rsv info has not inited yet!\n", |
| __func__, __LINE__); |
| return 1; |
| } |
| if (!source || size == 0) { |
| pr_info("%s %d parameter error %p %ld\n", |
| __func__, __LINE__, source, size); |
| return 1; |
| } |
| len = rsv_handler->dtb->size; |
| temp = kzalloc(len, GFP_KERNEL); |
| memset(temp, 0, len); |
| memcpy(temp, source, len > size ? size : len); |
| ret = meson_rsv_save(rsv_handler->dtb, temp); |
| pr_info("%s %d write 0x%lx bytes to dtb, ret %d\n", |
| __func__, __LINE__, len > size ? size : len, ret); |
| free(temp); |
| return ret; |
| } |
| |
| u32 meson_rsv_bbt_size(void) |
| { |
| if (!rsv_handler || |
| !rsv_handler->bbt) { |
| pr_info("%s %d rsv info has not inited yet!\n", |
| __func__, __LINE__); |
| return 0; |
| } |
| return rsv_handler->bbt->size; |
| } |
| |
| u32 meson_rsv_key_size(void) |
| { |
| if (!rsv_handler || |
| !rsv_handler->key) { |
| pr_info("%s %d rsv info has not inited yet!\n", |
| __func__, __LINE__); |
| return 0; |
| } |
| return rsv_handler->key->size; |
| } |
| |
| u32 meson_rsv_env_size(void) |
| { |
| if (!rsv_handler || |
| !rsv_handler->env) { |
| pr_info("%s %d rsv info has not inited yet!\n", |
| __func__, __LINE__); |
| return 0; |
| } |
| return rsv_handler->env->size; |
| } |
| |
| u32 meson_rsv_dtb_size(void) |
| { |
| if (!rsv_handler || |
| !rsv_handler->dtb) { |
| pr_info("%s %d rsv info has not inited yet!\n", |
| __func__, __LINE__); |
| return 0; |
| } |
| return rsv_handler->dtb->size; |
| } |
| |
| int meson_rsv_bbt_erase(void) |
| { |
| if (!rsv_handler || |
| !rsv_handler->bbt) { |
| pr_info("%s %d rsv info has not inited yet!\n", |
| __func__, __LINE__); |
| return 1; |
| } |
| return meson_rsv_erase(rsv_handler->bbt); |
| } |
| |
| int meson_rsv_key_erase(void) |
| { |
| if (!rsv_handler || |
| !rsv_handler->key) { |
| pr_info("%s %d rsv info has not inited yet!\n", |
| __func__, __LINE__); |
| return 1; |
| } |
| return meson_rsv_erase(rsv_handler->key); |
| } |
| |
| int meson_rsv_env_erase(void) |
| { |
| if (!rsv_handler || |
| !rsv_handler->env) { |
| pr_info("%s %d rsv info has not inited yet!\n", |
| __func__, __LINE__); |
| return 1; |
| } |
| return meson_rsv_erase(rsv_handler->env); |
| } |
| |
| int meson_rsv_dtb_erase(void) |
| { |
| if (!rsv_handler || |
| !rsv_handler->dtb) { |
| pr_info("%s %d rsv info has not inited yet!\n", |
| __func__, __LINE__); |
| return 1; |
| } |
| |
| return meson_rsv_erase(rsv_handler->dtb); |
| |
| } |