1. 概述

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

2. set_processor

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

/* arch/arm/kernel/setup.c */
list = lookup_processor_type(read_cpuid_id());
/* arch/arm/kernel/head-common.S */
ENTRY(lookup_processor_type)
stmfd sp!, {r4 - r6, r9, lr}
mov r9, r0
bl __lookup_processor_type
mov r0, r5
ldmfd sp!, {r4 - r6, r9, pc}
ENDPROC(lookup_processor_type)

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

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

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

/* arch/arm/kernel/setup.c::cpu_init */
__asm__ (
"msr cpsr_c, %1\n\t"
"add r14, %0, %2\n\t"
"mov sp, r14\n\t"
"msr cpsr_c, %3\n\t"
"add r14, %0, %4\n\t"
"mov sp, r14\n\t"
"msr cpsr_c, %5\n\t"
"add r14, %0, %6\n\t"
"mov sp, r14\n\t"
"msr cpsr_c, %7"
:
: "r" (stk),
PLC (PSR_F_BIT | PSR_I_BIT | IRQ_MODE),
"I" (offsetof(struct stack, irq[0])),
PLC (PSR_F_BIT | PSR_I_BIT | ABT_MODE),
"I" (offsetof(struct stack, abt[0])),
PLC (PSR_F_BIT | PSR_I_BIT | UND_MODE),
"I" (offsetof(struct stack, und[0])),
PLC (PSR_F_BIT | PSR_I_BIT | SVC_MODE)
: "r14");

3. setup_machine_tags

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

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

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

/* arch/arm/kernel/setup.c */
if (mdesc->fixup)
mdesc->fixup(tags, &from, &meminfo);
/* arch/arm/mach-s5p4418/cpu.c */
MACHINE_START(S5P4418, NXP_MACH_NAME)
.atag_offset = 0x00000100,
.fixup = cpu_fixup,
.map_io = cpu_map_io,
.init_irq = nxp_cpu_init_irq,
#ifdef CONFIG_ARM_GIC
.handle_irq = gic_handle_irq,
#else
.handle_irq = vic_handle_irq,
#endif
.timer = &nxp_cpu_sys_timer,
.init_machine = cpu_init_machine,
#if defined CONFIG_CMA && defined CONFIG_ION
.reserve = cpu_mem_reserve,
#endif
MACHINE_END
static void __init cpu_fixup(...)
{
mi->nr_banks = 1;
mi->bank[0].start = CFG_MEM_PHY_SYSTEM_BASE;
#if !defined(CFG_MEM_PHY_DMAZONE_SIZE)
mi->bank[0].size = CFG_MEM_PHY_SYSTEM_SIZE;
#else
mi->bank[0].size = CFG_MEM_PHY_SYSTEM_SIZE + CFG_MEM_PHY_DMAZONE_SIZE;
#endif
}

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

/* arch/arm/kernel/setup.c */
static int __init parse_tag(const struct tag *tag)
{
extern struct tagtable __tagtable_begin, __tagtable_end;
struct tagtable *t; for (t = &__tagtable_begin; t < &__tagtable_end; t++)
if (tag->hdr.tag == t->tag) {
t->parse(tag);
break;
} return t < &__tagtable_end;
}
/* arch/arm/kernel/setup.h */
struct tagtable {
__u32 tag;
int (*parse)(const struct tag *);
};

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. C# 使用MySQL事务的使用方法

    //使用事务来处理多条数据,如果不成功则回滚 public void getCheckListSubmit() { string _conStr = "................&qu ...

  2. Linux入门到放弃之八《任务计划管理》

    任务计划管理 1.每周一下午5:50将/data目录下的所有目录和文件归档并压缩为:backup.tar.gz 放在/home/backup目录下. 先新建/data目录,并在目录中随意生成几个文件 ...

  3. 1. Deep Q-Learning

    传统的强化学习算法具有很强的决策能力,但难以用于高维空间任务中,需要结合深度学习的高感知能力,因此延展出深度强化学习,最经典的就是DQN(Deep Q-Learning). DQN 2013 DQN的 ...

  4. oracle oracle sqldeveloper 12505 创建连接失败

    ref:http://blog.csdn.net/yangwenxue_admin/article/details/45062557

  5. 010_Java基础语法

    目录 Java基础语法 注释 单行注释 // 多行注释 /* */ 文档注释 /** */ 标识符 关键字 标识符注意点 数据类型 强类型语言 弱类型语言 Java基础语法 注释 单行注释 // 多行 ...

  6. angular页面

    <!DOCTYPE html><!--[if lt IE 9]> <html lang="zh" xmlns:ng="http://angu ...

  7. D. Road to Post Office 解析(思維)

    Codeforce 702 D. Road to Post Office 解析(思維) 今天我們來看看CF702D 題目連結 題目 略,請直接看原題. 前言 原本想說會不會也是要列式子解或者二分搜,沒 ...

  8. C语言之 判断语句基础与if语句反汇编

    0x01.判断语句介绍以及用法 判断语句有哪些? 1.If 用法1: if (条件) { //代码块 } 当条件成立,也就是为True时,执行{}中的代码 用法2: if(条件) { //代码块 } ...

  9. SpringBoot+Mybatis_Plus Generator

    AutoGenerator 是 MyBatis-Plus 的代码生成器,通过 AutoGenerator 可以快速生成 Entity.Mapper.Mapper XML.Service.Control ...

  10. 12 Servlet_04 Servlet增删改查 静态页面与动态页面 EL表达式 table表格的一些样式

    今天学习了servlet的增删改查: 存储数据 setAttribute(String name,Object obj );获取数据 getAttribute(String name);删除数据 re ...