yiffer的个人空间 https://blog.eetop.cn/edesign [收藏] [复制] [分享] [RSS]

空间首页 动态 记录 日志 相册 主题 分享 留言板 个人资料

日志

setup_arch

已有 996 次阅读| 2010-7-20 23:19 |个人分类:Linux移植

分析setup_arch(setup.c):
setup_processor 设置处理器的类型,并进行初始化
setup_machine 返回机器类型描述结构体信息
setup_arch()对内核参数的解析
setup_arch()函数是体系结构相关的内核初始化过程,这其中对内核参数有涉及的变量和操作如下
void __init setup_arch(char **cmdline_p)
{
char *from = default_command_line;
//定义了一个指向default_command_line的指针

//这里可能存在一些对from操作的machine-dep的函数

memcpy(saved_command_line, from, COMMAND_LINE_SIZE);
//这时的from所指向的就是完整待解析的内核参数,将它复
//制到saved_command_line中去(以供start_kernel()打印)
//之所以不直接使用default_command_line是因为在此之前
//有可能定义一些具体板子相关的对from的操作
saved_command_line[COMMAND_LINE_SIZE-1] = '\0';
//最后一位置为NULL
parse_cmdline(&meminfo, cmdline_p, from);
//调用parse_cmdline处理from指向的内核参数中关于内存的//部分
}
parse_cmdline 用于解析内核参数中关于内存的部分
parse_cmdline对内核参数的解析
parse_cmdline做了三件事,首先它解析了from所指向的完整的内核参数,中关于内存的部分,其次它将没有解析的部分复制到command_line中,最后它将start_kernel()传进来的内核参数指针指向command_line
内核参数中的“mem=xxxM@xxx”将会被parse_cmdline解析,并根据结果设置meminfo,而其余部分则被复制到command_line中
判断是软reboot还是硬reboot
指明代码段和数据段的起始地址和结束地址
bootmem_init()为了在启动阶段描述内存使用情况我们需要一些内存空间,这些空间叫做bootmem
void __init bootmem_init(struct meminfo *mi)
{
 struct node_info node_info[NR_NODES], *np = node_info;
 unsigned int bootmap_pages, bootmap_pfn, map_pg;
 int node, initrd_node;
 bootmap_pages = find_memend_and_nodes(mi, np);后面会详细分析这个函数,为了在
启动阶段描述内存使用情况我们需要一些内存空间,这些空间叫做bootmem,此时bootm
ap_pages表明了bootmem所需要的pages的数目
 bootmap_pfn   = find_bootmap_pfn(0, mi, bootmap_pages); 后面会详细分析这个函
数,通过这个函数,bootmap_pfn设置了bootmem所在的初始页号。也就是说从bootmap_
pfn到bootmap_pfn + bootmap_pages的内存页被用来描述初始化的时候的内存的用用情

 initrd_node   = check_initrd(mi);//俺们的板子没有用,应该是检查ramdisk的节点情况
 map_pg = bootmap_pfn;
 np += numnodes - 1;
 初始化node结构
 for (node = numnodes - 1; node >= 0; node--, np--) {
  if (np->end == 0) {
   if (node == 0)
    BUG();
   continue;
  }
  init_bootmem_node(NODE_DATA(node), map_pg, np->start, np->end); 后面会详细
分析这个函数
  free_bootmem_node_bank(node, mi);//释放所有内存,也就是把bootmem的区域全部
设置为0
  map_pg += np->bootmap_pages;
 我们有可能会保留一些内存以便使值不能被动态分配,具体要保留什么内容,后面会详
细分析
  if (node == 0)
   reserve_node_zero(bootmap_pfn, bootmap_pages);
 }
 if (map_pg != bootmap_pfn + bootmap_pages)
  BUG();
}
paging_init 页表初始化
创建内核页表,映射所有物理内存和io空间, 对于不同的处理器,这个函数差别很大。这个函数里面东西比较多,改天抽个时间认真研究一下!
request_standard_resources 建立资源链表
内核映象所占用的物理页面不允许动态分配,内核代码段和数据段所占用空间不能分配。
parse_options 解析命令行参数中剩下的部分(跟parse_cmdline 相比较)主要是环境变量和要执行的第一个脚本linuxrc
trap_init 主要是对一些系统保留的中断向量的初始化,下面是entry-armv.S中向量表的初始化
.LCvectors: swi SYS_ERROR0
  b __real_stubs_start + (vector_undefinstr - __stubs_start)
  ldr pc, __real_stubs_start + (.LCvswi - __stubs_start)
  b __real_stubs_start + (vector_prefetch - __stubs_start)
  b __real_stubs_start + (vector_data - __stubs_start)
  b __real_stubs_start + (vector_addrexcptn - __stubs_start)
  b __real_stubs_start + (vector_IRQ - __stubs_start)
  b __real_stubs_start + (vector_FIQ - __stubs_start)

init_IRQ 做与中断处理相关的初始化,将所有中断注册一个中断描述符。其中还要做dma的初始化。
浅析armlinux-setup_arch()->setup_arch()函数-最终

浅析armlinux-setup_arch()->setup_arch()函数-最终

文章来源:http:
//gliethttp.cublog.cn

建议首先参考《浅析armlinux2_4_19启动程序[head-armv.s文件]》与[http:
//gliethttp.cublog.cn]

《浅析setup_arch()函数tag_list的uboot[u-boot]由来》
《浅析armlinux-setup_arch()->setup_processor()函数1》
《浅析armlinux-setup_arch()->setup_machine()函数2 》
《浅析armlinux-setup_arch()->convert_to_tag_list()函数3 》
《浅析armlinux-setup_arch()->bootmem_init()函数4》
《浅析armlinux-setup_arch()->paging_init()函数5》
《浅析armlinux-seup_arch-alloc_bootmem_low_pages函数5-1》
《浅析armlinux-setup_arch()-memtable_init()函数5-2》
《浅析armlinux-setup_arch()-clear_mapping()函数5-2-1》
《浅析armlinux-setup_arch()-create_mapping()函数5-2-2》
《我看Buddy(伙伴)算法-为什么要有除2操作》
《浅析armlinux-paging_init()->at91rm9200_map_io()函数5-3》
《浅析armlinux-paging_init()->free_area_init_core()函数5-4》
《浅析armlinux-setup_arch()->request_standard_resources()函数6》
《浅析armlinux-setup_arch()->init_arch_irq()函数7》

//1.arch/arm/kernel/Setup.c->setup_arch()
void __init setup_arch(char **cmdline_p)
{
    struct tag *tags = (struct tag *)&init_tags;
    struct machine_desc *mdesc;
    char *from = default_command_line;

    ROOT_DEV = MKDEV(0, 255);//ROOT_DEV = 0xff;
//见《浅析armlinux-setup_arch()->setup_processor()函数1》[http://gliethttp.cublog.cn]
    setup_processor();
//见《浅析armlinux-setup_arch()->setup_machine(machine_arch_type)函数2》[http://gliethttp.cublog.cn]
    mdesc = setup_machine(machine_arch_type);//mdesc指向机器描述空间单元
//++++++++++++++
//arch/arm/mach-at91rm9200/Core.c
//位于.arch.info段
//MACHINE_START(AT91RM9200, "ATMEL AT91RM9200")//.nr = MACH_TYPE_##_type=MACH_TYPE_AT91RM9200=251
//与上面从u-boot传到r1中的一样[gliethttp]
//MAINTAINER("SAN People / ATMEL")
//BOOT_MEM(AT91_SDRAM_BASE, AT91C_BASE_SYS, AT91C_VA_BASE_SYS)
//BOOT_PARAMS(AT91_SDRAM_BASE + 0x100)//tag list存放的物理地址0x20000100[gliethttp]
//FIXUP(at91rm9200_fixup)
//MAPIO(at91rm9200_map_io)
//INITIRQ(at91rm9200_init_irq)
//MACHINE_END[gliethttp]
//--------------
    machine_name = mdesc->name;//machine_name="ATMEL AT91RM9200"

    if (mdesc->soft_reboot)//mdesc->soft_reboot初值为1
        reboot_setup("s");
//分析见后

    if (mdesc->param_offset)//mdesc->param_offset=AT91_SDRAM_BASE + 0x100=物理地址0x20000100
        tags = phys_to_virt(mdesc->param_offset);//将tag list物理地址转换成虚拟地址,以便访问

    if (mdesc->fixup)//调用at91rm9200_fixup()修正函数,分析见后
        mdesc->fixup(mdesc, (struct param_struct *)tags,
             &from, &meminfo);
//很明显由bootloader[u-boot-1.1.5]传递到物理地址0x20000100处的参数是tag list结构[gliethttp]
    if (tags->hdr.tag != ATAG_CORE)
        convert_to_tag_list(tags);
//对于tag list的生成,请参考《浅析setup_arch()函数tag_list的uboot[u-boot]由来》[http://gliethttp.cublog.cn]

    if (tags->hdr.tag == ATAG_CORE) {
        if (meminfo.nr_banks != 0)//如果是tag list,那么如果系统已经创建了默认的meminfo.nr_banks
            squash_mem_tags(tags);//那么使用squash_mem_tags失效所有ATAG_MEM初始化的东东为ATAG_NONE,tag->hdr.size不变
        parse_tags(tags);
    }

    if (meminfo.nr_banks == 0) {//如果tag list中没有传递ATAG_MEM参数,那么采用编译时的默认配置值
        meminfo.nr_banks = 1;
        meminfo.bank[0].start = PHYS_OFFSET;//0x20000000
        meminfo.bank[0].size = MEM_SIZE;//32M
    }
//_text,_etext,_edata,_end参见arch/arm/vmlinux-armv.lds.in链接脚本
    init_mm.start_code = (unsigned long) &_text;
    init_mm.end_code = (unsigned long) &_etext;
    init_mm.end_data = (unsigned long) &_edata;
    init_mm.brk     = (unsigned long) &_end;

    memcpy(saved_command_line, from, COMMAND_LINE_SIZE);
    saved_command_line[COMMAND_LINE_SIZE-1] = '\0';//追0,防止非法字符串越界
//parse_cmdline,主要完成phys_initrd_start,phys_initrd_size和mem的解析,并将*cmdline_p=command_line全局量
    parse_cmdline(&meminfo, cmdline_p, from);
//将kernel自身和位图管理页占用的页对应的页位图置1,标识相应页已被占用
//将initrd占用的页对应的页位图置1,标识相应页已被占用[gliethttp]
    bootmem_init(&meminfo);
//清空页目录项,建立at91rm9200中断向量表空间和io寄存器空间从虚拟地址到物理地址的映射表
//建立map位图和page页管理空间,以及free_area.map位图管理空间等
    paging_init(&meminfo, mdesc);
    request_standard_resources(&meminfo, mdesc);//登记注册所有需要登记注册的cpu总线上的设备实体
    init_arch_irq = mdesc->init_irq;
//中断初始化函数指针

#ifdef CONFIG_VT
#if defined(CONFIG_VGA_CONSOLE)
    conswitchp = &vga_con;//显示设备指针conswitchp指向vga_con设备
#elif defined(CONFIG_DUMMY_CONSOLE)//frambuffer选中后,就会使用dummy_con设备
    conswitchp = &dummy_con;//显示设备指针conswitchp指向dummy_con设备
#endif
#endif
}
//2.arch/arm/kernel/Process.c->reboot_setup()
static char reboot_mode = 'h';
int __init reboot_setup(char *str)
{
//reboot_mode将在machine_restart()系统重启时arch_reset()使用
    reboot_mode = str[0];//reboot_mode='s';
    return 1;
}
//3.arch/arm/mach-at91rm9200/Core.c->at91rm9200_fixup
static void __init at91rm9200_fixup(struct machine_desc *desc, struct param_struct *unused,
         char **cmdline, struct meminfo *mi)
{
#ifdef CONFIG_BLK_DEV_INITRD
//CONFIG_BLK_DEV_INITRD = 1

//设置根目录为ramdisk
    ROOT_DEV = MKDEV(RAMDISK_MAJOR, 0);//0x100
//由make menuconfig产生CONFIG_BLK_DEV_RAM_SIZE = 15360k = 15M
    setup_ramdisk(1, 0, 0, CONFIG_BLK_DEV_RAM_SIZE);
//    setup_initrd(0xc0100000, 3*1024*1024);
#endif
}
void __init setup_ramdisk(int doload, int prompt, int image_start, unsigned int rd_sz)
{
#ifdef CONFIG_BLK_DEV_RAM
    extern int rd_size, rd_image_start, rd_prompt, rd_doload;
    rd_image_start = image_start;
    rd_prompt = prompt;
    rd_doload = doload;
    if (rd_sz)
        rd_size = rd_sz;//15M
#endif
}
//4.解析tag list中各个元素,进而改变内核全局量,来影响内核
//arch/arm/kernel/Setup.c->parse_tags()
static void __init parse_tags(const struct tag *t)
{
    for (; t->hdr.size; t = tag_next(t))
        if (!parse_tag(t))//解析该t
            printk(KERN_WARNING
                "Ignoring unrecognised tag 0x%08x\n",
                t->hdr.tag);
}
//5.arch/arm/kernel/Setup.c->parse_tag()
static int __init parse_tag(const struct tag *tag)
{
    extern struct tagtable __tagtable_begin, __tagtable_end;
    struct tagtable *t;
    for (t = &__tagtable_begin; t < &__tagtable_end; t++)
        if (tag->hdr.tag == t->tag) {
            t->parse(tag);
            break;
        }
    return t < &__tagtable_end;//找到匹配项,1;否则0
}
//arch/arm/vmlinux-armv.lds.in链接脚本中有如下定以
//...
//__tagtable_begin = .;
//*(.taglist)
//__tagtable_end = .;
//...
//在include/asm-arm/Setup.h中有如下定义
#define __tag __attribute__((unused, __section__(".taglist")))
#define __tagtable(tag, fn) \
static struct tagtable __tagtable_##fn __tag = { tag, fn }
__tagtable(ATAG_CORE, parse_tag_core);//ATAG_CORE解析函数parse_tag_core
__tagtable(ATAG_MEM, parse_tag_mem32);//ATAG_MEM解析函数parse_tag_mem32
__tagtable(ATAG_RAMDISK, parse_tag_ramdisk);//ATAG_RAMDISK解析函数parse_tag_ramdisk
__tagtable(ATAG_INITRD, parse_tag_initrd);//ATAG_INITRD解析函数parse_tag_initrd
__tagtable(ATAG_SERIAL, parse_tag_serialnr);//ATAG_SERIAL解析函数parse_tag_serialnr
__tagtable(ATAG_CMDLINE, parse_tag_cmdline);//ATAG_CMDLINE解析函数parse_tag_cmdline
......



浅析armlinux-setup_arch()->bootmem_init()函数4

浅析armlinux-setup_arch()->bootmem_init()函数4

文章来源:http://gliethttp.cublog.cn

建议首先参考《浅析armlinux2_4_19启动程序[head-armv.s文件][http://gliethttp.cublog.cn]


//----------------------------------------
//1.arch/arm/mm/Init.c->bootmem_init()
void __init bootmem_init(struct meminfo *mi)
{
    struct node_info node_info[NR_NODES], *np = node_info;
    unsigned int bootmap_pages, bootmap_pfn, map_pg;
    int node, initrd_node;
    //计算为了管理所有mem内存,需管理位图占据页数目bootmap_pages,np中存储mem对应的页帧号
    bootmap_pages = find_memend_and_nodes(mi, np);
    //查找存放位图管理页的物理页帧号,实际是存放到_end后的后续页中
    bootmap_pfn = find_bootmap_pfn(0, mi, bootmap_pages);
    //检查initrd的合法性,同时将initrd所在内存bank的node返回给initrd_node
    initrd_node = check_initrd(mi);
    map_pg = bootmap_pfn;//位图页帧号
    np += numnodes - 1;
    for (node = numnodes - 1; node >= 0; node--, np--) {
        
/*
         * If there are no pages in this node, ignore it.
         * Note that node 0 must always have some pages.
         */

        if (np->end == 0) {
            if (node == 0)
                BUG();
            continue;
        }
        //将map_pg开始的位图管理空间全部置0xff
        init_bootmem_node(NODE_DATA(node), map_pg, np->start, np->end);
        //释放虚拟地址node_bootmem_map开始的位图管理的所有页,使相应页可用[gliethttp]
        free_bootmem_node_bank(node, mi);
        map_pg += np->bootmap_pages;
        
/*
         * If this is node 0, we need to reserve some areas ASAP -
         * we may use bootmem on node 0 to setup the other nodes.
         */

        if (node == 0)//我的at91rm9200开发板仅仅有一个node=0
//将kernel自身和位图管理页占用的页对应的页位图置1,标识相应页已被占用
            reserve_node_zero(bootmap_pfn, bootmap_pages);
    }
#ifdef CONFIG_BLK_DEV_INITRD//在我的at91rm9200开发板中,initrd是开启的
//并且phys_initrd_start=0x21100000
//phys_initrd_size=6000000=0x5B8D80=5.73M
//initrd_node=0;
    if (phys_initrd_size && initrd_node >= 0) {
//将initrd占用的页对应的页位图置1,标识相应页已被占用
        reserve_bootmem_node(NODE_DATA(initrd_node), phys_initrd_start,
                 phys_initrd_size);
        initrd_start = __phys_to_virt(phys_initrd_start);//转成虚拟地址
        initrd_end = initrd_start + phys_initrd_size;
    }
#endif
    if (map_pg != bootmap_pfn + bootmap_pages)//保证所有bootmap都已经被遍历
        BUG();
}
//----------------------------------------
//2.arch/arm/mm/Init.c->find_memend_and_nodes()
static unsigned int __init find_memend_and_nodes(struct meminfo *mi, struct node_info *np)
{unsigned int i, bootmem_pages = 0, memend_pfn = 0;
    for (i = 0; i < NR_NODES; i++) {//默认失效
        np[i].start = -1U;
        np[i].end = 0;
        np[i].bootmap_pages = 0;
    }
    for (i = 0; i < mi->nr_banks; i++) {
        unsigned long start, end;
        int node;
        if (mi->bank[i].size == 0) {
            mi->bank[i].node = -1;//该bank的node无效-1
            continue;
        }
        node = mi->bank[i].node;//at91rm9200dk中mem连续node=0
        if (node >= numnodes) {
            numnodes = node + 1;
            if (numnodes > NR_NODES)
                BUG();
        }
//获取当前bank的pfns
//#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK)//页边界对齐
//define O_PFN_UP(x) (PAGE_ALIGN(x) >> PAGE_SHIFT)
        start = O_PFN_UP(mi->bank[i].start);//获取该bank.start对应物理页帧号
//同理,//获取该bank.end对应物理页帧号
        end = O_PFN_DOWN(mi->bank[i].start + mi->bank[i].size);
        if (np[node].start > start)
            np[node].start = start;//存储
        if (np[node].end < end)
            np[node].end = end;//存储
        if (memend_pfn < end)
            memend_pfn = end;
    }
    for (i = 0; i < numnodes; i++) {
        if (np[i].end == 0)
            continue;
//bootmem_bootmap_pages计算pages个页需要多少个页来存储其位图管理信息
        np[i].bootmap_pages = bootmem_bootmap_pages(np[i].end - np[i].start);
        bootmem_pages += np[i].bootmap_pages;//累计位图管理信息页总数
    }
    
/*
     * This doesn't seem to be used by the Linux memory
     * manager any more. If we can get rid of it, we
     * also get rid of some of the stuff above as well.
     */

    max_low_pfn = memend_pfn - O_PFN_DOWN(PHYS_OFFSET);
// max_pfn = memend_pfn - O_PFN_DOWN(PHYS_OFFSET);
    mi->end = memend_pfn << PAGE_SHIFT;//所管理的物理内存结束地址
    return bootmem_pages;
}
//----------------------------------------
//3.mm/Bootmem.c->bootmem_bootmap_pages()
//计算pages个页需要多少个页来存储其位图管理信息
unsigned long __init bootmem_bootmap_pages (unsigned long pages)
{unsigned long mapsize;
    mapsize = (pages+7)/8;//所需8bits个数
    mapsize = (mapsize + ~PAGE_MASK) & PAGE_MASK;//mapsize个字节页对齐
    mapsize >>= PAGE_SHIFT;//mapsize个字节对应页数目
    return mapsize;
}
//----------------------------------------
//4.arch/arm/mm/Init.c->find_bootmap_pfn()
//查找存放位图管理页的物理页帧号-init_mm.brk
static unsigned int __init
find_bootmap_pfn(int node, struct meminfo *mi, unsigned int bootmap_pages)
{unsigned int start_pfn, bank, bootmap_pfn;
    start_pfn = V_PFN_UP(&_end);//将_end虚拟内存转换为对应的物理页帧号
    bootmap_pfn = 0;
    for (bank = 0; bank < mi->nr_banks; bank ++) {
        unsigned int start, end;
        if (mi->bank[bank].node != node)
            continue;
        start = O_PFN_UP(mi->bank[bank]


点赞

评论 (0 个评论)

facelist

您需要登录后才可以评论 登录 | 注册

  • 关注TA
  • 加好友
  • 联系TA
  • 0

    周排名
  • 0

    月排名
  • 0

    总排名
  • 0

    关注
  • 3

    粉丝
  • 0

    好友
  • 20

    获赞
  • 69

    评论
  • 3705

    访问数
关闭

站长推荐 上一条 /2 下一条

小黑屋| 关于我们| 联系我们| 在线咨询| 隐私声明| EETOP 创芯网
( 京ICP备:10050787号 京公网安备:11010502037710 )

GMT+8, 2024-4-23 23:29 , Processed in 0.027324 second(s), 14 queries , Gzip On, Redis On.

eetop公众号 创芯大讲堂 创芯人才网