返回列表 发帖

[站长原创] 移植内核必备知识-c函数start_kernel之前内核汇编做的事情

[站长原创] 移植内核必备知识-c函数start_kernel之前内核汇编做的事情

跟我学系列-移植内核必备知识-c函数start_kernel之前内核汇编做的事情


嵌入式开发联盟

www.mcuos.com

Osboy 站长原创

QQ:82475491

Mcuos.com@gmail.com

(一)linux内核程序的入口点

在arch/arm/head.S中有代码:

  1.         __HEAD
  2. ENTRY(stext)
  3.         setmode        PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9 @ ensure svc mode
  4.                                                 @ and irqs disabled
  5.         mrc        p15, 0, r9, c0, c0                @ get processor id
  6.         bl        __lookup_processor_type                @ r5=procinfo r9=cpuid
  7.         movs        r10, r5                                @ invalid processor (r5=0)?
  8. THUMB( it        eq )                @ force fixup-able long branch encoding
  9.         beq        __error_p                        @ yes, error 'p'

  10. #ifndef CONFIG_XIP_KERNEL
  11.         adr        r3, 2f
  12.         ldmia        r3, {r4, r8}
  13.         sub        r4, r3, r4                        @ (PHYS_OFFSET - PAGE_OFFSET)
  14.         add        r8, r8, r4                        @ PHYS_OFFSET
  15. #else
  16.         ldr        r8, =PLAT_PHYS_OFFSET
  17. #endif

  18.         /*
  19.          * r1 = machine no, r2 = atags or dtb,
  20.          * r8 = phys_offset, r9 = cpuid, r10 = procinfo
  21.          */
  22.         bl        __vet_atags
  23. #ifdef CONFIG_SMP_ON_UP
  24.         bl        __fixup_smp
  25. #endif
  26. #ifdef CONFIG_ARM_PATCH_PHYS_VIRT
  27.         bl        __fixup_pv_table
  28. #endif
  29.         bl        __create_page_tables
复制代码

那么在linux运行到start_kernel之前就是执行以上的程序代码。这里的ENTRY(stext),注意ENTRY仅仅是个宏定义,定义在include/linux/Linkage.h中,有代码:

  1. #ifndef ENTRY
  2. #define ENTRY(name) \
  3.   .globl name; \
  4.   ALIGN; \
  5.   name:
  6. #endif
复制代码

所以这里的ENTRY(stext)等同于:.globl stext,这段code的作用是给标号stext一个外部链接,类似于c语言的extern之类的作用。

这里的__HEAD是个宏定义,它定义在:include/linux/init.h里面的:

  1. #define __HEAD                .section        ".head.text","ax"
复制代码

上面的__HEAD就表示把head.S文件中的函数都放置在.head.text代码段中,而在include/asm-generic/vmlinux.lds.h中有定义:

  1. /* Section used for early init (in .S files) */
  2. #define HEAD_TEXT  *(.head.text)
复制代码

根据vmlinux.lds.S链接脚本可以知道:

  1.         .init : {                        /* Init code and data                */
  2.                 _stext = .;
  3.                 _sinittext = .;
  4.                         HEAD_TEXT
  5.                         INIT_TEXT
  6.                         ARM_EXIT_KEEP(EXIT_TEXT)
  7.                 _einittext = .
  8.                 ARM_CPU_DISCARD(PROC_INFO)
  9.                 __arch_info_begin = .;
复制代码

所以综上所述,内核的第一个执行的代码肯定是HEAD_TEXT段中的代码,所以head.S中的ENTRY(stext)第一个被执行到,然后依次走下去。

(2)__lookup_processor_type,匹配CPU架构ID

  1.         setmode        PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9 @ ensure svc mode
  2.                                                 @ and irqs disabled
  3.         mrc        p15, 0, r9, c0, c0                @ get processor id
  4.         bl        __lookup_processor_type                @ r5=procinfo r9=cpuid
  5.         movs        r10, r5                                @ invalid processor (r5=0)?
  6. THUMB( it        eq )                @ force fixup-able long branch encoding
  7.         beq        __error_p                        @ yes, error 'p'
复制代码

ARM架构虽然有7中工作模式,但是Linux真正使用的是SVC模式和USR模式,Linux在内核中工作模式采用SVC模式,所以第一句代码就是确保arm处于SVC模式。

mrc        p15, 0, r9, c0, c0,这句汇编就是从CP15协处理器中的R9寄存器读出CPU架构的ID,s3c6410的架构为arm11,所以有一串特定的架构识别码在每一个CPU的arm11 core中。下面我么分析:__lookup_processor_type函数:


.type
__lookup_processor_type_data, %object__lookup_processor_type_data:
.long
.
.long
__proc_info_begin
.long
__proc_info_end
.size
__lookup_processor_type_data, . - __lookup_processor_type_data在linux链接脚本文件里面有定义:

  1. #define PROC_INFO                                                        \
  2.         VMLINUX_SYMBOL(__proc_info_begin) = .;                                \
  3.         *(.proc.info.init)                                                \
  4.         VMLINUX_SYMBOL(__proc_info_end) = .;
复制代码

所以我们看出来__proc_info_begin,__proc_info_end其实就是一个区间,该区间内存放的是*(.proc.info.init)         段的代码,那么*(.proc.info.init)         段有哪些代码呢?我们看:arch/arm/mm/proc-v6.S中有代码:
  1.         .section ".proc.info.init", #alloc, #execinstr

  2.         /*
  3.          * Match any ARMv6 processor core.
  4.          */
  5.         .type        __v6_proc_info, #object
  6. __v6_proc_info:
  7.         .long        0x0007b000
  8.         .long        0x0007f000
  9.         ALT_SMP(.long \
  10.                 PMD_TYPE_SECT | \
  11.                 PMD_SECT_AP_WRITE | \
  12.                 PMD_SECT_AP_READ | \
  13.                 PMD_FLAGS_SMP)
  14.         ALT_UP(.long \
  15.                 PMD_TYPE_SECT | \
  16.                 PMD_SECT_AP_WRITE | \
  17.                 PMD_SECT_AP_READ | \
  18.                 PMD_FLAGS_UP)
  19.         .long   PMD_TYPE_SECT | \
  20.                 PMD_SECT_XN | \
  21.                 PMD_SECT_AP_WRITE | \
  22.                 PMD_SECT_AP_READ
  23.         b        __v6_setup
  24.         .long        cpu_arch_name
  25.         .long        cpu_elf_name
  26.         /* See also feat_v6_fixup() for HWCAP_TLS */
  27.         .long        HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP|HWCAP_JAVA|HWCAP_TLS
  28.         .long        cpu_v6_name
  29.         .long        v6_processor_functions
  30.         .long        v6wbi_tlb_fns
  31.         .long        v6_user_fns
  32.         .long        v6_cache_fns
  33.         .size        __v6_proc_info, . - __v6_proc_info
复制代码


下面紧接着分析__lookup_processor_type

__lookup_processor_type:
adr
r3, __lookup_processor_type_data
ldmia
r3, {r4 - r6} //从连续的存储空间中读取字到寄存器中。这段code执行完之后:r4 = r3 的虚拟地址 ,r5 = __proc_info_begin的虚拟地址,r6 = __proc_info_end的虚拟地址,注意:这些地址都是与PC相关的。
sub
r3, r3, r4
@ get offset between virt&phys//r3=r3-r4,相减的结果就是offset变成虚拟地址和物理地址直接的offset了。
add
r5, r5, r3
@ convert virt addresses to//r5=r5+r3,相加之后,r5变成__proc_info_begin的物理地址。
add
r6, r6, r3
@ physical address space//r6=r6+r3,相加之后,r6变成__proc_info_end的物理地址了。1:
ldmia
r5, {r3, r4}
@ value, mask//把__proc_info_begin开始的内存空间存放的变量load到r3,r4中,执行完之后,r3=0x0007b000, r4=0x0007f000,注意这里的r3的7b000的值,是我们在linux内核中静态设置好的armv6架构的cpu id。
and
r4, r4, r9
@ mask wanted bits//从实际的cpu中读出的r9为cpuid,如掩码相与应该得到一个实际的cpuid
teq
r3, r4//把实际读出来的cpuid和我们在内核中设置好的cpuid想对比,这两个值必须匹配。
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)
(3)__create_page_tables创建内核初始化时候的临时页表。


http://mcuos.com/thread-6952-1-1.html


可以参考这份文档加以学习。

分享到: QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友

返回列表
网页右侧QQ悬浮滚动在线客服
网页右侧QQ悬浮滚动在线客服