linuxbootsect.docx
- 文档编号:23235313
- 上传时间:2023-05-15
- 格式:DOCX
- 页数:17
- 大小:25.95KB
linuxbootsect.docx
《linuxbootsect.docx》由会员分享,可在线阅读,更多相关《linuxbootsect.docx(17页珍藏版)》请在冰豆网上搜索。
linuxbootsect
linux0.11内核学习--bootsect.s,万里长征第一步
呵呵,终于将linux0.11下面的boot文件夹下的三个文件读完,下面是相关注释,没有汇编基础的人也是可以读的。
废话少说,下面就是linux的源码了。
参考资料Linux内核完全注释.pdf
网上相关资料
!
时间 :
2010-1-14
!
工作:
阅读linux0.11源码中的bootsect.s
!
总体linux启动过程如下:
!
!
当PC得电源打开之后,80x86结构的CPU将自动进入实时模式,并且从0xFFFF0开始自动执行程序代码,这个地址通常是
!
ROM-BIOS的地址。
PC机的BIOS将执行系统的检测,并且在物理地址的0处开始初始化中断向量。
此后,它将可启动设备的第一
!
扇区(512字节)读入内存的绝对地址0x7c00处,并且跳转到这个地方。
启动设备通常是软盘或者是硬盘。
这里的叙述是很简单
!
的,但是这已经足够理解内核的初始化的工作过程。
!
!
linux的0x9000由BIOS读入到内存的绝对地址0x7c00(31k)处,当它被
!
执行时就会把自己移动到绝对地址0x90000处,并把启动设备中后2kb字节代码(boot/setup.s)读入到内存0x90200处,而内核的
!
其他部分则被读入到从地址0x10000的开始处。
在系统的加载期间显示信息?
Loading...",然后将控制权传递给boot/setup.s中
!
的代码.这是另一个实时模式汇编程序。
!
!
系统启动部分识别主机的某些特性以及vga卡的类型。
如果需要,它会要求用户为控制台选择显示模式。
然后整个系统从地址
!
0x10000移至0x0000处,进入保护模式病跳转至系统的余下部分。
此时所有的32位运行方式的设置启动被完成:
idt,gdt,ldt被
!
加载,处理器和协处理器也确认,分页的工作也设置好了。
最终将调用init/main.c中的main程序。
上述的操作的源代码是在
!
boot/head.s中的。
这可能是整个内核中最有诀窍的代码了。
注意如果在上述任何一步中出现了一步错误。
计算机就会死锁。
在
!
操作系统还没有完全运转之前是处理不了错误的。
!
!
!
bootsec.s文件说明如下:
!
bootsec.s代码是磁盘的引导块程序,驻留在磁盘的第一扇区。
在PC机加电rombios自检之后,引导扇区由bios加载到内存0x7c00
!
处,然后将自己移动到内存0x90000处。
该程序的主要作用是首先将setup模块从磁盘加载到内存中,紧接着bootsect的后面位置
!
(0x90200),然后利用bios中断0x13中断去磁盘参数表中当前引导盘的参数,然后在屏幕上显示"Loadingsystem..."字符串。
再者
!
将system模块从磁盘上加载到内存0x10000开始的地方。
随后确定根文件系统的设备号,如果没有指定,则根据所保存的引导盘的每
!
类型和种类,并保存设备号与boot_dev,最后长跳转到setup程序开始处0x90200执行setup程序。
!
!
!
注释如下:
!
!
SYS_SIZEisthenumberofclicks(16bytes)tobeloaded.
!
0x3000is0x30000bytes=196kB,morethanenoughforcurrent
!
versionsoflinux
!
SYSSIZE=0x3000
!
!
以下是这一段代码的翻译。
!
bootsect.s
!
bootsect.s被bios启动程序加载至0x7c0031k处,并将自己移动到地址0x90000576k处,并跳转到那里。
!
!
它然后利用bios中断将setup直接加载到自己后面0x90200576.5k,并将system加载到地址0x10000处。
!
!
注意:
目前的内核系统最大的长度限制为8*65536512k字节,即使是在将来这也应该没有问题的。
我想让他保持简单明了,
!
这样512k的最大内核长度应该足够了,尤其是这里没有向minix中一样包含缓冲区高速缓冲。
!
!
加载程序已经做的足够简单了,所以持续的独处错误将导致死循环。
只能手工重启。
只要可能,通过一次取出所有的扇区,加载的
!
过程可以做的很快.
!
!
!
!
bootsect.s (C)1991LinusTorvalds
!
!
bootsect.sisloadedat0x7c00bythebios-startuproutines,andmoves
!
iselfoutofthewaytoaddress0x90000,andjumpsthere.
!
!
Itthenloads'setup'directlyafteritself(0x90200)256b,andthesystem
!
at0x10000,usingBIOSinterrupts.
!
!
NOTE!
currentlysystemisatmost8*65536byteslong.Thisshouldbeno
!
problem,eveninthefuture.Iwanttokeepitsimple.This512kB
!
kernelsizeshouldbeenough,especiallyasthisdoesn'tcontainthe
!
buffercacheasinminix
!
!
Theloaderhasbeenmadeassimpleaspossible,andcontinuos
!
readerrorswillresultinaunbreakableloop.Rebootbyhand.It
!
loadsprettyfastbygettingwholesectorsatatimewheneverpossible.
!
六个全局标示符
.globlbegtext,begdata,begbss,endtext,enddata,endbss
!
文本段
.text
begtext:
!
数据段
.data
begdata:
!
堆栈段
.bss
begbss:
!
文本段
.text
SETUPLEN=4 !
nrofsetup-sectors setup程序的扇区数(setup-sectors)值
BOOTSEG =0x07c0 !
originaladdressofboot-sector bootsect的原始地址
INITSEG =0x9000 !
wemoveboothere-outoftheway 将bootsect移动到这里
SETUPSEG=0x9020 !
setupstartshere setup程序开始地址
SYSSEG =0x1000 !
systemloadedat0x10000(65536). 将system模块加载到的地址
ENDSEG =SYSSEG+SYSSIZE !
wheretostoploading 停止加载的地址
!
ROOT_DEV:
0x000-sametypeoffloppyasboot.
!
0x301-firstpartitiononfirstdriveetc
ROOT_DEV=0x306 !
根文件系统设备是第二硬盘的第一个分区
!
0x300--/dev/hd0
!
0x301--/dev/hd1
!
...
!
0x309--/dev/hd9
entrystart 告知连接程序,程序充start标号开始。
start:
////////////////////////////////////////////////////////////////////////////////////////////////////////////
!
下面的代码是将自身bootsect从目前位置0x07c031k移动到0x9000576k处,然后跳转到本程序的下一条语句处。
!
!
此时(在实时模式下)内存使用如下的分布:
!
0 0x7c00(bootsect.s)
!
--------++++++++++---------------------------
!
ds=0x7c00
!
<-
mov ax,#BOOTSEG
mov ds,ax
!
es=0x9000
mov ax,#INITSEG
mov es,ax
!
移动计数的值256
!
启动代码是512kb
mov cx,#256
!
源地址ds:
si=0x07co:
0x0000
sub si,si
!
目的地址es:
di=0x9000:
0x0000
sub di,di
!
重复执行知道cx=0。
循环程序实现的另一种方法是利用串操作处理的重复指令rep。
rep指令以cx为重复次数,
!
当指令被重复执行完一次,那么cx的值会自动减一。
rep指令和串操作指令movs,stos配合使用,它将这两条指令
!
重复执行cx次。
rep
!
移动一个字
movw
!
此时的内存使用情况如下:
!
0 0x7c00 0x9000
!
----------+++++++++---------+++++++++---------
!
两个使用中的内存是相同的
!
在汇编中的段的使用
!
!
间接跳转。
这里INITSEG指出跳转到的地址。
!
其格式为:
jmpioffset(标号),segmentselector
jmpi go,INITSEG--0x9000
//////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////
!
在将自己成功移动之后,下面的几步是为下面加载setup程序做准备。
!
!
在执行jmpi指令时,cs段会被自动更新。
注意的是cs寄存器在call或者是jmp指令时会自动更新。
将dsesss都设置成代码所
!
在的段。
go:
mov ax,cs
mov ds,ax
mov es,ax
!
将堆栈指针sp指向0x9ff00(0x9000:
0xff00)
!
putstackat0x9ff00.
!
SS被成为堆栈段寄存器,用于存放堆栈段的基值.
mov ss,ax
!
代码段的移动,需要重新设置堆栈段的位置。
sp只要指向远大于512便宜处都可以。
mov sp,#0xFF00 !
arbitraryvalue>>512
!
//////////////////////////////////////////////////////////////////////////////////////////////////////////
!
在bootsect程序块后紧随着加载setup模块。
注意es已经设置好了。
es是指附加段寄存器。
附加段寄存器是es,它的作用是很大的.
!
因为我们在处理数据的时候,往往需要用到两个数据段,特别是在字符串的处理方面,使用两个数据段简便了许多的操作.
!
!
loadthesetup-sectorsdirectlyafterthebootblock.
!
Notethat'es'isalreadysetup.
!
!
下面的这段代码的主要作用是使用int0x13把磁盘上的setup模块加载到内存中,位置在bootsect.s(0x90000+512字节)之后,
!
真个过程主要是操作寄存器ax,bx,cx,dx等四个寄存器。
!
!
!
///////////////////////////////////////////////////////////////////////////////////////////////////////
!
设置load_setup标号,是为了执行j load_setup语句。
load_setup:
mov dx,#0x0000 !
drive0,head0
mov cx,#0x0002 !
sector2,track0
mov bx,#0x0200 !
address=512,inINITSEG
mov ax,#0x0200+SETUPLEN !
service2,nrofsectors
int 0x13 !
readit 调用中断信号,开始读取。
!
进位操作标示符cf=0表示操作成功。
jnc ok_load_setup !
ok-continue·如果成功就跳转到下面的ok_load_setup
!
否则执行下面的代码,复位磁盘再次执行这段代码
mov dx,#0x0000
mov ax,#0x0000 !
resetthediskette 磁盘复位
int 0x13 !
ld86中就有j这条指令,等价于jmp。
这条语句的含义是:
!
跳转回去继续执行,如果总是失败系统,将总执行这段代码
j load_setup
!
//////////////////////////////////////////////////////////////////////////////////////////////////////
!
时间:
2010-1-15
!
工作量 :
继续阅读linux0.11源码
!
时间:
2010-1-17
!
工作量:
继续阅读linux源码。
!
!
如果上面讲setup模块顺利读入到内存中,那么执行下面的ok_load_setup代码。
!
ok_load_setup:
!
Getdiskdriveparameters,specificallynrofsectors/track
!
取得磁盘驱动器的参数,特别是没道的扇区数量。
!
取得磁盘的参数使用的是int0x13中断来实现,其调用格式如下:
!
ah=0x08 dl=启动器号
!
返回信息如下:
!
如果出错的话cf置位,并且ah=状态码
!
ah=0,al=0 bl=驱动器类型(at/ps2)
!
ch=最大磁盘号的低8位, cl=每磁盘的最大扇区数(位0-5),最大磁道号高2位(位6-7)
!
dh=最大磁道数 dl=驱动器数量
!
es:
di=软驱磁盘参数表
!
!
调用中断0x13
mov dl,#0x00 !
清空dl,以获得驱动器号
mov ax,#0x0800 !
AH=8isgetdriveparameters
int 0x13
mov ch,#0x00
///////////////////////////////////////////////////////////
!
先讲一下寄存器的默认组合问题,比如指令mov[si],ax表示将ax中的内容存入ds:
si指向的内存单元,也就是说在寄存器间
!
接寻址的情况下,以si间接寻址时总是默认以ds为相应的段地址寄存器。
同样di是以es为默认的段地址寄存器。
!
第二个要了解的是“段超越”的问题,就是在某些时候你不想使用默认的段地址寄存器,那
!
么你可以强制指定一个段地址寄存器(当然这种强制是在允许的情况下,建议看一下汇编
!
教材上的说明),同上例mov[si],ax表示存入ds:
si中,但如果你想存入cs指向的段中可
!
以这样movcs:
[si],ax,这样就强制指定将ax中的内容存入cs:
si的内存单元。
!
第三个要明白的是segcs这样的语句只影响到它下一条指令,比如在linux启动代码中的一段:
!
segcs
!
movsectors,ax
!
movax,#INITSEG
!
要说明两点:
!
第一,segcs只影响到movsectors,ax而不影响movax,#INITSEG
!
第二,如果以Masm语法写,segcs和movsectors,ax两句合起来等
!
价于movcs:
[sectors],ax,这里使用了间接寻址方式。
!
重复一下前面的解释,mov[sectors],ax表示将ax中的内容
!
存入ds:
sectors内存单元,而movcs:
[sectors],ax强制以
!
cs作为段地址寄存器,因此是将ax的内容存入cs:
sectors内存
!
单元,一般来说cs与ds的值是不同的,如果cs和ds的值一样,
!
那两条指令的运行结果会是一样的。
(编译后的指令后者比前
!
者一般长一个字节,多了一个前缀。
)
!
结论,segcs只是表明紧跟它的下一条语句将使用段超越,因为在编
!
译后的代码中可以清楚的看出段超越本质上就是加了一个字节
!
的指令前缀,因此as86把它单独作为一条指令来写也是合理的。
!
!
movcs:
[sectors],ax
!
!
下面的代码在linux2.6.x的内核中可能改变。
没有查证。
网上有关于其的讨论,认为其中含有错误。
segcs
mov sectors,cx !
sectors在下面定义,保存每个磁道扇区数
///////////////////////////////////////////////////////////
mov ax,#INITSEG
mov es,ax !
INITSEG=0x90000恢复es值
!
Printsomeinanemessage在显示一些信息("Loadingsystem...\n"回车换行。
共24个字符)
!
//////////////////////////////////////////////////
!
读取光标位置
mov ah,#0x03 !
readcursorpos
xor bh,bh !
bh=0,使用xor指令将bh清0,但是速度比赋值快
int 0x10
!
//////////////////////////////////////////////////
!
//////////////////////////////////////////////////
!
利用int0x10来实现将移动光标,并写字符创
mov cx,#24 !
共24个字符
mov bx,#0x0007 !
page0,attribute7(normal)
mov bp,#msg1 !
msgl下面定义,指向要显示的字符串
mov ax,#0x1301 !
writestring,movecursor
int 0x10 !
写字符串并且移动光标
!
//////////////////////////////////////////////////
!
ok,we'vewrittenthemessage,now
!
wewanttoloadthesystem(at0x10000)
!
现在开始将system模块加载到内存0x10000(64k)处。
!
/////////////////////////////////////////////////
mov ax,#SYSSEG
mov es,ax !
segmentof0x010000,现在es就是存放system的段地址
!
/////////////////////////////////////////////////
!
//////////////////////////////////////////////////
!
调用邋read_it来实现读取磁盘上的system模块,其中es为输入参数。
call read_it
!
//////////////////////////////////////////////////
!
关闭驱动器马达,这样就知道驱动器的状态了。
call kill_motor
!
/////////////////////////////////////////////////
!
Afterthatwecheckwhichroot-devicetouse.Ifthedeviceis
!
defined(!
=0),nothingisdoneandthegivendeviceisused.
!
Otherwise,either/dev/PS0(2,28)or/dev/at0(2,8),depending
!
onthenumberofsectorsthattheBIOSreportscurrently.
!
此后,我们检查使用哪个根文件系统设备。
如果已经指定了设备,就直接使用给定的设备,
!
否则就需要根据bios的报告的每磁道扇区数来确定到底是使用/dev/ps0还是/dev/at0
!
上面一行中的两个设备文件的含义:
!
在linux中软驱的主设备号是2,次设备号=type*4+nr,其中nr为0-3分别
!
对应软驱的abcd;type是软驱的类型(2->1.2m7->1.44m等)。
因为7*4+0=28,所
!
以/dev/ps0指的是1.44ma驱动器,起设备号是0x021c,同理/dev/at0(2,8)指的是1.2
!
ma的驱动器,其设备号是0x0208
segcs
mov ax,root_dev !
根设备号
!
////////////////////////////////////////////////////
!
使用cmp和jne指令来实现条件转移。
在汇编语句中的跳转指令分为有条件跳转和
!
无条件跳转,jne可解释为如下:
jumpnotequal
!
cmp ax,#0
jne root_defined
!
////////////////////////////////////////////
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- linuxbootsect
![提示](https://static.bdocx.com/images/bang_tan.gif)