ch1_系统启动_setup.S
创始人
2024-03-04 14:57:03
0

1 功能分析

大写的.s 后缀名, 是为了说明是一个16位, 实模式下的汇编语言, 小写的 s 是保护模式下的汇编语言;

1.1 使用中断,读取机器参数

setup.S 是一个操作系统的加载程序, 主要作用使用 ROM BIOS 中断读取机器系统数据, 并将这些数据保存到0X90000开始的位置, 即覆盖掉原先bootsect 程序所在地方,

所取得的参数和保留的内存位置见表 6–2 所示。

这些参数将被内核中相关程序使用,例如字符设备驱动程序集中的 console.c 和 tty_io.c程序等。

利用BIOS中断程序填下面这张表格的内容:

在这里插入图片描述
如表中,

1)保存光标的位置
2)得到扩展内存的大小
3)得到显示卡当前的显示模式
4)检测显示方式
5)读取硬盘参数表信息

1.2 setup 移动 system模块

然后, setup 程序将 system 模块从 0x10000-0x8ffff 整块向下移动到内存绝对地址 0x00000 处(当时认为内核系统模块 system 的长度不会超过此值:512KB)。

因为我们已经使用完所有的BIOS中断程序,所以由BIOS在0地址处建立的中断向量表也可以覆盖掉。

; 之前bootsect引导程序将system模块移动到(0x10000)处,
; 并把自己移动到(0x90000)处,把setup加载在它后面; setup 程序将整个system模块移动到0x00000处,
; 即把从0x10000到0x8ffff的内存数据块整块的向内存地址低端移动了0x10000的位置mov	ax,#0x0000cld			! 'direction'=0, movs moves forward
do_move:mov	es,ax		! destination segmentadd	ax,#0x1000cmp	ax,#0x9000  ! 判断代码是否移动完成jz	end_move    ! 移动完成则跳转mov	ds,ax		! source segmentsub	di,disub	si,simov cx,#0x8000  ! 循环移动,循环次数,每次循环完次数减 移动0x8000字rep		! 用于把内容从ds:si 复制es:di  以字节单位movsw           ! rep是repeat,rep配合 movw(movsb) 就是多次复制直到cx=0为止 复制的次数放在cx中jmp	do_move

1.3 加载描述符表

原始的实模式下, 16位时, 寻址方式是: 段寄存器左移四位 + 偏移寄存器。
此时,加载的描述符表包含两种:

  • . 中断描述符表
  • . 全局描述符表 GDT (global descriptor table )

全局描述符表的出现, 是为了解决32位 保护模式下的寻址方式问题

GDT 表: 是使用段寄存器CS作为一个索引在一个地址表里找到32位的基地址,
然后再和偏移寄存器EIP 中的32位 数值相加,
得到最终的地址放到地址总线上去选定内存。

而为了让硬件找到这个表, GDT 表的起始地址被放在了一个GDTR 的寄存器中;


为进入保护模式做准备,

  1. 加载中断描述符表寄存器(IDTR)和全局描述符表寄存器(GDTR),
  2. 开启 A20 地址线,
  3. 重新设置两个中断控制芯片 8259A,
  4. 将硬件中断号重新设置为 0x20 - 0x2f。

实模式和保护模式下的寻址方式的区别
在这里插入图片描述
a)图记住一点,段的最大长度固定为64KB;

b)图段寄存器中保存的不再是段基地址而是描述符表的索引,并且段的最大长度是可变的。

在这里插入图片描述

1.4 模式切换

setup.s 从实模式 切换到 保护模式,

设置CPU的机器状态字寄存器CR0的PE位,进入 32 位保护模式运行,并跳转到位于 system 模块最前面部分的 head.s 程序继续运行。

进入保护模式:jmpi 0,8
加载机器状态字(控制寄存器CR0),将0位置1,CPU切换到保护模式

