【Linux内核-02】引导(boot)与初始化(init)

引导启动程序(boot)

概述

boot/目录文件列表
bootsect.s
setup.s
head.s

注释:
实模式+Intel:bootsect.s, setup.s
保护模式+ATT:head.s

原理

步骤1:启动电源,CPU进入实模式,执行ROM-BIOS(位于内存0xFFFF0处),其功能为:(1)硬件检测;(2)在物理地址0处设置中断向量;(3)将引导扇区(第一个扇区,512字节)加载到0x07c00处。

步骤2:执行bootsect.s(位于内存0x7c00处),其功能为:(1)将自身移动到0x90000~0x90200,共512B。(2)将setup模块(共四个扇区)加载到0x90200处。(3)将内核system模块加载到0x10000处。

步骤3:执行setup.s(位于内存0x90200处),其功能为:(1)将system模块移动到物理内存起始地址。(2)进入保护模式并跳转到head.s

步骤4:执行head.s(位于内存0x00000处),其功能为:(1)加载GDT、IDT、LDT;设置分页工作;完成保护模式的相关设置。(2)执行init/main.c

源码分析

bootsect.S

SETUPLEN = 4				! setup程序代码占用扇区数
BOOTSEG  = 0x07c0			! bootsect程序代码所在内存原始地址
INITSEG  = 0x9000			! 将bootsect移动到0x9000处
SETUPSEG = 0x9020			! setup程序开始的地址

! entry指定程序入口
entry _start
_start:

! 将自身复制到0x9000处
	mov	ax,#BOOTSEG 
	mov	ds,ax  !基址ds=0x7c00
	mov	ax,#INITSEG 
	mov	es,ax  ! 基址es=0x9000
	mov	cx,#256
	sub	si,si  ! 偏移si=0
	sub	di,di  ! 偏移di=0
	! ds:si = 0x7c000
	! es:di = 0x90000
	rep  movw  ! 重复移动256字节(0x7c00→0x9000)
	jmpi	go,INITSEG  ! CS=INITSEG, ip=go.(顺序执行)
	
go:	mov	ax,cs
	mov	ds,ax  
	mov	es,ax 
	! 设置ds=es=cs=0x9000

! 加载setup.s程序
load_setup: 
	mov	dx,#0x0000		! dh:磁头号0, dl:驱动器号0
	mov	cx,#0x0002		! ch:柱面号0, cl:开始扇区2
	mov	bx,#0x0200		! es:bx = address = 0x9000 + 512
	mov	ax,#0x0200+SETUPLEN	! ah:读磁盘功能号2, al:扇区数量4
	int	0x13			! 读磁盘中断
	jnc	ok_load_setup	! ok,读setup.S到0x90200
	!加载错误
	mov	dx,#0x0000
	mov	ax,#0x0000		! 复位
	int	0x13
	j	load_setup		! 重读
	
! 输出一些信息
ok_load_setup:
	mov	ah,#0x03		! 当前位置
	xor	bh,bh
	int	0x10			! 中断:读当前光标
	
	mov	cx,#26			! 读26个字符
	mov	bx,#0x000c		! page 0, attribute c 
	mov	bp,#msg1		! es:bp = 指向待显示字符串
	mov	ax,#0x1301		! write string, move cursor
	int	0x10			! 显示中断
!开始执行setup代码
	jmpi 0,SETUPSEG

msg1:
	.byte 13,10			! 回车:13  换行:10
	.ascii "Duanos is loading..."
	.byte 13,10,13,10

setup.S

entry _start
_start:

!设置cs=ds=es=0x9020
	mov	ax,cs
	mov	ds,ax
	mov	es,ax

	mov	ah,#0x03		! 当前位置
	xor	bh,bh
	int	0x10			! 中断:读当前光标
	
	mov	cx,#28			! 读28个字符
	mov	bx,#0x000c		! page 0, attribute c 
	mov	bp,#msg1
	mov	ax,#0x1301		! write string, move cursor
	int	0x10
	
	...

! 获取拓展内存大小 => 0x9000:2;不同机器不同的内存(1m扩展)
	mov	ah,#0x88
	int	0x15
	mov	[2],ax
	
	...

! 移动system(0x10000→0x00000)
do_move:
	mov	es,ax		! destination segment: ex=0x0000
	add	ax,#0x1000
	cmp	ax,#0x9000
	jz	end_move	! system内容覆盖boot
	mov	ds,ax		! source segment: ds=0x1000
	sub	di,di
	sub	si,si
	! ds:si = 0x10000
	! es:di = 0x00000
	mov cx,#0x8000
	rep  movsw		! 重复移动8000字节
	jmp	do_move
	
	...
	
! 进入保护模式
	mov	ax,#0x0001	! 进入保护模式
	lmsw	ax		! mov cr0 ax (cr0 = 0 or 1 : 实模式 or 保护模式)
	! 此时寻址方式已经由实模式(16位)切换为保护模式(32位)
	jmpi	0,8		! 跳转地址0x0000

head.s

...					# startup_32
... 				# set gdt, idt, ldt, page_table

after_page_tables:
	pushl $0		
	pushl $0
	pushl $0		# main参数argv[3]
	pushl $L6		# 返回地址,跳转到main()
	pushl $main
	jmp setup_paging
L6:
	jmp L6			# 执行主函数

初始化程序(init)

概述

init/目录文件列表
main.c

内核初始化的相关工作

相关功能:各种初始化

  1. 初始化:陷阱门、块设备、字符设备和 tty。
  2. 开启中断,切换到任务0运行

原理

源码分析

void main(void)
{	
	//初始化工作
	mem_init(main_memory_start,memory_end);
	trap_init();
	blk_dev_init();
	chr_dev_init();
	tty_init();
	time_init();
	sched_init();
	buffer_init(buffer_memory_end);
	hd_init();
	floppy_init();
	sti();
	move_to_user_mode();
	if (!fork()) {		/* we count on this going ok */
		init();
	}
	
	for(;;) pause();
}