[置顶] Linux最小系统


@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

你可能感兴趣的:([置顶] Linux最小系统)