2123 lines
54 KiB
C
2123 lines
54 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
#include <linux/kernel.h>
|
|
#include <sunxi_board.h>
|
|
#include <common.h>
|
|
#ifdef CONFIG_AW_MTD_SPINAND
|
|
#include <linux/mtd/aw-spinand.h>
|
|
#endif
|
|
#ifdef CONFIG_AW_MTD_RAWNAND
|
|
#include <linux/mtd/aw-rawnand.h>
|
|
#endif
|
|
#include <sunxi_mbr.h>
|
|
#include <ubi_uboot.h>
|
|
#include <private_uboot.h>
|
|
#include <linux/mtd/mtd.h>
|
|
#include <linux/sizes.h>
|
|
#include <linux/mtd/aw-ubi.h>
|
|
/*
|
|
* undef crc32 -> crc32_le in linux/crc32.h
|
|
* crc32 for sunxi_mbr
|
|
*/
|
|
#undef crc32
|
|
#include <u-boot/crc.h>
|
|
#ifdef CONFIG_AW_MTD_SPINAND
|
|
#include "spinand/sunxi-spinand.h"
|
|
#endif
|
|
|
|
|
|
#define GAP_BUF_MAX_SECTS (128)
|
|
#define TYPE_STATIC_VOLUME (1 << 0)
|
|
|
|
struct ubi_nand_vol {
|
|
char name[PART_NAME_MAX_SIZE];
|
|
unsigned int addr;
|
|
unsigned int sects;
|
|
unsigned int type;
|
|
};
|
|
|
|
struct ubi_mbr {
|
|
unsigned int part_cnt;
|
|
struct ubi_nand_vol vols[NAND_MAX_PART_CNT];
|
|
};
|
|
|
|
struct ubi_vol_status {
|
|
#define INIT_UBI_VOL 0
|
|
#define CREATE_UBI_VOL 1
|
|
#define WRITE_UBI_VOL 2
|
|
int state;
|
|
#define DYNAMIC_VOL 0
|
|
#define STATIC_VOL 1
|
|
int type;
|
|
unsigned int plan_wr_sects;
|
|
unsigned int written_sects;
|
|
|
|
struct ubi_nand_vol *vol;
|
|
};
|
|
|
|
struct ubi_status {
|
|
int last_partno;
|
|
struct ubi_vol_status vols[NAND_MAX_PART_CNT];
|
|
};
|
|
|
|
struct ubi_info {
|
|
/*
|
|
* saving mbr with follow feature:
|
|
* 1. copy from sunxi_mbr_t
|
|
* 2. add mbr volume from sunxi_mbr_t
|
|
* 3. volume addr relative to user space address.
|
|
* it means the first volume mbr's addr is 0x00
|
|
* 4. volume sects do not align
|
|
*/
|
|
struct ubi_mbr nand_mbr;
|
|
/*
|
|
* saving mbr with follow feature:
|
|
* 1. copy from nand_mbr as above.
|
|
* 2. volume addr relative to the whole flash address.
|
|
* it means the first volume mbr's addr is NOT 0x00.
|
|
* the first volume always be the start of mtd device.
|
|
* 3. volume sects do not align
|
|
*/
|
|
struct ubi_mbr ubi_mbr;
|
|
struct ubi_status ubi_status;
|
|
struct ubi_mtd_info mtd_info;
|
|
|
|
char last_name[PART_NAME_MAX_SIZE];
|
|
int last_partno;
|
|
unsigned int last_offset;
|
|
char mtdids[20];
|
|
char mtdparts[512];
|
|
unsigned int ubootblks;
|
|
|
|
unsigned int last_vol_sects;
|
|
};
|
|
static struct ubi_info g_ubi_info;
|
|
|
|
extern int sunxi_mbr_convert_to_gpt(void *sunxi_mbr_buf, char *gpt_buf, int storage_type);
|
|
|
|
static unsigned int to_align(unsigned int size, unsigned int limit)
|
|
{
|
|
if (limit)
|
|
return (size + limit - 1) / limit * limit;
|
|
return size;
|
|
}
|
|
|
|
static inline struct ubi_info *get_ubi_info(void)
|
|
{
|
|
return &g_ubi_info;
|
|
}
|
|
#define ubi_to_nand_mbr(ubi_info) (&(ubi_info)->nand_mbr)
|
|
#define ubi_to_ubi_mbr(ubi_info) (&(ubi_info)->ubi_mbr)
|
|
#define ubi_to_ubi_status(ubi_info) (&(ubi_info)->ubi_status)
|
|
#define ubi_to_mtd(ubi_info) (&(ubi_info)->mtd_info)
|
|
#if 0
|
|
static void randomize(unsigned int *buf, unsigned int len, int seed)
|
|
{
|
|
int i;
|
|
|
|
srand(seed);
|
|
for (i = 0; i < len; i++)
|
|
*buf++ = rand();
|
|
}
|
|
#endif
|
|
|
|
static void print_ubi_mbr(struct ubi_mbr *mbr)
|
|
{
|
|
int i;
|
|
|
|
pr_info("%-8s %-10s %-10s %-10s %s\n", "partno", "addr", "sects",
|
|
"type", "name");
|
|
for (i = 0; i < mbr->part_cnt; i++) {
|
|
struct ubi_nand_vol *vol = &mbr->vols[i];
|
|
|
|
pr_info("%-8d 0x%08x 0x%08x 0x%08x %s\n", i,
|
|
vol->addr, vol->sects, vol->type, vol->name);
|
|
}
|
|
}
|
|
|
|
static int get_partno_by_addr(unsigned int offset, unsigned int sec_cnt)
|
|
{
|
|
int i;
|
|
unsigned int addr, sects;
|
|
struct ubi_info *ubinfo = get_ubi_info();
|
|
struct ubi_mbr *nand_mbr = ubi_to_nand_mbr(ubinfo);
|
|
|
|
if (!nand_mbr->part_cnt) {
|
|
pr_err("not found nand_mbr\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
for (i = 0; i < nand_mbr->part_cnt; i++) {
|
|
addr = nand_mbr->vols[i].addr;
|
|
sects = nand_mbr->vols[i].sects;
|
|
if (offset >= addr + sects)
|
|
continue;
|
|
if (offset + sec_cnt > addr + sects) {
|
|
pr_err("offset 0x%x with sects 0x%x over volume %s [0x%x 0x%x)\n",
|
|
offset, sec_cnt,
|
|
nand_mbr->vols[i].name, addr,
|
|
addr + sects);
|
|
break;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
pr_err("get partno from nand_mbr failed: offset 0x%x sects %u\n",
|
|
offset, sec_cnt);
|
|
print_ubi_mbr(nand_mbr);
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int get_volnum_by_nand_mbr_name(char *name)
|
|
{
|
|
int i;
|
|
struct ubi_info *ubinfo = get_ubi_info();
|
|
struct ubi_mbr *nand_mbr = ubi_to_nand_mbr(ubinfo);;
|
|
|
|
for (i = 0; i < nand_mbr->part_cnt; i++) {
|
|
if (name == NULL)
|
|
continue;
|
|
|
|
if (!strcmp((char *)nand_mbr->vols[i].name, name))
|
|
return i;
|
|
}
|
|
return -ENODEV;
|
|
}
|
|
|
|
|
|
static int get_volnum_by_name(char *name)
|
|
{
|
|
int i;
|
|
struct ubi_info *ubinfo = get_ubi_info();
|
|
struct ubi_mbr *ubi_mbr = ubi_to_ubi_mbr(ubinfo);;
|
|
|
|
for (i = 0; i < ubi_mbr->part_cnt; i++) {
|
|
if (name == NULL)
|
|
continue;
|
|
|
|
if (!strcmp((char *)ubi_mbr->vols[i].name, name))
|
|
return i;
|
|
}
|
|
return -ENODEV;
|
|
}
|
|
|
|
static int get_mtd_num_by_name(char *name)
|
|
{
|
|
int i;
|
|
struct ubi_info *ubinfo = get_ubi_info();
|
|
struct ubi_mtd_info *mtd_info = ubi_to_mtd(ubinfo);
|
|
|
|
if (name == NULL)
|
|
return -EINVAL;
|
|
|
|
for (i = 0; i < mtd_info->part_cnt; i++)
|
|
if (!strcmp(mtd_info->part[i].name, name))
|
|
return i;
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int get_mtd_num_to_attach(void)
|
|
{
|
|
int num;
|
|
|
|
num = sunxi_get_mtd_num_parts();
|
|
num = num ? num - 1 : -1;
|
|
|
|
pr_info("ubi attach the last part of mtd device: NO.%d\n", num);
|
|
|
|
return num;
|
|
}
|
|
|
|
static inline unsigned int offset_on_part(int partno, unsigned int addr)
|
|
{
|
|
struct ubi_info *ubinfo = get_ubi_info();
|
|
struct ubi_mbr *nand_mbr = ubi_to_nand_mbr(ubinfo);
|
|
|
|
return addr - nand_mbr->vols[partno].addr;
|
|
}
|
|
|
|
static int check_sunxi_mbr(sunxi_mbr_t *sunxi_mbr)
|
|
{
|
|
if (strncmp((const char *)sunxi_mbr->magic, SUNXI_MBR_MAGIC, 8)) {
|
|
pr_err("mbr magic error: %.8s wanted %s\n",
|
|
(char *)sunxi_mbr->magic, SUNXI_MBR_MAGIC);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (WORK_MODE_BOOT == get_boot_work_mode()) {
|
|
unsigned int crc = 0;
|
|
crc = crc32(0, (const unsigned char *)&sunxi_mbr->version,
|
|
SUNXI_MBR_SIZE - 4);
|
|
if (crc != sunxi_mbr->crc32) {
|
|
pr_err("mbr crc verify failed: wanted 0x%x get 0x%x\n",
|
|
sunxi_mbr->crc32, crc);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
if (!sunxi_mbr->PartCount) {
|
|
pr_err("partition count is zero, something error\n");
|
|
return -EINVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void adjust_sunxi_mbr(sunxi_mbr_t *org_mbr, unsigned int last_vol_sects)
|
|
{
|
|
int i = 0;
|
|
sunxi_mbr_t *tmp_mbr = org_mbr;
|
|
unsigned int partcount = org_mbr->PartCount;
|
|
|
|
if (last_vol_sects == org_mbr->array[partcount - 1].lenlo)
|
|
return;
|
|
|
|
org_mbr->array[partcount - 1].lenlo = last_vol_sects;
|
|
org_mbr->crc32 = crc32(0, (const unsigned char *)&org_mbr->version,
|
|
SUNXI_MBR_SIZE - 4);
|
|
for (i = 1; i < SUNXI_MBR_COPY_NUM; i++) {
|
|
tmp_mbr++;
|
|
tmp_mbr->array[partcount - 1].lenlo =
|
|
org_mbr->array[partcount - 1].lenlo;
|
|
tmp_mbr->crc32 = org_mbr->crc32;
|
|
}
|
|
}
|
|
|
|
static int init_nand_mbr(sunxi_mbr_t *sunxi_mbr, struct ubi_mbr *nand_mbr)
|
|
{
|
|
int i, ret;
|
|
|
|
if (!sunxi_mbr) {
|
|
pr_err("invalid sunxi_mbr\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = check_sunxi_mbr(sunxi_mbr);
|
|
if (ret)
|
|
return ret;
|
|
|
|
memset(nand_mbr, 0x00, sizeof(*nand_mbr));
|
|
|
|
if (!sunxi_mbr->PartCount) {
|
|
pr_err("no partition on sunxi_mbr\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* the first part is MBR */
|
|
nand_mbr->part_cnt = 1;
|
|
nand_mbr->vols[0].addr = 0x00;
|
|
nand_mbr->vols[0].sects = sunxi_mbr->array[0].addrlo;
|
|
nand_mbr->vols[0].type |= TYPE_STATIC_VOLUME;
|
|
strcpy((char *)nand_mbr->vols[0].name, "mbr");
|
|
|
|
nand_mbr->part_cnt += sunxi_mbr->PartCount;
|
|
|
|
for (i = 0; i < sunxi_mbr->PartCount; i++) {
|
|
nand_mbr->vols[i + 1].addr = sunxi_mbr->array[i].addrlo;
|
|
nand_mbr->vols[i + 1].sects = sunxi_mbr->array[i].lenlo;
|
|
strncpy((char *)nand_mbr->vols[i + 1].name,
|
|
(const char *)sunxi_mbr->array[i].name,
|
|
PART_NAME_MAX_SIZE);
|
|
nand_mbr->vols[i + 1].type = sunxi_mbr->array[i].user_type;
|
|
}
|
|
|
|
pr_info("MBR info (unalign):\n");
|
|
print_ubi_mbr(nand_mbr);
|
|
return 0;
|
|
}
|
|
|
|
static int init_mtd_info(struct ubi_mtd_info *mtd_info)
|
|
{
|
|
int ret = -EINVAL;
|
|
int i = 0;
|
|
struct ubi_mtd_part *mtd_part;
|
|
|
|
#ifdef CONFIG_AW_MTD_SPINAND
|
|
if (support_spinand())
|
|
ret = spinand_init_mtd_info(mtd_info);
|
|
#endif
|
|
#ifdef CONFIG_AW_MTD_RAWNAND
|
|
if (support_rawnand())
|
|
ret = rawnand_init_mtd_info(mtd_info);
|
|
#endif
|
|
|
|
if (ret) {
|
|
pr_err("%s-%d _init_mtd_info fail\n", __func__, __LINE__);
|
|
return ret;
|
|
}
|
|
|
|
pr_info("MTD info (%d)\n", mtd_info->part_cnt);
|
|
pr_info("pagesize: 0x%x\n", mtd_info->pagesize);
|
|
pr_info("blksize: 0x%x\n", mtd_info->blksize);
|
|
pr_info("%-4s %-10s %-10s %s\n", "num", "offset", "bytes", "name");
|
|
|
|
for (i = 0; i < mtd_info->part_cnt; i++) {
|
|
mtd_part = &mtd_info->part[i];
|
|
pr_info("%-4d 0x%08x 0x%08x %s\n",
|
|
mtd_part->partno, mtd_info->part[i].offset,
|
|
mtd_part->bytes, mtd_part->name);
|
|
}
|
|
return 0;
|
|
}
|
|
static void init_ubi_mbr(struct ubi_info *ubinfo)
|
|
{
|
|
int ret;
|
|
int i, k;
|
|
int mtdnum;
|
|
int32_t n_cur_part_len;
|
|
int32_t leb_sects;
|
|
int32_t ubi_min_vol_sects;
|
|
struct ubi_mbr *ubi_mbr = ubi_to_ubi_mbr(ubinfo);
|
|
struct ubi_mbr *nand_mbr = ubi_to_nand_mbr(ubinfo);
|
|
struct ubi_status *ubi_status = ubi_to_ubi_status(ubinfo);
|
|
struct ubi_mtd_info *mtd_info = ubi_to_mtd(ubinfo);
|
|
|
|
memset(ubi_mbr, 0x00, sizeof(struct ubi_mbr));
|
|
memset(ubi_status, 0x00, sizeof(struct ubi_status));
|
|
ubi_status->last_partno = -1;
|
|
|
|
mtdnum = get_mtd_num_to_attach();
|
|
|
|
/*
|
|
* multiplane with subpage, make it need only on super page to save
|
|
* EC header and VID header
|
|
*/
|
|
leb_sects = to_sects(mtd_info->blksize - 1 * mtd_info->pagesize);
|
|
ubi_min_vol_sects = leb_sects;
|
|
|
|
for (i = 0, k = 0; i < nand_mbr->part_cnt; i++) {
|
|
ret = get_mtd_num_by_name((char *)nand_mbr->vols[i].name);
|
|
if (ret >= 0)
|
|
continue;
|
|
|
|
memcpy(ubi_mbr->vols[k].name, nand_mbr->vols[i].name,
|
|
sizeof(nand_mbr->vols[i].name));
|
|
ubi_mbr->vols[k].type = nand_mbr->vols[i].type;
|
|
|
|
if (k)
|
|
ubi_mbr->vols[k].addr = ubi_mbr->vols[k - 1].addr +
|
|
ubi_mbr->vols[k - 1].sects;
|
|
else
|
|
ubi_mbr->vols[k].addr = mtdnum >= 0 ?
|
|
(unsigned int)to_sects(mtd_info->part[mtdnum].offset) : -1;
|
|
|
|
/* align leb_size */
|
|
n_cur_part_len = to_align(nand_mbr->vols[i].sects, leb_sects);
|
|
if (n_cur_part_len)
|
|
ubi_mbr->vols[k].sects = max_t(unsigned int,
|
|
n_cur_part_len, ubi_min_vol_sects);
|
|
else
|
|
ubi_mbr->vols[k].sects = 0;
|
|
|
|
k++;
|
|
ubi_mbr->part_cnt++;
|
|
}
|
|
|
|
pr_info("MBR info (align):\n");
|
|
print_ubi_mbr(ubi_mbr);
|
|
}
|
|
|
|
static int create_mtdparts_do(void)
|
|
{
|
|
int ret = 0;
|
|
char cmd[2][20];
|
|
char *argv[2];
|
|
|
|
argv[0] = cmd[0];
|
|
argv[1] = cmd[1];
|
|
|
|
memset(cmd, 0x00, sizeof(cmd));
|
|
sprintf(argv[0], "mtdparts");
|
|
sprintf(argv[1], "default");
|
|
ret = sunxi_do_mtdparts(0, 2, argv);
|
|
if (!ret) {
|
|
debug("mtdparts info:\n");
|
|
sunxi_do_mtdparts(0, 1, argv);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int ubi_attach_mtd_do(int mtdnum)
|
|
{
|
|
int ret;
|
|
char cmd[6][20];
|
|
char *argv[6];
|
|
char *mtd_name;
|
|
int i;
|
|
|
|
mtd_name = sunxi_get_mtdparts_name(mtdnum);
|
|
if (mtd_name == NULL) {
|
|
pr_err("mtd_name is NULL !!!\n");
|
|
return -EINVAL;
|
|
}
|
|
for (i = 0; i < 6; i++)
|
|
argv[i] = cmd[i];
|
|
|
|
memset(cmd, 0x00, sizeof(cmd));
|
|
sprintf(&cmd[0][0], "ubi");
|
|
sprintf(&cmd[1][0], "part");
|
|
sprintf(&cmd[2][0], "%s", mtd_name);
|
|
|
|
/* ubi part [part] [offset] */
|
|
ret = sunxi_do_ubi(0, 3, argv);
|
|
if (ret)
|
|
pr_err("ubi part %s err !\n", mtd_name);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
static int check_volume_existed_do(char *name)
|
|
{
|
|
char cmd[6][20];
|
|
char *argv[6];
|
|
int i;
|
|
|
|
if (get_volnum_by_name(name) < 0) {
|
|
pr_err("not found volume %s in mbr !!!\n", name);
|
|
return -ENODEV;
|
|
}
|
|
|
|
for (i = 0; i < 6; i++)
|
|
argv[i] = cmd[i];
|
|
|
|
memset(cmd, 0x00, sizeof(cmd));
|
|
sprintf(&cmd[0][0], "ubi");
|
|
sprintf(&cmd[1][0], "check");
|
|
sprintf(&cmd[2][0], "%s", name);
|
|
|
|
return sunxi_do_ubi(0, 3, argv);
|
|
}
|
|
|
|
static void check_and_adjust_ubi_mbr(struct ubi_nand_vol *vol, struct ubi_mbr *ubi_mbr, int partno)
|
|
{
|
|
int volumesize;
|
|
int i = 0;
|
|
char cmd[3][20];
|
|
char *argv[3];
|
|
|
|
for (i = 0; i < 3; i++)
|
|
argv[i] = cmd[i];
|
|
|
|
memset(cmd, 0x00, sizeof(cmd));
|
|
sprintf(argv[0], "ubi");
|
|
sprintf(argv[1], "get");
|
|
sprintf(argv[2], "%s", vol->name);
|
|
|
|
/* ubi create vloume size type */
|
|
volumesize = sunxi_do_ubi(0, 3, argv);
|
|
if (volumesize != -1) {
|
|
if (to_sects(volumesize) != ubi_mbr->vols[partno].sects) {
|
|
pr_info("existed volume %s size is %d(sects) , inside ubi part size is %d(sects),"
|
|
"existed volume size to update inside ubi part size\n",
|
|
vol->name, to_sects(volumesize), ubi_mbr->vols[partno].sects);
|
|
ubi_mbr->vols[partno].sects = to_sects(volumesize);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
static int create_ubi_volume_do(struct ubi_nand_vol *vol)
|
|
{
|
|
int i, ret;
|
|
char cmd[5][20];
|
|
char *argv[5];
|
|
|
|
for (i = 0; i < 5; i++)
|
|
argv[i] = cmd[i];
|
|
|
|
ret = check_volume_existed_do((char *)vol->name);
|
|
if (!ret) {
|
|
pr_info("volume %s existed, not create again\n", vol->name);
|
|
return ret;
|
|
}
|
|
|
|
memset(cmd, 0x00, sizeof(cmd));
|
|
sprintf(argv[0], "ubi");
|
|
sprintf(argv[1], "create");
|
|
sprintf(argv[2], "%s", vol->name);
|
|
sprintf(argv[3], "0x%016x", to_bytes(vol->sects));
|
|
if (vol->type & TYPE_STATIC_VOLUME)
|
|
sprintf(&cmd[4][0], "s");
|
|
else
|
|
sprintf(&cmd[4][0], "d");
|
|
|
|
/* ubi create vloume size type */
|
|
ret = sunxi_do_ubi(0, 5, argv);
|
|
if (ret) {
|
|
pr_err("'ubi create %s %s %s' failed, return %d\n",
|
|
argv[2], argv[3], argv[4], ret);
|
|
return ret;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int create_ubi_volume(struct ubi_info *ubinfo)
|
|
{
|
|
int ret = -EINVAL;
|
|
int i;
|
|
char *mtd_name;
|
|
int mtd_num;
|
|
unsigned int mtd_bytes;
|
|
unsigned int vol_total_bytes = 0;
|
|
struct ubi_mbr *ubi_mbr = ubi_to_ubi_mbr(ubinfo);
|
|
struct ubi_mbr *nand_mbr = ubi_to_nand_mbr(ubinfo);
|
|
struct ubi_status *ubi_status = ubi_to_ubi_status(ubinfo);
|
|
|
|
if (ubi_mbr == NULL) {
|
|
pr_err("UBI mbr is NULL !!!\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!ubi_mbr->part_cnt)
|
|
return 0;
|
|
|
|
mtd_num = get_mtd_num_to_attach();
|
|
if (mtd_num < 0) {
|
|
pr_err("invalid mtd num %d\n", mtd_num);
|
|
return -EINVAL;
|
|
}
|
|
|
|
mtd_name = sunxi_get_mtdparts_name(mtd_num);
|
|
mtd_bytes = sunxi_get_mtdpart_size(mtd_num);
|
|
if (mtd_name == NULL) {
|
|
pr_err("mtd_name is NULL !!!\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
for (i = 0; i < ubi_mbr->part_cnt; i++)
|
|
vol_total_bytes += to_bytes(ubi_mbr->vols[i].sects);
|
|
|
|
if (vol_total_bytes > mtd_bytes) {
|
|
pr_err("ubi volume total size is larger than mtd size.\n"
|
|
"ubi_vol_total_bytes : 0x%x, mtd_bytes: 0x%x\n",
|
|
vol_total_bytes, mtd_bytes);
|
|
return -EINVAL;
|
|
}
|
|
|
|
pr_info("ubi attatch mtd, name: %s\n\n", mtd_name);
|
|
|
|
ret = ubi_attach_mtd_do(mtd_num);
|
|
if (ret)
|
|
return ret;
|
|
|
|
for (i = 0; i < ubi_mbr->part_cnt; i++) {
|
|
ret = create_ubi_volume_do(&ubi_mbr->vols[i]);
|
|
if (ret)
|
|
return ret;
|
|
ubi_status->vols[i].state = CREATE_UBI_VOL;
|
|
}
|
|
|
|
/*
|
|
* reset the last volume size
|
|
* The last volume always be 0 for aw sys_partition.
|
|
* It means that the last volume should resize to the max size.
|
|
* However, the max size is defined by ubi, so we insert a function
|
|
* @check_add_adjust_ubi_mbr to check and get the last volume size,
|
|
*/
|
|
check_and_adjust_ubi_mbr(&ubi_mbr->vols[i - 1], nand_mbr, i - 1);
|
|
check_and_adjust_ubi_mbr(&ubi_mbr->vols[i -1], ubi_mbr, i -1);
|
|
if (ubinfo->last_vol_sects == 0)
|
|
ubinfo->last_vol_sects = nand_mbr->vols[i - 1].sects;
|
|
return ret;
|
|
}
|
|
|
|
static int read_ubi_volume_do(char *vol_name, unsigned int sectors, void *buf)
|
|
{
|
|
size_t bytes;
|
|
char cmd[6][20];
|
|
char *argv[6];
|
|
int i;
|
|
|
|
bytes = to_bytes(sectors);
|
|
|
|
for (i = 0; i < 6; i++)
|
|
argv[i] = cmd[i];
|
|
|
|
memset(cmd, 0x00, sizeof(cmd));
|
|
sprintf(&cmd[0][0], "ubi");
|
|
sprintf(&cmd[1][0], "read");
|
|
sprintf(&cmd[2][0], "0x%016x", (unsigned int)(ulong)buf);
|
|
sprintf(&cmd[3][0], "%s", vol_name);
|
|
sprintf(&cmd[4][0], "0x%016x", (unsigned int)(ulong)bytes);
|
|
|
|
return sunxi_do_ubi(0, 5, argv);
|
|
}
|
|
|
|
static int write_ubi_volume_do(char *vol_name, void *buf, unsigned int bytes,
|
|
unsigned int full_bytes)
|
|
{
|
|
char cmd[6][20];
|
|
char *argv[6];
|
|
int i;
|
|
|
|
memset(cmd, 0x00, sizeof(cmd));
|
|
|
|
for (i = 0; i < 6; i++)
|
|
argv[i] = cmd[i];
|
|
|
|
/* ubi write.part address volume size [fullsize] */
|
|
sprintf(&cmd[0][0], "ubi");
|
|
sprintf(&cmd[1][0], "write.part");
|
|
sprintf(&cmd[2][0], "0x%016x", (unsigned int)(ulong)buf);
|
|
sprintf(&cmd[3][0], "%s", vol_name);
|
|
sprintf(&cmd[4][0], "0x%016x", bytes);
|
|
sprintf(&cmd[5][0], "0x%016x", full_bytes);
|
|
|
|
if (full_bytes)
|
|
return sunxi_do_ubi(0, 6, argv);
|
|
else
|
|
return sunxi_do_ubi(0, 5, argv);
|
|
}
|
|
|
|
static int write_ubi_volume(struct ubi_info *ubinfo, char *name,
|
|
unsigned int sectors, void *buf)
|
|
{
|
|
int ret;
|
|
size_t full_bytes, bytes;
|
|
unsigned int plan_wr_sects, written_sects;
|
|
int num;
|
|
struct ubi_status *ubi_status = ubi_to_ubi_status(ubinfo);
|
|
char *last_name = ubinfo->last_name;
|
|
|
|
num = get_volnum_by_nand_mbr_name(name);
|
|
if (num < 0) {
|
|
pr_err("not found volume named %s\n", name);
|
|
return -EINVAL;
|
|
}
|
|
|
|
plan_wr_sects = ubi_status->vols[num].plan_wr_sects;
|
|
written_sects = ubi_status->vols[num].written_sects;
|
|
bytes = to_bytes(sectors);
|
|
|
|
if ((written_sects + sectors) > plan_wr_sects) {
|
|
pr_err("write %u sectors over limit %u sectors, resize it\n",
|
|
sectors, plan_wr_sects - written_sects);
|
|
bytes = to_bytes(plan_wr_sects - written_sects);
|
|
}
|
|
|
|
full_bytes = 0;
|
|
if (strcmp(last_name, name)) {
|
|
if (ubi_status->vols[num].state != CREATE_UBI_VOL) {
|
|
int vol_num;
|
|
struct ubi_mbr *ubi_mbr = ubi_to_ubi_mbr(ubinfo);
|
|
struct ubi_nand_vol *vol;
|
|
|
|
vol_num = get_volnum_by_name(name);
|
|
if (vol_num < 0) {
|
|
pr_err("not found volume named %s\n", name);
|
|
return -EINVAL;
|
|
}
|
|
vol = &ubi_mbr->vols[vol_num];
|
|
|
|
if (check_volume_existed_do(name)) {
|
|
ret = create_ubi_volume_do(vol);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
}
|
|
|
|
strcpy(last_name, name);
|
|
full_bytes = to_bytes(plan_wr_sects);
|
|
}
|
|
ret = write_ubi_volume_do(name, buf, bytes, full_bytes);
|
|
if (ret)
|
|
pr_err("write volume %s with bytes %u full_bytes %u failed\n",
|
|
name, bytes, full_bytes);
|
|
|
|
ubi_status->last_partno = num;
|
|
ubi_status->vols[num].written_sects += sectors;
|
|
ubi_status->vols[num].state = WRITE_UBI_VOL;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int read_mtd_do(loff_t from, size_t len, size_t *retlen, u_char *buf)
|
|
{
|
|
struct mtd_info *mtd = NULL;
|
|
#ifdef CONFIG_AW_MTD_SPINAND
|
|
if (support_spinand()) {
|
|
struct aw_spinand *spinand = get_spinand();
|
|
mtd = spinand_to_mtd(spinand);
|
|
}
|
|
#endif
|
|
#ifdef CONFIG_AW_MTD_RAWNAND
|
|
if (support_rawnand()) {
|
|
struct aw_nand_chip *chip = get_rawnand();
|
|
mtd = awnand_chip_to_mtd(chip);
|
|
}
|
|
#endif
|
|
|
|
return mtd ? mtd_read(mtd, from, len, retlen, buf) : -EINVAL;
|
|
}
|
|
|
|
static int write_mtd_do(loff_t to, size_t len, size_t *retlen, u_char *buf)
|
|
{
|
|
struct mtd_info *mtd = NULL;
|
|
|
|
#ifdef CONFIG_AW_MTD_SPINAND
|
|
if (support_spinand()) {
|
|
struct aw_spinand *spinand = get_spinand();
|
|
mtd = spinand_to_mtd(spinand);
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_AW_MTD_RAWNAND
|
|
if (support_rawnand()) {
|
|
struct aw_nand_chip *chip = get_rawnand();
|
|
mtd = awnand_chip_to_mtd(chip);
|
|
}
|
|
#endif
|
|
|
|
return mtd ? mtd_write(mtd, to, len, retlen, buf) : -EINVAL;
|
|
}
|
|
|
|
static int flush_mtd_dev(struct ubi_mtd_info *mtd_info)
|
|
{
|
|
u_char *upd_buf;
|
|
int num;
|
|
unsigned int col;
|
|
size_t retlen;
|
|
unsigned int to, len, pgsize, offset;
|
|
unsigned int plan_wr_bytes, written_bytes, upd_received;
|
|
int ret;
|
|
|
|
num = mtd_info->last_partno;
|
|
if (num < 0)
|
|
return 0;
|
|
|
|
offset = mtd_info->part[num].offset;
|
|
pgsize = mtd_info->pagesize;
|
|
upd_buf = (u_char *)mtd_info->wbuf;
|
|
|
|
upd_received = to_bytes(mtd_info->part[num].written_sects);
|
|
written_bytes = upd_received / pgsize * pgsize;
|
|
|
|
plan_wr_bytes = to_bytes(mtd_info->part[num].plan_wr_sects);
|
|
plan_wr_bytes = to_align(plan_wr_bytes, pgsize);
|
|
|
|
if (written_bytes >= plan_wr_bytes)
|
|
return 0;
|
|
|
|
len = plan_wr_bytes - written_bytes;
|
|
col = upd_received % pgsize;
|
|
if (col)
|
|
memset(upd_buf + col, 0xff, pgsize - col);
|
|
else
|
|
memset(upd_buf, 0xff, pgsize);
|
|
|
|
while (len) {
|
|
to = offset + written_bytes;
|
|
ret = write_mtd_do(to, pgsize, &retlen, upd_buf);
|
|
if (ret) {
|
|
pr_err("mtd write failed with size:%x, ret:%u\n",
|
|
pgsize, retlen);
|
|
return ret;
|
|
}
|
|
|
|
memset(upd_buf, 0xff, pgsize);
|
|
written_bytes += pgsize;
|
|
len -= pgsize;
|
|
}
|
|
mtd_info->part[num].written_sects = to_sects(written_bytes);
|
|
|
|
pr_info("volume %s total written :0x%x\n", mtd_info->part[num].name,
|
|
to_sects(written_bytes));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int read_mtd_part(struct ubi_mtd_info *mtd_info, int num,
|
|
unsigned int start, unsigned int sectors, void *buffer)
|
|
{
|
|
size_t len, retlen;
|
|
unsigned int offset;
|
|
unsigned int partsize;
|
|
unsigned int from;
|
|
#ifdef CONFIG_AW_MTD_SPINAND
|
|
struct aw_spinand *spinand = get_spinand();
|
|
struct mtd_info *mtd = spinand_to_mtd(spinand);
|
|
#else
|
|
struct aw_nand_chip *chip = get_rawnand();
|
|
struct mtd_info *mtd = awnand_chip_to_mtd(chip);
|
|
#endif
|
|
unsigned int block = 0;
|
|
unsigned int left_len;
|
|
unsigned int read, read_start;
|
|
loff_t ofs = 0;
|
|
static int last_read_mtd = -1;
|
|
static int skip_size;
|
|
int ret;
|
|
|
|
if (last_read_mtd != num) {
|
|
last_read_mtd = num;
|
|
skip_size = 0;
|
|
}
|
|
|
|
partsize = mtd_info->part[num].bytes;
|
|
offset = mtd_info->part[num].offset;
|
|
len = to_bytes(sectors);
|
|
from = offset + to_bytes(start) + skip_size;
|
|
|
|
if (to_bytes(start + sectors) > partsize) {
|
|
pr_err("%s,out of mtdsize(0x%x).\n", __func__, partsize);
|
|
pr_err("start:%0x, sectors:%0x.\n", start, sectors);
|
|
return -EINVAL;
|
|
}
|
|
|
|
flush_mtd_dev(mtd_info);
|
|
|
|
left_len = len;
|
|
read_start = from;
|
|
|
|
block = read_start / mtd->erasesize;
|
|
ofs = block * mtd->erasesize;
|
|
|
|
if (len > mtd->erasesize - (from & mtd->erasesize_mask)) {
|
|
check0:
|
|
if (mtd->_block_isbad(mtd, ofs)) {
|
|
pr_info("check 0: read %d (in block %d) is bad, skip,"
|
|
"its data store in next good block\n", from, (unsigned int)ofs / mtd->erasesize);
|
|
skip_size += mtd->erasesize;
|
|
read_start += mtd->erasesize;
|
|
ofs += mtd->erasesize;
|
|
|
|
goto check0;
|
|
}
|
|
|
|
read = from & mtd->erasesize_mask;
|
|
ret = read_mtd_do(read_start, read, &retlen, buffer);
|
|
if (ret) {
|
|
pr_err("read from %llu len %llu err\n", from, len);
|
|
return ret;
|
|
}
|
|
|
|
left_len -= read;
|
|
buffer += read;
|
|
read_start += read;
|
|
|
|
}
|
|
|
|
while (left_len) {
|
|
check1:
|
|
block = read_start / mtd->erasesize;
|
|
ofs = block * mtd->erasesize;
|
|
if (mtd->_block_isbad(mtd, ofs)) {
|
|
pr_info("check1: read %d (in block %d) is bad, skip,"
|
|
"its data store in next good block\n", read_start, block);
|
|
read_start += mtd->erasesize;
|
|
skip_size += mtd->erasesize;
|
|
|
|
goto check1;
|
|
}
|
|
if (left_len <= mtd->erasesize)
|
|
read = left_len;
|
|
else
|
|
read = mtd->erasesize;
|
|
|
|
ret = read_mtd_do(read_start, read, &retlen, buffer);
|
|
if (ret) {
|
|
pr_err("read from %llu len %llu err\n", from, len);
|
|
return ret;
|
|
}
|
|
|
|
left_len -= read;
|
|
read_start += read;
|
|
buffer += read;
|
|
from += read;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
static int write_mtd_part(struct ubi_mtd_info *mtd_info, int num,
|
|
unsigned int sectors, void *buffer)
|
|
{
|
|
static int last_num = -1;
|
|
size_t len, retlen;
|
|
unsigned int col, rest_pgsize;
|
|
unsigned int to, offset, pgsize, upd_received;
|
|
u_char *src_buf, *upd_buf;
|
|
#ifdef CONFIG_AW_MTD_SPINAND
|
|
struct aw_spinand *spinand = get_spinand();
|
|
struct mtd_info *mtd = spinand_to_mtd(spinand);
|
|
#else
|
|
struct aw_nand_chip *chip = get_rawnand();
|
|
struct mtd_info *mtd = awnand_chip_to_mtd(chip);
|
|
#endif
|
|
unsigned int block = 0;
|
|
unsigned int skip_size = 0;
|
|
|
|
if (num != last_num) {
|
|
pr_err("First mtd write. part_name : %s, wr_sectors: 0x%0x\n",
|
|
mtd_info->part[num].name, sectors);
|
|
pr_err("%s, cur_num : 0x%x. last_num : 0x%x\n", __func__,
|
|
num, last_num);
|
|
|
|
/* the first write mtdx, then flush the last_num mtdx */
|
|
flush_mtd_dev(mtd_info);
|
|
|
|
last_num = num;
|
|
mtd_info->part[num].written_sects = 0;
|
|
mtd_info->last_partno = num;
|
|
mtd_info->part[num].skip_sects = 0;
|
|
mtd_info->part[num].last_skip_block = -1;
|
|
}
|
|
|
|
offset = mtd_info->part[num].offset;
|
|
skip_size = to_bytes(mtd_info->part[num].skip_sects);
|
|
upd_received = to_bytes(mtd_info->part[num].written_sects);
|
|
src_buf = (u_char *)buffer;
|
|
upd_buf = (u_char *)mtd_info->wbuf;
|
|
pgsize = mtd_info->pagesize;
|
|
if (pgsize > MTD_W_BUF_SIZE) {
|
|
pr_err("Err: pgsize(0x%x) is larger than bufsize(64KB).\n", pgsize);
|
|
return -EINVAL;
|
|
}
|
|
|
|
check:
|
|
|
|
block = (upd_received + skip_size + offset) / mtd->erasesize;
|
|
loff_t ofs = block * mtd->erasesize;
|
|
if (mtd->_block_isbad(mtd, ofs) && block != mtd_info->part[num].last_skip_block) {
|
|
pr_info("write %d (in block %d) is bad, skip\n", upd_received, (unsigned int)ofs / mtd->erasesize);
|
|
mtd_info->part[num].last_skip_block = block;
|
|
mtd_info->part[num].skip_sects += to_sects(mtd->erasesize);
|
|
skip_size = to_bytes(mtd_info->part[num].skip_sects);
|
|
pr_info("skip %d , write in block %d\n", skip_size, (offset + upd_received + skip_size) / mtd->erasesize);
|
|
|
|
goto check;
|
|
}
|
|
|
|
len = to_bytes(sectors);
|
|
col = upd_received % pgsize;
|
|
rest_pgsize = pgsize - col;
|
|
if (col) {
|
|
if (len >= rest_pgsize) {
|
|
pr_debug("col:0x%x, rest_pgsize:%x, len:%x\n",
|
|
col, rest_pgsize, len);
|
|
pr_debug("mtd writing :0x%x\n", rest_pgsize >> 9);
|
|
|
|
memcpy(upd_buf + col, src_buf, rest_pgsize);
|
|
to = (offset + upd_received + skip_size) / pgsize * pgsize;
|
|
if (write_mtd_do(to, pgsize, &retlen, upd_buf)) {
|
|
pr_err("%d, %s, Err:wr_size:%x, written:%0x\n",
|
|
__LINE__, __func__, pgsize, retlen);
|
|
return -EINVAL;
|
|
}
|
|
|
|
src_buf += rest_pgsize;
|
|
len -= rest_pgsize;
|
|
upd_received += rest_pgsize;
|
|
} else {
|
|
memcpy(upd_buf + col, src_buf, len);
|
|
len = 0;
|
|
upd_received += len;
|
|
}
|
|
}
|
|
|
|
while (len) {
|
|
if (len >= pgsize) {
|
|
pr_debug("mtd writing :0x%x\n", to_bytes(pgsize));
|
|
|
|
memcpy(upd_buf, src_buf, pgsize);
|
|
to = offset + upd_received + skip_size;
|
|
if (to % mtd->erasesize == 0) {
|
|
struct erase_info erase = {
|
|
.addr = to,
|
|
.len = mtd->erasesize,
|
|
};
|
|
|
|
pr_debug("erase %u to %u (block %d)\n", to, to + mtd->erasesize,
|
|
to / mtd->erasesize);
|
|
if (mtd->_erase(mtd, &erase)) {
|
|
pr_err("erase %u to %u fail\n", to, to + mtd->erasesize);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
if (write_mtd_do(to, pgsize, &retlen, upd_buf)) {
|
|
pr_err("%d, %s,Err: wr_size:%x, written:%0x\n",
|
|
__LINE__, __func__, pgsize, retlen);
|
|
return -EINVAL;
|
|
}
|
|
|
|
src_buf += pgsize;
|
|
len -= pgsize;
|
|
upd_received += pgsize;
|
|
} else {
|
|
debug("mtd copy to buf: 0x%x\n", (unsigned int)(len >> 9));
|
|
|
|
memcpy(upd_buf, src_buf, len);
|
|
upd_received += len;
|
|
len = 0;
|
|
}
|
|
}
|
|
mtd_info->part[num].written_sects = to_sects(upd_received);
|
|
|
|
pr_debug("%s total written :0x%x\n", mtd_info->part[num].name,
|
|
to_sects(upd_received));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int fill_gap_do(struct ubi_info *ubinfo, char *vol_name,
|
|
int64_t gap_size, char fill_data)
|
|
{
|
|
char *gap_buf;
|
|
int i, ret = 0;
|
|
|
|
struct ubi_device *ubi = ubi_devices[0];
|
|
struct ubi_status *ubi_status = ubi_to_ubi_status(ubinfo);
|
|
|
|
int last_volnum = get_volnum_by_name(vol_name);
|
|
if (last_volnum < 0)
|
|
return -EINVAL;
|
|
|
|
int leb_sects = to_sects(ubi->leb_size);
|
|
int64_t remain = 0;
|
|
fill_data = 0x55;
|
|
|
|
if (!gap_size)
|
|
return 0;
|
|
|
|
pr_info("fill gap start: volume %s sects 0x%x\n",
|
|
vol_name, (unsigned int )gap_size);
|
|
|
|
gap_buf = (void *)malloc(to_bytes(GAP_BUF_MAX_SECTS));
|
|
if (!gap_buf) {
|
|
pr_err("Err: malloc memory fail, line: %d, %s\n",
|
|
__LINE__, __func__);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
for (i = 0; i < to_bytes(GAP_BUF_MAX_SECTS); i++) {
|
|
if (i % 2 == 0)
|
|
gap_buf[i] = 0x55;
|
|
else if (i % 2 == 1)
|
|
gap_buf[i] = 0xaa;
|
|
}
|
|
|
|
if (ubi_status->vols[last_volnum].written_sects % leb_sects) {
|
|
remain = (leb_sects - (ubi_status->vols[last_volnum].written_sects % leb_sects));
|
|
|
|
/*to fill the last used peb of the volume*/
|
|
for (i = remain; i > 0 ; i -= GAP_BUF_MAX_SECTS) {
|
|
ret = write_ubi_volume(ubinfo, vol_name,
|
|
min_t(unsigned int, i, GAP_BUF_MAX_SECTS), gap_buf);
|
|
if (ret) {
|
|
pr_err("fill gap fail with %d back!\n", ret);
|
|
free(gap_buf);
|
|
return ret;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*data pattern 0xff don't really write to flash*/
|
|
memset(gap_buf, 0xff, GAP_BUF_MAX_SECTS << 9);
|
|
for (i = gap_size - remain; i > 0; i -= GAP_BUF_MAX_SECTS) {
|
|
ret = write_ubi_volume(ubinfo, vol_name,
|
|
min_t(unsigned int, i, GAP_BUF_MAX_SECTS), gap_buf);
|
|
if (ret) {
|
|
pr_err("fill gap fail with %d back!\n", ret);
|
|
free(gap_buf);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
pr_info("fill gap end: volume %s\n", vol_name);
|
|
free(gap_buf);
|
|
return 0;
|
|
}
|
|
|
|
static int fill_gap(struct ubi_info *ubinfo, int partno)
|
|
{
|
|
int ret = 0;
|
|
int64_t gap = 0;
|
|
char *name;
|
|
struct ubi_status *ubi_status = ubi_to_ubi_status(ubinfo);
|
|
struct ubi_mbr *nand_mbr = ubi_to_nand_mbr(ubinfo);
|
|
unsigned last_volnum;
|
|
|
|
if (partno < 0)
|
|
return 0;
|
|
|
|
name = (char *)nand_mbr->vols[partno].name;
|
|
last_volnum = get_volnum_by_nand_mbr_name(name);
|
|
if (last_volnum < 0)
|
|
return -EINVAL;
|
|
|
|
gap = ubi_status->vols[last_volnum].plan_wr_sects -
|
|
ubi_status->vols[last_volnum].written_sects;
|
|
|
|
if (gap)
|
|
ret = fill_gap_do(ubinfo, name, gap, 0x00);
|
|
return ret;
|
|
}
|
|
|
|
static int read_ubi_volume(struct ubi_info *ubinfo, char *name,
|
|
unsigned int offs, unsigned int sectors, void *buf)
|
|
{
|
|
size_t full_size, size;
|
|
int num, volnum;
|
|
loff_t offp;
|
|
struct ubi_mbr *ubi_mbr = ubi_to_ubi_mbr(ubinfo);
|
|
|
|
volnum = get_volnum_by_name(name);
|
|
if (volnum < 0) {
|
|
pr_err("volume %s is not in ubi mbr !!!\n", name);
|
|
return -ENODEV;
|
|
}
|
|
|
|
offp = to_bytes((loff_t)offs);
|
|
full_size = to_bytes((size_t)ubi_mbr->vols[volnum].sects);
|
|
size = (size_t)sectors << 9;
|
|
if (size > full_size) {
|
|
pr_warn("Warning: par overflow.");
|
|
pr_warn("size(byte):%0x, full_size(byte):%0x\n",
|
|
size, full_size);
|
|
size = full_size;
|
|
}
|
|
|
|
num = get_volnum_by_nand_mbr_name(name);
|
|
if (num < 0) {
|
|
pr_err("volume %s is not in ubi mbr !!!\n", name);
|
|
return -ENODEV;
|
|
}
|
|
|
|
/*
|
|
* aw burning tools will check the written data if finish downloading
|
|
* image, we should fill full to volume.
|
|
*/
|
|
if (num == ubinfo->last_partno) {
|
|
int ret;
|
|
|
|
ret = fill_gap(ubinfo, num);
|
|
if (ret) {
|
|
pr_err("ubi flush fail!!!\n");
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return sunxi_ubi_volume_read(name, offp, (char *)buf, size);
|
|
}
|
|
|
|
static int init_ubi_info(struct ubi_info *ubinfo,
|
|
sunxi_mbr_t *org_mbr)
|
|
{
|
|
int ret;
|
|
struct ubi_mtd_info *mtd_info = ubi_to_mtd(ubinfo);
|
|
struct ubi_mbr *nand_mbr = ubi_to_nand_mbr(ubinfo);
|
|
|
|
ret = init_mtd_info(mtd_info);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = init_nand_mbr(org_mbr, nand_mbr);
|
|
if (ret)
|
|
return ret;
|
|
|
|
init_ubi_mbr(ubinfo);
|
|
ubinfo->last_offset = ubinfo->last_partno = -1;
|
|
return 0;
|
|
}
|
|
|
|
static int init_ubi(struct ubi_info *ubinfo,
|
|
sunxi_mbr_t *org_mbr)
|
|
{
|
|
int ret;
|
|
|
|
ret = create_mtdparts_do();
|
|
if (ret) {
|
|
pr_err("create mtd partitions failed\n");
|
|
return ret;
|
|
}
|
|
|
|
ret = init_ubi_info(ubinfo, org_mbr);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = create_ubi_volume(ubinfo);
|
|
if (ret)
|
|
return ret;
|
|
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
#define PART_ENV_BUF_SIZE 1024
|
|
|
|
static unsigned int get_gpt_from_mbr_vol(unsigned int start,
|
|
unsigned int sectors, void *buffer)
|
|
{
|
|
int mtd_num;
|
|
int ret = 0;
|
|
char *mbr_buf = NULL, *gpt_buf = NULL;
|
|
struct ubi_info *ubinfo = get_ubi_info();
|
|
struct ubi_mbr *nand_mbr = ubi_to_nand_mbr(ubinfo);
|
|
struct ubi_mtd_info *mtd_info = ubi_to_mtd(ubinfo);
|
|
|
|
gpt_buf = malloc(8 * 1024);
|
|
if (!gpt_buf) {
|
|
pr_err("malloc 8k gpt_buf fail\n");
|
|
goto err;
|
|
}
|
|
memset(gpt_buf, 0, 8 * 1024);
|
|
|
|
mtd_num = get_mtd_num_by_name("mbr");
|
|
if (mtd_num >= 0) {
|
|
ret = read_mtd_part(mtd_info, mtd_num, start, sectors,
|
|
buffer);
|
|
} else {
|
|
mbr_buf = (char *)malloc(to_bytes(MBR_SECTORS));
|
|
if (!mbr_buf) {
|
|
pr_err("allocate for mbr buf failed\n");
|
|
goto free_gpt_buf;
|
|
}
|
|
ret = read_ubi_volume_do("mbr", MBR_SECTORS, mbr_buf);
|
|
if (ret) {
|
|
pr_err("read volume mbr failed return %d\n", ret);
|
|
goto free_mbr_buf;
|
|
}
|
|
|
|
ret = check_sunxi_mbr((sunxi_mbr_t *)mbr_buf);
|
|
if (ret)
|
|
goto free_mbr_buf;
|
|
sunxi_mbr_convert_to_gpt(mbr_buf, (char *)gpt_buf, STORAGE_SPI_NAND);
|
|
memcpy(buffer, gpt_buf + to_bytes(start), to_bytes(sectors));
|
|
}
|
|
|
|
if (!ret && !start && !nand_mbr->part_cnt) {
|
|
ret = init_ubi_info(ubinfo, (sunxi_mbr_t *)mbr_buf);
|
|
if (ret)
|
|
goto free_mbr_buf;
|
|
}
|
|
|
|
ret = sectors;
|
|
free_mbr_buf:
|
|
if (mbr_buf)
|
|
free(mbr_buf);
|
|
free_gpt_buf:
|
|
if (gpt_buf)
|
|
free(gpt_buf);
|
|
err:
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* The last volume always be 0 for aw sys_partition.
|
|
* It means that the last volume should resize to the max size.
|
|
* However, the max size is defined by ubi, so we insert a function
|
|
* @mtd_set_last_vol_sects to get the last volume size,
|
|
*/
|
|
int mtd_set_last_vol_sects(unsigned int sects)
|
|
{
|
|
struct ubi_info *ubinfo = get_ubi_info();
|
|
|
|
ubinfo->last_vol_sects = sects;
|
|
pr_info("reset last volume size to 0x%x\n", sects);
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_AW_MTD_SPINAND
|
|
uint64_t spinand_sys_part_offset(void)
|
|
{
|
|
struct aw_spinand *spinand = get_spinand();
|
|
struct aw_spinand_chip *chip = spinand_to_chip(spinand);
|
|
struct aw_spinand_info *info = chip->info;
|
|
unsigned int phy_blk_bytes = info->phy_block_size(chip);
|
|
unsigned int uboot_end;
|
|
uint64_t offset = 0;
|
|
|
|
spinand_uboot_blknum(NULL, &uboot_end);
|
|
offset += uboot_end * phy_blk_bytes;
|
|
|
|
#if IS_ENABLED(CONFIG_AW_MTD_SPINAND_FASTBOOT)
|
|
unsigned int bootsize = CONFIG_BIMAGE_SIZE * 1024 * 1024; // boot.img default define 20MiB
|
|
/* boot partition */
|
|
offset += bootsize;
|
|
/* boot_backup partition */
|
|
offset += bootsize;
|
|
#endif
|
|
|
|
offset += AW_SPINAND_RESERVED_PHY_BLK_FOR_SECURE_STORAGE * phy_blk_bytes;
|
|
#if IS_ENABLED(CONFIG_AW_SPINAND_PSTORE_MTD_PART)
|
|
offset += AW_SPINAND_RESERVED_FOR_PSTORE_KB * SZ_1K;;
|
|
#endif
|
|
#if IS_ENABLED(CONFIG_RAW_KERNEL)
|
|
unsigned int blk_bytes = info->block_size(chip);
|
|
/* kernel */
|
|
if (CONFIG_KERNEL_SIZE_BYTE) {
|
|
offset += ALIGN(CONFIG_KERNEL_SIZE_BYTE, blk_bytes);
|
|
}
|
|
#endif
|
|
|
|
return offset;
|
|
}
|
|
|
|
static void set_env_mtdparts_spinand(void)
|
|
{
|
|
struct ubi_info *ubinfo = get_ubi_info();
|
|
struct aw_spinand *spinand = get_spinand();
|
|
struct aw_spinand_chip *chip = spinand_to_chip(spinand);
|
|
struct aw_spinand_info *info = chip->info;
|
|
unsigned int phy_blk_bytes = info->phy_block_size(chip);
|
|
int wlen;
|
|
struct ubi_mtd_part mtd_part;
|
|
unsigned int uboot_start, uboot_end, offset = 0;
|
|
|
|
spinand_uboot_blknum(&uboot_start, &uboot_end);
|
|
ubinfo->ubootblks = uboot_end - uboot_start;
|
|
wlen = snprintf(ubinfo->mtdparts, 512, "mtdparts=nand:");
|
|
|
|
/* boot0 */
|
|
snprintf((char *)&mtd_part.name, PART_NAME_MAX_SIZE, "boot0");
|
|
mtd_part.offset = 0;
|
|
mtd_part.bytes = uboot_start * phy_blk_bytes;
|
|
offset += mtd_part.bytes;
|
|
wlen += snprintf(ubinfo->mtdparts + wlen, 512 - wlen,
|
|
"%uk@%u(%s)ro,", mtd_part.bytes / SZ_1K,
|
|
mtd_part.offset, mtd_part.name);
|
|
/* uboot */
|
|
snprintf((char *)&mtd_part.name, PART_NAME_MAX_SIZE, "uboot");
|
|
mtd_part.offset = offset;
|
|
mtd_part.bytes = uboot_end * phy_blk_bytes - mtd_part.offset;
|
|
offset += mtd_part.bytes;
|
|
wlen += snprintf(ubinfo->mtdparts + wlen, 512 - wlen,
|
|
"%uk@%u(%s)ro,", mtd_part.bytes / SZ_1K,
|
|
mtd_part.offset, mtd_part.name);
|
|
|
|
/*boot mtd part append to uboot mtd part tail, boot0 is easy to handle*/
|
|
#if IS_ENABLED(CONFIG_AW_MTD_SPINAND_FASTBOOT)
|
|
unsigned int bootsize = CONFIG_BIMAGE_SIZE * 1024 * 1024; // boot.img default define 20MiB
|
|
|
|
snprintf((char *)&mtd_part.name, PART_NAME_MAX_SIZE, "boot");
|
|
mtd_part.offset = offset;
|
|
mtd_part.bytes = bootsize;
|
|
offset += mtd_part.bytes;
|
|
wlen += snprintf(ubinfo->mtdparts + wlen, 512 - wlen,
|
|
"%uk@%u(%s)ro,", mtd_part.bytes / SZ_1K,
|
|
mtd_part.offset, mtd_part.name);
|
|
|
|
snprintf((char *)&mtd_part.name, PART_NAME_MAX_SIZE, "boot_backup");
|
|
mtd_part.offset = offset;
|
|
mtd_part.bytes = bootsize;
|
|
offset += mtd_part.bytes;
|
|
wlen += snprintf(ubinfo->mtdparts + wlen, 512 - wlen,
|
|
"%uk@%u(%s)ro,", mtd_part.bytes / SZ_1K,
|
|
mtd_part.offset, mtd_part.name);
|
|
#endif
|
|
|
|
spinand->sec_sto.chip = chip;
|
|
spinand->sec_sto.startblk = offset / phy_blk_bytes;
|
|
spinand->sec_sto.endblk = spinand->sec_sto.startblk +
|
|
AW_SPINAND_RESERVED_PHY_BLK_FOR_SECURE_STORAGE;
|
|
|
|
/* secure storage */
|
|
snprintf((char *)&mtd_part.name, PART_NAME_MAX_SIZE, "secure_storage");
|
|
mtd_part.offset = offset;
|
|
mtd_part.bytes = AW_SPINAND_RESERVED_PHY_BLK_FOR_SECURE_STORAGE;
|
|
mtd_part.bytes *= phy_blk_bytes;
|
|
offset += mtd_part.bytes;
|
|
wlen += snprintf(ubinfo->mtdparts + wlen, 512 - wlen,
|
|
"%uk@%u(%s)ro,", mtd_part.bytes / SZ_1K,
|
|
mtd_part.offset, mtd_part.name);
|
|
#if IS_ENABLED(CONFIG_AW_SPINAND_PSTORE_MTD_PART)
|
|
/* pstore */
|
|
snprintf((char *)&mtd_part.name, PART_NAME_MAX_SIZE, "pstore");
|
|
mtd_part.offset = offset;
|
|
mtd_part.bytes = AW_SPINAND_RESERVED_FOR_PSTORE_KB * SZ_1K;
|
|
offset += mtd_part.bytes;
|
|
wlen += snprintf(ubinfo->mtdparts + wlen, 512 - wlen,
|
|
"%uk@%u(%s)ro,", mtd_part.bytes / SZ_1K,
|
|
mtd_part.offset, mtd_part.name);
|
|
#endif
|
|
#if IS_ENABLED(CONFIG_RAW_KERNEL)
|
|
unsigned int blk_bytes = info->block_size(chip);
|
|
/* kernel */
|
|
snprintf((char *)&mtd_part.name, PART_NAME_MAX_SIZE, "kernel");
|
|
mtd_part.offset = offset;
|
|
if (CONFIG_KERNEL_SIZE_BYTE)
|
|
mtd_part.bytes = ALIGN(CONFIG_KERNEL_SIZE_BYTE, blk_bytes);
|
|
else
|
|
mtd_part.bytes = blk_bytes;
|
|
offset += mtd_part.bytes;
|
|
wlen += snprintf(ubinfo->mtdparts + wlen, 512 - wlen,
|
|
"%uk@%u(%s)ro,", mtd_part.bytes / SZ_1K,
|
|
mtd_part.offset, mtd_part.name);
|
|
#endif
|
|
/* user data */
|
|
wlen += snprintf(ubinfo->mtdparts + wlen, 512 - wlen, "-(sys)");
|
|
|
|
pr_info("mtdparts: %s\n", ubinfo->mtdparts);
|
|
snprintf(ubinfo->mtdids, 20, "nand0=nand");
|
|
sunxi_set_defualt_mtdpart(ubinfo->mtdids, ubinfo->mtdparts);
|
|
}
|
|
|
|
int spinand_mtd_flush_last_volume(void)
|
|
{
|
|
struct ubi_info *ubinfo = get_ubi_info();
|
|
|
|
fill_gap(ubinfo, ubinfo->last_partno);
|
|
ubinfo->last_partno = -1;
|
|
*ubinfo->last_name = 0;
|
|
return 0;
|
|
}
|
|
|
|
unsigned int spinand_mtd_write_ubi(unsigned int start, unsigned int sectors,
|
|
void *buffer)
|
|
{
|
|
char *name = NULL;
|
|
int ret;
|
|
int num, mtd_num;
|
|
struct ubi_info *ubinfo = get_ubi_info();
|
|
struct ubi_mbr *nand_mbr = ubi_to_nand_mbr(ubinfo);
|
|
struct ubi_mtd_info *mtd_info = ubi_to_mtd(ubinfo);
|
|
struct ubi_status *ubi_status = ubi_to_ubi_status(ubinfo);
|
|
unsigned int offset;
|
|
|
|
/*
|
|
* The start sectors [0-80) is used for mbr. Write to this area means
|
|
* spinand ubi haven't initialized, we do it now.
|
|
*/
|
|
|
|
if (!start && (sectors <= MBR_SECTORS)) {
|
|
if (sectors != MBR_SECTORS) {
|
|
pr_err("the buffer size for mbr is wrongly: %d sectors\n", sectors);
|
|
goto err;
|
|
}
|
|
|
|
ret = init_ubi(ubinfo, buffer);
|
|
if (ret) {
|
|
pr_err("initialize sunxi spinand ubi failed\n");
|
|
goto err;
|
|
}
|
|
/*
|
|
* After creating all volumes, we got the actucal size of last
|
|
* partition(UDISK), it`s time to correct the corresponding
|
|
* partition entry info in sunxi_mbr
|
|
*/
|
|
if (WORK_MODE_BOOT != get_boot_work_mode())
|
|
adjust_sunxi_mbr(buffer, ubinfo->last_vol_sects);
|
|
}
|
|
|
|
num = get_partno_by_addr(start, sectors);
|
|
if (!sectors || num < 0)
|
|
goto err;
|
|
|
|
/*
|
|
* new start for a volume, it means this volume has data to download.
|
|
* we should set ubi_status
|
|
*/
|
|
ret = offset_on_part(num, start);
|
|
if (!ret) {
|
|
/*
|
|
* the last volume has 0 size default for aw platform with
|
|
* sys_partition. But some customers need to download image to
|
|
* the last volume, which need the real size of the last volume.
|
|
*/
|
|
if (num + 1 == nand_mbr->part_cnt)
|
|
ubi_status->vols[num].plan_wr_sects =
|
|
ubinfo->last_vol_sects;
|
|
else
|
|
ubi_status->vols[num].plan_wr_sects =
|
|
nand_mbr->vols[num].sects;
|
|
ubi_status->vols[num].written_sects = 0;
|
|
}
|
|
|
|
name = (char *)nand_mbr->vols[num].name;
|
|
mtd_num = get_mtd_num_by_name(name);
|
|
if (mtd_num >= 0) {
|
|
ret = write_mtd_part(mtd_info, mtd_num, sectors, buffer);
|
|
return ret ? 0 : sectors;
|
|
}
|
|
|
|
ret = check_volume_existed_do(name);
|
|
if (ret) {
|
|
pr_err("Warning: ubi volume does not exist!\n");
|
|
goto err;
|
|
}
|
|
|
|
offset = offset_on_part(num, start);
|
|
if (num == ubinfo->last_partno) {
|
|
if (offset != ubinfo->last_offset) {
|
|
pr_err("offset 0x%x smaller than last offset 0x%x\n",
|
|
ubinfo->last_offset, offset);
|
|
goto err;
|
|
}
|
|
} else {
|
|
/* write to new volume */
|
|
if (offset) {
|
|
pr_err("offset should be 0 but 0x%x when write to new volume %s\n",
|
|
offset, (char *)nand_mbr->vols[num].name);
|
|
return -EINVAL;
|
|
}
|
|
ret = fill_gap(ubinfo, ubinfo->last_partno);
|
|
if (ret != 0)
|
|
pr_warn("fill gap fail, part:%s, start:%0x, sectors:%0x\n",
|
|
name, start, sectors);
|
|
}
|
|
ubinfo->last_offset = offset + sectors;
|
|
ubinfo->last_partno = num;
|
|
|
|
pr_debug("write to volume %s: offset 0x%x sects 0x%x\n", name,
|
|
start, sectors);
|
|
|
|
ret = write_ubi_volume(ubinfo, name, sectors, buffer);
|
|
|
|
return ret ? 0 : sectors;
|
|
err:
|
|
return 0;
|
|
}
|
|
|
|
|
|
unsigned int spinand_mtd_read_ubi(unsigned int start, unsigned int sectors,
|
|
void *buffer)
|
|
{
|
|
char *name = NULL;
|
|
unsigned int offset;
|
|
int ret = 0;
|
|
int num, mtd_num;
|
|
struct ubi_info *ubinfo = get_ubi_info();
|
|
struct ubi_mbr *nand_mbr = ubi_to_nand_mbr(ubinfo);
|
|
struct ubi_mtd_info *mtd_info = ubi_to_mtd(ubinfo);
|
|
|
|
if ((start < MBR_SECTORS) &&
|
|
((start + sectors) <= MBR_SECTORS)) {
|
|
return get_gpt_from_mbr_vol(start, sectors, buffer);
|
|
}
|
|
|
|
num = get_partno_by_addr(start, sectors);
|
|
if (!sectors || num < 0)
|
|
return 0;
|
|
|
|
name = (char *)nand_mbr->vols[num].name;
|
|
offset = offset_on_part(num, start);
|
|
|
|
mtd_num = get_mtd_num_by_name(name);
|
|
if (mtd_num >= 0) {
|
|
ret = read_mtd_part(mtd_info, mtd_num, offset, sectors,
|
|
buffer);
|
|
return ret < 0 ? 0 : sectors;
|
|
}
|
|
|
|
ret = check_volume_existed_do(name);
|
|
if (ret) {
|
|
debug("Warning: ubi volume does not exist!\n");
|
|
return 0;
|
|
}
|
|
|
|
ret = read_ubi_volume(ubinfo, name, offset, sectors, buffer);
|
|
return ret ? 0 : sectors;
|
|
}
|
|
|
|
|
|
int spinand_mtd_update_ubi_env(void)
|
|
{
|
|
struct ubi_info *ubinfo = get_ubi_info();
|
|
struct ubi_mbr *ubi_mbr = ubi_to_ubi_mbr(ubinfo);
|
|
char parts_set[PART_ENV_BUF_SIZE], *ppset;
|
|
char buf[PART_ENV_BUF_SIZE];
|
|
char *nand_root = NULL;
|
|
char *rootname;
|
|
int i;
|
|
|
|
#define BLOCK_DEVICE "/dev/ubiblock"
|
|
#define BLOCK_DEVICE_NUM "0_x"
|
|
|
|
rootname = env_get("root_partition");
|
|
if (rootname)
|
|
pr_info("root_partition is %s\n", rootname);
|
|
|
|
nand_root = env_get("nand_root");
|
|
if (rootname)
|
|
pr_info("nand_root is %s\n", nand_root);
|
|
|
|
memset(parts_set, 0, PART_ENV_BUF_SIZE);
|
|
ppset = parts_set;
|
|
for (i = 0; i < ubi_mbr->part_cnt; i++) {
|
|
memset(buf, 0, PART_ENV_BUF_SIZE);
|
|
|
|
/* set parts_set */
|
|
snprintf(buf, PART_ENV_BUF_SIZE, "ubi0_%d", i);
|
|
ppset += snprintf(ppset,
|
|
PART_ENV_BUF_SIZE - (ppset - parts_set),
|
|
"%s@%s:", ubi_mbr->vols[i].name, buf);
|
|
|
|
/* set root */
|
|
if (rootname && !strcmp(rootname, ubi_mbr->vols[i].name)) {
|
|
if (!strncmp(nand_root, BLOCK_DEVICE, strlen(BLOCK_DEVICE))) {
|
|
/*rootfs mount as ext4、squashfs ...*/
|
|
snprintf(buf, PART_ENV_BUF_SIZE,
|
|
"/dev/ubiblock0_%d", i);
|
|
#ifdef CONFIG_SUNXI_DM_VERITY
|
|
pr_info("set verity_dev to %s\n", buf);
|
|
env_set("verity_dev", buf);
|
|
pr_info("set root to /dev/dm-0\n");
|
|
env_set("nand_root", "/dev/dm-0");
|
|
#else
|
|
pr_info("set root to %s\n", buf);
|
|
env_set("nand_root", buf);
|
|
#endif
|
|
} else {
|
|
/*rootfs mount as ubifs*/
|
|
snprintf(buf, PART_ENV_BUF_SIZE,
|
|
"ubi0_%d", i);
|
|
pr_info("set root to %s\n", buf);
|
|
env_set("nand_root", buf);
|
|
}
|
|
}
|
|
}
|
|
|
|
env_set("partitions", parts_set);
|
|
|
|
|
|
snprintf(buf, PART_ENV_BUF_SIZE, "%u", ubinfo->ubootblks);
|
|
pr_info("set aw-ubi-spinand.ubootblks to %u\n", ubinfo->ubootblks);
|
|
env_set("aw-ubi-spinand.ubootblks", buf);
|
|
|
|
snprintf(buf, PART_ENV_BUF_SIZE, "%d", get_mtd_num_to_attach());
|
|
pr_info("set ubi_attach_mtdnum to %d\n", get_mtd_num_to_attach());
|
|
env_set("ubi_attach_mtdnum", buf);
|
|
|
|
pr_info("update ubi part info ok\n");
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* ubi->good_peb_count = ubi->peb_count - ubi->bad_peb_count; @ubi_attach
|
|
* @ubi_read_volume_table
|
|
* ubi->avail_pebs = ubi->good_peb_count - ubi->corr_peb_count
|
|
* ubi->avail_pebs -= all vol->reserved_pebs; @init_volumes
|
|
* ubi->avail_pebs -= UBI_LAYOUT_VOLUME_EBS; @init_volumes, 2
|
|
* ubi->avail_pebs -= WL_RESERVED_PEBS; @ubi_wl_init, 1
|
|
* ubi->avail_pebs -= EBA_RESERVED_PEBS; @ubi_eba_init, 1
|
|
* ubi->avail_pebs -= ubi->beb_rsvd_pebs; @ubi_eba_init
|
|
*/
|
|
int spinand_ubi_user_volumes_size(void)
|
|
{
|
|
int pebs = 0;
|
|
int leb_size = 0;
|
|
int vol_pebs = 0;
|
|
int i = 0;
|
|
struct ubi_device *ubi = ubi_devices[0];
|
|
|
|
if (ubi) {
|
|
leb_size = ubi->leb_size;
|
|
pebs = ubi->rsvd_pebs - ubi->beb_rsvd_pebs - 2 - 1 - 1;
|
|
/*UBI_INT_VOL_COUNT: 1*/
|
|
for (i = 0; i < (ubi->vol_count - 1); i++) {
|
|
vol_pebs += ubi->volumes[i]->reserved_pebs;
|
|
}
|
|
if (vol_pebs != pebs)
|
|
pr_info("warning vol_pebs: %d, %d\n", vol_pebs, pebs);
|
|
}
|
|
pr_info("logical area info: %d %d last_lba: %d\n", pebs, leb_size,
|
|
pebs * (leb_size >> 9) - 1);
|
|
return leb_size * pebs;
|
|
}
|
|
|
|
int spinand_mtd_attach_mtd(void)
|
|
{
|
|
int num;
|
|
struct ubi_info *ubinfo = get_ubi_info();
|
|
struct ubi_mtd_info *mtd_info = ubi_to_mtd(ubinfo);
|
|
|
|
/* support to configure size of mtd part 0 (boot_parts) */
|
|
set_env_mtdparts_spinand();
|
|
|
|
if (create_mtdparts_do())
|
|
return -EINVAL;
|
|
|
|
init_mtd_info(mtd_info);
|
|
num = get_mtd_num_to_attach();
|
|
if (num < 0)
|
|
return -EINVAL;
|
|
|
|
if (ubi_attach_mtd_do(num))
|
|
return -EINVAL;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif /*CONFIG_AW_MTD_SPINAND*/
|
|
|
|
#ifdef CONFIG_AW_MTD_RAWNAND
|
|
|
|
static void set_env_mtdparts_rawnand(void)
|
|
{
|
|
struct ubi_info *ubinfo = get_ubi_info();
|
|
struct aw_nand_chip *chip = get_rawnand();
|
|
|
|
struct aw_nand_sec_sto *rawnand_sec_sto = get_rawnand_sec_sto();
|
|
unsigned int phy_blk_bytes = chip->erasesize;
|
|
int wlen;
|
|
struct ubi_mtd_part mtd_part;
|
|
unsigned int uboot_start, uboot_end, offset = 0;
|
|
|
|
rawnand_uboot_blknum(&uboot_start, &uboot_end);
|
|
ubinfo->ubootblks = uboot_end - uboot_start;
|
|
wlen = snprintf(ubinfo->mtdparts, 512, "mtdparts=nand:");
|
|
|
|
rawnand_sec_sto->startblk = uboot_end;
|
|
rawnand_sec_sto->endblk = uboot_end +
|
|
AW_RAWNAND_RESERVED_PHY_BLK_FOR_SECURE_STORAGE;
|
|
|
|
/* boot0 */
|
|
snprintf((char *)&mtd_part.name, PART_NAME_MAX_SIZE, "boot0");
|
|
mtd_part.offset = 0;
|
|
mtd_part.bytes = uboot_start * phy_blk_bytes;
|
|
offset += mtd_part.bytes;
|
|
wlen += snprintf(ubinfo->mtdparts + wlen, 512 - wlen,
|
|
"%uk@%u(%s)ro,", mtd_part.bytes / SZ_1K,
|
|
mtd_part.offset, mtd_part.name);
|
|
/* uboot */
|
|
snprintf((char *)&mtd_part.name, PART_NAME_MAX_SIZE, "uboot");
|
|
mtd_part.offset = offset;
|
|
mtd_part.bytes = uboot_end * phy_blk_bytes - mtd_part.offset;
|
|
offset += mtd_part.bytes;
|
|
wlen += snprintf(ubinfo->mtdparts + wlen, 512 - wlen,
|
|
"%uk@%u(%s)ro,", mtd_part.bytes / SZ_1K,
|
|
mtd_part.offset, mtd_part.name);
|
|
/* secure storage */
|
|
snprintf((char *)&mtd_part.name, PART_NAME_MAX_SIZE, "secure_storage");
|
|
mtd_part.offset = offset;
|
|
mtd_part.bytes = AW_RAWNAND_RESERVED_PHY_BLK_FOR_SECURE_STORAGE;
|
|
mtd_part.bytes *= phy_blk_bytes;
|
|
offset += mtd_part.bytes;
|
|
wlen += snprintf(ubinfo->mtdparts + wlen, 512 - wlen,
|
|
"%uk@%u(%s)ro,", mtd_part.bytes / SZ_1K,
|
|
mtd_part.offset, mtd_part.name);
|
|
#if IS_ENABLED(CONFIG_AW_RAWNAND_PSTORE_MTD_PART)
|
|
/* pstore */
|
|
snprintf((char *)&mtd_part.name, PART_NAME_MAX_SIZE, "pstore");
|
|
mtd_part.offset = offset;
|
|
mtd_part.bytes = AW_SPINAND_RESERVED_FOR_PSTORE_KB * SZ_1K;
|
|
offset += mtd_part.bytes;
|
|
wlen += snprintf(ubinfo->mtdparts + wlen, 512 - wlen,
|
|
"%uk@%u(%s)ro,", mtd_part.bytes / SZ_1K,
|
|
mtd_part.offset, mtd_part.name);
|
|
#endif
|
|
#if IS_ENABLED(CONFIG_RAW_KERNEL)
|
|
unsigned int blk_bytes = phy_blk_bytes * 2;
|
|
/* kernel */
|
|
snprintf((char *)&mtd_part.name, PART_NAME_MAX_SIZE, "kernel");
|
|
mtd_part.offset = offset;
|
|
if (CONFIG_KERNEL_SIZE_BYTE)
|
|
mtd_part.bytes = ALIGN(CONFIG_KERNEL_SIZE_BYTE, blk_bytes);
|
|
else
|
|
mtd_part.bytes = blk_bytes;
|
|
offset += mtd_part.bytes;
|
|
wlen += snprintf(ubinfo->mtdparts + wlen, 512 - wlen,
|
|
"%uk@%u(%s)ro,", mtd_part.bytes / SZ_1K,
|
|
mtd_part.offset, mtd_part.name);
|
|
#endif
|
|
/* user data */
|
|
wlen += snprintf(ubinfo->mtdparts + wlen, 512 - wlen, "-(sys)");
|
|
|
|
pr_info("mtdparts: %s\n", ubinfo->mtdparts);
|
|
snprintf(ubinfo->mtdids, 20, "nand0=nand");
|
|
sunxi_set_defualt_mtdpart(ubinfo->mtdids, ubinfo->mtdparts);
|
|
}
|
|
|
|
int rawnand_mtd_flush_last_volume(void)
|
|
{
|
|
struct ubi_info *ubinfo = get_ubi_info();
|
|
|
|
fill_gap(ubinfo, ubinfo->last_partno);
|
|
ubinfo->last_partno = -1;
|
|
*ubinfo->last_name = 0;
|
|
return 0;
|
|
}
|
|
|
|
unsigned int rawnand_mtd_write_ubi(unsigned int start, unsigned int sectors,
|
|
void *buffer)
|
|
{
|
|
char *name = NULL;
|
|
int ret;
|
|
int num, mtd_num;
|
|
struct ubi_info *ubinfo = get_ubi_info();
|
|
struct ubi_mbr *nand_mbr = ubi_to_nand_mbr(ubinfo);
|
|
struct ubi_mtd_info *mtd_info = ubi_to_mtd(ubinfo);
|
|
struct ubi_status *ubi_status = ubi_to_ubi_status(ubinfo);
|
|
unsigned int offset;
|
|
|
|
/*
|
|
* The start sectors [0-80) is used for mbr. Write to this area means
|
|
* rawnand ubi haven't initialized, we do it now.
|
|
*/
|
|
if (!start && (sectors <= MBR_SECTORS)) {
|
|
if (sectors != MBR_SECTORS) {
|
|
pr_err("the buffer size for mbr is wrongly: %d sectors\n", sectors);
|
|
goto err;
|
|
}
|
|
|
|
ret = init_ubi(ubinfo, buffer);
|
|
if (ret) {
|
|
pr_err("initialize sunxi rawnand ubi failed\n");
|
|
goto err;
|
|
}
|
|
/*
|
|
* After creating all volumes, we got the actucal size of last
|
|
* partition(UDISK), it`s time to correct the corresponding
|
|
* partition entry info in sunxi_mbr
|
|
*/
|
|
printf("WORK_MODE_BOOT@%d get_boot_work_mode@%d\n",
|
|
WORK_MODE_BOOT, get_boot_work_mode());
|
|
if (WORK_MODE_BOOT != get_boot_work_mode())
|
|
adjust_sunxi_mbr(buffer, ubinfo->last_vol_sects);
|
|
}
|
|
|
|
num = get_partno_by_addr(start, sectors);
|
|
|
|
if (!sectors || num < 0)
|
|
goto err;
|
|
|
|
/*
|
|
* new start for a volume, it means this volume has data to download.
|
|
* we should set ubi_status
|
|
*/
|
|
ret = offset_on_part(num, start);
|
|
if (!ret) {
|
|
/*
|
|
* the last volume has 0 size default for aw platform with
|
|
* sys_partition. But some customers need to download image to
|
|
* the last volume, which need the real size of the last volume.
|
|
*/
|
|
if (num + 1 == nand_mbr->part_cnt)
|
|
ubi_status->vols[num].plan_wr_sects =
|
|
ubinfo->last_vol_sects;
|
|
else
|
|
ubi_status->vols[num].plan_wr_sects =
|
|
nand_mbr->vols[num].sects;
|
|
ubi_status->vols[num].written_sects = 0;
|
|
}
|
|
|
|
name = (char *)nand_mbr->vols[num].name;
|
|
mtd_num = get_mtd_num_by_name(name);
|
|
if (mtd_num >= 0) {
|
|
ret = write_mtd_part(mtd_info, mtd_num, sectors, buffer);
|
|
return ret ? 0 : sectors;
|
|
}
|
|
|
|
ret = check_volume_existed_do(name);
|
|
if (ret) {
|
|
pr_err("Warning: ubi volume does not exist!\n");
|
|
goto err;
|
|
}
|
|
|
|
offset = offset_on_part(num, start);
|
|
if (num == ubinfo->last_partno) {
|
|
if (offset != ubinfo->last_offset) {
|
|
pr_err("offset 0x%x smaller than last offset 0x%x\n",
|
|
ubinfo->last_offset, offset);
|
|
goto err;
|
|
}
|
|
} else {
|
|
/* write to new volume */
|
|
if (offset) {
|
|
pr_err("offset should be 0 but 0x%x when write to new volume %s\n",
|
|
offset, (char *)nand_mbr->vols[num].name);
|
|
return -EINVAL;
|
|
}
|
|
ret = fill_gap(ubinfo, ubinfo->last_partno);
|
|
if (ret != 0)
|
|
pr_warn("fill gap fail, part:%s, start:%0x, sectors:%0x\n",
|
|
name, start, sectors);
|
|
/*
|
|
*pr_warn("don't fill gap, part:%s, start:%0x, sectors:%0x\n",
|
|
* name, start, sectors);
|
|
*/
|
|
}
|
|
ubinfo->last_offset = offset + sectors;
|
|
ubinfo->last_partno = num;
|
|
|
|
pr_debug("write to volume %s: offset 0x%x sects 0x%x\n", name,
|
|
start, sectors);
|
|
|
|
ret = write_ubi_volume(ubinfo, name, sectors, buffer);
|
|
|
|
return ret ? 0 : sectors;
|
|
err:
|
|
return 0;
|
|
}
|
|
|
|
unsigned int rawnand_mtd_read_ubi(unsigned int start, unsigned int sectors,
|
|
void *buffer)
|
|
{
|
|
char *name = NULL;
|
|
unsigned int offset;
|
|
int ret = 0;
|
|
int num, mtd_num;
|
|
struct ubi_info *ubinfo = get_ubi_info();
|
|
struct ubi_mbr *nand_mbr = ubi_to_nand_mbr(ubinfo);
|
|
struct ubi_mtd_info *mtd_info = ubi_to_mtd(ubinfo);
|
|
|
|
if ((start < MBR_SECTORS) &&
|
|
((start + sectors) <= MBR_SECTORS)) {
|
|
return get_gpt_from_mbr_vol(start, sectors, buffer);
|
|
}
|
|
|
|
num = get_partno_by_addr(start, sectors);
|
|
if (!sectors || num < 0)
|
|
return 0;
|
|
|
|
name = (char *)nand_mbr->vols[num].name;
|
|
offset = offset_on_part(num, start);
|
|
|
|
mtd_num = get_mtd_num_by_name(name);
|
|
if (mtd_num >= 0) {
|
|
ret = read_mtd_part(mtd_info, mtd_num, offset, sectors,
|
|
buffer);
|
|
return ret < 0 ? 0 : sectors;
|
|
}
|
|
|
|
ret = check_volume_existed_do(name);
|
|
if (ret) {
|
|
debug("Warning: ubi volume does not exist!\n");
|
|
return 0;
|
|
}
|
|
|
|
ret = read_ubi_volume(ubinfo, name, offset, sectors, buffer);
|
|
return ret ? 0 : sectors;
|
|
}
|
|
|
|
int rawnand_mtd_update_ubi_env(void)
|
|
{
|
|
struct ubi_info *ubinfo = get_ubi_info();
|
|
struct ubi_mbr *ubi_mbr = ubi_to_ubi_mbr(ubinfo);
|
|
char parts_set[PART_ENV_BUF_SIZE], *ppset;
|
|
char buf[PART_ENV_BUF_SIZE];
|
|
char *nand_root = NULL;
|
|
char *rootname;
|
|
int i;
|
|
|
|
#define BLOCK_DEVICE "/dev/ubiblock"
|
|
#define BLOCK_DEVICE_NUM "0_x"
|
|
|
|
rootname = env_get("root_partition");
|
|
if (rootname)
|
|
pr_info("root_partition is %s\n", rootname);
|
|
|
|
nand_root = env_get("nand_root");
|
|
if (rootname)
|
|
pr_info("nand_root is %s\n", nand_root);
|
|
|
|
memset(parts_set, 0, PART_ENV_BUF_SIZE);
|
|
ppset = parts_set;
|
|
for (i = 0; i < ubi_mbr->part_cnt; i++) {
|
|
memset(buf, 0, PART_ENV_BUF_SIZE);
|
|
|
|
/* set parts_set */
|
|
snprintf(buf, PART_ENV_BUF_SIZE, "ubi0_%d", i);
|
|
ppset += snprintf(ppset,
|
|
PART_ENV_BUF_SIZE - (ppset - parts_set),
|
|
"%s@%s:", ubi_mbr->vols[i].name, buf);
|
|
|
|
/* set root */
|
|
if (rootname && !strcmp(rootname, ubi_mbr->vols[i].name)) {
|
|
if (!strncmp(nand_root, BLOCK_DEVICE, strlen(BLOCK_DEVICE))) {
|
|
/*rootfs mount as ext4、squashfs ...*/
|
|
snprintf(buf, PART_ENV_BUF_SIZE,
|
|
"/dev/ubiblock0_%d", i);
|
|
#ifdef CONFIG_SUNXI_DM_VERITY
|
|
pr_info("set verity_dev to %s\n", buf);
|
|
env_set("verity_dev", buf);
|
|
pr_info("set root to /dev/dm-0\n");
|
|
env_set("nand_root", "/dev/dm-0");
|
|
#else
|
|
pr_info("set root to %s\n", buf);
|
|
env_set("nand_root", buf);
|
|
#endif
|
|
|
|
} else {
|
|
/*rootfs mount as ubifs*/
|
|
snprintf(buf, PART_ENV_BUF_SIZE,
|
|
"ubi0_%d", i);
|
|
pr_info("set root to %s\n", buf);
|
|
env_set("nand_root", buf);
|
|
}
|
|
}
|
|
}
|
|
|
|
env_set("partitions", parts_set);
|
|
|
|
|
|
snprintf(buf, PART_ENV_BUF_SIZE, "%u", ubinfo->ubootblks);
|
|
pr_info("set aw-ubi-rawnand.ubootblks to %u\n", ubinfo->ubootblks);
|
|
env_set("aw-ubi-rawnand.ubootblks", buf);
|
|
|
|
snprintf(buf, PART_ENV_BUF_SIZE, "%d", get_mtd_num_to_attach());
|
|
pr_info("set ubi_attach_mtdnum to %d\n", get_mtd_num_to_attach());
|
|
env_set("ubi_attach_mtdnum", buf);
|
|
|
|
pr_info("update ubi part info ok\n");
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* ubi->good_peb_count = ubi->peb_count - ubi->bad_peb_count; @ubi_attach
|
|
* @ubi_read_volume_table
|
|
* ubi->avail_pebs = ubi->good_peb_count - ubi->corr_peb_count
|
|
* ubi->avail_pebs -= all vol->reserved_pebs; @init_volumes
|
|
* ubi->avail_pebs -= UBI_LAYOUT_VOLUME_EBS; @init_volumes, 2
|
|
* ubi->avail_pebs -= WL_RESERVED_PEBS; @ubi_wl_init, 1
|
|
* ubi->avail_pebs -= EBA_RESERVED_PEBS; @ubi_eba_init, 1
|
|
* ubi->avail_pebs -= ubi->beb_rsvd_pebs; @ubi_eba_init
|
|
*/
|
|
int rawnand_ubi_user_volumes_size(void)
|
|
{
|
|
int pebs = 0;
|
|
int leb_size = 0;
|
|
int vol_pebs = 0;
|
|
int i = 0;
|
|
struct ubi_device *ubi = ubi_devices[0];
|
|
|
|
if (ubi) {
|
|
leb_size = ubi->leb_size;
|
|
pebs = ubi->rsvd_pebs - ubi->beb_rsvd_pebs - 2 - 1 - 1;
|
|
/*UBI_INT_VOL_COUNT: 1*/
|
|
for (i = 0; i < (ubi->vol_count - 1); i++) {
|
|
vol_pebs += ubi->volumes[i]->reserved_pebs;
|
|
}
|
|
if (vol_pebs != pebs)
|
|
pr_info("warning vol_pebs: %d, %d\n", vol_pebs, pebs);
|
|
}
|
|
pr_info("logical area info: %d %d last_lba: %d\n", pebs, leb_size,
|
|
pebs * (leb_size >> 9) - 1);
|
|
return leb_size * pebs;
|
|
}
|
|
|
|
int rawnand_mtd_attach_mtd(void)
|
|
{
|
|
int num;
|
|
struct ubi_info *ubinfo = get_ubi_info();
|
|
struct ubi_mtd_info *mtd_info = ubi_to_mtd(ubinfo);
|
|
|
|
/* support to configure size of mtd part 0 (boot_parts) */
|
|
set_env_mtdparts_rawnand();
|
|
|
|
if (create_mtdparts_do())
|
|
return -EINVAL;
|
|
|
|
init_mtd_info(mtd_info);
|
|
num = get_mtd_num_to_attach();
|
|
if (num < 0)
|
|
return -EINVAL;
|
|
|
|
if (ubi_attach_mtd_do(num))
|
|
return -EINVAL;
|
|
|
|
return 0;
|
|
|
|
}
|
|
#endif /*CONFIG_AW_MTD_RAWNAND*/
|
|
|