Chinaunix首页 | 论坛 | 博客
  • 博客访问: 410159
  • 博文数量: 62
  • 博客积分: 1483
  • 博客等级: 上尉
  • 技术积分: 779
  • 用 户 组: 普通用户
  • 注册时间: 2009-02-24 12:25
文章分类

全部博文(62)

文章存档

2012年(2)

2011年(6)

2010年(6)

2009年(48)

我的朋友

分类: LINUX

2009-10-10 19:46:41

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) |
给主人留下些什么吧!~~