1. 概述

之前已经写了几篇Linux内核启动相关的文章,比如:《解压内核镜像》《调用 start_kernel》都是用汇编语言写的,这些代码的作用仅仅是把内核镜像放置到特定的位置,同时配置好C语言的运行环境,再有就是简单的把内核镜像所在区域的页表设置一下,在开启MMU之后就正式开始了C语言代码的执行,C语言代码的入口是start_kernel这个函数,本文要介绍其中的set_arch这个函数,该函数的作用是查找给定机器ID的数据结构信息、配置内存条信息、解析bootloader传递命令行参数,然后根据machine_desc结构体所记录的信息对机器进行一些必要的设置,最后开始正式建立完整的页表,大致流程如下图所示。

2. set_processor

该函数首先调用汇编代码来查找给定机器ID的proc_info数据,找到之后取出其中的processor结构体,该结构体中包含了很多任务切换相关的底层函数。

  1. /* arch/arm/kernel/setup.c */
  2. list = lookup_processor_type(read_cpuid_id());
  3. /* arch/arm/kernel/head-common.S */
  4. ENTRY(lookup_processor_type)
  5. stmfd sp!, {r4 - r6, r9, lr}
  6. mov r9, r0
  7. bl __lookup_processor_type
  8. mov r0, r5
  9. ldmfd sp!, {r4 - r6, r9, pc}
  10. ENDPROC(lookup_processor_type)

cacheid_init函数根据CPU ID设置缓冲相关的标志位;cpu_init调用刚刚找到的processor中的processor._proc_init函数,不过该函数没有做什么实际操作。

  1. /* arch/arm/mm/proc-v7.S */
  2. ENTRY(cpu_v7_proc_init)
  3. mov pc, lr
  4. ENDPROC(cpu_v7_proc_init)

设置内核启动时所在CPU不同异常模式下的栈指针。

  1. /* arch/arm/kernel/setup.c::cpu_init */
  2. __asm__ (
  3. "msr cpsr_c, %1\n\t"
  4. "add r14, %0, %2\n\t"
  5. "mov sp, r14\n\t"
  6. "msr cpsr_c, %3\n\t"
  7. "add r14, %0, %4\n\t"
  8. "mov sp, r14\n\t"
  9. "msr cpsr_c, %5\n\t"
  10. "add r14, %0, %6\n\t"
  11. "mov sp, r14\n\t"
  12. "msr cpsr_c, %7"
  13. :
  14. : "r" (stk),
  15. PLC (PSR_F_BIT | PSR_I_BIT | IRQ_MODE),
  16. "I" (offsetof(struct stack, irq[0])),
  17. PLC (PSR_F_BIT | PSR_I_BIT | ABT_MODE),
  18. "I" (offsetof(struct stack, abt[0])),
  19. PLC (PSR_F_BIT | PSR_I_BIT | UND_MODE),
  20. "I" (offsetof(struct stack, und[0])),
  21. PLC (PSR_F_BIT | PSR_I_BIT | SVC_MODE)
  22. : "r14");

3. setup_machine_tags

根据机器ID查找machine_desc结构体,如果没有找到就打印一条提示信息,然后直接宕机。而此时使用的打印函数是early_print的话,再根据《printk流程分析》,此时其实还没有注册console驱动,因此如果没有打开early_printk功能,则系统就悄无声息的死机了。

  1. /* arch/arm/kernel/setup.c */
  2. for_each_machine_desc(p)
  3. if (nr == p->nr) {
  4. printk("Machine: %s\n", p->name);
  5. mdesc = p;
  6. break;
  7. }

