前几天在分析MTD驱动,说实话,还是有些不明白的地方,特别是ECC检测那块,所以笔记也还没写完,我的计划是在6月之前,但是这几天又是在看不进去,那就往后继续吧,状态好的时候再去看看。
今天分析的是VIVI中的私有数据的管理,也就是vivi主函数中的init_priv_data();
我们跟着函数执行的顺序,看看init_priv_data()的源代码:
vivi的私有参数需要存放在0x33df0000开始出的48k的地方
一共有三种参数,每种占用16k的空间,下面我会具体分析:
linux_cmd |
16K |
parameter_tlb |
16K |
MTD_partition |
16K |
通过上图我们就可以知道vivi私有参数的存放的位置和大小。以及它们相对偏移量
int init_priv_data(void)
{
int ret_def;
#ifdef CONFIG_PARSE_PRIV_DATA
int ret_saved;
#endif
ret_def = get_default_priv_data();
#ifdef CONFIG_PARSE_PRIV_DATA
ret_saved = load_saved_priv_data();
if (ret_def && ret_saved) {
printk("Could not found vivi parameters.\n");
return -1;
} else if (ret_saved && !ret_def) {
printk("Could not found stored vivi parameters.");
printk(" Use default vivi parameters.\n");
} else {
printk("Found saved vivi parameters.\n");
}
#else
if (ret_def) {
printk("Could not found vivi parameters\n");
return -1;
} else {
printk("Found default vivi parameters\n");
}
#endif
#ifdef CONFIG_DEBUG_VIVI_PRIV
display_param_tlb();
display_mtd_partition();
#endif
return 0;
}
|
可以看到私有数据分为两步,首先读取默认的数据到数据区,然后获取保存的私有数据,如果存在则覆盖原有的数据
init_priv_data()--->get_default_priv_data()
static inline int
get_default_priv_data(void)
{
if (get_default_param_tlb())
return NO_DEFAULT_PARAM;
if (get_default_linux_cmd())
return NO_DEFAULT_LINUXCMD;
if (get_default_mtd_partition())
return NO_DEFAULT_MTDPART;
return 0;
}
|
从上面这个函数我们也可以看出,VIVI中的私有数据包括3个部分,它们分别是:param_tlb,linux_cmd,mtd_partition。这几个函数在成功的时候都是返回0。
首先我们来看get_default_param_tlb():
int get_default_param_tlb(void)
{
char *src = (char *)&default_vivi_parameters;
char *dst = (char *)(VIVI_PRIV_RAM_BASE + PARAMETER_TLB_OFFSET);
int num = default_nb_params;
if (src == NULL) return -1;
/*printk("number of vivi parameters = %d\n", num); */
*(nb_params) = num;
if ((sizeof(vivi_parameter_t)*num) > PARAMETER_TLB_SIZE) {
printk("Error: too large partition table\n");
return -1;
}
memcpy(dst, vivi_param_magic, 8);
dst += 16;
memcpy(dst, src, (sizeof(vivi_parameter_t)*num));
return 0;
}
|
我们先来看看上面这个函数,上面的函数还是有很多技巧的:
src是默认参数存放的地方,其实就是定义了一个结构数组,里面包含了各种我们需要的参数:
vivi_parameter_t default_vivi_parameters[] = {
{ "mach_type", MACH_TYPE, NULL },
{ "media_type", MT_S3C2440, NULL },
{ "boot_mem_base", 0x30000000, NULL },
{ "baudrate", UART_BAUD_RATE, NULL },
{ "xmodem", 1, NULL },
{ "xmodem_one_nak", 0, NULL },
{ "xmodem_initial_timeout", 300000, NULL },
{ "xmodem_timeout", 1000000, NULL },
{ "boot_delay", 0x1000000, NULL }
};
|
typedef struct parameter {
char name[MAX_PARAM_NAME];
param_value_t value;
void (*update_func)(param_value_t value);
} vivi_parameter_t;
|
其次在计算数组元素个数的时候使用了一个宏:
int default_nb_params = ARRAY_SIZE(default_vivi_parameters);
|
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
|
在本章的一开始我就提到了每个数据区只分配了16K的数据空间,当然你也可以按照自己的系统来分配,但是一定要匹配起来,在这里的if语句就是防止数据区过界。在vivi中有定义
然后就是拷贝vivi参数的魔数,我的理解魔数的作用其实就是一个简单检测的作用,在读取的时候先读取魔数,是否于我们设定的魔数相同,相同则继续,不相同说明失败。
这里的魔数是vivipara
const char vivi_param_magic[8] = {'V', 'I', 'V', 'I', 'P', 'A', 'R', 'A'};
|
这里魔数只有8位,但是实际我们给了16位,我不是很理解(是不是考虑对齐或者其他作用),我参考了CalmArrow的解释
“内存的入口地址为VIVI_PRIV_RAM_BASE+PARAMETER_TLB_OFFSET,开始的8个字节放magic number,这里vivi定义为“VIVIPARA”,后面空下8个字节,留作扩展,从第17个字节开始放置真正的param”结合上面的分析,其实parameter_tlb其实是放在(0x33df0000+16K)处
int get_default_linux_cmd(void)
{
char *src = linux_cmd;
char *dst = (char *)(VIVI_PRIV_RAM_BASE + LINUX_CMD_OFFSET);
if (src == NULL) return -1;
memcpy((char *)dst, (char *)linux_cmd_magic, 8);
dst += 8;
memcpy(dst, src, (strlen(src) + 1));
return 0;
}
|
同样,我们先来看linux_cmd的定义:
char linux_cmd[] = "noinitrd root=/dev/mtdblock2 init=/linuxrc console=ttySAC0";
|
其实上面这行我没怎么懂,查了一些:“
root=/dev/mtdblock2 init=/linuxrc console=ttySAC0中root=/dev/mtdblock2 init=/linuxrc是与启动文件系统有关,后面的是打开的控制台”
魔数:
const char linux_cmd_magic[8] = {'V', 'I', 'V', 'I', 'C', 'M', 'D', 'L'};
|
其他的于第一部分一样
下面就是最后一部分了,MTD_partition_tlb
还是先来看看他的函数源代码:
int get_default_mtd_partition(void)
{
char *src_parts = (char *)&default_mtd_partitions;
char *dst_parts = (char *)(VIVI_PRIV_RAM_BASE + MTD_PART_OFFSET);
int num = default_nb_part;
if (src_parts == NULL) return -1;
/* printk("number of mtd partitions: %d\n", num); */
*(nb_mtd_parts) = num;
if ((sizeof(mtd_partition_t)*num + 16) > MTD_PART_SIZE) {
printk("too large mtd partition table\n");
return -1;
}
memcpy(dst_parts, mtd_part_magic, 8); /* copy mtd magic */
dst_parts += 16;
/* copy partition table */
memcpy(dst_parts, src_parts, (sizeof(mtd_partition_t)*num));
return 0;
}
|
首先定义了mtd_partition的结构体:
typedef struct mtd_partiton {
char name[MAX_PART_NAME]; /* partition name */
ulong offset;
ulong size;
ulong flag;
} mtd_partition_t;
|
#ifdef CONFIG_S3C2440_NAND_BOOT
mtd_partition_t default_mtd_partitions[] = { /*struct mtd_partition_t was defined at include/priv_data.h*/
{
name: "vivi",
offset: 0,
size: 0x00020000,
flag: 0
}, { /* add by thisway.diy, 2006.06.20 */
name: "eboot",
offset: 0x00020000,
size: 0x00020000,
flag: 0
}, {
name: "param",
offset: 0x00040000,
size: 0x00010000,
flag: 0
},
#ifdef CONFIG_BOOT_LOGO
{
name: "logo",
offset: 0x00050000,
size: 0x00180000,
flag: 0
}, {
name: "kernel",
offset: 0x001D0000,
size: 0x00200000,
flag: 0
}, {
name: "root",
offset: 0x003D0000,
size: 0x03C2C000,
flag: 0
}
#else
{
name: "kernel",
offset: 0x00050000,
size: 0x00200000,
flag: 0
}, {
name: "root",
offset: 0x00250000,
size: 0x03DAC000,
flag: 0
}
#endif
};
#endif
|
mtd_partition的OFFSET为0,因此mtd_partition直接放在从0x33df0000开始的16k的地址空间,后面的跟前面的分析相同。到这里,默认的vivi 私有数据已经传送完成。接下来就是读取设定的数据,如果存在则直接覆盖默认的数据:
int
load_saved_priv_data(void)
{
char *buf = (char *)(DRAM_BASE);
char *dst = (char *)(VIVI_PRIV_RAM_BASE);
if (read_saved_priv_data_blk(buf)) {
printk("invalid (saved) parameter block\n");
return -1;
}
/* load parameter table */
if (strncmp((buf + PARAMETER_TLB_OFFSET), vivi_param_magic, 8) != 0)
return WRONG_MAGIC_PARAM;
memcpy(dst + PARAMETER_TLB_OFFSET, buf + PARAMETER_TLB_OFFSET,
PARAMETER_TLB_SIZE);
/* load linux command line */
if (strncmp((buf + LINUX_CMD_OFFSET), linux_cmd_magic, 8) != 0)
return WRONG_MAGIC_LINUXCMD;
memcpy((dst + LINUX_CMD_OFFSET), buf + LINUX_CMD_OFFSET, LINUX_CMD_SIZE);
/* load mtd partition table */
if (strncmp(buf + MTD_PART_OFFSET, mtd_part_magic, 8) != 0)
return WRONG_MAGIC_MTDPART;
memcpy(dst + MTD_PART_OFFSET, buf + MTD_PART_OFFSET, MTD_PART_SIZE);
return 0;
}
|
来看if语句调用的函数:read_saved_priv_data_blk();
int
read_saved_priv_data_blk(char *buf)
{
//从vivi数据存放在nand flash中的地址,读取48K数据到buf缓存区中
//#define VIVI_PRIV_ROM_BASE 0x00fc0000
//#define VIVI_PRIV_SIZE 48K
char *src = (char *)(VIVI_PRIV_ROM_BASE);
size_t size = (size_t)(VIVI_PRIV_SIZE);
#ifdef CONFIG_USE_PARAM_BLK
{ //{ name: "param", // offset: 0x00040000, // size: 0x00010000, // flag: 0 // }, mtd_partition_t *part = get_mtd_partition("param");
if (part == NULL) {
printk("Could not found 'param' partition\n");
return -1;
}
src = (char *)part->offset;
}
#endif
return read_mem(buf, src, size);
}
|
从上面的函数可以看出,如果定了#ifdef CONFIG_USE_PARAM_BLK
则是利用MTD partition表来找到vivi param的位置,我们看到vivi param在nand flash的偏移量为0x0004000
大小为0x0001000(64k),在MTD partition分区列表中有该项(“param”)的情况下。
下面来具体分析get_mtd_partition()函数
mtd_partition_t *get_mtd_partition(const char *name)
{
int i, num = *nb_mtd_parts;
mtd_partition_t *parts = mtd_parts; //通过遍历寻找于名字相同的MTD partition分区。并且返回分区的地址
for (i = 0; i < num; i++, parts++) {
if ((strncmp(parts->name, name, strlen(name)) == 0) &&
(strlen(parts->name) == strlen(name))) {
return parts;
}
}
return NULL;
}
|
先来看看两个常量:
mtd_partition_t *mtd_parts = \
(mtd_partition_t *)(VIVI_PRIV_RAM_BASE + MTD_PART_OFFSET + 16);
//下面的变量我是真没看懂,按理说(VIVI_PRIV_RAM_BASE + MTD_PART_OFFSET + 8)根本
//没定义,在之前的8个字节则是定义了mtd partition的魔数(VIVIMTDP),后面的8个字节
//没有赋值,然后在再是存放vivi partition的参数
//在这里我的理解是,他应该是希望把在MTD partition所定义分区的个数保存在此地址,这样才解释的通。但是我没找到,如果有知道的麻烦相告,chenming8605@gmail.com
/***********************************************
* 后来看到了,这部分已经解决,看到了一个函数
/vivi/drivers/mtd/mtdpart.c
* +--------------------------------+ * | magic (4 bytes) | * +--------------------------------+ * | number of partitions (4 bytes) | * +--------------------------------+ * | | * | partition table | * | | * +--------------------------------+ * */* * add mtd partitions */ int add_mtd_partition(mtd_partition_t *new_part) { int num = *nb_mtd_parts; mtd_partition_t *parts = mtd_parts;
memcpy((parts + num), new_part, sizeof(mtd_partition_t)); num++; *(nb_mtd_parts) = num; return 0; } 可以看出,每次增加一个mtd_partition分区,则加一,说明我前面的推断是正确的:) ************************************************/
int *nb_mtd_parts = (int *)(VIVI_PRIV_RAM_BASE + MTD_PART_OFFSET + 8);
|
还有一个函数我们需要注意:mem_read(buf, src, len)
其实这个函数封装了另外一个函数,我们来看实现:
read_mem(char *dst, char *src, size_t size)
{
return nand_read_ll(dst, (unsigned long)src, (int)size);
}
|
我想对于这个函数就不需要解释了把。在vivi启动的stage 1就应该熟悉。就是从src开始读取size字节的数据到dst。
这部分到这里就结束了。可能知识点还有遗漏!欢迎指正
阅读(1204) | 评论(0) | 转发(0) |