我是靠谱客的博主 野性花卷,最近开发中收集的这篇文章主要介绍linux mips 启动分析,Linux/MIPS启动分析,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

Linux启动入口主要代码在 arch/mips/kernel/head.S文件中 kernel_entry函数以汇编形式出现

主要干了以下几件事:

1.  BSS段清0

2.  从boot传过来的参数赋值到全局变量

3. clear context register

4. 根据init_thread_union建立$gp寄存器  并设置$sp寄存器,堆栈指针    (PTR_LA      $28, init_thread_union)

5. 做好上述准备后就跳转到/arch/mips/kernel/main.c中的start_kernel()初始化硬件平台相关的代码

主要涉及的数据结构 在arch/mips/kernel/init_task.c

union thread_unioninit_thread_union__init_task_data

__attribute__((__aligned__(THREAD_SIZE))) =

{ INIT_THREAD_INFO(init_task) };

THREAD_SIZE在这里为8K,__attribute__((__aligned__(THREAD_SIZE)))表示这个数据结构以8K对齐

struct task_struct init_task = INIT_TASK(init_task);

union thread_union {

struct thread_info thread_info;

unsigned long stack[THREAD_SIZE/sizeof(long)];

};

套用linux设计与实现的图表示Thread_info、stack、task_struct的关系

0818b9ca8b590ca3270a3433284dd417.png

在mips的head.S的工作就是

0818b9ca8b590ca3270a3433284dd417.png

这就是传说中的0号进程

1. 进程0是所有其他进程的祖先, 也称作idle进程或swapper进程。

2. 进程0是在系统初始化时由kernel自身从无到有创建。(过程集中在start_kernel函数内)

3. 进程0的数据成员大部分是静态定义的,即由预先定义好的(如上)INIT_TASK, INIT_MM等宏初始化。

进程0的描述符init_task定义在arch/arm/kernel/init_task.c,由INIT_TASK宏初始化。 init_mm等结构体定义在include/linux/init_task.h内,为init_task成员的初始值,分别由对应的初始化宏如INIT_MM等初始化

下面是转载网上大虾的详细分析

系统加电起动后,MIPS 处理器默认的程序入口是0xBFC00000,此地址在无缓存的KSEG1的地址区域内,对应的物理地址是 0x1FC00000,即CPU从0x1FC00000开始取第一条指令,这个地址在硬件上已经确定为FLASH的位置,Bootloader将 Linux 内核映像拷贝到  RAM  中某个空闲地址处,然后一般有个内存移动操作,目的地址在 arch/mips/Makefile 内指定:

load-$(CONFIG_MIPS_PB1550) += 0xFFFFFFFF80100000,

则最终bootloader定会将内核移到物理地址   0x00100000  处。

上面Makefile 里指定的的 load 地址,最后会被编译系统写入到 arch/mips/kernel/vmlinux.lds 中:

OUTPUT_ARCH(mips)

ENTRY(kernel_entry)

jiffies = jiffies_64;

SECTIONS

{

. = 0xFFFFFFFF80100000;

/* read-only */

_text = .; /* Text and read-only data */

.text : {

*(.text)

...

这个文件最终会以参数 -Xlinker --script -Xlinker vmlinux.lds 的形式传给 gcc,并最终传给链接器 ld 来控制其行为。

ld 会将 .text 节的地址链接到 0xFFFFFFFF80100000 处。

关于内核 ELF 文件的入口地址(Entry point),即 bootloader 移动完内核后,直接跳转到的地址,由ld 写入 ELF的头中,其会依次用下面的方法尝试设置入口点,当遇到成功时则停止:

a. 命令行选项 -e entry

b. 脚本中的 ENTRY(symbol)

c. 如果有定义 start 符号,则使用start符号(symbol)

d. 如果存在 .text 节,则使用第一个字节的地址。

e. 地址0

注意到上面的 ld script 中,用 ENTRY 宏设置了内核的 entry point 是 kernel_entry,因此内核取得控制权后执行的第一条指令是在 kernel_entry 处。

*********************************************

linux  内核启动的第一个阶段是从  /arch/mips/kernel/head.s文件开始的。

而此处正是内核入口函数kernel_entry(),该函数定义在 /arch/mips/kernel/head.s文件里。

kernel_entry()函数是体系结构相关的汇编语言,它首先初始化内核堆栈段,来为创建系统中的第一个进程进行准备,

接着用一段循环将内核映像的未初始化数据段(bss段,在_edata和_end之间)清零,

最后跳转到  /init/main.c 中的 start_kernel()初始化硬件平台相关的代码。

*********************************************

NESTED(kernel_entry, 16,sp)            # kernelentry point

声明函数   kernel_entry,函数的堆栈为 16byte,返回地址保存在  $sp 寄存器中。

-----------------------------

声明函数入口

#define NESTED(symbol, framesize,rpc)                 

.globl symbol;                        

.align 2;                             

.type  symbol,@function;              

.ent   symbol,0;                      

symbol:     .frame  sp, framesize, rpc

汇编伪指令  frame 用来声明堆栈布局。

它有三个参数:

1)第一个参数  framereg:声明用于访问局部堆栈的寄存器,一般为  $sp。

2)第二个参数  framesize:申明该函数已分配堆栈的大小,应该符合  $sp + framesize = 原来的  $sp。