找到mdesc之后,执行mdesc->fixup(),该调用实际执行的函数是定义在cpu.c中的cpu_fixup函数,该函数的作用是设置内存条个数以及对应物理起始地址和大小。

  1. /* arch/arm/kernel/setup.c */
  2. if (mdesc->fixup)
  3. mdesc->fixup(tags, &from, &meminfo);
  4. /* arch/arm/mach-s5p4418/cpu.c */
  5. MACHINE_START(S5P4418, NXP_MACH_NAME)
  6. .atag_offset = 0x00000100,
  7. .fixup = cpu_fixup,
  8. .map_io = cpu_map_io,
  9. .init_irq = nxp_cpu_init_irq,
  10. #ifdef CONFIG_ARM_GIC
  11. .handle_irq = gic_handle_irq,
  12. #else
  13. .handle_irq = vic_handle_irq,
  14. #endif
  15. .timer = &nxp_cpu_sys_timer,
  16. .init_machine = cpu_init_machine,
  17. #if defined CONFIG_CMA && defined CONFIG_ION
  18. .reserve = cpu_mem_reserve,
  19. #endif
  20. MACHINE_END
  21. static void __init cpu_fixup(...)
  22. {
  23. mi->nr_banks = 1;
  24. mi->bank[0].start = CFG_MEM_PHY_SYSTEM_BASE;
  25. #if !defined(CFG_MEM_PHY_DMAZONE_SIZE)
  26. mi->bank[0].size = CFG_MEM_PHY_SYSTEM_SIZE;
  27. #else
  28. mi->bank[0].size = CFG_MEM_PHY_SYSTEM_SIZE + CFG_MEM_PHY_DMAZONE_SIZE;
  29. #endif
  30. }

接下来就是解析bootloader传递的命令行参数,通过tag->hdr.tag查找内核中预置的对应类型tag的解析函数,然后调用对应类型tag的parse函数即可实现对参数的解析。

  1. /* arch/arm/kernel/setup.c */
  2. static int __init parse_tag(const struct tag *tag)
  3. {
  4. extern struct tagtable __tagtable_begin, __tagtable_end;
  5. struct tagtable *t;
  6. for (t = &__tagtable_begin; t < &__tagtable_end; t++)
  7. if (tag->hdr.tag == t->tag) {
  8. t->parse(tag);
  9. break;
  10. }
  11. return t < &__tagtable_end;
  12. }
  13. /* arch/arm/kernel/setup.h */
  14. struct tagtable {
  15. __u32 tag;
  16. int (*parse)(const struct tag *);
  17. };

4. 总结

为了避免文章篇幅太长,所以会拆分成三四篇来写,下面是本文的总结:

  • setup_processor:根据给定机器ID查找机器描述信息,然后再根据CPU ID设置cache相关的标志位,再执行processor._proc_init对处理器进行初始化,最后设置CPU不同异常模式下的栈指针;
  • setup_machine_tags:根据机器ID查找machine_desc结构体,然后执行cpu_fixup函数配置内存条信息,最后解析bootloader传递的命令行参数。

