




已阅读5页,还剩16页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
ARM linux的启动部分源代码简略分析 ARM linux的启动部分源代码简略分析以友善之臂的mini2440开发板为平台,以较新的内核linux-版本为例,仅作说明之用。当内核映像被加载到RAM之后,Bootloader的控制权被释放。内核映像并不是可直接运行的目标代码,而是一个压缩过的zImage(小内核)。但是,也并非是zImage映像中的一切均被压缩了,映像中包含未被压缩的部分,这部分中包含解压缩程序,解压缩程序会解压缩映像中被压缩的部分。zImage使用gzip压缩的,它不仅仅是一个压缩文件,而且在这个文件的开头部分内嵌有gzip解压缩代码。当zImage被调用时它从arch/arm/boot/compressed/head.S的start汇编例程开始执行。这个例程进行一些基本的硬件设置,并调用arch/arm/boot/compressed/misc.c中的decompress_kernel()解压缩内核。arch/arm/kernel/head.S文件是内核真正的启动入口点,一般是由解压缩内核的程序来调用的。首先先看下对于运行这个文件的要求:MMU = off; D-cache = off; I-cache = 无所谓,开也可以,关也可以; r0 = 0;r1 = 机器号;r2 = atags 指针。这段代码是位置无关的,所以,如果以地址0xC0008000来链接内核,那么就可以直接用_pa(0xc0008000)地址来调用这里的代码。其实,在这个(Linux内核中总共有多达几十个的以head.S命名的文件)head.S文件中的一项重要工作就是设置内核的临时页表,不然mmu开起来也玩不转,但是内核怎么知道如何映射内存呢?linux的内核将映射到虚地址0xCxxxxxxx处,但他怎么知道在4GB的地址空间中有哪一片ram是可用的,从而可以映射过去呢?因为不同的系统有不通的内存映像,所以,LINUX约定,要调用内核代码,一定要满足上面的调用要求,以为最初的内核代码提供一些最重要的关于机器的信息。内核代码开始的时候,R1存放的是系统目标平台的代号,对于一些常见的,标准的平台,内核已经提供了支持,只要在编译的时候选中就行了,例如对X86平台,内核是从物理地址1M开始映射的。好了好了,看下面的代码。arch/arm/kernel/head.SENTRY(stext)是这个文件的入口点。最初的几行是这样的: setmode PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9 ensure svc mode and irqs disabled/ 设置为SVC模式,关闭中断和快速中断/ 此处设定系统的工作状态为SVC,arm有7种状态每种状态/ 都有自己的堆栈,SVC为管理模式,具有完全的权限,可以执行任意指令/ 访问任意地址的内存/ setmode是一个宏,其定义为:/ .macro setmode, mode, reg/ msr cpsr_c, #mode/ .endm mrc p15, 0, r9, c0, c0 get processor id bl _lookup_processor_type r5=procinfo r9=cpuid movs r10, r5 invalid processor (r5=0)? beq _error_p yes, error p这几行是查询处理器的类型的,我们知道arm系列有很多型号,arm7、arm9、arm11、Cortex核等等类型,这么多型号要如何区分呢?其实,在arm的15号协处理器(其实ARM暂时也就这么一个协处理器)中有一个只读寄存器,存放与处理器相关信息。_lookup_processor_type是arch/arm/kernel/head-common.S文件中定义的一个例程,这个head-common.S用include命令被包含在head.S文件中。其定义为:_lookup_processor_type: adr r3, 3f ldmia r3, r5 - r7 add r3, r3, #8 sub r3, r3, r7 get offset between virt&phys add r5, r5, r3 convert virt addresses to add r6, r6, r3 physical address space1: ldmia r5, r3, r4 value, mask and r4, r4, r9 mask wanted bits teq r3, r4 beq 2f add r5, r5, #PROC_INFO_SZ sizeof(proc_info_list) cmp r5, r6 blo 1b mov r5, #0 unknown processor2: mov pc, lrENDPROC(_lookup_processor_type)这个例程接受处理器ID(保存在寄存器r9中)为参数,查找链接器建立的支持的处理器表。此时此刻还不能使用_proc_info表的绝对地址,因为这时候MMU还没有开启,所以此时运行的程序没有在正确的地址空间中。所以不得不计算偏移量。若没有找到processor ID对应的处理器,则在r5寄存器中返回返回0,否则返回一个proc_info_list结构体的指针(在物理地址空间)。proc_info_list结构体在文件中定义:struct proc_info_list unsigned int cpu_val; unsigned int cpu_mask; unsigned long _cpu_mm_mmu_flags; /* used by head.S */ unsigned long _cpu_io_mmu_flags; /* used by head.S */ unsigned long _cpu_flush; /* used by head.S */ const char *arch_name; const char *elf_name; unsigned int elf_hwcap; const char *cpu_name; struct processor *proc; struct cpu_tlb_fns *tlb; struct cpu_user_fns *user; struct cpu_cache_fns *cache;第一项是CPUid,将与协处理器中读出的id作比较,其余的字段也都是与处理器相关的信息,到下面初始化的过程中自然会用到。另外,这个例程加载符地址的代码也是挺值得我辈学习的: adr r3, 3f加载一个符号的地址,这个符号在加载语句前面(下面)定义,forward嘛,这个符号为3,离这条语句最近的那个。在那个符号为3的位置我们看到这样的代码: .align 23: .long _proc_info_begin .long _proc_info_end4: .long . .long _arch_info_begin .long _arch_info_end搜索这两个符号的值,在文件arch/arm/kernel/vmlinux.lds.S中: _proc_info_begin = .; *(..init) _proc_info_end = .;这两个符号分别是一种初始化的段的结束开始地址和结束地址。为了了解由struct proc_info_list结构体组成的段的实际构成,我们还是得要了解一下在系统中到底都有哪些变量是声明了要被放到这个段的。用关键字..init来搜,全部都是arch/arm/mm/proc-*.S文件,这些都是特定于处理器的汇编语言文件,对于我们的mini2440, 自然是要看proc-arm920.S文件的,在其中可以看到这些内容: .section ..init, #alloc, #execinstr .type _arm920_proc_info,#object_arm920_proc_info: .long 0x41009200 .long 0xff00fff0 .long PMD_TYPE_SECT | PMD_SECT_BUFFERABLE | PMD_SECT_CACHEABLE | PMD_BIT4 | PMD_SECT_AP_WRITE | PMD_SECT_AP_READ .long PMD_TYPE_SECT | PMD_BIT4 | PMD_SECT_AP_WRITE | PMD_SECT_AP_READ b _arm920_setup .long cpu_arch_name .long cpu_elf_name .long HWCAP_SWP | HWCAP_HALF | HWCAP_THUMB .long cpu_arm920_name .long arm920_processor_functions .long v4wbi_tlb_fns .long v4wb_user_fns#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH .long arm920_cache_fns#else .long v4wt_cache_fns#endif .size _arm920_proc_info, . - _arm920_proc_info看到这儿我们再回国头去看_lookup_processor_type的代码: ldmia r3, r5 - r7 add r3, r3, #8 sub r3, r3, r7尽管符号3处只有两个有效值,但它加载了三个数,而第三个数,我们看到是这样定义的:.long ._lookup_processor_type中,给r3加上8,也就是让r3指向“.”的地址,然后用r3减r7来获取虚拟地址与物理地址的差,这样看来,“.”就应该是虚拟空间(编译地址)里那个数据的地址。之后的代码获得_proc_info_begin和_arch_info_end这两个符号在物理空间中的地址: add r5, r5, r3 convert virt addresses to add r6, r6, r3然后便是在那个段中逐个的检查struct proc_info_list结构体,以找到与我们的CPU相匹配的:1: ldmia r5, r3, r4 value, mask and r4, r4, r9 mask wanted bits teq r3, r4 beq 2f add r5, r5, #PROC_INFO_SZ sizeof(proc_info_list) cmp r5, r6 blo 1b mov r5, #0 unknown processor2: mov pc, lr_lookup_processor_type例程会返回在文件arch/arm/mm/proc-arm920.S中定义的一个保存有与我们的处理器相关的信息的struct proc_info_list结构体的地址。接下来我们继续看stext的代码: bl _lookup_machine_type r5=machinfo movs r8, r5 invalid machine (r5=0)? beq _error_a yes, error a在获得了处理器信息之后,则调用_lookup_machine_type来查找机器信息。这个例程同样也在arch/arm/kernel/head-common.S文件中定义。这个例程的定义如下:_lookup_machine_type: adr r3, 4b ldmia r3, r4, r5, r6 sub r3, r3, r4 get offset between virt&phys add r5, r5, r3 convert virt addresses to add r6, r6, r3 physical address space1: ldr r3, r5, #MACHINFO_TYPE get machine type teq r3, r1 matches loader number? beq 2f found add r5, r5, #SIZEOF_MACHINE_DESC next machine_desc cmp r5, r6 blo 1b mov r5, #0 unknown machine2: mov pc, lrENDPROC(_lookup_machine_type)处理的过程和上面的_lookup_processor_type还是挺相似的。这个例程接收r1中传进来的机器号作为参数,然后,在一个由struct machine_desc结构体组成的段中查找和我们的机器号匹配的struct machine_desc结构体,这个结构体在arch/arm/include/asm/mach/arch.h文件中定义,用于保存机器的信息:struct machine_desc /* * Note! The first four elements are used * by assembler code in head.S, head-common.S */ unsigned int nr; /* architecture number */ unsigned int phys_io; /* start of physical io */ unsigned int io_pg_offst; /* byte offset for io * page tabe entry */ const char *name; /* architecture name */ unsigned long boot_params; /* tagged list */ unsigned int video_start; /* start of video RAM */ unsigned int video_end; /* end of video RAM */ unsigned int reserve_lp0 :1; /* never has lp0 */ unsigned int reserve_lp1 :1; /* never has lp1 */ unsigned int reserve_lp2 :1; /* never has lp2 */ unsigned int soft_reboot :1; /* soft reboot */ void (*fixup)(struct machine_desc *, struct tag *, char *, struct meminfo *); void (*map_io)(void);/* IO mapping function */ void (*init_irq)(void); struct sys_timer *timer; /* system tick timer */ void (*init_machine)(void);同样这个例程也用到了同上面很相似的方式来获得符号的地址: adr r3, 4bb代表back,即向后,这个符号为4,紧接着我们前面看到的那个为3的标号:4: .long . .long _arch_info_begin .long _arch_info_end在文件arch/arm/kernel/vmlinux.lds.S中我们可以看到段的定义: _arch_info_begin = .; *(..init) _arch_info_end = .;这两个符号也是分别表示某种初始化的段的开始地址和结束地址。为了找到段的填充内容,还是得要了解一下到底都有哪些struct machine_desc结构体类型变量声明了要被放到这个段的。用关键字..init 来搜索所有的内核源文件。在arch/arm/include/asm/mach/arch.h文件中我们看到:#define MACHINE_START(_type,_name) static const struct machine_desc _mach_desc_#_type _used _attribute_(_section_(..init) = .nr = MACH_TYPE_#_type, .name = _name,#define MACHINE_END ; 定义机器结构体,也就是..init段中的内容,都是要通过两个宏MACHINE_START和MACHINE_END来完成的啊,MACHINE_START宏定义一个truct machine_desc结构体,并初始化它的机器号字段和机器名字段,可以在arch/arm/tools/mach-types文件中看到各种平台的机器号的定义。那接着我们来搜MACHINE_START吧,这是一个用于定义机器结构体的宏,所以可以看到这个符号好像都是在arch/arm/mach-*/mach-*.c这样的文件中出现的,我们感兴趣的应该是arch/arm/mach-s3c2440/ mach-mini2440.c文件中的这个符号:MACHINE_START(MINI2440, MINI2440) /* Maintainer: Michel Pollet */ .phys_io = S3C2410_PA_UART, .io_pg_offst = (u32)S3C24XX_VA_UART) 18) & 0xfffc, .boot_params = S3C2410_SDRAM_PA + 0x100, .map_io = mini2440_map_io, .init_machine = mini2440_init, .init_irq = s3c24xx_init_irq, .timer = &s3c24xx_timer,MACHINE_ENDOK, _lookup_machine_type这个例程的我们也搞明白了。回忆一下,启动代码现在已经完成的工作,R10寄存器中为指向proc_info_list结构体的指针(物理地址空间),这个结构体包含有关于我们的处理器的一些重要信息。R8寄存器中为指向一个与我们的平台相匹配的machine_desc结构体的指针,这个结构体中保存有一些关于我们的平台的重要信息。回来接着看arch/arm/kernel/head.S文件中的stext: bl _vet_atags这个例程同样同样也是在arch/arm/kernel/head-common.S文件中定义:_vet_atags: tst r2, #0x3 aligned? bne 1f ldr r5, r2, #0 is first tag ATAG_CORE? cmp r5, #ATAG_CORE_SIZE cmpne r5, #ATAG_CORE_SIZE_EMPTY bne 1f ldr r5, r2, #4 ldr r6, =ATAG_CORE cmp r5, r6 bne 1f mov pc, lr atag pointer is ok1: mov r2, #0 mov pc, lrENDPROC(_vet_atags)这个例程接收机器信息(R8寄存器)为参数,并检测r2中传入的ATAGS 指针的合法性。内核使用tag来作为bootloader传递内核参数的方式。系统要求r2中传进来的ATAGS指针式4字节对齐的,同时要求ATAGS列表的第一个tag是一个ATAG_CORE类型的。此时R10寄存器中保存有指向CPU信息结构体的指针,R8寄存器中保存有指向机器结构体的指针,R2寄存器中保存有指向tag表的指针,R9中还保存有CPU ID信息。回到arch/arm/kernel/head.S文件中的stext,之后就要进入初始化过程中比较关键的一步了,开始设置mmu,但首先要填充一个临时的内核页表,映射4m的内存,这在初始化过程中是足够了:bl _create_page_tables这个例程设置初始页表,这里只设置最起码的数量,只要能使内核运行即可,r8 = machinfo,r9 = cpuid,r10 = procinfo,在r4寄存器中返回物理页表地址。_create_page_tables例程在文件arch/arm/kernel/head.S中定义:_create_page_tables: pgtbl r4 page table address/ pgtbl是一个宏,本文件的前面部分有定义:/ .macro pgtbl, rd/ ldr rd, =(KERNEL_RAM_PADDR - 0x4000)/ .endm/ KERNEL_RAM_PADDR在本文件的前面有定义,为(PHYS_OFFSET + TEXT_OFFSET)/ PHYS_OFFSET在arch/arm/mach-s3c2410/include/mach/memory.h定义,/ 为UL(0x30000000)/ 而TEXT_OFFSET在arch/arm/Makefile中定义,为内核镜像在内存中到内存/ 开始位置的偏移(字节),为$(textofs-y)/ textofs-y也在文件arch/arm/Makefile中定义,/ 为textofs-y := 0x00008000/ r4 = 30004000为临时页表的起始地址/ 首先即是初始化16K的页表,高12位虚拟地址为页表索引,所以为/ 4K*4 = 16K,大页表,每一个页表项,映射1MB虚拟地址。/ 这个地方还来了个循环展开,以优化性能。 mov r0, r4 mov r3, #0 add r6, r0, #0x40001: str r3, r0, #4 str r3, r0, #4 str r3, r0, #4 str r3, r0, #4 teq r0, r6 bne 1b ldr r7, r10, #PROCINFO_MM_MMUFLAGS mm_mmuflags/ PROCINFO_MM_MMUFLAGS在arch/arm/kernel/asm-offsets.c文件中定义,/ 为DEFINE(PROCINFO_MM_MMUFLAGS, / offsetof(struct proc_info_list, _cpu_mm_mmu_flags);/ R10寄存器保存的指针指向是我们前面找到的proc_info_list结构嘛。/ 为内核的第一个MB创建一致的映射,以为打开MMU做准备,这个映射将会被 / paging_init()移除,这里使用程序计数器来获得相应的段的基地址。/ 这个地方是直接映射。 mov r6, pc mov r6, r6, lsr #20 start of kernel section orr r3, r7, r6, lsl #20 flags + kernel base str r3, r4, r6, lsl #2 identity mapping/ 接下来为内核的直接映射区设置页表。KERNEL_START在文件的前面定义,/ 为KERNEL_RAM_VADDR,即内核的虚拟地址。/ 而KERNEL_RAM_VADDR在文件的前面定义,则为(PAGE_OFFSET + TEXT_OFFSET)/ 映射完整的内核代码段,初始化数据段。/ PAGE_OFFSET为内核镜像开始的虚拟地址,在/ arch/arm/include/asm/memory.h中定义。在配置内核时选定具体值,默认/ 为0xC0000000。/ 因为最高12位的值是页表中的偏移地址,而第三高的四位必然为0, / 每个页表项为4字节,右移20位之后,还得再左移两位回来,所以,这里只/ 是左移18位。/ R3寄存器在经过了上面的操作之后,实际上是变成了指向内核镜像代码段/ 的指针(物理地址),在这个地方,再一次为内核镜像的第一个MB做了映射。/ R6随后指向了内核镜像的尾部。R0为页表项指针。/ 这里以1MB为单位来映射内核镜像。 add r0, r4, #(KERNEL_START & 0xff000000) 18 str r3, r0, #(KERNEL_START & 0x00f00000) 18! ldr r6, =(KERNEL_END - 1) add r0, r0, #4 add r6, r4, r6, lsr #18 /得到页表的结束物理地址1: cmp r0, r6 add r3, r3, #1 18 orr r6, r7, #(PHYS_OFFSET & 0xff000000) .if (PHYS_OFFSET & 0x00f00000) orr r6, r6, #(PHYS_OFFSET & 0x00f00000) .endif str r6, r0/ 上面的这个步骤显得似乎有些多余。/ 总结一下,这个建立临时页表的过程:/ 1、为内核镜像的第一个MB建立直接映射/ 2、为内核镜像完整的建立从虚拟地址到物理地址的映射/ 3、为物理内存的第一个MB建立到内核的虚拟地址空间的第一个MB的映射。/ OK,内核的临时页表建立完毕。整个初始化临时页表的过程都没有修改R8,/ R9和R10。 mov pc, lrENDPROC(_create_page_tables)回到stext: ldr r13, _switch_data address to jump to after mmu has been enabled这个地方实际上是在r13中保存了另一个例程的地址。后面的分析中,遇到执行到这个例程的情况时会有详细说明。接着看stext: adr lr, BSYM(_enable_mmu) return (PIC) addressBSYM()是一个宏,在文件arch/arm/include/asm/unified.h中定义,为:#define BSYM(sym) sym也就是说这个语句也仅仅是把_enable_mmu例程的地址加载进lr寄存器中。为了方便之后调用的函数返回时,直接执行_enable_mmu例程。接着看stext下一句:ARM( add pc, r10, #PROCINFO_INITFUNC )ARM()也是一个宏,同样在文件arch/arm/include/asm/unified.h中定义,当配置内核为生成ARM镜像,则为:#define ARM(x.) x所以这一条语句也就是在调用一个例程。R10中保存的是procinfo结构的地址。PROCINFO_INITFUNC符号在arch/arm/kernel/asm-offsets.c文件中定义,为:DEFINE(PROCINFO_INITFUNC, offsetof(struct proc_info_list, _cpu_flush);也就是调用结构体proc_info_list的_cpu_flush成员函数。回去查看arch/arm/mm/proc-arm920.S文件中struct proc_info_list结构体的变量的定义,可以看到这个成员为: b _arm920_setup也就是说,在设置好内核临时页表之后调用了例程_arm920_setup,这个例程同样在arch/arm/mm/proc-arm920.S中:_arm920_setup: mov r0, #0 mcr p15, 0, r0, c7, c7 invalidate I,D caches on v4 mcr p15, 0, r0, c7, c10, 4 drain write buffer on v4#ifdef CONFIG_MMU mcr p15, 0, r0, c8, c7 invalidate I,D TLBs on v4#endif adr r5, arm920_crval ldmia r5, r5, r6 mrc p15, 0, r0, c1, c0 get control register v4 bic r0, r0, r5 orr r0, r0, r6 mov pc, lr这一段首先使i,dcaches内容无效,然后清除writebuffer,接着使TLB内容无效。接下来加载变量arm920_crval的地址,我们看到arm920_crval变量的内容为:rm920_crval: crval clear=0x00003f3f, mmuset=0x00003135, ucset=0x00001130crval为一个宏,在arch/arm/mm/proc-macros.S中定义: .macro crval, clear, mmuset, ucset#ifdef CONFIG_MMU .word clear .word mmuset#else .word clear .word ucset#endif .endm其实也就是定义两个变量而已。之后,在r0中,得到了我们想要往协处理器相应寄存器中写入的内容。之后的 _arm920_setup返回,mov pc, lr,即是调用例程_enable_mmu,这个例程在文件arch/arm/kernel/head.S中:_enable_mmu:#ifdef CONFIG_ALIGNMENT_TRAP orr r0, r0, #CR_A#else bic r0, r0, #CR_A#endif#ifdef CONFIG_CPU_DCACHE_DISABLE bic r0, r0, #CR_C#endif#ifdef CONFIG_CPU_BPREDICT_DISABLE bic r0, r0, #CR_Z#endif#ifdef CONFIG_CPU_ICACHE_DISABLE bic r0, r0, #CR_I#endif mov r5, #(domain_val(DOMAIN_USER, DOMAIN_MANAGER) | domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) | domain_val(DOMAIN_TABLE, DOMAIN_MANAGER) | domain_val(DOMAIN_IO, DOMAIN_CLIENT) mcr p15, 0, r5, c3, c0, 0 load domain access register mcr p15, 0, r4, c2, c0, 0 load page table pointer b _turn_mmu_on在这儿设置了页目录地址(r4寄存器中保存),然后设置domain的保护,在前面建立页表的例程中,注意到,页表项的控制信息,是从struct proc_info_list结构体的某字段中取的,其页目录项的 domain都是0,domain寄存器中的domain0对应的是0b11,表示访问模式为manager,不受限制。在这里同时也完成r0的某些位的进一步设置。然后,_enable_mmu例程又调用了_turn_mmu_on,在同一个文件中定义:_turn_mmu_on: mov r0, r0 mcr p15, 0, r0, c1, c0, 0 write control reg mrc p15, 0, r3, c0, c0, 0 read id reg mov r3, r3 mov r3, r13 mov pc, r3ENDPROC(_turn_mmu_on)接下来写控制寄存器:mcrp15,0,r0,c1,c0,0一切设置就此生效,到此算是完成了打开d,icache和mmu的工作。注意:arm的dcache必须和mmu一起打开,而icache可以单独打开。其实,cache和mmu的关系实在是紧密,每一个页表项都有标志标示是否是cacheable的,可以说本来就是设计一起使用的前面有提到过,r13中存放的其实是另外一个例程的地址,其值是变量_switch_data的第一个字段,即一个函数指针的值,_switch_data变量是在arch/arm/kernel/head-common.S中定义的:_switch_data: .long _mmap_switched .long _data_loc r4 .long _data r5 .long _bss_start r6 .long _end r7 .long processor_id r4 .long _machine_arch_type r5 .long _atags_pointer r6 .long cr_alignment r7 .long init_thread_union + THREAD_START_SP sp前面的ldr r13 _switch_data,实际上也就是加载符号_mmap_switched的地址,实际上_mmap_switched是一个arch/arm/kernel/head-common.S中定义的例程。接着来看这个例程的定义,在arch/arm/kernel/head-common.S文件中:_mmap_switched: adr r3, _switch_data + 4 ldmia r3!, r4, r5, r6, r7 cmp r4, r5 Copy data segment if needed1: cmpne r5, r6 ldrne fp, r4, #4 strne fp, r5, #4 bne 1b mov fp, #0 Clear BSS (and zero fp)1: cmp r6, r7 strcc fp, r6,#4 bcc 1b ldmia r3, r4, r5, r6, r7, sp str r9, r4 Save processor ID str r1, r5 Save machine type str r2, r6 Save atags pointer bic r4, r0, #CR_A Clear A bit stmia r7, r0, r4 Save control register values b start_kernelENDPROC(_mmap_switched)这个例程完成如下工作:1、使r3指向_switch_data变量的第二个字段(从1开始计数)。2、执行了一条加载指令,也就是在r4, r5, r6, r7寄存器中分别加载4个符号_data_loc,_data, _bss_start ,_end的地址,这四个符号都是在链接脚本arch/arm/kernel/vmlinux.lds.S中出现的,标识了镜像各个段的地址,我们应该不难猜出他们所代表的段。3、如果需要的话则复制数据段(数据段和BSS段是紧邻的)。4、初始化BSS段,全部清零,BSS是未初始化的全局变量区域。5、又看到一条加载指令,同样在一组寄存器中加载借个符号的地址,r4中为processor_id,r5中为_machine_arch_type, r6中为_atags_pointer, r7中为cr_alignment ,sp中为init_thread_union + THREAD_START_SP。6、接着我们看到下面的几条语句,则是用前面获取的信息来初始化那些全局变量r9,机器号被保存到processor_id处;r1寄存器的值,机器号,被保存到变量_machine_arch_type中,其他的也一样。7、重新设置堆栈指针,指向init_task的堆栈。init_task是系统的第一个任务,init_task的堆栈在taskstructure的后8K,我们后面会看到。8、最后就要跳到C代码的 start_kernel。 b start_kernel到此为止,汇编部分的初始化代码就结束了O,My God.初始化代码的汇编部分终于结束。从而进入了与体系结构无
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 四川省广元市川师大万达中学2025-2026学年高二上学期第一次月考(8月)历史试题(含答案)
- 2025年中国蕃茄牛肉米线数据监测报告
- 课件时长的确定
- 锅炉(承压)设备焊工基础考核试卷及答案
- 铁合金回转窑工质量管控考核试卷及答案
- 巧克力塑形师工艺创新考核试卷及答案
- 课件无广告原因
- 拜耳法溶出工成本预算考核试卷及答案
- 2025年中国猪皮二层箱包革数据监测报告
- 金属牙齿考试题及答案
- 二年级语文上册《有趣的动物》课件PPT
- 不干胶贴标机设计学士学位论文
- 《劳动合同书》-河南省人力资源和社会保障厅劳动关系处监制(2016.11.15)
- 钢轨检测报告
- 战略管理:概念与案例
- GB/T 3505-2009产品几何技术规范(GPS)表面结构轮廓法术语、定义及表面结构参数
- GB/T 11186.1-1989涂膜颜色的测量方法第一部分:原理
- 09S304 卫生设备安装图集
- 功能材料概论-课件
- 微纳加工课件
- 危重病人紧急气道管理课件
评论
0/150
提交评论