(一)Linux源码分析-硬件开机与引导
硬件开机
从硬件开机到系统启动,都经历了什么
BIOS做完计算机的初始化后,将会选择启动设备的第一个扇区进行加载,并将CPU控制权转移到
0x7C00的位置,将由Bootloader完成从实模式到保护模式的切换
bootsect.s
start
start就是Linux的bios启动程序入口,被加载到0x7c00处 给ds赋值BOOTSEG作为数据加载段,es为INITSEG作为代码拷贝处,使用rep汇编指令身内存拷贝到INITSEG,并跳转到INITSEG执行。
start:mov ax,#BOOTSEGmov ds,axmov ax,#INITSEGmov es,axmov cx,#256sub si,sisub di,direpmovwjmpi go,INITSEG
INITSEG
设置堆栈0x9ff00
go: mov ax,csmov ds,axmov es,ax
! put stack at 0x9ff00.mov ss,axmov sp,#0xFF00 ! arbitrary value >>512
使用BIOS中断INT 13从磁盘第二个扇区加载setup
load_setup:
! ah = 0x02 - 读磁盘扇区到内存;al = 需要读出的扇区数量;
! ch = 磁道(柱面)号的低8 位; cl = 开始扇区(0-5 位),磁道号高2 位(6-7);
! dh = 磁头号; dl = 驱动器号(如果是硬盘则要置位7);
! es:bx ??指向数据缓冲区; 如果出错则CF 标志置位。mov dx,#0x0000 ! drive 0, head 0mov cx,#0x0002 ! sector 2, track 0mov bx,#0x0200 ! address = 512, in INITSEGmov ax,#0x0200+SETUPLEN ! service 2, nr of sectorsint 0x13 ! read itjnc ok_load_setup ! ok - continuemov dx,#0x0000mov ax,#0x0000 ! reset the disketteint 0x13j load_setup
打印字符串(省略代码),验证读取的扇区并跳转到setup.s 程序的开始处,程序继续执行,bootsect.s职责完成。
seg csmov ax,root_devcmp ax,#0jne root_definedseg csmov bx,sectorsmov ax,#0x0208 ! /dev/ps0 - 1.2Mbcmp bx,#15je root_definedmov ax,#0x021c ! /dev/PS0 - 1.44Mbcmp bx,#18je root_defined
undef_root:jmp undef_root
root_defined:seg csmov root_dev,ax! after that (everyting loaded), we jump to
! the setup-routine loaded directly after
! the bootblock:jmpi 0,SETUPSEG
setup.s
setup负责从bios中读取数据并将它复制到系统内存中
start
...!获取扩展内存大小
mov ah,#0x88
int 0x15
mov [2],ax!获取显示卡的模式
mov ah,#0x0f
int 0x10
mov [4],bx ! bh = display page
mov [6],ax ! al = video mode, ah = window width!检查显示方式(EGA/VGA)并取参数。
mov ah,#0x12
mov bl,#0x10
int 0x10
mov [8],ax
mov [10],bx
mov [12],cx...
移动内存
将system移到正确的地方,把从0x10000 到0x8ffff移动到0x00000位置。
mov ax,#0x0000cld ! 'direction'=0, movs moves forward
do_move:mov es,ax ! destination segmentadd ax,#0x1000cmp ax,#0x9000jz end_movemov ds,ax ! source segmentsub di,disub si,simov cx,#0x8000repmovswjmp do_move
IDT,GDT
idt_48:
!描述表长度.word 0 ! idt limit=0
!描述表基地址.word 0,0 ! idt base=0Lgdt_48:
!段最大长度限制.word 0x800 ! gdt limit=2048, 256 GDT entries
!描述表基地址.word 512+gdt,0x9 ! gdt base = 0X9xxxx
将内存移动完成后,使用lidt 加载中断描述符寄存器(IDT)。lgdt加载全局描述符表
end_move:!恢复ax寄存器的SETUPSEG值mov ax,#SETUPSEG ! right, forgot this at first. didn't work :-)mov ds,axlidt idt_48 ! load idt with 0,0lgdt gdt_48 ! load gdt with whatever appropriate
保护模式
主要是通过修改CR0寄存器进入保护模式
段选择符长度为16位
位0-1表示请求的特权级0-3(0-3环)
位1-2标识是全局或者局部描述符
位3-15是描述符表项的索引
段8(0b0000,0000,0000,1000)
表示请求特权级0、使用全局描述符表中的第1 项,由下面的代码可知基地址是0
gdt:.word 0,0,0,0 ! dummy.word 0x07FF ! 8Mb - limit=2047 (2048*4096=8Mb).word 0x0000 ! base address=0.word 0x9A00 ! code read/exec.word 0x00C0 ! granularity=4096, 386.word 0x07FF ! 8Mb - limit=2047 (2048*4096=8Mb).word 0x0000 ! base address=0.word 0x9200 ! data read/write.word 0x00C0 ! granularity=4096, 386
lmsw修改cr0寄存器进入保护模式,然后跳转到cs段8 offset0处,执行system的代码
mov ax,#0x0001 ! protected mode (PE) bitlmsw ax ! This is it!!跳转到cs段8 offset0处jmpi 0,8 ! jmp offset 0 of segment 8 (cs)
head.s
head.s是从绝对地址0x00000000开始
startup_32
startup_32:!装载段寄存器movl $0x10,%eaxmov %ax,%dsmov %ax,%esmov %ax,%fsmov %ax,%gs!设置系统堆栈lss _stack_start,%esp!装载idt,gdt子程序call setup_idtcall setup_gdtmovl $0x10,%eax # reload all the segment registersmov %ax,%ds # after changing gdt. CS was alreadymov %ax,%es # reloaded in 'setup_gdt'mov %ax,%fsmov %ax,%gslss _stack_start,%esp!A20 地址线是否已经开启xorl %eax,%eax
1: incl %eax # check that A20 really IS enabledmovl %eax,0x000000 # loop forever if it isn'tcmpl %eax,0x100000je 1b...jmp after_page_tables
调用main函数
after_page_tables:pushl $0 # These are the parameters to main :-)pushl $0pushl $0pushl $L6!压入跳转的main地址pushl $_mainjmp setup_paging...!首先对5 页内存(1 页目录 + 4 页页表)清零movl $1024*5,%ecxxorl %eax,%eaxxorl %edi,%edicld;rep;stosl!设置页目录中的项movl $pg0+7,_pg_dirmovl $pg1+7,_pg_dir+4movl $pg2+7,_pg_dir+8movl $pg3+7,_pg_dir+12movl $pg3+4092,%edi!最后1 项对应物理内存页面的地址是0xfff000,加上属性标志7,即为0xfff007movl $0xfff007,%eaxstd
1: stoslsubl $0x1000,%eaxjge 1bxorl %eax,%eax!设置启动使用分页处理(cr0 的PG 标志,位31)movl %eax,%cr3movl %cr0,%eaxorl $0x80000000,%eaxmovl %eax,%cr0!弹出压入的main地址,开始执行main.c程序ret
head.s执行完后的内存视图
关注我的技术公众号
不定期分析各种技术文章
(一)Linux源码分析-硬件开机与引导相关推荐
- linux源码分析之cpu初始化 kernel/head.s,linux源码分析之cpu初始化
linux源码分析之cpu初始化 kernel/head.s 收藏 来自:http://blog.csdn.net/BoySKung/archive/2008/12/09/3486026.aspx l ...
- tcp/ip 协议栈Linux源码分析一 IPv4分片报文重组分析一
内核版本:3.4.39 之前因工作原因接触到了IPv4 报文重组这个话题,一直以来对这个重组流程不是很清楚,所以很多功能的实现都避开了分片报文的处理,一方面是因为重组比较复杂,另一方面是经验不多无从下 ...
- tcp/ip 协议栈Linux源码分析五 IPv6分片报文重组分析一
做防火墙模块的时候遇到过IPv6分片报文处理的问题是,当时的问题是netfilter无法基于传输层的端口拦截IPv6分片报文,但是IPv4的分片报文可以.分析了内核源码得知是因为netfilter的连 ...
- tcp/ip 协议栈Linux源码分析四 IPv4分片 ip_fragment函数分析
内核版本:3.4.39 很多项目涉及到IP分片的时候都是绕过去了,感觉分片挺难的.但是老这么做也不行啊,抽空分析了内核的分片处理函数ip_fragment,也不是特别复杂,感觉挺简单的,看来事情只有实 ...
- (二)Linux源码分析-init
main 功能 前面介绍了head.s将执行权交给main.c,内核初始化完成,现在开始进入C代码的分析了.Init主要是取得前面setup.s设置的根文件设备号和一下内存全局变量,初始化陷阱门,块设 ...
- 第一次作业:深入Linux源码分析进程模型
一.进程的概念 第一,进程是一个实体.每一个进程都有它自己的地址空间,一般情况下,包括文本区域(text region).数据区域(data region)和堆栈(stack region).文本区域 ...
- linux 定时器_通过linux源码分析nodejs的keep-alive
之前已经分析过了keep-alive,最近在使用nodejs的keep-alive的时候发现了遗漏了一个内容.本文进行一个补充说明.我们先看一下nodejs中keep-alive的使用. enable ...
- tcp/ip 协议栈Linux源码分析三 IPv4分片报文重组分析三
继续上篇,上次讲到了分片队列的查找操作,剩下的就是分片队列插入和重组两个部分了,这个也是分片重组的关键部分. 将收到的分片插入到分片队列是由函数inet_frag_queue()函数完成,这个函数比较 ...
- tcp/ip 协议栈Linux源码分析二 IPv4分片报文重组分析二
继续接着上篇讲,之前我们说过,收到分片报文后首先会检查分片报文所占内存是否过大,如果超过阈值的话就要调用ip_evictor函数去释放一些旧的分片队列,关于如何释放分片队列资源上一篇已经总结完成,接下 ...
最新文章
- MCSE2003学习之四
- python并且怎么表示_Python-如何在Python中表示“Enum”?
- 生产环境可以用吗_小型熔喷布设备可以生产出好的熔喷布吗?
- 地铁人多不多可在线查询了 高德地图率先在北京上线新功能
- Linux计划任务之_Crontab
- ❤️使用Spring注解开发(建议收藏)
- python中字符a如何变成b_python 如何把'a=b'这样的字符解析成dict类型
- mac免费CAD模型设计软件FreeCAD怎样设置中文模式
- ubuntu 下如何调节显卡风扇转速?
- linux系统显卡显存容量,Linux下检查显存大小
- 这篇文章有毒《持续更新中。。。》
- 人人开源搭建后台管理系统 逆向工程生成CRUD代码
- 【送豪礼】死了都要爱!不告白不痛快!
- react 购物车组件
- Rotating reference frame
- 【解题报告】博弈专场 (CF 2000~2200)前五题
- 那些著名的黑客事件 七
- 机器学习算法——神经网络5(ART 1网络)
- 【Linux】linux下删除/清空文件夹/文件命令
- 德国IT行业薪酬2019年终大盘点