逐行解读Linux0.11系统引导(一)

本文内容的重点是x86硬件架构下Linux0.11的引导代码部分,不讨论BIOS本身及其加载,简略介绍BIOS加载引导扇区的部分。
和现在流行的UEFI不同,这个古老的系统由BIOS引导。

BIOS是英文"Basic Input Output System"的缩略词,直译过来后中文名称就是"基本输入输出系统"。——百度百科

1. 加载硬盘数据之前

我们都知道,CPU只能执行内存中的代码。如果代码在硬盘等外设上面,就需要先将其加载进内存再执行。
在Windows下,当我们双击exe程序希望运行它的时候,操作系统会帮我们把硬盘中的程序读入内存然后运行。在按下计算机电源键的时候,操作系统自身也需要"被"加载进内存。这个操作可以分为几个步骤,第一步的加载就是由BIOS完成的,这时CPU处于实模式。

  • 注:所有x86 CPU在刚上电时都运行在“实模式”下,这个时候的CPU就相当于一个8086,运行16位代码并拥有20位寻址能力。

2. BIOS加载引导扇区

  1. 什么是引导扇区:引导扇区就是磁盘等存储外设的第一个物理扇区。这个扇区又叫MBR(Master Boot Record,主引导记录)
    Linux0.11的代码就从这里开始。
  2. BIOS会把指定磁盘的第一个物理扇区的512字节内容复制到内存的0x07C00处,这是一个约定,所有的BIOS都会这么做。
    这个时候CS:IP就指向物理地址0x07c00,下一步CPU就会运行从磁盘读来的第一条指令,也就是Linux0.11的第一条指令。至此,第一部分的引导就完成了。

3. Linux的代码第一次被CPU运行

  1. 当第一句Linux的代码被运行,BIOS就结束了它加载MBR的使命。从此,计算机的控制权就交给了Linux。
  2. 前面提到这时候处于实模式,MBR中放的是一段8086的机器码,这段程序是由汇编语言写成的,源文件是/boot/bootsect.s。他的作用是将另外两批Linux代码,分别是4个扇区的/boot/setup.s和240个扇区的system模块复制进入内存,本篇文章介绍到屏幕上显示“Loading system …”为止,后面的内容将在下一篇文章中介绍。

以下是这个文件的开头几行,每一个数据的作用将在后面说明。

SYSSIZE  = 0x3000
SETUPLEN = 4                 ! nr of setup-sectors
BOOTSEG  = 0x07c0            ! original address of boot-sector
INITSEG  = 0x9000	         ! we move boot here - out of the way
SETUPSEG = 0x9020            ! setup starts here
SYSSEG   = 0x1000            ! system loaded at 0x10000 (65536).
ENDSEG   = SYSSEG + SYSSIZE  ! where to stop loading

(注意注释符是!不是

  1. bootsect.s的前几条指令如下,它首先做的事是把自身(位于0x07c00,也就是段地址为BOOTSEG,偏移量为0)复制到物理地址0x90000(段地址为INITSEG,偏移量为0)处。需要说明以下rep命令,它的作用是重复执行下面一条命令直到CX寄存器为0,每执行一次会将CX的值减1。可以看到执行了256次“字传送”操作,也就是移动了512个字节(一个扇区)的数据。
start:
	mov	ax,#BOOTSEG
	mov	ds,ax
	mov	ax,#INITSEG
	mov	es,ax
	mov	cx,#256
	sub	si,si
	sub	di,di
	rep
	movw

下面有一个关键的jmpi的指令,它将CS的值置为INITSEG也就是0x9000,IP的值置为标号go所标记的指令处的偏移量。这个指令使程序跳转到新复制到的位置继续执行。然后重置数据段地址寄存器和附加段地址寄存器的值为INITSEG的值,也就是0x9000。并且将栈顶指针指向0x9FF00

	jmpi	go,INITSEG
go:	mov	ax,cs
	mov	ds,ax
	mov	es,ax
	mov	ss,ax
	mov	sp,#0xFF00		! arbitrary value >>512
  1. 在第一段代码把自己在内存中的位置重新安排好之后,就要开始装载开始提到的4个扇区的Linux程序了。这个装载操作要借助BIOS提供的中断服务程序:直接磁盘服务(Direct Disk Service——INT 13H)的02H功能。如果操作成功则跳转到标号ok_load_setup:处,否则使用功能号00H将磁盘系统复位后重新读取磁盘数据。

02H功能:从硬盘读取数据:

  • AH存放功能号02H,AL存放要读取的扇区数量
  • ES:BX表示了缓冲区的地址,即复制的目标地址,BX就是缓冲区的偏移量
  • CH存放柱面号,CL存放读取的起始扇区
  • DH存放磁头号,DL存放驱动器号,00H~7FH:软盘;80H~0FFH:硬盘

02H功能执行完成会影响CF标志位:

  • CF=0——操作成功,AH=00H,AL=传输的扇区数
  • CF=1——操作失败,AH=状态代码(略)

00H功能:磁盘系统复位:

  • AH存放功能号00H
  • DL存放驱动器号,00H~7FH:软盘;80H~0FFH:硬盘

00H功能执行完成后会影响CF标志位:

  • CF=0——操作成功,AH=00H
  • CF=1——操作失败,AH=状态代码

load_setup:
	mov	dx,#0x0000		! drive 0, head 0
	mov	cx,#0x0002		! sector 2, track 0
	mov	bx,#0x0200		! address = 512, in INITSEG
	mov	ax,#0x0200+SETUPLEN	! service 2, nr of sectors
	int	0x13			! read it
	jnc	ok_load_setup		! ok - continue
	mov	dx,#0x0000
	mov	ax,#0x0000		! reset the diskette
	int	0x13
	j	load_setup

ok_load_setup:
  1. 下一步是读取磁盘参数,用到int 13H的08H功能,从代码中也能看出来Linux0.11是存在软盘中的

08H功能:读取磁盘参数

  • AH存放功能号08H ,
  • DL存放驱动器号,00H~7FH:软盘;80H~0FFH:硬盘

00H功能执行完成后会影响CF标志位:

  • CF=1——操作失败,AH=状态代码,
  • CF=0——操作成功:
    BL存储读取到的磁盘容量01H — 360K,02H — 1.2M,03H — 720K ,04H — 1.44M
    CH存储柱面数的低8位
    CL的位7-6存储柱面数的高2位
    CL的位5-0存储扇区数
    DH存储磁头数
    DL存储驱动器数
    ES:DI表示磁盘驱动器参数表地址

ok_load_setup:

! Get disk drive parameters, specifically nr of sectors/track

	mov	dl,#0x00
	mov	ax,#0x0800		! AH=8 is get drive parameters
	int	0x13
	mov	ch,#0x00
	seg cs
	mov	sectors,cx
	mov	ax,#INITSEG
	mov	es,ax

sectors:
	.word 0
  1. 在屏幕上显示一些信息,使用int 10H,这段信息就是经典的“Loading system …”
! Print some inane message

	mov	ah,#0x03		! read cursor pos
	xor	bh,bh
	int	0x10
	
	mov	cx,#24
	mov	bx,#0x0007		! page 0, attribute 7 (normal)
	mov	bp,#msg1
	mov	ax,#0x1301		! write string, move cursor
	int	0x10

! 作者注:这些数据放在源文件末尾,为方便阅读,放在这里
msg1:
	.byte 13,10
	.ascii "Loading system ..."
	.byte 13,10,13,10
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章