zoukankan      html  css  js  c++  java
  • head.s 简单分析

    linux 内核版本 2.6.27.29
     
    /*
    *  linux/arch/arm/kernel/head.S
    *  Kernel startup code for all 32-bit CPUs
    */
     
    #include <linux/linkage.h>
    #include <linux/init.h>
    #include <asm/assembler.h>
    #include <asm/domain.h>
    #include <asm/ptrace.h>
    #include <asm/asm-offsets.h>
    #include <asm/memory.h>
    #include <asm/thread_info.h>
    #include <asm/system.h>
     
    #if (PHYS_OFFSET & 0x001fffff)
    #error "PHYS_OFFSET must be at an even 2MiB boundary!"
    #endif
    //PAGE_OFFSET 内核空间的起始虚拟地址 0xc0000000   @ include/asm-arm/memeory.h//
    //TEXT_OFFSET 内核在RAM中起始位置相对于RAM起始地址的偏移 0x00008000 @ arch/arm/Makefile +131//
    //PHYS_OFFSET RAM的起始物理地址  平台相关 @ include/asm-arm/arch- *** /memory.h//
    #define KERNEL_RAM_VADDR    (PAGE_OFFSET + TEXT_OFFSET) 
    #define KERNEL_RAM_PADDR    (PHYS_OFFSET + TEXT_OFFSET)
    /*
    * swapper_pg_dir is the virtual address of the initial page table.
    * We place the page tables 16K below KERNEL_RAM_VADDR.  Therefore, we must
    * make sure that KERNEL_RAM_VADDR is correctly set.  Currently, we expect
    * the least significant 16 bits to be 0x8000, but we could probably
    * relax this restriction to KERNEL_RAM_VADDR >= PAGE_OFFSET + 0x4000.
    */
    #if (KERNEL_RAM_VADDR & 0xffff) != 0x8000
    #error KERNEL_RAM_VADDR must start at 0xXXXX8000
    #endif
        .globl    swapper_pg_dir
        .equ    swapper_pg_dir, KERNEL_RAM_VADDR - 0x4000
        .macro    pgtbl, rd
        ldr     d, =(KERNEL_RAM_PADDR - 0x4000)
        .endm
    #ifdef CONFIG_XIP_KERNEL
    #define KERNEL_START    XIP_VIRT_ADDR(CONFIG_XIP_PHYS_ADDR)
    #define KERNEL_END    _edata_loc
    #else
    #define KERNEL_START    KERNEL_RAM_VADDR  //0xc0008000
    #define KERNEL_END    _end
    #endif
    /*
    * 内核启动入口 stext,bootloader 执行完成后通过调用stext进入内核
    * ---------------------------
    *
    * This is normally called from the decompressor code.  The requirements
    * are: MMU = off, D-cache = off, I-cache = dont care, r0 = 0,
    * r1 = machine nr, r2 = atags pointer.
    *
    * This code is mostly position independent, so if you link the kernel at
    * 0xc0008000, you call this at __pa(0xc0008000).
    *
    * See linux/arch/arm/tools/mach-types for the complete list of machine
    * numbers for r1.
    *
    * We're trying to keep crap to a minimum; DO NOT add any machine specific
    * crap here - that's what the boot loader (or in extreme, well justified
    * circumstances, zImage) is for.
    */
        .section ".text.head", "ax"
        .type    stext, %function
    ENTRY(stext)
        msr    cpsr_c, #PSR_F_BIT | PSR_I_BIT | SVC_MODE @ ensure svc mode
                            @ and irqs disabled
    msr把通用寄存器的数据写入程序状态寄存器
    msr {cond} <SR>{<_><fsxc>},<Rn|immed>
    SR: CPSR, SPSR
    <fxsc>: f表示改写状态寄存器种的24-31bit
    s表示改写状态寄存器种的16-23bit
    x表示改写状态寄存器种的8-15bit
    c表示改写状态寄存器种的0-7bit
        mrc    p15, 0, r9, c0, c0        @ get processor id   将c0的值传给r9
    mrc将协处理器p15的内容传到寄存器中
    mrc {cond} <coproc>,<opcode_1>,<Rd>,<CRn>,<CRm>{,opcode_2}
    coproc 协处理器编号
    opcode_1表示协处理器的执行操作码
        bl    __lookup_processor_type        @ r5=procinfo r9=cpuid
     在  __proc_info_begin ~  __proc_info_end 这个段中寻找对应 cpuid(r9)的 struct proc_info_list 结构体内容找到之后将该结构体首地址存入r5 返回
        movs    r10, r5                @ invalid processor (r5=0)?
    movs <Rd>,<operand>
    将r5 赋给r10  movs 中的s表示执行结果影响cpsr中的标志位
        beq    __error_p            @ yes, error 'p'
        bl    __lookup_machine_type        @ r5=machinfo
        movs    r8, r5                @ invalid machine (r5=0)?
        beq    __error_a            @ yes, error 'a'
        bl    __vet_atags
    判断内核参数格式是否正确? 有疑问
        bl    __create_page_tables
        /*
         * The following calls CPU specific code in a position independent
         * manner.  See arch/arm/mm/proc-*.S for details.  r10 = base of
         * xxx_proc_info structure selected by __lookup_machine_type
         * above.  On return, the CPU will be ready for the MMU to be
         * turned on, and r0 will hold the CPU control register value.
         */
        ldr    r13, __switch_data        @ address to jump to after
                            @ mmu has been enabled
    下面的__enable_mmu调用  __turn_mmu_on, __turn_mmu_on执行完成后修改pc=r13
    ldr{cond}{h|b} Rd,<addr_mode>
    {h|b} h表示装载存储半字数据(16bits)并自动清除Rd的高16bits  b装载低8bits。。。
        adr    lr, __enable_mmu        @ return (PIC) address
    adr 伪指令  将__enable_mmu的地址赋值给lr寄存器
        add    pc, r10, #PROCINFO_INITFUNC        
    PROCINFO_INITFUNC 定义位于 /linux-2.6.27.29/arm/kernel/asm-offsets.c:DEFINE(PROCINFO_INITFUNC,    offsetof(struct proc_info_list, __cpu_flush));
    即程序跳转到对应cpu的 struct proc_info_list 结构体中的 __cpu_flush成员存储的函数地址去执行 对应cpu的结构体内容定义在arch/arm/mm/proc-(cpu型号).S中 对应465行
    r10存储的是从r5得到的procinfo的基地址, PROCINFO_INITFUNC是在 arch/arm/kernel/asm-offset.c 中107行定义. 该行将pc设为 proc_info_list的 __cpu_flush 对象的地址
    __cpu_flush 函数执行完成后跳转至lr地址处,即__enable_mmu函数
     
    以下代码SMP cpu 适用!!
    #if defined(CONFIG_SMP)
        .type   secondary_startup, #function
    ENTRY(secondary_startup)
        /*
         * Common entry point for secondary CPUs.
         *
         * Ensure that we're in SVC mode, and IRQs are disabled.  Lookup
         * the processor type - there is no need to check the machine type
         * as it has already been validated by the primary processor.
         */
        msr    cpsr_c, #PSR_F_BIT | PSR_I_BIT | SVC_MODE
        mrc    p15, 0, r9, c0, c0        @ get processor id
        bl    __lookup_processor_type
        movs    r10, r5                @ invalid processor?
        moveq    r0, #'p'            @ yes, error 'p'
        beq    __error
        /*
         * Use the page tables supplied from  __cpu_up.
         */
        adr    r4, __secondary_data
        ldmia    r4, {r5, r7, r13}        @ address to jump to after
        sub    r4, r4, r5            @ mmu has been enabled
        ldr    r4, [r7, r4]            @ get secondary_data.pgdir
        adr    lr, __enable_mmu        @ return address
        add    pc, r10, #PROCINFO_INITFUNC    @ initialise processor
                            @ (return control reg)
        /*
         * r6  = &secondary_data
         */
    ENTRY(__secondary_switched)
        ldr    sp, [r7, #4]            @ get secondary_data.stack
        mov    fp, #0
        b    secondary_start_kernel
        .type    __secondary_data, %object
    __secondary_data:
        .long    .
        .long    secondary_data
        .long    __secondary_switched
    #endif /* defined(CONFIG_SMP) */
    以上代码SMP cpu 适用!!
     
     
    /*
    * Setup common bits before finally enabling the MMU.  Essentially
    * this is just loading the page table pointer and domain access
    * registers.
    */
        .type    __enable_mmu, %function
    __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
    /*
    * Enable the MMU.  This completely changes the structure of the visible
    * memory space.  You will not be able to trace execution through this.
    * If you have an enquiry about this, *please* check the linux-arm-kernel
    * mailing list archives BEFORE sending another post to the list.
    *
    *  r0  = cp#15 control register
    *  r13 = *virtual* address to jump to upon completion
    *
    * other registers depend on the function called upon completion
    */
        .align    5
        .type    __turn_mmu_on, %function
    __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, r3
        mov    pc, r13
    /*
    * Setup the initial page tables.  We only setup the barest
    * amount which are required to get the kernel running, which
    * generally means mapping in the kernel code.
    *
    * r8  = machinfo
    * r9  = cpuid
    * r10 = procinfo
    *
    * Returns:
    *  r0, r3, r6, r7 corrupted
    *  r4 = physical page table address
    */
        .type    __create_page_tables, %function
    __create_page_tables:
        pgtbl    r4                @ page table address
    宏pgtbl 在 arch/arm/kernel/head.S 中定义
    可以看到,页表是位于 KERNEL_RAM_ADDR 下面 16k 的位置  
     /*
         * Clear the 16K level 1 swapper page table
         */
        mov    r0, r4
        mov    r3, #0
        add    r6, r0, #0x4000
    1:    str    r3, [r0], #4
        str    r3, [r0], #4
        str    r3, [r0], #4
        str    r3, [r0], #4
        teq    r0, r6
        bne    1b
    以上将这16k 的页表清0.
        ldr    r7, [r10, #PROCINFO_MM_MMUFLAGS] @ mm_mmuflags
     r10 对应struct proc_info_list 结构体对象首地址    
    获得proc_info_list的__cpu_mm_mmu_flags的值,并存储到 r7中
     /*
         * Create identity mapping for first MB of kernel to
         * cater for the MMU enable.  This identity mapping
         * will be removed by paging_init().  We use our current program
         * counter to determine corresponding section base address.
         */
        mov    r6, pc, lsr #20            @ start of kernel section
    将pc值逻辑右移20bit后的值赋给r6
        orr    r3, r7, r6, lsl #20        @ flags + kernel base
     r3 = r7 | (r6 << 20); flags + kernel base,得到页表中需要设置的值.
        str    r3, [r4, r6, lsl #2]        @ identity mapping
    设置页表: mem[r4 + r6 * 4] = r3,这里,因为页表的每一项是32 bits(4 bytes),所以要乘以4(<<2).
        /*
         * Now setup the pagetables for our kernel direct
         * mapped region.
         */
        add    r0, r4,  #(KERNEL_START & 0xff000000) >> 18
    KERNEL_START:0xc0008000 即KERNEL_RAM_VADDR 
        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 << 20
        strls    r3, [r0], #4
        bls    1b
    以上将KERNEL_START 到KERNEL_END 的内核代码的映射目录设置好
    #ifdef CONFIG_XIP_KERNEL
        /*
         * Map some ram to cover our .data and .bss areas.
         */
        orr    r3, r7, #(KERNEL_RAM_PADDR & 0xff000000)
        .if    (KERNEL_RAM_PADDR & 0x00f00000)
        orr    r3, r3, #(KERNEL_RAM_PADDR & 0x00f00000)
        .endif
        add    r0, r4,  #(KERNEL_RAM_VADDR & 0xff000000) >> 18
        str    r3, [r0, #(KERNEL_RAM_VADDR & 0x00f00000) >> 18]!
        ldr    r6, =(_end - 1)
        add    r0, r0, #4
        add    r6, r4, r6, lsr #18
    1:    cmp    r0, r6
        add    r3, r3, #1 << 20
        strls    r3, [r0], #4
        bls    1b
    #endif
        /*
         * Then map first 1MB of ram in case it contains our boot params.
         */
        add    r0, r4, #PAGE_OFFSET >> 18
        orr    r6, r7, #(PHYS_OFFSET & 0xff000000)
        .if    (PHYS_OFFSET & 0x00f00000)
        orr    r6, r6, #(PHYS_OFFSET & 0x00f00000)
        .endif
        str    r6, [r0]
    #ifdef CONFIG_DEBUG_LL
        ldr    r7, [r10, #PROCINFO_IO_MMUFLAGS] @ io_mmuflags
        /*
         * Map in IO space for serial debugging.
         * This allows debug messages to be output
         * via a serial console before paging_init.
         */
        ldr    r3, [r8, #MACHINFO_PGOFFIO]
        add    r0, r4, r3
        rsb    r3, r3, #0x4000            @ PTRS_PER_PGD*sizeof(long)
        cmp    r3, #0x0800            @ limit to 512MB
        movhi    r3, #0x0800
        add    r6, r0, r3
        ldr    r3, [r8, #MACHINFO_PHYSIO]
        orr    r3, r3, r7
    1:    str    r3, [r0], #4
        add    r3, r3, #1 << 20
        teq    r0, r6
        bne    1b
    #if defined(CONFIG_ARCH_NETWINDER) || defined(CONFIG_ARCH_CATS)
        /*
         * If we're using the NetWinder or CATS, we also need to map
         * in the 16550-type serial port for the debug messages
         */
        add    r0, r4, #0xff000000 >> 18
        orr    r3, r7, #0x7c000000
        str    r3, [r0]
    #endif
    #ifdef CONFIG_ARCH_RPC
        /*
         * Map in screen at 0x02000000 & SCREEN2_BASE
         * Similar reasons here - for debug.  This is
         * only for Acorn RiscPC architectures.
         */
        add    r0, r4, #0x02000000 >> 18
        orr    r3, r7, #0x02000000
        str    r3, [r0]
        add    r0, r4, #0xd8000000 >> 18
        str    r3, [r0]
    #endif
    #endif
        mov    pc, lr
        .ltorg
    #include "head-common.S"
     
     
     
     
     
  • 相关阅读:
    深入理解java虚拟机(7)---线程安全 & 锁优化
    深入理解java虚拟机(6)---内存模型与线程 & Volatile
    java注解框架
    敏捷软件开发---闲话敏捷
    深入理解java虚拟机(5)---字节码执行引擎
    龙应台写给儿子安德烈的信
    wojilu中的路由
    如何使用豆约翰博客备份专家批量下载新浪微博
    haha
    http://www.jsbreakouts.org/---各种html5框架实现打砖块游戏 breakout
  • 原文地址:https://www.cnblogs.com/linuxiaogang/p/8182810.html
Copyright © 2011-2022 走看看