Linux内存管理学习1 —— head.S中的段页表的建立

2023-06-20  

平台

TQ2440

Qemu+vexpress-ca9

Linux-4.10.17

 

概述

   在Linux自解压完毕后,开始执行arch/arm/kernel/head.S,然后跳转到init/main.c中的start_kernel开始执行。在head.S中为了便利Linux内核启动,会建立临时的段页表。这里以TQ2440和vexpress-ca9为例,其中TQ2440使用的SoC是S3C2440,ARM核心是ARM920T,指令集是ARMv4T,而vexpress-ca9是ARM核心是Cortex-A9,指令集是ARMv7。为了便于理解,在分析的时候主要以2440为主,只是顺便说一下ARMv7,因为这两个大同小异。

下面是代码分析时的一些条件

1、以设备树的方式启动Linux内核

2、下面是一些宏和变量的说明:

 

说明 TQ2440(ARM920T) vxpress(Cortex-A9)
CONFIG_ARM_LPAE
No No
TEXT_OFFSET 内核代码段相对于内核地址空间的偏移量 0x8000 0x8000
PAGE_OFFSET 内核地址空间的偏移量 0xC000_0000 0xC000_0000
 KERNEL_RAM_VADDR =PAGE_OFFSET+TEXT_OFFSET 0xC000_8000
0xC000_8000
PG_DIR_SIZE 一级页表的大小 0x4000 (16KB) 0x4000 (16KB)
PMD_ORDER 一级页表的每个页表项占用的字节(2^(PMD_ORDER)) 2^2 = 4 2^2 = 4
swapper_pg_dir

一级页表的虚拟起始地址

KERNEL_RAM_VADDR - PG_DIR_SIZE

0xC000_4000 0xC000_4000
CONFIG_ARM_VIRT_EXT
No Yes
CONFIG_XIP_KERNEL
No No
CONFIG_SMP
No Yes
CONFIG_SMP_ON_UP
No Yes
CONFIG_ARM_PATCH_PHYS_VIRT
Yes Yes
CONFIG_CPU_32v4T  ARM指令集 Yes No
CONFIG_CPU_32v7  ARM指令集 No Yes
CONFIG_CPU_V7M  ARM指令集 No No
__LINUX_ARM_ARCH__ ARM指令集 4 7
CONFIG_CPU_DCACHE_WRITETHROUGH
No No

 

 3、地址空间:

对于TQ2440,板子上面有64MB的物理内存,所以物理内存地址范围是: 0x3000_0000 ~ 0x3400_0000

对于express板子,分配了1GB的物理内存,所以物理内存地址范围是: 0x6000_0000 ~ 0xA000_0000

 

正文

在进入head.S是,MMU和D-Cache是关闭的,r0是0,r1的值任意,r2的值是dtb镜像在内存中的物理起始地址。

下面是对head.S精简后的代码:

1 ENTRY(stext)

 2 

 3 #ifdef CONFIG_ARM_VIRT_EXT

 4     bl    __hyp_stub_install

 5 #endif

 6     @ ensure svc mode and all interrupts masked

 7     safe_svcmode_maskall r9

 8 

 9     mrc    p15, 0, r9, c0, c0        @ get processor id

10     bl    __lookup_processor_type        @ r5=procinfo r9=cpuid

11     movs    r10, r5                @ invalid processor (r5=0)?

12     beq    __error_p            @ yes, error 'p'

13 

14     adr    r3, 2f

15     ldmia    r3, {r4, r8}

16     sub    r4, r3, r4            @ (PHYS_OFFSET - PAGE_OFFSET)

17     add    r8, r8, r4            @ PHYS_OFFSET

18 

19     /*

20      * r1 = machine no, r2 = atags or dtb,

21      * r8 = phys_offset, r9 = cpuid, r10 = procinfo

22      */

23     bl    __vet_atags

24 #ifdef CONFIG_SMP_ON_UP

25     bl    __fixup_smp

26 #endif

27 

28     bl    __fixup_pv_table

29 

30     bl    __create_page_tables

31 

32     /*

33      * The following calls CPU specific code in a position independent

34      * manner.  See arch/arm/mm/proc-*.S for details.  r10 = base of

35      * xxx_proc_info structure selected by __lookup_processor_type

36      * above.

37      *

38      * The processor init function will be called with:

39      *  r1 - machine type

40      *  r2 - boot data (atags/dt) pointer

41      *  r4 - translation table base (low word)

42      *  r5 - translation table base (high word, if LPAE)

43      *  r8 - translation table base 1 (pfn if LPAE)

44      *  r9 - cpuid

45      *  r13 - virtual address for __enable_mmu -> __turn_mmu_on

46      *

47      * On return, the CPU will be ready for the MMU to be turned on,

48      * r0 will hold the CPU control register value, r1, r2, r4, and

49      * r9 will be preserved.  r5 will also be preserved if LPAE.

50      */

51     ldr    r13, =__mmap_switched        @ address to jump to after

52                         @ mmu has been enabled

53     badr    lr, 1f                @ return (PIC) address

54 

55     mov    r8, r4                @ set TTBR1 to swapper_pg_dir

56 

57     ldr    r12, [r10, #PROCINFO_INITFUNC]

58     add    r12, r12, r10

59     ret    r12

60 1:    b    __enable_mmu

61 ENDPROC(stext)

62     .ltorg

63 2:    .long    .

64     .long    PAGE_OFFSET


下面开始分析上面的代码:


1、第4行的__hyp_stub_install在vexpress上会执行,而在2440上不执行,这里暂时忽略


2、第7行的 safe_svcmode_maskall r9 确保处理器进入SVC模式,同时关闭IRQ和FIQ中断。对于2440,做了如下操作:


msr  cpsr_c, #(PSR_F_BIT | PSR_I_BIT | SVC_MODE)

3、第9行 mrc p15, 0, r9, c0, c0 用于获得processor id。


对于2440, CP15的C0的值是0x4112920x,参考手册 ARM920T Technical Reference Manual 的2.3节 CP15 register map summary

对于vexpress,CP15的C0的值是0x414FC091,参考手册 ARM® Cortex®‑A9 Technical Reference Manual 的 4. System Control

 

比如对于2440,执行完第3行代码后,r9的值就是0x4112920x,而对于vexpress,r9的值是0x414FC091。

4、第10到12行,遍历kernel的".proc.info.init"段,找到与该处理器ID匹配的proc_info_list结构体,如果找到的话,r5寄存器存放的是该proc_info_list的物理地址,第11行将该地址存放到r10中,如果没有找到的话,

寄存器r5值是0,执行完第11行的movs代码后,第12行的beq就会成立,跳转到__error_p处,如果配置了CONFIG_DEBUG_LL,就会打印相应的错误信息:

Error: unrecognized/unsupported processor variant (0xXXXXXXX)

上面括号中是实际从CP15的C0里读到的值。

下面我们看看对于2440和vexpress这两个板子,与之匹配的proc.info.init字段都分别是什么?

对于2440,该部分定义在arch/arm/mm/proc-arm920.S中:

 1     define_processor_functions arm920, dabort=v4t_early_abort, pabort=legacy_pabort, suspend=1

 2 

 3     .section ".rodata"

 4 

 5     string    cpu_arch_name, "armv4t"

 6     string    cpu_elf_name, "v4"

 7     string    cpu_arm920_name, "ARM920T"

 8 

 9     .align

10 

11     .section ".proc.info.init", #alloc

12 

13     .type    __arm920_proc_info,#object

14 __arm920_proc_info:

15     .long    0x41009200

16     .long    0xff00fff0

17     .long   PMD_TYPE_SECT |

18         PMD_SECT_BUFFERABLE |

19         PMD_SECT_CACHEABLE |

20         PMD_BIT4 |

21         PMD_SECT_AP_WRITE |

22         PMD_SECT_AP_READ

23     .long   PMD_TYPE_SECT |

24         PMD_BIT4 |

25         PMD_SECT_AP_WRITE |

26         PMD_SECT_AP_READ

27     initfn    __arm920_setup, __arm920_proc_info

28     .long    cpu_arch_name

29     .long    cpu_elf_name

30     .long    HWCAP_SWP | HWCAP_HALF | HWCAP_THUMB

31     .long    cpu_arm920_name

32     .long    arm920_processor_functions

33     .long    v4wbi_tlb_fns

34     .long    v4wb_user_fns

35     .long    arm920_cache_fns

36     .size    __arm920_proc_info, . - __arm920_proc_info


第1行的define_processor_functions是一个宏,定义在arch/arm/mm/proc-macros.S中,根据传入的参数展开后如下:


    .type    arm920_processor_functions, #object

    .align 2

ENTRY(arm920_processor_functions)

    .word    dabort

    .word    pabort

    .word    cpu_arm920_proc_init

    .word    cpu_arm920_proc_fin

    .word    cpu_arm920_reset

    .word    cpu_arm920_do_idle

    .word    cpu_arm920_dcache_clean_area

    .word    cpu_arm920_switch_mm

    .word    cpu_arm920_set_pte_ext

    .word    cpu_arm920_suspend_size

    .word    cpu_arm920_do_suspend

    .word    cpu_arm920_do_resume

    .size    arm920_processor_functions, . - arm920_processor_functions


第4到7行只读,存放了一下字符串,将来在启动阶段(start_kernel --> setup_arch --> setup_processor)会被打印出来


    pr_info("CPU: %s [%08x] revision %d (ARMv%s), cr=%08lxn",

        cpu_name, read_cpuid_id(), read_cpuid_id() & 15,

        proc_arch[cpu_architecture()], get_cr());

如:


[    0.000000] CPU: ARM920T [41129200] revision 0 (ARMv4T), cr=c000717f


第15到35行的数据将来可以通过一个struct 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;

};


第27行 initfn __arm920_setup, __arm920_proc_info 展开后是: __arm920_setup -  __arm920_proc_info,也就是这里存放了一个这两个符号的地址偏差,将来就可以根据__arm920_proc_info轻松地找到__arm920_setup

文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。