linux引导程序解析Word文档下载推荐.docx

上传人:b****5 文档编号:18829519 上传时间:2023-01-01 格式:DOCX 页数:34 大小:112.60KB
下载 相关 举报
linux引导程序解析Word文档下载推荐.docx_第1页
第1页 / 共34页
linux引导程序解析Word文档下载推荐.docx_第2页
第2页 / 共34页
linux引导程序解析Word文档下载推荐.docx_第3页
第3页 / 共34页
linux引导程序解析Word文档下载推荐.docx_第4页
第4页 / 共34页
linux引导程序解析Word文档下载推荐.docx_第5页
第5页 / 共34页
点击查看更多>>
下载资源
资源描述

linux引导程序解析Word文档下载推荐.docx

《linux引导程序解析Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《linux引导程序解析Word文档下载推荐.docx(34页珍藏版)》请在冰豆网上搜索。

linux引导程序解析Word文档下载推荐.docx

movss,ax

movsp,#0xFF00

利用BIOS中断INT13将setup模块,从磁盘第2个扇区开始读到0x90200开始处,共读4个扇区

load_setup:

movdx,#0x0000!

drive0,head0

movcx,#0x0002!

sector2,track0

movbx,#0x0200!

address=512,inINITSEG

movax,#0x0200+SETUP_LEN!

service2,nrofsectors

int0x13

jncok_load_setup

出错则重新执行加载程序

movax,#0x0000

jload_setup

利用int13中断,得到磁盘驱动器的参数,特别是每道磁道的扇区数量

movax,0x0800

movdl,0x00

重新设置es的值

movsectors,cx

printsomemessage

movah,#0x03!

读光标位置,返回光标位置在dx中

xorbh,bh

int0x10

movcx,#24

movbx,#0x0007

movbp,#msg1

movax,#0x1301

okwehavewrittenthemessage,现在开始将system模块加载到0x10000开始处

movax,#SYSSEG

callread_it!

读磁盘上system模块,es为输入参数

callkill_motor!

关闭马达

确定根文件系统所在的设备号

segcs

movax,root_dev

cmpax,#0

jneroot_defined

movbx,sectors

movax,#0x0208

cmpbx,#15

jeroot_defined

movax,#0x021c

cmpbx,#18

undef_root:

jmpundef_root

root_defined:

movroot_dev,ax

jmpi0,SETUPSEG!

此处跳进setup程序

下面是将system模块加载进内存的子函数

sread:

.word1+SETUPLEN!

sectorsreadofcurrenttrack

head:

.word0

track:

保证es在64kb处

read_it:

movax,es

testax,#0xfff

die:

jnedie

xorbx,bx

rp_read:

!

接着判断是否已经读入全部的数据,比较当前所读的段是否就是系统数据末端所处的段

movax,es!

如果不是,就跳转至下面的ok1标号处继续读数据

cmpax,#ENDSEG

jbok1_read

ret!

如果到达了系统末端,就结束此循环

ok1_read:

计算和验证当前磁道上需要读取的扇区数目,放在ax寄存器中,根据当前磁道还未读取的扇区数和

段内数据字节开始偏移的位置,计算如果全部读取这些未读扇区,所读的字节是否会超过64kb的限制

若会超过,则根据此次最多能读入的字节数,反算出需要读取的扇区数。

movax,sectors!

取每磁道的扇区数

subax,sread!

减去当前磁道已读扇区数

movcx,ax!

cx=ax为当前磁道的未读扇区数

shlcx,#9!

当前未读的字节数

addcx,bx!

此次操作之后,段内偏移地址现在的值

jncok2_read!

若没有超过64kb,则跳转至ok2_read

jeok2_read

若加上此次将读取的磁道上所有未读扇区时会超过64kb,则反算出可以最多加载多少扇区数目

xorax,ax

subax,bx

shrax,#9!

转换成扇区数目

ok2_read:

读当前磁道上指定开始扇区(cl)和需读扇区数(al)的数据到es:

bx开始处。

然后将磁道上已经读取的扇区数目

与磁道最大扇区数sectors作比较,如果小于sectors说明当前磁道上还有扇区未读

callread_track

cx等于当前操作以读扇区数目

addax,sread!

加上当前磁道已读扇区数目

segcs

cmpax,sectors!

如果当前磁道上还有扇区未读,则跳转到ok3_read

jneok3_read

如果该磁道的当前磁头面所有扇区已经读完,则读该磁道的下一磁头面(1号磁头)上的数据,如果已经读完则去读下一磁道

movax,#1

subax,head!

判断当前的磁头号,如果是0磁头,则去读1磁头

jneok4_read!

读1号磁头

inctrack!

读下一磁道

ok4_read:

movhead,ax!

保存当前的磁头号

xorax,ax!

清除当前磁道的已读扇区数

ok3_read:

如果当前磁道上还有未读的扇区,则首先保存当前磁道的已读扇区数目,然后调整存放数据的开始位置,若小于64kb边界值

