355 lines
9.4 KiB
C
355 lines
9.4 KiB
C
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
|
/*
|
|
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <amlogic/storage.h>
|
|
#include <linux/mtd/mtd.h>
|
|
#include <asm/amlogic/arch/cpu_config.h>
|
|
#include <amlogic/store_wrapper.h>
|
|
#include <u-boot/sha256.h>
|
|
#include <amlogic/aml_mmc.h>
|
|
|
|
#define debugP(fmt...) //printf("Dbg[WRP]L%d:", __LINE__),printf(fmt)
|
|
#define errorP(fmt...) printf("Err[WRP]L%d:", __LINE__),printf(fmt)
|
|
#define wrnP(fmt...) printf("wrn:"fmt)
|
|
#define MsgP(fmt...) printf("[WRP]"fmt)
|
|
|
|
//change part logic offset to physical address for mtd, not changed if not MTD
|
|
static int mtd_find_phy_off_by_lgc_off(const char* partName, const loff_t logicAddr, loff_t* phyAddr)
|
|
{
|
|
if (!(BOOT_NAND_MTD == store_get_type() || BOOT_SNAND == store_get_type())) {
|
|
*phyAddr = logicAddr;
|
|
return 0;
|
|
}
|
|
#ifndef CONFIG_CMD_MTD
|
|
MsgP("Exception, boottype is MTD or snand, BUT CMD_MTD not defined\n");
|
|
#else
|
|
struct mtd_info * mtdPartInf = NULL;
|
|
mtdPartInf = get_mtd_device_nm(partName);
|
|
if (!mtdPartInf) {
|
|
errorP("device(%s) is null\n", partName);
|
|
return CMD_RET_FAILURE;
|
|
}
|
|
|
|
if (IS_ERR(mtdPartInf)) {
|
|
errorP("device(%s) is err\n", partName);
|
|
return CMD_RET_FAILURE;
|
|
}
|
|
|
|
const unsigned eraseSz = mtdPartInf->erasesize;
|
|
const unsigned offsetInBlk = logicAddr & (eraseSz - 1);
|
|
loff_t off = 0;
|
|
int canSpeedUp = 0;
|
|
static struct {
|
|
loff_t lastblkPhyOff;
|
|
loff_t lastblkLgcOff;
|
|
char partName[64];
|
|
}_map4SpeedUp = {0};
|
|
if ( !strcmp(partName, _map4SpeedUp.partName) && logicAddr >= _map4SpeedUp.lastblkLgcOff) {
|
|
canSpeedUp = 1;
|
|
} else {
|
|
_map4SpeedUp.lastblkLgcOff = _map4SpeedUp.lastblkPhyOff = 0;
|
|
strncpy(_map4SpeedUp.partName, partName, 63);
|
|
}
|
|
|
|
if ( canSpeedUp ) {
|
|
if ( logicAddr >= _map4SpeedUp.lastblkLgcOff &&
|
|
logicAddr < _map4SpeedUp.lastblkLgcOff + eraseSz) {
|
|
*phyAddr = _map4SpeedUp.lastblkPhyOff + offsetInBlk;
|
|
return 0;
|
|
}
|
|
_map4SpeedUp.lastblkPhyOff += eraseSz;
|
|
_map4SpeedUp.lastblkLgcOff += eraseSz;
|
|
off = _map4SpeedUp.lastblkPhyOff;
|
|
}
|
|
for (; off < mtdPartInf->size; off += eraseSz, _map4SpeedUp.lastblkPhyOff += eraseSz) {
|
|
if (mtd_block_isbad(mtdPartInf, off)) {
|
|
MsgP("bad blk at %08llx\n", (unsigned long long)off);
|
|
} else {
|
|
if ( logicAddr >= _map4SpeedUp.lastblkLgcOff &&
|
|
logicAddr < _map4SpeedUp.lastblkLgcOff + eraseSz) {
|
|
*phyAddr = _map4SpeedUp.lastblkPhyOff + offsetInBlk;
|
|
return 0;
|
|
}
|
|
_map4SpeedUp.lastblkLgcOff += eraseSz;
|
|
}
|
|
}
|
|
#endif// #ifndef CONFIG_CMD_MTD
|
|
return __LINE__;
|
|
}
|
|
|
|
int store_logic_write(const char *name, loff_t off, size_t size, void *buf)
|
|
{
|
|
loff_t phyAddr = 0;
|
|
if (mtd_find_phy_off_by_lgc_off(name, off, &phyAddr)) {
|
|
errorP("Fail find phy addr\n");
|
|
return -__LINE__;
|
|
}
|
|
return store_write(name, phyAddr, size, buf);
|
|
}
|
|
|
|
int store_logic_read(const char *name, loff_t off, size_t size, void *buf)
|
|
{
|
|
loff_t phyAddr = 0;
|
|
if (mtd_find_phy_off_by_lgc_off(name, off, &phyAddr)) {
|
|
errorP("Fail find phy addr\n");
|
|
return -__LINE__;
|
|
}
|
|
return store_read(name, phyAddr, size, buf);
|
|
}
|
|
|
|
//get partition logic size
|
|
u64 store_logic_cap(const char* partName)
|
|
{
|
|
if (!(BOOT_NAND_MTD == store_get_type() || BOOT_SNAND == store_get_type())) {
|
|
return store_part_size(partName);
|
|
}
|
|
|
|
#ifndef CONFIG_CMD_MTD
|
|
MsgP("Exception, boottype is MTD or snand, BUT CMD_MTD not defined\n");
|
|
return 0;
|
|
#else
|
|
//get mtd part logic size (i.e, not including the bad blocks)
|
|
struct mtd_info * mtdPartInf = NULL;
|
|
uint64_t partSzLgc = 0;
|
|
loff_t off = 0;
|
|
|
|
mtdPartInf = get_mtd_device_nm(partName);
|
|
if (IS_ERR(mtdPartInf)) {
|
|
errorP("device(%s) is err\n", partName);
|
|
return CMD_RET_FAILURE;
|
|
}
|
|
const unsigned eraseSz = mtdPartInf->erasesize;
|
|
const uint64_t partSzPhy = mtdPartInf->size;
|
|
|
|
partSzLgc = partSzPhy;
|
|
for (; off < partSzPhy; off += eraseSz) {
|
|
if (mtd_block_isbad(mtdPartInf, off)) {
|
|
partSzLgc -= eraseSz;
|
|
}
|
|
}
|
|
return partSzLgc;
|
|
#endif// #ifndef CONFIG_CMD_MTD
|
|
}
|
|
|
|
int store_gpt_ops(size_t sz, void *buf, int is_wr)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (!sz || sz >= 0x100000) {
|
|
errorP("sz 0x%zx to large\n", sz);
|
|
return -__LINE__;
|
|
}
|
|
|
|
if (is_wr)
|
|
ret = store_gpt_write(buf);
|
|
else
|
|
ret = store_gpt_read(buf);
|
|
|
|
return ret;
|
|
}
|
|
|
|
#ifdef CONFIG_CMD_MMC
|
|
static int _amlmmc_rdwr_bootloader(int dev, int map, unsigned int size, void *src, int iswrite)
|
|
{
|
|
int ret = 0, i, count = 3;
|
|
unsigned long n;
|
|
char *partname[3] = {"user", "boot0", "boot1"};
|
|
struct mmc *mmc = NULL;
|
|
lbaint_t start = 1, blkcnt;
|
|
|
|
mmc = find_mmc_device(dev);
|
|
if (!mmc) {
|
|
printf("%s() %d: not valid emmc %d\n", __func__, __LINE__, dev);
|
|
return -1;
|
|
}
|
|
/* make sure mmc is initialized! */
|
|
ret = mmc_init(mmc);
|
|
if (ret) {
|
|
printf("%s() %d: emmc %d init %d\n", __func__, __LINE__, dev, ret);
|
|
return -2;
|
|
}
|
|
|
|
blkcnt = (size + mmc->read_bl_len - 1) / mmc->read_bl_len;
|
|
|
|
/* erase bootloader in user/boot0/boot1 */
|
|
for (i = 0; i < count; i++) {
|
|
if (map & (1 << i)) {
|
|
if (blk_select_hwpart_devnum(UCLASS_MMC, 1, i)) {
|
|
printf("%s() %d: switch dev %d to %s fail\n",
|
|
__func__, __LINE__, dev, partname[i]);
|
|
ret = -3;
|
|
break;
|
|
}
|
|
|
|
printf("To %s " LBAFU " blocks at " LBAFU " @%s\n",
|
|
iswrite ? "Write" : "Read", blkcnt, start, partname[i]);
|
|
if (iswrite)
|
|
n = blk_dwrite(mmc_get_blk_desc(mmc), start, blkcnt, src);
|
|
else
|
|
n = blk_dread(mmc_get_blk_desc(mmc), start, blkcnt, src);
|
|
if (n != blkcnt) {
|
|
printf("mmc rd/wr %s failed\n", partname[i]);
|
|
ret = -4;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* try to switch back to user. */
|
|
if (blk_select_hwpart_devnum(UCLASS_MMC, 1, 0)) {
|
|
errorP("Fail switch to user\n");
|
|
return -5;
|
|
}
|
|
return ret;
|
|
}
|
|
#else
|
|
static int _amlmmc_rdwr_bootloader(int dev, int map, unsigned int size, void *src, int iswrite)
|
|
{
|
|
errorP("MMC not enable\n");
|
|
return -1;
|
|
}
|
|
#endif// #ifdef CONFIG_CMD_MMC
|
|
|
|
static int _amlmmc_write_bootloader(int dev, int map, unsigned int size, void *src)
|
|
{
|
|
return _amlmmc_rdwr_bootloader(dev, map, size, src, 1);
|
|
}
|
|
|
|
static int _amlmmc_read_bootloader(int dev, int map, unsigned int size, void *src)
|
|
{
|
|
return _amlmmc_rdwr_bootloader(dev, map, size, src, 0);
|
|
}
|
|
|
|
int store_bootloader_ops(int ops, const char *name, void *pdata, unsigned int szdata)
|
|
{
|
|
int map = 0;
|
|
int cpy = 0;
|
|
int ret = -1;
|
|
const int DEV = 1;
|
|
const char *_names[] = {"bootloader-user", "bootloader-boot0", "bootloader-boot1"};
|
|
|
|
for (; cpy < ARRAY_SIZE(_names); ++cpy) {
|
|
if (!strcmp(_names[cpy], name))
|
|
break;
|
|
}
|
|
if (cpy > 2) {
|
|
errorP("inval cpy %d\n", cpy);
|
|
return -__LINE__;
|
|
}
|
|
map = (1<<cpy);
|
|
switch (ops) {
|
|
case _STORE_BOOT_OP_WRITE: {
|
|
ret = _amlmmc_write_bootloader(DEV, map, szdata, pdata);
|
|
break;
|
|
}
|
|
case _STORE_BOOT_OP_READ: {
|
|
ret = _amlmmc_read_bootloader(DEV, map, szdata, pdata);
|
|
break;
|
|
}
|
|
case _STORE_BOOT_OP_ERASE:
|
|
default:
|
|
errorP("inval op:%d\n", ops);
|
|
return -__LINE__;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
#ifndef CONFIG_AML_V3_FACTORY_BURN//storage wrapper
|
|
#define FB_ERR(fmt ...) printf("[ERR]%s:L%d:", __func__, __LINE__), printf(fmt)
|
|
#define FB_EXIT(fmt ...) do { FB_ERR(fmt); return -__LINE__; } while (0)
|
|
#define FB_MSG(fmt ...) printf("[MSG]" fmt)
|
|
#define FBS_EXIT(buf, fmt ...) do { FBS_ERR(buf, fmt); return -__LINE__; } while (0)
|
|
|
|
int bootloader_copy_sz(void)
|
|
{
|
|
return store_boot_copy_size("bootloader");
|
|
}
|
|
|
|
static int _bootloader_write(u8 *dataBuf, unsigned off, unsigned binSz, const char *bootName)
|
|
{
|
|
int iCopy = 0;
|
|
const int bootCpyNum = store_boot_copy_num(bootName);
|
|
const int bootCpySz = (int)store_boot_copy_size(bootName);
|
|
|
|
FB_MSG("[%s] CpyNum %d, bootCpySz 0x%x\n", bootName, bootCpyNum, bootCpySz);
|
|
if (binSz + off > bootCpySz)
|
|
FB_EXIT("bootloader sz(0x%x) + off(0x%x) > bootCpySz 0x%x\n",
|
|
binSz, off, bootCpySz);
|
|
|
|
if (off) {
|
|
FB_ERR("current only 0 supported!\n");
|
|
return -__LINE__;
|
|
}
|
|
|
|
for (; iCopy < bootCpyNum; ++iCopy) {
|
|
int ret = store_boot_write(bootName, iCopy, binSz, dataBuf);
|
|
|
|
if (ret)
|
|
FB_EXIT("FAil in program[%s] at copy[%d]\n", bootName, iCopy);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int bootloader_write(u8 *dataBuf, unsigned off, unsigned binSz)
|
|
{
|
|
return _bootloader_write(dataBuf, off, binSz, "bootloader");
|
|
}
|
|
|
|
static int _bootloader_read(u8 *pBuf, unsigned off, unsigned binSz, const char *bootName)
|
|
{
|
|
int iCopy = 0;
|
|
const int bootCpyNum = store_boot_copy_num(bootName);
|
|
const int bootCpySz = (int)store_boot_copy_size(bootName);
|
|
int validCpyNum = bootCpyNum;//at least valid cpy num
|
|
int actVldCpyNum = 0;//actual valid copy num
|
|
|
|
if (binSz + off > bootCpySz) {
|
|
FB_ERR("bootloader sz(0x%x) + off(0x%x) > bootCpySz 0x%x\n", binSz, off, bootCpySz);
|
|
return -__LINE__;
|
|
}
|
|
if (off)
|
|
FB_EXIT("current only 0 supported!\n");
|
|
|
|
for (iCopy = 0; iCopy < bootCpyNum; ++iCopy) {
|
|
void *dataBuf = iCopy ? pBuf + binSz : pBuf;
|
|
int ret = store_boot_read(bootName, iCopy, binSz, dataBuf);
|
|
|
|
if (ret) {
|
|
FB_ERR("Fail to read boot[%s] at copy[%d]\n", bootName, iCopy);
|
|
continue;
|
|
}
|
|
if (iCopy) {
|
|
if (!actVldCpyNum) {//NO valid cpy yet, so copy0 NOT valid also
|
|
memcpy(pBuf, dataBuf, binSz);
|
|
}
|
|
if (memcmp(pBuf, dataBuf, binSz)) {
|
|
FB_ERR("[%s] copy[%d] content NOT the same as copy[0]\n",
|
|
bootName, iCopy);//maybe ddr err as nand not err
|
|
memset(pBuf, 0, binSz);
|
|
return -__LINE__;
|
|
}
|
|
}
|
|
++actVldCpyNum;
|
|
}
|
|
if (actVldCpyNum < validCpyNum) {
|
|
FB_ERR("[%s]actual valid copy num %d < configured num %d\n",
|
|
bootName, actVldCpyNum, validCpyNum);
|
|
memset(pBuf, 0, binSz);
|
|
return -__LINE__;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int bootloader_read(u8 *pBuf, unsigned off, unsigned binSz)
|
|
{
|
|
return _bootloader_read(pBuf, off, binSz, "bootloader");
|
|
}
|
|
#endif// #ifndef CONFIG_AML_V3_FACTORY_BURN//storage wrapper
|
|
|