【linux】【操作系统】引导启动程序之setup.s源码阅读

在这里插入图片描述

setup.s是Intel x86架构下的汇编语言代码,用于系统设置和初始化。它的主要功能是从BIOS获取系统数据,如内存大小、磁盘信息等,并将这些数据保存到一个安全的位置,以便后续的保护模式系统读取。代码还负责将系统从实模式切换到保护模式。

  • 设置数据段和代码段寄存器:通过将初始化段寄存器(INITSEG)设置为0x9000,将系统段寄存器(SYSSEG)设置为0x1000,将设置段寄存器(SETUPSEG)设置为0x9020,来定义数据和代码段的位置。
  • 保存光标位置:通过调用BIOS中断0x10的0x03功能号,获取当前光标位置,并将其保存在内存地址0x90000处。
  • 获取内存大小:通过调用BIOS中断0x15的0x88功能号,获取扩展内存的大小(以KB为单位),并将其保存在内存地址0x90002处。
  • 获取视频卡信息:通过调用BIOS中断0x10的0x0f功能号,获取视频卡的相关信息,如显示页、视频模式、窗口宽度等,并将其保存在内存的特定位置。
  • 获取硬盘数据:从BIOS中读取硬盘0和硬盘1的数据,并将其保存在内存地址0x90080和0x90090处。
  • 检查硬盘1是否存在:通过向硬盘1发送命令并检查返回值,来确定硬盘1是否存在。如果存在,则继续执行;否则,在内存地址0x90090处填充0x00。
  • 移动到保护模式:关闭中断,将系统移动到其正确的位置,并加载段描述符。然后,启用A20地址线,重新编程中断控制器,并最终跳转到保护模式下的地址0x00000。
  • 这段代码是操作系统引导加载器的一部分,用于在Intel x86架构的计算机上初始化系统并准备加载操作系统内核。

详细解析

1. 初始化与数据保存区域定义
  • INITSEG: 定义为0x9000,这是初始化代码的段基址。
  • SYSSEG: 定义为0x1000,这是系统代码的段基址。
  • SETUPSEG: 定义为0x9020,这是设置代码的段基址。
2. 保存光标位置
  • 调用BIOS中断0x100x03功能号,读取光标位置并保存在0x90000处,供后续使用。
3. 获取内存信息
  • 使用BIOS中断0x150x88功能号,获取扩展内存大小(单位:千字节),并将结果保存在0x90002处。
4. 获取视频卡数据
  • 通过BIOS中断0x100x0f功能号,获取视频卡信息,包括显示页面、视频模式及窗口宽度,分别保存在0x900040x90006处。
5. 获取硬盘数据
  • 读取硬盘0和硬盘1的BIOS参数块,保存在0x900800x90090处。
6. 检查硬盘1状态
  • 发送命令到硬盘1,根据返回值判断硬盘1是否存在,若不存在则清空0x90090处的数据。
7. 切换至保护模式
  • 关闭中断,开始将系统代码从0x10000移动到0x90000,为进入保护模式做准备。
  • 加载全局描述符表(GDT)和中断描述符表(IDT),并使能A20地址线。
  • 重置中断控制器,将硬件中断起点设为0x20
  • 最终通过修改机器状态字(LMSW)进入保护模式,并跳转到地址0x00000,开始执行32位保护模式代码。
8. 硬件初始化
  • 包括A20地址线的使能和中断控制器的重新配置,确保硬件中断的正确处理。
9. 描述符表设置
  • GDT: 全局描述符表,包含代码段和数据段描述符。
  • IDT: 中断描述符表,初始化为空。
10. 结束标记
  • endtext, enddata, endbss: 标记文本段、数据段和未初始化数据段的结束位置。

总结

此段代码是Linux内核引导过程中的关键部分,负责从实模式过渡到保护模式,同时收集必要的硬件信息,为后续的系统初始化和内核加载做准备。

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

! NOTE! These had better be the same as in bootsect.s!

INITSEG  = 0x9000	! we move boot here - out of the way
SYSSEG   = 0x1000	! system loaded at 0x10000 (65536).
SETUPSEG = 0x9020	! this is the current segment

.globl begtext, begdata, begbss, endtext, enddata, endbss
.text
begtext:
.data
begdata:
.bss
begbss:
.text

entry start
start:

! ok, the read went well so we get current cursor position and save it for
! posterity.

	mov	ax,#INITSEG	! this is done in bootsect already, but...
	mov	ds,ax
	mov	ah,#0x03	! read cursor pos
	xor	bh,bh
	int	0x10		! save it in known place, con_init fetches
	mov	[0],dx		! it from 0x90000.