则跳转到rp_read处,继续读数据

movsread,ax!

保存当前磁道的已读扇区数

上次已读扇区数*512字节

addbx,cx!

调整当前段内数据开始位置

jncrp_read

否则说明已经读取64kb数据,此时调整当前段,为读下一段数据作准备

addax,#0x1000

jmprp_read

read_track子程序,读当前磁道上指定开始扇区和需读扇区数的数据到es:

int0x13,ah=2,al=需读扇区数,es:

bx缓冲区开始位置

read_track:

pushax

pushbx

pushcx

pushdx

movdx,track

movcx,sread

inccx

movch,dl

movdx,head

movdh,dl

movdl,#0

anddx,#0x0100

movah,#2

int0x13

jcbad_rt

popdx

popcx

popbx

popax

ret

bad_rt:

movax,#0

movdx,#0

jmpread_track

关闭软驱马达的子程序

kill_motor:

movdx,#0x3f2

moval,#0

outb

sectors:

.word0!

存放当前启动软盘每磁道的扇区数

msg1:

.byte13,10

.ascii"

Loadingsystem

"

.byte13,10,13,10

.org508

root_dev:

.wordROOT_DEV!

这里存放根文件系统的所在设备号

boot_flag:

.word0xAA55

endtext:

enddata:

endbss:

2setup.s程序分析

setup.s是一个操作系统的加载程序,他的主要作用就是利用BIOS的读取机器系统数据,并将这些数据保存到0x90000开始的位置,(覆盖了bootsect程序所在的地方)。

这些参数将被内核中相关程序使用。

参数诸如光标位置,显存等信息。

然后setup程序将system模块从0x10000-0x8ffff(任务system模块不会超过512kb),整体移动到绝对内存地址为0x0000处。

接着加载中断描述表寄存器idtr和全局描述表寄存器gdtr,开启A20地址线,重新设置两个中断控制芯片8259A,将硬件中断号重新设置为0x20---0x2f。

最后设置CPU的控制寄存器CR0,从而进入32位保护模式运行,并跳入到system模块最前面部分的head.s程序继续运行。

为了能让head.s在32位保护模式下运行,在本程序临时设置了中断描述符表(IDT)和全局描述符表(GDT),

在GDT中设置了当前代码段的描述符和数据段的描述符,在head.s中会重新设置这些描述符表。

必须使用lgdt把描述符表的基地址告知CPU,再将机器状态字置位即可进入32位保护模式。

INITSEG=0x9000!

wemoveboothere

SETUPSEG=0x9020!

本程序所在的段地址

保存光标位置已备以后需要

这段代码使用BIOS中断取屏幕当前的光标位置,然后保存在内存0x90000处就可以使用

movax,#INITSEG

movah,#0x03

int0x10!

利用BIOS中断将当前光标位置存档到dx

mov[0],dx!

将光标位置存放在0x90000处

得到内存的大小值

利用BIOS中断0x15功能号ah=0x88取系统所含扩展内存大小并保存在内存0x90002处。

movah,#0x88

int0x15

mov[2],ax

得到显示卡的属性

调用BIOS中断0x10,功能号ah=0x0f

返回:

ah=字符列数;

al=显示模式;

bh=当前显示页

0x90004存放当前页,0x90006存放显示模式,0x90007存放字符列数

movah,#0x0f

mov[4],bx!

bh=displaypage

mov[6],ax!

al=videomode,ah=windowwidth

检查显示方式并取参数

movah,#0x12

movbl,#0x10

mov[8],ax

mov[10],bx

mov[12],cx

取得第一个硬盘的信息

ldssi,[4*0x41]

movdi,#0x0080

movcx,#0x10

rep

movsb

取得第二个硬盘

ldssi,[4*0x46]!

取中断向量0x46的值,即hd1的参数值------>

ds:

si

movdi,#0x0090!

传输目的地址0x9000:

0x0090

检查系统是否有第二个硬盘

movax,#0x01500

movdl,#0x81

jcno_disk1

cmpah,#3

jeis_disk1

no_disk1:

第二块硬盘不存在,所以清空参数表

movdi,#0x0090

movax,#0x00

stosb

is_disk1:

从此开始进入了保护模式

cli

首先把system模块移动到正确的位置

cld

do_move:

cmpax,#0x9000

jzend_move

movcx,#0x8000

movsw

jmpdo_move

end_move:

在此处加载段描述符表,这里需要设置全局描述符表和中断描述符表

movax,#SETUPSEG

lidtidt_48

lgdtgdt_48

打开A20地址线

callempty_8042

moval,#0xD1

out#0x64,al

moval,#0xDF

out#0x60,al

8259芯片主片端口是0x20-0x29,从片的端口是0xA0-0xA9。

moval,#0x11

out#0x20,al

.word0x00eb,0x00eb

out#0xA0,al

8259芯片设置中断号从0x20开始

