blob: 6b8e5eb341a3d454b45a06083310fd3126a1e9cd [file] [log] [blame]
#include "io.h"
#include "string.h"
#include "debug.h"
#include "util.h"
#include "flash_adaptor.h"
#include "load_vt.h"
#define CRC32_SIZE (4)
extern unsigned int crc32(unsigned int crc, unsigned char *buf, unsigned int len);
/*version table*/
#define MAX_VERSION_TABLE 32
#define CLEAR_VT_SIZE 2048
typedef struct {
int num;
ver_table_entry_t vt[MAX_VERSION_TABLE];
unsigned int dev_ids[MAX_VERSION_TABLE];
}ver_table_t;
static ver_table_t vts;
/*version table end*/
static void dump_version_entry(ver_table_entry_t * vte)
{
INFO("%s: part1(start=%d, blks=%d, version=%08u%04u), part2(start=%d, blks=%d, version=%08u%04u)\n", vte->name,
vte->part1_start_blkind, vte->part1_blks, vte->part1_version.major_version, vte->part1_version.minor_version,
#ifdef CONFIG_EMMC_WRITE_PROTECT
vte->part1_start_blkind, vte->part1_blks, vte->part1_version.major_version, vte->part1_version.minor_version);
#else
vte->part2_start_blkind, vte->part2_blks, vte->part2_version.major_version, vte->part2_version.minor_version);
#endif
}
void dump_version_table(void)
{
int i = 0;
for(i = 0; i < vts.num; i++) {
INFO("[%02d,sd%02d] ", i, vts.dev_ids[i]);
dump_version_entry(&(vts.vt[i]));
}
}
// because vt is encrypted, we need decrypt it then parse it
int parse_version_table(unsigned char* buff)
{
unsigned int i;
unsigned int dev_id = 0;
unsigned vt_size = 0;
ver_table_entry_t * vt_entry;
version_table_t *vt = (version_table_t *)(buff);
vt_size = sizeof(version_table_t) + vt->num_entries * sizeof(ver_table_entry_t);
/* check calculated version table size*/
if(vt_size >= 2048) {
ERR("ERROR: vt_size is too big %d!!!\n", vt_size);
return FLASH_OP_ERR;
}
if(vt->magic != MAGIC_NUMBER || 0xffffffff != crc32(0, (unsigned char *)buff, vt_size + CRC32_SIZE)) {
ERR("EMMC: MAGIC NUMBER or CRC error. %d\n", vt_size);
return FLASH_OP_ERR;
}
dev_id = 0;
vts.num = 0;
for(i = 0;i < vt->num_entries;i++) {
vt_entry = &vt->table[i];
memcpy(&(vts.vt[i]), vt_entry, sizeof(ver_table_entry_t));
vts.dev_ids[i] = dev_id;
vts.num ++;
dev_id = flash_dev_id_inc(dev_id);
#ifdef CONFIG_EMMC_WRITE_PROTECT
#else
if (vt_entry->part1_start_blkind != vt_entry->part2_start_blkind) {
/* double copy of the partition */
dev_id = flash_dev_id_inc(dev_id);
}
#endif
}
return 0;
}
inline static unsigned get_aligned(unsigned address, unsigned page_size) {
return (address + page_size - 1) / page_size * page_size;
}
int get_partition_info(void * tbuff)
{
//clear pt is located at the last 1K of boot partition(block)
//encrypted pt is located at last 14K of boot partition(block)
//we need a buff that is at least 64bytes aligned address because of the DMA of EMMC
unsigned char * buff = (unsigned char *)(uintmax_t)get_aligned((unsigned)(uintmax_t)tbuff, 64);
int num = 0, i = 0;
int ret = 0;
long long start = 0;
num = get_boot_partition_number();
for(i = 1; i <= num; i++) {
//read the clear pt only
start = get_block_size() - CLEAR_VT_SIZE;
if(read_flash_from_part(i, start, CLEAR_VT_SIZE, buff) != 0) {
ERR("read partiton info error\n");
continue;
}
ret = parse_version_table(buff);
if(ret != 0) {
ERR("parse partiton info error\n");
continue;
}
return 0;
}
return FLASH_SWITCH_PART_NOEXIST;
}
int find_partition(const void *name)
{
int i = 0;
for(i = 0; i < vts.num; i++) {
//FIXME: ugly condition
//avoid boot = bootloader, boot = bootlogo
if(strlen(name) != strlen(vts.vt[i].name))
continue;
if(memcmp(name, vts.vt[i].name, strlen(name)) == 0) {
return i;
}
}
return PARTITION_NOT_EXIST;
}
int fetch_partition_info(int num, ver_table_entry_t *vt)
{
if((num >= 0) && (num < vts.num)) {
memcpy(vt, &(vts.vt[num]), sizeof(ver_table_entry_t));
return (int)(vts.dev_ids[num]);
}
return PARTITION_NOT_EXIST;
}
unsigned int get_version_table_entry_number()
{
return vts.num;
}
long long read_image(ver_table_entry_t *vt,unsigned int image_size,unsigned char *image_buff)
{
return read_image_from_offset(vt, 0, image_size, image_buff);
}
long long read_image_from_offset(ver_table_entry_t *vt, unsigned int pt_offset, unsigned int size, unsigned char *image_buf)
{
long long start = 0, end = 0;
start = vt->part1_start_blkind;
start *= get_block_size();
start += pt_offset;
end = vt->part1_start_blkind + vt->part1_blks;
end *= get_block_size();
//image_size must not exceed the current partition
if((start + size) > end) {
ERR("the image is exceed the partition(%x > %x). Possible hacker attack detected!\n",
(start + size), end);
return -1;
}
//For EMMC:switch to user area
//For nand and nor: nothing
switch_flash_part(0);
return read_flash(start, size, image_buf);
}
//FIXME: how this function can be unified
#ifdef ENABLE_EMMC
void set_flash_ts_param(char *param_buf)
{
ver_table_entry_t vt_fts;
unsigned int fts_dev_id = 0;
int num = find_partition(FTS_NAME);
*param_buf = '\0';
if(num >= 0) {
fts_dev_id = (unsigned int)fetch_partition_info(num, &vt_fts);
sprintf(param_buf, " emmc_ts.dev_id=%d emmc_ts.size=%d emmc_ts.erasesize=%d emmc_ts.writesize=%d",
fts_dev_id, vt_fts.part1_blks * get_block_size(), get_block_size(), 512);
}
}
#endif
#ifdef ENABLE_NAND
void set_flash_ts_param(char *param_buf)
{
ver_table_entry_t vt_fts;
unsigned int fts_dev_id = 0;
int num = find_partition(FTS_NAME);
*param_buf = '\0';
if(num >= 0) {
fts_dev_id = (unsigned int)fetch_partition_info(num, &vt_fts);
sprintf(param_buf, " flash_ts.dev_id=%d flash_ts.size=%d flash_ts.erasesize=%d flash_ts.writesize=%d",
fts_dev_id, vt_fts.part1_blks * get_block_size(), get_block_size(), get_page_size());
}
}
#endif