! Get memory size (extended mem, kB)

	mov	ah,#0x88
	int	0x15
	mov	[2],ax

! Get video-card data:

	mov	ah,#0x0f
	int	0x10
	mov	[4],bx		! bh = display page
	mov	[6],ax		! al = video mode, ah = window width

! check for EGA/VGA and some config parameters

	mov	ah,#0x12
	mov	bl,#0x10
	int	0x10
	mov	[8],ax
	mov	[10],bx
	mov	[12],cx

! Get hd0 data

	mov	ax,#0x0000
	mov	ds,ax
	lds	si,[4*0x41]
	mov	ax,#INITSEG
	mov	es,ax
	mov	di,#0x0080
	mov	cx,#0x10
	rep
	movsb

! Get hd1 data

	mov	ax,#0x0000
	mov	ds,ax
	lds	si,[4*0x46]
	mov	ax,#INITSEG
	mov	es,ax
	mov	di,#0x0090
	mov	cx,#0x10
	rep
	movsb

! Check that there IS a hd1 :-)

	mov	ax,#0x01500
	mov	dl,#0x81
	int	0x13
	jc	no_disk1
	cmp	ah,#3
	je	is_disk1
no_disk1:
	mov	ax,#INITSEG
	mov	es,ax
	mov	di,#0x0090
	mov	cx,#0x10
	mov	ax,#0x00
	rep
	stosb
is_disk1:

! now we want to move to protected mode ...

	cli			! no interrupts allowed !

! first we move the system to it's rightful place

	mov	ax,#0x0000
	cld			! 'direction'=0, movs moves forward
do_move:
	mov	es,ax		! destination segment
	add	ax,#0x1000
	cmp	ax,#0x9000
	jz	end_move
	mov	ds,ax		! source segment
	sub	di,di
	sub	si,si
	mov 	cx,#0x8000
	rep
	movsw
	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_48		! load idt with 0,0
	lgdt	gdt_48		! load gdt with whatever appropriate

! that was painless, now we enable A20

	call	empty_8042
	mov	al,#0xD1		! command write
	out	#0x64,al
	call	empty_8042
	mov	al,#0xDF		! A20 on
	out	#0x60,al
	call	empty_8042

! 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 sequence
	out	#0x20,al		! send it to 8259A-1
	.word	0x00eb,0x00eb		! jmp $+2, jmp $+2
	out	#0xA0,al		! and to 8259A-2
	.word	0x00eb,0x00eb
	mov	al,#0x20		! start of hardware int's (0x20)
	out	#0x21,al
	.word	0x00eb,0x00eb
	mov	al,#0x28		! start of hardware int's 2 (0x28)
	out	#0xA1,al
	.word	0x00eb,0x00eb
	mov	al,#0x04		! 8259-1 is master
	out	#0x21,al
	.word	0x00eb,0x00eb
	mov	al,#0x02		! 8259-2 is slave
	out	#0xA1,al
	.word	0x00eb,0x00eb
	mov	al,#0x01		! 8086 mode for both
	out	#0x21,al
	.word	0x00eb,0x00eb
	out	#0xA1,al
	.word	0x00eb,0x00eb
	mov	al,#0xFF		! mask off all interrupts for now
	out	#0x21,al
	.word	0x00eb,0x00eb
	out	#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.

	mov	ax,#0x0001	! protected mode (PE) bit
	lmsw	ax		! This is it!
	jmpi	0,8		! jmp offset 0 of segment 8 (cs)

! 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.
empty_8042:
	.word	0x00eb,0x00eb
	in	al,#0x64	! 8042 status port
	test	al,#2		! is input buffer full?
	jnz	empty_8042	! yes - loop
	ret

gdt:
	.word	0,0,0,0		! dummy

	.word	0x07FF		! 8Mb - limit=2047 (2048*4096=8Mb)
	.word	0x0000		! base address=0
	.word	0x9A00		! code read/exec
	.word	0x00C0		! granularity=4096, 386

	.word	0x07FF		! 8Mb - limit=2047 (2048*4096=8Mb)
	.word	0x0000		! base address=0
	.word	0x9200		! data read/write
	.word	0x00C0		! granularity=4096, 386

idt_48:
	.word	0			! idt limit=0
	.word	0,0			! idt base=0L

gdt_48:
	.word	0x800		! gdt limit=2048, 256 GDT entries
	.word	512+gdt,0x9	! gdt base = 0X9xxxx
	
.text
endtext:
.data
enddata:
.bss
endbss:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值