moval,#0x20

out#0x21,al

moval,#0x28

out#0xA1,al

moval,#0x04

out#0x21,al

.word0x00eb,0x00eb

moval,#0x02

moval,#0x01

moval,#0xFF

下面设置并进入32位保护模式运行,首先加载机器状态字,也称控制寄存器cr0

在设置该bit之后,随后的一条指令必须是一条段间跳转指令,一用于刷新当前指令队列

因为CPU在执行一条指令之前就已经从内存读取该指令并对其进行解码。

movax,#0x0001

lmswax

jmpi0,8

下面这个子程序检查键盘命令队列是否为空

empty_8042:

inal,#0x64

testal,#2

jnzempty_8042

全局描述符表开始处

gdt:

.word0,0,0,0

代码段选择符的值

.word0x07FF

.word0x0000

.word0x9A00

.word0x00C0

数据段选择符的值

.word0x9200

idt_48:

.word0

.word0,0

gdt_48:

.word0x800

.word512+gdt,0x9

三head.s程序

功能描述:

head.s程序在被编译生成目标文件之后会与内核其他程序一起被链接成system模块,位于system模块最前面,所以称之为head程序的原因。

system模块将被放置在磁盘上setup模块之后开始的扇区中,即从磁盘上第6个扇区开始位置。

linux内核一般大约有120KB,在磁盘上大概占用240个扇区。

之后我们将在保护模式下编程,head.s使用as和ld编译器和连接器。

这段程序实际上处于绝对地址0处开始的地方,首先是加载各个数据段寄存器,重新设置中断描述符表idt,共256项,并使各个表项指向一个只报错误的哑中断子程序ignore_int。

在设置了中断描述符表之后,本程序又重新设置了全局段描述符表gdt,主要是把gdt表设置在比较合理的地方,接着设置管理内存的分页处理机制,将页目录表放在绝对物理地址0开始处,紧随后边将放置可以寻址16MB内存的4个页表,并设置它们的表项。

最后,head.s程序利用返回指令将预先放置在堆栈中的main.c程序的入口地址弹出,去执行main()程序。

以下是部分代码分析

(1)首先是建立IDT和GDT表

#建立IDT表

setup_idt:

leaignore_int,%edx

movl$0x00080000,%eax

movw%dx,%ax

movw$0x8E00,%dx

lea_idt,%edi

mov$256,%ecx

rp_sidt:

movl%eax,(%edi)#eax的高16位是选择符,低16位是段内偏移的低16位

movl%edx,4(%edi)#edx的高16位是段内偏移地址的高16位,低16位是权限位

addl$8,%edi

dec%ecx#重复设置总共256个中断描述符

jnerp_sidt

lidtidt_descr#加载中断描述符表寄存器

setup_gdt:

lgdtgdt_descr#加载全局描述符表寄存器

ret

由代码可知,256个idt均指向了一个哑中断ignore_int,加载gdt的过程更简单,只是将gdt描述符表的基地址加载进gdtr寄存器。

(2)页目录表和页表之间的映射

在linux1.1中,在绝对内存地址的0x000000处是一个大小为4k的页目录表,然后在内存0x1000,0x2000,0x3000,0x4000处分别是4个页表的首地址,也就是说linux0.11仅仅能访问16M的内存空间,内存映射的算法如下:

首先在内存0x00000即页目录表设置4个页表首地址,注意添加权限属性。

然后从最后一个页表的最后一个表项,倒序的将物理地址添加进页表中,最后一个页表项的内容是64M-4096+7(7表示页面在内存,且用户可读可访问)。

.align2

setup_paging:

#首先为5页内存进行清空处理

#1个页目录表,4个页表

movl$1024*5,%ecx

xorl%eax,%eax

xorl%edi,%edi

cld;

rep;

stosl

#页目录中只需要4个页目录,7是属性,表示该页存在内存中,且用户可以访问

movl$pg0+7,_pg_dir

movl$pg1+7,_pg_dir+4

movl$pg2+7,_pg_dir+8

movl$pg3+7,_pg_dir+12

#从最后一项倒序的写入

movl$pg3+4092,%edi#最后一页的最后一项

movl$0xfff007,%eax#16M-4096+7

std

stosl

subl$0x1000,%eax

jge1b

#设置页目录表基址寄存器cr3的值,指向页目录表。

cr3中保存的是页目录表的物理地址

movl%eax,%cr3

#设置启动分页处理

movl%cr0,%eax

orl%0x80000000,%eax

movl%eax,%cr0

#该返回指令执行先前压入堆栈的main函数的入口地址

(3)head中还需要为程序跳转进main函数作准备,当完成了页面设置的时候,上面代码的最后一句ret,即

完成了跳入main函数中继续执行。

设置main函数的代码如下:

.org0x5000#定义下面的内存数据块从偏移0x5000处开始

_tmp_flop

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

当前位置:首页 > 职业教育 > 职高对口

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

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