Chinaunix首页 | 论坛 | 博客
  • 博客访问: 852648
  • 博文数量: 190
  • 博客积分: 7021
  • 博客等级: 少将
  • 技术积分: 1752
  • 用 户 组: 普通用户
  • 注册时间: 2010-05-17 19:26
文章分类

全部博文(190)

文章存档

2014年(9)

2011年(32)

2010年(149)

我的朋友

分类: LINUX

2010-05-21 13:15:16

前几天在分析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。
这部分到这里就结束了。可能知识点还有遗漏!欢迎指正
阅读(1160) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~