编译器:nasm
虚拟机:bochs
操作系统:macosx
两个汇编程序boot.s和init.s
boot.s模拟引导程序,写入启动盘的0磁道,1扇区(512字节)中,在操作系统启动时,被bios加载进0x7c00处,并从这开始执行,它负责把init.s读入内存0x10000处,然后拷贝到0x0处,到这为止操作系统都运行在实模式下
(实模式的寻址方式是段地址<<4 + 偏移地址
,如段基址0x7c0,偏移地址0x03,则实际地址是0x7c03。)然后准备进入保护模式,设置gdt、idt表,通过设置cr0寄存器的pe位,进入保护模式,然后通过jmp 段描述符:0
跳入操作系统内核执行。
init.s就模拟内核程序,boot.s把它拷贝到0x0处,进入保护模式,然后从0x0开始执行。
这次的init.s就写一个不断在屏幕上打印字符’A’的程序。
其实从0x0开始运行时是内核态,这样写比较简单,没有涉及到任务,用户态。
init.s引导程序:
BOOTSEG equ 0x07c0
SYSSEG equ 0x1000
SYSLEN equ 3
section .text vstart=0
jmp BOOTSEG:start
start:
mov ax, cs
mov ss, ax
mov ds, ax
;读磁盘
mov ax, 0x0200+SYSLEN
mov cx, 0x0002
mov dx, 0x0000
mov bx, SYSSEG
mov es, bx
xor bx, bx
int 0x13
jnc ok_load
ok_load:
;移动到内存0x0处(原地址ds:si,目的地址es:di)
cli
mov ax, SYSSEG
mov ds, ax
xor ax, ax
mov es, ax
mov cx, SYSLEN*128
sub si, si
sub di, di
rep movsw
;加载idt和gdt基地址寄存器idtr和ldtr
mov ax, BOOTSEG
mov ds, ax
lidt [idt_48]
lgdt [gdt_48]
;设置cr0寄存器到pe位,进入保护模式
mov ax, 0x0001
lmsw ax
jmp 8:0
gdt:
dw 0,0,0,0
dw 0x07ff ;代码段
dw 0x0000
dw 0x9a00
dw 0x00c0
dw 0x07ff ;数据段
dw 0x0000
dw 0x9200
dw 0x00c0
idt_48:
dw 0
dw 0,0
gdt_48:
dw 0x7ff
dw 0x7c00+gdt,0
times 510-($-$$) db 0
dw 0xaa55
模拟内核代码init.s,重新设置了gdt表,新增了一个显示内存段,就是0xb8000开始的显存,通过在这里写入字符,直接显示在屏幕上:
[BITS 32]
SCRN_SEL equ 0x18
section .text vstart=0
;现在是保护模式
;
;重新定义gdt
;除了代码段、数据段再定义一个显示内存段(0xb8000开始的显存)
;
;在内核代码段中执行不断打印字母'A'的程序
start_up:
mov eax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
lss esp, [init_stack]
lgdt [lgdt_opcode]
f: mov al, 'A'
call print
jmp f
print:
;打印字母al中的字母
mov ebx, SCRN_SEL
mov gs, bx
mov ebx, [scr_loc]
shl ebx, 1
mov byte [gs:ebx], al
mov byte [gs:ebx+1], 07
shr ebx, 1
inc ebx
cmp ebx, 2000
jb l
mov ebx, 0
l: mov [scr_loc], ebx
ret
scr_loc dd 0 ; 如果dd大于2000就复位为0,因为屏幕是25*80=2000,一屏最多显示2000个字符
lgdt_opcode:
dw (end_gdt-gdt)-1
dw gdt, 0
;gdt: dq 0x0000_0000_0000_0000 ;gdt表。第一项不用
; dq 0x00c0_9a00_0000_07ff ;内核代码段。其选择符是0x08
; dq 0x00c0_9200_0000_07ff ;内核数据段。其选择符是0x10
; dq 0x00c0_920b_8000_0002 ;显示内存段。其选择符是0x18
gdt: dw 0,0,0,0
;dq 0x0000000000000000 why not work?
dw 0x07ff ;代码段
dw 0x0000
dw 0x9a00
dw 0x00c0
dw 0x07ff ;数据段
dw 0x0000
dw 0x9200
dw 0x00c0
dd 0x80000002
dd 0x00c0920b
end_gdt:
times 128 dd 0 ;内核堆栈段
init_stack:
dd init_stack ; esp
dw 0x10 ; ss
参考:赵炯《Linux内核完全剖析》