系统加电起动后,MIPS处理器默认的程序入口是0xBFC00000,此地址在无缓存的KSEG1的地址区域内,对应的物理地址是

0x1FC00000,即CPU从0x1FC00000开始取第一条指令,这个地址在硬件上已经确定为FLASH的位置,Bootloader将

Linux内核映像拷贝到

RAM 中某个空闲地址处,然后一般有个内存移动操作,目的地址在arch/mips/Makefile内指定:

core-$(CONFIG_MIPS_ADM5120)+= arch/mips/adm5120/

load-$(CONFIG_MIPS_ADM5120)+= 0xffffffff80002000

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

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

OUTPUT_ARCH(mips)

ENTRY(kernel_entry)

jiffies = jiffies_64;

SECTIONS

{

. = 0xFFFFFFFF80002000;

/* read-only */

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

.text : {

*(.text)

...

这个文件最终会以参数-Xlinker --script -Xlinker

vmlinux.lds的形式传给gcc,并最终传给链接器ld来控制其行为。ld会将.text节的地址链接到0xFFFFFFFF80002000处。

关于内核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)

# kernel entry point

声明函数   kernel_entry,函数的堆栈为16 byte,返回地址保存在  $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

# cpu specific 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 temp temp2

#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 stackp temp 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)

最后跳转到  /init/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) };

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

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

kernel_entry()函数是体系结构相关的汇编语言,它首先初始化内核堆栈段,来为创建系统中的第一个进程进行准备,接着用一段循环将内核映像的未初始化数据段(bss段,在_edata和_end之间)清零,

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

下面讲述   start_kernel()函数。

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

asmlinkage void __init start_kernel(void)

