mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-09 04:10:18 +09:00
mtd: nand: add nandkey and secure storge
PD#148390: mtd: nand: add nandkey and secure storage Change-Id: I220688cd14d8bf16105871e0bf0b233591b1bd33 Signed-off-by: yi.zeng <yi.zeng@amlogic.com>
This commit is contained in:
@@ -13885,6 +13885,9 @@ F: sound/soc/codecs/amlogic/aml_pmu3.h
|
||||
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>
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
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
|
||||
|
||||
@@ -117,19 +117,23 @@ struct _ext_info {
|
||||
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;
|
||||
};
|
||||
|
||||
@@ -416,6 +420,9 @@ struct aml_nand_bch_desc {
|
||||
|
||||
#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;
|
||||
@@ -484,7 +491,16 @@ struct aml_nandkey_info_t {
|
||||
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;
|
||||
@@ -541,6 +557,8 @@ struct aml_nand_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;
|
||||
@@ -800,11 +818,11 @@ int aml_key_init(struct aml_nand_chip *aml_chip);
|
||||
|
||||
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);*/
|
||||
|
||||
@@ -859,6 +877,8 @@ extern uint8_t nand_boot_flag;
|
||||
/*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);
|
||||
|
||||
@@ -306,6 +306,7 @@ static int aml_nand_add_partition(struct aml_nand_chip *aml_chip)
|
||||
unsigned int bad_blk_addr[128];
|
||||
#ifdef CONFIG_AMLOGIC_M8B_NANDKEY
|
||||
uint64_t key_block;
|
||||
uint64_t secure_block = 0;
|
||||
#endif
|
||||
|
||||
mini_part_size =
|
||||
@@ -340,6 +341,10 @@ static int aml_nand_add_partition(struct aml_nand_chip *aml_chip)
|
||||
#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
|
||||
|
||||
@@ -457,6 +462,8 @@ static int aml_nand_add_partition(struct aml_nand_chip *aml_chip)
|
||||
} else {
|
||||
temp_parts->size -= key_block*mtd->erasesize;
|
||||
}
|
||||
if (meson_secure_enabled())
|
||||
temp_parts->size -= secure_block*mtd->erasesize;
|
||||
|
||||
#endif
|
||||
}
|
||||
@@ -2617,7 +2624,11 @@ int aml_nand_init(struct aml_nand_chip *aml_chip)
|
||||
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 */
|
||||
|
||||
|
||||
@@ -58,8 +58,9 @@ int aml_nand_rsv_erase_protect(struct mtd_info *mtd, unsigned int block_addr)
|
||||
{
|
||||
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)
|
||||
|
||||
@@ -1,114 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
@@ -1022,20 +1022,32 @@ void __attribute__((unused)) nand_info_page_prepare(
|
||||
**/
|
||||
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);
|
||||
@@ -1059,8 +1071,8 @@ void __attribute__((unused)) nand_info_page_prepare(
|
||||
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.
|
||||
@@ -1078,21 +1090,50 @@ void __attribute__((unused)) nand_info_page_prepare(
|
||||
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,
|
||||
|
||||
993
drivers/amlogic/mtd_meson8b/nand_key.c
Normal file
993
drivers/amlogic/mtd_meson8b/nand_key.c
Normal file
@@ -0,0 +1,993 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
743
drivers/amlogic/mtd_meson8b/secure_storage.c
Normal file
743
drivers/amlogic/mtd_meson8b/secure_storage.c
Normal file
@@ -0,0 +1,743 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user