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    \rd, =(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"
 
 
 
 
 
posted @ 2018-01-03 11:09  学号党  阅读(1215)  评论(0编辑  收藏  举报