一.简介
rockchip vendor storage是一种用于存储SN, MAC, LAN, BT等数据的区域,它具有不会丢失和系统启动各个阶段都可以访问的特性。它使用GPT分区表格式,并在U-boot, kernel和用户空间中提供了相应的驱动文件和接口。
rockchip vendor storage是一种特殊的存储区域,用于存储一些设备相关的信息,比如序列号,MAC地址,蓝牙地址等。这些信息通常是在出厂时写入的,不会随着系统更新而改变。
rockchip vendor storage的特点是:
不会丢失:即使系统出现异常或者被恢复出厂设置,vendor storage中的数据也不会被擦除或者覆盖。
可访问性:vendor storage中的数据可以在系统启动的各个阶段被访问,包括U-boot, kernel和用户空间。这样可以方便地获取或者设置一些设备相关的参数。
安全性:vendor storage中的数据是加密的,只有通过特定的接口才能读写。这样可以防止数据被篡改或者泄露。
存储数据结构:
也就是说 vendor storage 是从 eMMC 3.5M 之后开始存放,每个元素是 64K,一共 4 块总共 256K。
二.VendorStorage的数据结构
VendorStorage的头部结构包含标签,版本,索引,项目数,空闲偏移和空闲大小,其定义如下:
struct vendor_hdr {
u32 tag;
u32 version;
u16 next_index;
u16 item_num;
u16 free_offset; /* Free space offset */
u16 free_size; /* Free space size */
};
VendorStorage的信息结构包含头部,项目,数据,哈希和版本2。版本2和头部的版本一起用于确保当前Vendor块内容的完整性,其定义如下:
struct vendor_info {
struct vendor_hdr * hdr;
struct vendor_item * item;
u8 * data;
u32 * hash;
u32 * version2;
};
三.VendorStorage u-boot阶段接口
路径:
u-boot\arch\arm\mach-rockchip\vendor.c
u-boot\arch\arm\include\asm\arch\vendor.h
读写接口
vendor_storage_read()/vendor_storage_write()
在Uboot阶段,VendorStorage的读写由vendor_storage_read()和vendor_storage_write()函数实现。
int vendor_storage_read(u16 id, void *pbuf, u16 size)
{
int ret = 0;
u32 i;
u16 offset;
struct vendor_item *item;
/* init vendor storage */
if (!bootdev_type) {
ret = vendor_storage_init();
if (ret < 0)
return ret;
}
item = vendor_info.item;
for (i = 0; i < vendor_info.hdr->item_num; i++) {
if ((item + i)->id == id) {
debug("[Vendor INFO]:Find the matching item, id=%d\n", id);
/* Correct the size value */
if (size > (item + i)->size)
size = (item + i)->size;
offset = (item + i)->offset;
memcpy(pbuf, (vendor_info.data + offset), size);
return size;
}
}
debug("[Vendor ERROR]:No matching item, id=%d\n", id);
return -EINVAL;
}
/*
* @id: item id, first 4 id is occupied:
* VENDOR_SN_ID
* VENDOR_WIFI_MAC_ID
* VENDOR_LAN_MAC_ID
* VENDOR_BLUETOOTH_ID
* @pbuf: write data buffer;
* @size: write bytes;
*
* return: bytes equal to @size is success, other fail;
*/
int vendor_storage_write(u16 id, void *pbuf, u16 size)
{
u32 i, j, next_index, align_size, alloc_size, next_size;
u16 part_size, max_item_num, offset, part_num;
struct vendor_item *item;
int cnt, ret = 0;
/* init vendor storage */
if (!bootdev_type) {
ret = vendor_storage_init();
if (ret < 0)
return ret;
}
switch (bootdev_type) {
case IF_TYPE_MMC:
part_size = EMMC_VENDOR_PART_BLKS;
max_item_num = EMMC_VENDOR_ITEM_NUM;
part_num = VENDOR_PART_NUM;
break;
case IF_TYPE_RKNAND:
case IF_TYPE_SPINAND:
part_size = NAND_VENDOR_PART_BLKS;
max_item_num = NAND_VENDOR_ITEM_NUM;
part_num = NAND_VENDOR_PART_NUM;
break;
case IF_TYPE_SPINOR:
part_size = FLASH_VENDOR_PART_BLKS;
max_item_num = FLASH_VENDOR_ITEM_NUM;
part_num = VENDOR_PART_NUM;
break;
#ifdef CONFIG_MTD_BLK
case IF_TYPE_MTD:
part_size = FLASH_VENDOR_PART_BLKS;
max_item_num = FLASH_VENDOR_ITEM_NUM;
part_num = MTD_VENDOR_PART_NUM;
break;
#endif
default:
ret = -ENODEV;
break;
}
/* Invalid bootdev? */
if (ret < 0)
return ret;
next_index = vendor_info.hdr->next_index;
/* algin to 64 bytes*/
align_size = (size + VENDOR_BTYE_ALIGN) & (~VENDOR_BTYE_ALIGN);
if (size > align_size)
return -EINVAL;
item = vendor_info.item;
/* If item already exist, update the item data */
for (i = 0; i < vendor_info.hdr->item_num; i++) {
if ((item + i)->id == id) {
alloc_size = ((item + i)->size + VENDOR_BTYE_ALIGN) & (~VENDOR_BTYE_ALIGN);
if (size > alloc_size) {
if (vendor_info.hdr->free_size < align_size)
return -EINVAL;
debug("[Vendor INFO]:Find the matching item, id=%d and resize\n", id);
offset = (item + i)->offset;
for (j = i; j < vendor_info.hdr->item_num - 1; j++) {
(item + j)->id = (item + j + 1)->id;
(item + j)->size = (item + j + 1)->size;
(item + j)->offset = offset;
next_size = ((item + j + 1)->size + VENDOR_BTYE_ALIGN) & (~VENDOR_BTYE_ALIGN);
memcpy((vendor_info.data + offset),
(vendor_info.data + (item + j + 1)->offset),
next_size);
offset += next_size;
}
(item + j)->id = id;
(item + j)->offset = offset;
(item + j)->size = size;
memcpy((vendor_info.data + offset), pbuf, size);
vendor_info.hdr->free_offset = offset + align_size;
vendor_info.hdr->free_size -= align_size - alloc_size;
} else {
debug("[Vendor INFO]:Find the matching item, id=%d\n", id);
offset = (item + i)->offset;
memcpy((vendor_info.data + offset), pbuf, size);
(item + i)->size = size;
}
vendor_info.hdr->version++;
*(vendor_info.version2) = vendor_info.hdr->version;
vendor_info.hdr->next_index++;
if (vendor_info.hdr->next_index >= part_num)
vendor_info.hdr->next_index = 0;
cnt = vendor_ops((u8 *)vendor_info.hdr, part_size * next_index, part_size, 1);
return (cnt == part_size) ? size : -EIO;
}
}
/*
* If item does not exist, and free size is enough,
* creat a new one
*/
if ((vendor_info.hdr->item_num < max_item_num) &&
(vendor_info.hdr->free_size >= align_size)) {
debug("[Vendor INFO]:Create new Item, id=%d\n", id);
item = vendor_info.item + vendor_info.hdr->item_num;
item->id = id;
item->offset = vendor_info.hdr->free_offset;
item->size = size;
vendor_info.hdr->free_offset += align_size;
vendor_info.hdr->free_size -= align_size;
memcpy((vendor_info.data + item->offset), pbuf, size);
vendor_info.hdr->item_num++;
vendor_info.hdr->version++;
vendor_info.hdr->next_index++;
*(vendor_info.version2) = vendor_info.hdr->version;
if (vendor_info.hdr->next_index >= part_num)
vendor_info.hdr->next_index = 0;
cnt = vendor_ops((u8 *)vendor_info.hdr, part_size * next_index, part_size, 1);
return (cnt == part_size) ? size : -EIO;
}
debug("[Vendor ERROR]:Vendor has no space left!\n");
return -ENOMEM;
}
四.VendorStorage kernel阶段接口
文件路径:
/kernel/drivers/soc/rockchip/sdmmc_vendor_storage.c
/kernel/drivers/soc/rockchip/rk_vendor_storage.c
kernel阶段读写接口:
int rk_vendor_read(u32 id, void *pbuf, u32 size)
{
if (_vendor_read)
return _vendor_read(id, pbuf, size);
return -1;
}
EXPORT_SYMBOL(rk_vendor_read);
int rk_vendor_write(u32 id, void *pbuf, u32 size)
{
if (_vendor_write)
return _vendor_write(id, pbuf, size);
return -1;
}
EXPORT_SYMBOL(rk_vendor_write);
五.用户态读写接口
static int emmc_vendor_read(unsigned int id, void *pbuf, unsigned int size)
{
unsigned int i;
if (!g_vendor){
DEBUG(("emmc_vendor_read g_vendor fail\n"));
return -ENOMEM;
}
DEBUG(("emmc_vendor_read: id=%u, pbuf=%p, size=%u\n", id, pbuf, size));
for (i = 0; i < g_vendor->item_num; i++) {
if (g_vendor->item[i].id == id) {
if (size > g_vendor->item[i].size)
size = g_vendor->item[i].size;
memcpy(pbuf,
&g_vendor->data[g_vendor->item[i].offset],
size);
DEBUG(("emmc_vendor_read: read %u bytes from offset %u\n", size, g_vendor->item[i].offset));
return size;
}
}
DEBUG(("emmc_vendor_read: id=%u not found\n", id));
return (-1);
}
static int emmc_vendor_write(unsigned int id, void *pbuf, unsigned int size)
{
unsigned int i, j, next_index, align_size, alloc_size, item_num;
unsigned int offset, next_size;
unsigned char *p_data;
int ret = 0;
struct vendor_item *item;
struct vendor_item *next_item;
if (!g_vendor)
return -ENOMEM;
p_data = g_vendor->data;
item_num = g_vendor->item_num;
align_size = ALIGN(size, 0x40); /* align to 64 bytes*/
next_index = g_vendor->next_index;
for (i = 0; i < item_num; i++) {
item = &g_vendor->item[i];
if (item->id == id) {
alloc_size = ALIGN(item->size, 0x40);
if (size > alloc_size) {
if (g_vendor->free_size < align_size) {
ret = -EINVAL;
goto exit;
}
offset = item->offset;
for (j = i; j < item_num - 1; j++) {
item = &g_vendor->item[j];
next_item = &g_vendor->item[j + 1];
item->id = next_item->id;
item->size = next_item->size;
item->offset = offset;
next_size = ALIGN(next_item->size,
0x40);
memcpy(&p_data[offset],
&p_data[next_item->offset],
next_size);
offset += next_size;
}
item = &g_vendor->item[j];
item->id = id;
item->offset = offset;
item->size = size;
memcpy(&p_data[item->offset], pbuf, size);
g_vendor->free_offset = offset + align_size;
g_vendor->free_size -= (align_size -
alloc_size);
} else {
memcpy(&p_data[item->offset],
pbuf,
size);
g_vendor->item[i].size = size;
}
g_vendor->version++;
g_vendor->version2 = g_vendor->version;
g_vendor->next_index++;
if (g_vendor->next_index >= EMMC_VENDOR_PART_NUM)
g_vendor->next_index = 0;
emmc_vendor_ops((unsigned char *)g_vendor, EMMC_VENDOR_PART_START +
EMMC_VENDOR_PART_SIZE * next_index,
EMMC_VENDOR_PART_SIZE, 1);
goto exit;
}
}
if (g_vendor->free_size >= align_size) {
item = &g_vendor->item[g_vendor->item_num];
item->id = id;
item->offset = g_vendor->free_offset;
item->size = size;
g_vendor->free_offset += align_size;
g_vendor->free_size -= align_size;
memcpy(&g_vendor->data[item->offset], pbuf, size);
g_vendor->item_num++;
g_vendor->version++;
g_vendor->version2 = g_vendor->version;
g_vendor->next_index++;
if (g_vendor->next_index >= EMMC_VENDOR_PART_NUM)
g_vendor->next_index = 0;
emmc_vendor_ops((unsigned char *)g_vendor, EMMC_VENDOR_PART_START +
EMMC_VENDOR_PART_SIZE * next_index,
EMMC_VENDOR_PART_SIZE, 1);
goto exit;
}
ret = -1;
exit:
return ret;
}