AMLOGIC MTD MESON8B DRIVER
M: Yonghui Yu <yonghui.yu@amlogic.com>
F: drivers/amlogic/mtd_meson8b/
+M: Yi.Zeng <yi.zeng@amlogic.com>
+F: drivers/amlogic/mtd_meson8b/nand_key.c
+F: drivers/amlogic/mtd_meson8b/secure_storage.c
AMLOGIC PM/SLEEP M8B DRIVER SUPPORT
M: Qiufang Dai <qiufang.dai@amlogic.com>
obj-$(CONFIG_AMLOGIC_M8B_NAND) += aml_nand.o \
m3_nand.o \
env_old.o \
- key_old.o \
+ nand_key.o \
nand_flash.o \
mtd_driver.o \
- new_nand.o
+ new_nand.o \
+ secure_storage.o
uint32_t read_info;
uint32_t new_type;
uint32_t page_per_blk;
- uint32_t xlc;
+ uint32_t secure_block;
+ uint32_t secure_start_blk;
+ //uint32_t xlc;
uint32_t ce_mask;
- uint32_t boot_num;
- uint32_t each_boot_pages;
- uint32_t rsv[2];
+ //uint32_t boot_num;
+ //uint32_t each_boot_pages;
+ //uint32_t rsv[2];
+ uint32_t secure_end_blk;
/* add new below, */
+ uint32_t reserved;
};
/*max size is 384 bytes*/
struct _nand_page0 {
struct nand_setup nand_setup;
unsigned char page_list[16];
- struct _nand_cmd retry_usr[32];
+ struct _nand_cmd retry_usr[164];
struct _ext_info ext_info;
};
#define NAND_MINI_PART_BLOCKNUM 2
+#define NAND_KEY_SAVE_MULTI_BLOCK //key save in multi block same time
+
+
struct aml_nand_read_retry {
u8 flag;
u8 reg_cnt;
int start_block;
int end_block;
};
-
+struct aml_nandsecure_info_t {
+ struct mtd_info *mtd;
+ struct env_valid_node_t *secure_valid_node;
+ struct env_free_node_t *secure_free_node;
+ u_char secure_valid;
+ u_char secure_init;
+ u_char part_num_before_sys;
+ int start_block;
+ int end_block;
+};
struct aml_nand_chip {
struct mtd_info *mtd;
struct nand_chip chip;
struct aml_nandenv_info_t *aml_nandenv_info;
unsigned int update_env_flag;
struct aml_nandkey_info_t *aml_nandkey_info;
+ struct aml_nandsecure_info_t *aml_nandsecure_info;
+ unsigned int secure_protect;
#else
struct aml_nandrsv_info_t *aml_nandbbt_info;
struct aml_nandrsv_info_t *aml_nandenv_info;
int aml_nand_rsv_erase_protect(struct mtd_info *mtd, unsigned int block_addr);
-int aml_nand_save_key(struct mtd_info *mtd, u_char *buf);
+//int aml_nand_save_key(struct mtd_info *mtd, u_char *buf);
-int aml_nand_read_key(struct mtd_info *mtd, size_t offset, u_char *buf);
+//int aml_nand_read_key(struct mtd_info *mtd, size_t offset, u_char *buf);
-int aml_nand_key_check(struct mtd_info *mtd);
+//int aml_nand_key_check(struct mtd_info *mtd);
/*int aml_nand_free_valid_env(struct mtd_info *mtd);*/
/*external defined variable*/
extern int info_disprotect;
extern struct aml_nand_flash_dev aml_nand_flash_ids[];
+extern int secure_device_init(struct mtd_info *mtd);
+extern bool meson_secure_enabled(void);
void aml_nand_new_nand_param_init(struct mtd_info *mtd,
struct aml_nand_flash_dev *type);
unsigned int bad_blk_addr[128];
#ifdef CONFIG_AMLOGIC_M8B_NANDKEY
uint64_t key_block;
+ uint64_t secure_block = 0;
#endif
mini_part_size =
#ifdef CONFIG_AMLOGIC_M8B_NANDKEY
key_block = aml_chip->aml_nandkey_info->end_block
- aml_chip->aml_nandkey_info->start_block + 1;
+ if (meson_secure_enabled())
+ secure_block =
+ aml_chip->aml_nandsecure_info->end_block
+ - aml_chip->aml_nandsecure_info->start_block + 1;
#endif
} else {
temp_parts->size -= key_block*mtd->erasesize;
}
+ if (meson_secure_enabled())
+ temp_parts->size -= secure_block*mtd->erasesize;
#endif
}
err = aml_key_init(aml_chip);
if (err)
pr_err("invalid nand key\n");
-
+ if (meson_secure_enabled()) {
+ err = secure_device_init(mtd);
+ if (err)
+ pr_err("invalid secure device\n");
+ }
#endif /* CONFIG_AMLOGIC_M8B_NANDKEY */
#endif /* CONFIG_AMLOGIC_M8B_NAND */
{
if (!_aml_rsv_isprotect())
return 0;
- pr_err(" %s() %d: fixme\n", __func__, __LINE__);
#if 0
+ pr_err(" %s() %d: fixme\n", __func__, __LINE__);
+
if (aml_chip->aml_nandkey_info != NULL) {
if (aml_chip->aml_nandkey_info->valid)
if ((block_addr >= aml_chip->aml_nandkey_info->start_block)
+++ /dev/null
-/*
- * drivers/amlogic/mtd_old/key_old.c
- *
- * Copyright (C) 2017 Amlogic, Inc. 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 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.
- *
- */
-#include "key_old.h"
-#include "env_old.h"
-
-static struct aml_nand_chip *aml_chip_key;
-
-int aml_nand_key_check(struct mtd_info *mtd)
-{
- int ret = 0;
-#if 0
- struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd);
-
- ret = aml_nand_scan_rsv_info(mtd, aml_chip->aml_nandkey_info);
- if ((ret != 0) && ((ret != (-1))))
- pr_info("%s %d\n", __func__, __LINE__);
-
- if (aml_chip->aml_nandkey_info->valid == 0)
- pr_info("%s %d NO key exist\n", __func__, __LINE__);
-#endif
- pr_info("%s() %d\n", __func__, __LINE__);
- return ret;
-}
-
-int aml_nand_key_init(struct mtd_info *mtd)
-{
- int ret = 0, error;
- struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd);
- //struct nand_chip *chip = &aml_chip->chip;
- struct aml_nandkey_info_t *key_info;
- int remain_start_block, remain_total_block;
- int remain_block = 0, bad_blk_cnt = 0;
- int phys_erase_shift, max_key_blk;
- loff_t offset;
-
- aml_chip->aml_nandkey_info =
- kzalloc(sizeof(struct aml_nandkey_info_t),
- GFP_KERNEL);
- if (aml_chip->aml_nandkey_info == NULL) {
- pr_err("%s() %d: ENOMEM\n", __func__, __LINE__);
- return -ENOMEM;
- }
- key_info = aml_chip->aml_nandkey_info;
-
- key_info->env_init = 0;
- key_info->mtd = mtd;
- /*key_info->env_valid_node->phy_blk_addr = -1;*/
-
-
- phys_erase_shift = fls(mtd->erasesize) - 1;
- max_key_blk = (NAND_MINIKEY_PART_SIZE >> phys_erase_shift);
- if (max_key_blk < NAND_MINIKEY_PART_BLOCKNUM)
- max_key_blk = NAND_MINIKEY_PART_BLOCKNUM;
-
- offset = mtd->size - mtd->erasesize;
- remain_start_block = (int)(offset >> phys_erase_shift);
- remain_total_block = REMAIN_TAIL_BLOCK_NUM;
- key_info->start_block = remain_start_block;
- key_info->end_block = remain_start_block;
- bad_blk_cnt = 0;
- do {
- offset = mtd->erasesize;
- offset *= remain_start_block;
- error = mtd->_block_isbad(mtd, offset);
- if (error) {
- key_info->nand_bbt_info.nand_bbt[bad_blk_cnt++] =
- remain_start_block;
- if (bad_blk_cnt >= MAX_BAD_BLK_NUM) {
- pr_err("bad block too much,%s\n", __func__);
- return -ENOMEM;
- }
- key_info->start_block--;
- remain_start_block--;
- continue;
- }
- remain_start_block--;
- } while (++remain_block < remain_total_block);
-
- key_info->start_block -= (remain_block-1);
-
- pr_info("%s()%d: key [%d,%d]\n", __func__, __LINE__,
- key_info->start_block, key_info->end_block);
-
- return ret;
-}
-
-int aml_key_init(struct aml_nand_chip *aml_chip)
-{
- int ret = 0;
- struct mtd_info *mtd = aml_chip->mtd;
- /* avoid null */
- aml_chip_key = aml_chip;
-
- ret = aml_nand_key_init(mtd);
- /* fixme, NOOP key */
- /* storage_ops_read(amlnf_key_read); */
- /* storage_ops_write(amlnf_key_write); */
- return ret;
-}
-
**/
u32 devops_len = (BOOT_PAGES_PER_COPY-1) * aml_chip->page_size;
/* struct hw_controller *controller = &(aml_chip->controller); */
- int i, nand_read_info;
+ int i, nand_read_info, ran_mode = 0;
u32 en_slc, configure_data;
- u32 boot_num = 1, each_boot_pages;
+ u32 boot_num = 1;
u32 valid_pages = BOOT_TOTAL_PAGES;
+ unsigned char chip_num = 0, plane_num = 0, micron_nand = 0;
+ int k, secure_block, valid_chip_num = 0;
struct _nand_page0 *p_nand_page0 = NULL;
struct _ext_info *p_ext_info = NULL;
struct nand_setup *p_nand_setup = NULL;
+ struct aml_nand_chip *aml_chip_device1 = NULL;
+ struct aml_nand_platform *plat = NULL;
p_nand_page0 = (struct _nand_page0 *) page0_buf;
p_nand_setup = &p_nand_page0->nand_setup;
p_ext_info = &p_nand_page0->ext_info;
+ for (i = 0; i < aml_nand_mid_device.dev_num; i++) {
+ plat = &aml_nand_mid_device.aml_nand_platform[i];
+ if (!strncmp((char *)plat->name, NAND_NORMAL_NAME,
+ strlen((const char *)NAND_NORMAL_NAME))) {
+ aml_chip_device1 = plat->aml_chip;
+ break;
+ }
+ }
configure_data = NFC_CMD_N2M(aml_chip->ran_mode,
aml_chip->bch_mode, 0, (chip->ecc.size >> 3),
chip->ecc.steps);
0,
NAND_PAGELIST_CNT);
/* chip_num occupy the lowest 2 bit */
- nand_read_info = controller->chip_num;
-
+ /* nand_read_info = controller->chip_num; */
+ nand_read_info = 1;
/*
*make it
*1)calu the number of boot saved and pages each boot needs.
else
break;
}
- each_boot_pages = valid_pages/boot_num;
+ /* each_boot_pages = valid_pages/boot_num; */
p_ext_info->read_info = nand_read_info;
p_ext_info->page_per_blk = aml_chip->block_size / aml_chip->page_size;
/* fixme, only ce0 is enabled! */
p_ext_info->ce_mask = 0x01;
+ p_ext_info->new_type = aml_chip->new_nand_info.type;
/* xlc is not in using for now */
- p_ext_info->xlc = 1;
- p_ext_info->boot_num = boot_num;
- p_ext_info->each_boot_pages = each_boot_pages;
-
+ /* p_ext_info->xlc = 1; */
+ /* p_ext_info->boot_num = boot_num; */
+ /* p_ext_info->each_boot_pages = each_boot_pages; */
+#if 1
+ if (meson_secure_enabled()) {
+ p_ext_info->secure_start_blk =
+ aml_chip_device1->aml_nandsecure_info->start_block;
+ p_ext_info->secure_end_blk =
+ aml_chip_device1->aml_nandsecure_info->end_block;
+
+ valid_chip_num = 0;
+ for (k = 0; k < 0; k++) {
+ if (aml_chip_device1->valid_chip[k])
+ valid_chip_num++;
+ }
+ chip_num = valid_chip_num;
+ if (aml_chip_device1->plane_num == 2)
+ plane_num = 1;
+ ran_mode = aml_chip_device1->ran_mode;
+ if ((aml_chip_device1->mfr_type == NAND_MFR_MICRON) ||
+ (aml_chip_device1->mfr_type == NAND_MFR_INTEL))
+ micron_nand = 1;
+ nand_read_info = chip_num | (plane_num << 2) |
+ (ran_mode << 3) | (micron_nand << 4);
+ p_ext_info->read_info = nand_read_info;
+ secure_block =
+ aml_chip_device1->aml_nandsecure_info->start_block;
+ p_ext_info->secure_block = secure_block;
+ }
+#endif
/* pr_info("new_type = 0x%x\n", p_ext_info->new_type); */
pr_info("page_per_blk = 0x%x\n", p_ext_info->page_per_blk);
- pr_info("boot_num = %d each_boot_pages = %d", boot_num,
- each_boot_pages);
+ /*
+ *pr_info("boot_num = %d each_boot_pages = %d", boot_num,
+ * each_boot_pages);
+ */
}
static int m3_nand_boot_write_page_hwecc(struct mtd_info *mtd,
--- /dev/null
+/*
+ * drivers/amlogic/mtd_meson8b/nand_key.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. 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 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.
+ *
+ */
+
+/* #include "key_old.h" */
+#include "aml_mtd.h"
+#include "env_old.h"
+#include "linux/crc32.h"
+#include <linux/amlogic/unifykey/key_manage.h>
+
+#define KEYSIZE (CONFIG_KEYSIZE - (sizeof(uint32_t)))
+
+/*error code*/
+#define NAND_KEY_RD_ERR 2
+#define NAND_KEY_CONFIG_ERR 3
+
+#define REMAIN_TAIL_BLOCK_NUM 8
+
+
+/* static struct aml_nand_chip *aml_chip_key; */
+static int default_keyironment_size =
+ (KEYSIZE - sizeof(struct aml_nand_bbt_info));
+
+struct mesonkey_t {
+ uint32_t crc;/* CRC32 over data bytes */
+ uint8_t data[KEYSIZE];/* Environment data */
+};
+
+static struct env_free_node_t *alloc_fn(void)
+{
+ struct env_free_node_t *fn;
+
+ fn = kzalloc(sizeof(struct env_free_node_t), GFP_KERNEL);
+ return fn;
+}
+
+static int aml_nand_read_key(struct mtd_info *mtd,
+ uint64_t offset, u_char *buf)
+{
+ struct env_oobinfo_t *key_oobinfo;
+ int error = 0;
+ uint64_t addr = offset;
+ size_t amount_loaded = 0;
+ size_t len;
+ struct mtd_oob_ops oob_ops;
+ struct mtd_oob_region aml_oob_region;
+ uint8_t *data_buf;
+ uint8_t key_oob_buf[sizeof(struct env_oobinfo_t)];
+
+ struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd);
+
+ if (!aml_chip->aml_nandkey_info->env_valid)
+ return 1;
+
+ data_buf = kzalloc(mtd->writesize, GFP_KERNEL);
+ if (data_buf == NULL)
+ return -ENOMEM;
+
+ key_oobinfo = (struct env_oobinfo_t *)key_oob_buf;
+
+ error = mtd->ooblayout->free(mtd, 0,
+ (struct mtd_oob_region *)&aml_oob_region);
+ if (error != 0) {
+ pr_err("read oob free failed %s() %d\n",
+ __func__, __LINE__);
+ error = NAND_KEY_RD_ERR;
+ goto exit;
+ }
+
+ while (amount_loaded < CONFIG_KEYSIZE) {
+
+ oob_ops.mode = MTD_OPS_AUTO_OOB;
+ oob_ops.len = mtd->writesize;
+ oob_ops.ooblen = sizeof(struct env_oobinfo_t);
+ oob_ops.ooboffs = aml_oob_region.offset;
+ oob_ops.datbuf = data_buf;
+ oob_ops.oobbuf = key_oob_buf;
+
+ memset((uint8_t *)oob_ops.datbuf, 0x0, mtd->writesize);
+ memset((uint8_t *)oob_ops.oobbuf, 0x0, oob_ops.ooblen);
+
+ error = mtd->_read_oob(mtd, addr, &oob_ops);
+ if ((error != 0) && (error != -EUCLEAN)) {
+ pr_err("blk check failed: %llx, %d\n",
+ (uint64_t)addr, error);
+ error = NAND_KEY_RD_ERR;
+ goto exit;
+ }
+
+ if (memcmp(key_oobinfo->name, ENV_KEY_MAGIC, 4))
+ pr_err("invalid key magic: %llx\n", (uint64_t)addr);
+
+ addr += mtd->writesize;
+ len = min(mtd->writesize, CONFIG_KEYSIZE - amount_loaded);
+ memcpy(buf + amount_loaded, data_buf, len);
+ amount_loaded += mtd->writesize;
+ }
+ if (amount_loaded < CONFIG_KEYSIZE) {
+ error = NAND_KEY_CONFIG_ERR;
+ pr_err("key size err, w:%d,c %d, %s\n",
+ mtd->writesize, CONFIG_KEYSIZE, __func__);
+ goto exit;
+ }
+exit:
+ kfree(data_buf);
+ return error;
+}
+
+
+static int aml_nand_write_key(struct mtd_info *mtd,
+ uint64_t offset, u_char *buf)
+{
+ struct env_oobinfo_t *key_oobinfo;
+ int error = 0;
+ uint64_t addr = 0;
+ size_t amount_saved = 0;
+ size_t len;
+ struct mtd_oob_ops oob_ops;
+ struct mtd_oob_region oob_reg;
+ uint8_t *data_buf;
+ uint8_t key_oob_buf[sizeof(struct env_oobinfo_t)];
+
+ struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd);
+
+ data_buf = kzalloc(mtd->writesize, GFP_KERNEL);
+ if (data_buf == NULL)
+ return -ENOMEM;
+
+ addr = offset;
+ key_oobinfo = (struct env_oobinfo_t *)key_oob_buf;
+ memcpy(key_oobinfo->name, ENV_KEY_MAGIC, 4);
+ key_oobinfo->ec = aml_chip->aml_nandkey_info->env_valid_node->ec;
+ key_oobinfo->timestamp =
+ aml_chip->aml_nandkey_info->env_valid_node->timestamp;
+ key_oobinfo->status_page = 1;
+
+ error = mtd->ooblayout->free(mtd, 0, (struct mtd_oob_region *)&oob_reg);
+ if (error != 0) {
+ pr_err("read oob free failed: %s() %d\n", __func__, __LINE__);
+ error = 1;
+ goto exit;
+ }
+
+ while (amount_saved < CONFIG_KEYSIZE) {
+
+ oob_ops.mode = MTD_OPS_AUTO_OOB;
+ oob_ops.len = mtd->writesize;
+ oob_ops.ooblen = sizeof(struct env_oobinfo_t);
+ oob_ops.ooboffs = oob_reg.offset;
+ oob_ops.datbuf = data_buf;
+ oob_ops.oobbuf = key_oob_buf;
+
+ memset((uint8_t *)oob_ops.datbuf, 0x0, mtd->writesize);
+ len = min(mtd->writesize, CONFIG_KEYSIZE - amount_saved);
+ memcpy((uint8_t *)oob_ops.datbuf, buf + amount_saved, len);
+
+ error = mtd->_write_oob(mtd, addr, &oob_ops);
+ if (error) {
+ pr_err("blk write failed: %llx, %d\n",
+ (uint64_t)addr, error);
+ error = 1;
+ goto exit;
+ }
+
+ addr += mtd->writesize;
+ amount_saved += mtd->writesize;
+ }
+ if (amount_saved < CONFIG_KEYSIZE) {
+ error = 1;
+ pr_err("amount_saved < CONFIG_KEYSIZE, %s\n", __func__);
+ goto exit;
+ }
+
+ aml_chip->aml_nandkey_info->env_valid = 1;
+exit:
+ kfree(data_buf);
+ return error;
+}
+
+static int aml_nand_get_key(struct mtd_info *mtd, u_char *buf)
+{
+ struct aml_nand_bbt_info *nand_bbt_info;
+ int error = 0, flag = 0;
+ uint64_t addr = 0;
+ struct mesonkey_t *key_ptr = (struct mesonkey_t *)buf;
+ struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd);
+ struct env_valid_node_t *cur_valid_node, *tail_valid_node;
+
+ cur_valid_node = aml_chip->aml_nandkey_info->env_valid_node;
+ tail_valid_node = cur_valid_node->next;
+ do {
+ while (tail_valid_node != NULL) {
+ if (cur_valid_node->rd_flag == NAND_KEY_RD_ERR) {
+ cur_valid_node = tail_valid_node;
+ tail_valid_node = tail_valid_node->next;
+ } else
+ break;
+ }
+ if (cur_valid_node->rd_flag == NAND_KEY_RD_ERR) {
+ error = NAND_KEY_RD_ERR;
+ pr_err("no valid block get key,%s\n", __func__);
+ goto exit;
+ }
+
+ addr = cur_valid_node->phy_blk_addr;
+ addr *= mtd->erasesize;
+ addr += cur_valid_node->phy_page_addr * mtd->writesize;
+ pr_info("read:addr:0x%llx,phy_blk_addr:%d,phy_page_addr:%d,%s:%d\n",
+ addr,
+ cur_valid_node->phy_blk_addr,
+ cur_valid_node->phy_page_addr,
+ __func__, __LINE__);
+
+ error = aml_nand_read_key(mtd, addr, (u_char *)key_ptr);
+ if (error) {
+ pr_err("nand key read fail,%s\n", __func__);
+ if (error == NAND_KEY_RD_ERR) {
+ cur_valid_node->rd_flag = NAND_KEY_RD_ERR;
+ flag = 1;
+ } else
+ goto exit;
+ } else
+ flag = 0;
+ } while (flag);
+ nand_bbt_info = &aml_chip->aml_nandkey_info->nand_bbt_info;
+ memcpy(nand_bbt_info->bbt_head_magic,
+ key_ptr->data + default_keyironment_size,
+ sizeof(struct aml_nand_bbt_info));
+exit:
+ return error;
+}
+
+
+static int aml_nand_save_key(struct mtd_info *mtd, u_char *buf)
+{
+ struct aml_nand_bbt_info *nand_bbt_info;
+ struct env_free_node_t *env_free_node, *key_tmp_node;
+ int error = 0, pages_per_blk, i = 1;
+ uint64_t addr = 0;
+ struct erase_info aml_key_erase_info;
+ struct mesonkey_t *key_ptr = (struct mesonkey_t *)buf;
+ int group_block_count = 0;
+ int group_max_block = NAND_MINIKEY_PART_BLOCKNUM;
+ struct env_valid_node_t *tmp_valid_node, *tail_valid_node;
+ struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd);
+ struct aml_nandkey_info_t *keyinfo;
+
+ if (!aml_chip->aml_nandkey_info->env_init)
+ return 1;
+
+ keyinfo = aml_chip->aml_nandkey_info;
+ pages_per_blk = mtd->erasesize / mtd->writesize;
+ if ((mtd->writesize < CONFIG_KEYSIZE) &&
+ (keyinfo->env_valid == 1))
+ i = (CONFIG_KEYSIZE + mtd->writesize - 1) / mtd->writesize;
+
+ if (keyinfo->env_valid) {
+ keyinfo->env_valid_node->phy_page_addr += i;
+ tail_valid_node = keyinfo->env_valid_node->next;
+ while (tail_valid_node) {
+ tail_valid_node->phy_page_addr += i;
+ tail_valid_node = tail_valid_node->next;
+ }
+
+ if ((keyinfo->env_valid_node->phy_page_addr
+ + i) > pages_per_blk) {
+
+ env_free_node = alloc_fn();
+ if (env_free_node == NULL)
+ return -ENOMEM;
+
+ env_free_node->phy_blk_addr =
+ keyinfo->env_valid_node->phy_blk_addr;
+ env_free_node->ec = keyinfo->env_valid_node->ec;
+ key_tmp_node = keyinfo->env_free_node;
+ if (keyinfo->env_free_node == NULL)
+ keyinfo->env_free_node = env_free_node;
+ else {
+ while (key_tmp_node->next != NULL)
+ key_tmp_node = key_tmp_node->next;
+ key_tmp_node->next = env_free_node;
+ }
+ tail_valid_node = keyinfo->env_valid_node->next;
+ while (tail_valid_node) {
+ env_free_node = alloc_fn();
+ if (env_free_node == NULL)
+ return -ENOMEM;
+
+ env_free_node->phy_blk_addr =
+ tail_valid_node->phy_blk_addr;
+ env_free_node->ec = tail_valid_node->ec;
+ keyinfo->env_valid_node->next =
+ tail_valid_node->next;
+ kfree(tail_valid_node);
+ tail_valid_node =
+ keyinfo->env_valid_node->next;
+
+ key_tmp_node = keyinfo->env_free_node;
+ while (key_tmp_node->next != NULL)
+ key_tmp_node = key_tmp_node->next;
+ key_tmp_node->next = env_free_node;
+ }
+
+ key_tmp_node = keyinfo->env_free_node;
+ keyinfo->env_valid_node->phy_blk_addr =
+ key_tmp_node->phy_blk_addr;
+ keyinfo->env_valid_node->phy_page_addr = 0;
+ keyinfo->env_valid_node->ec = key_tmp_node->ec;
+ keyinfo->env_valid_node->timestamp += 1;
+ keyinfo->env_valid_node->next = NULL;
+ keyinfo->env_free_node = key_tmp_node->next;
+ kfree(key_tmp_node);
+
+ group_block_count++;
+ tail_valid_node = keyinfo->env_valid_node;
+ key_tmp_node = keyinfo->env_free_node;
+ while (key_tmp_node &&
+ group_block_count < group_max_block) {
+ tmp_valid_node =
+ kzalloc(sizeof(struct env_valid_node_t),
+ GFP_KERNEL);
+ if (tmp_valid_node == NULL)
+ return -ENOMEM;
+
+ tmp_valid_node->ec = key_tmp_node->ec;
+ tmp_valid_node->phy_blk_addr =
+ key_tmp_node->phy_blk_addr;
+ tmp_valid_node->phy_page_addr = 0;
+ tmp_valid_node->timestamp += 1;
+ tmp_valid_node->next = NULL;
+ keyinfo->env_free_node = key_tmp_node->next;
+ kfree(key_tmp_node);
+ key_tmp_node = keyinfo->env_free_node;
+ while (tail_valid_node->next != NULL)
+ tail_valid_node = tail_valid_node->next;
+ tail_valid_node->next = tmp_valid_node;
+ group_block_count++;
+ }
+ }
+ } else {
+
+ key_tmp_node = keyinfo->env_free_node;
+ keyinfo->env_valid_node->phy_blk_addr =
+ key_tmp_node->phy_blk_addr;
+ keyinfo->env_valid_node->phy_page_addr = 0;
+ keyinfo->env_valid_node->ec = key_tmp_node->ec;
+ keyinfo->env_valid_node->timestamp += 1;
+ keyinfo->env_valid_node->next = NULL;
+ keyinfo->env_free_node = key_tmp_node->next;
+ kfree(key_tmp_node);
+ group_block_count++;
+ tail_valid_node = keyinfo->env_valid_node;
+ key_tmp_node = keyinfo->env_free_node;
+ while (key_tmp_node &&
+ group_block_count < group_max_block) {
+ tmp_valid_node =
+ kzalloc(sizeof(struct env_valid_node_t),
+ GFP_KERNEL);
+ tmp_valid_node->ec = key_tmp_node->ec;
+ tmp_valid_node->phy_blk_addr =
+ key_tmp_node->phy_blk_addr;
+ tmp_valid_node->phy_page_addr = 0;
+ tmp_valid_node->timestamp += 1;
+ tmp_valid_node->next = NULL;
+ keyinfo->env_free_node = key_tmp_node->next;
+ kfree(key_tmp_node);
+ key_tmp_node = keyinfo->env_free_node;
+ while (tail_valid_node->next != NULL)
+ tail_valid_node = tail_valid_node->next;
+ tail_valid_node->next = tmp_valid_node;
+ group_block_count++;
+ }
+ }
+
+ tail_valid_node = keyinfo->env_valid_node;
+ while (tail_valid_node != NULL) {
+ addr = tail_valid_node->phy_blk_addr;
+ addr *= mtd->erasesize;
+ addr += tail_valid_node->phy_page_addr *
+ mtd->writesize;
+ pr_info(
+ "write:addr:0x%llx,phy_blk_addr:%d,phy_page_addr:%d,%s:%d\n",
+ addr,
+ tail_valid_node->phy_blk_addr,
+ tail_valid_node->phy_page_addr,
+ __func__, __LINE__);
+
+ if (tail_valid_node->phy_page_addr == 0) {
+ memset(&aml_key_erase_info, 0,
+ sizeof(struct erase_info));
+ aml_key_erase_info.mtd = mtd;
+ aml_key_erase_info.addr = addr;
+ aml_key_erase_info.len = mtd->erasesize;
+ error = mtd->_erase(mtd, &aml_key_erase_info);
+ if (error) {
+ pr_err("key free blk erase failed %d\n", error);
+ mtd->_block_markbad(mtd, addr);
+ return error;
+ }
+ tail_valid_node->ec++;
+ }
+ nand_bbt_info = &keyinfo->nand_bbt_info;
+ if ((!memcmp(nand_bbt_info->bbt_head_magic,
+ BBT_HEAD_MAGIC, 4)) &&
+ (!memcmp(nand_bbt_info->bbt_tail_magic,
+ BBT_TAIL_MAGIC, 4))) {
+
+ memcpy(key_ptr->data + default_keyironment_size,
+ keyinfo->nand_bbt_info.bbt_head_magic,
+ sizeof(struct aml_nand_bbt_info));
+ key_ptr->crc =
+ (crc32((0 ^ 0xffffffffL),
+ key_ptr->data, KEYSIZE) ^ 0xffffffffL);
+ }
+
+ if (aml_nand_write_key(mtd,
+ addr, (u_char *) key_ptr)) {
+ pr_err("update nand key FAILED!\n");
+ return 1;
+ }
+ tail_valid_node = tail_valid_node->next;
+ }
+
+ return error;
+}
+
+static struct env_free_node_t *get_free_tail(struct env_free_node_t *head)
+{
+ struct env_free_node_t *fn = head;
+
+ while (fn->next != NULL)
+ fn = fn->next;
+ return fn;
+}
+
+static int aml_nand_key_init(struct mtd_info *mtd)
+{
+ struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd);
+ struct nand_chip *chip = &aml_chip->chip;
+ struct env_oobinfo_t *key_oobinfo;
+ /* free_node; */
+ struct env_free_node_t *fn;
+ /* tmp_node; */
+ struct env_free_node_t *tn;
+ struct env_free_node_t *key_prev_node;
+ struct aml_nandkey_info_t *k_info;
+ #ifdef NAND_KEY_SAVE_MULTI_BLOCK
+ struct env_valid_node_t *env_valid_node;
+ struct env_valid_node_t *tmp_valid_node;
+ struct env_free_node_t *multi_free_node;
+ struct env_free_node_t *free_tmp_node;
+ int have_env_free_node_flag;
+ #endif
+ struct mtd_oob_region aml_oob_region;
+ int error = 0, start_blk, key_blk, i;
+ int pages_per_blk, bad_blk_cnt = 0;
+ int max_key_blk, phys_erase_shift;
+ uint64_t offset;
+ uint8_t *data_buf;
+ struct mtd_oob_ops aml_oob_ops;
+ uint8_t key_oob_buf[sizeof(struct env_oobinfo_t)];
+ int remain_block = 0;
+ int remain_start_block;
+ int remain_total_blk;
+ int key_end;
+ uint32_t env_node_size;
+
+ data_buf = kzalloc(mtd->writesize, GFP_KERNEL);
+ if (data_buf == NULL)
+ return -ENOMEM;
+
+ aml_chip->aml_nandkey_info =
+ kzalloc(sizeof(struct aml_nandkey_info_t), GFP_KERNEL);
+
+ if (aml_chip->aml_nandkey_info == NULL) {
+ kfree(data_buf);
+ data_buf = NULL;
+ return -ENOMEM;
+ }
+ env_node_size = sizeof(struct env_free_node_t);
+ k_info = aml_chip->aml_nandkey_info;
+ k_info->env_init = 0;
+ k_info->mtd = mtd;
+ k_info->env_valid_node =
+ kzalloc(sizeof(struct env_valid_node_t), GFP_KERNEL);
+ if (k_info->env_valid_node == NULL) {
+ kfree(data_buf);
+ data_buf = NULL;
+ kfree(k_info);
+ k_info = NULL;
+ return -ENOMEM;
+ }
+ k_info->env_valid_node->phy_blk_addr = -1;
+
+ phys_erase_shift = fls(mtd->erasesize) - 1;
+ max_key_blk = (NAND_MINIKEY_PART_SIZE >> phys_erase_shift);
+ if (max_key_blk < NAND_MINIKEY_PART_BLOCKNUM)
+ max_key_blk = NAND_MINIKEY_PART_BLOCKNUM;
+
+#ifdef NEW_NAND_SUPPORT
+ if ((aml_chip->new_nand_info.type)
+ && (aml_chip->new_nand_info.type < 10))
+ offset += RETRY_NAND_BLK_NUM * mtd->erasesize;
+#endif
+
+ /*
+ *start_blk = (int)(offset >> phys_erase_shift);
+ *total_blk = (int)(mtd->size >> phys_erase_shift);
+ *aml_chip->aml_nandkey_info->start_block=start_blk;
+ *pr_info("start_blk=%d\n",aml_chip->aml_nandkey_info->start_block);
+ *aml_chip->aml_nandkey_info->end_block=start_blk;
+ */
+ pages_per_blk = (1 <<
+ (chip->phys_erase_shift - chip->page_shift));
+ key_oobinfo = (struct env_oobinfo_t *)key_oob_buf;
+ /*
+ *if ((default_keyironment_size
+ * + sizeof(struct aml_nand_bbt_info)) > KEYSIZE)
+ *total_blk = start_blk + max_key_blk;
+ */
+
+#define REMAIN_TAIL_BLOCK_NUM 8
+ offset = mtd->size - mtd->erasesize;
+ remain_start_block =
+ (int)(offset >> phys_erase_shift);
+ remain_total_blk = REMAIN_TAIL_BLOCK_NUM;
+ k_info->start_block = remain_start_block;
+ k_info->end_block = remain_start_block;
+ bad_blk_cnt = 0;
+ do {
+ offset = mtd->erasesize;
+ offset *= remain_start_block;
+ error = mtd->_block_isbad(mtd, offset);
+ if (error) {
+ k_info->nand_bbt_info.nand_bbt[bad_blk_cnt++]
+ = remain_start_block;
+ if (bad_blk_cnt >= MAX_BAD_BLK_NUM) {
+ pr_err("bad block too much,%s\n", __func__);
+ return -ENOMEM;
+ }
+ k_info->start_block--;
+ remain_start_block--;
+ continue;
+ }
+ remain_start_block--;
+ } while (++remain_block < remain_total_blk);
+ k_info->start_block -= (remain_block - 1);
+ pr_info(
+ "key start_blk=%d,end_blk=%d,%s:%d\n",
+ k_info->start_block,
+ k_info->end_block, __func__, __LINE__);
+
+ key_blk = 0;
+ start_blk = k_info->start_block;
+
+ error = mtd->ooblayout->free(mtd, 0,
+ (struct mtd_oob_region *)&aml_oob_region);
+ if (error != 0) {
+ pr_err(" oob free failed: %s() %d\n",
+ __func__, __LINE__);
+ return -ERANGE;
+ }
+ key_end = remain_total_blk + k_info->start_block;
+ do {
+ offset = mtd->erasesize;
+ offset *= start_blk;
+ error = mtd->_block_isbad(mtd, offset);
+ if (error) {
+ start_blk++;
+ continue;
+ }
+ aml_oob_ops.mode = MTD_OPS_AUTO_OOB;
+ aml_oob_ops.len = mtd->writesize;
+ aml_oob_ops.ooblen = sizeof(struct env_oobinfo_t);
+ aml_oob_ops.ooboffs = aml_oob_region.offset;
+ /* aml_oob_ops.ooboffs = mtd->ecclayout->oobfree[0].offset; */
+ aml_oob_ops.datbuf = data_buf;
+ aml_oob_ops.oobbuf = key_oob_buf;
+ memset((uint8_t *)aml_oob_ops.datbuf,
+ 0x0, mtd->writesize);
+ memset((uint8_t *)aml_oob_ops.oobbuf,
+ 0x0, aml_oob_ops.ooblen);
+
+ error = mtd->_read_oob(mtd, offset, &aml_oob_ops);
+ if ((error != 0) && (error != -EUCLEAN)) {
+ pr_err(
+ "blk check good but read failed: %llx, %d\n",
+ (uint64_t)offset, error);
+ continue;
+ }
+
+ k_info->env_init = 1;
+ if (!memcmp(key_oobinfo->name, ENV_KEY_MAGIC, 4)) {
+ k_info->env_valid = 1;
+ if (k_info->env_valid_node->phy_blk_addr >= 0) {
+ fn = alloc_fn();
+ if (fn == NULL)
+ return -ENOMEM;
+
+ fn->dirty_flag = 1;
+ have_env_free_node_flag = 0;
+ if (key_oobinfo->timestamp >
+ k_info->env_valid_node->timestamp) {
+
+ fn->phy_blk_addr =
+ k_info->env_valid_node->phy_blk_addr;
+ fn->ec =
+ k_info->env_valid_node->ec;
+ fn->next = NULL;
+ have_env_free_node_flag = 1;
+
+ tmp_valid_node =
+ k_info->env_valid_node->next;
+ while (tmp_valid_node != NULL) {
+ k_info->env_valid_node->next =
+ tmp_valid_node->next;
+ multi_free_node =
+ kzalloc(env_node_size,
+ GFP_KERNEL);
+ multi_free_node->phy_blk_addr =
+ tmp_valid_node->phy_blk_addr;
+ multi_free_node->ec =
+ tmp_valid_node->ec;
+ kfree(tmp_valid_node);
+ multi_free_node->dirty_flag = 1;
+ free_tmp_node = fn;
+ free_tmp_node =
+ get_free_tail(free_tmp_node);
+ free_tmp_node->next =
+ multi_free_node;
+ tmp_valid_node =
+ k_info->env_valid_node->next;
+ }
+ k_info->env_valid_node->phy_blk_addr =
+ start_blk;
+ k_info->env_valid_node->phy_page_addr
+ = 0;
+ k_info->env_valid_node->ec =
+ key_oobinfo->ec;
+ k_info->env_valid_node->timestamp =
+ key_oobinfo->timestamp;
+ k_info->env_valid_node->next = NULL;
+
+ } else if (key_oobinfo->timestamp ==
+ k_info->env_valid_node->timestamp) {
+ tmp_valid_node =
+ k_info->env_valid_node;
+ env_valid_node =
+ kzalloc(sizeof(struct env_valid_node_t),
+ GFP_KERNEL);
+ if (env_valid_node == NULL)
+ return -ENOMEM;
+ env_valid_node->phy_blk_addr
+ = start_blk;
+ env_valid_node->phy_page_addr = 0;
+ env_valid_node->timestamp =
+ key_oobinfo->timestamp;
+ env_valid_node->ec = key_oobinfo->ec;
+ while (tmp_valid_node->next != NULL) {
+ tmp_valid_node =
+ tmp_valid_node->next;
+ }
+ tmp_valid_node->next = env_valid_node;
+ } else {
+ fn->phy_blk_addr
+ = start_blk;
+ fn->ec = key_oobinfo->ec;
+ have_env_free_node_flag = 1;
+ }
+
+ if (have_env_free_node_flag) {
+ if (k_info->env_free_node == NULL)
+ k_info->env_free_node = fn;
+ else {
+ tn =
+ k_info->env_free_node;
+ tn = get_free_tail(tn);
+ tn->next = fn;
+ }
+ } else {
+ kfree(fn);
+ fn = NULL;
+ }
+ } else {
+ k_info->env_valid_node->phy_blk_addr =
+ start_blk;
+ k_info->env_valid_node->phy_page_addr = 0;
+ k_info->env_valid_node->ec =
+ key_oobinfo->ec;
+ k_info->env_valid_node->timestamp =
+ key_oobinfo->timestamp;
+ }
+ } else if (key_blk < max_key_blk) {
+ fn = alloc_fn();
+ if (fn == NULL)
+ return -ENOMEM;
+
+ fn->phy_blk_addr = start_blk;
+ fn->ec = key_oobinfo->ec;
+ if (k_info->env_free_node == NULL)
+ k_info->env_free_node = fn;
+ else {
+ tn = k_info->env_free_node;
+ key_prev_node = tn;
+ while (tn != NULL) {
+ if (tn->dirty_flag == 1)
+ break;
+ key_prev_node = tn;
+ tn = tn->next;
+ }
+ if (key_prev_node == tn) {
+ fn->next = tn;
+ k_info->env_free_node = fn;
+ } else {
+ key_prev_node->next = fn;
+ fn->next = tn;
+ }
+ }
+ }
+ key_blk++;
+ if ((key_blk >= max_key_blk) && (k_info->env_valid == 1))
+ break;
+
+ } while ((++start_blk) < key_end);
+ if (start_blk >= key_end) {
+ memcpy(k_info->nand_bbt_info.bbt_head_magic,
+ BBT_HEAD_MAGIC, 4);
+ memcpy(k_info->nand_bbt_info.bbt_tail_magic,
+ BBT_TAIL_MAGIC, 4);
+ }
+
+ if (k_info->env_valid == 1) {
+
+ aml_oob_ops.mode = MTD_OPS_AUTO_OOB;
+ aml_oob_ops.len = mtd->writesize;
+ aml_oob_ops.ooblen = sizeof(struct env_oobinfo_t);
+ aml_oob_ops.ooboffs = aml_oob_region.offset;
+ aml_oob_ops.datbuf = data_buf;
+ aml_oob_ops.oobbuf = key_oob_buf;
+
+ for (i = 0; i < pages_per_blk; i++) {
+
+ memset((uint8_t *)aml_oob_ops.datbuf,
+ 0x0, mtd->writesize);
+ memset((uint8_t *)aml_oob_ops.oobbuf,
+ 0x0, aml_oob_ops.ooblen);
+
+ offset = k_info->env_valid_node->phy_blk_addr;
+ offset *= mtd->erasesize;
+ offset += i * mtd->writesize;
+ error = mtd->_read_oob(mtd, offset, &aml_oob_ops);
+ if ((error != 0) && (error != -EUCLEAN)) {
+ pr_err(
+ "read failed: %llx, %d\n",
+ (uint64_t)offset, error);
+ continue;
+ }
+
+ #ifdef NAND_KEY_SAVE_MULTI_BLOCK
+ if (!memcmp(key_oobinfo->name, ENV_KEY_MAGIC, 4)) {
+ k_info->env_valid_node->phy_page_addr = i;
+ tmp_valid_node = k_info->env_valid_node->next;
+ while (tmp_valid_node != NULL) {
+ tmp_valid_node->phy_page_addr = i;
+ tmp_valid_node = tmp_valid_node->next;
+ }
+ } else
+ break;
+ #else
+ if (!memcmp(key_oobinfo->name, ENV_KEY_MAGIC, 4))
+ k_info->env_valid_node->phy_page_addr = i;
+ else
+ break;
+ #endif
+ }
+ }
+ #ifdef NAND_KEY_SAVE_MULTI_BLOCK
+ if ((mtd->writesize < CONFIG_KEYSIZE)
+ && (k_info->env_valid == 1)) {
+ i = (CONFIG_KEYSIZE + mtd->writesize - 1) / mtd->writesize;
+ k_info->env_valid_node->phy_page_addr -= (i - 1);
+
+ tmp_valid_node = k_info->env_valid_node->next;
+ while (tmp_valid_node != NULL) {
+ tmp_valid_node->phy_page_addr -= (i - 1);
+ tmp_valid_node = tmp_valid_node->next;
+ }
+ }
+ #else
+ if ((mtd->writesize < CONFIG_KEYSIZE)
+ && (k_info->env_valid == 1)) {
+ i = (CONFIG_KEYSIZE + mtd->writesize - 1) / mtd->writesize;
+ k_info->env_valid_node->phy_page_addr -= (i - 1);
+ }
+ #endif
+
+ offset =
+ k_info->env_valid_node->phy_blk_addr;
+ offset *= mtd->erasesize;
+ offset +=
+ k_info->env_valid_node->phy_page_addr
+ * mtd->writesize;
+ if (k_info->env_valid_node->phy_blk_addr < 0)
+ pr_err("aml nand key not have valid addr: not wrote\n");
+ else
+ pr_info("aml nand key valid addr: %llx\n", offset);
+ #ifdef NAND_KEY_SAVE_MULTI_BLOCK
+ tmp_valid_node = k_info->env_valid_node->next;
+ while (tmp_valid_node != NULL) {
+ offset = tmp_valid_node->phy_blk_addr;
+ offset *= mtd->erasesize;
+ offset += tmp_valid_node->phy_page_addr * mtd->writesize;
+ pr_info("aml nand key valid addr: %llx\n", (uint64_t)offset);
+ tmp_valid_node = tmp_valid_node->next;
+ }
+ #endif
+
+ pr_info(
+ "CONFIG_KEYSIZE=%d;KEYSIZE=%d;bbt=%d;default_keyironment_size=%d\n",
+ CONFIG_KEYSIZE, KEYSIZE,
+ sizeof(struct aml_nand_bbt_info),
+ default_keyironment_size);
+ kfree(data_buf);
+
+ return 0;
+}
+
+
+static int aml_nand_key_check(struct mtd_info *mtd)
+{
+ struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd);
+ struct aml_nand_platform *plat = aml_chip->platform;
+ struct platform_nand_chip *chip =
+ &plat->platform_nand_data.chip;
+ struct aml_nand_bbt_info *nand_bbt_info;
+ struct aml_nandkey_info_t *k_info;
+ struct mesonkey_t *key_ptr;
+ int error = 0, start_blk, total_blk, update_key_flag = 0;
+ int i, j, nr, phys_erase_shift;
+ uint64_t offset;
+
+ chip = chip;
+ nr = 0;
+ error = aml_nand_key_init(mtd);
+ if (error)
+ return error;
+ key_ptr = kzalloc(sizeof(struct mesonkey_t), GFP_KERNEL);
+ if (key_ptr == NULL)
+ return -ENOMEM;
+ k_info = aml_chip->aml_nandkey_info;
+
+ if (k_info->env_valid == 1)
+ pr_info("%s() %d, inited\n", __func__, __LINE__);
+ else {
+ update_key_flag = 1;
+ nand_bbt_info = (struct aml_nand_bbt_info *)
+ (key_ptr->data + default_keyironment_size);
+
+ memcpy(nand_bbt_info->nand_bbt,
+ k_info->nand_bbt_info.nand_bbt,
+ MAX_BAD_BLK_NUM * sizeof(int16_t));
+ memcpy(nand_bbt_info->bbt_head_magic,
+ BBT_HEAD_MAGIC, 4);
+ memcpy(nand_bbt_info->bbt_tail_magic,
+ BBT_TAIL_MAGIC, 4);
+ phys_erase_shift = fls(mtd->erasesize) - 1;
+ offset = k_info->start_block;
+ offset *= mtd->erasesize;
+
+ start_blk = (int)(offset >> phys_erase_shift);
+ total_blk = (int)(mtd->size >> phys_erase_shift);
+ for (i = start_blk; i < total_blk; i++) {
+ aml_chip->block_status[i] = NAND_BLOCK_GOOD;
+ for (j = 0; j < MAX_BAD_BLK_NUM; j++) {
+ if (nand_bbt_info->nand_bbt[j] == i) {
+ aml_chip->block_status[i] =
+ NAND_BLOCK_BAD;
+ break;
+ }
+ }
+ }
+ memcpy(k_info->nand_bbt_info.bbt_head_magic,
+ key_ptr->data + default_keyironment_size,
+ sizeof(struct aml_nand_bbt_info));
+ }
+
+
+ if (update_key_flag) {
+ error = aml_nand_save_key(mtd, (u_char *)key_ptr);
+ if (error) {
+ pr_err("nand key save failed: %d\n", error);
+ goto exit;
+ }
+ }
+
+exit:
+ kfree(key_ptr);
+ return 0;
+}
+
+static struct mtd_info *nand_key_mtd;
+
+static int32_t nand_key_read(uint8_t *buf,
+ uint32_t len, uint32_t *actual_length)
+{
+ struct mesonkey_t *key_ptr = NULL;
+ int error = 0;
+ uint32_t *length;
+
+ length = actual_length;
+ *length = 0;
+ if (len > default_keyironment_size) {
+ pr_err("key data len too much,%s\n", __func__);
+ return -EFAULT;
+ }
+ key_ptr = kzalloc(CONFIG_KEYSIZE, GFP_KERNEL);
+ if (key_ptr == NULL)
+ return -ENOMEM;
+ memset(key_ptr, 0, CONFIG_KEYSIZE);
+ error = aml_nand_get_key(nand_key_mtd, (u_char *)key_ptr);
+ if (error) {
+ pr_err("read key error,%s\n", __func__);
+ error = -EFAULT;
+ goto exit;
+ }
+ memcpy(buf, key_ptr->data + 0, len);
+ *length = len;
+exit:
+ kfree(key_ptr);
+ return 0;
+}
+
+static int32_t nand_key_write(uint8_t *buf,
+ uint32_t len, uint32_t *actual_length)
+{
+ struct mesonkey_t *key_ptr = NULL;
+ int error = 0;
+ uint32_t *length;
+
+ length = actual_length;
+ *length = 0;
+ if (len > default_keyironment_size) {
+ pr_err("key data len error,%s\n", __func__);
+ return -EFAULT;
+ }
+ key_ptr = kzalloc(CONFIG_KEYSIZE, GFP_KERNEL);
+ if (key_ptr == NULL)
+ return -ENOMEM;
+ memset(key_ptr, 0, CONFIG_KEYSIZE);
+ memcpy(key_ptr->data + 0, buf, len);
+
+ error = aml_nand_save_key(nand_key_mtd, (u_char *)key_ptr);
+ if (error) {
+ pr_err("save key error,%s\n", __func__);
+ error = -EFAULT;
+ goto exit;
+ }
+ *length = len;
+
+exit:
+ kfree(key_ptr);
+ return error;
+}
+int aml_key_init(struct aml_nand_chip *aml_chip)
+{
+ int err = 0;
+
+ pr_info("nand key: nand_key_probe.\n");
+ err = aml_nand_key_check(aml_chip->mtd);
+ if (err)
+ pr_err("invalid nand key\n");
+
+ nand_key_mtd = aml_chip->mtd;
+
+ storage_ops_read(nand_key_read);
+ storage_ops_write(nand_key_write);
+ pr_info("nand key init ok! %s()%d\n", __func__, __LINE__);
+
+ return err;
+}
--- /dev/null
+/*
+ * drivers/amlogic/mtd_meson8b/secure_storage.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. 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 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.
+ *
+ */
+
+#include "aml_mtd.h"
+#include "env_old.h"
+
+#define CONFIG_SECURE_SIZE (0x10000*2)/* 128k */
+#define SECURE_SIZE (CONFIG_SECURE_SIZE - (sizeof(uint32_t)))
+#define SECURE_STORE_MAGIC (0x6365736e)
+#define REMAIN_BLOCK_NUM 4
+#define NAND_SECURE_BLK 2
+
+
+
+struct secure_t {
+ uint32_t crc; /* CRC32 over data bytes */
+ uint8_t data[SECURE_SIZE]; /* Environment data */
+};
+
+struct secure_oobinfo_t {
+ int32_t name;
+ uint32_t timestamp;
+};
+
+struct mtd_info *nand_secure_mtd;
+static int aml_nand_read_secure(struct mtd_info *mtd,
+ loff_t offset, uint8_t *buf)
+{
+ struct secure_oobinfo_t *secure_oobinfo;
+ struct mtd_oob_ops *aml_oob_ops = NULL;
+ struct mtd_oob_region aml_oob_region;
+ int error = 0, err;
+ loff_t addr = 0, len;
+ size_t amount_loaded = 0;
+ uint8_t *data_buf = NULL;
+ uint8_t secure_oob_buf[sizeof(struct secure_oobinfo_t)];
+ struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd);
+ struct aml_nandsecure_info_t *s_info;
+
+ s_info = aml_chip->aml_nandsecure_info;
+ if (!s_info->secure_valid)
+ return 1;
+ err = mtd->ooblayout->free(mtd, 0,
+ (struct mtd_oob_region *)&aml_oob_region);
+ if (err != 0) {
+ pr_err("read oob free failed :func : %s,line : %d\n",
+ __func__, __LINE__);
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ addr = s_info->secure_valid_node->phy_blk_addr;
+ addr *= mtd->erasesize;
+ addr += s_info->secure_valid_node->phy_page_addr
+ * mtd->writesize;
+ pr_err("%s(): valid addr: %llx at block %d page %d\n",
+ __func__, (uint64_t)addr,
+ s_info->secure_valid_node->phy_blk_addr,
+ s_info->secure_valid_node->phy_page_addr);
+
+ data_buf = kzalloc(mtd->writesize, GFP_KERNEL);
+ if (data_buf == NULL) {
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ aml_oob_ops = kzalloc(sizeof(struct mtd_oob_ops), GFP_KERNEL);
+ if (aml_oob_ops == NULL) {
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ secure_oobinfo = (struct secure_oobinfo_t *)secure_oob_buf;
+ while (amount_loaded < CONFIG_SECURE_SIZE) {
+ aml_oob_ops->mode = MTD_OPS_AUTO_OOB;
+ aml_oob_ops->len = mtd->writesize;
+ aml_oob_ops->ooblen = sizeof(struct secure_oobinfo_t);
+ aml_oob_ops->ooboffs = aml_oob_region.offset;
+ aml_oob_ops->datbuf = data_buf;
+ aml_oob_ops->oobbuf = secure_oob_buf;
+
+ memset((uint8_t *)aml_oob_ops->datbuf, 0x0,
+ mtd->writesize);
+ memset((uint8_t *)aml_oob_ops->oobbuf, 0x0,
+ aml_oob_ops->ooblen);
+
+ error = mtd->_read_oob(mtd, addr, aml_oob_ops);
+ if ((error != 0) && (error != -EUCLEAN)) {
+ pr_err("blk check good but read failed: %llx, %d\n",
+ (uint64_t)addr, error);
+ err = -EIO;
+ goto exit;
+ }
+
+ if (secure_oobinfo->name != SECURE_STORE_MAGIC)
+ pr_err("%s() invalid magic: %llx, magic = ox%x\n",
+ __func__,
+ (uint64_t)addr, secure_oobinfo->name);
+
+ addr += mtd->writesize;
+ len = min(mtd->writesize, CONFIG_SECURE_SIZE - amount_loaded);
+ memcpy(buf + amount_loaded, data_buf, len);
+ amount_loaded += mtd->writesize;
+ }
+ if (amount_loaded < CONFIG_SECURE_SIZE) {
+ err = -EIO;
+ goto exit;
+ }
+
+ kfree(data_buf);
+ kfree(aml_oob_ops);
+ return 0;
+
+exit:
+ kfree(aml_oob_ops);
+ aml_oob_ops = NULL;
+ kfree(data_buf);
+ data_buf = NULL;
+ return err;
+}
+
+static int aml_nand_write_secure(struct mtd_info *mtd,
+ loff_t offset, uint8_t *buf)
+{
+ struct secure_oobinfo_t *secure_oobinfo;
+ struct mtd_oob_ops *aml_oob_ops = NULL;
+ struct mtd_oob_region aml_oob_region;
+ struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd);
+ uint8_t secure_oob_buf[sizeof(struct secure_oobinfo_t)];
+ size_t len, amount_saved = 0;
+ uint8_t *data_buf = NULL;
+ int error = 0, err;
+ loff_t addr = 0;
+
+ aml_oob_ops = kzalloc(sizeof(struct mtd_oob_ops), GFP_KERNEL);
+ if (aml_oob_ops == NULL) {
+ err = -ENOMEM;
+ goto exit;
+ }
+ data_buf = kzalloc(mtd->writesize, GFP_KERNEL);
+ if (data_buf == NULL) {
+ err = -ENOMEM;
+ goto exit;
+ }
+ err = mtd->ooblayout->free(mtd, 0,
+ (struct mtd_oob_region *)&aml_oob_region);
+ if (err != 0) {
+ pr_err("%s() %d: read oob failed\n", __func__, __LINE__);
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ addr = offset;
+ secure_oobinfo = (struct secure_oobinfo_t *)secure_oob_buf;
+ secure_oobinfo->name = SECURE_STORE_MAGIC;
+ secure_oobinfo->timestamp =
+ aml_chip->aml_nandsecure_info->secure_valid_node->timestamp;
+
+ while (amount_saved < CONFIG_SECURE_SIZE) {
+ aml_oob_ops->mode = MTD_OPS_AUTO_OOB;
+ aml_oob_ops->len = mtd->writesize;
+ aml_oob_ops->ooblen = sizeof(struct secure_oobinfo_t);
+ aml_oob_ops->ooboffs = aml_oob_region.offset;
+ aml_oob_ops->datbuf = data_buf;
+ aml_oob_ops->oobbuf = secure_oob_buf;
+ memset((uint8_t *)aml_oob_ops->datbuf, 0x0, mtd->writesize);
+ len = min(mtd->writesize, CONFIG_SECURE_SIZE - amount_saved);
+ memcpy((uint8_t *)aml_oob_ops->datbuf, buf + amount_saved, len);
+
+ error = mtd->_write_oob(mtd, addr, aml_oob_ops);
+ if (error) {
+ pr_err("write failed: %llx, %d\n",
+ (uint64_t)addr, error);
+ err = 1;
+ goto exit;
+ }
+
+ addr += mtd->writesize;
+ amount_saved += mtd->writesize;
+ }
+
+ if (amount_saved < CONFIG_SECURE_SIZE) {
+ err = 1;
+ goto exit;
+ }
+
+ kfree(data_buf);
+ kfree(aml_oob_ops);
+ return 0;
+
+exit:
+ kfree(aml_oob_ops);
+ aml_oob_ops = NULL;
+ kfree(data_buf);
+ data_buf = NULL;
+ return err;
+}
+
+/*
+ * alloc free node
+ */
+static struct env_free_node_t *alloc_fn(void)
+{
+ return kzalloc(sizeof(struct env_free_node_t), GFP_KERNEL);
+}
+
+int aml_nand_save_secure(struct mtd_info *mtd, uint8_t *buf)
+{
+ struct env_free_node_t *fn = NULL;
+ struct env_free_node_t *tn;
+ struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd);
+ struct erase_info *nand_erase_info;
+ int error = 0, pages_per_blk, i = 1;
+ int16_t blk;
+ loff_t addr = 0;
+ struct secure_t *secure_ptr = (struct secure_t *)buf;
+ struct aml_nandsecure_info_t *sinfo;
+ struct env_valid_node_t *svn;
+
+ sinfo = aml_chip->aml_nandsecure_info;
+ if (!sinfo->secure_init)
+ return 1;
+ nand_erase_info = kzalloc(sizeof(struct erase_info), GFP_KERNEL);
+ if (nand_erase_info == NULL) {
+ pr_err("%s %d no mem for nand_erase_info\n",
+ __func__, __LINE__);
+ error = -ENOMEM;
+ goto exit;
+ }
+
+ pages_per_blk = mtd->erasesize / mtd->writesize;
+ if ((mtd->writesize < CONFIG_SECURE_SIZE)
+ && (sinfo->secure_valid == 1))
+ i = (CONFIG_SECURE_SIZE + mtd->writesize - 1) / mtd->writesize;
+
+ svn = sinfo->secure_valid_node;
+ if (sinfo->secure_valid) {
+ svn->phy_page_addr += i;
+ /* if current valid node block is full, get a free one */
+ if ((svn->phy_page_addr + i) > pages_per_blk) {
+
+ fn = alloc_fn();
+ if (fn == NULL) {
+ error = -ENOMEM;
+ goto exit;
+ }
+ tn = alloc_fn();
+ if (tn == NULL) {
+ pr_err("%s %d no mem for secure_tmp_node\n",
+ __func__, __LINE__);
+ error = -ENOMEM;
+ goto exit;
+ }
+ #if 0
+ /* fixme, bug here! */
+ tn = (struct env_free_node_t *)(svn);
+
+ fn = sinfo->secure_free_node;
+
+ svn->phy_blk_addr = fn->phy_blk_addr;
+ svn->phy_page_addr = 0;
+ svn->timestamp += 1;
+ sinfo->secure_free_node = tn;
+ #endif
+ blk = svn->phy_blk_addr;
+ fn = sinfo->secure_free_node;
+ svn->phy_blk_addr = fn->phy_blk_addr;
+ svn->phy_page_addr = 0;
+ svn->timestamp += 1;
+ sinfo->secure_free_node->phy_blk_addr = blk;
+ }
+ } else {
+ /* get a free node from free list */
+ tn = sinfo->secure_free_node;
+ svn->phy_blk_addr = tn->phy_blk_addr;
+ svn->phy_page_addr = 0;
+ svn->timestamp += 1;
+ sinfo->secure_free_node = tn->next;
+ kfree(tn);
+ }
+
+ addr = svn->phy_blk_addr;
+ addr *= mtd->erasesize;
+ addr += svn->phy_page_addr * mtd->writesize;
+
+ if (svn->phy_page_addr == 0) {
+
+ memset(nand_erase_info, 0, sizeof(struct erase_info));
+ nand_erase_info->mtd = mtd;
+ nand_erase_info->addr = addr;
+ nand_erase_info->len = mtd->erasesize;
+
+ aml_chip->key_protect = 1;
+ aml_chip->secure_protect = 1;
+
+ error = mtd->_erase(mtd, nand_erase_info);
+ if (error) {
+ pr_err("secure free blk erase failed %d\n", error);
+ mtd->_block_markbad(mtd, addr);
+ goto exit;
+ }
+ aml_chip->secure_protect = 0;
+ aml_chip->key_protect = 0;
+
+ }
+
+ if (aml_nand_write_secure(mtd, addr, (uint8_t *) secure_ptr)) {
+ pr_err("nand secure info update FAILED!\n");
+ error = 1;
+ goto exit;
+ }
+ pr_err("nand secure info save Ok\ns");
+ kfree(nand_erase_info);
+ return error;
+
+exit:
+ kfree(nand_erase_info);
+ nand_erase_info = NULL;
+ kfree(fn);
+ fn = NULL;
+ return error;
+}
+
+
+static int aml_nand_secure_init(struct mtd_info *mtd)
+{
+ struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd);
+ struct nand_chip *chip = &aml_chip->chip;
+ struct secure_oobinfo_t *secure_oobinfo;
+ /* free node */
+ struct env_free_node_t *fn;
+ /* temp node */
+ struct env_free_node_t *tn;
+ /* previous node */
+ struct env_free_node_t *pn;
+ int error = 0, err, start_blk, tmp_blk;
+ int secure_blk, i, pages_per_blk;
+ int max_secure_blk, phys_erase_shift;
+ loff_t offset;
+ uint8_t *data_buf = NULL;
+ uint32_t remain_start_block, remain_tatol_block;
+ uint32_t remain_block, total_blk;
+ struct mtd_oob_ops *aml_oob_ops = NULL;
+ struct mtd_oob_region aml_oob_region;
+ uint8_t secure_oob_buf[sizeof(struct secure_oobinfo_t)];
+ struct aml_nandsecure_info_t *sinfo;
+ struct env_valid_node_t *svn;
+
+ err = mtd->ooblayout->free(mtd, 0,
+ (struct mtd_oob_region *)&aml_oob_region);
+
+ if (err != 0) {
+ pr_err("%s() %d: read oob free failed\n",
+ __func__, __LINE__);
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ aml_oob_ops = kzalloc(sizeof(struct mtd_oob_ops), GFP_KERNEL);
+ if (aml_oob_ops == NULL) {
+ err = -ENOMEM;
+ goto exit;
+ }
+ data_buf = kzalloc(mtd->writesize, GFP_KERNEL);
+ if (data_buf == NULL) {
+ err = -ENOMEM;
+ goto exit;
+ }
+ aml_chip->aml_nandsecure_info
+ = kzalloc(sizeof(struct aml_nandsecure_info_t), GFP_KERNEL);
+ if (aml_chip->aml_nandsecure_info == NULL) {
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ sinfo = aml_chip->aml_nandsecure_info;
+ sinfo->mtd = mtd;
+
+ sinfo->secure_valid_node
+ = kzalloc(sizeof(struct env_valid_node_t), GFP_KERNEL);
+ if (sinfo->secure_valid_node == NULL) {
+ pr_err("%s %d no mem\n", __func__, __LINE__);
+ err = -ENOMEM;
+ goto exit;
+ }
+ svn = sinfo->secure_valid_node;
+ svn->phy_blk_addr = -1;
+
+ phys_erase_shift = fls(mtd->erasesize) - 1;
+ max_secure_blk = NAND_SECURE_BLK;
+
+ offset = mtd->size - mtd->erasesize;
+ total_blk = (int)(offset >> phys_erase_shift);
+
+ pages_per_blk = (1 << (chip->phys_erase_shift - chip->page_shift));
+ secure_oobinfo = (struct secure_oobinfo_t *)secure_oob_buf;
+#if 1
+ remain_tatol_block = REMAIN_BLOCK_NUM;
+ remain_block = 0;
+ remain_start_block = aml_chip->aml_nandkey_info->start_block - 1;
+ sinfo->end_block = remain_start_block;
+ do {
+ offset = mtd->erasesize;
+ offset *= remain_start_block;
+ pr_err(">>>> off 0x%llx\n", offset);
+ error = mtd->_block_isbad(mtd, offset);
+ if (error == FACTORY_BAD_BLOCK_ERROR) {
+ remain_start_block--;
+ continue;
+ }
+ remain_start_block--;
+ } while (++remain_block < remain_tatol_block);
+
+ sinfo->start_block = (int)(offset >> phys_erase_shift);
+ pr_err("%s,%d : secure start blk=%d end_blk=%d\n",
+ __func__, __LINE__,
+ sinfo->start_block,
+ sinfo->end_block);
+#else
+ int bad_blk_cnt = 0;
+
+ offset = mtd->size - mtd->erasesize;
+ /* if without keys, scan secureblocks @ the tails */
+ remain_start_block = (int)(offset >> phys_erase_shift);
+ remain_block = 0;
+ remain_tatol_block = REMAIN_BLOCK_NUM;
+ sinfo->start_block = remain_start_block;
+ sinfo->end_block = remain_start_block;
+ bad_blk_cnt = 0;
+ do {
+ offset = mtd->erasesize;
+ offset *= remain_start_block;
+ error = mtd->_block_isbad(mtd, offset);
+ if (error == FACTORY_BAD_BLOCK_ERROR) {
+ sinfo->start_block--;
+ remain_start_block--;
+ continue;
+ }
+ remain_start_block--;
+ } while (++remain_block < remain_tatol_block);
+ sinfo->start_block -= (remain_block - 1);
+ pr_err("secure start_blk=%d,end_blk=%d,%s:%d\n",
+ sinfo->start_block,
+ sinfo->end_block,
+ __func__, __LINE__);
+#endif
+
+ tmp_blk = start_blk = sinfo->start_block;
+ secure_blk = 0;
+ do {
+
+ offset = mtd->erasesize;
+ offset *= start_blk;
+ error = mtd->_block_isbad(mtd, offset);
+ if (error)
+ continue;
+ aml_oob_ops->mode = MTD_OPS_AUTO_OOB;
+ aml_oob_ops->len = mtd->writesize;
+ aml_oob_ops->ooblen = sizeof(struct secure_oobinfo_t);
+ aml_oob_ops->ooboffs = aml_oob_region.offset;
+ aml_oob_ops->datbuf = data_buf;
+ aml_oob_ops->oobbuf = secure_oob_buf;
+
+ memset((uint8_t *)aml_oob_ops->datbuf,
+ 0x0, mtd->writesize);
+ memset((uint8_t *)aml_oob_ops->oobbuf,
+ 0x0, aml_oob_ops->ooblen);
+
+ error = mtd->_read_oob(mtd, offset, aml_oob_ops);
+ if ((error != 0) && (error != -EUCLEAN)) {
+ pr_err("blk check good but read failed: %llx, %d\n",
+ (uint64_t)offset, error);
+ continue;
+ }
+
+ sinfo->secure_init = 1;
+ if ((secure_oobinfo->name
+ == SECURE_STORE_MAGIC)) {
+ sinfo->secure_valid = 1;
+ if (svn->phy_blk_addr >= 0) {
+ fn = alloc_fn();
+ if (fn == NULL) {
+ pr_err("%s %d no mem for secure_free_node\n",
+ __func__, __LINE__);
+ err = -ENOMEM;
+ goto exit;
+ }
+ fn->dirty_flag = 1;
+ if (secure_oobinfo->timestamp
+ > svn->timestamp) {
+ fn->phy_blk_addr
+ = svn->phy_blk_addr;
+ svn->phy_blk_addr
+ = start_blk;
+ svn->phy_page_addr = 0;
+ svn->timestamp
+ = secure_oobinfo->timestamp;
+ } else
+ fn->phy_blk_addr = start_blk;
+ if (sinfo->secure_free_node == NULL)
+ sinfo->secure_free_node = fn;
+ else {
+ tn = sinfo->secure_free_node;
+ while (tn->next != NULL)
+ tn = tn->next;
+ tn->next = fn;
+ }
+ } else {
+ svn->phy_blk_addr = start_blk;
+ svn->phy_page_addr = 0;
+ svn->timestamp = secure_oobinfo->timestamp;
+ }
+ } else if (secure_blk < max_secure_blk) {
+ fn = alloc_fn();
+ if (fn == NULL) {
+ err = -ENOMEM;
+ goto exit;
+ }
+ fn->phy_blk_addr = start_blk;
+ if (sinfo->secure_free_node == NULL) {
+ sinfo->secure_free_node = fn;
+ } else {
+ tn = sinfo->secure_free_node;
+ pn = tn;
+ while (tn != NULL) {
+ if (tn->dirty_flag == 1)
+ break;
+ pn = tn;
+ tn = tn->next;
+ }
+ if (pn == tn) {
+ fn->next = tn;
+ sinfo->secure_free_node = fn;
+ } else {
+ pn->next = fn;
+ fn->next = tn;
+ }
+ }
+ }
+ secure_blk++;
+
+ if ((secure_blk >= max_secure_blk)
+ && (sinfo->secure_valid == 1))
+ break;
+ } while ((++start_blk) <= sinfo->end_block);
+
+ if (sinfo->secure_valid == 1) {
+ aml_oob_ops->mode = MTD_OPS_AUTO_OOB;
+ aml_oob_ops->len = mtd->writesize;
+ aml_oob_ops->ooblen = sizeof(struct secure_oobinfo_t);
+ aml_oob_ops->ooboffs = aml_oob_region.offset;
+ aml_oob_ops->datbuf = data_buf;
+ aml_oob_ops->oobbuf = secure_oob_buf;
+
+ for (i = 0; i < pages_per_blk; i++) {
+
+ memset((uint8_t *)aml_oob_ops->datbuf,
+ 0x0, mtd->writesize);
+ memset((uint8_t *)aml_oob_ops->oobbuf,
+ 0x0, aml_oob_ops->ooblen);
+
+ offset = svn->phy_blk_addr;
+ offset *= mtd->erasesize;
+ offset += i * mtd->writesize;
+ error = mtd->_read_oob(mtd, offset, aml_oob_ops);
+ if ((error != 0) && (error != -EUCLEAN)) {
+ pr_err("blk check good but read failed: %llx, %d\n",
+ (uint64_t)offset, error);
+ continue;
+ }
+
+ if (secure_oobinfo->name == SECURE_STORE_MAGIC)
+ svn->phy_page_addr = i;
+ else
+ break;
+ }
+ }
+ if ((mtd->writesize < CONFIG_SECURE_SIZE)
+ && (sinfo->secure_valid == 1)) {
+ i = (CONFIG_SECURE_SIZE + mtd->writesize - 1) / mtd->writesize;
+ svn->phy_page_addr -= (i - 1);
+ }
+
+ pr_err("secure_valid_node->add =%d\n",
+ svn->phy_blk_addr);
+ if (sinfo->secure_free_node)
+ pr_err("secure_free_node->add =%d\n",
+ sinfo->secure_free_node->phy_blk_addr);
+
+ offset = svn->phy_blk_addr;
+ offset *= mtd->erasesize;
+ offset += mtd->writesize
+ * svn->phy_page_addr;
+ pr_err("aml nand secure info valid addr: %llx\n", (uint64_t)offset);
+ pr_err("CONFIG_SECURE_SIZE=0x%x;\n", CONFIG_SECURE_SIZE);
+
+ kfree(data_buf);
+ kfree(aml_oob_ops);
+ return 0;
+
+exit:
+ kfree(data_buf);
+ data_buf = NULL;
+ kfree(aml_oob_ops);
+ aml_oob_ops = NULL;
+ kfree(aml_chip->aml_nandsecure_info->secure_valid_node);
+ aml_chip->aml_nandsecure_info->secure_valid_node = NULL;
+ kfree(aml_chip->aml_nandsecure_info);
+ aml_chip->aml_nandsecure_info = NULL;
+ kfree(fn);
+ fn = NULL;
+ return err;
+
+}
+
+static int secure_info_check(struct mtd_info *mtd)
+{
+ struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd);
+ struct secure_t *secure_ptr;
+ int error = 0;
+
+ error = aml_nand_secure_init(mtd);
+ if (error)
+ return error;
+
+ secure_ptr = kzalloc(sizeof(struct secure_t), GFP_KERNEL);
+ if (secure_ptr == NULL)
+ return -ENOMEM;
+ /* default secure data set to a5; */
+ memset(secure_ptr, 0xa5, sizeof(struct secure_t));
+
+ if (aml_chip->aml_nandsecure_info->secure_valid == 1) {
+
+ goto exit;
+ } else {
+ pr_info("nand secure info save\n");
+ error = aml_nand_save_secure(mtd, (uint8_t *)secure_ptr);
+ if (error)
+ pr_err("nand secure info save failed\n");
+ }
+
+exit:
+ kfree(secure_ptr);
+ return 0;
+
+}
+
+int secure_storage_nand_read(char *buf, uint32_t len)
+{
+ int err, size;
+ uint8_t *storage_buf = NULL;
+
+ if (nand_secure_mtd == NULL) {
+ pr_err("secure storage is init fail\n");
+ return 1;
+ }
+ storage_buf = kzalloc(CONFIG_SECURE_SIZE, GFP_KERNEL);
+ if (storage_buf == NULL)
+ return -ENOMEM;
+ err = aml_nand_read_secure(nand_secure_mtd, 0, storage_buf);
+ if (err < 0) {
+ pr_err("%s:%d,read fail\n", __func__, __LINE__);
+ kfree(storage_buf);
+ return err;
+ } else if (err == 1) {
+ pr_err("%s:%d,no any key\n", __func__, __LINE__);
+ return 0;
+ }
+
+ if (len > CONFIG_SECURE_SIZE) {
+ size = CONFIG_SECURE_SIZE;
+ pr_info("%s() %d: len %d, %d, act %d\n",
+ __func__, __LINE__,
+ len, CONFIG_SECURE_SIZE,
+ CONFIG_SECURE_SIZE);
+ } else
+ size = len;
+
+ memcpy(buf, storage_buf, size);
+ kfree(storage_buf);
+ return 0;
+}
+
+int secure_storage_nand_write(char *buf, uint32_t len)
+{
+ int err = 0, size;
+ uint8_t *storage_buf = NULL;
+
+ if (nand_secure_mtd == NULL) {
+ pr_err("secure storage init fail\n");
+ return 1;
+ }
+ storage_buf = kzalloc(CONFIG_SECURE_SIZE, GFP_KERNEL);
+ if (storage_buf == NULL)
+ return -ENOMEM;
+
+ if (len > CONFIG_SECURE_SIZE) {
+ size = CONFIG_SECURE_SIZE;
+ pr_err("%s()%d: len:%x %x, act %x byte\n",
+ __func__, __LINE__,
+ len, CONFIG_SECURE_SIZE,
+ CONFIG_SECURE_SIZE);
+ } else
+ size = len;
+ memcpy(storage_buf, buf, size);
+ err = aml_nand_save_secure(nand_secure_mtd, storage_buf);
+ if (err)
+ pr_err("%s:%d,secure storage write fail\n",
+ __func__, __LINE__);
+
+ kfree(storage_buf);
+ return err;
+}
+
+int secure_device_init(struct mtd_info *mtd)
+{
+ int ret = 0;
+
+ nand_secure_mtd = mtd;
+ pr_info("%s(): %d\n", __func__, __LINE__);
+
+ ret = secure_info_check(mtd);
+ if (ret)
+ pr_err("invalid secure info\n");
+
+ return ret;
+
+}
+