{

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

char * command_line;

extern struct kernel_param __start___param[],

__stop___param[];

定义了核的参数数据结构

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

smp_setup_processor_id();

设置  SMP多核的  CPU核的ID号,单核不进行任何操作,我们不关心。

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

unwind_init();

MIPS体系结构中,这个函数是个空函数(可能调用setup_arch,配置核的相关函数)

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

lockdep_init();

初始化核依赖关系哈希表。

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

local_irq_disable();

关闭当前  CPU核的中断

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

early_boot_irqs_off();

通过一个静态全局变量

early_boot_irqs_enabled来帮助我们调试代码,

通过这个标记可以帮助我们知道是否在“early bootup code”,

也可以通过这个标志警告是否有无效的中断打开。

early_boot_irqs_on()函数配置使用,参考下面。

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

early_init_irq_lock_class();

每一个中断都有一个    IRQ描述符 (struct

irq_desc)来进行描述。

这个函数的主要作用是设置所有的   IRQ描述符 (struct

irq_desc)的锁是统一的锁,

还是每一个    IRQ描述符 (struct

irq_desc)都有一个小锁。

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

lock_kernel();

获取大内核锁,这种大内核锁锁定整个内核。

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

tick_init();

如果没有定义

CONFIG_GENERIC_CLOCKEVENTS宏定义,则这个函数为空函数,

如果定义了这个宏,这执行初始化  tick控制功能,注册clockevents的框架。

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

boot_cpu_init();

对于  CPU核的系统来说,设置第一个  CPU核为活跃

CPU核。

对于单  CPU核系统来说,设置CPU核为活跃  CPU核。

参考《linux-mips启动分析(2-1)》。

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

page_address_init();

当定义了CONFIG_HIGHMEM 宏,并且没有定义

WANT_PAGE_VIRTUAL 宏时,非空函数。

其他情况为空函数。

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

printk(KERN_NOTICE);

printk(linux_banner);

输出打印版本信息。

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

setup_arch(&command_line);

每种体系结构都有自己的  setup_arch()函数,这些是体系结构相关的。

如何确定编译那个体系结构的    setup_arch()函数呢?

主要由  linux源码树顶层  Makefile中

ARCH变量来决定的。

例如:  MIPS体系结构的。

SUBARCH := mips

ARCH

?= $(SUBARCH)

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

setup_command_line(command_line);

保存未改变的  comand_line 到字符数组

static_command_line[] 中。

保存

boot_command_line到字符数组     saved_command_line[]

中。

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

unwind_setup();

空函数。

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

setup_per_cpu_areas();

如果没有定义  CONFIG_SMP宏,则这个函数为空函数。

如果定义了    CONFIG_SMP宏,

则这个

setup_per_cpu_areas()函数给每个CPU分配内存,并拷贝   .data.percpu段的数据。

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

如果没有定义  CONFIG_SMP宏,则这个函数为空函数。

如果定义了    CONFIG_SMP宏,这个函数

smp_prepare_boot_cpu();

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

sched_init();

核心进程调度器初始化,调度器的初始化优先于任何中断的建立(包括  timer中断)。

并且初始化进程0,即  idle进程,但是并没有设置idle进程的

NEED_RESCHED标志,

以完成内核剩余的启动部分。

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

preempt_disable();

进制内核的抢占。使当前进程的   struct

thread_info结构  preempt_count成员的值增加1。

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

建立各个节点的管理区的  zonelist,便于分配内存的

fallback使用。

这个链表的作用: 这个链表是为了在一个分配不能够满足时可以考察下一个管理区来设置了。

在考察结束时,分配将从  ZONE_HIGHMEM回退到

ZONE_NORMAL,

在分配时从  ZONE_NORMAL退回到  ZONE_DMA就不会回退了。

build_all_zonelists();

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

page_alloc_init();

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

在  MIPS体系结构下,这个函数已经在

arch_mem_init() 函数中调用了一次。

这个函数的具体分析详细分析,请看《linux-mips启动分析(4)》。

所以这个函数直接返回。

parse_early_param();

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

打印  linux启动命令行参数。

printk(KERN_NOTICE "Kernel command line: %s\n",

boot_command_line);

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

这个函数的意思对  linux启动命令行参数进行再分析和处理。

这两个变量   __start___param和

__stop___param在

链接脚本

arch/mips/kernel/vmlinux.lds中定义。

最后一个参数为,当不能够识别  linux启动命令行参数时,调用的函数。

parse_args("Booting kernel",

static_command_line, __start___param,

__stop___param - __start___param, &unknown_bootoption);

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

检查中断是否已经打开了,如果已将打开了,关闭中断。

if (!irqs_disabled()) {

local_irq_disable();

}

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

sort_main_extable();

这个函数对内核建立的异常处理调用函数表(exception table)

根据异常的向量号进行堆排序。

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

设置  CPU的异常处理函数,TLB重填,cache出错,还有通用异常处理表的初始化。

trap_init();

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

初始化  RCU机制,这个步骤必须比本地  timer的初始化早。

rcu_init();

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

用来初始化中断处理硬件相关的寄存器和中断描述符数组     irq_desc[] 数组,

每个中断号都有一个对应的中断描述符。

参考《linux-mips启动分析(11)》。

init_IRQ();

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

系统在初始化阶段动态的分配了  4  个

hashtable,并把它们的地址存入  pid_hash[] 数组。

便于从PID查找 进程描述符地址。

linux-mips启动分析,linux mips启动分析 - MIPS技术及应用社区相关推荐

  1. linux mips 启动分析,Linux/MIPS启动分析

    Linux启动入口主要代码在 arch/mips/kernel/head.S文件中 kernel_entry函数以汇编形式出现 主要干了以下几件事: 1.  BSS段清0 2.  从boot传过来的参 ...

  2. Linux 启动流程即init程序分析--2

    3.upstart介绍     upstart是一个基于事件的init的替代程序,这意味着服务的启动和停止都基于事件的通信. upstart 正在由 Scott James Remnant 进行开发, ...

  3. 低温linux内核启动readl,Linux内核启动流程分析(一)

    很久以前分析的,一直在电脑的一个角落,今天发现贴出来和大家分享下.由于是word直接粘过来的有点乱,敬请谅解! S3C2410 Linux 2.6.35.7启动分析(第一阶段) 1.依据arch/ar ...

  4. linux内核参数分析,linux内核启动第一阶段分析

    linux内核启动第一阶段分析 http://blog.csdn.net/aaronychen/article/details/2838341 本文的很多内容是参考了网上某位大侠的文章写的<&l ...

  5. 从linux启动到rootfs的挂载分析 https://blog.csdn.net/kevin_hcy/article/details/17663341

    从linux启动到rootfs的挂载分析   2012-05-02 15:50:49|  分类:默认分类 |  标签:|字号大中小 订阅 简单的来说,根文件系统包括虚拟根文件系统和真实根文件系统.在K ...

  6. Linux内核分析实验3——分析linux内核启动过程

    本文大量内容引用自孟宁老师在<LINUX操作系统分析>课程中的内容 <Linux内核分析>MOOC课程 http://www.xuetangx.com/courses/cour ...

  7. linux内核启动分析 三,Linux内核分析 实验三:跟踪分析Linux内核的启动过程

    贺邦 + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程 http://mooc.study.163.com/course/USTC-1000029000 一. 实验过程 ...

  8. 通过从代码层面分析Linux内核启动来探知操作系统的启动过程

    通过从代码层面分析Linux内核启动来探知操作系统的启动过程 前言说明 本篇为网易云课堂Linux内核分析课程的第三周作业,我将围绕Linux 3.18的内核中的start_kernel到init进程 ...

  9. Linux 启动流程即init程序分析--1

    1.init程序剖析     init进程是内核引导过程完成时创建的第一个进程.Linux使用了init进程来对组成Linux的服务和应用程序进行初始化.     当 init 进程启动时(使用传统的 ...

最新文章

  1. python bs4 + requests4 简单爬虫
  2. 树莓派上利用 Tensorflow 实现小车的自动驾驶
  3. 【APICloud系列|35】APICLloud开源官方模块
  4. 两个栈实现一个队列/两个队列实现一个栈
  5. mybatis-plus的 mapper.xml 路径配置的坑
  6. 论文盘点:CVPR 2018 Top 20,华人发明的SENet居首!
  7. android插件数字,Android自定义控件实现带文本与数字的圆形进度条
  8. 操作系统————P1 概念、功能和目标
  9. 深入理解 WordPress 数据库中的用户数据 wp_user
  10. C 线性表的链式存储实现及插入、删除等操作示例
  11. linux运维搭建官网,Linux运维学习之LAMP搭建个人博客网站
  12. python中tab的用法_详解Python中expandtabs()方法的使用
  13. paip.flex or Silverlight
  14. whois信息收集企业备案信息
  15. Operation CREATE USER failed for 'lin'@'host'
  16. 公司账号服务单点登录到gitlab
  17. linux drm 内存管理,linux DRM GEM 笔记
  18. ValueError: With n_samples=0, test_size=0.3 and train_size=None, the resulting train set will be emp
  19. 服务器基本故障及排查方法
  20. JS或JQuery动态创建Html元素的一些方法

热门文章

  1. 姓雷取名:雷姓好听到爆的女孩名字
  2. DICOM图形转换(四)-- RTDOSE的DVH在WEB上显示
  3. linux移植过程中近日遇到问题汇总贴
  4. 服务器8g内存条显示只有7g,win7系统电脑8G内存只显示7.86G可用的解决方法
  5. pikachu靶场RCE的学习
  6. Idea 设置快捷键Ctrl + W关闭当前界面
  7. 【实验课】一群人围一圈,隔一个杀一个,剩一个,最后活谁?
  8. 软件及工具下载与学习视频下载
  9. OpenFOAM无反射边界条件源码学习
  10. ApacheDS 安装以及LDAPS配置(图文)