;进入保护模式,只是跳转到绝对地址0x00000处
; 加载机器状态字(控制寄存器CR0),将0位置1,CPU切换到保护模式mov	ax,#0x0001	! protected mode (PE) bit 保护模式比特位(PE)lmsw	ax		! This is it!             加载状态寄存器;段选择符8表示请求特权0级,使用GDT第二个段描述符jmpi	0,8		! jmp offset 0 of segment 8 (cs)  跳转至cs段偏移地址位0处(system已经移动到0x00000处)

2 .设备的划分

2.1. 磁盘

  • 软盘: 是早期的产物, 负责从计算机上搬运出数据, 现在这个基本功能使用U盘完成了。 软盘并不是装在电脑里面的,而是可移动的,一般用来存储文件和不同电脑之间进行拷贝文件,就功能上来说它和现在的U盘是一样的,只是外形、存储原理不一样,它的容量要比硬盘小的多,比如最常用的3.5英寸的软盘容量只有1.44MB。

  • 硬盘:一般都装在机箱里面,容量较大,用来存储数据。

磁盘包括软盘和硬盘

一个磁盘由多个盘片(如下图中的 0 号盘片)叠加而成。盘片的表面涂有磁性物质,这些磁性物质用来记录二进制数据。因为正反两面都可涂上磁性物质,故一个盘片可能会有两个盘面。

在这里插入图片描述

2.2 磁道

每个盘片被划分成多个圆圈, 由内向外,圆圈逐个变大, 这样每个圆圈就形成了所谓的磁道。

在对每个圈,进行分段的划分弧度, 这样一个个分段的弧, 就形成了一个个扇区。
在这里插入图片描述

由此,

  1. 不同的磁道, 容量大小不同, 因为圆圈的大小不同;
  2. 同一个磁道下的各个扇区相同, 因为是在同一个磁道下划分的。

2.3 柱面

所有的盘面中相对位置相同的磁道组成柱面, 类似于一个圆柱面,
每个盘面对应一个磁头。所有的磁头都是连在同一个磁臂上的,因此所有磁头只能“共进退”。

在这里插入图片描述

磁盘的物理地址
可用(柱面号,盘面号,扇区号)来定位任意一个“磁盘块”

可根据该地址读取一个“块”,操作如下:

① 根据“柱面号”移动磁臂,让磁头指向指定柱面;

② 激活指定盘面对应的磁头;

③ 磁盘旋转的过程中,指定的扇区会从磁头下面划过,这样就完成了对指定扇区的读/写

3. 描述符表

描述符表其实就是内存中描述符项的一个阵列。
在这里插入图片描述

描述符表有两类:

  • 全局描述符表(Gobal descriptor table-GDT)和

  • 局部描述符表(Local descri ptan⁡table−LDT)。

处理器是通过:
使用GDTR寄存器来定位GDT表
LDTR寄存器来定位当前的LDT表。

这两个寄存器以线性地址的方式保存了描述符表的基地址和表的长度。

完整setup.s 代码

