219 lines
6.3 KiB
C
219 lines
6.3 KiB
C
#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");
|
||
}
|