2020301918-os/kern/start.c
2022-10-18 18:23:01 +08:00

219 lines
6.3 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "type.h"
#include "protect.h"
#include "stdio.h"
#include "assert.h"
#include "trap.h"
#include "x86.h"
struct Pseudodesc gdt_ptr, idt_ptr;
DESCRIPTOR gdt[GDT_SIZE];
DESCRIPTOR ldt[LDT_SIZE];
GATE idt[IDT_SIZE];
TSS tss;
/*
* 当发生不可挽回的错误时就打印错误信息并使CPU核休眠
*/
void
_panic(const char *file, int line, const char *fmt,...)
{
va_list ap;
// 确保CPU核不受外界中断的影响
asm volatile("cli");
asm volatile("cld");
va_start(ap, fmt);
kprintf("\x1b[0m\x1b[91mkernel panic at %s:%d: ", file, line);
vkprintf(fmt, ap);
kprintf("\n\x1b[0m");
va_end(ap);
// 休眠CPU核直接罢工
while(1)
asm volatile("hlt");
}
/*
* 很像panic但是不会休眠CPU核就是正常打印信息
*/
void
_warn(const char *file, int line, const char *fmt,...)
{
va_list ap;
va_start(ap, fmt);
kprintf("\x1b[0m\x1b[93mkernel warning at %s:%d: ", file, line);
vkprintf(fmt, ap);
kprintf("\n\x1b[0m");
va_end(ap);
}
/*
* 再创建一遍gdt表与loader中的gdt表不同的是
* 新增了tss和ldt的两个的全局描述符
*/
static void
init_gdt()
{
init_segment(&gdt[0], 0, 0, 0);
// 代码段(cs)
init_segment(&gdt[1], 0, 0xfffff, DA_CR | DA_32 | DA_LIMIT_4K);
// 数据段(ds,es,fs,ss)
init_segment(&gdt[2], 0, 0xfffff, DA_DRW | DA_32 | DA_LIMIT_4K);
// 显存段(gs)
init_segment(&gdt[3], 0xb8000, 0xffff, DA_DRW | DA_DPL3);
tss.ss0 = SELECTOR_FLAT_RW;
tss.iobase = sizeof(tss); /* 没有I/O许可位图 */
// tss段
init_segment(&gdt[4], (u32)&tss, sizeof(tss) - 1, DA_386TSS);
// ldt段
init_segment(&gdt[5], (u32)ldt, sizeof(ldt) - 1, DA_LDT);
gdt_ptr.pd_base = (u32)gdt;
gdt_ptr.pd_lim = sizeof(gdt) - 1;
}
/*
* 创建ldt表ldt表用于用户态用户进程的段寄存器不能存放内核权限的gdt
* 而是使用用户权限的ldt
*/
static void
init_ldt()
{
init_segment(&ldt[0], 0, 0, 0);
// 代码段(cs)
init_segment(&ldt[1], 0, 0xfffff,
DA_CR | DA_32 | DA_LIMIT_4K | DA_DPL3);
// 数据段(ds,es,fs,ss)
init_segment(&ldt[2], 0, 0xfffff,
DA_DRW | DA_32 | DA_LIMIT_4K | DA_DPL3);
// 用户态的gs继承自gdt的显存段
}
/*
* 创建idt表看起来挺吓人的实际上全是按照手册抄的。
*/
static void
init_idt()
{
// 异常处理
init_gate(idt + INT_VECTOR_DIVIDE, DA_386IGate,
divide_error, PRIVILEGE_KRNL);
init_gate(idt + INT_VECTOR_DEBUG, DA_386IGate,
single_step_exception, PRIVILEGE_KRNL);
init_gate(idt + INT_VECTOR_NMI, DA_386IGate,
nmi, PRIVILEGE_KRNL);
init_gate(idt + INT_VECTOR_BREAKPOINT, DA_386IGate,
breakpoint_exception, PRIVILEGE_USER);
init_gate(idt + INT_VECTOR_OVERFLOW, DA_386IGate,
overflow, PRIVILEGE_USER);
init_gate(idt + INT_VECTOR_BOUNDS, DA_386IGate,
bounds_check, PRIVILEGE_KRNL);
init_gate(idt + INT_VECTOR_INVAL_OP, DA_386IGate,
inval_opcode, PRIVILEGE_KRNL);
init_gate(idt + INT_VECTOR_COPROC_NOT, DA_386IGate,
copr_not_available, PRIVILEGE_KRNL);
init_gate(idt + INT_VECTOR_DOUBLE_FAULT,DA_386IGate,
double_fault, PRIVILEGE_KRNL);
init_gate(idt + INT_VECTOR_COPROC_SEG, DA_386IGate,
copr_seg_overrun, PRIVILEGE_KRNL);
init_gate(idt + INT_VECTOR_INVAL_TSS, DA_386IGate,
inval_tss, PRIVILEGE_KRNL);
init_gate(idt + INT_VECTOR_SEG_NOT, DA_386IGate,
segment_not_present, PRIVILEGE_KRNL);
init_gate(idt + INT_VECTOR_STACK_FAULT, DA_386IGate,
stack_exception, PRIVILEGE_KRNL);
init_gate(idt + INT_VECTOR_PROTECTION, DA_386IGate,
general_protection, PRIVILEGE_KRNL);
init_gate(idt + INT_VECTOR_PAGE_FAULT, DA_386IGate,
page_fault, PRIVILEGE_KRNL);
init_gate(idt + INT_VECTOR_COPROC_ERR, DA_386IGate,
copr_error, PRIVILEGE_KRNL);
// 外设中断
init_gate(idt + INT_VECTOR_IRQ0 + 0, DA_386IGate,
hwint00, PRIVILEGE_KRNL);
init_gate(idt + INT_VECTOR_IRQ0 + 1, DA_386IGate,
hwint01, PRIVILEGE_KRNL);
init_gate(idt + INT_VECTOR_IRQ0 + 2, DA_386IGate,
hwint02, PRIVILEGE_KRNL);
init_gate(idt + INT_VECTOR_IRQ0 + 3, DA_386IGate,
hwint03, PRIVILEGE_KRNL);
init_gate(idt + INT_VECTOR_IRQ0 + 4, DA_386IGate,
hwint04, PRIVILEGE_KRNL);
init_gate(idt + INT_VECTOR_IRQ0 + 5, DA_386IGate,
hwint05, PRIVILEGE_KRNL);
init_gate(idt + INT_VECTOR_IRQ0 + 6, DA_386IGate,
hwint06, PRIVILEGE_KRNL);
init_gate(idt + INT_VECTOR_IRQ0 + 7, DA_386IGate,
hwint07, PRIVILEGE_KRNL);
init_gate(idt + INT_VECTOR_IRQ8 + 0, DA_386IGate,
hwint08, PRIVILEGE_KRNL);
init_gate(idt + INT_VECTOR_IRQ8 + 1, DA_386IGate,
hwint09, PRIVILEGE_KRNL);
init_gate(idt + INT_VECTOR_IRQ8 + 2, DA_386IGate,
hwint10, PRIVILEGE_KRNL);
init_gate(idt + INT_VECTOR_IRQ8 + 3, DA_386IGate,
hwint11, PRIVILEGE_KRNL);
init_gate(idt + INT_VECTOR_IRQ8 + 4, DA_386IGate,
hwint12, PRIVILEGE_KRNL);
init_gate(idt + INT_VECTOR_IRQ8 + 5, DA_386IGate,
hwint13, PRIVILEGE_KRNL);
init_gate(idt + INT_VECTOR_IRQ8 + 6, DA_386IGate,
hwint14, PRIVILEGE_KRNL);
init_gate(idt + INT_VECTOR_IRQ8 + 7, DA_386IGate,
hwint15, PRIVILEGE_KRNL);
idt_ptr.pd_base = (u32)idt;
idt_ptr.pd_lim = sizeof(idt) - 1;
}
/*
* 初始化8259A设置外设中断看起来挺吓人的实际上全是按照手册抄的。
*/
static void
init_8259A()
{
outb(INT_M_CTL, 0x11); // Master 8259, ICW1.
outb(INT_S_CTL, 0x11); // Slave 8259, ICW1.
// Master 8259, ICW2. 设置 '主8259' 的中断入口地址为 0x20.
outb(INT_M_CTLMASK, INT_VECTOR_IRQ0);
// Slave 8259, ICW2. 设置 '从8259' 的中断入口地址为 0x28
outb(INT_S_CTLMASK, INT_VECTOR_IRQ8);
// Master 8259, ICW3. IR2 对应 '从8259'.
outb(INT_M_CTLMASK, 0x4);
// Slave 8259, ICW3. 对应 '主8259' 的 IR2.
outb(INT_S_CTLMASK, 0x2);
outb(INT_M_CTLMASK, 0x1); // Master 8259, ICW4.
outb(INT_S_CTLMASK, 0x1); // Slave 8259, ICW4.
outb(INT_M_CTLMASK, 0xFF); // Master 8259, OCW1.
outb(INT_S_CTLMASK, 0xFF); // Slave 8259, OCW1.
}
/*
* 内核初始化函数
*/
void cstart()
{
// 清屏并将光标置为开头
// ANSI转义序列由\x1b(ascii码27为Esc)开头的序列
// 能够控制光标控制终端linux上也在用这个控制终端
// 目前支持的功能在inc/terminal.c中的kprintf函数有写
// \x1b[2J 清屏
// \x1b[H 将光标放置到左上角
kprintf("\x1b[2J\x1b[H");
kprintf("---loading gdt ");
init_gdt();
kprintf("ldt ");
init_ldt();
kprintf("8259A ");
init_8259A();
kprintf("idt ");
init_idt();
kprintf("---\n");
}