!
!	setup.s		(C) 1991 Linus Torvalds
!
! setup.s is responsible for getting the system data from the BIOS,
! and putting them into the appropriate places in system memory.
! both setup.s and system has been loaded by the bootblock.
!
! This code asks the bios for memory/disk/other parameters, and
! puts them in a "safe" place: 0x90000-0x901FF, ie where the
! boot-block used to be. It is then up to the protected mode
! system to read them from there before the area is overwritten
! for buffer-blocks.
!
; setup从BIOS中获取数据,并将这些数据保存到0x90000开始的位置处(0x90000-0x901FF覆盖了原来bootsect程序所在的地方)
; 此时setup和system已经由bootsect引导块加载到内存中
; 
! NOTE! These had better be the same as in bootsect.s!INITSEG  = 0x9000	! we move boot here - out of the way 原来bootsect所在段
SYSSEG   = 0x1000	! system loaded at 0x10000 (65536).   system所在0x10000处
SETUPSEG = 0x9020	! this is the current segment          本程序所在段地址.globl begtext, begdata, begbss, endtext, enddata, endbss
.text
begtext:
.data
begdata:
.bss
begbss:
.textentry start
start:! ok, the read went well so we get current cursor position and save it for
! posterity.
; 保存光标的位置
; 使用BIOS中断取屏幕当前光标的位置(列,行),保存到内存(0x90000)处,2个byte
; 控制台初始化程序会到此处读取该值
; BISO 中断0x10 功能号 ah = 0x30 ,读光标的位置
; 输入:bh=页号
; 返回:返回:ch = 扫描开始线,cl = 结束开始线,dh = 行号(0x00顶端),dl=列号(0x00最左边)mov	ax,#INITSEG ! this is done in bootsect already, but...mov	ds,axmov	ah,#0x03	! read cursor pos 功能号 ah = 0x30 ,读光标的位置xor	bh,bhint	0x10		! save it in known place, con_init fetchesmov	[0],dx		! it from 0x90000.  将ds设置成0x90000(INITSEG)! Get memory size (extended mem, kB)
; 得到扩展内存的大小
; 利用BIOS中断0x15 功能号 ah= 0x88取系统所含扩展内存大小并保存到0x90002处
; 返回: ax= 0x10000(1M)处开始的扩展内存大小,若出错CF置位,ax=出错码mov	ah,#0x88int	0x15mov	[2],ax      !扩展内存的大小保存到0x90002处! Get video-card data:
; 得到显示卡当前的显示模式
; 调用BIOS中断0x10,功能号 ah = 0x0f
; 返回:ah=字符列数,al=显示模式,bh=显示当前页数mov	ah,#0x0fint	0x10mov	[4],bx		! bh = display pagemov	[6],ax		! al = video mode, ah = window width! check for EGA/VGA and some config parameters
; 检测显示方式
; 调用BIOS中断0x10, 功能号 ah=0x12,bl=0x10mov	ah,#0x12mov	bl,#0x10int	0x10mov	[8],ax      ! 0x90008 =axmov	[10],bx     ! 0x9000A = 安装的显示内存,0x9000B = 显示状态mov	[12],cx		!0X9000C = 显卡特性参数! Get hd0 data
; 取第一个硬盘信息
; 第一个硬盘参数表的首地址是中断向量0x41的向量值
; 第二个紧跟着对应着中断向量0x46
; 下面两个程序分别复制BIOS有关硬盘参数表,
; 第一个硬盘存放在0x90080,第二个硬盘存放在0x90090mov	ax,#0x0000mov	ds,axlds	si,[4*0x41]         !取中断向量0x41对应的地址 ,hd0参数表的地址--> ds:simov	ax,#INITSEG         mov	es,axmov	di,#0x0080          !传输的目的地址(0x9000:0x0080) -->es:dimov	cx,#0x10            ! 循环次数,每次循环完次数减一,共传输16个字节rep						! rep是repeat,rep配合 movw(movsb) 就是多次复制直到cx=0为止 复制的次数放在cx中movsb                   ! 用于把内容从ds:si 复制es:di  以字节单位! Get hd1 datamov	ax,#0x0000mov	ds,axlds	si,[4*0x46]         !取中断向量0x41对应的地址 ,hd0参数表的地址--> ds:simov	ax,#INITSEG mov	es,axmov	di,#0x0090mov	cx,#0x10repmovsb! Check that there IS a hd1 :-)
; 检测是否有第二个硬盘,如果没有则把第2个清零
; 利用BIOS中断调用0x13的取盘的类型,功能号 ah =0x15mov	ax,#0x01500mov	dl,#0x81         ! dl = 驱动器号(0x8X是硬盘,0x81是第一个硬盘,0x82是第二个硬盘)int	0x13jc	no_disk1         ! 第二个不存在cmp	ah,#3			 ! ah =类型码 指硬盘je	is_disk1         ! 存在; 第二个硬盘不存在,对第二个硬盘表清零
no_disk1:mov	ax,#INITSEGmov	es,axmov	di,#0x0090mov	cx,#0x10mov	ax,#0x00repstosb
; 第二个硬盘存在,进入保护模式,从此开始不允许中段
is_disk1:! now we want to move to protected mode ...cli			! no interrupts allowed !! first we move the system to it's rightful place
; bootsect引导程序将system模块移动到(0x10000)处,
; 并把自己移动到(0x90000)处,把setup加载在它后面
; 下面这段程序将整个system模块移动到0x00000处,
; 即把从0x10000到0x8ffff的内存数据块整块的向内存地址低端移动了0x10000的位置mov	ax,#0x0000cld			! 'direction'=0, movs moves forward
do_move:mov	es,ax		! destination segmentadd	ax,#0x1000cmp	ax,#0x9000  ! 判断代码是否移动完成jz	end_move    ! 移动完成则跳转mov	ds,ax		! source segmentsub	di,disub	si,simov 	cx,#0x8000   ! 循环移动,循环次数,每次循环完次数减 移动0x8000字rep				! 用于把内容从ds:si 复制es:di  以字节单位movsw           ! rep是repeat,rep配合 movw(movsb) 就是多次复制直到cx=0为止 复制的次数放在cx中jmp	do_move! then we load the segment descriptors
; 加载段描述符,设置全局描述符表和中断描述表end_move:mov	ax,#SETUPSEG	! right, forgot this at first. didn't work :-)mov	ds,ax
; lidt指令用于加载中断描述符表(IDT)寄存器
; 中断描述符表中每一个8个字节对应每个中断发生时所需要的中断程序地址入口lidt	idt_48		! load idt with 0,0   
; lgdt指令用于加载全局描述符表(GDT)寄存器
; 全局描述符表中每个描述符项(8字节)描述了保护模式下数据段和代码段的信息lgdt	gdt_48		! load gdt with whatever appropriate! that was painless, now we enable A20
; 开启A20地址线,为了能够访问和使用1MB以上的物理内存call	empty_8042    ! 测试8042状态寄存器,等待输入缓冲器空,mov	al,#0xD1		  ! command write 0xD1命令码表示写数据到8042的P2端口out	#0x64,alcall	empty_8042    !等待输入缓冲器空,看命令是否被接受mov	al,#0xDF		  ! A20 onout	#0x60,alcall	empty_8042    !若此时输入缓冲器为空,则表示A20线也选通 ! well, that went ok, I hope. Now we have to reprogram the interrupts :-(
! we put them right after the intel-reserved hardware interrupts, at
! int 0x20-0x2F. There they won't mess up anything. Sadly IBM really
! messed this up with the original PC, and they haven't been able to
! rectify it afterwards. Thus the bios puts interrupts at 0x08-0x0f,
! which is used for the internal hardware interrupts as well. We just
! have to reprogram the 8259's, and it isn't fun.; 重新对中断进行编程mov	al,#0x11		! initialization sequenceout	#0x20,al		! send it to 8259A-1  发送到8259A主芯片
;   0x00eb直接使用机器码表示两条相对跳转指令,起延时作用.word	0x00eb,0x00eb		! jmp $+2, jmp $+2out	#0xA0,al		! and to 8259A-2   再发送到8259A从芯片.word	0x00eb,0x00eb
;   系统硬件中断号被设置成0x20开始mov	al,#0x20		! start of hardware int's (0x20)out	#0x21,al		!送主芯片ICW2命令字,设置起始中断,要送奇端口.word	0x00eb,0x00ebmov	al,#0x28		! start of hardware int's 2 (0x28)out	#0xA1,al        !送主芯片ICW2命令字,从芯片的起始中断号.word	0x00eb,0x00ebmov	al,#0x04		! 8259-1 is masterout	#0x21,al        !ICW3.word	0x00eb,0x00ebmov	al,#0x02		! 8259-2 is slaveout	#0xA1,al.word	0x00eb,0x00ebmov	al,#0x01		! 8086 mode for bothout	#0x21,al.word	0x00eb,0x00ebout	#0xA1,al.word	0x00eb,0x00ebmov	al,#0xFF		! mask off all interrupts for nowout	#0x21,al.word	0x00eb,0x00ebout	#0xA1,al! well, that certainly wasn't fun :-(. Hopefully it works, and we don't
! need no steenking BIOS anyway (except for the initial loading :-).
! The BIOS-routine wants lots of unnecessary data, and it's less
! "interesting" anyway. This is how REAL programmers do it.
!
! Well, now's the time to actually move into protected mode. To make
! things as simple as possible, we do no register set-up or anything,
! we let the gnu-compiled 32-bit programs do that. We just jump to
! absolute address 0x00000, in 32-bit protected mode.; 进入保护模式,只是跳转到绝对地址0x00000处; 加载机器状态字(控制寄存器CR0),将0位置1,CPU切换到保护模式mov	ax,#0x0001	! protected mode (PE) bit 保护模式比特位(PE)lmsw	ax		! This is it!             加载状态寄存器;段选择符8表示请求特权0级,使用GDT第二个段描述符jmpi	0,8		! jmp offset 0 of segment 8 (cs)  跳转至cs段偏移地址位0处(system已经移动到0x00000处)! This routine checks that the keyboard command queue is empty
! No timeout is used - if this hangs there is something wrong with
! the machine, and we probably couldn't proceed anyway.; 检差键盘命令队列是否为空
; 只有当输入缓冲器为空(键盘控制器状态寄存器位1 = 0)才可以进行写命令
empty_8042:.word	0x00eb,0x00eb      !延时作用in	al,#0x64	! 8042 status porttest	al,#2		! is input buffer full?jnz	empty_8042	! yes - loopret; GDT全局描述符表开始处,描述符表由多个8字节长的描述符项组成,
; 3个描述符项
; 第一项没有作用,但是必须存在
; 第二项是系统代码段描述符
; 第三项是系统数据段描述符
gdt:.word	0,0,0,0		! dummy  第一个描述符 不用; 在GDT表这里的偏移量是0x80,它是内核代码段选择符的值.word	0x07FF		! 8Mb - limit=2047 (2048*4096=8Mb).word	0x0000		! base address=0.word	0x9A00		! code read/exec.word	0x00C0		! granularity=4096, 386; 在GDT表这里的偏移量是0x10,它是内核数据段选择符的值.word	0x07FF		! 8Mb - limit=2047 (2048*4096=8Mb).word	0x0000		! base address=0.word	0x9200		! data read/write.word	0x00C0		! granularity=4096, 386; 加载中断描述符表寄存器(idtr)
; 这里设置一个长度为0的空表
idt_48:.word	0			! idt limit=0.word	0,0			! idt base=0L; 加载全局描述符表寄存器(gdtr)
; GDT表长度为2kb
gdt_48:.word	0x800		! gdt limit=2048, 256 GDT entries.word	512+gdt,0x9	! gdt base = 0X9xxxx.text
endtext:
.data
enddata:
.bss
endbss:

相关内容

热门资讯

知名短剧女演员在承认对助理动手... 近日,短剧女演员左一被曝和助理张女士发生肢体冲突,双方冲突录音曝光。两人疑因工资结算问题起争执。 1...
平安人寿起诉华夏控股已开庭,多... 这不仅是平安系与华夏幸福之间多年投资债务纠纷的集中爆发,更是前者对600亿巨额亏损及敞口的又一次维权...
男子被冒名贷款13年,导致征信... 男子从未到某银行贷款,却在办理购车贷款时发现,自己在该银行张家界某支行有不良征信记录,原因是1999...
威海公安机关悬赏追捕两名台湾籍... 威海公安12月24日发布悬赏通告:2025年6月,我局在对刘某某等7名“宏泰58号”大陆船员侦查过程...
佳县:医保政策搬上体育课堂 阳光讯(记者 高小雨 通讯员 康亮亮 高强)近日,佳县医保局走进佳县龙岗实验中学,将医保政策宣讲搬上...
委内瑞拉通过法律应对美国封锁 中新社北京12月24日电 加拉加斯消息:委内瑞拉全国代表大会23日一致通过法律,旨在打击海盗行为、封...
专栏作家 | 刍议与贸易相关的... 近些年来,本人的研究主要聚集于经济学管理学领域,但由于所在单位的工作性质和常开展政策咨询服务的原因,...
5万元至25万元奖励!公安机关... 央视网消息:据威海公安微信公众号消息,12月24日,威海市公安局发布悬赏通告。2025年6月,威海市...