OS2008 4.0 chinook 系统中,有四个 mtdblock 分区,为了确定他们是在哪里定义的,我们需要下载内核代码来找线索。不过 4.1 diablo 已经出了,那我们就以 kernel-source-diablo-2.6.21 为参照。
注册 driver首先 N800 的 flash 属于 onenand 类型的 NAND flash, Nokia 修改了代码,在onenand 的基础上增加了 drivers/mtd/onenand/omap2.c 为他的 flash 专门做了onenand 上层的驱动, 在这个文件里,主要关注 :
1、 driver registration : omap2_onenand_driver 结构被注册为一个 platform bus 的 driver
2、 driver probe function : omap2_onenand_probe
当有新的 platform device 注册时,该函数就有可能被调用到。
3、 n800 flash device 注册时会在传给 probe 的数据里附带分区信息, 所有mtdblock分区会在 probe 函数里被注册。
注册 devicearch/arm/mach-omap2/board-n800-flash.c 里的代码将注册 flash device :
1、首先 n800_flash_init 被 nokia_n800_init 调用, nokia_n800_init 是 该 machine type 的初始函数,见 arch/arm/mach-omap2/board-n800.c 里的定义 : MACHINE_START(NOKIA_N800, "Nokia N800") 。
2、 n800_flash_init 调用 omap_get_nr_config 获得分区信息,然后用 platform_device_register 注册设备,并附带分区信息。 这样上面注册的 driver 的 probe 函数就会被掉用到拉。
好啦,现在到了最关键的问题 omap_get_nr_config 是什么?
见 arch/arm/plat-omap/common.c
* const void *__omap_get_config(u16 tag, size_t len, int nr)
{
return get_config(tag, len, nr, NULL);
}
而 get_config 则是 arm linux 自己的 tags 机制了, 所谓 tags 就是 arm linux 用来和 bootloader 沟通的启动参数。
关于内核参数传递tag,我就不复述,转贴一篇我搜到的相关文章,
原文地址 :
http://blog.chinaunix.net/u1/47239/showart_375680.html
不过注释一点, 该机制是 arm linux的,其他平台并没有广泛采用,比如 x86 下就不是。
/转载======================================================================
Linux内核参数传递Tag
|
|
|
在2.4(具体哪个版本记不清了)以后的Linux内核中引入了一种新的向内核传递参数的方法tag标记。内核参数通过一个静态的tag链表在启动的时候传递到内核。每个tag的结构为
+-----------+
tag_header
+-----------+
tag_xxx
+-----------+
其中tag_header为tag头,表明tag_xxx的类型和大小,之所以要标识tag_xxx的类型是因为不同的tag需要不同的处理函数(下文讲tagtable的时候会分析到)。tag_header的结构为
struct tag_header { int size; int tag; }
|
size表示tag的结构大小,tag为表示tag类型的常量。这个静态的链表必须以tag_header.tag = ATAG_CORE开始,并以tag_header.tag = ATAG_NONE结束。由于不同的tag所使用的格式可能不尽相同,所以内核又定义了一个结构tagtable来把tag和相应的操作函数关联起来
struct tagtable { u32 tag; int (*parse)(const struct tag*); }
|
其中tag为标识入ATAG_NONE,ATAG_CORE等。parse为处理函数。Linux内核将tagtable也组成了一个静态的链表放入.taglist.init节中,这是通过__tagtable宏来实现的
#define __tag __attribute_used__ __attribute__((__section__ (“.taglist.init”)))
#define __tagble(tag,fn) static struct tagtable __tagtable_##fn __tag = {tag, fn}
|
以处理命令行参数为例:
static int __init parse_tag_cmdline(const struct tag* tag) { strlcpy(default_command_line, tag->u.cmdline.cmdline, COMMAND_LINE_SIZE); } __tagtable(ATAG_CMDLINE, parse_tag_cmdline)
|
可以看到parse_tag_cmdline将命令行参数拷贝到default_command_line里,__tagtable将ATAG_CMDLINE和parse_tag_cmdline挂钩。
以上已经分析了内核和tag相关的两个重要结构。现在分析具体的实现。内核中定义了一些默认的tags
static struct init_tags { struct tag_header hdr1; struct tag_core core; struct tag_header hdr2; struct tag_mem32 mem; struct tag_header hdr3; }init_tags __initdata = { { tag_size(tag_core), ATAG_CORE }, { 1, PAGE_SIZE, 0xff }, { tag_size(tag_mem32), ATAG_MEM }, { MEM_SIZE, PHYS_OFFSET }, { 0, ATAG_NONE } }
|
上述结构中一个tag_header和tag_xxx形成了tag的完整描述,tag_size返回tag_head和tag_xxx的总大小,在tag_size中我们要注意的是u32*指针加1地址值实际上地址加了4
#define tag_next(t) ((struct tag*)((u32*)(t)+(t)->hdr.size))
#define tag_size(type) ((sizeof(struct tag_header)+sizeof(struct type)) >> 2
tag_size实际上计算的是(tag_head+tag_xxx)/4。经过进一步的分析还发现每个tag在内存中的大小并不是相同的,这一点可以从tag_next看出,tag_next只是将指针移到了下一个tag的tag_header处,这种内存布局更加紧凑。对tag的处理代码在arch/arm/setup.c setup_arch里面。以下是一部分的关键代码
struct tag *tags = (struct tag*)&init_tags; //tags指向默认的tag链表 …… mdesc = setup_machine(machine_arch_type);// mdesc包含启动参数在内存中的地址 if( mdesc->boot_params ) tags = phys_to_vert(mdesc->boot_params);// bootloader有传递启动参数到内核 if( tags->hdr.tag != ATAG_CORE ) convert_to_tag_list(tags);//如果是旧的启动参数结构,将其转成新的tag链表的形式 if( tags->hdr.tags != ATAG_CORE ) tags = (struct tag*)&init_tags;//转换失败,使用内置的启动参数 if( tags->hdr.tag == ATAG_CORE ) { if( meminfo.nr_banks != 0 ) squash_mem_tags(tags);//如果在meminfo中有配置内存tag则跳过对内存tag的处理 parse_tags(tags); }
|
*注:2.6.18内核smdk2410的meminfo没有设置nr_banks,所以必须在内核的启动参数里面传递mem=”memory size”@”memory base address”,否则系统识别内存错误,这点从系统的启动信息就可以看出来,而且在加载initrd的时候也会遇到内存溢出的错误
static void __init parse_tags(const struct tag* t) { for(; t->hdr.size; t=tag_next(t)) { if( !parse_tag(t)) printk(…); } }
|
parse_tags遍历tag链表调用parse_tag对tag进行处理。parse_tags在tabtable中寻找tag的处理函数(通过tag_header结构中的tag)。 | |
转载/======================================================================
好了,对 tags 有基本了解了以后,我们看 N800 的内核怎么处理的:
在 arch/arm/plat-omap/common.c 里,注册了一个 tag 类处理函数和一个 tag 类别:
* static int __init parse_tag_omap(const struct tag *tag)
{
u32 size = tag->hdr.size - (sizeof(tag->hdr) >> 2);
size <<= 2;
if (size > sizeof(omap_bootloader_tag))
return -1;
memcpy(omap_bootloader_tag, tag->u.omap.data, size);
omap_bootloader_tag_len = size;
return 0;
}
* __tagtable(ATAG_BOARD, parse_tag_omap);
好啦,这下所有线索都串联起来了,所以 N800 的分区是在启动参数tags 中的 ATAG_BOARD 类别 tag 中定义的。
阅读(1909) | 评论(0) | 转发(0) |