linuxbootsect.docx

上传人:b****2 文档编号:23235313 上传时间:2023-05-15 格式:DOCX 页数:17 大小:25.95KB
下载 相关 举报
linuxbootsect.docx_第1页
第1页 / 共17页
linuxbootsect.docx_第2页
第2页 / 共17页
linuxbootsect.docx_第3页
第3页 / 共17页
linuxbootsect.docx_第4页
第4页 / 共17页
linuxbootsect.docx_第5页
第5页 / 共17页
点击查看更多>>
下载资源
资源描述

linuxbootsect.docx

《linuxbootsect.docx》由会员分享,可在线阅读,更多相关《linuxbootsect.docx(17页珍藏版)》请在冰豆网上搜索。

linuxbootsect.docx

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

 !

////////////////////////////////////////////

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 小学教育 > 语文

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1