您好,欢迎访问代理记账网站
移动应用 微信公众号 联系我们

咨询热线 -

电话 15988168888

联系客服
  • 价格透明
  • 信息保密
  • 进度掌控
  • 售后无忧

(一)Linux源码分析-硬件开机与引导

硬件开机

从硬件开机到系统启动,都经历了什么
BIOS做完计算机的初始化后,将会选择启动设备的第一个扇区进行加载,并将CPU控制权转移到
0x7C00的位置,将由Bootloader完成从实模式到保护模式的切换

bootsect.s

start

start就是Linux的bios启动程序入口,被加载到0x7c00处 给ds赋值BOOTSEG作为数据加载段,es为INITSEG作为代码拷贝处,使用rep汇编指令身内存拷贝到INITSEG,并跳转到INITSEG执行。

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

INITSEG

设置堆栈0x9ff00

go:	mov	ax,cs
	mov	ds,ax
	mov	es,ax
! put stack at 0x9ff00.
	mov	ss,ax
	mov	sp,#0xFF00		! arbitrary value >>512

使用BIOS中断INT 13从磁盘第二个扇区加载setup

load_setup:
! ah = 0x02 - 读磁盘扇区到内存;al = 需要读出的扇区数量;
! ch = 磁道(柱面)号的低8 位; cl = 开始扇区(0-5 位),磁道号高2 位(6-7);
! dh = 磁头号; dl = 驱动器号(如果是硬盘则要置位7);
! es:bx ??指向数据缓冲区; 如果出错则CF 标志置位。
	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

打印字符串(省略代码),验证读取的扇区并跳转到setup.s 程序的开始处,程序继续执行,bootsect.s职责完成。

	seg cs
	mov	ax,root_dev
	cmp	ax,#0
	jne	root_defined
	seg cs
	mov	bx,sectors
	mov	ax,#0x0208		! /dev/ps0 - 1.2Mb
	cmp	bx,#15
	je	root_defined
	mov	ax,#0x021c		! /dev/PS0 - 1.44Mb
	cmp	bx,#18
	je	root_defined
undef_root:
	jmp undef_root
root_defined:
	seg cs
	mov	root_dev,ax

! after that (everyting loaded), we jump to
! the setup-routine loaded directly after
! the bootblock:

	jmpi	0,SETUPSEG

setup.s

setup负责从bios中读取数据并将它复制到系统内存中

start

...

!获取扩展内存大小
mov	ah,#0x88
int	0x15
mov	[2],ax

!获取显示卡的模式
mov	ah,#0x0f
int	0x10
mov	[4],bx		! bh = display page
mov	[6],ax		! al = video mode, ah = window width

!检查显示方式(EGA/VGA)并取参数。
mov	ah,#0x12
mov	bl,#0x10
int	0x10
mov	[8],ax
mov	[10],bx
mov	[12],cx

...

移动内存

将system移到正确的地方,把从0x10000 到0x8ffff移动到0x00000位置。

	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

IDT,GDT

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

将内存移动完成后,使用lidt 加载中断描述符寄存器(IDT)。lgdt加载全局描述符表

end_move:
    !恢复ax寄存器的SETUPSEG值
	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

保护模式

主要是通过修改CR0寄存器进入保护模式

段选择符长度为16位
位0-1表示请求的特权级0-3(0-3环)
位1-2标识是全局或者局部描述符
位3-15是描述符表项的索引
段8(0b0000,0000,0000,1000)
表示请求特权级0、使用全局描述符表中的第1 项,由下面的代码可知基地址是0

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

lmsw修改cr0寄存器进入保护模式,然后跳转到cs段8 offset0处,执行system的代码

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

head.s

head.s是从绝对地址0x00000000开始

startup_32

startup_32:
    !装载段寄存器
	movl $0x10,%eax
	mov %ax,%ds
	mov %ax,%es
	mov %ax,%fs
	mov %ax,%gs
	!设置系统堆栈
	lss _stack_start,%esp
	!装载idt,gdt子程序
	call setup_idt
	call setup_gdt
	movl $0x10,%eax		# reload all the segment registers
	mov %ax,%ds		# after changing gdt. CS was already
	mov %ax,%es		# reloaded in 'setup_gdt'
	mov %ax,%fs
	mov %ax,%gs
	lss _stack_start,%esp
	
	!A20 地址线是否已经开启
	xorl %eax,%eax
1: incl %eax # check that A20 really IS enabled
    movl %eax,0x000000 # loop forever if it isn't
    cmpl %eax,0x100000
    je 1b
	...
	
	jmp after_page_tables

调用main函数

after_page_tables:
	pushl $0		# These are the parameters to main :-)
	pushl $0
	pushl $0
	pushl $L6
	!压入跳转的main地址
	pushl $_main
	jmp setup_paging
	
	...
	
	!首先对5 页内存(1 页目录 + 4 页页表)清零
	movl $1024*5,%ecx
	xorl %eax,%eax
	xorl %edi,%edi
	cld;rep;stosl
	
	!设置页目录中的项
	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
	!最后1 项对应物理内存页面的地址是0xfff000,加上属性标志7,即为0xfff007
	movl $0xfff007,%eax
	std
1:	stosl
	subl $0x1000,%eax
	jge 1b
	xorl %eax,%eax
	!设置启动使用分页处理(cr0 的PG 标志,位31)
	movl %eax,%cr3
	movl %cr0,%eax
	orl $0x80000000,%eax
	movl %eax,%cr0
	
	!弹出压入的main地址,开始执行main.c程序
	ret

head.s执行完后的内存视图

image


分享:

低价透明

统一报价,无隐形消费

金牌服务

一对一专属顾问7*24小时金牌服务

信息保密

个人信息安全有保障

售后无忧

服务出问题客服经理全程跟进