linux 启动之setup_arch函数(一)

来源:互联网 发布:如何在淘宝开旗舰店 编辑:程序博客网 时间:2024/04/27 12:11
01void __init setup_arch(char **cmdline_p)02{03 struct tag *tags = (struct tag *)&init_tags;04 struct machine_desc *mdesc;05 char *from = default_command_line;0607 unwind_init();0809 setup_processor();10 mdesc = setup_machine(machine_arch_type);11 machine_name = mdesc->name;1213 if (mdesc->soft_reboot)14  reboot_setup("s");1516 if (__atags_pointer)17  tags = phys_to_virt(__atags_pointer);18 else if (mdesc->boot_params)19  tags = phys_to_virt(mdesc->boot_params);2021 /*22  * If we have the old style parameters, convert them to23  * a tag list.24  */25 if (tags->hdr.tag != ATAG_CORE)26  convert_to_tag_list(tags);27 if (tags->hdr.tag != ATAG_CORE)28  tags = (struct tag *)&init_tags;2930 if (mdesc->fixup)31  mdesc->fixup(mdesc, tags, &from, &meminfo);3233 if (tags->hdr.tag == ATAG_CORE) {34  if (meminfo.nr_banks != 0)35   squash_mem_tags(tags);36  save_atags(tags);37  parse_tags(tags);38 }3940 init_mm.start_code = (unsigned long) _text;41 init_mm.end_code   = (unsigned long) _etext;42 init_mm.end_data   = (unsigned long) _edata;43 init_mm.brk    = (unsigned long) _end;4445 memcpy(boot_command_line, from, COMMAND_LINE_SIZE);46 boot_command_line[COMMAND_LINE_SIZE-1] = '\0';47 parse_cmdline(cmdline_p, from);48 paging_init(mdesc);49 request_standard_resources(&meminfo, mdesc);5051#ifdef CONFIG_SMP52 smp_init_cpus();53#endif5455 cpu_init();5657 /*58  * Set up various architecture-specific pointers59  */60 init_arch_irq = mdesc->init_irq;61 system_timer = mdesc->timer;62 init_machine = mdesc->init_machine;6364#ifdef CONFIG_VT65#if defined(CONFIG_VGA_CONSOLE)66 conswitchp = &vga_con;67#elif defined(CONFIG_DUMMY_CONSOLE)68 conswitchp = &dummy_con;69#endif70#endif71 early_trap_init();72}73

第3行定义一个标记,它由一个tag_header结构和一个联合体组成。tag_header结构表示类型和长度,在这是分别是54410001,长度为5.联合体中的core的标志位为1,页的大小为4096,rootdev为255.联合体中mem内存的开始位置为0x1000,大小2。
第7行为空函数。
第9行建立当前处理器的信息,并初始化一些变量。
第10行建立当前开发平台的信息。
第11行机器的名字,这里SMDK2440
第12-31行,没有执行这些代码
第33-38行meminfo.nr_banks为0,save_atags是个空函数,parse_tags列出所有的标记。
第40行初始化第一个进程代码段的开始位置0x c002c000
第41行初始化第一个进程代码段的结束位置0x c03a2000
第42行初始化第一个进程数据段的结束位置0x c03c84e0
第43行初始化第一个进程堆的位置0x c03f55e4
第48行页表初始化
第49行资源的请求
第55行处理器的初始化,本开发平台只有一个处理器
第60行平台的中断函数来初始化全局的中断函数
第61行平台的timer函数来初始化全局的timer
第71行中断向量表初始化

01static void __init setup_processor(void)02{03 struct proc_info_list *list;0405 /*06  * locate processor in the list of supported processor07  * types.  The linker builds this table for us from the08  * entries in arch/arm/mm/proc-*.S09  */10 list = lookup_processor_type(read_cpuid_id());11 if (!list) {12  printk("CPU configuration botched (ID %08x), unable "13         "to continue.\n", read_cpuid_id());14  while (1);15 }1617 cpu_name = list->cpu_name;1819#ifdef MULTI_CPU20 processor = *list->proc;21#endif22#ifdef MULTI_TLB23 cpu_tlb = *list->tlb;24#endif25#ifdef MULTI_USER26 cpu_user = *list->user;27#endif28#ifdef MULTI_CACHE29 cpu_cache = *list->cache;30#endif3132 printk("CPU: %s [%08x] revision %d (ARMv%s), cr=%08lx\n",33        cpu_name, read_cpuid_id(), read_cpuid_id() & 15,34        proc_arch[cpu_architecture()], cr_alignment);3536 sprintf(init_utsname()->machine, "%s%c", list->arch_name, ENDIANNESS);37 sprintf(elf_platform, "%s%c", list->elf_name, ENDIANNESS);38 elf_hwcap = list->elf_hwcap;39#ifndef CONFIG_ARM_THUMB40 elf_hwcap &= ~HWCAP_THUMB;41#endif4243 cacheid_init();44 cpu_proc_init();45}

第3行定义一个proc_info_list结构体,编译的时候被放到了.proc.info段中,在vmlinux.lds中可以查到。
第10行返回的处理器的信息放到list结构体中。
第17行显示的CPU的名字为ARM920T。
第43行cache初始化。
第44行定义宏,放到.proc.info段中.

01void __init paging_init(struct machine_desc *mdesc)02{03 void *zero_page;0405 build_mem_type_table();06 sanity_check_meminfo();07 prepare_page_table();08 bootmem_init();09 devicemaps_init(mdesc);10 kmap_init();1112 top_pmd = pmd_off_k(0xffff0000);1314 /*15  * allocate the zero page.  Note that this always succeeds and16  * returns a zeroed result.17  */18 zero_page = alloc_bootmem_low_pages(PAGE_SIZE);19 empty_zero_page = virt_to_page(zero_page);20 flush_dcache_page(empty_zero_page);21}

在介绍这个函数,先介绍一下页表和mmu的一些知识,在虚拟地址转换成物理地址过程中用页号在页表中去查找对应的物理页,这是因为内存是按页划分的。
第3行定义一个void型的指针
第5行根据使用的CPU的类型来设置mem_type,本平台的CPU为ARM920T
第6行对内存的检查,主要检查一下,内存转化成虚拟地址后,会不会与高端内存重叠,是完全重叠还是部分重叠。
第7行页表建立前需要清除掉第一阶段创建的映射,主要清除内核映射以下的空间,页表空间及高端内存空间。
第8行物理内存映射,建立内存的分配的方法,在伙伴系统算法之前用这个方法分配内存。
第9行对设备建立映射
第10行高端永久内存映射
第12行最后的页表也是最高的页表地址,在这里是0xc0007ff8,其实pgd是从0xc0007000开始到0xc0008000结束的。
第18行使用alloc分配器分配4096个字节的空间

01static void __init sanity_check_meminfo(void)02{03 int i, j;0405 for (i = 0, j = 0; i < meminfo.nr_banks; i++) {06  struct membank *bank = &meminfo.bank[j];07  *bank = meminfo.bank[i];0809#ifdef CONFIG_HIGHMEM10  /*11   * Split those memory banks which are partially overlapping12   * the vmalloc area greatly simplifying things later.13   */14  if (__va(bank->start) < VMALLOC_MIN &&15      bank->size > VMALLOC_MIN - __va(bank->start)) {16   if (meminfo.nr_banks >= NR_BANKS) {17    printk(KERN_CRIT "NR_BANKS too low, "18       "ignoring high memory\n");19   } else if (cache_is_vipt_aliasing()) {20    printk(KERN_CRIT "HIGHMEM is not yet supported "21       "with VIPT aliasing cache, "22       "ignoring high memory\n");23   } else {24    memmove(bank + 1, bank,25     (meminfo.nr_banks - i) * sizeof(*bank));26    meminfo.nr_banks++;27    i++;28    bank[1].size -= VMALLOC_MIN - __va(bank->start);29    bank[1].start = __pa(VMALLOC_MIN - 1) + 1;30    j++;31   }32   bank->size = VMALLOC_MIN - __va(bank->start);33  }34#else35  /*36   * Check whether this memory bank would entirely overlap37   * the vmalloc area.38   */39  if (__va(bank->start) >= VMALLOC_MIN ||40      __va(bank->start) < (void *)PAGE_OFFSET) {41   printk(KERN_NOTICE "Ignoring RAM at %.8lx-%.8lx "42          "(vmalloc region overlap).\n",43          bank->start, bank->start + bank->size - 1);44   continue;45  }4647  /*48   * Check whether this memory bank would partially overlap49   * the vmalloc area.50   */51  if (__va(bank->start + bank->size) > VMALLOC_MIN ||52      __va(bank->start + bank->size) < __va(bank->start)) {53   unsigned long newsize = VMALLOC_MIN - __va(bank->start);54   printk(KERN_NOTICE "Truncating RAM at %.8lx-%.8lx "55          "to -%.8lx (vmalloc region overlap).\n",56          bank->start, bank->start + bank->size - 1,57          bank->start + newsize - 1);58   bank->size = newsize;59  }60#endif61  j++;62 }63 meminfo.nr_banks = j;64}

在内存的容量小于894M时,也会有高端内存,这个内存有什么作用,其实就是在分配容量很大的空间时,而内存不能一次满足,就会用这个vmalloc分配,如果你的内存是64M,哪么64M以上的,就是虚拟的高端内存。
第5行代码循环检查内存bank,本开发平台只有一个内存bank。没有用到高端内存,所以跳过IF语句
第39行检查内存的开始地址转化成虚拟地址是否和高端内存是否完全重合。
第40行检查内存是否部分和高端内存重合。

01static inline void prepare_page_table(void)02{03 unsigned long addr;0405 /*06  * Clear out all the mappings below the kernel image.07  */08 for (addr = 0; addr < MODULES_VADDR; addr += PGDIR_SIZE)09  pmd_clear(pmd_off_k(addr));1011#ifdef CONFIG_XIP_KERNEL12 /* The XIP kernel is mapped in the module area -- skip over it */13 addr = ((unsigned long)_etext + PGDIR_SIZE - 1) & PGDIR_MASK;14#endif15 for ( ; addr < PAGE_OFFSET; addr += PGDIR_SIZE)16  pmd_clear(pmd_off_k(addr));1718 /*19  * Clear out all the kernel space mappings, except for the first20  * memory bank, up to the end of the vmalloc region.21  */22 for (addr = __phys_to_virt(bank_phys_end(&meminfo.bank[0]));23      addr < VMALLOC_END; addr += PGDIR_SIZE)24  pmd_clear(pmd_off_k(addr));25}

这个函数主要是为建立内存的映射做准备工作,清除页目录项
第8-9行循环清除掉内核空间以下的映射。
第15-16行接着上面的地址开始清除。清除一级页表地址。
第22-24行清除的高端内存地址的映射。以直接映射的最后地址开始。

01void __init bootmem_init(void)02{03 struct meminfo *mi = &meminfo;04 unsigned long memend_pfn = 0;05 int node, initrd_node;0607 /*08  * Locate which node contains the ramdisk image, if any.09  */10 initrd_node = check_initrd(mi);1112 /*13  * Run through each node initialising the bootmem allocator.14  */15 for_each_node(node) {16  unsigned long end_pfn = bootmem_init_node(node, mi);1718  /*19   * Reserve any special node zero regions.20   */21  if (node == 0)22   reserve_node_zero(NODE_DATA(node));2324  /*25   * If the initrd is in this node, reserve its memory.26   */27  if (node == initrd_node)28   bootmem_reserve_initrd(node);2930  /*31   * Remember the highest memory PFN.32   */33  if (end_pfn > memend_pfn)34   memend_pfn = end_pfn;35 }3637 /*38  * sparse_init() needs the bootmem allocator up and running.39  */40 sparse_init();4142 /*43  * Now free memory in each node - free_area_init_node needs44  * the sparse mem_map arrays initialized by sparse_init()45  * for memmap_init_zone(), otherwise all PFNs are invalid.46  */47 for_each_node(node)48  bootmem_free_node(node, mi);4950 high_memory = __va((memend_pfn << PAGE_SHIFT) - 1) + 1;5152 /*53  * This doesn't seem to be used by the Linux memory manager any54  * more, but is used by ll_rw_block.  If we can get rid of it, we55  * also get rid of some of the stuff above as well.56  *57  * Note: max_low_pfn and max_pfn reflect the number of _pages_ in58  * the system, not the maximum PFN.59  */60 max_pfn = max_low_pfn = memend_pfn - PHYS_PFN_OFFSET;61}

第15行给指定的结点主节点创建映射,end_pfn为0x00034000.node为0,表明只有一块内存
第16行结束的页帧号,指的是直接能映射的最大的页帧号。
第22行保留不同类型的0结点
第33行代码memend_pfn内存结束页帧号,的值0x 00034000
第40行代码为空函数
第50行high_memory的值为0xc4000000,这个指的是内核直接影射的最后一个地址,也是高端内存的开始地址
第60行max_pfn为最后一个可用的页帧号的值为0x 00004000

static unsigned long __init bootmem_init_node(int node, struct meminfo *mi){                                                                          01 unsigned long start_pfn, end_pfn, boot_pfn;02  unsigned int boot_pages;                                                 03  pg_data_t *pgdat;                                                        04 int i;                                                                   05 start_pfn = -1UL;                                                        06 end_pfn = 0;                                                             07 /*                                                                       08  * Calculate the pfn range, and map the memory banks for this node.      09  */                                                                      10 for_each_nodebank(i, mi, node) {                                         11  struct membank *bank = &mi->bank[i];                                   12  unsigned long start, end;                                              13  start = bank_pfn_start(bank);                                 14  end = bank_pfn_end(bank);                                              15  if (start_pfn > start)                                                 16   start_pfn = start;                                                   17  if (end_pfn < end)                                                     18   end_pfn = end;                                                       19  map_memory_bank(bank);                                                 20 }                                                                        21 /*                                                                       22  * If there is no memory in this node, ignore it.                        23  */                                                                      24 if (end_pfn == 0)                                                        25  return end_pfn;                                                        26 /*                                                                       27  * Allocate the bootmem bitmap page.                                     28  */                                                                      29 boot_pages = bootmem_bootmap_pages(end_pfn - start_pfn);                 30 boot_pfn = find_bootmap_pfn(node, mi, boot_pages);                       31 /*                                                                       32  * Initialise the bootmem allocator for this node, handing the           33  * memory banks over to bootmem.                                         34  */                                                                      35 node_set_online(node);                                                   36 pgdat = NODE_DATA(node);                                                 37 init_bootmem_node(pgdat, boot_pfn, start_pfn, end_pfn);                  38 for_each_nodebank(i, mi, node) {                                         39  struct membank *bank = &mi->bank[i];                                   40  free_bootmem_node(pgdat, bank_phys_start(bank), bank_phys_size(bank)); 41  memory_present(node, bank_pfn_start(bank), bank_pfn_end(bank));        42 }                                                                        43 /*                                                                       44  * Reserve the bootmem bitmap for this node.                             45  */                                                                      46 reserve_bootmem_node(pgdat, boot_pfn << PAGE_SHIFT, boot_pages << PAGE_SHIFT, BOOTMEM_DEFAULT);                    48 return end_pfn;                                                          49}                                                                          50   

第1行定义分别是开始的页帧号,结束的页帧号  
第3行定义pg_data_t类型的指针对应一个结点。
第13行开始的页帧号
第14行结束的页帧号
第19行映射内存,下面会分析
第24行如果结束的页帧号为0,说明没有可用内存
第29行计算出需要多少页来存放bitmap ,下面会介绍
第30行查找存放位图的页帧号  
第36行NODE_DATA返回contig_page_data结构指针
第37行以每位都置为1初始化节点的位图
第40行标记出节点位图中的空闲内存
第46行为这个节点保留这个位图   
第48行返回结束的页帧号

01static inline void map_memory_bank(struct membank *bank)02{03#ifdef CONFIG_MMU04 struct map_desc map;0506 map.pfn = bank_pfn_start(bank);07 map.virtual = __phys_to_virt(bank_phys_start(bank));08 map.length = bank_phys_size(bank);09 map.type = MT_MEMORY;1011 create_mapping(&map);12#endif13}

平台的内存映射
第6行开始的页帧号
第7行转化后的虚拟地址
第8行内存的长度
第9行平台映射的类型,是内存还是设备,这里是内存。
第11行创建映射。

01void __init create_mapping(struct map_desc *md)02{03 unsigned long phys, addr, length, end;04 const struct mem_type *type;05 pgd_t *pgd;0607 if (md->virtual != vectors_base() && md->virtual < TASK_SIZE) {08  printk(KERN_WARNING "BUG: not creating mapping for "09         "0x%08llx at 0x%08lx in user region\n",10         __pfn_to_phys((u64)md->pfn), md->virtual);11  return;12 }1314 if ((md->type == MT_DEVICE || md->type == MT_ROM) &&15     md->virtual >= PAGE_OFFSET && md->virtual < VMALLOC_END) {16  printk(KERN_WARNING "BUG: mapping for 0x%08llx at 0x%08lx "17         "overlaps vmalloc space\n",18         __pfn_to_phys((u64)md->pfn), md->virtual);19 }2021 type = &mem_types[md->type];2223 /*24  * Catch 36-bit addresses25  */26 if (md->pfn >= 0x100000) {27  create_36bit_mapping(md, type);28  return;29 }3031 addr = md->virtual & PAGE_MASK;32 phys = (unsigned long)__pfn_to_phys(md->pfn);33 length = PAGE_ALIGN(md->length + (md->virtual & ~PAGE_MASK));3435 if (type->prot_l1 == 0 && ((addr | phys | length) & ~SECTION_MASK)) {36  printk(KERN_WARNING "BUG: map for 0x%08lx at 0x%08lx can not "37         "be mapped using pages, ignoring.\n",38         __pfn_to_phys(md->pfn), addr);39  return;40 }4142 pgd = pgd_offset_k(addr);43 end = addr + length;44 do {45  unsigned long next = pgd_addr_end(addr, end);4647  alloc_init_section(pgd, addr, next, phys, type);4849  phys += next - addr;50  addr = next;51 } while (pgd++, addr != end);52}

第7行检查平台的内存的虚拟地址是否中断向量的地址
第14行检查平台是否映射的内存
第26行如果内存的大小超过4G就启用36位地址
第42行pgd的开始地址为0xc0007000.
第43行结束地址,为开始地址屏蔽后12位加上长度。
第45行下一个要映射的地址
第47行使用段映射。
第49行映射的物理地址。
第51行步长以2M为单位