2008-11-19 18:47:05 +08:00
|
|
|
/*
|
|
|
|
* Unsorted Block Image commands
|
|
|
|
*
|
|
|
|
* Copyright (C) 2008 Samsung Electronics
|
|
|
|
* Kyungmin Park <kyungmin.park@samsung.com>
|
|
|
|
*
|
2009-04-25 02:24:19 +08:00
|
|
|
* Copyright 2008-2009 Stefan Roese <sr@denx.de>, DENX Software Engineering
|
2008-11-19 18:47:05 +08:00
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
|
|
* published by the Free Software Foundation.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <common.h>
|
|
|
|
#include <command.h>
|
2019-08-01 23:46:46 +08:00
|
|
|
#include <env.h>
|
2008-11-19 18:47:05 +08:00
|
|
|
#include <exports.h>
|
2020-02-03 22:36:16 +08:00
|
|
|
#include <malloc.h>
|
2015-09-03 07:24:57 +08:00
|
|
|
#include <memalign.h>
|
2018-09-29 18:58:29 +08:00
|
|
|
#include <mtd.h>
|
2008-11-19 18:47:05 +08:00
|
|
|
#include <nand.h>
|
|
|
|
#include <onenand_uboot.h>
|
2020-02-03 22:36:15 +08:00
|
|
|
#include <dm/devres.h>
|
2008-11-19 18:47:05 +08:00
|
|
|
#include <linux/mtd/mtd.h>
|
|
|
|
#include <linux/mtd/partitions.h>
|
2014-06-24 16:10:04 +08:00
|
|
|
#include <linux/err.h>
|
2008-11-19 18:47:05 +08:00
|
|
|
#include <ubi_uboot.h>
|
2016-09-21 10:28:55 +08:00
|
|
|
#include <linux/errno.h>
|
2008-11-19 18:47:05 +08:00
|
|
|
#include <jffs2/load_kernel.h>
|
|
|
|
|
2013-04-08 18:32:49 +08:00
|
|
|
#undef ubi_msg
|
|
|
|
#define ubi_msg(fmt, ...) printf("UBI: " fmt "\n", ##__VA_ARGS__)
|
|
|
|
|
2008-11-19 18:47:05 +08:00
|
|
|
/* Private own data */
|
|
|
|
static struct ubi_device *ubi;
|
|
|
|
|
2010-11-02 00:28:22 +08:00
|
|
|
#ifdef CONFIG_CMD_UBIFS
|
2018-07-06 16:25:12 +08:00
|
|
|
#include <ubifs_uboot.h>
|
2010-11-02 00:28:22 +08:00
|
|
|
#endif
|
|
|
|
|
2008-11-19 18:47:05 +08:00
|
|
|
static void display_volume_info(struct ubi_device *ubi)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < (ubi->vtbl_slots + 1); i++) {
|
|
|
|
if (!ubi->volumes[i])
|
|
|
|
continue; /* Empty record */
|
|
|
|
ubi_dump_vol_info(ubi->volumes[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void display_ubi_info(struct ubi_device *ubi)
|
|
|
|
{
|
|
|
|
ubi_msg("MTD device name: \"%s\"", ubi->mtd->name);
|
|
|
|
ubi_msg("MTD device size: %llu MiB", ubi->flash_size >> 20);
|
|
|
|
ubi_msg("physical eraseblock size: %d bytes (%d KiB)",
|
|
|
|
ubi->peb_size, ubi->peb_size >> 10);
|
|
|
|
ubi_msg("logical eraseblock size: %d bytes", ubi->leb_size);
|
|
|
|
ubi_msg("number of good PEBs: %d", ubi->good_peb_count);
|
|
|
|
ubi_msg("number of bad PEBs: %d", ubi->bad_peb_count);
|
|
|
|
ubi_msg("smallest flash I/O unit: %d", ubi->min_io_size);
|
|
|
|
ubi_msg("VID header offset: %d (aligned %d)",
|
|
|
|
ubi->vid_hdr_offset, ubi->vid_hdr_aloffset);
|
|
|
|
ubi_msg("data offset: %d", ubi->leb_start);
|
|
|
|
ubi_msg("max. allowed volumes: %d", ubi->vtbl_slots);
|
|
|
|
ubi_msg("wear-leveling threshold: %d", CONFIG_MTD_UBI_WL_THRESHOLD);
|
|
|
|
ubi_msg("number of internal volumes: %d", UBI_INT_VOL_COUNT);
|
|
|
|
ubi_msg("number of user volumes: %d",
|
|
|
|
ubi->vol_count - UBI_INT_VOL_COUNT);
|
|
|
|
ubi_msg("available PEBs: %d", ubi->avail_pebs);
|
|
|
|
ubi_msg("total number of reserved PEBs: %d", ubi->rsvd_pebs);
|
|
|
|
ubi_msg("number of PEBs reserved for bad PEB handling: %d",
|
|
|
|
ubi->beb_rsvd_pebs);
|
|
|
|
ubi_msg("max/mean erase counter: %d/%d", ubi->max_ec, ubi->mean_ec);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ubi_info(int layout)
|
|
|
|
{
|
|
|
|
if (layout)
|
|
|
|
display_volume_info(ubi);
|
|
|
|
else
|
|
|
|
display_ubi_info(ubi);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-01-25 14:27:11 +08:00
|
|
|
static int ubi_check_volumename(const struct ubi_volume *vol, char *name)
|
|
|
|
{
|
|
|
|
return strcmp(vol->name, name);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ubi_check(char *name)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < (ubi->vtbl_slots + 1); i++) {
|
|
|
|
if (!ubi->volumes[i])
|
|
|
|
continue; /* Empty record */
|
|
|
|
|
|
|
|
if (!ubi_check_volumename(ubi->volumes[i], name))
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-04-10 17:25:43 +08:00
|
|
|
return 1;
|
2014-01-25 14:27:11 +08:00
|
|
|
}
|
|
|
|
|
2008-11-19 18:47:05 +08:00
|
|
|
static int verify_mkvol_req(const struct ubi_device *ubi,
|
|
|
|
const struct ubi_mkvol_req *req)
|
|
|
|
{
|
2011-03-14 21:34:21 +08:00
|
|
|
int n, err = EINVAL;
|
2008-11-19 18:47:05 +08:00
|
|
|
|
|
|
|
if (req->bytes < 0 || req->alignment < 0 || req->vol_type < 0 ||
|
|
|
|
req->name_len < 0)
|
|
|
|
goto bad;
|
|
|
|
|
|
|
|
if ((req->vol_id < 0 || req->vol_id >= ubi->vtbl_slots) &&
|
|
|
|
req->vol_id != UBI_VOL_NUM_AUTO)
|
|
|
|
goto bad;
|
|
|
|
|
|
|
|
if (req->alignment == 0)
|
|
|
|
goto bad;
|
|
|
|
|
2011-03-14 21:34:21 +08:00
|
|
|
if (req->bytes == 0) {
|
|
|
|
printf("No space left in UBI device!\n");
|
|
|
|
err = ENOMEM;
|
2008-11-19 18:47:05 +08:00
|
|
|
goto bad;
|
2011-03-14 21:34:21 +08:00
|
|
|
}
|
2008-11-19 18:47:05 +08:00
|
|
|
|
|
|
|
if (req->vol_type != UBI_DYNAMIC_VOLUME &&
|
|
|
|
req->vol_type != UBI_STATIC_VOLUME)
|
|
|
|
goto bad;
|
|
|
|
|
|
|
|
if (req->alignment > ubi->leb_size)
|
|
|
|
goto bad;
|
|
|
|
|
|
|
|
n = req->alignment % ubi->min_io_size;
|
|
|
|
if (req->alignment != 1 && n)
|
|
|
|
goto bad;
|
|
|
|
|
|
|
|
if (req->name_len > UBI_VOL_NAME_MAX) {
|
2011-03-14 21:34:21 +08:00
|
|
|
printf("Name too long!\n");
|
|
|
|
err = ENAMETOOLONG;
|
2008-11-19 18:47:05 +08:00
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
bad:
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2019-09-12 22:41:01 +08:00
|
|
|
static int ubi_create_vol(char *volume, int64_t size, int dynamic, int vol_id,
|
|
|
|
bool skipcheck)
|
2008-11-19 18:47:05 +08:00
|
|
|
{
|
|
|
|
struct ubi_mkvol_req req;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (dynamic)
|
|
|
|
req.vol_type = UBI_DYNAMIC_VOLUME;
|
|
|
|
else
|
|
|
|
req.vol_type = UBI_STATIC_VOLUME;
|
|
|
|
|
2016-09-13 13:14:01 +08:00
|
|
|
req.vol_id = vol_id;
|
2008-11-19 18:47:05 +08:00
|
|
|
req.alignment = 1;
|
|
|
|
req.bytes = size;
|
|
|
|
|
|
|
|
strcpy(req.name, volume);
|
|
|
|
req.name_len = strlen(volume);
|
|
|
|
req.name[req.name_len] = '\0';
|
2019-09-12 22:41:01 +08:00
|
|
|
req.flags = 0;
|
|
|
|
if (skipcheck)
|
|
|
|
req.flags |= UBI_VOL_SKIP_CRC_CHECK_FLG;
|
|
|
|
|
2008-11-19 18:47:05 +08:00
|
|
|
/* It's duplicated at drivers/mtd/ubi/cdev.c */
|
|
|
|
err = verify_mkvol_req(ubi, &req);
|
|
|
|
if (err) {
|
|
|
|
printf("verify_mkvol_req failed %d\n", err);
|
|
|
|
return err;
|
|
|
|
}
|
2013-09-04 22:16:58 +08:00
|
|
|
printf("Creating %s volume %s of size %lld\n",
|
2008-11-19 18:47:05 +08:00
|
|
|
dynamic ? "dynamic" : "static", volume, size);
|
|
|
|
/* Call real ubi create volume */
|
|
|
|
return ubi_create_volume(ubi, &req);
|
|
|
|
}
|
|
|
|
|
2011-03-14 21:34:21 +08:00
|
|
|
static struct ubi_volume *ubi_find_volume(char *volume)
|
2008-11-19 18:47:05 +08:00
|
|
|
{
|
2010-04-05 11:40:50 +08:00
|
|
|
struct ubi_volume *vol = NULL;
|
2011-03-14 21:34:21 +08:00
|
|
|
int i;
|
2008-11-19 18:47:05 +08:00
|
|
|
|
|
|
|
for (i = 0; i < ubi->vtbl_slots; i++) {
|
|
|
|
vol = ubi->volumes[i];
|
2011-03-14 21:34:21 +08:00
|
|
|
if (vol && !strcmp(vol->name, volume))
|
|
|
|
return vol;
|
2008-11-19 18:47:05 +08:00
|
|
|
}
|
2011-03-14 21:34:21 +08:00
|
|
|
|
|
|
|
printf("Volume %s not found!\n", volume);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ubi_remove_vol(char *volume)
|
|
|
|
{
|
|
|
|
int err, reserved_pebs, i;
|
|
|
|
struct ubi_volume *vol;
|
|
|
|
|
|
|
|
vol = ubi_find_volume(volume);
|
|
|
|
if (vol == NULL)
|
|
|
|
return ENODEV;
|
|
|
|
|
|
|
|
printf("Remove UBI volume %s (id %d)\n", vol->name, vol->vol_id);
|
2008-11-19 18:47:05 +08:00
|
|
|
|
|
|
|
if (ubi->ro_mode) {
|
|
|
|
printf("It's read-only mode\n");
|
2011-03-14 21:34:21 +08:00
|
|
|
err = EROFS;
|
2008-11-19 18:47:05 +08:00
|
|
|
goto out_err;
|
|
|
|
}
|
|
|
|
|
2011-03-14 21:34:21 +08:00
|
|
|
err = ubi_change_vtbl_record(ubi, vol->vol_id, NULL);
|
2008-11-19 18:47:05 +08:00
|
|
|
if (err) {
|
|
|
|
printf("Error changing Vol tabel record err=%x\n", err);
|
|
|
|
goto out_err;
|
|
|
|
}
|
|
|
|
reserved_pebs = vol->reserved_pebs;
|
|
|
|
for (i = 0; i < vol->reserved_pebs; i++) {
|
|
|
|
err = ubi_eba_unmap_leb(ubi, vol, i);
|
|
|
|
if (err)
|
|
|
|
goto out_err;
|
|
|
|
}
|
|
|
|
|
|
|
|
kfree(vol->eba_tbl);
|
2011-03-14 21:34:21 +08:00
|
|
|
ubi->volumes[vol->vol_id]->eba_tbl = NULL;
|
|
|
|
ubi->volumes[vol->vol_id] = NULL;
|
2008-11-19 18:47:05 +08:00
|
|
|
|
|
|
|
ubi->rsvd_pebs -= reserved_pebs;
|
|
|
|
ubi->avail_pebs += reserved_pebs;
|
|
|
|
i = ubi->beb_rsvd_level - ubi->beb_rsvd_pebs;
|
|
|
|
if (i > 0) {
|
|
|
|
i = ubi->avail_pebs >= i ? i : ubi->avail_pebs;
|
|
|
|
ubi->avail_pebs -= i;
|
|
|
|
ubi->rsvd_pebs += i;
|
|
|
|
ubi->beb_rsvd_pebs += i;
|
|
|
|
if (i > 0)
|
|
|
|
ubi_msg("reserve more %d PEBs", i);
|
|
|
|
}
|
|
|
|
ubi->vol_count -= 1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
out_err:
|
2015-10-22 12:19:21 +08:00
|
|
|
ubi_err(ubi, "cannot remove volume %s, error %d", volume, err);
|
2011-03-14 21:34:21 +08:00
|
|
|
if (err < 0)
|
|
|
|
err = -err;
|
2008-11-19 18:47:05 +08:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2020-03-24 02:20:47 +08:00
|
|
|
static int ubi_rename_vol(char *oldname, char *newname)
|
|
|
|
{
|
|
|
|
struct ubi_volume *vol;
|
|
|
|
struct ubi_rename_entry rename;
|
|
|
|
struct ubi_volume_desc desc;
|
|
|
|
struct list_head list;
|
|
|
|
|
|
|
|
vol = ubi_find_volume(oldname);
|
|
|
|
if (!vol) {
|
|
|
|
printf("%s: volume %s doesn't exist\n", __func__, oldname);
|
|
|
|
return ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("Rename UBI volume %s to %s\n", oldname, newname);
|
|
|
|
|
|
|
|
if (ubi->ro_mode) {
|
|
|
|
printf("%s: ubi device is in read-only mode\n", __func__);
|
|
|
|
return EROFS;
|
|
|
|
}
|
|
|
|
|
|
|
|
rename.new_name_len = strlen(newname);
|
|
|
|
strcpy(rename.new_name, newname);
|
|
|
|
rename.remove = 0;
|
|
|
|
desc.vol = vol;
|
|
|
|
desc.mode = 0;
|
|
|
|
rename.desc = &desc;
|
|
|
|
INIT_LIST_HEAD(&rename.list);
|
|
|
|
INIT_LIST_HEAD(&list);
|
|
|
|
list_add(&rename.list, &list);
|
|
|
|
|
|
|
|
return ubi_rename_volumes(ubi, &list);
|
|
|
|
}
|
|
|
|
|
2014-06-23 06:22:08 +08:00
|
|
|
static int ubi_volume_continue_write(char *volume, void *buf, size_t size)
|
2008-11-19 18:47:05 +08:00
|
|
|
{
|
2011-03-14 21:34:21 +08:00
|
|
|
int err = 1;
|
2008-11-19 18:47:05 +08:00
|
|
|
struct ubi_volume *vol;
|
|
|
|
|
2011-03-14 21:34:21 +08:00
|
|
|
vol = ubi_find_volume(volume);
|
|
|
|
if (vol == NULL)
|
|
|
|
return ENODEV;
|
|
|
|
|
2008-11-19 18:47:05 +08:00
|
|
|
err = ubi_more_update_data(ubi, vol, buf, size);
|
|
|
|
if (err < 0) {
|
2011-03-14 21:34:21 +08:00
|
|
|
printf("Couldnt or partially wrote data\n");
|
|
|
|
return -err;
|
2008-11-19 18:47:05 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (err) {
|
|
|
|
size = err;
|
|
|
|
|
|
|
|
err = ubi_check_volume(ubi, vol->vol_id);
|
2011-03-14 21:34:21 +08:00
|
|
|
if (err < 0)
|
|
|
|
return -err;
|
2008-11-19 18:47:05 +08:00
|
|
|
|
|
|
|
if (err) {
|
2015-10-22 12:19:21 +08:00
|
|
|
ubi_warn(ubi, "volume %d on UBI device %d is corrupt",
|
|
|
|
vol->vol_id, ubi->ubi_num);
|
2008-11-19 18:47:05 +08:00
|
|
|
vol->corrupted = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
vol->checked = 1;
|
|
|
|
ubi_gluebi_updated(vol);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-09-04 22:16:59 +08:00
|
|
|
int ubi_volume_begin_write(char *volume, void *buf, size_t size,
|
|
|
|
size_t full_size)
|
|
|
|
{
|
|
|
|
int err = 1;
|
|
|
|
int rsvd_bytes = 0;
|
|
|
|
struct ubi_volume *vol;
|
|
|
|
|
|
|
|
vol = ubi_find_volume(volume);
|
|
|
|
if (vol == NULL)
|
|
|
|
return ENODEV;
|
|
|
|
|
|
|
|
rsvd_bytes = vol->reserved_pebs * (ubi->leb_size - vol->data_pad);
|
2017-04-15 22:25:25 +08:00
|
|
|
if (size > rsvd_bytes) {
|
2013-09-04 22:16:59 +08:00
|
|
|
printf("size > volume size! Aborting!\n");
|
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = ubi_start_update(ubi, vol, full_size);
|
|
|
|
if (err < 0) {
|
|
|
|
printf("Cannot start volume update\n");
|
|
|
|
return -err;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ubi_volume_continue_write(volume, buf, size);
|
|
|
|
}
|
|
|
|
|
|
|
|
int ubi_volume_write(char *volume, void *buf, size_t size)
|
|
|
|
{
|
|
|
|
return ubi_volume_begin_write(volume, buf, size, size);
|
|
|
|
}
|
|
|
|
|
2013-04-08 18:32:47 +08:00
|
|
|
int ubi_volume_read(char *volume, char *buf, size_t size)
|
2008-11-19 18:47:05 +08:00
|
|
|
{
|
2011-03-14 21:34:21 +08:00
|
|
|
int err, lnum, off, len, tbuf_size;
|
2008-11-19 18:47:05 +08:00
|
|
|
void *tbuf;
|
|
|
|
unsigned long long tmp;
|
2011-03-14 21:34:21 +08:00
|
|
|
struct ubi_volume *vol;
|
2008-11-19 18:47:05 +08:00
|
|
|
loff_t offp = 0;
|
2017-08-31 00:38:52 +08:00
|
|
|
size_t len_read;
|
2008-11-19 18:47:05 +08:00
|
|
|
|
2011-03-14 21:34:21 +08:00
|
|
|
vol = ubi_find_volume(volume);
|
|
|
|
if (vol == NULL)
|
|
|
|
return ENODEV;
|
2008-11-19 18:47:05 +08:00
|
|
|
|
|
|
|
if (vol->updating) {
|
|
|
|
printf("updating");
|
2011-03-14 21:34:21 +08:00
|
|
|
return EBUSY;
|
2008-11-19 18:47:05 +08:00
|
|
|
}
|
|
|
|
if (vol->upd_marker) {
|
|
|
|
printf("damaged volume, update marker is set");
|
2011-03-14 21:34:21 +08:00
|
|
|
return EBADF;
|
2008-11-19 18:47:05 +08:00
|
|
|
}
|
|
|
|
if (offp == vol->used_bytes)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (size == 0) {
|
2011-03-14 21:34:21 +08:00
|
|
|
printf("No size specified -> Using max size (%lld)\n", vol->used_bytes);
|
2008-11-19 18:47:05 +08:00
|
|
|
size = vol->used_bytes;
|
|
|
|
}
|
|
|
|
|
2018-07-26 23:17:24 +08:00
|
|
|
printf("Read %zu bytes from volume %s to %p\n", size, volume, buf);
|
2018-06-25 17:19:12 +08:00
|
|
|
|
2008-11-19 18:47:05 +08:00
|
|
|
if (vol->corrupted)
|
|
|
|
printf("read from corrupted volume %d", vol->vol_id);
|
|
|
|
if (offp + size > vol->used_bytes)
|
2011-09-30 18:13:25 +08:00
|
|
|
size = vol->used_bytes - offp;
|
2008-11-19 18:47:05 +08:00
|
|
|
|
|
|
|
tbuf_size = vol->usable_leb_size;
|
|
|
|
if (size < tbuf_size)
|
|
|
|
tbuf_size = ALIGN(size, ubi->min_io_size);
|
2015-08-18 19:06:37 +08:00
|
|
|
tbuf = malloc_cache_aligned(tbuf_size);
|
2008-11-19 18:47:05 +08:00
|
|
|
if (!tbuf) {
|
|
|
|
printf("NO MEM\n");
|
2011-03-14 21:34:21 +08:00
|
|
|
return ENOMEM;
|
2008-11-19 18:47:05 +08:00
|
|
|
}
|
|
|
|
len = size > tbuf_size ? tbuf_size : size;
|
|
|
|
|
|
|
|
tmp = offp;
|
|
|
|
off = do_div(tmp, vol->usable_leb_size);
|
|
|
|
lnum = tmp;
|
2017-08-31 00:38:52 +08:00
|
|
|
len_read = size;
|
2008-11-19 18:47:05 +08:00
|
|
|
do {
|
|
|
|
if (off + len >= vol->usable_leb_size)
|
|
|
|
len = vol->usable_leb_size - off;
|
|
|
|
|
|
|
|
err = ubi_eba_read_leb(ubi, vol, lnum, tbuf, off, len, 0);
|
|
|
|
if (err) {
|
|
|
|
printf("read err %x\n", err);
|
2011-03-14 21:34:21 +08:00
|
|
|
err = -err;
|
2008-11-19 18:47:05 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
off += len;
|
|
|
|
if (off == vol->usable_leb_size) {
|
|
|
|
lnum += 1;
|
|
|
|
off -= vol->usable_leb_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
size -= len;
|
|
|
|
offp += len;
|
|
|
|
|
|
|
|
memcpy(buf, tbuf, len);
|
|
|
|
|
|
|
|
buf += len;
|
|
|
|
len = size > tbuf_size ? tbuf_size : size;
|
|
|
|
} while (size);
|
|
|
|
|
2017-08-31 00:38:52 +08:00
|
|
|
if (!size)
|
|
|
|
env_set_hex("filesize", len_read);
|
|
|
|
|
2008-11-19 18:47:05 +08:00
|
|
|
free(tbuf);
|
2011-03-14 21:34:21 +08:00
|
|
|
return err;
|
2008-11-19 18:47:05 +08:00
|
|
|
}
|
|
|
|
|
2018-09-29 18:58:29 +08:00
|
|
|
static int ubi_dev_scan(struct mtd_info *info, const char *vid_header_offset)
|
2008-11-19 18:47:05 +08:00
|
|
|
{
|
2009-07-07 22:59:46 +08:00
|
|
|
char ubi_mtd_param_buffer[80];
|
2008-11-19 18:47:05 +08:00
|
|
|
int err;
|
|
|
|
|
2018-09-29 18:58:29 +08:00
|
|
|
if (!vid_header_offset)
|
|
|
|
sprintf(ubi_mtd_param_buffer, "%s", info->name);
|
|
|
|
else
|
|
|
|
sprintf(ubi_mtd_param_buffer, "%s,%s", info->name,
|
|
|
|
vid_header_offset);
|
2008-11-19 18:47:05 +08:00
|
|
|
|
2009-07-07 22:59:46 +08:00
|
|
|
err = ubi_mtd_param_parse(ubi_mtd_param_buffer, NULL);
|
2018-09-29 18:58:29 +08:00
|
|
|
if (err)
|
2011-03-14 21:34:21 +08:00
|
|
|
return -err;
|
2008-11-19 18:47:05 +08:00
|
|
|
|
|
|
|
err = ubi_init();
|
2018-09-29 18:58:29 +08:00
|
|
|
if (err)
|
2011-03-14 21:34:21 +08:00
|
|
|
return -err;
|
2008-11-27 21:07:09 +08:00
|
|
|
|
2008-11-19 18:47:05 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-09-17 15:17:53 +08:00
|
|
|
static int ubi_set_skip_check(char *volume, bool skip_check)
|
|
|
|
{
|
|
|
|
struct ubi_vtbl_record vtbl_rec;
|
|
|
|
struct ubi_volume *vol;
|
|
|
|
|
|
|
|
vol = ubi_find_volume(volume);
|
|
|
|
if (!vol)
|
|
|
|
return ENODEV;
|
|
|
|
|
|
|
|
printf("%sing skip_check on volume %s\n",
|
|
|
|
skip_check ? "Sett" : "Clear", volume);
|
|
|
|
|
|
|
|
vtbl_rec = ubi->vtbl[vol->vol_id];
|
|
|
|
if (skip_check) {
|
|
|
|
vtbl_rec.flags |= UBI_VTBL_SKIP_CRC_CHECK_FLG;
|
|
|
|
vol->skip_check = 1;
|
|
|
|
} else {
|
|
|
|
vtbl_rec.flags &= ~UBI_VTBL_SKIP_CRC_CHECK_FLG;
|
|
|
|
vol->skip_check = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ubi_change_vtbl_record(ubi, vol->vol_id, &vtbl_rec);
|
|
|
|
}
|
|
|
|
|
2018-10-31 19:37:20 +08:00
|
|
|
static int ubi_detach(void)
|
2008-11-19 18:47:05 +08:00
|
|
|
{
|
2013-04-08 18:32:47 +08:00
|
|
|
#ifdef CONFIG_CMD_UBIFS
|
|
|
|
/*
|
|
|
|
* Automatically unmount UBIFS partition when user
|
|
|
|
* changes the UBI device. Otherwise the following
|
|
|
|
* UBIFS commands will crash.
|
|
|
|
*/
|
|
|
|
if (ubifs_is_mounted())
|
|
|
|
cmd_ubifs_umount();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Call ubi_exit() before re-initializing the UBI subsystem
|
|
|
|
*/
|
2018-09-29 18:58:29 +08:00
|
|
|
if (ubi)
|
2013-04-08 18:32:47 +08:00
|
|
|
ubi_exit();
|
|
|
|
|
2018-09-29 18:58:29 +08:00
|
|
|
ubi = NULL;
|
|
|
|
|
2016-06-07 14:55:40 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ubi_part(char *part_name, const char *vid_header_offset)
|
|
|
|
{
|
2018-09-29 18:58:29 +08:00
|
|
|
struct mtd_info *mtd;
|
2016-06-07 14:55:40 +08:00
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
ubi_detach();
|
2018-09-29 18:58:29 +08:00
|
|
|
|
|
|
|
mtd_probe_devices();
|
|
|
|
mtd = get_mtd_device_nm(part_name);
|
|
|
|
if (IS_ERR(mtd)) {
|
2013-04-08 18:32:47 +08:00
|
|
|
printf("Partition %s not found!\n", part_name);
|
|
|
|
return 1;
|
|
|
|
}
|
2018-09-29 18:58:29 +08:00
|
|
|
put_mtd_device(mtd);
|
2013-04-08 18:32:47 +08:00
|
|
|
|
2018-09-29 18:58:29 +08:00
|
|
|
err = ubi_dev_scan(mtd, vid_header_offset);
|
2013-04-08 18:32:47 +08:00
|
|
|
if (err) {
|
|
|
|
printf("UBI init error %d\n", err);
|
cmd: ubi: Add additional message upon UBI attach error
When trying to attach an UBI MTD partition via "ubi part", it may happen
that the MTD partition defined in U-Boot (via mtdparts) is not big
enough than the one, where the UBI device has been created on. This
may lead to errors, which are not really descriptive to debug and
solve this issue, like:
ubi0 error: vtbl_check: too large reserved_pebs 1982, good PEBs 1020
ubi0 error: vtbl_check: volume table check failed: record 0, error 9
or:
ubi0 error: init_volumes: not enough PEBs, required 1738, available 1020
ubi0 error: ubi_wl_init: no enough physical eraseblocks (-718, need 1)
ubi0 error: ubi_attach_mtd_dev: failed to attach mtd1, error -12
Lets add an additional message upon attach failure, to aid the U-Boot
user to solve this problem.
Signed-off-by: Stefan Roese <sr@denx.de>
Cc: Stefano Babic <sbabic@denx.de>
Cc: Heiko Schocher <hs@denx.de>
2018-06-26 14:12:32 +08:00
|
|
|
printf("Please check, if the correct MTD partition is used (size big enough?)\n");
|
2013-04-08 18:32:47 +08:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
ubi = ubi_devices[0];
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-05-11 01:40:03 +08:00
|
|
|
static int do_ubi(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
|
2013-04-08 18:32:47 +08:00
|
|
|
{
|
2013-09-04 22:16:58 +08:00
|
|
|
int64_t size = 0;
|
2013-04-08 18:32:47 +08:00
|
|
|
ulong addr = 0;
|
2019-09-12 22:41:01 +08:00
|
|
|
bool skipcheck = false;
|
2013-04-08 18:32:47 +08:00
|
|
|
|
|
|
|
if (argc < 2)
|
|
|
|
return CMD_RET_USAGE;
|
|
|
|
|
2019-01-06 19:26:28 +08:00
|
|
|
if (strcmp(argv[1], "detach") == 0)
|
2016-06-07 14:55:40 +08:00
|
|
|
return ubi_detach();
|
|
|
|
|
2008-11-19 18:47:05 +08:00
|
|
|
if (strcmp(argv[1], "part") == 0) {
|
2009-07-07 22:59:46 +08:00
|
|
|
const char *vid_header_offset = NULL;
|
2009-04-25 02:24:19 +08:00
|
|
|
|
2008-11-19 18:47:05 +08:00
|
|
|
/* Print current partition */
|
|
|
|
if (argc == 2) {
|
2018-09-29 18:58:29 +08:00
|
|
|
if (!ubi) {
|
|
|
|
printf("Error, no UBI device selected!\n");
|
2008-11-19 18:47:05 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2018-09-29 18:58:29 +08:00
|
|
|
printf("Device %d: %s, MTD partition %s\n",
|
|
|
|
ubi->ubi_num, ubi->ubi_name, ubi->mtd->name);
|
2008-11-19 18:47:05 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-07-17 07:06:04 +08:00
|
|
|
if (argc < 3)
|
2011-12-10 16:44:01 +08:00
|
|
|
return CMD_RET_USAGE;
|
2008-11-19 18:47:05 +08:00
|
|
|
|
2009-07-07 22:59:46 +08:00
|
|
|
if (argc > 3)
|
|
|
|
vid_header_offset = argv[3];
|
2008-11-19 18:47:05 +08:00
|
|
|
|
2013-04-08 18:32:47 +08:00
|
|
|
return ubi_part(argv[2], vid_header_offset);
|
2008-11-19 18:47:05 +08:00
|
|
|
}
|
|
|
|
|
2018-09-29 18:58:29 +08:00
|
|
|
if ((strcmp(argv[1], "part") != 0) && !ubi) {
|
|
|
|
printf("Error, no UBI device selected!\n");
|
2008-11-19 18:47:05 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strcmp(argv[1], "info") == 0) {
|
|
|
|
int layout = 0;
|
|
|
|
if (argc > 2 && !strncmp(argv[2], "l", 1))
|
|
|
|
layout = 1;
|
|
|
|
return ubi_info(layout);
|
|
|
|
}
|
|
|
|
|
2014-01-25 14:27:11 +08:00
|
|
|
if (strcmp(argv[1], "check") == 0) {
|
|
|
|
if (argc > 2)
|
|
|
|
return ubi_check(argv[2]);
|
|
|
|
|
|
|
|
printf("Error, no volume name passed\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2008-11-19 18:47:05 +08:00
|
|
|
if (strncmp(argv[1], "create", 6) == 0) {
|
|
|
|
int dynamic = 1; /* default: dynamic volume */
|
2016-09-13 13:14:01 +08:00
|
|
|
int id = UBI_VOL_NUM_AUTO;
|
2008-11-19 18:47:05 +08:00
|
|
|
|
|
|
|
/* Use maximum available size */
|
|
|
|
size = 0;
|
|
|
|
|
2019-09-12 22:41:01 +08:00
|
|
|
/* E.g., create volume with "skipcheck" bit set */
|
|
|
|
if (argc == 7) {
|
|
|
|
skipcheck = strncmp(argv[6], "--skipcheck", 11) == 0;
|
|
|
|
argc--;
|
|
|
|
}
|
|
|
|
|
2016-09-13 13:14:01 +08:00
|
|
|
/* E.g., create volume size type vol_id */
|
|
|
|
if (argc == 6) {
|
|
|
|
id = simple_strtoull(argv[5], NULL, 16);
|
|
|
|
argc--;
|
|
|
|
}
|
|
|
|
|
2008-11-19 18:47:05 +08:00
|
|
|
/* E.g., create volume size type */
|
|
|
|
if (argc == 5) {
|
|
|
|
if (strncmp(argv[4], "s", 1) == 0)
|
|
|
|
dynamic = 0;
|
|
|
|
else if (strncmp(argv[4], "d", 1) != 0) {
|
|
|
|
printf("Incorrect type\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
argc--;
|
|
|
|
}
|
|
|
|
/* E.g., create volume size */
|
|
|
|
if (argc == 4) {
|
2017-01-19 18:45:35 +08:00
|
|
|
if (argv[3][0] != '-')
|
|
|
|
size = simple_strtoull(argv[3], NULL, 16);
|
2008-11-19 18:47:05 +08:00
|
|
|
argc--;
|
|
|
|
}
|
|
|
|
/* Use maximum available size */
|
2011-03-14 21:34:21 +08:00
|
|
|
if (!size) {
|
2013-09-04 22:16:58 +08:00
|
|
|
size = (int64_t)ubi->avail_pebs * ubi->leb_size;
|
|
|
|
printf("No size specified -> Using max size (%lld)\n", size);
|
2011-03-14 21:34:21 +08:00
|
|
|
}
|
2008-11-19 18:47:05 +08:00
|
|
|
/* E.g., create volume */
|
2019-09-12 22:41:01 +08:00
|
|
|
if (argc == 3) {
|
|
|
|
return ubi_create_vol(argv[2], size, dynamic, id,
|
|
|
|
skipcheck);
|
|
|
|
}
|
2008-11-19 18:47:05 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (strncmp(argv[1], "remove", 6) == 0) {
|
|
|
|
/* E.g., remove volume */
|
|
|
|
if (argc == 3)
|
|
|
|
return ubi_remove_vol(argv[2]);
|
|
|
|
}
|
|
|
|
|
2020-03-24 02:20:47 +08:00
|
|
|
if (IS_ENABLED(CONFIG_CMD_UBI_RENAME) && !strncmp(argv[1], "rename", 6))
|
|
|
|
return ubi_rename_vol(argv[2], argv[3]);
|
|
|
|
|
2019-09-17 15:17:53 +08:00
|
|
|
if (strncmp(argv[1], "skipcheck", 9) == 0) {
|
|
|
|
/* E.g., change skip_check flag */
|
|
|
|
if (argc == 4) {
|
|
|
|
skipcheck = strncmp(argv[3], "on", 2) == 0;
|
|
|
|
return ubi_set_skip_check(argv[2], skipcheck);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-11-19 18:47:05 +08:00
|
|
|
if (strncmp(argv[1], "write", 5) == 0) {
|
2013-04-08 18:32:47 +08:00
|
|
|
int ret;
|
|
|
|
|
2008-11-19 18:47:05 +08:00
|
|
|
if (argc < 5) {
|
|
|
|
printf("Please see usage\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
addr = simple_strtoul(argv[2], NULL, 16);
|
2008-11-24 15:31:16 +08:00
|
|
|
size = simple_strtoul(argv[4], NULL, 16);
|
2008-11-19 18:47:05 +08:00
|
|
|
|
2013-09-04 22:16:59 +08:00
|
|
|
if (strlen(argv[1]) == 10 &&
|
|
|
|
strncmp(argv[1] + 5, ".part", 5) == 0) {
|
|
|
|
if (argc < 6) {
|
|
|
|
ret = ubi_volume_continue_write(argv[3],
|
|
|
|
(void *)addr, size);
|
|
|
|
} else {
|
|
|
|
size_t full_size;
|
|
|
|
full_size = simple_strtoul(argv[5], NULL, 16);
|
|
|
|
ret = ubi_volume_begin_write(argv[3],
|
|
|
|
(void *)addr, size, full_size);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ret = ubi_volume_write(argv[3], (void *)addr, size);
|
|
|
|
}
|
2013-04-08 18:32:47 +08:00
|
|
|
if (!ret) {
|
2013-09-04 22:16:58 +08:00
|
|
|
printf("%lld bytes written to volume %s\n", size,
|
2013-04-08 18:32:47 +08:00
|
|
|
argv[3]);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
2008-11-19 18:47:05 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (strncmp(argv[1], "read", 4) == 0) {
|
|
|
|
size = 0;
|
|
|
|
|
|
|
|
/* E.g., read volume size */
|
|
|
|
if (argc == 5) {
|
2008-11-24 15:31:16 +08:00
|
|
|
size = simple_strtoul(argv[4], NULL, 16);
|
2008-11-19 18:47:05 +08:00
|
|
|
argc--;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* E.g., read volume */
|
|
|
|
if (argc == 4) {
|
|
|
|
addr = simple_strtoul(argv[2], NULL, 16);
|
|
|
|
argc--;
|
|
|
|
}
|
|
|
|
|
2013-04-08 18:32:47 +08:00
|
|
|
if (argc == 3) {
|
2008-11-19 18:47:05 +08:00
|
|
|
return ubi_volume_read(argv[3], (char *)addr, size);
|
2013-04-08 18:32:47 +08:00
|
|
|
}
|
2008-11-19 18:47:05 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
printf("Please see usage\n");
|
2011-03-14 21:34:21 +08:00
|
|
|
return 1;
|
2008-11-19 18:47:05 +08:00
|
|
|
}
|
|
|
|
|
2010-07-31 21:01:53 +08:00
|
|
|
U_BOOT_CMD(
|
2019-09-12 22:41:01 +08:00
|
|
|
ubi, 7, 1, do_ubi,
|
2009-01-28 08:03:12 +08:00
|
|
|
"ubi commands",
|
2016-06-07 14:55:40 +08:00
|
|
|
"detach"
|
|
|
|
" - detach ubi from a mtd partition\n"
|
|
|
|
"ubi part [part] [offset]\n"
|
2009-07-07 22:59:46 +08:00
|
|
|
" - Show or set current partition (with optional VID"
|
|
|
|
" header offset)\n"
|
2008-11-19 18:47:05 +08:00
|
|
|
"ubi info [l[ayout]]"
|
|
|
|
" - Display volume and ubi layout information\n"
|
2014-01-25 14:27:11 +08:00
|
|
|
"ubi check volumename"
|
|
|
|
" - check if volumename exists\n"
|
2019-09-12 22:41:01 +08:00
|
|
|
"ubi create[vol] volume [size] [type] [id] [--skipcheck]\n"
|
2017-01-19 18:45:35 +08:00
|
|
|
" - create volume name with size ('-' for maximum"
|
|
|
|
" available size)\n"
|
2008-11-19 18:47:05 +08:00
|
|
|
"ubi write[vol] address volume size"
|
|
|
|
" - Write volume from address with size\n"
|
2013-09-04 22:16:59 +08:00
|
|
|
"ubi write.part address volume size [fullsize]\n"
|
|
|
|
" - Write part of a volume from address\n"
|
2008-11-19 18:47:05 +08:00
|
|
|
"ubi read[vol] address volume [size]"
|
|
|
|
" - Read volume to address with size\n"
|
|
|
|
"ubi remove[vol] volume"
|
|
|
|
" - Remove volume\n"
|
2020-03-24 02:20:47 +08:00
|
|
|
#if IS_ENABLED(CONFIG_CMD_UBI_RENAME)
|
|
|
|
"ubi rename oldname newname\n"
|
|
|
|
#endif
|
2019-09-17 15:17:53 +08:00
|
|
|
"ubi skipcheck volume on/off - Set or clear skip_check flag in volume header\n"
|
2008-11-19 18:47:05 +08:00
|
|
|
"[Legends]\n"
|
2009-07-18 04:26:54 +08:00
|
|
|
" volume: character name\n"
|
|
|
|
" size: specified in bytes\n"
|
2009-05-24 23:06:54 +08:00
|
|
|
" type: s[tatic] or d[ynamic] (default=dynamic)"
|
2008-11-19 18:47:05 +08:00
|
|
|
);
|