一、boot.bin:
加载loader.bin到内存并运行,该内容及下面的加载内核的方法可查看前一篇文章。
二、loader.bin:
加载内核到内存、跳入保护模式、开启分段分页机制、重新放置内核并向内核交出控制权。
这里要分析的只有“重新放置内核并向内核交出控制权”,这段内容。
InitKernel: ; 遍历每个Program Header,根据其中信息确定把什么放入内存,放到什么位置,以及放多少。
xor esi, esi
mov cx, word [BaseOfKernelFilePhyAddr + 2Ch]; \u2513 ecx <- pELFHdr->e_phnum
movzx ecx, cx ; \u251b
mov esi, [BaseOfKernelFilePhyAddr + 1Ch] ; esi <- pELFHdr->e_phoff
add esi, BaseOfKernelFilePhyAddr ; esi <- OffsetOfKernel + pELFHdr->e_phoff
.Begin:
mov eax, [esi + 0]
cmp eax, 0 ; PT_NULL
jz .NoAction
push dword [esi + 010h] ; size \u2513
mov eax, [esi + 04h] ; \u2503
add eax, BaseOfKernelFilePhyAddr ; \u2523 ::memcpy( (void*)(pPHdr->p_vaddr),
push eax ; src \u2503 uchCode + pPHdr->p_offset,
push dword [esi + 08h] ; dst \u2503 pPHdr->p_filesz;
call MemCpy ; \u2503
add esp, 12 ; \u251b
.NoAction:
add esi, 020h ; esi += pELFHdr->e_phentsize
dec ecx
jnz .Begin
ret
;***************************************************************
jmp SelectorFlatC:KernelEntryPointPhyAddr ; 正式进入内核
;***************************************************************
KernelEntryPointPhyAddr的值是0x30400。这个值与内核编译时指定的入口地址一致。
三、kernel
_start: ;内核入口
; 把 esp 从 LOADER 挪到 KERNEL
mov esp, StackTop ; 堆栈在 bss 段中
mov dword [disp_pos], 0
sgdt [gdt_ptr] ; cstart() 中将会用到 gdt_ptr
call cstart ; 1)在此函数中改变了gdt_ptr,让它指向新的GDT; 2)初始化中断、异常处理
lgdt [gdt_ptr] ; 使用新的GDT
lidt [idt_ptr]
jmp SELECTOR_KERNEL_CS:csinit
csinit:
sti
hlt
1)切换堆栈和GDT
PUBLIC void cstart()
{
disp_str("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
"-----\"cstart\" begins-----\n");
/* a)将 LOADER 中的 GDT 复制到新的 GDT 中 */
memcpy(&gdt, /* New GDT */
(void*)(*((u32*)(&gdt_ptr[2]))), /* Base of Old GDT */
*((u16*)(&gdt_ptr[0])) + 1 /* Limit of Old GDT */
);
/* gdt_ptr[6] 共 6 个字节:0~15:Limit 16~47:Base。用作 sgdt/lgdt 的参数。*/
u16* p_gdt_limit = (u16*)(&gdt_ptr[0]);
u32* p_gdt_base = (u32*)(&gdt_ptr[2]);
*p_gdt_limit = GDT_SIZE * sizeof(DESCRIPTOR) - 1;
*p_gdt_base = (u32)&gdt;
/* idt_ptr[6] 共 6 个字节:0~15:Limit 16~47:Base。用作 sidt/lidt 的参数。*/
u16* p_idt_limit = (u16*)(&idt_ptr[0]);
u32* p_idt_base = (u32*)(&idt_ptr[2]);
*p_idt_limit = IDT_SIZE * sizeof(GATE) - 1;
*p_idt_base = (u32)&idt;
2)初始化中断、异常处理
init_prot();
disp_str("-----\"cstart\" ends-----\n");
}
2)初始化中断、异常处理
PUBLIC void init_prot()
{
init_8259A();
// 全部初始化成中断门(没有陷阱门)
init_idt_desc(INT_VECTOR_DIVIDE, DA_386IGate,
divide_error, PRIVILEGE_KRNL);
init_idt_desc(INT_VECTOR_DEBUG, DA_386IGate,
single_step_exception, PRIVILEGE_KRNL);
.
.
.
init_idt_desc(INT_VECTOR_IRQ8 + 6, DA_386IGate,
hwint14, PRIVILEGE_KRNL);
init_idt_desc(INT_VECTOR_IRQ8 + 7, DA_386IGate,
hwint15, PRIVILEGE_KRNL);
}
*----------------------------------------------------------------------*
初始化 386 中断门
*======================================================================*/
PRIVATE void init_idt_desc(unsigned char vector, u8 desc_type,
int_handler handler, unsigned char privilege)
{
GATE * p_gate = &idt[vector];
u32 base = (u32)handler;
p_gate->offset_low = base & 0xFFFF;
p_gate->selector = SELECTOR_KERNEL_CS;
p_gate->dcount = 0;
p_gate->attr = desc_type | (privilege << 5);
p_gate->offset_high = (base >> 16) & 0xFFFF;
}