arm-linux(kernel-2.6.13)的启动过程(1.4/2)回到 setup_arch()
request_standard_resources(&meminfo, mdesc);
在看这段代码前,先学学resource结构体。
/*
* Resources are tree-like, allowing
* nesting etc..
*/resource有点像树形结构,允许像鸟巢那样。
struct resource {
const char *name;
unsigned long start, end;
unsigned long flags;
struct resource *parent, *sibling, *child;//说的就是这里了,父亲,儿子,兄弟。
};
内容包括,本io资源的名字,起始,结束物理地址,标志(表征该io资源的类型,很多很多类型)。
/*
* Standard memory resources
*/
static struct resource mem_res[] = {
{ "Video RAM", 0, 0, IORESOURCE_MEM },
{ "Kernel text", 0, 0, IORESOURCE_MEM },
{ "Kernel data", 0, 0, IORESOURCE_MEM }
};
#define video_ram mem_res[0]
#define kernel_code mem_res[1] //内核代码段io资源
#define kernel_data mem_res[2] //内核数据段io资源
request_standard_resources的内容如下:
static void __init
request_standard_resources(struct meminfo *mi, struct machine_desc *mdesc)
{
struct resource *res;
int i;
kernel_code.start = virt_to_phys(&_text);
kernel_code.end = virt_to_phys(&_etext - 1);
kernel_data.start = virt_to_phys(&__data_start);
kernel_data.end = virt_to_phys(&_end - 1);
取得代码段 数据段 物理地址,这里的代码段不包括init段了,lds里描述说是真正的代码段。
难道init段的内容可以被覆盖???
for (i = 0; i < mi->nr_banks; i++) {
unsigned long virt_start, virt_end;
if (mi->bank[i].size == 0)
continue;
virt_start = __phys_to_virt(mi->bank[i].start); //取得本ram的起始和结束虚拟地址
virt_end = virt_start + mi->bank[i].size - 1;
res = alloc_bootmem_low(sizeof(*res));
//分配resource结构大小的内存空间,经过页对齐(向上),结构得到了一个内存页,这就是内存的页管理策略。
res->name = "System RAM";
res->start = __virt_to_phys(virt_start);
res->end = __virt_to_phys(virt_end);
res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
为系统的ram也建立了一个资源结构体,表征系统的ram资源。
request_resource(&iomem_resource, res);
然后申请这个"System RAM"资源,他的总根是iomem_resource
if (kernel_code.start >= res->start &&
kernel_code.end <= res->end)
request_resource(res, &kernel_code);
if (kernel_data.start >= res->start &&
kernel_data.end <= res->end)
request_resource(res, &kernel_data);
}
申请内核代码,数据段 所在ram的io资源,他们作为刚才申请的"System RAM"的子资源,也就是
iomem_resource -> "System RAM" -> kernel_code
|
kernel_data
这样的关系,"System RAM"的子资源是kernel_code,kernel_data与kernel_code是sibling关系,他们的parent都是"System RAM"。
if (mdesc->video_start) {
video_ram.start = mdesc->video_start;
video_ram.end = mdesc->video_end;
request_resource(&iomem_resource, &video_ram);
}
如果有视频缓冲区的话,显然arm没有。在后面分析request_resource函数。
可以看出,对io资源的申请,这里不需要验证冲突与否,因为这里肯定不会冲突。
/*
* Some machines don't have the possibility of ever
* possessing lp0, lp1 or lp2
*/
if (mdesc->reserve_lp0)
request_resource(&ioport_resource, &lp0);
if (mdesc->reserve_lp1)
request_resource(&ioport_resource, &lp1);
if (mdesc->reserve_lp2)
request_resource(&ioport_resource, &lp2);
}
看看这个函数
request_resource,在这之前,现看看下面两个resource结构。
struct resource ioport_resource = {
.name = "PCI IO",
.start = 0x0000,
.end = IO_SPACE_LIMIT,
.flags = IORESOURCE_IO,
};
#define IO_SPACE_LIMIT 0xffffffff
EXPORT_SYMBOL(ioport_resource);
struct resource iomem_resource = {
.name = "PCI mem",
.start = 0UL,
.end = ~0UL,
.flags = IORESOURCE_MEM,
};
EXPORT_SYMBOL(iomem_resource);
static DEFINE_RWLOCK(resource_lock);
上面的两个resource数据结构是io内存,和io端口的总根,其他的io资源都是他们派生出来的。
int request_resource(struct resource *root, struct resource *new)
{
struct resource *conflict;
write_lock(&resource_lock); //加写入锁
conflict = __request_resource(root, new); 这个是主体,在后面
write_unlock(&resource_lock);
return conflict ? -EBUSY : 0;
}
/* Return the conflict entry if you can't request it */
static struct resource * __request_resource(struct resource *root, struct resource *new)
{
unsigned long start = new->start;
unsigned long end = new->end;
struct resource *tmp, **p;
if (end < start)
return root;
if (start < root->start)
return root;
if (end > root->end)
return root;
申请的资源必须在根resouce的区域内。
p = &root->child; //取得子资源的地址
for (;;) {
tmp = *p; //取得子资源的内容
if (!tmp || tmp->start > end) {
//如果还没有子资源,那就太好了,直接方进去
//如果有了子资源,但是等待加入到子资源的资源 在现有 子资源的前面,也可以加进来,这时候,子资源的指针,指向刚才加入的那个子资源。
new->sibling = tmp;
*p = new;
new->parent = root;
return NULL;
}
到了这里说明待加入的子资源在现有子资源的后面。
p = &tmp->sibling; //取得现有子资源的兄弟指针
if (tmp->end < start) //继续寻找
continue;
return tmp; //到了这里,说明有冲突。
}
}
static struct resource * __request_resource(struct resource *root, struct resource *new)
__request_resource的功能:
把new作为子资源加入到root中。插入的方法是按地里位置排列的,
root->child指向 最前面的子资源。
回到 setup_arch()
cpu_init();
struct stack {
u32 irq[3];
u32 abt[3];
u32 und[3];
} ____cacheline_aligned;
static struct stack stacks[NR_CPUS];
这个stacks是什么时候初始化的呢???
不会全是0吧?
去看看,
c03311ec :
...
c0331200 : //可见stacks是256字节对齐的。
c0331248 :
物理地址应该是30331200
lzd> md 0x30331200
30331200: 00000000 00000000 00000000 00000000 ................ 这里就是stacks的内容了
30331210: 00000000 00000000 00000000 00000000 ................
30331220: 00000000 00000000 00000000 00000000 ................
30331230: 00000000 00000000 00000000 00000000 ................
30331240: c025dada c026b200 6e696f6e 64727469 ..%...&.noinitrd 命令行的内容,没错的
30331250: 6f6f7220 642f3d74 6d2f7665 6c626474 root=/dev/mtdbl
30331260: 326b636f 696e6920 6c2f3d74 78756e69 ock2 init=/linux
30331270: 63206372 6f736e6f 743d656c 41537974 rc console=ttySA
30331280: 00003043 00000000 00000000 00000000 C0..............
stacks里的内容确实是0。
/*
* cpu_init - initialise one CPU.
*
* cpu_init dumps the cache information, initialises SMP specific
* information, and sets up the per-CPU stacks.
*/
void cpu_init(void)
{
unsigned int cpu = smp_processor_id(); //单处理器,返回0
struct stack *stk = &stacks[cpu];
if (cpu >= NR_CPUS) {
printk(KERN_CRIT "CPU%u: bad primary CPU number\n", cpu);
BUG();
}
dump_cpu_info(cpu);
/*
* setup stacks for re-entrant exception handlers
*/
__asm__ (
"msr cpsr_c, %1\n\t" //进入irq模式
"add sp, %0, %2\n\t" //设置irq堆栈
"msr cpsr_c, %3\n\t" //进入abt模式
"add sp, %0, %4\n\t" //设置abt堆栈
"msr cpsr_c, %5\n\t"
"add sp, %0, %6\n\t"
"msr cpsr_c, %7" //返回到管理模式
:
: "r" (stk),
"I" (PSR_F_BIT | PSR_I_BIT | IRQ_MODE),
"I" (offsetof(struct stack, irq[0])),
"I" (PSR_F_BIT | PSR_I_BIT | ABT_MODE),
"I" (offsetof(struct stack, abt[0])),
"I" (PSR_F_BIT | PSR_I_BIT | UND_MODE),
"I" (offsetof(struct stack, und[0])),
"I" (PSR_F_BIT | PSR_I_BIT | SVC_MODE)
: "r14");
} //我对上面的内容感到苦恼,这些堆栈被设置到那里了阿?可以那样设置吗???
天阿,我发现自己还不知道sp在那里呢?找。
在head.S中
.long init_thread_union + THREAD_START_SP @ sp
...
ldmia r3, {r4, r5, r6, sp} //在这里设置的sp,天啊,当时没弄清楚饿 :-(
...
b start_kernel
init_thread_union是一个thread_union联合,在这里,他被用作thread_info结构体,并得到了初始化。
union thread_union init_thread_union
__attribute__((__section__(".init.task"))) =
{ INIT_THREAD_INFO(init_task) };
在lds中。
.data : AT(__data_loc) {
__data_start = .; /* address in memory */
/*
* first, the init task union, aligned
* to an 8192 byte boundary.
*/
*(.init.task)
堆栈在这里,这个东西可不能乱设阿。
这个 thread_union 有2个页面大小哦,因为后面的stack是8k,取大的。
union thread_union {
struct thread_info thread_info;
unsigned long stack[THREAD_SIZE/sizeof(long)];
};
下面的就是初始化的任务数据结构。
struct task_struct init_task = INIT_TASK(init_task);
EXPORT_SYMBOL(init_task);
#define init_thread_info (init_thread_union.thread_info)
#define init_stack (init_thread_union.stack)
#define INIT_THREAD_INFO(tsk) \
{ \
.task = &tsk, \
.exec_domain = &default_exec_domain, \
.flags = 0, \
.preempt_count = 1, \
.addr_limit = KERNEL_DS, \
.cpu_domain = domain_val(DOMAIN_USER, DOMAIN_MANAGER) | \
domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) | \
domain_val(DOMAIN_IO, DOMAIN_CLIENT), \
.restart_block = { \
.fn = do_no_restart_syscall, \
}, \
}
#define THREAD_SIZE 8192
#define THREAD_START_SP (THREAD_SIZE - 8)
所以堆栈指针就出来了。
.long init_thread_union + THREAD_START_SP @ sp
也就是说,sp的位置在 init_thread_union + 8192 - 8 的位置。
至于具体在那里?在下面
而且在init_thread_union地址处的几个内存空间中是有数据的,就是初始化INIT_THREAD_INFO(tsk)的数据。
如下验证:
c02a4000 <__data_start>:
c02a4000: 00000000 andeq r0, r0, r0 // .flags = 0
c02a4004: 00000001 andeq r0, r0, r1 // .preempt_count
c02a4008: 00000000 andeq r0, r0, r0
c02a400c: c02a60c0 eorgt r6, sl, r0, asr #1
c02a4010: c02aa040 eorgt sl, sl, r0, asr #32
c02a4014: 00000000 andeq r0, r0, r0
c02a4018: 0000001f andeq r0, r0, pc, lsl r0
...
c02a4180: c0052300 andgt r2, r5, r0, lsl #6
...
c02a6000 <__nosave_begin>: //一直到这里才有数据哦,也就是2也页面的大小的堆栈。
回到 setup_arch()
/*
* Set up various architecture-specific pointers
*/
init_arch_irq = mdesc->init_irq;
system_timer = mdesc->timer;
init_machine = mdesc->init_machine;
conswitchp = &dummy_con;
}
设置下面的全局量
void (*init_arch_irq)(void) __initdata = NULL;
struct sys_timer *system_timer;
static void (*init_machine)(void) __initdata;
const struct consw *conswitchp;
为以后的初始化作准备。
阅读(2414) | 评论(0) | 转发(0) |