GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)

This commit is contained in:
lai
2026-09-06 03:52:57 +08:00
commit b1928b41c0
21813 changed files with 4413081 additions and 0 deletions
@@ -0,0 +1,72 @@
menu "DFU support"
config DFU
bool
imply DFU_OVER_USB if USB_GADGET
config DFU_OVER_USB
bool
select HASH
depends on USB_GADGET
config DFU_OVER_TFTP
bool
depends on NET
if DFU
config DFU_TFTP
bool "DFU via TFTP"
select DFU_OVER_TFTP
help
This option allows performing update of DFU-managed medium with data
sent via TFTP boot.
Detailed description of this feature can be found at ./doc/README.dfutftp
config DFU_MMC
bool "MMC back end for DFU"
help
This option enables using DFU to read and write to MMC based storage.
config DFU_NAND
bool "NAND back end for DFU"
depends on CMD_MTDPARTS
depends on MTD_RAW_NAND
help
This option enables using DFU to read and write to NAND based
storage.
config DFU_RAM
bool "RAM back end for DFU"
help
This option enables using DFU to read and write RAM on the target.
config DFU_SF
bool "SPI flash back end for DFU"
help
This option enables using DFU to read and write to SPI flash based
storage.
config DFU_SF_PART
bool "MTD partition support for SPI flash back end"
depends on DFU_SF && CMD_MTDPARTS
default y
help
This option enables the support of "part" and "partubi" target in
SPI flash DFU back end.
config DFU_MTD
bool "MTD back end for DFU"
depends on DM_MTD
help
This option enables using DFU to read and write to on any MTD device.
config DFU_VIRT
bool "VIRTUAL flash back end for DFU"
help
This option enables using DFU to read and write to VIRTUAL device
used at board level to manage specific behavior
(OTP update for example).
endif
endmenu
@@ -0,0 +1,13 @@
# SPDX-License-Identifier: GPL-2.0+
#
# Copyright (C) 2012 Samsung Electronics
# Lukasz Majewski <l.majewski@samsung.com>
obj-$(CONFIG_$(SPL_)DFU) += dfu.o
obj-$(CONFIG_$(SPL_)DFU_MMC) += dfu_mmc.o
obj-$(CONFIG_$(SPL_)DFU_MTD) += dfu_mtd.o
obj-$(CONFIG_$(SPL_)DFU_NAND) += dfu_nand.o
obj-$(CONFIG_$(SPL_)DFU_RAM) += dfu_ram.o
obj-$(CONFIG_$(SPL_)DFU_SF) += dfu_sf.o
obj-$(CONFIG_$(SPL_)DFU_TFTP) += dfu_tftp.o
obj-$(CONFIG_$(SPL_)DFU_VIRT) += dfu_virt.o
@@ -0,0 +1,707 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* dfu.c -- DFU back-end routines
*
* Copyright (C) 2012 Samsung Electronics
* author: Lukasz Majewski <l.majewski@samsung.com>
*/
#include <common.h>
#include <env.h>
#include <errno.h>
#include <malloc.h>
#include <mmc.h>
#include <fat.h>
#include <dfu.h>
#include <hash.h>
#include <linux/list.h>
#include <linux/compiler.h>
static LIST_HEAD(dfu_list);
static int dfu_alt_num;
static int alt_num_cnt;
static struct hash_algo *dfu_hash_algo;
/*
* The purpose of the dfu_flush_callback() function is to
* provide callback for dfu user
*/
__weak void dfu_flush_callback(struct dfu_entity *dfu)
{
}
/*
* The purpose of the dfu_initiated_callback() function is to
* provide callback for dfu user
*/
__weak void dfu_initiated_callback(struct dfu_entity *dfu)
{
}
/*
* The purpose of the dfu_usb_get_reset() function is to
* provide information if after USB_DETACH request
* being sent the dfu-util performed reset of USB
* bus.
*
* Described behaviour is the only way to distinct if
* user has typed -e (detach) or -R (reset) when invoking
* dfu-util command.
*
*/
__weak bool dfu_usb_get_reset(void)
{
#ifdef CONFIG_SPL_DFU_NO_RESET
return false;
#else
return true;
#endif
}
static int dfu_find_alt_num(const char *s)
{
int i = 0;
for (; *s; s++)
if (*s == ';')
i++;
return ++i;
}
/*
* treat dfu_alt_info with several interface information
* to allow DFU on several device with one command,
* the string format is
* interface devstring'='alternate list (';' separated)
* and each interface separated by '&'
*/
int dfu_config_interfaces(char *env)
{
struct dfu_entity *dfu;
char *s, *i, *d, *a, *part;
int ret = -EINVAL;
int n = 1;
s = env;
for (; *s; s++) {
if (*s == ';')
n++;
if (*s == '&')
n++;
}
ret = dfu_alt_init(n, &dfu);
if (ret)
return ret;
s = env;
while (s) {
ret = -EINVAL;
i = strsep(&s, " ");
if (!i)
break;
d = strsep(&s, "=");
if (!d)
break;
a = strsep(&s, "&");
if (!a)
a = s;
do {
part = strsep(&a, ";");
ret = dfu_alt_add(dfu, i, d, part);
if (ret)
return ret;
} while (a);
}
return ret;
}
int dfu_init_env_entities(char *interface, char *devstr)
{
const char *str_env;
char *env_bkp;
int ret = 0;
#ifdef CONFIG_SET_DFU_ALT_INFO
set_dfu_alt_info(interface, devstr);
#endif
str_env = env_get("dfu_alt_info");
if (!str_env) {
pr_err("\"dfu_alt_info\" env variable not defined!\n");
return -EINVAL;
}
env_bkp = strdup(str_env);
if (!interface && !devstr)
ret = dfu_config_interfaces(env_bkp);
else
ret = dfu_config_entities(env_bkp, interface, devstr);
if (ret) {
pr_err("DFU entities configuration failed!\n");
pr_err("(partition table does not match dfu_alt_info?)\n");
goto done;
}
done:
free(env_bkp);
return ret;
}
static unsigned char *dfu_buf;
static unsigned long dfu_buf_size;
static enum dfu_device_type dfu_buf_device_type;
unsigned char *dfu_free_buf(void)
{
free(dfu_buf);
dfu_buf = NULL;
return dfu_buf;
}
unsigned long dfu_get_buf_size(void)
{
return dfu_buf_size;
}
unsigned char *dfu_get_buf(struct dfu_entity *dfu)
{
char *s;
/* manage several entity with several contraint */
if (dfu_buf && dfu->dev_type != dfu_buf_device_type)
dfu_free_buf();
if (dfu_buf != NULL)
return dfu_buf;
s = env_get("dfu_bufsiz");
if (s)
dfu_buf_size = (unsigned long)simple_strtol(s, NULL, 0);
if (!s || !dfu_buf_size)
dfu_buf_size = CONFIG_SYS_DFU_DATA_BUF_SIZE;
if (dfu->max_buf_size && dfu_buf_size > dfu->max_buf_size)
dfu_buf_size = dfu->max_buf_size;
dfu_buf = memalign(CONFIG_SYS_CACHELINE_SIZE, dfu_buf_size);
if (dfu_buf == NULL)
printf("%s: Could not memalign 0x%lx bytes\n",
__func__, dfu_buf_size);
dfu_buf_device_type = dfu->dev_type;
return dfu_buf;
}
static char *dfu_get_hash_algo(void)
{
char *s;
s = env_get("dfu_hash_algo");
if (!s)
return NULL;
if (!strcmp(s, "crc32")) {
debug("%s: DFU hash method: %s\n", __func__, s);
return s;
}
pr_err("DFU hash method: %s not supported!\n", s);
return NULL;
}
static int dfu_write_buffer_drain(struct dfu_entity *dfu)
{
long w_size;
int ret;
/* flush size? */
w_size = dfu->i_buf - dfu->i_buf_start;
if (w_size == 0)
return 0;
if (dfu_hash_algo)
dfu_hash_algo->hash_update(dfu_hash_algo, &dfu->crc,
dfu->i_buf_start, w_size, 0);
ret = dfu->write_medium(dfu, dfu->offset, dfu->i_buf_start, &w_size);
if (ret)
debug("%s: Write error!\n", __func__);
/* point back */
dfu->i_buf = dfu->i_buf_start;
/* update offset */
dfu->offset += w_size;
puts("#");
return ret;
}
void dfu_transaction_cleanup(struct dfu_entity *dfu)
{
/* clear everything */
dfu->crc = 0;
dfu->offset = 0;
dfu->i_blk_seq_num = 0;
dfu->i_buf_start = dfu_get_buf(dfu);
dfu->i_buf_end = dfu->i_buf_start;
dfu->i_buf = dfu->i_buf_start;
dfu->r_left = 0;
dfu->b_left = 0;
dfu->bad_skip = 0;
dfu->inited = 0;
}
int dfu_transaction_initiate(struct dfu_entity *dfu, bool read)
{
int ret = 0;
if (dfu->inited)
return 0;
dfu_transaction_cleanup(dfu);
if (dfu->i_buf_start == NULL)
return -ENOMEM;
dfu->i_buf_end = dfu->i_buf_start + dfu_get_buf_size();
if (read) {
ret = dfu->get_medium_size(dfu, &dfu->r_left);
if (ret < 0)
return ret;
debug("%s: %s %lld [B]\n", __func__, dfu->name, dfu->r_left);
}
dfu->inited = 1;
dfu_initiated_callback(dfu);
return 0;
}
int dfu_flush(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num)
{
int ret = 0;
ret = dfu_write_buffer_drain(dfu);
if (ret)
return ret;
if (dfu->flush_medium)
ret = dfu->flush_medium(dfu);
if (dfu_hash_algo)
printf("\nDFU complete %s: 0x%08x\n", dfu_hash_algo->name,
dfu->crc);
dfu_flush_callback(dfu);
dfu_transaction_cleanup(dfu);
return ret;
}
int dfu_write(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num)
{
int ret;
debug("%s: name: %s buf: 0x%p size: 0x%x p_num: 0x%x offset: 0x%llx bufoffset: 0x%lx\n",
__func__, dfu->name, buf, size, blk_seq_num, dfu->offset,
(unsigned long)(dfu->i_buf - dfu->i_buf_start));
ret = dfu_transaction_initiate(dfu, false);
if (ret < 0)
return ret;
if (dfu->i_blk_seq_num != blk_seq_num) {
printf("%s: Wrong sequence number! [%d] [%d]\n",
__func__, dfu->i_blk_seq_num, blk_seq_num);
dfu_transaction_cleanup(dfu);
return -1;
}
/* DFU 1.1 standard says:
* The wBlockNum field is a block sequence number. It increments each
* time a block is transferred, wrapping to zero from 65,535. It is used
* to provide useful context to the DFU loader in the device."
*
* This means that it's a 16 bit counter that roll-overs at
* 0xffff -> 0x0000. By having a typical 4K transfer block
* we roll-over at exactly 256MB. Not very fun to debug.
*
* Handling rollover, and having an inited variable,
* makes things work.
*/
/* handle rollover */
dfu->i_blk_seq_num = (dfu->i_blk_seq_num + 1) & 0xffff;
/* flush buffer if overflow */
if ((dfu->i_buf + size) > dfu->i_buf_end) {
ret = dfu_write_buffer_drain(dfu);
if (ret) {
dfu_transaction_cleanup(dfu);
return ret;
}
}
/* we should be in buffer now (if not then size too large) */
if ((dfu->i_buf + size) > dfu->i_buf_end) {
pr_err("Buffer overflow! (0x%p + 0x%x > 0x%p)\n", dfu->i_buf,
size, dfu->i_buf_end);
dfu_transaction_cleanup(dfu);
return -1;
}
memcpy(dfu->i_buf, buf, size);
dfu->i_buf += size;
/* if end or if buffer full flush */
if (size == 0 || (dfu->i_buf + size) > dfu->i_buf_end) {
ret = dfu_write_buffer_drain(dfu);
if (ret) {
dfu_transaction_cleanup(dfu);
return ret;
}
}
return 0;
}
static int dfu_read_buffer_fill(struct dfu_entity *dfu, void *buf, int size)
{
long chunk;
int ret, readn;
readn = 0;
while (size > 0) {
/* get chunk that can be read */
chunk = min((long)size, dfu->b_left);
/* consume */
if (chunk > 0) {
memcpy(buf, dfu->i_buf, chunk);
if (dfu_hash_algo)
dfu_hash_algo->hash_update(dfu_hash_algo,
&dfu->crc, buf,
chunk, 0);
dfu->i_buf += chunk;
dfu->b_left -= chunk;
size -= chunk;
buf += chunk;
readn += chunk;
}
/* all done */
if (size > 0) {
/* no more to read */
if (dfu->r_left == 0)
break;
dfu->i_buf = dfu->i_buf_start;
dfu->b_left = dfu->i_buf_end - dfu->i_buf_start;
/* got to read, but buffer is empty */
if (dfu->b_left > dfu->r_left)
dfu->b_left = dfu->r_left;
ret = dfu->read_medium(dfu, dfu->offset, dfu->i_buf,
&dfu->b_left);
if (ret != 0) {
debug("%s: Read error!\n", __func__);
return ret;
}
if (dfu->b_left == 0)
break;
dfu->offset += dfu->b_left;
dfu->r_left -= dfu->b_left;
puts("#");
}
}
return readn;
}
int dfu_read(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num)
{
int ret = 0;
debug("%s: name: %s buf: 0x%p size: 0x%x p_num: 0x%x i_buf: 0x%p\n",
__func__, dfu->name, buf, size, blk_seq_num, dfu->i_buf);
ret = dfu_transaction_initiate(dfu, true);
if (ret < 0)
return ret;
if (dfu->i_blk_seq_num != blk_seq_num) {
printf("%s: Wrong sequence number! [%d] [%d]\n",
__func__, dfu->i_blk_seq_num, blk_seq_num);
return -1;
}
/* handle rollover */
dfu->i_blk_seq_num = (dfu->i_blk_seq_num + 1) & 0xffff;
ret = dfu_read_buffer_fill(dfu, buf, size);
if (ret < 0) {
printf("%s: Failed to fill buffer\n", __func__);
return -1;
}
if (ret < size) {
if (dfu_hash_algo)
debug("%s: %s %s: 0x%x\n", __func__, dfu->name,
dfu_hash_algo->name, dfu->crc);
puts("\nUPLOAD ... done\nCtrl+C to exit ...\n");
dfu_transaction_cleanup(dfu);
}
return ret;
}
static int dfu_fill_entity(struct dfu_entity *dfu, char *s, int alt,
char *interface, char *devstr)
{
char *st;
debug("%s: %s interface: %s dev: %s\n", __func__, s, interface, devstr);
st = strsep(&s, " ");
strcpy(dfu->name, st);
dfu->alt = alt;
dfu->max_buf_size = 0;
dfu->free_entity = NULL;
/* Specific for mmc device */
if (strcmp(interface, "mmc") == 0) {
if (dfu_fill_entity_mmc(dfu, devstr, s))
return -1;
} else if (strcmp(interface, "mtd") == 0) {
if (dfu_fill_entity_mtd(dfu, devstr, s))
return -1;
} else if (strcmp(interface, "nand") == 0) {
if (dfu_fill_entity_nand(dfu, devstr, s))
return -1;
} else if (strcmp(interface, "ram") == 0) {
if (dfu_fill_entity_ram(dfu, devstr, s))
return -1;
} else if (strcmp(interface, "sf") == 0) {
if (dfu_fill_entity_sf(dfu, devstr, s))
return -1;
} else if (strcmp(interface, "virt") == 0) {
if (dfu_fill_entity_virt(dfu, devstr, s))
return -1;
} else {
printf("%s: Device %s not (yet) supported!\n",
__func__, interface);
return -1;
}
dfu_get_buf(dfu);
return 0;
}
void dfu_free_entities(void)
{
struct dfu_entity *dfu, *p, *t = NULL;
dfu_free_buf();
list_for_each_entry_safe_reverse(dfu, p, &dfu_list, list) {
list_del(&dfu->list);
if (dfu->free_entity)
dfu->free_entity(dfu);
t = dfu;
}
if (t)
free(t);
INIT_LIST_HEAD(&dfu_list);
alt_num_cnt = 0;
}
int dfu_alt_init(int num, struct dfu_entity **dfu)
{
char *s;
int ret;
dfu_alt_num = num;
debug("%s: dfu_alt_num=%d\n", __func__, dfu_alt_num);
dfu_hash_algo = NULL;
s = dfu_get_hash_algo();
if (s) {
ret = hash_lookup_algo(s, &dfu_hash_algo);
if (ret)
pr_err("Hash algorithm %s not supported\n", s);
}
*dfu = calloc(sizeof(struct dfu_entity), dfu_alt_num);
if (!*dfu)
return -1;
return 0;
}
int dfu_alt_add(struct dfu_entity *dfu, char *interface, char *devstr, char *s)
{
struct dfu_entity *p_dfu;
int ret;
if (alt_num_cnt >= dfu_alt_num)
return -1;
p_dfu = &dfu[alt_num_cnt];
ret = dfu_fill_entity(p_dfu, s, alt_num_cnt, interface, devstr);
if (ret)
return -1;
list_add_tail(&p_dfu->list, &dfu_list);
alt_num_cnt++;
return 0;
}
int dfu_config_entities(char *env, char *interface, char *devstr)
{
struct dfu_entity *dfu;
int i, ret;
char *s;
ret = dfu_alt_init(dfu_find_alt_num(env), &dfu);
if (ret)
return -1;
for (i = 0; i < dfu_alt_num; i++) {
s = strsep(&env, ";");
ret = dfu_alt_add(dfu, interface, devstr, s);
if (ret) {
/* We will free "dfu" in dfu_free_entities() */
return -1;
}
}
return 0;
}
const char *dfu_get_dev_type(enum dfu_device_type t)
{
const char *const dev_t[] = {NULL, "eMMC", "OneNAND", "NAND", "RAM",
"SF", "MTD", "VIRT"};
return dev_t[t];
}
const char *dfu_get_layout(enum dfu_layout l)
{
const char *const dfu_layout[] = {NULL, "RAW_ADDR", "FAT", "EXT2",
"EXT3", "EXT4", "RAM_ADDR" };
return dfu_layout[l];
}
void dfu_show_entities(void)
{
struct dfu_entity *dfu;
puts("DFU alt settings list:\n");
list_for_each_entry(dfu, &dfu_list, list) {
printf("dev: %s alt: %d name: %s layout: %s\n",
dfu_get_dev_type(dfu->dev_type), dfu->alt,
dfu->name, dfu_get_layout(dfu->layout));
}
}
int dfu_get_alt_number(void)
{
return dfu_alt_num;
}
struct dfu_entity *dfu_get_entity(int alt)
{
struct dfu_entity *dfu;
list_for_each_entry(dfu, &dfu_list, list) {
if (dfu->alt == alt)
return dfu;
}
return NULL;
}
int dfu_get_alt(char *name)
{
struct dfu_entity *dfu;
char *str;
list_for_each_entry(dfu, &dfu_list, list) {
if (dfu->name[0] != '/') {
if (!strncmp(dfu->name, name, strlen(dfu->name)))
return dfu->alt;
} else {
/*
* One must also consider absolute path
* (/boot/bin/uImage) available at dfu->name when
* compared "plain" file name (uImage)
*
* It is the case for e.g. thor gadget where lthor SW
* sends only the file name, so only the very last part
* of path must be checked for equality
*/
str = strstr(dfu->name, name);
if (!str)
continue;
/*
* Check if matching substring is the last element of
* dfu->name (uImage)
*/
if (strlen(dfu->name) ==
((str - dfu->name) + strlen(name)))
return dfu->alt;
}
}
return -ENODEV;
}
int dfu_write_from_mem_addr(struct dfu_entity *dfu, void *buf, int size)
{
unsigned long dfu_buf_size, write, left = size;
int i, ret = 0;
void *dp = buf;
/*
* Here we must call dfu_get_buf(dfu) first to be sure that dfu_buf_size
* has been properly initialized - e.g. if "dfu_bufsiz" has been taken
* into account.
*/
dfu_get_buf(dfu);
dfu_buf_size = dfu_get_buf_size();
debug("%s: dfu buf size: %lu\n", __func__, dfu_buf_size);
for (i = 0; left > 0; i++) {
write = min(dfu_buf_size, left);
debug("%s: dp: 0x%p left: %lu write: %lu\n", __func__,
dp, left, write);
ret = dfu_write(dfu, dp, write, i);
if (ret) {
pr_err("DFU write failed\n");
return ret;
}
dp += write;
left -= write;
}
ret = dfu_flush(dfu, NULL, 0, i);
if (ret)
pr_err("DFU flush failed!");
return ret;
}
@@ -0,0 +1,410 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* dfu.c -- DFU back-end routines
*
* Copyright (C) 2012 Samsung Electronics
* author: Lukasz Majewski <l.majewski@samsung.com>
*/
#include <common.h>
#include <malloc.h>
#include <errno.h>
#include <div64.h>
#include <dfu.h>
#include <ext4fs.h>
#include <fat.h>
#include <mmc.h>
static unsigned char *dfu_file_buf;
static u64 dfu_file_buf_len;
static long dfu_file_buf_filled;
static int mmc_block_op(enum dfu_op op, struct dfu_entity *dfu,
u64 offset, void *buf, long *len)
{
struct mmc *mmc;
u32 blk_start, blk_count, n = 0;
int ret, part_num_bkp = 0;
mmc = find_mmc_device(dfu->data.mmc.dev_num);
if (!mmc) {
pr_err("Device MMC %d - not found!", dfu->data.mmc.dev_num);
return -ENODEV;
}
/*
* We must ensure that we work in lba_blk_size chunks, so ALIGN
* this value.
*/
*len = ALIGN(*len, dfu->data.mmc.lba_blk_size);
blk_start = dfu->data.mmc.lba_start +
(u32)lldiv(offset, dfu->data.mmc.lba_blk_size);
blk_count = *len / dfu->data.mmc.lba_blk_size;
if (blk_start + blk_count >
dfu->data.mmc.lba_start + dfu->data.mmc.lba_size) {
puts("Request would exceed designated area!\n");
return -EINVAL;
}
if (dfu->data.mmc.hw_partition >= 0) {
part_num_bkp = mmc_get_blk_desc(mmc)->hwpart;
ret = blk_select_hwpart_devnum(IF_TYPE_MMC,
dfu->data.mmc.dev_num,
dfu->data.mmc.hw_partition);
if (ret)
return ret;
}
debug("%s: %s dev: %d start: %d cnt: %d buf: 0x%p\n", __func__,
op == DFU_OP_READ ? "MMC READ" : "MMC WRITE",
dfu->data.mmc.dev_num, blk_start, blk_count, buf);
switch (op) {
case DFU_OP_READ:
n = blk_dread(mmc_get_blk_desc(mmc), blk_start, blk_count, buf);
break;
case DFU_OP_WRITE:
n = blk_dwrite(mmc_get_blk_desc(mmc), blk_start, blk_count,
buf);
break;
default:
pr_err("Operation not supported\n");
}
if (n != blk_count) {
pr_err("MMC operation failed");
if (dfu->data.mmc.hw_partition >= 0)
blk_select_hwpart_devnum(IF_TYPE_MMC,
dfu->data.mmc.dev_num,
part_num_bkp);
return -EIO;
}
if (dfu->data.mmc.hw_partition >= 0) {
ret = blk_select_hwpart_devnum(IF_TYPE_MMC,
dfu->data.mmc.dev_num,
part_num_bkp);
if (ret)
return ret;
}
return 0;
}
static int mmc_file_buffer(struct dfu_entity *dfu, void *buf, long *len)
{
if (dfu_file_buf_len + *len > CONFIG_SYS_DFU_MAX_FILE_SIZE) {
dfu_file_buf_len = 0;
return -EINVAL;
}
/* Add to the current buffer. */
memcpy(dfu_file_buf + dfu_file_buf_len, buf, *len);
dfu_file_buf_len += *len;
return 0;
}
static int mmc_file_op(enum dfu_op op, struct dfu_entity *dfu,
void *buf, u64 *len)
{
char dev_part_str[8];
int ret;
int fstype;
loff_t size = 0;
switch (dfu->layout) {
case DFU_FS_FAT:
fstype = FS_TYPE_FAT;
break;
case DFU_FS_EXT4:
fstype = FS_TYPE_EXT;
break;
default:
printf("%s: Layout (%s) not (yet) supported!\n", __func__,
dfu_get_layout(dfu->layout));
return -1;
}
snprintf(dev_part_str, sizeof(dev_part_str), "%d:%d",
dfu->data.mmc.dev, dfu->data.mmc.part);
ret = fs_set_blk_dev("mmc", dev_part_str, fstype);
if (ret) {
puts("dfu: fs_set_blk_dev error!\n");
return ret;
}
switch (op) {
case DFU_OP_READ:
ret = fs_read(dfu->name, (size_t)buf, 0, 0, &size);
if (ret) {
puts("dfu: fs_read error!\n");
return ret;
}
*len = size;
break;
case DFU_OP_WRITE:
ret = fs_write(dfu->name, (size_t)buf, 0, *len, &size);
if (ret) {
puts("dfu: fs_write error!\n");
return ret;
}
break;
case DFU_OP_SIZE:
ret = fs_size(dfu->name, &size);
if (ret) {
puts("dfu: fs_size error!\n");
return ret;
}
*len = size;
break;
default:
return -1;
}
return ret;
}
int dfu_write_medium_mmc(struct dfu_entity *dfu,
u64 offset, void *buf, long *len)
{
int ret = -1;
switch (dfu->layout) {
case DFU_RAW_ADDR:
ret = mmc_block_op(DFU_OP_WRITE, dfu, offset, buf, len);
break;
case DFU_FS_FAT:
case DFU_FS_EXT4:
ret = mmc_file_buffer(dfu, buf, len);
break;
default:
printf("%s: Layout (%s) not (yet) supported!\n", __func__,
dfu_get_layout(dfu->layout));
}
return ret;
}
int dfu_flush_medium_mmc(struct dfu_entity *dfu)
{
int ret = 0;
if (dfu->layout != DFU_RAW_ADDR) {
/* Do stuff here. */
ret = mmc_file_op(DFU_OP_WRITE, dfu, dfu_file_buf,
&dfu_file_buf_len);
/* Now that we're done */
dfu_file_buf_len = 0;
}
return ret;
}
int dfu_get_medium_size_mmc(struct dfu_entity *dfu, u64 *size)
{
int ret;
switch (dfu->layout) {
case DFU_RAW_ADDR:
*size = dfu->data.mmc.lba_size * dfu->data.mmc.lba_blk_size;
return 0;
case DFU_FS_FAT:
case DFU_FS_EXT4:
dfu_file_buf_filled = -1;
ret = mmc_file_op(DFU_OP_SIZE, dfu, NULL, size);
if (ret < 0)
return ret;
if (*size > CONFIG_SYS_DFU_MAX_FILE_SIZE)
return -1;
return 0;
default:
printf("%s: Layout (%s) not (yet) supported!\n", __func__,
dfu_get_layout(dfu->layout));
return -1;
}
}
static int mmc_file_unbuffer(struct dfu_entity *dfu, u64 offset, void *buf,
long *len)
{
int ret;
u64 file_len;
if (dfu_file_buf_filled == -1) {
ret = mmc_file_op(DFU_OP_READ, dfu, dfu_file_buf, &file_len);
if (ret < 0)
return ret;
dfu_file_buf_filled = file_len;
}
if (offset + *len > dfu_file_buf_filled)
return -EINVAL;
/* Add to the current buffer. */
memcpy(buf, dfu_file_buf + offset, *len);
return 0;
}
int dfu_read_medium_mmc(struct dfu_entity *dfu, u64 offset, void *buf,
long *len)
{
int ret = -1;
switch (dfu->layout) {
case DFU_RAW_ADDR:
ret = mmc_block_op(DFU_OP_READ, dfu, offset, buf, len);
break;
case DFU_FS_FAT:
case DFU_FS_EXT4:
ret = mmc_file_unbuffer(dfu, offset, buf, len);
break;
default:
printf("%s: Layout (%s) not (yet) supported!\n", __func__,
dfu_get_layout(dfu->layout));
}
return ret;
}
void dfu_free_entity_mmc(struct dfu_entity *dfu)
{
if (dfu_file_buf) {
free(dfu_file_buf);
dfu_file_buf = NULL;
}
}
/*
* @param s Parameter string containing space-separated arguments:
* 1st:
* raw (raw read/write)
* fat (files)
* ext4 (^)
* part (partition image)
* 2nd and 3rd:
* lba_start and lba_size, for raw write
* mmc_dev and mmc_part, for filesystems and part
* 4th (optional):
* mmcpart <num> (access to HW eMMC partitions)
*/
int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *devstr, char *s)
{
const char *entity_type;
size_t second_arg;
size_t third_arg;
struct mmc *mmc;
const char *argv[3];
const char **parg = argv;
dfu->data.mmc.dev_num = simple_strtoul(devstr, NULL, 10);
for (; parg < argv + sizeof(argv) / sizeof(*argv); ++parg) {
*parg = strsep(&s, " ");
if (*parg == NULL) {
pr_err("Invalid number of arguments.\n");
return -ENODEV;
}
}
entity_type = argv[0];
/*
* Base 0 means we'll accept (prefixed with 0x or 0) base 16, 8,
* with default 10.
*/
second_arg = simple_strtoul(argv[1], NULL, 0);
third_arg = simple_strtoul(argv[2], NULL, 0);
mmc = find_mmc_device(dfu->data.mmc.dev_num);
if (mmc == NULL) {
pr_err("Couldn't find MMC device no. %d.\n",
dfu->data.mmc.dev_num);
return -ENODEV;
}
if (mmc_init(mmc)) {
pr_err("Couldn't init MMC device.\n");
return -ENODEV;
}
dfu->data.mmc.hw_partition = -EINVAL;
if (!strcmp(entity_type, "raw")) {
dfu->layout = DFU_RAW_ADDR;
dfu->data.mmc.lba_start = second_arg;
dfu->data.mmc.lba_size = third_arg;
dfu->data.mmc.lba_blk_size = mmc->read_bl_len;
/*
* Check for an extra entry at dfu_alt_info env variable
* specifying the mmc HW defined partition number
*/
if (s)
if (!strcmp(strsep(&s, " "), "mmcpart"))
dfu->data.mmc.hw_partition =
simple_strtoul(s, NULL, 0);
} else if (!strcmp(entity_type, "part")) {
disk_partition_t partinfo;
struct blk_desc *blk_dev = mmc_get_blk_desc(mmc);
int mmcdev = second_arg;
int mmcpart = third_arg;
int offset = 0;
if (part_get_info(blk_dev, mmcpart, &partinfo) != 0) {
pr_err("Couldn't find part #%d on mmc device #%d\n",
mmcpart, mmcdev);
return -ENODEV;
}
/*
* Check for an extra entry at dfu_alt_info env variable
* specifying the mmc HW defined partition number
*/
if (s)
if (!strcmp(strsep(&s, " "), "offset"))
offset = simple_strtoul(s, NULL, 0);
dfu->layout = DFU_RAW_ADDR;
dfu->data.mmc.lba_start = partinfo.start + offset;
dfu->data.mmc.lba_size = partinfo.size-offset;
dfu->data.mmc.lba_blk_size = partinfo.blksz;
} else if (!strcmp(entity_type, "fat")) {
dfu->layout = DFU_FS_FAT;
} else if (!strcmp(entity_type, "ext4")) {
dfu->layout = DFU_FS_EXT4;
} else {
pr_err("Memory layout (%s) not supported!\n", entity_type);
return -ENODEV;
}
/* if it's NOT a raw write */
if (strcmp(entity_type, "raw")) {
dfu->data.mmc.dev = second_arg;
dfu->data.mmc.part = third_arg;
}
dfu->dev_type = DFU_DEV_MMC;
dfu->get_medium_size = dfu_get_medium_size_mmc;
dfu->read_medium = dfu_read_medium_mmc;
dfu->write_medium = dfu_write_medium_mmc;
dfu->flush_medium = dfu_flush_medium_mmc;
dfu->inited = 0;
dfu->free_entity = dfu_free_entity_mmc;
/* Check if file buffer is ready */
if (!dfu_file_buf) {
dfu_file_buf = memalign(CONFIG_SYS_CACHELINE_SIZE,
CONFIG_SYS_DFU_MAX_FILE_SIZE);
if (!dfu_file_buf) {
pr_err("Could not memalign 0x%x bytes",
CONFIG_SYS_DFU_MAX_FILE_SIZE);
return -ENOMEM;
}
}
return 0;
}
@@ -0,0 +1,311 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* dfu_mtd.c -- DFU for MTD device.
*
* Copyright (C) 2019,STMicroelectronics - All Rights Reserved
*
* Based on dfu_nand.c
*/
#include <common.h>
#include <dfu.h>
#include <mtd.h>
#include <jffs2/load_kernel.h>
static bool mtd_is_aligned_with_block_size(struct mtd_info *mtd, u64 size)
{
return !do_div(size, mtd->erasesize);
}
static int mtd_block_op(enum dfu_op op, struct dfu_entity *dfu,
u64 offset, void *buf, long *len)
{
u64 off, lim, remaining;
struct mtd_info *mtd = dfu->data.mtd.info;
struct mtd_oob_ops io_op = {};
int ret = 0;
bool has_pages = mtd->type == MTD_NANDFLASH ||
mtd->type == MTD_MLCNANDFLASH;
/* if buf == NULL return total size of the area */
if (!buf) {
*len = dfu->data.mtd.size;
return 0;
}
off = dfu->data.mtd.start + offset + dfu->bad_skip;
lim = dfu->data.mtd.start + dfu->data.mtd.size;
if (off >= lim) {
printf("Limit reached 0x%llx\n", lim);
*len = 0;
return op == DFU_OP_READ ? 0 : -EIO;
}
/* limit request with the available size */
if (off + *len >= lim)
*len = lim - off;
if (!mtd_is_aligned_with_block_size(mtd, off)) {
printf("Offset not aligned with a block (0x%x)\n",
mtd->erasesize);
return 0;
}
/* first erase */
if (op == DFU_OP_WRITE) {
struct erase_info erase_op = {};
remaining = round_up(*len, mtd->erasesize);
erase_op.mtd = mtd;
erase_op.addr = off;
erase_op.len = mtd->erasesize;
erase_op.scrub = 0;
while (remaining) {
if (erase_op.addr + remaining > lim) {
printf("Limit reached 0x%llx while erasing at offset 0x%llx\n",
lim, off);
return -EIO;
}
ret = mtd_erase(mtd, &erase_op);
if (ret) {
/* Abort if its not a bad block error */
if (ret != -EIO) {
printf("Failure while erasing at offset 0x%llx\n",
erase_op.fail_addr);
return 0;
}
printf("Skipping bad block at 0x%08llx\n",
erase_op.addr);
} else {
remaining -= mtd->erasesize;
}
/* Continue erase behind bad block */
erase_op.addr += mtd->erasesize;
}
}
io_op.mode = MTD_OPS_AUTO_OOB;
io_op.len = *len;
if (has_pages && io_op.len > mtd->writesize)
io_op.len = mtd->writesize;
io_op.ooblen = 0;
io_op.datbuf = buf;
io_op.oobbuf = NULL;
/* Loop over to do the actual read/write */
remaining = *len;
while (remaining) {
if (off + remaining > lim) {
printf("Limit reached 0x%llx while %s at offset 0x%llx\n",
lim, op == DFU_OP_READ ? "reading" : "writing",
off);
if (op == DFU_OP_READ) {
*len -= remaining;
return 0;
} else {
return -EIO;
}
}
/* Skip the block if it is bad */
if (mtd_is_aligned_with_block_size(mtd, off) &&
mtd_block_isbad(mtd, off)) {
off += mtd->erasesize;
dfu->bad_skip += mtd->erasesize;
continue;
}
if (op == DFU_OP_READ)
ret = mtd_read_oob(mtd, off, &io_op);
else
ret = mtd_write_oob(mtd, off, &io_op);
if (ret) {
printf("Failure while %s at offset 0x%llx\n",
op == DFU_OP_READ ? "reading" : "writing", off);
return -EIO;
}
off += io_op.retlen;
remaining -= io_op.retlen;
io_op.datbuf += io_op.retlen;
io_op.len = remaining;
if (has_pages && io_op.len > mtd->writesize)
io_op.len = mtd->writesize;
}
return ret;
}
static int dfu_get_medium_size_mtd(struct dfu_entity *dfu, u64 *size)
{
*size = dfu->data.mtd.info->size;
return 0;
}
static int dfu_read_medium_mtd(struct dfu_entity *dfu, u64 offset, void *buf,
long *len)
{
int ret = -1;
switch (dfu->layout) {
case DFU_RAW_ADDR:
ret = mtd_block_op(DFU_OP_READ, dfu, offset, buf, len);
break;
default:
printf("%s: Layout (%s) not (yet) supported!\n", __func__,
dfu_get_layout(dfu->layout));
}
return ret;
}
static int dfu_write_medium_mtd(struct dfu_entity *dfu,
u64 offset, void *buf, long *len)
{
int ret = -1;
switch (dfu->layout) {
case DFU_RAW_ADDR:
ret = mtd_block_op(DFU_OP_WRITE, dfu, offset, buf, len);
break;
default:
printf("%s: Layout (%s) not (yet) supported!\n", __func__,
dfu_get_layout(dfu->layout));
}
return ret;
}
static int dfu_flush_medium_mtd(struct dfu_entity *dfu)
{
struct mtd_info *mtd = dfu->data.mtd.info;
u64 remaining;
int ret;
/* in case of ubi partition, erase rest of the partition */
if (dfu->data.nand.ubi) {
struct erase_info erase_op = {};
erase_op.mtd = dfu->data.mtd.info;
erase_op.addr = round_up(dfu->data.mtd.start + dfu->offset +
dfu->bad_skip, mtd->erasesize);
erase_op.len = mtd->erasesize;
erase_op.scrub = 0;
remaining = dfu->data.mtd.start + dfu->data.mtd.size -
erase_op.addr;
while (remaining) {
ret = mtd_erase(mtd, &erase_op);
if (ret) {
/* Abort if its not a bad block error */
if (ret != -EIO)
break;
printf("Skipping bad block at 0x%08llx\n",
erase_op.addr);
}
/* Skip bad block and continue behind it */
erase_op.addr += mtd->erasesize;
remaining -= mtd->erasesize;
}
}
return 0;
}
static unsigned int dfu_polltimeout_mtd(struct dfu_entity *dfu)
{
/*
* Currently, Poll Timeout != 0 is only needed on nand
* ubi partition, as sectors which are not used need
* to be erased
*/
if (dfu->data.nand.ubi)
return DFU_MANIFEST_POLL_TIMEOUT;
return DFU_DEFAULT_POLL_TIMEOUT;
}
int dfu_fill_entity_mtd(struct dfu_entity *dfu, char *devstr, char *s)
{
char *st;
struct mtd_info *mtd;
bool has_pages;
int ret, part;
mtd = get_mtd_device_nm(devstr);
if (IS_ERR_OR_NULL(mtd))
return -ENODEV;
put_mtd_device(mtd);
dfu->dev_type = DFU_DEV_MTD;
dfu->data.mtd.info = mtd;
has_pages = mtd->type == MTD_NANDFLASH || mtd->type == MTD_MLCNANDFLASH;
dfu->max_buf_size = has_pages ? mtd->erasesize : 0;
st = strsep(&s, " ");
if (!strcmp(st, "raw")) {
dfu->layout = DFU_RAW_ADDR;
dfu->data.mtd.start = simple_strtoul(s, &s, 16);
s++;
dfu->data.mtd.size = simple_strtoul(s, &s, 16);
} else if ((!strcmp(st, "part")) || (!strcmp(st, "partubi"))) {
char mtd_id[32];
struct mtd_device *mtd_dev;
u8 part_num;
struct part_info *pi;
dfu->layout = DFU_RAW_ADDR;
part = simple_strtoul(s, &s, 10);
sprintf(mtd_id, "%s,%d", devstr, part - 1);
printf("using id '%s'\n", mtd_id);
mtdparts_init();
ret = find_dev_and_part(mtd_id, &mtd_dev, &part_num, &pi);
if (ret != 0) {
printf("Could not locate '%s'\n", mtd_id);
return -1;
}
dfu->data.mtd.start = pi->offset;
dfu->data.mtd.size = pi->size;
if (!strcmp(st, "partubi"))
dfu->data.mtd.ubi = 1;
} else {
printf("%s: Memory layout (%s) not supported!\n", __func__, st);
return -1;
}
if (!mtd_is_aligned_with_block_size(mtd, dfu->data.mtd.start)) {
printf("Offset not aligned with a block (0x%x)\n",
mtd->erasesize);
return -EINVAL;
}
if (!mtd_is_aligned_with_block_size(mtd, dfu->data.mtd.size)) {
printf("Size not aligned with a block (0x%x)\n",
mtd->erasesize);
return -EINVAL;
}
dfu->get_medium_size = dfu_get_medium_size_mtd;
dfu->read_medium = dfu_read_medium_mtd;
dfu->write_medium = dfu_write_medium_mtd;
dfu->flush_medium = dfu_flush_medium_mtd;
dfu->poll_timeout = dfu_polltimeout_mtd;
/* initial state */
dfu->inited = 0;
return 0;
}
@@ -0,0 +1,246 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* dfu_nand.c -- DFU for NAND routines.
*
* Copyright (C) 2012-2013 Texas Instruments, Inc.
*
* Based on dfu_mmc.c which is:
* Copyright (C) 2012 Samsung Electronics
* author: Lukasz Majewski <l.majewski@samsung.com>
*/
#include <common.h>
#include <malloc.h>
#include <errno.h>
#include <div64.h>
#include <dfu.h>
#include <linux/mtd/mtd.h>
#include <jffs2/load_kernel.h>
#include <nand.h>
static int nand_block_op(enum dfu_op op, struct dfu_entity *dfu,
u64 offset, void *buf, long *len)
{
loff_t start, lim;
size_t count, actual;
int ret;
struct mtd_info *mtd;
/* if buf == NULL return total size of the area */
if (buf == NULL) {
*len = dfu->data.nand.size;
return 0;
}
start = dfu->data.nand.start + offset + dfu->bad_skip;
lim = dfu->data.nand.start + dfu->data.nand.size - start;
count = *len;
mtd = get_nand_dev_by_index(nand_curr_device);
if (nand_curr_device < 0 ||
nand_curr_device >= CONFIG_SYS_MAX_NAND_DEVICE ||
!mtd) {
printf("%s: invalid nand device\n", __func__);
return -1;
}
if (op == DFU_OP_READ) {
ret = nand_read_skip_bad(mtd, start, &count, &actual,
lim, buf);
} else {
nand_erase_options_t opts;
memset(&opts, 0, sizeof(opts));
opts.offset = start;
opts.length = count;
opts.spread = 1;
opts.quiet = 1;
opts.lim = lim;
/* first erase */
ret = nand_erase_opts(mtd, &opts);
if (ret)
return ret;
/* then write */
ret = nand_write_skip_bad(mtd, start, &count, &actual,
lim, buf, WITH_WR_VERIFY);
}
if (ret != 0) {
printf("%s: nand_%s_skip_bad call failed at %llx!\n",
__func__, op == DFU_OP_READ ? "read" : "write",
start);
return ret;
}
/*
* Find out where we stopped writing data. This can be deeper into
* the NAND than we expected due to having to skip bad blocks. So
* we must take this into account for the next write, if any.
*/
if (actual > count)
dfu->bad_skip += actual - count;
return ret;
}
static inline int nand_block_write(struct dfu_entity *dfu,
u64 offset, void *buf, long *len)
{
return nand_block_op(DFU_OP_WRITE, dfu, offset, buf, len);
}
static inline int nand_block_read(struct dfu_entity *dfu,
u64 offset, void *buf, long *len)
{
return nand_block_op(DFU_OP_READ, dfu, offset, buf, len);
}
static int dfu_write_medium_nand(struct dfu_entity *dfu,
u64 offset, void *buf, long *len)
{
int ret = -1;
switch (dfu->layout) {
case DFU_RAW_ADDR:
ret = nand_block_write(dfu, offset, buf, len);
break;
default:
printf("%s: Layout (%s) not (yet) supported!\n", __func__,
dfu_get_layout(dfu->layout));
}
return ret;
}
int dfu_get_medium_size_nand(struct dfu_entity *dfu, u64 *size)
{
*size = dfu->data.nand.size;
return 0;
}
static int dfu_read_medium_nand(struct dfu_entity *dfu, u64 offset, void *buf,
long *len)
{
int ret = -1;
switch (dfu->layout) {
case DFU_RAW_ADDR:
ret = nand_block_read(dfu, offset, buf, len);
break;
default:
printf("%s: Layout (%s) not (yet) supported!\n", __func__,
dfu_get_layout(dfu->layout));
}
return ret;
}
static int dfu_flush_medium_nand(struct dfu_entity *dfu)
{
int ret = 0;
u64 off;
/* in case of ubi partition, erase rest of the partition */
if (dfu->data.nand.ubi) {
struct mtd_info *mtd = get_nand_dev_by_index(nand_curr_device);
nand_erase_options_t opts;
if (nand_curr_device < 0 ||
nand_curr_device >= CONFIG_SYS_MAX_NAND_DEVICE ||
!mtd) {
printf("%s: invalid nand device\n", __func__);
return -1;
}
memset(&opts, 0, sizeof(opts));
off = dfu->offset;
if ((off & (mtd->erasesize - 1)) != 0) {
/*
* last write ended with unaligned length
* sector is erased, jump to next
*/
off = off & ~((mtd->erasesize - 1));
off += mtd->erasesize;
}
opts.offset = dfu->data.nand.start + off +
dfu->bad_skip;
opts.length = dfu->data.nand.start +
dfu->data.nand.size - opts.offset;
ret = nand_erase_opts(mtd, &opts);
if (ret != 0)
printf("Failure erase: %d\n", ret);
}
return ret;
}
unsigned int dfu_polltimeout_nand(struct dfu_entity *dfu)
{
/*
* Currently, Poll Timeout != 0 is only needed on nand
* ubi partition, as the not used sectors need an erase
*/
if (dfu->data.nand.ubi)
return DFU_MANIFEST_POLL_TIMEOUT;
return DFU_DEFAULT_POLL_TIMEOUT;
}
int dfu_fill_entity_nand(struct dfu_entity *dfu, char *devstr, char *s)
{
char *st;
int ret, dev, part;
dfu->data.nand.ubi = 0;
dfu->dev_type = DFU_DEV_NAND;
st = strsep(&s, " ");
if (!strcmp(st, "raw")) {
dfu->layout = DFU_RAW_ADDR;
dfu->data.nand.start = simple_strtoul(s, &s, 16);
s++;
dfu->data.nand.size = simple_strtoul(s, &s, 16);
} else if ((!strcmp(st, "part")) || (!strcmp(st, "partubi"))) {
char mtd_id[32];
struct mtd_device *mtd_dev;
u8 part_num;
struct part_info *pi;
dfu->layout = DFU_RAW_ADDR;
dev = simple_strtoul(s, &s, 10);
s++;
part = simple_strtoul(s, &s, 10);
sprintf(mtd_id, "%s%d,%d", "nand", dev, part - 1);
debug("using id '%s'\n", mtd_id);
mtdparts_init();
ret = find_dev_and_part(mtd_id, &mtd_dev, &part_num, &pi);
if (ret != 0) {
printf("Could not locate '%s'\n", mtd_id);
return -1;
}
dfu->data.nand.start = pi->offset;
dfu->data.nand.size = pi->size;
if (!strcmp(st, "partubi"))
dfu->data.nand.ubi = 1;
} else {
printf("%s: Memory layout (%s) not supported!\n", __func__, st);
return -1;
}
dfu->get_medium_size = dfu_get_medium_size_nand;
dfu->read_medium = dfu_read_medium_nand;
dfu->write_medium = dfu_write_medium_nand;
dfu->flush_medium = dfu_flush_medium_nand;
dfu->poll_timeout = dfu_polltimeout_nand;
/* initial state */
dfu->inited = 0;
return 0;
}
@@ -0,0 +1,86 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2013
* Afzal Mohammed <afzal.mohd.ma@gmail.com>
*
* Reference: dfu_mmc.c
* Copyright (C) 2012 Samsung Electronics
* author: Lukasz Majewski <l.majewski@samsung.com>
*/
#include <common.h>
#include <malloc.h>
#include <errno.h>
#include <dfu.h>
static int dfu_transfer_medium_ram(enum dfu_op op, struct dfu_entity *dfu,
u64 offset, void *buf, long *len)
{
if (dfu->layout != DFU_RAM_ADDR) {
pr_err("unsupported layout: %s\n", dfu_get_layout(dfu->layout));
return -EINVAL;
}
if (offset > dfu->data.ram.size) {
pr_err("request exceeds allowed area\n");
return -EINVAL;
}
if (op == DFU_OP_WRITE)
memcpy(dfu->data.ram.start + offset, buf, *len);
else
memcpy(buf, dfu->data.ram.start + offset, *len);
return 0;
}
static int dfu_write_medium_ram(struct dfu_entity *dfu, u64 offset,
void *buf, long *len)
{
return dfu_transfer_medium_ram(DFU_OP_WRITE, dfu, offset, buf, len);
}
int dfu_get_medium_size_ram(struct dfu_entity *dfu, u64 *size)
{
*size = dfu->data.ram.size;
return 0;
}
static int dfu_read_medium_ram(struct dfu_entity *dfu, u64 offset,
void *buf, long *len)
{
return dfu_transfer_medium_ram(DFU_OP_READ, dfu, offset, buf, len);
}
int dfu_fill_entity_ram(struct dfu_entity *dfu, char *devstr, char *s)
{
const char *argv[3];
const char **parg = argv;
for (; parg < argv + sizeof(argv) / sizeof(*argv); ++parg) {
*parg = strsep(&s, " ");
if (*parg == NULL) {
pr_err("Invalid number of arguments.\n");
return -ENODEV;
}
}
dfu->dev_type = DFU_DEV_RAM;
if (strcmp(argv[0], "ram")) {
pr_err("unsupported device: %s\n", argv[0]);
return -ENODEV;
}
dfu->layout = DFU_RAM_ADDR;
dfu->data.ram.start = (void *)simple_strtoul(argv[1], NULL, 16);
dfu->data.ram.size = simple_strtoul(argv[2], NULL, 16);
dfu->write_medium = dfu_write_medium_ram;
dfu->get_medium_size = dfu_get_medium_size_ram;
dfu->read_medium = dfu_read_medium_ram;
dfu->inited = 0;
return 0;
}
@@ -0,0 +1,205 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved.
*/
#include <common.h>
#include <malloc.h>
#include <errno.h>
#include <div64.h>
#include <dfu.h>
#include <spi.h>
#include <spi_flash.h>
#include <jffs2/load_kernel.h>
#include <linux/mtd/mtd.h>
static int dfu_get_medium_size_sf(struct dfu_entity *dfu, u64 *size)
{
*size = dfu->data.sf.size;
return 0;
}
static int dfu_read_medium_sf(struct dfu_entity *dfu, u64 offset, void *buf,
long *len)
{
return spi_flash_read(dfu->data.sf.dev, dfu->data.sf.start + offset,
*len, buf);
}
static u64 find_sector(struct dfu_entity *dfu, u64 start, u64 offset)
{
return (lldiv((start + offset), dfu->data.sf.dev->sector_size)) *
dfu->data.sf.dev->sector_size;
}
static int dfu_write_medium_sf(struct dfu_entity *dfu,
u64 offset, void *buf, long *len)
{
int ret;
ret = spi_flash_erase(dfu->data.sf.dev,
find_sector(dfu, dfu->data.sf.start, offset),
dfu->data.sf.dev->sector_size);
if (ret)
return ret;
ret = spi_flash_write(dfu->data.sf.dev, dfu->data.sf.start + offset,
*len, buf);
if (ret)
return ret;
return 0;
}
static int dfu_flush_medium_sf(struct dfu_entity *dfu)
{
u64 off, length;
if (!CONFIG_IS_ENABLED(DFU_SF_PART) || !dfu->data.sf.ubi)
return 0;
/* in case of ubi partition, erase rest of the partition */
off = find_sector(dfu, dfu->data.sf.start, dfu->offset);
/* last write ended with unaligned length jump to next */
if (off != dfu->data.sf.start + dfu->offset)
off += dfu->data.sf.dev->sector_size;
length = dfu->data.sf.start + dfu->data.sf.size - off;
if (length)
return spi_flash_erase(dfu->data.sf.dev, off, length);
return 0;
}
static unsigned int dfu_polltimeout_sf(struct dfu_entity *dfu)
{
/*
* Currently, Poll Timeout != 0 is only needed on nand
* ubi partition, as sectors which are not used need
* to be erased
*/
if (CONFIG_IS_ENABLED(DFU_SF_PART) && dfu->data.sf.ubi)
return DFU_MANIFEST_POLL_TIMEOUT;
return DFU_DEFAULT_POLL_TIMEOUT;
}
static void dfu_free_entity_sf(struct dfu_entity *dfu)
{
spi_flash_free(dfu->data.sf.dev);
}
static struct spi_flash *parse_dev(char *devstr)
{
unsigned int bus;
unsigned int cs;
unsigned int speed = CONFIG_SF_DEFAULT_SPEED;
unsigned int mode = CONFIG_SF_DEFAULT_MODE;
char *s, *endp;
struct spi_flash *dev;
s = strsep(&devstr, ":");
if (!s || !*s || (bus = simple_strtoul(s, &endp, 0), *endp)) {
printf("Invalid SPI bus %s\n", s);
return NULL;
}
s = strsep(&devstr, ":");
if (!s || !*s || (cs = simple_strtoul(s, &endp, 0), *endp)) {
printf("Invalid SPI chip-select %s\n", s);
return NULL;
}
s = strsep(&devstr, ":");
if (s && *s) {
speed = simple_strtoul(s, &endp, 0);
if (*endp || !speed) {
printf("Invalid SPI speed %s\n", s);
return NULL;
}
}
s = strsep(&devstr, ":");
if (s && *s) {
mode = simple_strtoul(s, &endp, 0);
if (*endp || mode > 3) {
printf("Invalid SPI mode %s\n", s);
return NULL;
}
}
dev = spi_flash_probe(bus, cs, speed, mode);
if (!dev) {
printf("Failed to create SPI flash at %d:%d:%d:%d\n",
bus, cs, speed, mode);
return NULL;
}
return dev;
}
int dfu_fill_entity_sf(struct dfu_entity *dfu, char *devstr, char *s)
{
char *st;
char *devstr_bkup = strdup(devstr);
dfu->data.sf.dev = parse_dev(devstr_bkup);
free(devstr_bkup);
if (!dfu->data.sf.dev)
return -ENODEV;
dfu->dev_type = DFU_DEV_SF;
dfu->max_buf_size = dfu->data.sf.dev->sector_size;
st = strsep(&s, " ");
if (!strcmp(st, "raw")) {
dfu->layout = DFU_RAW_ADDR;
dfu->data.sf.start = simple_strtoul(s, &s, 16);
s++;
dfu->data.sf.size = simple_strtoul(s, &s, 16);
} else if (CONFIG_IS_ENABLED(DFU_SF_PART) &&
(!strcmp(st, "part") || !strcmp(st, "partubi"))) {
char mtd_id[32];
struct mtd_device *mtd_dev;
u8 part_num;
struct part_info *pi;
int ret, dev, part;
dfu->layout = DFU_RAW_ADDR;
dev = simple_strtoul(s, &s, 10);
s++;
part = simple_strtoul(s, &s, 10);
sprintf(mtd_id, "%s%d,%d", "nor", dev, part - 1);
printf("using id '%s'\n", mtd_id);
mtdparts_init();
ret = find_dev_and_part(mtd_id, &mtd_dev, &part_num, &pi);
if (ret != 0) {
printf("Could not locate '%s'\n", mtd_id);
return -1;
}
dfu->data.sf.start = pi->offset;
dfu->data.sf.size = pi->size;
if (!strcmp(st, "partubi"))
dfu->data.sf.ubi = 1;
} else {
printf("%s: Memory layout (%s) not supported!\n", __func__, st);
spi_flash_free(dfu->data.sf.dev);
return -1;
}
dfu->get_medium_size = dfu_get_medium_size_sf;
dfu->read_medium = dfu_read_medium_sf;
dfu->write_medium = dfu_write_medium_sf;
dfu->flush_medium = dfu_flush_medium_sf;
dfu->poll_timeout = dfu_polltimeout_sf;
dfu->free_entity = dfu_free_entity_sf;
/* initial state */
dfu->inited = 0;
return 0;
}
@@ -0,0 +1,64 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2015
* Lukasz Majewski <l.majewski@majess.pl>
*/
#include <common.h>
#include <malloc.h>
#include <errno.h>
#include <dfu.h>
int dfu_tftp_write(char *dfu_entity_name, unsigned int addr, unsigned int len,
char *interface, char *devstring)
{
char *s, *sb;
int alt_setting_num, ret;
struct dfu_entity *dfu;
debug("%s: name: %s addr: 0x%x len: %d device: %s:%s\n", __func__,
dfu_entity_name, addr, len, interface, devstring);
ret = dfu_init_env_entities(interface, devstring);
if (ret)
goto done;
/*
* We need to copy name pointed by *dfu_entity_name since this text
* is the integral part of the FDT image.
* Any implicit modification (i.e. done by strsep()) will corrupt
* the FDT image and prevent other images to be stored.
*/
s = strdup(dfu_entity_name);
sb = s;
if (!s) {
ret = -ENOMEM;
goto done;
}
strsep(&s, "@");
debug("%s: image name: %s strlen: %zd\n", __func__, sb, strlen(sb));
alt_setting_num = dfu_get_alt(sb);
free(sb);
if (alt_setting_num < 0) {
pr_err("Alt setting [%d] to write not found!",
alt_setting_num);
ret = -ENODEV;
goto done;
}
dfu = dfu_get_entity(alt_setting_num);
if (!dfu) {
pr_err("DFU entity for alt: %d not found!", alt_setting_num);
ret = -ENODEV;
goto done;
}
ret = dfu_write_from_mem_addr(dfu, (void *)(uintptr_t)addr, len);
done:
dfu_free_entities();
return ret;
}
@@ -0,0 +1,49 @@
// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
/*
* Copyright (C) 2019, STMicroelectronics - All Rights Reserved
*/
#include <common.h>
#include <dfu.h>
#include <errno.h>
#include <malloc.h>
int __weak dfu_write_medium_virt(struct dfu_entity *dfu, u64 offset,
void *buf, long *len)
{
debug("%s: off=0x%llx, len=0x%x\n", __func__, offset, (u32)*len);
return 0;
}
int __weak dfu_get_medium_size_virt(struct dfu_entity *dfu, u64 *size)
{
*size = 0;
return 0;
}
int __weak dfu_read_medium_virt(struct dfu_entity *dfu, u64 offset,
void *buf, long *len)
{
debug("%s: off=0x%llx, len=0x%x\n", __func__, offset, (u32)*len);
*len = 0;
return 0;
}
int dfu_fill_entity_virt(struct dfu_entity *dfu, char *devstr, char *s)
{
debug("%s: devstr = %s\n", __func__, devstr);
dfu->dev_type = DFU_DEV_VIRT;
dfu->layout = DFU_RAW_ADDR;
dfu->data.virt.dev_num = simple_strtoul(devstr, NULL, 10);
dfu->write_medium = dfu_write_medium_virt;
dfu->get_medium_size = dfu_get_medium_size_virt;
dfu->read_medium = dfu_read_medium_virt;
dfu->inited = 0;
return 0;
}