• boot 用于在架点的时候把 head 中的内核代码加载到内存某个指定位置,在设置好 GDT 表等信息之后,把处理器设置成运行在保护模式下,然后跳转到 head 代码处运行
    • 首先利用 ROM 的 BIOS 中断 int 0x13 把软盘中的 head 代码读入到内存 0x10000,然后再把这段代码移动到内存 0 开始,最后设置设置控制寄存器 CR0 中的开启保护运行模式标志,并跳转到内存 0 处开始执行 head 代码
    • 为什么不直接移动到 0 呢,因为在这里存放 BIOS 的中断过程,移动到时候本身就要用到
  • head 实现两个运行在 r3 的任务在始终控制下的相互切花运行,并且实现了在屏幕上显示字符的系统调用

PC 机在加电启动的时候,会把启动盘第一个扇区加载到物理内存 0x7c00 位置开始处,并把执行权转移到 0x7c00 处开始执行

这是 head 的空间布局(以段表为中心)

这是内存视图

Linux内核完全注释(修正版v3.0), p.141

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
 
 
load_system:
    mov     dx,#0x0000
    mov     cx,#0x0002
    mov     ax,#SYSSEG
    mov     es,ax
    xor     bx,bx
    mov     ax,#0x200+SYSLEN
    int     0x13                ! 利用BIOS中断从启动盘读取head代码 ES:BX 读入缓冲区位置 AH 读扇区功能号 AL 读的扇区数
    jnc     ok_load
die:jmp     die
 
! move kernel code to 0x0
ok_load:
    cli
    mov     ax,#SYSSEG
    mov     ds,ax
    xor     ax,ax
    mov     es,ax
    mov     cx,#0x1000
    sub     si,si
    sub     di,di
    mov cx,#0x1000
    rep 
    movw
    mov     ax,#BOOTSEG
    mov     ds,ax
    lidt    idt_48              ! load IDTR
    lgdt    gdt_48              ! load GDTR
    mov     ax,#0x0001
    lmsw    ax
    jmpi    0,8
 
gdt:
    .word   0,0,0,0
    .word   0x07ff
    .word   0x0000
    .word   0x9a00
    .word   0x00c0
    .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
 
 
LATCH=11930
SCRN_SEL=0x18
TSS0_SEL=0x20
LDT0_SEL=0x28
TSS1_SEL=0x30
LDT1_SEL=0x38

.text
startup_32:
# load segment register
    movl    $0x10,%eax
    mov     %ax,%ds
    lss     init_stack,%esp # loads a far pointer (segment selector and offset)
# load idt & gdt
    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
# set 8253 timer: int per 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
# set int (8) gate (128) in idt
    movl    $0x00080000,%eax    # 高字节是内核代码段选择符
    movw    $timer_interrupt,%ax # 中断描述符
    movw    $0x8e00,%dx # 中断门类型 r0
    movl    $0x08,%ecx
    lea     idt(,%ecx,8),%esi
    movl    %eax,(%esi)
    movl    %edx,4(%esi)
    movw    $system_interrupt,%ax # 陷阱门描述符
    movw    $0xef00,%dx # 陷阱门类型 r3
    movl    $0x80,%ecx
    lea     idt(,%ecx,8),%esi
    movl    %eax,(%esi)
    movl    %edx,4(%esi)
# 在堆栈中人工建立中断返回时的场景
    pushfl # push the entire 32-bit EFLAGS register onto the stack
    andl    $0xffffbfff,(%esp)
    popfl
    movl    $TSS0_SEL,%eax
    ltr     %ax
    movl    $LDT0_SEL,%eax
    lldt    %ax
    movl    $0,current # ?
    sti
    pushl   $0x17
    pushl   $init_stack
    pushfl
    pushl   $0x0f
    pushl   $task0
    iret


setup_gdt:
    lgdt    lgdt_opcode
    ret


# 设置idt中所有 256 个中断门描述符都为同一个 ingore_int
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
    mov     $SCRN_SEL,%ebx
    mov     %bx,%gs
    movl    src_loc,%bx
    shl     $1,%ebx
    movb    %al,%gs:(%ebx)
    shr     $1,%ebx
    incl    %ebx
    cmpl    $2000,%ebx
    jb      1f
    movl    $0,%ebx
1:  movl    %ebx,src_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


/* data */

current:    .long   0
src_loc:    .long   0

.align 2
lidt_opcode:.word   256*8-1
            .long   idt

lgdt_opcode:.word   (end_gdt-gdt)-1
            .long   gdt

.align 3
idt:        .fill   256,8,0

gdt:        .quad   0x0000000000000000
            .quad   0x00c09a00000007ff
            .quad   0x00c09200000007ff
            .quad   0x00c0920b80000002
            .word   0x68,tss0,0xe900,0x0
            .word   0x40,ldt0,0xe200,0x0
            .word   0x68,tss1,0xe900,0x0
            .word   0x40,ldt1,0xe200,0x0
end_gdt:    .fill   128,4,0

init_stack: .long   init_stack
            .word   0x10

.align 3
ldt0:       .quad   0x0000000000000000
            .quad   0x00c0fa00000003ff
            .quad   0x00c00f20000003ff

tss0:       .long   0
            .long   krn_stk0,0x10
            .long   0,0,0,0,0
            .long   0,0,0,0,0
            .long   0,0,0,0,0
            .long   0,0,0,0,0,0
            .long   LDT0_SEL,0x08000000


# 内核栈
            .fill   128,4,0
krn_stk0:

.align 3
ldt1:       .quad   0x0000000000000000
            .quad   0x00c0fa00000003ff
            .quad   0x00c00f20000003ff

tss1:       .long   0
            .long   krn_stk1,0x10
            .long   0,0,0,0,0
            .long   task1,0x200
            .long   0,0,0,0
            .long   usr_stk1,0,0,0
            .long   0x17,0x07,0x17,0x17,0x17,0x17
            .long   LDT1_SEL,0x08000000
            
# 内核栈
            .fill   128,4,0
krn_stk1:

task0:
    movl    $0x17,%eax
    movw    %ax,%ds
    movl    $65,%al  # 'A'
    int     $0x80
    movl    $0xfff,%ecx
1:  loop    1b
    jmp     task0

task1:
    movl    $66,%al # 'B'
    int     $0x80
    movl    $0xfff,%ecx
1:  loop    1b
    jmp     task1

# 用户栈
            .fill   128,4,0
usr_stk1: