专注于操作系统24之boot,loader,kernel

         在第6篇文章中,我们已能导入操作系统内核(该内核非常小,姑且称为内核),但也只是在实模式下,没有进入保护模式。前面我们已经学习了关于保护模式的很多内容,所以让操作系统进入保护模式是非常简单的。这里先解释一下boot,loader,kernel的意思和作用。boot是引导程序,它的作用是把loader(loader专门用于加载kernel)加载到内存,执行loader。loader是加载程序,它的作用是把kernel加载到内存,并进入保护模式,然后,把cpu交给kernel。kernel是操作系统中与硬件直接相关的部分和经常共用的代码。一句话,boot加载loader到内存,执行loader,再由loader加载内核到内存,并进入保护模式,然后,将cpu交给kernel,那么操作系统就启动起来了。

      我们这里暂时不考虑kernel,只是让boot加载loader到内存,执行loader,loader进入保护模式(本来在进入保护模式前我们要加载kernel的,但我们还没有kernel,所以暂时不管)。

      下面给出代码,这个代码和第6篇文章中的代码很相似,只是加上了进入保护模式的代码。


myboot.asm 的代码

;;nasm 2.07 汇编
;;nasm myboot.asm -o myboot.img
org 7c00H

call welcome      ;调用欢迎模块,显示欢迎字符
call loadloader   ;导入loader


welcome:            ;欢迎模块 
	   mov ax,cs   ;调用BIOS的10号中断显示字符
	   mov es,ax
	   mov bp,hello
	   mov bx,000ch
	   mov dx,0005h
       mov cx,18
	   mov ax,1301h
	   int 10h
	   ret
loadloader:         ;导入loader
       reset:        ;磁盘系统复位
	      mov ax,0  ;调用BIOS的13号中断使磁盘系统复位
		  mov dl,0
		  int 13h
		  jc reset      ;如果复位成功CF=0,如果不成功CF!=0,重新复位
       load:              ;将loader导入到内存的es:bx位置,这里是01000h:0h
	     mov ax,01000h   ;这个位置可自己设定
         mov es,ax       ;这里的loader是放在软盘的第二个扇区,所以,调用BIOS的13号中断
	   	mov bx,0h       ;将loader从软盘导入到内存

		mov cx,0002h
		mov dx,0000h
		mov ax,0201h
		int 13h
		jc load          ;导入成功,CF=0,不成功CF!=0,则重新导入

  jmp 1000h:0000h        ;导入成功后,跳到内存的01000h:0h的位置(因已导入loader,所以这里放的是loader的代码),
                         ;执行loader的代码
hello: db 'hello this is boot'   ;欢迎的字符

times 510-($-$$) db 0      ;补满剩下的空间,最后两个字节为0aa55h表示是引导扇区
dw 0aa55h


myloader.asm 的代码


;;nasm 2.07 汇编
;;nasm myloader.asm -o myloader.img
org 0h    ;在本段的偏移地址为0h的地方载入

%macro Descriptor 3             ;定义Descriptor结构体宏
	dw	%2 & 0FFFFh				; 段界限 1				(2 字节)
	dw	%1 & 0FFFFh				; 段基址 1				(2 字节)
	db	(%1 >> 16) & 0FFh			; 段基址 2				(1 字节)
	dw	((%2 >> 8) & 0F00h) | (%3 & 0F0FFh)	; 属性 1 + 段界限 2 + 属性 2		(2 字节)
	db	(%1 >> 24) & 0FFh			; 段基址 3				(1 字节)
%endmacro ; 共 8 字节

DA_C		EQU	98h	; 只读代码段的属性值
DA_DRW		EQU	92h	; 允许读写的数据段的属性值
DA_32		EQU	4000h	;32位段的属性值

jmp start
	               ;现在进行第一步:建立GDT表
[SECTION .gdt]     ;用于放GDT表的段
;GDT
LABEL_GDT: Descriptor 0,0,0 ;空描述符,当一个任务没有LDT时,会把LDTR清空,这时,LDTR便指向空描述符。

LABEL_CODE32: Descriptor 0,SegCode32Len-1,DA_C + DA_32 ; 代码段的描述符

LABEL_VIDEO: Descriptor 0B8000h,0ffffh,DA_DRW    ;显存的描述符,0b8000h是显存的首地址
;GDT 结束

GdtLenth equ $ - LABEL_GDT   ;计算GDT表的长度

GdtPtr dw GdtLenth - 1 ; 准备存入GDTR的信息
       dd 0    ;    用于存放GDT表的物理地址,后面会计算


;GDT选择子

SelectorCode32 equ LABEL_CODE32 - LABEL_GDT ;代码段的选择子
SelectorVideo  equ LABEL_VIDEO - LABEL_GDT  ;显存所在段的选择子

;[SECTION .gdt]结束

 hello: db 'this is loader,loading kernel'  ; 欢迎字符

start:
call welcome  ;调用欢迎模块,表示进入了loader
call loadkernel    ;加载内核,进入保护模式(我们现在还没有内核,所以只进入保护模式)

welcome:         ;欢迎模块,调用BIOS的10h号中断,显示欢迎字符
       mov ax,cs
	   mov es,ax
	   mov bp,hello

	   mov bx,000ch
	   mov dx,0200h
	   mov cx,29

	   mov ax,1301h
	   int 10h
	   
loadkernel:
jmp LABEL_BEGIN

[SECTION .s16]
[BITS 16]	;目标处理器模式
LABEL_BEGIN:
mov ax,cs
mov ds,ax
mov es,ax
mov ss,ax
mov sp,0100h

;初始化32位段的描述符

xor eax,eax   ; 同或运算,这里是将eax清零

mov ax,cs
shl eax,4     ;左移四位,相当于乘以16,实模式下计算物理地址
add eax,LABEL_SEG_CODE32    ;eax中为32位段的物理地址

mov word [LABEL_CODE32 + 2],ax   ;将32位段的物理地址存入段的描述符中

shr eax,16
mov byte [LABEL_CODE32 + 4],al
mov byte [LABEL_CODE32 + 7],ah

;计算GDT表的物理地址,将GDT表的物理地址信息存入GdtPtr中
xor eax,eax
mov ax,cs
shl eax,4
add eax,LABEL_GDT
mov dword [GdtPtr + 2],eax

;现在进行第二步:将GDT表的信息加载到GDTR中
lgdt [GdtPtr]  ;装载GDTR

;关中断
cli

;现在进行第三步:打开地址线
in al,92h
or al,00000010b
out 92h,al

;现在进行第四步:置PE为1,表示CPU处于保护模式下
mov eax,cr0
or eax,1
mov cr0,eax
;现在进入第五步:跳到保护模式的代码

jmp dword SelectorCode32:0  ;这里将SelectorCode32存入CS中作为选择子
;[SECTION .s16] 结束

;保护模式下的代码,32位代码
[SECTION .s32]
[BITS 32]  
LABEL_SEG_CODE32:
mov ax,SelectorVideo ;视频选择子,用于找到显存段的描述符
mov gs,ax 

mov edi,(80 * 10 + 0)*2  ;屏幕的第10行,第0列
mov ah,0Ch
mov al,'p'
mov [gs:edi],ax ;将字符‘p’输出到屏幕
jmp $
SegCode32Len equ $ - LABEL_SEG_CODE32  ;计算32位代码段的长度

times 512-($-$$) db 0
执行步骤:与第6篇文章的相同,具体的解释请参见第6篇文章

1.nasm myboot.asm -o myboot.img

2.nasm myloader.asm -o mykernel.img

3.partcopy myloader.img 0 200 myboot.img 200

4.把myboot.img 装入虚拟机

下面是运行的结果图



你可能感兴趣的:(写操作系统)