Linux0.11代码浅析

发布时间 2023-09-27 16:09:46作者: Euler0525

Linux0.11

"pull oneself up by one's bootstraps"?

开机上电……

1. BIOS

开机程序被刷入ROM中

  1. 开机自检:哔哔哔——
  2. BIOS中设定启动顺序

2. 引导

把控制权转交给排在第一位的存储设备,读取第一个512字节

1-446: 调用操作系统
447-510: 分区表
511-512: 主引导记录签名(0x55, 0xaa)

3. 硬盘启动

4. 操作系统

/boot/bootsect.s

筹备内存访问

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

! ROOT_DEV:	0x000 - same type of floppy as boot.
!		0x301 - first partition on first drive etc
ROOT_DEV = 0x306

entry start
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	go,INITSEG
go:	mov	ax,cs
	mov	ds,ax
	mov	es,ax
! put stack at 0x9ff00.
	mov	ss,ax
	mov	sp,#0xFF00		! arbitrary value >>512

首先将ds寄存器设定为0x07c0,es寄存器设定为0x9000;cx设定为256,si和di清零,此时

DS = 0X7C00
ES = 0X9000
CX = 256
SI = 0, DI = 0

以上操作均为了下一步的rep movw服务,它表示重复复制一个字(word 16bits=2Bytes)

  • 重复复制cx次,即256次,总共复制512字节
  • 从DS:SI复制到ES:DI,即从0x7c00复制到0x90000

接下来,执行jmpi go, INITSEG,表示跳转到0x9000:go处执行指令,等价于

cs = 0x9000
ip = [go]

go标签表示bootsect.s编译成二进制文件bootsect后,go的内存地址偏移量,就是上面代码第24行的位置

下面执行go处的代码,将cs的内容赋值给ds,es和ss,然后sp设定为0xFF00

  • DS寄存器是数据段寄存器,512个字节从0x7c00移动到0x90000,所以DS改为指向0新000

  • SS寄存器是栈段寄存器,SP是栈顶指针,此时SS:SP指向0x9FF00

bootsect

至此,操作系统初步规划了内存,为访问代码和数据设置了基址,把栈顶指针指向了远离代码的位置。

操作系统代码放入内存

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:
...

第一部分的代码解决了访问数据段、代码段和栈段的问题,接下来要将操作系统代码加载到内存:

首先将dx,cx,bx,ax依次赋值,作为中断程序的参数(通过寄存器传参),然后发起中断,实现从硬盘第2个扇区开始,把数据加载到0x90200处,一共加载了4个扇区(第2,3,4,5扇区)

bootsect2

之后的jnc表示如果复制成功,跳转到ok_load_setup,如果失败,就会重复执行复制代码……

下面是ok_load_setup主要代码

! 省略非主逻辑代码(包括输出字符串等)
ok_load_setup
	...
    mov	ax,#SYSSEG
	mov	es,ax		! segment of 0x010000
	call read_it
	...
    jmpi	0,SETUPSEG

其作用是把硬盘第6个扇区开始的240个扇区加载到内存0x10000处,然后指针跳转到0x90200处,即第2个扇区的开始位置

最终内存如下图:

bootsect3

下面将进入setup.s文件,未完待续……