3)第三个参数  returnreg:这个寄存器用来保存返回地址。

----------------------------

kernel_entry_setup          # cpuspecific setup

----------------------------

这个宏一般为空的,在 include/asm-mips/mach-generic/kernel-entry-init.h 文件中定义。

某些MIPS CPU需要额外的设置一些控制寄

存器,和具体的平台相关,一般为空宏;某些多核MIPS,启动时所

有的core的入口一起指向   kernel_entry,然后在该宏里分叉,

boot core 继续往下,其它的则不停的判断循环,直到boot core 唤醒之

----------------------------

setup_c0_status_pri

设置   cp0_status 寄存器

----------------------------

.macro  setup_c0_status_pri

#ifdef CONFIG_64BIT

setup_c0_status ST0_KX 0

#else

setup_c0_status 0 0

#endif

.endm

----------------------------

ARC64_TWIDDLE_PC

除非 CONFIG_ARC64,否则为空操作

-----------------------------

#ifdef CONFIG_MIPS_MT_SMTC

mtc0    zero, CP0_TCCONTEXT__bss_start

mfc0    t0, CP0_STATUS

ori t0, t0, 0xff1f

xori    t0, t0, 0x001e

mtc0    t0, CP0_STATUS

#endif /* CONFIG_MIPS_MT_SMTC */

宏定义   CONFIG_MIPS_MT_SMTC 是使用多核的 SMTC Linux 时定义的。一般情况下不考虑。

MIPS已经开发出  SMP Linux的改进版,叫做SMTC(线程上下文对称多处理) Linux。

SMTC Linux能理解轻量级  TC 的概念,并能因此减少某些与SMP Linux相关的开销。

----------------------------

PTR_LA      t0,__bss_start     # clear .bss

LONG_S      zero, (t0)

PTR_LA      t1, __bss_stop -LONGSIZE

1:

PTR_ADDIU   t0, LONGSIZE

LONG_S      zero, (t0)

bne     t0, t1, 1b

清除  BSS 段,清 0。

变量   __bss_start  和  __bss_stop 在连接文件arch/mips/kernel/vmlinux.lds 中定义。

--------------------------------

LONG_S      a0,fw_arg0     # firmware arguments

LONG_S      a1, fw_arg1

LONG_S      a2, fw_arg2

LONG_S      a3, fw_arg3

把  bootloader 传递给内核的启动参数保存在 fw_arg0,fw_arg1,fw_arg2,fw_arg3 变量中。

变量  fw_arg0 为内核参数的个数,其余分别为字符串指针,为  *** = XXXX  的格式。

----------------------------------

MTC0        zero,CP0_CONTEXT   # clear context register

清除  CP0 的 context register,这个寄存器用来保存页表的起始地址。

----------------------------------

PTR_LA      $28, init_thread_union

初始化  $gp 寄存器,这个寄存器的地址指向一个  union,

THREAD_SIZE  大小,最低处是一个thread_info 结构

---------------------------------

PTR_LI      sp, _THREAD_SIZE - 32

PTR_ADDU    sp, $28

设置  $sp 寄存器,堆栈指针。  $sp = (init_thread_union 的地址) +  _THREAD_SIZE- 32

的得出  $sp 指向这个  union  结构的结尾地址  - 32 字节地址。

-----------------------------------

set_saved_sp    sp, t0, t1

把  这个 CPU 核的堆栈地址  $sp 保存到  kernelsp[NR_CPUS] 数组。

---------------------------------

如果定义了  CONFIG_SMP 宏,即多  CPU 核。

.macro  set_saved_sp stackp temptemp2

#ifdef CONFIG_MIPS_MT_SMTC

mfc0    temp,CP0_TCBIND

#else

MFC0    temp,CP0_CONTEXT

#endif

LONG_SRL    temp,PTEBASE_SHIFT

LONG_S  stackp,kernelsp(temp)

.endm

如果没有定义  CONFIG_SMP 宏,单  CPU 核。

.macro  set_saved_sp stackptemp temp2

LONG_S  stackp, kernelsp

.endm

变量  kernelsp 的定义,在 arch/mips/kernel/setup.c 文件中。

unsigned long kernelsp[NR_CPUS];

把  这个 CPU 核的堆栈地址  $sp 保存到  kernelsp[NR_CPUS] 数组。

---------------------------------

PTR_SUBU    sp, 4 *SZREG       # init stack pointer

---------------------------------

j       start_kernel

END(kernel_entry)

最后跳转到  /arch/mips/kernel/main.c 中的 start_kernel()初始化硬件平台相关的代码。

----------------------------------

******************************************

这个   init_thread_union 变量在 arch/mips/kernel/init_task.c 文件中定义。

union thread_union init_thread_union

__attribute__((__section__(".data.init_task"),

__aligned__(THREAD_SIZE))) =

{ INIT_THREAD_INFO(init_task) };

******************************************

问题:

1)这个  init_thread_union 结构体指针是怎么初始化的?

******************************************

最后

以上就是野性花卷为你收集整理的linux mips 启动分析,Linux/MIPS启动分析的全部内容,希望文章能够帮你解决linux mips 启动分析,Linux/MIPS启动分析所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(34)

评论列表共有 0 条评论

立即
投稿
返回
顶部