Linux内核源码分析之set_arch (一)的更多相关文章

  1. Linux内核源码分析--内核启动之(3)Image内核启动(C语言部分)(Linux-3.0 ARMv7)

    http://blog.chinaunix.net/uid-20543672-id-3157283.html Linux内核源码分析--内核启动之(3)Image内核启动(C语言部分)(Linux-3 ...

  2. Linux内核源码分析 day01——内存寻址

    前言 Linux内核源码分析 Antz系统编写已经开始了内核部分了,在编写时同时也参考学习一点Linux内核知识. 自制Antz操作系统 一个自制的操作系统,Antz .半图形化半命令式系统,同时嵌入 ...

  3. Linux内核源码分析--内核启动之(6)Image内核启动(do_basic_setup函数)(Linux-3.0 ARMv7)【转】

    原文地址:Linux内核源码分析--内核启动之(6)Image内核启动(do_basic_setup函数)(Linux-3.0 ARMv7) 作者:tekkamanninja 转自:http://bl ...

  4. Linux内核源码分析--内核启动之(4)Image内核启动(setup_arch函数)(Linux-3.0 ARMv7)【转】

    原文地址:Linux内核源码分析--内核启动之(4)Image内核启动(setup_arch函数)(Linux-3.0 ARMv7) 作者:tekkamanninja 转自:http://blog.c ...

  5. Linux内核源码分析方法_转

    Linux内核源码分析方法 转自:http://www.cnblogs.com/fanzhidongyzby/archive/2013/03/20/2970624.html 一.内核源码之我见 Lin ...

  6. Linux内核源码分析之setup_arch (二)

    1. 概述 接着上一篇<Linux内核源码分析之setup_arch (一)>继续分析,本文首先分析arm_memblock_init函数,然后分析内核启动阶段的是如何进行内存管理的. 2 ...

  7. Linux内核源码分析之setup_arch (三)

    1. 前言 在 Linux内核源码分析之setup_arch (二) 中介绍了当前启动阶段的内存分配函数memblock_alloc,该内存分配函数在本篇将要介绍paging_init中用于页表和内存 ...

  8. Linux内核源码分析之setup_arch (四)

    前言 Linux内核源码分析之setup_arch (三) 基本上把setup_arch主要的函数都分析了,由于距离上一篇时间比较久了,所以这里重新贴一下大致的流程图,本文主要分析的是bootmem_ ...

  9. Linux内核源码分析方法

    一.内核源码之我见 Linux内核代码的庞大令不少人“望而生畏”,也正因为如此,使得人们对Linux的了解仅处于泛泛的层次.如果想透析Linux,深入操作系统的本质,阅读内核源码是最有效的途径.我们都 ...

随机推荐

  1. rabbitmq与erlang版本

    来源自https://www.rabbitmq.com/which-erlang.html erlang安装包下载地址 https://packages.erlang-solutions.com/er ...

  2. maven项目导入并运行

    idea导入maven工程流程 找到要导入的文件位置 打开导入 选择manve 一直next就好 选择jdk,选择自己的jdk--home就可以 点击finished 等待坐标导入,查看右侧maven ...

  3. Stream(三)

    public class Test08 { /* * 二.中间的加工操作 * (1)filter(Predicate p):过滤 * (2)distinct():去重 * (3)limit(long  ...

  4. 支持向量机SVM基本问题

    1.SVM的原理是什么? SVM是一种二类分类模型.它的基本模型是在特征空间中寻找间隔最大化的分离超平面的线性分类器.(间隔最大是它有别于感知机) 试图寻找一个超平面来对样本分割,把样本中的正例和反例 ...

  5. 一些常见JAVA问题

    原文:https://blog.csdn.net/weiyongxuan/article/details/45920765 一.Java的异常的基类是java.lang.Throwable 二.守护线 ...

  6. 微信小程序UI自动化: minium文档部署

    目录 参考资料 1. 在线文档(临时) 2. 本地部署 参考资料 https://git.weixin.qq.com/minitest/minium-doc 1. 在线文档(临时) 其实上面的链接里面 ...

  7. drf 视图使用及源码分析

    前言 drf视图的源码非常的绕,但是实现的功能却非常的神奇. 它能够帮你快速的解决ORM增删改查的重复代码,非常的方便好用. 下面是它源码中的一句话: class ViewSetMixin: &quo ...

  8. 关于eslint的使用与配置,以及prettier的使用

    eslint官网:https://eslint.bootcss.com/docs/user-guide/getting-started eslint配置:http://eslint.cn/docs/r ...

  9. DTU有哪些功能特点?

    DTU指的是一种数据终端设备(Data Terminal unit),可以把它简单理解为下位GPRS发射终端,在进行通信的时候,传输数据的链路两端肯定是存在DTU,所传信息进行格式转换和数据整理效验都 ...

  10. C# 中的 in 参数和性能分析

    in 修饰符也是从 C# 7.2 开始引入的,它与我们上一篇中讨论的 <C# 中的只读结构体(readonly struct)>[1] 是紧密相关的. in 修饰符 in 修饰符通过引用传 ...