@version: 0.0
@author: zhaojiong
@learner: liminghao
@project: linux0.0
@run: bochs-->6-->accord to your need debug this simple and classic sample.
@Summary:
该内核实例由两个文件构成:一个使用as86语言编制的引导启动程序boot.s, 用于在计算机系统加电时
从启动盘上把内核代码加载到内存中;另一个是使用GNU as汇编语言编制的内核程序head.s,其中实现
了两个运行在特权级3上的任务在时钟中断控制下相互切换运行,并且还实现了在屏幕上显示字符的一个
系统调用.
boot.s:
/* ROM BIOS中的程序会将存放在启动盘上第一个扇区加载加载到物理内存0x7c00(31KB)位置处,并
* 把执行权转移到0x7c00处开始运行boot程序代码,刚开始程序与实模式运行
* 开启实模式:将寄存器CR0,最低位0位,又称PM位清零,计算机加电时其是清零的
* 内存访问方式: 段寄存器:偏移量--->实际物理地址=段寄存器 << 4 + 偏移量 = 段寄存器 * 16
* + 偏移量
*/
.globl begtext, begdata, begbss, endtext, enddata, endbss
.text
begtext:
.data
begdata:
.bss
begbss:
.text
BOOTSEG = 0x07c0
SYSSEG = 0x1000
SYSLEN = 17
entry start
start:
jmpi go, #BOOTSEG
go: mov ax, cs
mov ds, ax
mov ss, ax # 在此设置一个临时堆栈,用于保存数据
mov sp, #0x400
/* 利用BIOS 13号中断int 13, 加载内核代码到地址0x10000处
* Input: ah = 0x02, 读扇区到内存; al = 需要读出的扇区数量
* ch = 磁道号的低8位; cl = 0~5, 开始扇区, 6~7磁道号高2位
* dh = 磁头号; dl = 驱动器号
* es:bx = 只想数据缓冲区
* Output: CF置位, 出错, ah = 出错码.
*/
load_system:
mov dx, #0x0000
mov cx, #0x0002
mov ax, #SYSSEG
mov es, ax
xor bx, bx
mov ax, #0x200+SYSLEN
int 0x13
jnc ok_load
die: jmp die
/* 将内核映像成功加载到地址0x10000处时,有需将其移动到0x00000位置处.
* NOTE!!! 不将其直接移动到0x00000原因是要用到放置在从0x00000开始处的BIOS中断处理函数
* ds:si --> es:di
*/
ok_load:
cli
mov ax, #SYSSEG
mov ds, ax
xor ax, ax
mov es, ax
mov cx, #0x1000
sub si, si
sub di, di
rep
movw
/* 将内核映像移动到0x00000处后,设置一个临时GDT表,暂时用于保护模式的寻址
* 保护模式寻址: 通过段选择符去查找GDT表,从中读出段起始地址,段起始地址加上偏移地址获得
* 物理地址
* 段选择符, 相当于一个数组的下标, GDT表相当于一个数组, 用该下标去数组中寻找偏移地址
* 而这个数组首地址存在于一个寄存器中: 详见《LINUX 内核完全剖析》P86
*/
mov ax, #BOOTSEG
mov ds, ax
lidt idt_48
lgdt gdt_48
mov ax, #0x0001
lmsw ax
jmpi 0, 8
! The fllows is global descriptor, the first is not used
gdt: .word 0, 0, 0, 0
! the second is code descriptor
.word 0x07FF ! PL=0x07FF, is paragraph limit 0~15 bit
.word 0x0000 ! BA=0x000000, is base address 0~24 bit
.word 0x9A00 ! TYPE=A=1010=1CRA, P:DPL:S=9=1:00:1, NOTE: S=0 is a system descriptor
! DPL=Privilege Level=00~11=0, 1, 2, 3, P=0 is descriptor useless
.word 0x00C0 ! PL=0x0, is paragraph limit 16-19 bit
! G:D/B:0:AVL=C=1100
! G=0 represent the unit of PL is 1Byte, =1 is 4KB
! D/B=0 is a 16 bit paragraph, =1 is a 32 bit paragraph
! AVL is give coders use
! BA=0x00, is base address 24~31 bit
.word 0x07FF
.word 0x0000
.word 0x9200
.word 0x00C0
idt_48: .word 0
.word 0, 0
gdt_48: .word 0x7ff
.word 0x7c00 + gdt, 0
.org 510
.word 0xAA55 // 说明这是一个引导扇区
.text
endtext:
.data
enddata:
.bss
endbss:
NOTE!!!
1.段选择符(数组下标):
|--------------------------------|
| INDEX |TI|RPL|
|--------------------------------|
TI=0, 说明要在数组GDT中进行查找; TI=1, 说明要在数组LDT中进行查找
RPL, 请求特权级
INDEX是真正的数组下标
2.GDT数组
元素(段描述符):
见《LINUX内核完全剖析》P90
数组首地址和数组大小: 存放在GDTR寄存器中,使用命令lgdt加载
3.一句话概括保护模式寻址:
使用数组下标去数组中寻找基地址,当然在寻找过程中要考虑三个问题:
1).数组下标:程序员给出,一般放在段寄存器中
2).数组首地址和数组大小:由CPU中寄存器GDTR给出
3).数组:数组本身存放在内存中
4.保护模式寻址作用:
将CPU可以寻址的线性地址空间按段进行区分,程序中使用逻辑地址,就可以经过这种方式计算出线性
地址
NOTE:什么是线性地址空间?
假设计算机具有32根地址线,则2^32=4GB,4GB就是线性地址空间
逻辑地址:一般是由程序员给出,也就是变量左值,由编译器进行加工了一下
#head.s: 运行在32位保护模式下
/* @function:
* @1: 重新设置GDT数组
* @2: 设置系统定时器芯片
* @3: 重新设置IDT数组并且设置时钟和系统调用的中断处理函数地址(中断门)
* @4: 移动到任务A中运行
*/
LATCH = 11930
SCRN_SEL = 0x18
TSS0_SEL = 0x20
LDT0_SEL = 0x28
TSS1_SEL = 0x30
LDT1_SEL = 0x38
.text
startup_32:
movl $0x10, %eax
mov %ax, %ds
lss init_stack, %esp
call setup_idt
call setup_gdt
movl $0x10, %eax
mov %ax, %ds
mov %ax, %es
mov %ax, %fs
mov %ax, %gs
lss init_stack, %esp
// 设置8253定时器芯片.把计数器通道0设置成每隔10ms向中断控制器发送一个中断请求信号
movb $0x36, %al
movl $0x43, %edx
outb %al, %dx
movl $LATCH, %eax
movl $0x40, %edx
outb %al, %dx
movb %ah, %al
outb %al, %dx
// P107, P115, P121, P137
movl $0x00080000, %eax
movw $timer_interrupt, %ax
movl $0x8E00, %edx
movl $0x08, %ecx
lea idt(, %ecx, 8), %esi
movl %eax, (%esi)
movl %edx, 4(%esi)
movw $system_interrupt, %ax
movw $0xef00, %dx
movl $0x80, %ecx
lea idt(, %ecx, 8), %esi
movl %eax, (%esi)
movl %edx, 4(%esi)
pushfl
andl $0xffffbfff, (%esp)
popfl
movl $TSS1_SEL, %eax
ltr %ax
movl $LDT1_SEL, %eax
lldt %ax
movl $1, current
sti
pushl $0x17
pushl $init_stack
pushfl
pushl $0x0f
pushl $task1
iret
setup_gdt:
lgdt lgdt_opcode
ret
setup_idt:
lea ignore_int, %edx
movl $0x00080000, %eax
movw %dx, %ax
movw $0x8E00, %dx
lea idt, %edi
mov $256, %ecx
rp_idt: movl %eax, (%edi)
movl %edx, 4(%edi)
addl $8, %edi
dec %ecx
jne rp_idt
lidt lidt_opcode
ret
write_char:
push %gs
pushl %ebx
movl $SCRN_SEL, %ebx
mov %bx, %gs
movl scr_loc, %ebx
shl $1, %ebx
movb %al, %gs:(%ebx)
shr $1, %ebx
incl %ebx
cmpl $2000, %ebx
jb 1f
movl $0, %ebx
1: movl %ebx, scr_loc
popl %ebx
pop %gs
ret
.align 2
ignore_int:
push %ds
pushl %eax
movl $0x10, %eax
mov %ax, %ds
movl $67, %eax
call write_char
popl %eax
pop %ds
iret
.align 2
timer_interrupt:
push %ds
pushl %eax
movl $0x10, %eax
mov %ax, %ds
movb $0x20, %al
outb %al, $0x20
movl $1, %eax
cmpl %eax, current
je 1f
movl %eax, current
ljmp $TSS1_SEL, $0
jmp 2f
1: movl $0, current
ljmp $TSS0_SEL, $0
2: popl %eax
pop %ds
iret
.align 2
system_interrupt:
push %ds
pushl %edx
pushl %ecx
pushl %ebx
pushl %eax
movl $0x10, %edx
mov %dx, %ds
call write_char
popl %eax
popl %ebx
popl %ecx
popl %edx
pop %ds
iret
current:
.long 0
scr_loc:
.long 0
.align 2
lidt_opcode:
.word 256 * 8 - 1
.long idt
lgdt_opcode:
.word (end_gdt-gdt) - 1
.long gdt
.align 8
idt: .fill 256, 8, 0
gdt: .quad 0x0000000000000000 # not use
.quad 0x00c09a00000007ff # code, 0x08
.quad 0x00c09200000007ff # data, 0x10
.quad 0x00c0920b80000002 # video memory, 0x18
.word 0x68, tss0, 0xe900, 0x0 # TSS0, 0x20
.word 0x40, ldt0, 0xe200, 0x0 # LDT0, 0x28
.word 0x68, tss1, 0xe900, 0x0 # TSS1, 0x30
.word 0x40, ldt1, 0xe200, 0x0 # LDT1, 0x38
end_gdt:
.fill 128, 4, 0
init_stack:
.long init_stack
.word 0x10
.align 8
ldt0: .quad 0x0000000000000000
.quad 0x00c0fa00000003ff
.quad 0x00c0f200000003ff
tss0: .long 0
.long krn_stk0, 0x10 # esp0, ss0
.long 0, 0, 0, 0, 0 # esp1, ss1, esp2, ss2, cr3
.long task0, 0x200 # eip, eflags
.long 0, 0, 0, 0 # eax, ecx, edx, ebx
.long usr_stk0, 0, 0, 0 # esp, ebp, esi, edi
.long 0x17, 0x0f, 0x17, 0x17, 0x17, 0x17 # es, cs, ss, ds, fs, gs
.long LDT0_SEL, 0x8000000
.fill 128, 4, 0
krn_stk0:
.align 8
ldt1: .quad 0x0000000000000000
.quad 0x00c0fa00000003ff
.quad 0x00c0f200000003ff
tss1: .long 0
.long krn_stk1, 0x10 # esp0, ss0
.long 0, 0, 0, 0, 0 # esp1, ss1, esp2, ss2, cr3
.long task1, 0x200 # eip, eflags
.long 0, 0, 0, 0 # eax, ecx, edx, ebx
.long usr_stk1, 0, 0, 0 # esp, ebp, esi, edi
.long 0x17, 0x0f, 0x17, 0x17, 0x17, 0x17 # es, cs, ss, ds, fs, gs
.long LDT1_SEL, 0x8000000
.fill 128, 4, 0
krn_stk1:
task0:
movl $0x17, %eax
movw %ax, %ds
movb $65, %al /* print 'A' */
int $0x80
movl $0xfff, %ecx
1: loop 1b
jmp task0
.fill 128, 4, 0
usr_stk0:
task1:
movl $0x17, %eax
movw %ax, %ds
movb $66, %al /* print 'B' */
int $0x80
movl $0xfff, %ecx
1: loop 1b
jmp task1
.fill 128, 4, 0
usr_stk1:
工程完整下载链接:http://download.csdn.net/my