一起分析Linux系统设计思想——03内核启动流程分析(二)

    科技2022-07-12  158

    在学习资料满天飞的大环境下,知识变得非常零散,体系化的知识并不多,这就导致很多人每天都努力学习到感动自己,最终却收效甚微,甚至放弃学习。我的使命就是过滤掉大量的垃圾信息,将知识体系化,以短平快的方式直达问题本质,把大家从大海捞针的痛苦中解脱出来。

    文章目录

    3 __lookup_processor_type剖析(接上篇)3.1 本节新用到的汇编指令3.2 代码流程分析3.3 proc_info结构体3.3.1 汇编定义结构体3.3.2 C语言结构体声明

    3 __lookup_processor_type剖析(接上篇)

    3.1 本节新用到的汇编指令

    本节用新用到的汇编指令如下:

    指令功能说明ldmLoad multiple registers将内存中的值load到多个寄存器中,属于pop操作stmStore Multiple将多个寄存器中的值store到内存中,属于push操作subSubtract减法andAnd逻辑与teqTest bitwise equality相等测试指令(该指令用于把一个寄存器Rn的内容和另一个操作数operand2按位进行异或运算,并根据运算结果更新CPSR中条件标志位的值cmpCompare比较两个操作数是否相同(作差,结果影响CPSR)

    stm有8种操作模式,主要用于现场保护、数据复制、参数传送和堆栈操作等,详细说明见下表:

    模式功能说明iaIncrease After每次传送后地址加4,其中的寄存器 从左到右执行 ,例如:STMIA R0,{R1,LR} 先存R1,再存LRibIncrease Before每次传送前地址加4,其余同iadaDecrease After每次传送后地址减4,其中的寄存器 从右到左执行 ,例如:STMDA R0,{R1,LR} 先存LR,再存R1dbDecrease Before每次传送前地址减4,其余同dafdFull Descending满减栈(每次传送 前 地址 减 4)faFull Increase满增栈(每次传送 前 地址 加 4)edEmpty Descending空减栈(每次传送 后 地址 减 4)eaEmpty Increase空增栈(每次传送 后 地址 加 4)

    3.2 代码流程分析

    /* /arch/arm/kernel/head-common.S */ /* * Read processor ID register (CP#15, CR0), and look up in the linker-built * supported processor list. Note that we can't use the absolute addresses * for the __proc_info lists since we aren't running with the MMU on * (and therefore, we are not in the correct address space). We have to * calculate the offset. * * r9 = cpuid * Returns: * r3, r4, r6 corrupted * r5 = proc_info pointer in physical address space 如何匹配失败r5中赋值为0(空指针) * r9 = cpuid (preserved) */ .type __lookup_processor_type, %function __lookup_processor_type: adr r3, 3f @ 3f中3为标号名,f指示forword方向查找;本句adr指令将3标号对应的“.”所标识的运行时地址(因为MMU还未打开,所以此时是物理地址)存储到r3中。 ldmda r3, {r5 - r7} @ 本行代码等同于:r7 = [r3] = . = 3f所在的链接地址(虚拟地址),r6 = [r3-4] = __proc_info_end, r5 = [r3-4*2] = __proc_info_begin. sub r3, r3, r7 @ get offset between virt&phys,r3 = r3 - r7 = 物理地址-虚拟地址=offset,所以,物理地址 = 虚拟地址 + offset,下面两行就是在利用这个公式。 add r5, r5, r3 @ convert virt addresses to add r6, r6, r3 @ physical address space 1: ldmia r5, {r3, r4} @ value, mask,等价于r3 = [r5] = cpuid's value, r4 = [r5+4] = mask。 and r4, r4, r9 @ mask wanted bits 使用r4中的掩码对r9中的cpuid进行处理。 teq r3, r4 @ 此时r4中存储的是从协处理器中读取的并经过掩码处理的cpuid,r3中存储的是内核支持的cpuid,将两个值进行按位异或,结果影响CPSR的Z位,如果Z=1,说明匹配,否则,不匹配。 beq 2f @ 如果软硬件cpuid匹配则跳转到标号2处执行。 add r5, r5, #PROC_INFO_SZ @ sizeof(proc_info_list) proc_info_list是C语言形式的proc_info结构体定义(详见3.3);如果上一次软硬件cpuid不匹配,则将r5指向下一个结构体首地址。 cmp r5, r6 @ r5-r6 ? 0 ,循环截至条件判断, blo 1b @ r5-r6小于0,说明.proc.info.init内的内容还没有遍历完,跳转到标号1处继续遍历。 mov r5, #0 @ unknown processor,如果遍历完还没有匹配的结果,直接给r5清空。 2: mov pc, lr @ 函数返回 /* * Look in include/asm-arm/procinfo.h and arch/arm/kernel/arch.[ch] for * more information about the __proc_info and __arch_info structures. */ .long __proc_info_begin .long __proc_info_end 3: .long . .long __arch_info_begin .long __arch_info_end

    3.3 proc_info结构体

    3.3.1 汇编定义结构体

    以arm920为例的proc_info结构体定义:

    /* /arch/arm/mm/proc-arm920.S */ .section ".proc.info.init", #alloc, #execinstr @ 将下述结构体指定存放在.proc.info.init节中。 .type __arm920_proc_info,#object @ 定义proc_info结构体并赋值 __arm920_proc_info: .long 0x41009200 @ cpuid .long 0xff00fff0 @ mask(掩码) .long PMD_TYPE_SECT | \ PMD_SECT_BUFFERABLE | \ PMD_SECT_CACHEABLE | \ PMD_BIT4 | \ PMD_SECT_AP_WRITE | \ PMD_SECT_AP_READ .long PMD_TYPE_SECT | \ PMD_BIT4 | \ PMD_SECT_AP_WRITE | \ PMD_SECT_AP_READ b __arm920_setup .long cpu_arch_name .long cpu_elf_name .long HWCAP_SWP | HWCAP_HALF | HWCAP_THUMB .long cpu_arm920_name .long arm920_processor_functions .long v4wbi_tlb_fns .long v4wb_user_fns #ifndef CONFIG_CPU_DCACHE_WRITETHROUGH .long arm920_cache_fns #else .long v4wt_cache_fns #endif .size __arm920_proc_info, . - __arm920_proc_info

    3.3.2 C语言结构体声明

    C语言定义的结构体是汇编定义的结构体的另外一种形式而已,各个成员的含义和偏移必须保证一致。

    /* /include/asm-arm/procinfo.h */ /* * Note! struct processor is always defined if we're * using MULTI_CPU, otherwise this entry is unused, * but still exists. * * NOTE! The following structure is defined by assembly * language, NOT C code. For more information, check: * arch/arm/mm/proc-*.S and arch/arm/kernel/head.S */ struct proc_info_list { unsigned int cpu_val; /*cpuid 值*/ unsigned int cpu_mask; /*cpuid 掩码*/ 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; };

    <完>

    Processed: 0.012, SQL: 8