Linux内核源码分析--内核启动之(2)Image内核启动(汇编部分)(Linux-3.0 ARMv7) 【转】

简介: 转自:http://blog.chinaunix.net/uid-25909619-id-4938389.html  在完成了zImage自解压之后,就跳转到了解压后的内核(也就是vmlinux的bin版本Image),具体的入口可以在arch/arm/kernel/vmlinux.

转自:http://blog.chinaunix.net/uid-25909619-id-4938389.html 

在完成了zImage自解压之后,就跳转到了解压后的内核(也就是vmlinux的bin版本Image),具体的入口可以在arch/arm/kernel/vmlinux.lds.S(最终的链接脚本是通过这个文件产生的)中获得:

 

  1. ......
  2. SECTIONS
  3. {
  4. #ifdef CONFIG_XIP_KERNEL
  5. . = XIP_VIRT_ADDR(CONFIG_XIP_PHYS_ADDR);
  6. #else
  7. . = PAGE_OFFSET + TEXT_OFFSET;
  8. #endif
  9. .init : { /* Init code and data */
  10. _stext = .;
  11. _sinittext = .;
  12. ......

 

    这个入口在arch/arm/kernel/head.S中,这个文件就是Linux内核真正启动的地方,是初始化部分的开始,用汇编写成。他必须为后面的C代码做好准备,下面先给出程序的流程图,后面是中文注释的代码。
 
    这里有一些宏定义必须知道他的含义:

出现的位置

默认值

定义

KERNEL_RAM_ADDR

arch/arm/kernel/head.S

0xC0008000

内核在内存中的虚拟地址

PAGE_OFFSET

arch/arm/include/asm/memory.h 

0xC0000000

内核虚拟地址空间的起始地址

TEXT_OFFSET

arch/arm/Makefile

0x00008000

内核起始位置相对于内存起始位置的偏移

PHYS_OFFSET

 

arch/arm/include/asm/memory.h 

 

构架相关

物理内存的起始地址

 
 

arch/arm/kernel/head.S

  1. /*
  2.  * linux/arch/arm/kernel/head.S
  3.  *
  4.  * Copyright (C) 1994-2002 Russell King
  5.  * Copyright (c) 2003 ARM Limited
  6.  * All Rights Reserved
  7.  *
  8.  * This program is free software; you can redistribute it and/or modify
  9.  * it under the terms of the GNU General Public License version 2 as
  10.  * published by the Free Software Foundation.
  11.  *
  12.  * 所有32-bit CPU的内核启动代码
  13.  */
  14. #include <linux/linkage.h>
  15. #include <linux/init.h>
  16. #include <asm/assembler.h>
  17. #include <asm/domain.h>
  18. #include <asm/ptrace.h>
  19. #include <asm/asm-offsets.h>
  20. #include <asm/memory.h>
  21. #include <asm/thread_info.h>
  22. #include <asm/system.h>
  23. #ifdef CONFIG_DEBUG_LL
  24. #include <mach/debug-macro.S>
  25. #endif
  26.  
  27. /*
  28.  * swapper_pg_dir 是初始页表的虚拟地址.
  29.  * 我们将页表放在KERNEL_RAM_VADDR以下16K的空间中. 因此我们必须保证
  30.  * KERNEL_RAM_VADDR已经被正常设置. 当前, 我们期望的是
  31.  * 这个地址的最后16 bits为0x8000, 但我们或许可以放宽这项限制到
  32.  * KERNEL_RAM_VADDR >= PAGE_OFFSET + 0x4000.
  33.  */
  34. #define KERNEL_RAM_VADDR    (PAGE_OFFSET + TEXT_OFFSET)
  35. #if (KERNEL_RAM_VADDR & 0xffff) != 0x8000
  36. #error KERNEL_RAM_VADDR must start at 0xXXXX8000
  37. #endif
  38.     .globl    swapper_pg_dir
  39.     .equ    swapper_pg_dir, KERNEL_RAM_VADDR - 0x4000
  40. /*
  41.  * TEXT_OFFSET 是内核代码(解压后)相对于RAM起始的偏移.
  42.  * 而#TEXT_OFFSET - 0x4000就是页表相对于RAM起始的偏移.
  43.  * 这个宏的作用是将phys(RAM的启示地址)加上页表的偏移,
  44.  * 而得到页表的起始物理地址
  45.  */
  46.     .macro    pgtbl, rd, phys
  47.     add    \rd, \phys, #TEXT_OFFSET - 0x4000
  48.     .endm
  49. #ifdef CONFIG_XIP_KERNEL
  50. #define KERNEL_START    XIP_VIRT_ADDR(CONFIG_XIP_PHYS_ADDR)
  51. #define KERNEL_END    _edata_loc
  52. #else
  53. #define KERNEL_START    KERNEL_RAM_VADDR
  54. #define KERNEL_END    _end
  55. #endif
  56. /*
  57.  * 内核启动入口点.
  58.  * ---------------------------
  59.  *
  60.  * 这个入口正常情况下是在解压完成后被调用的.
  61.  * 调用条件: MMU = off, D-cache = off, I-cache = dont care, r0 = 0,
  62.  * r1 = machine nr, r2 = atags or dtb pointer.
  63.  * 这些条件在解压完成后会被逐一满足,然后才跳转过来。
  64.  *
  65.  * 这些代码大多数是位置无关的, 如果你的内核入口地址在连接时确定为
  66.  * 0xc0008000, 你调用此函数的物理地址就是 __pa(0xc0008000).
  67.  *
  68.  * 完整的machineID列表,请参见 linux/arch/arm/tools/mach-types
  69.  *
  70.  * 我们尽量让代码简洁; 不在此处添加任何设备特定的代码
  71.  * - 这些特定的初始化代码是boot loader的工作(或在极端情况下,
  72.  * 有充分理由的情况下, 可以由zImage完成)。
  73.  */
  74.     __HEAD
  75. ENTRY(stext)
  76.     setmode    PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9 @ CPU模式设置宏
  77.                                                     @ (进入svc模式并且关闭中断)
  78.     mrc    p15, 0, r9, c0, c0                       @ 获取处理器id-->r9
  79.     bl    __lookup_processor_type                   @ 返回r5=procinfo r9=cpuid
  80.     movs    r10, r5                                 @ r10=r5,并可以检测r5=0?注意当前r10的值
  81.  THUMB( it    eq )            @ force fixup-able long branch encoding
  82.     beq    __error_p            @ yes, error 'p'如果r5=0,则内核处理器不匹配,出错~死循环
  83.     /*
  84.      * 获取RAM的起始物理地址,并保存于 r8 = phys_offset
  85.      * XIP内核与普通在RAM中运行的内核不同
  86.      * (1)CONFIG_XIP_KERNEL
  87.      *         通过运行时计算????
  88.      * (2)正常RAM中运行的内核
  89.      *         通过编译时确定(PLAT_PHYS_OFFSET 一般在arch/arm/mach-xxx/include/mach/memory.h定义)
  90.      *        
  91.      */
  92. #ifndef CONFIG_XIP_KERNEL
  93.     adr    r3, 2f
  94.     ldmia    r3, {r4, r8}
  95.     sub    r4, r3, r4            @ (PHYS_OFFSET - PAGE_OFFSET)
  96.     add    r8, r8, r4            @ PHYS_OFFSET
  97. #else
  98.     ldr    r8, =PLAT_PHYS_OFFSET
  99. #endif
  100.     /*
  101.      * r1 = machine no, r2 = atags or dtb,
  102.      * r8 = phys_offset, r9 = cpuid, r10 = procinfo
  103.      */
  104.     bl    __vet_atags            @ 判断r2(内核启动参数)指针的有效性
  105. #ifdef CONFIG_SMP_ON_UP
  106.     bl    __fixup_smp            @ ???如果运行SMP内核在单处理器系统中启动,做适当调整
  107. #endif
  108. #ifdef CONFIG_ARM_PATCH_PHYS_VIRT
  109.     bl    __fixup_pv_table    @ ????根据内核在内存中的位置修正物理地址与虚拟地址的转换机制
  110. #endif
  111.     bl    __create_page_tables    @ 初始化页表!
  112.     /*
  113.      * 以下使用位置无关的方法调用的是CPU特定代码。
  114.      * 详情请见arch/arm/mm/proc-*.S
  115.      * r10 = xxx_proc_info 结构体的基地址(在上面__lookup_processor_type函数中选中的)
  116.      * 返回时, CPU 已经为 MMU 的启动做好了准备,
  117.      * 且 r0 保存着CPU控制寄存器的值.
  118.      */
  119.     ldr    r13, =__mmap_switched                @ 在MMU启动之后跳入的第一个虚拟地址
  120.     adr    lr, BSYM(1f)                        @ 设置返回的地址(PIC)
  121.     mov    r8, r4                                @ 将swapper_pg_dir的物理地址放入r8,
  122.                                             @ 以备__enable_mmu中将其放入TTBR1
  123.  ARM(    add    pc, r10, #PROCINFO_INITFUNC    )    @ 跳入构架相关的初始化处理器函数(例如A8的是__v7_setup)
  124.  THUMB(    add    r12, r10, #PROCINFO_INITFUNC    )    @主要目的只配置CP15(包括缓存配置)
  125.  THUMB(    mov    pc, r12                )
  126. 1:    b    __enable_mmu                        @ 启动MMU
  127. ENDPROC(stext)
  128.     .ltorg
  129. #ifndef CONFIG_XIP_KERNEL
  130. 2:    .long    .
  131.     .long    PAGE_OFFSET
  132. #endif
  133. /*
  134.  * 创建初始化页表. 我们只创建最基本的页表,
  135.  * 以满足内核运行的需要,
  136.  * 这通常意味着仅映射内核代码本身.
  137.  *
  138.  * r8 = phys_offset, r9 = cpuid, r10 = procinfo
  139.  *
  140.  * 返回:
  141.  * r0, r3, r5-r7 被篡改
  142.  * r4 = 页表物理地址
  143.  */
  144. __create_page_tables:
  145.     pgtbl    r4, r8                @ 现在r4 = 页表的起始物理地址
  146.     /*
  147.      * 清零16K的一级初始页表区
  148.      * 这些页表在内核自解压时被设置过
  149.      * (此时MMU已关闭)
  150.      */
  151.     mov    r0, r4
  152.     mov    r3, #0
  153.     add    r6, r0, #0x4000
  154. 1:    str    r3, [r0], #4
  155.     str    r3, [r0], #4
  156.     str    r3, [r0], #4
  157.     str    r3, [r0], #4
  158.     teq    r0, r6
  159.     bne    1b
  160.     /*
  161.      * 获取节描述符的默认配置(除节基址外的其他配置)
  162.      * 这个数据依构架而不同,数据是用汇编文件配置的:
  163.      * arch/arm/mm/proc-xxx.S
  164.      * (此时MMU已关闭)
  165.      */
  166.     ldr    r7, [r10, #PROCINFO_MM_MMUFLAGS] @ 获取mm_mmuflags(节描述符默认配置),保存于r7
  167.     /*
  168.      * 创建特定映射,以满足__enable_mmu的需求。
  169.      * 此特定映射将被paging_init()删除。
  170.      * 
  171.      * 其实这个特定的映射就是仅映射__enable_mmu功能函数区的页表
  172.      * 以保证在启用mmu时代码的正确执行--1:1映射(物理地址=虚拟地址)
  173.      */
  174.     adr    r0, __enable_mmu_loc
  175.     ldmia    r0, {r3, r5, r6}
  176.     sub    r0, r0, r3            @ 获取编译时确定的虚拟地址到当前物理地址的偏移
  177.     add    r5, r5, r0            @ __enable_mmu的当前物理地址
  178.     add    r6, r6, r0            @ __enable_mmu_end的当前物理地址
  179.     mov    r5, r5, lsr #20        @ __enable_mmu的节基址
  180.     mov    r6, r6, lsr #20        @ __enable_mmu_end的节基址
  181. 1:    orr    r3, r7, r5, lsl #20        @ 生成节描述符:flags + 节基址
  182.     str    r3, [r4, r5, lsl #2]    @ 设置节描述符,1:1映射(物理地址=虚拟地址)
  183.     teq    r5, r6                    @ 完成映射?(理论上一次就够了,这个函数应该不会大于1M吧~)
  184.     addne    r5, r5, #1            @ r5 = 下一节的基址
  185.     bne    1b
  186.     /*
  187.      * 现在创建内核的逻辑映射区页表(节映射)
  188.      * 创建范围:KERNEL_START---KERNEL_END
  189.      * KERNEL_START:内核最终运行的虚拟地址
  190.      * KERNEL_END:内核代码结束的虚拟地址(bss段之后,但XIP不是)
  191.      */
  192.     mov    r3, pc                @ 获取当前物理地址
  193.     mov    r3, r3, lsr #20        @ r3 = 当前物理地址的节基址
  194.     orr    r3, r7, r3, lsl #20    @ r3 为当前物理地址的节描述符
  195.     /*
  196.      * 下面是为了确定页表项的入口地址
  197.      * 其实页表入口项的偏移就反应了对应的虚拟地址的高位
  198.      *
  199.      * 由于ARM指令集的8bit位图问题,只能分两次得到
  200.      * KERNEL_START:内核最终运行的虚拟地址
  201.      *
  202.      */
  203.     add    r0, r4, #(KERNEL_START & 0xff000000) >> 18
  204.     str    r3, [r0, #(KERNEL_START & 0x00f00000) >> 18]!
  205.     ldr    r6, =(KERNEL_END - 1)
  206.     add    r0, r0, #4
  207.     add    r6, r4, r6, lsr #18    @ r6 = 内核逻辑映射结束的节基址
  208. 1:    cmp    r0, r6
  209.     add    r3, r3, #1 << 20    @ 生成节描述符(只需做基址递增)
  210.     strls    r3, [r0], #4    @ 设置节描述符
  211.     bls    1b
  212. #ifdef CONFIG_XIP_KERNEL
  213.     /*
  214.      * 如果是XIP技术的内核,上面的映射只能映射内核代码和只读数据部分
  215.      * 这里我们再映射一些RAM来作为 .data and .bss 空间.
  216.      */
  217.     add    r3, r8, #TEXT_OFFSET
  218.     orr    r3, r3, r7            @ 生成节描述符:flags + 节基址
  219.     add    r0, r4, #(KERNEL_RAM_VADDR & 0xff000000) >> 18
  220.     str    r3, [r0, #(KERNEL_RAM_VADDR & 0x00f00000) >> 18]!
  221.     ldr    r6, =(_end - 1)
  222.     add    r0, r0, #4
  223.     add    r6, r4, r6, lsr #18
  224. 1:    cmp    r0, r6
  225.     add    r3, r3, #1 << 20
  226.     strls    r3, [r0], #4
  227.     bls    1b
  228. #endif
  229.     /*
  230.      * 然后映射启动参数区(现在r2中的atags物理地址) 
  231.      * 或者
  232.      * 如果启动参数区的虚拟地址没有确定(或者无效),则会映射RAM的头1MB.
  233.      */
  234.     mov    r0, r2, lsr #20
  235.     movs    r0, r0, lsl #20
  236.     moveq    r0, r8                @ 如果atags指针无效,则r0 = r8(映射RAM的头1MB)
  237.     sub    r3, r0, r8
  238.     add    r3, r3, #PAGE_OFFSET    @ 转换为虚拟地址
  239.     add    r3, r4, r3, lsr #18        @ 确定页表项(节描述符)入口地址
  240.     orr    r6, r7, r0                @ 生成节描述符
  241.     str    r6, [r3]                @ 设置节描述符
  242.     /*
  243.      * 下面是调试信息的输出函数区
  244.      * 这里做了IO内存空间的节映射
  245.      */
  246. #ifdef CONFIG_DEBUG_LL
  247. #ifndef CONFIG_DEBUG_ICEDCC
  248.     /*
  249.      * 为串口调试映射IO内存空间(将串口IO内存之上的所有地址都映射了)
  250.      * 这允许调试信息(在paging_init之前)从串口控制台输出
  251.      * 
  252.      */
  253.     addruart r7, r3        @ 宏代码,位于arch/arm/mach-xxx/include/mach/debug-macro.S
  254.                         @ 作用是将串口控制寄存器的基址放入r7(物理地址)和r3(虚拟地址)
  255.     mov    r3, r3, lsr #20
  256.     mov    r3, r3, lsl #2
  257.     add    r0, r4, r3        @ r0为串口IO内存映射页表项的入口地址
  258.     rsb    r3, r3, #0x4000            @ 16K(PTRS_PER_PGD*sizeof(long))-r3
  259.     cmp    r3, #0x0800            @ limit to 512MB,入口地址有效性检查(只能在最后#0x0800内)
  260.     movhi    r3, #0x0800        @ 也就是说虚拟地址被限制在3.5G以上
  261.     add    r6, r0, r3            @ r6为页表结束地址
  262.     mov    r3, r7, lsr #20
  263.     ldr    r7, [r10, #PROCINFO_IO_MMUFLAGS] @ io_mmuflags
  264.     orr    r3, r7, r3, lsl #20    @ 生成节描述符
  265. 1:    str    r3, [r0], #4
  266.     add    r3, r3, #1 << 20
  267.     teq    r0, r6
  268.     bne    1b
  269. #else /* CONFIG_DEBUG_ICEDCC */
  270.     /* 我们无需任何串口调试映射 for ICEDCC */
  271.     ldr    r7, [r10, #PROCINFO_IO_MMUFLAGS] @ io_mmuflags
  272. #endif /* !CONFIG_DEBUG_ICEDCC */
  273. #if defined(CONFIG_ARCH_NETWINDER) || defined(CONFIG_ARCH_CATS)
  274.     /*
  275.      * 如果我们在使用 NetWinder 或 CATS,我们也需要为调试信息映射
  276.      * 16550-type 串口
  277.      */
  278.     add    r0, r4, #0xff000000 >> 18
  279.     orr    r3, r7, #0x7c000000
  280.     str    r3, [r0]
  281. #endif
  282. #ifdef CONFIG_ARCH_RPC
  283.     /*
  284.      * Map in screen at 0x02000000 & SCREEN2_BASE
  285.      * Similar reasons here - for debug. This is
  286.      * only for Acorn RiscPC architectures.
  287.      */
  288.     add    r0, r4, #0x02000000 >> 18
  289.     orr    r3, r7, #0x02000000
  290.     str    r3, [r0]
  291.     add    r0, r4, #0xd8000000 >> 18
  292.     str    r3, [r0]
  293. #endif
  294. #endif
  295.     mov    pc, lr        @页表创建结束,返回
  296. ENDPROC(__create_page_tables)
  297.     .ltorg
  298.     .align
  299. __enable_mmu_loc:
  300.     .long    .
  301.     .long    __enable_mmu
  302.     .long    __enable_mmu_end
  303. #if defined(CONFIG_SMP)
  304.     __CPUINIT
  305. ENTRY(secondary_startup)
  306.     /*
  307.      * Common entry point for secondary CPUs.
  308.      *
  309.      * Ensure that we're in SVC mode, and IRQs are disabled. Lookup
  310.      * the processor type - there is no need to check the machine type
  311.      * as it has already been validated by the primary processor.
  312.      */
  313.     setmode    PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9
  314.     mrc    p15, 0, r9, c0, c0        @ get processor id
  315.     bl    __lookup_processor_type
  316.     movs    r10, r5                @ invalid processor?
  317.     moveq    r0, #'p'            @ yes, error 'p'
  318.  THUMB( it    eq )        @ force fixup-able long branch encoding
  319.     beq    __error_p
  320.     /*
  321.      * Use the page tables supplied from __cpu_up.
  322.      */
  323.     adr    r4, __secondary_data
  324.     ldmia    r4, {r5, r7, r12}        @ address to jump to after
  325.     sub    lr, r4, r5            @ mmu has been enabled
  326.     ldr    r4, [r7, lr]            @ get secondary_data.pgdir
  327.     add    r7, r7, #4
  328.     ldr    r8, [r7, lr]            @ get secondary_data.swapper_pg_dir
  329.     adr    lr, BSYM(__enable_mmu)        @ return address
  330.     mov    r13, r12            @ __secondary_switched address
  331.  ARM(    add    pc, r10, #PROCINFO_INITFUNC    ) @ initialise processor
  332.                          @ (return control reg)
  333.  THUMB(    add    r12, r10, #PROCINFO_INITFUNC    )
  334.  THUMB(    mov    pc, r12                )
  335. ENDPROC(secondary_startup)
  336.     /*
  337.      * r6 = &secondary_data
  338.      */
  339. ENTRY(__secondary_switched)
  340.     ldr    sp, [r7, #4]            @ get secondary_data.stack
  341.     mov    fp, #0
  342.     b    secondary_start_kernel
  343. ENDPROC(__secondary_switched)
  344.     .align
  345.     .type    __secondary_data, %object
  346. __secondary_data:
  347.     .long    .
  348.     .long    secondary_data
  349.     .long    __secondary_switched
  350. #endif /* defined(CONFIG_SMP) */
  351. /*
  352.  * 在最后启动MMU前,设置一些常用位 Essentially
  353.  * 其实,这里只是加载了页表指针和域访问控制数据寄存器
  354.  * 
  355.  *
  356.  * r0 = cp#15 control register
  357.  * r1 = machine ID
  358.  * r2 = atags or dtb pointer
  359.  * r4 = page table pointer
  360.  * r9 = processor ID
  361.  * r13 = 最后要跳入的虚拟地址
  362.  */
  363. __enable_mmu:
  364. #ifdef CONFIG_ALIGNMENT_TRAP
  365.     orr    r0, r0, #CR_A
  366. #else
  367.     bic    r0, r0, #CR_A
  368. #endif
  369. #ifdef CONFIG_CPU_DCACHE_DISABLE
  370.     bic    r0, r0, #CR_C
  371. #endif
  372. #ifdef CONFIG_CPU_BPREDICT_DISABLE
  373.     bic    r0, r0, #CR_Z
  374. #endif
  375. #ifdef CONFIG_CPU_ICACHE_DISABLE
  376.     bic    r0, r0, #CR_I
  377. #endif
  378.     mov    r5, #(domain_val(DOMAIN_USER, DOMAIN_MANAGER) | \
  379.          domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) | \
  380.          domain_val(DOMAIN_TABLE, DOMAIN_MANAGER) | \
  381.          domain_val(DOMAIN_IO, DOMAIN_CLIENT))    @设置域访问控制数据
  382.     mcr    p15, 0, r5, c3, c0, 0        @ 载入域访问控制数据到DACR
  383.     mcr    p15, 0, r4, c2, c0, 0        @ 载入页表基址到TTBR0
  384.     b    __turn_mmu_on                @ 开启MMU
  385. ENDPROC(__enable_mmu)
  386. /*
  387.  * 使能 MMU. 这完全改变了可见的内存地址空间结构。
  388.  * 您将无法通过这里跟踪执行。
  389.  * 如果你已对此进行探究, *请*在向邮件列表发送另一个新帖之前,
  390.  * 检查linux-arm-kernel的邮件列表归档
  391.  *
  392.  * r0 = cp#15 control register
  393.  * r1 = machine ID
  394.  * r2 = atags or dtb pointer
  395.  * r9 = processor ID
  396.  * r13 = 最后要跳入的*虚拟*地址
  397.  *
  398.  * 其他寄存器依赖上面的调用函数
  399.  */
  400.     .align    5
  401. __turn_mmu_on:
  402.     mov    r0, r0
  403.     mcr    p15, 0, r0, c1, c0, 0        @ 设置cp#15控制寄存器(启用MMU)
  404.     mrc    p15, 0, r3, c0, c0, 0        @ read id reg
  405.     mov    r3, r3
  406.     mov    r3, r13                        @ r3中装入最后要跳入的*虚拟*地址
  407.     mov    pc, r3                        @ 跳转到__mmap_switched
  408. __enable_mmu_end:
  409. ENDPROC(__turn_mmu_on)
  410. #ifdef CONFIG_SMP_ON_UP
  411.     __INIT
  412. __fixup_smp:
  413.     and    r3, r9, #0x000f0000    @ architecture version
  414.     teq    r3, #0x000f0000        @ CPU ID supported?
  415.     bne    __fixup_smp_on_up    @ no, assume UP
  416.     bic    r3, r9, #0x00ff0000
  417.     bic    r3, r3, #0x0000000f    @ mask 0xff00fff0
  418.     mov    r4, #0x41000000
  419.     orr    r4, r4, #0x0000b000
  420.     orr    r4, r4, #0x00000020    @ val 0x4100b020
  421.     teq    r3, r4            @ ARM 11MPCore?
  422.     moveq    pc, lr            @ yes, assume SMP
  423.     mrc    p15, 0, r0, c0, c0, 5    @ read MPIDR
  424.     and    r0, r0, #0xc0000000    @ multiprocessing extensions and
  425.     teq    r0, #0x80000000        @ not part of a uniprocessor system?
  426.     moveq    pc, lr            @ yes, assume SMP
  427. __fixup_smp_on_up:
  428.     adr    r0, 1f
  429.     ldmia    r0, {r3 - r5}
  430.     sub    r3, r0, r3
  431.     add    r4, r4, r3
  432.     add    r5, r5, r3
  433.     b    __do_fixup_smp_on_up
  434. ENDPROC(__fixup_smp)
  435.     .align
  436. 1:    .word    .
  437.     .word    __smpalt_begin
  438.     .word    __smpalt_end
  439.     .pushsection .data
  440.     .globl    smp_on_up
  441. smp_on_up:
  442.     ALT_SMP(.long    1)
  443.     ALT_UP(.long    0)
  444.     .popsection
  445. #endif
  446.     .text
  447. __do_fixup_smp_on_up:
  448.     cmp    r4, r5
  449.     movhs    pc, lr
  450.     ldmia     {r0, r6}
  451.  ARM(    str    r6, [r0, r3]    )
  452.  THUMB(    add    r0, r0, r3    )
  453. #ifdef __ARMEB__
  454.  THUMB(    mov    r6, r6, ror #16    )    @ Convert word order for big-endian.
  455. #endif
  456.  THUMB(    strh    r6, [r0], #2    )    @ For Thumb-2, store as two halfwords
  457.  THUMB(    mov    r6, r6, lsr #16    )    @ to be robust against misaligned r3.
  458.  THUMB(    strh    r6, [r0]    )
  459.     b    __do_fixup_smp_on_up
  460. ENDPROC(__do_fixup_smp_on_up)
  461. ENTRY(fixup_smp)
  462.     stmfd     {r4 - r6, lr}
  463.     mov    r4, r0
  464.     add    r5, r0, r1
  465.     mov    r3, #0
  466.     bl    __do_fixup_smp_on_up
  467.     ldmfd     {r4 - r6, pc}
  468. ENDPROC(fixup_smp)
  469. #ifdef CONFIG_ARM_PATCH_PHYS_VIRT
  470. /* __fixup_pv_table - patch the stub instructions with the delta between
  471.  * PHYS_OFFSET and PAGE_OFFSET, which is assumed to be 16MiB aligned and
  472.  * can be expressed by an immediate shifter operand. The stub instruction
  473.  * has a form of '(add|sub) rd, rn, #imm'.
  474.  */
  475.     __HEAD
  476. __fixup_pv_table:
  477.     adr    r0, 1f
  478.     ldmia    r0, {r3-r5, r7}
  479.     sub    r3, r0, r3    @ PHYS_OFFSET - PAGE_OFFSET
  480.     add    r4, r4, r3    @ adjust table start address
  481.     add    r5, r5, r3    @ adjust table end address
  482.     add    r7, r7, r3    @ adjust __pv_phys_offset address
  483.     str    r8, [r7]    @ save computed PHYS_OFFSET to __pv_phys_offset
  484. #ifndef CONFIG_ARM_PATCH_PHYS_VIRT_16BIT
  485.     mov    r6, r3, lsr #24    @ constant for add/sub instructions
  486.     teq    r3, r6, lsl #24 @ must be 16MiB aligned
  487. #else
  488.     mov    r6, r3, lsr #16    @ constant for add/sub instructions
  489.     teq    r3, r6, lsl #16    @ must be 64kiB aligned
  490. #endif
  491. THUMB(    it    ne        @ cross section branch )
  492.     bne    __error
  493.     str    r6, [r7, #4]    @ save to __pv_offset
  494.     b    __fixup_a_pv_table
  495. ENDPROC(__fixup_pv_table)
  496.     .align
  497. 1:    .long    .
  498.     .long    __pv_table_begin
  499.     .long    __pv_table_end
  500. 2:    .long    __pv_phys_offset
  501.     .text
  502. __fixup_a_pv_table:
  503. #ifdef CONFIG_THUMB2_KERNEL
  504. #ifdef CONFIG_ARM_PATCH_PHYS_VIRT_16BIT
  505.     lsls    r0, r6, #24
  506.     lsr    r6, #8
  507.     beq    1f
  508.     clz    r7, r0
  509.     lsr    r0, #24
  510.     lsl    r0, r7
  511.     bic    r0, 0x0080
  512.     lsrs    r7, #1
  513.     orrcs r0, #0x0080
  514.     orr    r0, r0, r7, lsl #12
  515. #endif
  516. 1:    lsls    r6, #24
  517.     beq    4f
  518.     clz    r7, r6
  519.     lsr    r6, #24
  520.     lsl    r6, r7
  521.     bic    r6, #0x0080
  522.     lsrs    r7, #1
  523.     orrcs    r6, #0x0080
  524.     orr    r6, r6, r7, lsl #12
  525.     orr    r6, #0x4000
  526.     b    4f
  527. 2:    @ at this point the C flag is always clear
  528.     add r7, r3
  529. #ifdef CONFIG_ARM_PATCH_PHYS_VIRT_16BIT
  530.     ldrh    ip, [r7]
  531.     tst    ip, 0x0400    @ the i bit tells us LS or MS byte
  532.     beq    3f
  533.     cmp    r0, #0        @ set C flag, and ...
  534.     biceq    ip, 0x0400    @ immediate zero value has a special encoding
  535.     streqh    ip, [r7]    @ that requires the i bit cleared
  536. #endif
  537. 3:    ldrh    ip, [r7, #2]
  538.     and    ip, 0x8f00
  539.     orrcc    ip, r6    @ mask in offset bits 31-24
  540.     orrcs    ip, r0    @ mask in offset bits 23-16
  541.     strh    ip, [r7, #2]
  542. 4:    cmp    r4, r5
  543.     ldrcc    r7, [r4], #4    @ use branch for delay slot
  544.     bcc    2b
  545.     bx    lr
  546. #else
  547. #ifdef CONFIG_ARM_PATCH_PHYS_VIRT_16BIT
  548.     and    r0, r6, #255    @ offset bits 23-16
  549.     mov    r6, r6, lsr #8    @ offset bits 31-24
  550. #else
  551.     mov    r0, #0        @ just in case...
  552. #endif
  553.     b    3f
  554. 2:    ldr    ip, [r7, r3]
  555.     bic    ip, ip, #0x000000ff
  556.     tst    ip, #0x400    @ rotate shift tells us LS or MS byte
  557.     orrne    ip, ip, r6    @ mask in offset bits 31-24
  558.     orreq    ip, ip, r0    @ mask in offset bits 23-16
  559.     str    ip, [r7, r3]
  560. 3:    cmp    r4, r5
  561.     ldrcc    r7, [r4], #4    @ use branch for delay slot
  562.     bcc    2b
  563.     mov    pc, lr
  564. #endif
  565. ENDPROC(__fixup_a_pv_table)
  566. ENTRY(fixup_pv_table)
  567.     stmfd     {r4 - r7, lr}
  568.     ldr    r2, 2f            @ get address of __pv_phys_offset
  569.     mov    r3, #0            @ no offset
  570.     mov    r4, r0            @ r0 = table start
  571.     add    r5, r0, r1        @ r1 = table size
  572.     ldr    r6, [r2, #4]        @ get __pv_offset
  573.     bl    __fixup_a_pv_table
  574.     ldmfd     {r4 - r7, pc}
  575. ENDPROC(fixup_pv_table)
  576.     .align
  577. 2:    .long    __pv_phys_offset
  578.     .data
  579.     .globl    __pv_phys_offset
  580.     .type    __pv_phys_offset, %object
  581. __pv_phys_offset:
  582.     .long    0
  583.     .size    __pv_phys_offset, . - __pv_phys_offset
  584. __pv_offset:
  585.     .long    0
  586. #endif
  587. #include "head-common.S"
arch/arm/kernel/head-common.S
  1. /*
  2.  * linux/arch/arm/kernel/head-common.S
  3.  *
  4.  * Copyright (C) 1994-2002 Russell King
  5.  * Copyright (c) 2003 ARM Limited
  6.  * All Rights Reserved
  7.  *
  8.  * This program is free software; you can redistribute it and/or modify
  9.  * it under the terms of the GNU General Public License version 2 as
  10.  * published by the Free Software Foundation.
  11.  *
  12.  */
  13. #define ATAG_CORE 0x54410001
  14. #define ATAG_CORE_SIZE ((2*4 + 3*4) >> 2)
  15. #define ATAG_CORE_SIZE_EMPTY ((2*4) >> 2)
  16. #ifdef CONFIG_CPU_BIG_ENDIAN
  17. #define OF_DT_MAGIC 0xd00dfeed
  18. #else
  19. #define OF_DT_MAGIC 0xedfe0dd0 /* 0xd00dfeed in big-endian */
  20. #endif
  21. /*
  22.  * 异常处理. 一些我们无法处理的错误.
  23.  * 我们应当告诉用户(这些错误信息),但因为我们甚至无法保证是在正确的架构上运行,
  24.  * 所以我们什么都不做(死循环)。
  25.  *
  26.  * 如果 CONFIG_DEBUG_LL 被设置,我们试图打印出错误信息,
  27.  * 并希望这可以对我们有帮助 (例如这对bootloader没有提供适当的处理器ID
  28.  * 是有帮助的).
  29.  */
  30.     __HEAD
  31.  
  32. /* 确定r2(内核启动参数)指针的有效性。 The heuristic 要求
  33.  * 是4Byte对齐的、在物理内存的头16K中,且以ATAG_CORE标记开头。
  34.  * 如果选择了CONFIG_OF_FLATTREE,dtb指针也是可以接受的.
  35.  * 
  36.  * 在这个函数的未来版本中 可能会对物理地址的要求更为宽松,
  37.  * 且如果有必要的话,可能可以移动ATAGS数据块.
  38.  *
  39.  * 返回:
  40.  * r2 可能是有效的 atags 指针, 有效的 dtb 指针,或者0
  41.  * r5, r6 被篡改
  42.  */
  43. __vet_atags:
  44.     tst    r2, #0x3            @ 是否4Byte对齐?
  45.     bne    1f                    @ 不是则认为指针无效,返回
  46.     ldr    r5, [r2, #0]        @获取r2指向的前4Byte,用于下面测试
  47. #ifdef CONFIG_OF_FLATTREE
  48.     ldr    r6, =OF_DT_MAGIC        @ is it a DTB?
  49.     cmp    r5, r6
  50.     beq    2f
  51. #endif
  52.     /* 内核启动参数块的规范是:
  53.      * (wait for updata)
  54.      */
  55.     cmp    r5, #ATAG_CORE_SIZE        @ 第一个tag是ATAG_CORE吗?测试的是tag_header中的size
  56.                                 @ 如果为ATAG_CORE,那么必为ATAG_CORE_SIZE
  57.     cmpne    r5, #ATAG_CORE_SIZE_EMPTY    @ 如果第一个tag的tag_header中的size为ATAG_CORE_SIZE_EMPTY
  58.                                         @ 说明此处也有atags
  59.     bne    1f
  60.     ldr    r5, [r2, #4]            @ 第一个tag_header的tag(魔数)
  61.     ldr    r6, =ATAG_CORE            @ 获取ATAG_CORE的魔数
  62.     cmp    r5, r6                    @ 判断第一个tag是否为ATAG_CORE
  63.     bne    1f                        @ 不是则认为指针无效,返回
  64. 2:    mov    pc, lr                @ atag/dtb 指针有效
  65. 1:    mov    r2, #0
  66.     mov    pc, lr
  67. ENDPROC(__vet_atags)
  68. /*
  69.  * 以下的代码段是在MMU开启的状态下执行的,
  70.  * 而且使用的是绝对地址; 这不是位置无关代码.
  71.  *
  72.  * r0 = cp#15 控制寄存器值
  73.  * r1 = machine ID
  74.  * r2 = atags/dtb pointer
  75.  * r9 = processor ID
  76.  */
  77.     __INIT
  78. __mmap_switched:
  79.     adr    r3, __mmap_switched_data
  80.     ldmia     {r4, r5, r6, r7}
  81.     cmp    r4, r5                @ 如果有必要,拷贝数据段。
  82.                             @ 对比__data_loc和_sdata
  83.                             @ __data_loc是数据段在内核代码映像中的存储位置
  84.                             @ _sdata是数据段的链接位置(在内存中的位置)
  85.                             @ 如果是XIP技术的内核,这两个数据肯定不同
  86. 1:    cmpne    r5, r6            @ 检测数据是否拷贝完成
  87.     ldrne    fp, [r4], #4
  88.     strne    fp, [r5], #4
  89.     bne    1b
  90.     mov    fp, #0                @ 清零 BSS 段(and zero fp)
  91. 1:    cmp    r6, r7                @ 检测是否完成
  92.     strcc    fp, [r6],#4
  93.     bcc    1b
  94.     /* 这里将需要的数据从寄存器中转移到全局变量中,
  95.      * 因为最后会跳入C代码,寄存器会被使用。
  96.      */
  97.  ARM(    ldmia    r3, {r4, r5, r6, r7, sp})
  98.  THUMB(    ldmia    r3, {r4, r5, r6, r7}    )
  99.  THUMB(    ldr    sp, [r3, #16]        )
  100.     str    r9, [r4]            @ 保存 processor ID到全局变量processor_id
  101.     str    r1, [r5]            @ 保存 machine type到全局变量__machine_arch_type
  102.     str    r2, [r6]            @ 保存 atags指针到全局变量__atags_pointer
  103.     bic    r4, r0, #CR_A            @ 清除cp15 控制寄存器值的 'A' bit(禁用对齐错误检查)
  104.     stmia    r7, {r0, r4}            @ 保存控制寄存器值到全局变量cr_alignment(在arch/arm/kernel/entry-armv.S)
  105.     b    start_kernel        @ 跳入C代码(init/main.c)
  106. ENDPROC(__mmap_switched)
  107.     .align    2
  108.     .type    __mmap_switched_data, %object
  109. __mmap_switched_data:
  110.     .long    __data_loc            @ r4
  111.     .long    _sdata                @ r5
  112.     .long    __bss_start            @ r6
  113.     .long    _end                @ r7
  114.     .long    processor_id            @ r4
  115.     .long    __machine_arch_type        @ r5
  116.     .long    __atags_pointer            @ r6
  117.     .long    cr_alignment            @ r7
  118.     .long    init_thread_union + THREAD_START_SP @ sp
  119.     .size    __mmap_switched_data, . - __mmap_switched_data
  120. /*
  121.  * 这里提供一个 C-API 版本的 __lookup_processor_type
  122.  */
  123. ENTRY(lookup_processor_type)
  124.     stmfd     {r4 - r6, r9, lr}
  125.     mov    r9, r0
  126.     bl    __lookup_processor_type
  127.     mov    r0, r5
  128.     ldmfd     {r4 - r6, r9, pc}
  129. ENDPROC(lookup_processor_type)
  130. /*
  131.  * 读取处理器ID寄存器 (CP#15, CR0), 并且查找编译时确定的处理器
  132.  * 支持列表. 注意:我们不能对__proc_info使用绝对地址,
  133.  * 因为我们还没有重新初始化页表(MMU已关闭,之前是解压时使用的1:1映射)。
  134.  * (我们不在正确的地址空间:内核是按虚拟地址(0xc00008000)编译的,
  135.  * 而现在我们运行在MMU关闭的情况下)。
  136.  * 我们必须计算偏移量。
  137.  *
  138.  *    r9 = cpuid
  139.  * Returns:
  140.  *    r3, r4, r6 被篡改
  141.  *    r5 = proc_info 指针(物理地址空间)
  142.  *    r9 = cpuid (保留)
  143.  */
  144.     __CPUINIT
  145. __lookup_processor_type:
  146.     adr    r3, __lookup_processor_type_data        @获取运行时的地址数据
  147.     ldmia    r3, {r4 - r6}    @获取编译时确定的地址数据(虚拟地址)
  148.     sub    r3, r3, r4            @ 获取地址偏移 virt&phys(r3)
  149.     add    r5, r5, r3            @ 将虚拟地址空间转换为物理地址空间
  150.     add    r6, r6, r3            @ r5=__proc_info_begin r6=__proc_info_end
  151. 1:    ldmia    r5, {r3, r4}    @ 获取proc_info_list结构体中的value, mask
  152.     and    r4, r4, r9            @ 利用掩码处理从CP15获取的处理器ID
  153.     teq    r3, r4                @ 对比编译时确定的处理器ID
  154.     beq    2f                    @ 若处理器ID匹配,返回
  155.     add    r5, r5, #PROC_INFO_SZ        @ 利用sizeof(proc_info_list)跳入下一个处理器ID的匹配
  156.     cmp    r5, r6                @ 是否已经处理完proc_info_list数据
  157.     blo    1b                    @ 如果还有proc_info_list数据,再次检查匹配
  158.     mov    r5, #0                @ 否则,编译的内核与此处理器不匹配,r5 = #0
  159. 2:    mov    pc, lr
  160. ENDPROC(__lookup_processor_type)
  161. /*
  162.  * 参见 <asm/procinfo.h> 中关于 __proc_info 结构体的信息.
  163.  */
  164.     .align    2
  165.     .type    __lookup_processor_type_data, %object
  166. __lookup_processor_type_data:
  167.     .long    .
  168.     .long    __proc_info_begin
  169.     .long    __proc_info_end
  170.     .size    __lookup_processor_type_data, . - __lookup_processor_type_data
  171. /*
  172.  * 处理器ID不匹配时的入口
  173.  * 如果启用了调试信息,会从consol打印提示信息
  174.  * 之后会进入__error的死循环
  175.  */
  176. __error_p:
  177. #ifdef CONFIG_DEBUG_LL
  178.     adr    r0, str_p1
  179.     bl    printascii
  180.     mov    r0, r9
  181.     bl    printhex8
  182.     adr    r0, str_p2
  183.     bl    printascii
  184.     b    __error
  185. str_p1:    .asciz    "\nError: unrecognized/unsupported processor variant (0x"
  186. str_p2:    .asciz    ").\n"
  187.     .align
  188. #endif
  189. ENDPROC(__error_p)
  190. /*
  191.  * 出错时的死循环入口
  192.  */
  193. __error:
  194. #ifdef CONFIG_ARCH_RPC
  195. /*
  196.  * 出错时屏幕变红 - RiscPC only.
  197.  */
  198.     mov    r0, #0x02000000
  199.     mov    r3, #0x11
  200.     orr    r3, r3, r3, lsl #8
  201.     orr    r3, r3, r3, lsl #16
  202.     str    r3, [r0], #4
  203.     str    r3, [r0], #4
  204.     str    r3, [r0], #4
  205.     str    r3, [r0], #4
  206. #endif
  207. 1:    mov    r0, r0
  208.     b    1b
  209. ENDPROC(__error)
 
 
【作者】 张昺华
【新浪微博】 张昺华--sky
【twitter】 @sky2030_
【facebook】 张昺华 zhangbinghua
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
目录
相关文章
|
Linux C语言
RISC-V Linux汇编启动过程分析
RISC-V Linux汇编启动过程分析
|
12月前
|
存储 安全 Windows
3.6 Windows驱动开发:内核进程汇编与反汇编
在笔者上一篇文章`《内核MDL读写进程内存》`简单介绍了如何通过MDL映射的方式实现进程读写操作,本章将通过如上案例实现远程进程反汇编功能,此类功能也是ARK工具中最常见的功能之一,通常此类功能的实现分为两部分,内核部分只负责读写字节集,应用层部分则配合反汇编引擎对字节集进行解码,此处我们将运用`capstone`引擎实现这个功能。
64 0
3.6 Windows驱动开发:内核进程汇编与反汇编
|
6月前
|
存储 安全 Linux
Linux内嵌汇编
Linux内嵌汇编
76 0
|
11月前
|
Linux
linux编写汇编
linux编写汇编
46 0
|
安全 开发工具 Windows
驱动开发:内核实现进程汇编与反汇编
在笔者上一篇文章`《驱动开发:内核MDL读写进程内存》`简单介绍了如何通过MDL映射的方式实现进程读写操作,本章将通过如上案例实现远程进程反汇编功能,此类功能也是ARK工具中最常见的功能之一,通常此类功能的实现分为两部分,内核部分只负责读写字节集,应用层部分则配合反汇编引擎对字节集进行解码,此处我们将运用`capstone`引擎实现这个功能。
270 0
|
存储 移动开发 前端开发
linux内核1-GNU汇编入门_X86-64&ARM(下)
linux内核1-GNU汇编入门_X86-64&ARM(下)
|
存储 前端开发 rax
linux内核1-GNU汇编入门_X86-64&ARM(上)
linux内核1-GNU汇编入门_X86-64&ARM
|
Linux C语言 芯片
Linux系统中使用汇编初始化外设方法
大家好,我是ST。 今天主要和大家聊一聊,如何使用汇编语言来实现芯片外设的初始化功能。
176 0
Linux系统中使用汇编初始化外设方法
|
Linux 编译器 C语言
『Linux从入门到精通』第 ⑦ 期 - Linux编译器——gcc/g++(预处理、编译、汇编、链接)
『Linux从入门到精通』第 ⑦ 期 - Linux编译器——gcc/g++(预处理、编译、汇编、链接)
139 0
驱动开发:内核LDE64引擎计算汇编长度
本章开始`LyShark`将介绍如何在内核中实现`InlineHook`挂钩这门技术,内核挂钩的第一步需要实现一个动态计算汇编指令长度的功能,该功能可以使用`LDE64`这个反汇编引擎,该引擎小巧简单可以直接在驱动中使用,LDE引擎是`BeaEngine`引擎的一部分,后来让`BeatriX`打包成了一个`ShellCode`代码,并可以通过`typedef`动态指针的方式直接调用功能,本章内容作为后期`Hook`挂钩的铺垫部分,独立出来也是因为代码太多太占空间一篇文章写下来或很长影响阅读。
241 0
驱动开发:内核LDE64引擎计算汇编长度