From 71e3d80ed9ad65e591471617a7f3cdd1b78f1373 Mon Sep 17 00:00:00 2001 From: yanguangshaonian Date: Wed, 12 Apr 2023 09:09:36 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=AD=E6=96=AD=E4=B8=8A=E4=B8=8B=E6=96=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/kernel/handle.asm | 36 +++++-- src/kernel/interrupt.c | 28 ++++-- src/kernel/main.c | 22 ++--- src/kernel/printk.c | 4 + src/kernel/schdule.asm | 3 +- src/kernel/start.asm | 2 +- src/kernel/task.c | 22 +++-- src/lib/console.c | 216 ++++++++++++++++++++++++++++++++++++++++- 8 files changed, 294 insertions(+), 39 deletions(-) diff --git a/src/kernel/handle.asm b/src/kernel/handle.asm index 839e107..1cd7c6e 100644 --- a/src/kernel/handle.asm +++ b/src/kernel/handle.asm @@ -8,7 +8,7 @@ ; c语言整理的其实也是下面的 handler_entry_table extern handler_table -; 中断发生时栈的布局为: 低地址(栈顶) %1(中断向量号) gp错误码(如果有) ip cs eflags 低高地址(栈底) +; 中断发生时栈的布局为: 低地址(栈顶) %1(中断向量号) gp错误码(如果有) ip cs eflags 高地址(栈底) section .text ; ## gp错误码 @@ -20,15 +20,19 @@ section .text ; 由于0x80 我们没有中断处理函数, 手动软中断出发之后, 又会立即硬中断所以会又触发0x0d一般性保护异常, 把异常压入栈 ; 在80286中 gp错误码是16个bit, 现在为了兼容 扩充到了32位 -; ## eflag ; sti打开中断这个指令会把 if位 置为1, +; ## eflag ; sti打开中断这个指令会把 if位 置为1, 如果是0表示中断时禁用状态 ; 如果使用sti指令 后压入栈的 eflags就是 0x212, 如果关闭中断则压入栈的eflags就变成了0x012, 0x212或者0x012 这俩数, 他们的bit位的第九位存在差别 -; 此时压入栈的是0x212, 而eflag寄存器则是0x12, 因为我们初始化中断描述符的时候, 设置的是中断门 所以第九位被置为了0保存在了eflag寄存器中 -; 所以, 在中断的时候 eflag中的 if位又被设置成了0(关闭中断状态), 如果我们想在中断处理函数内调度用户任务, 需要打开中断, 让用户任务执行的时候可以触发中断 +; 此时压入栈的是0x212, 而eflag寄存器则是0x12(是为了防止在处理中断的时候又被触发中断), 因为我们初始化中断描述符的时候, 设置的是中断门 所以第九位被置为了0保存在了eflag寄存器中 +; 所以, 在中断的时候 eflag中的 if位又被设置成了0(关闭中断状态), +; 如果我们想在中断处理函数内调度用户任务, 且由中断调用的schdule切换到用户任务执行中又可以被中断 需要打开中断, 让用户任务执行的时候可以触发中断 +; 注意在 schdule函数体内, 中断是关闭状态, 只有tash_switch 切换完之后ret, 再到外面iret完成之后, 恢复到用户任务执行的时候才会回复用户任务eflag状态 +; 用户的任务因为是在中断内进行的调度, 而进入中断 if标志位又被置为0, 所以由中断函数进入到当前任务中时, 需要把任务的eflag置为1(打开中断) +; 这样中断在调用完中断处理函数之后, 会pop保存的寄存器状态, 我们这个函数一进来就把 if标志位置 设置为1了, 也就跟着恢复了 ; 时钟中断时 0x20 这个也没有错误码 %macro INTERRUPT_HANDLER 2 interrupt_handler_%1: - xchg bx, bx ; 这时候观察栈, 如果有状态码 + ; xchg bx, bx ; 这时候观察栈, 如果有状态码 %ifn %2 push 0x20222202 ; 这个是给没有错误码的调用添加一个自己定义的魔数 %endif @@ -38,11 +42,29 @@ interrupt_handler_%1: interrupt_entry: - mov eax, [esp] + ; 进入中断, 保存寄存器上下文信息, 一共保存了 12个 + push ds + push es + push fs + push gs + pusha ; 8个寄存器 + + mov eax, [esp + 12 * 4] ; 找到%1 压入的中断向量 + + ; 调用中断处理函数 handler_table 中存储了中断处理函数的指针, 并把 上方push %1 中断号传入中断处理函数的指针(所以这里 *4) + push eax call [handler_table + eax * 4] - ; 弹栈, 弹出 %1, 弹出0x20222202, 后面调用iret + add esp, 4 ; 弹出调用中断处理函数之前push 的 eax + + ; 弹出12个寄存器 + popa + pop gs + pop fs + pop es + pop ds + ; 弹栈, 弹出 宏里面的生成的 %1, 弹出0x20222202, 后面调用iret add esp, 8 iret diff --git a/src/kernel/interrupt.c b/src/kernel/interrupt.c index 7f700cb..0da9681 100644 --- a/src/kernel/interrupt.c +++ b/src/kernel/interrupt.c @@ -13,15 +13,33 @@ u8* handler_table[IDT_SIZE]; // 为了让我们从c语言加载 idt表, 中断时 会先进入这个指针所在函数内, 然后继续跳入到 handler_table中 extern handler_entry_table[ENTRY_SIZE]; +// 导入调度函数 +extern void schdule(); + // 异常 中断处理函数默认函数 -void exception_handle(u8 handle_num){ +void exception_handle(u8 handle_num, + u32 edi, u32 esi, u32 ebp, u32 esp, + u32 ebx, u32 edx, u32 ecx, u32 eax, + u32 gs, u32 fs, u32 es, u32 ds, + u32 vector0, u32 error, u32 eip, u32 cs, u32 eflags){ char *msg; if (handle_num < 22) { msg = messages[handle_num]; } else{ msg = messages[15]; } + printk("Exception as [0x%02X] %s\n", handle_num, msg); + + // 打印异常发生时, 一些寄存器的状态 + printk("\nEXCEPTION : %s \n", msg); + printk(" VECTOR : 0x%02X\n", handle_num); + printk(" ERROR : 0x%08X\n", error); + printk(" EFLAGS : 0x%08X\n", eflags); + printk(" CS : 0x%02X\n", cs); + printk(" EIP : 0x%08X\n", eip); + printk(" ESP : 0x%08X\n", esp); + hang(); } @@ -39,13 +57,11 @@ void send_eoi(u8 handle_num) } } -u32 counter = 0; // 外中断 中断处理函数默认函数 -void default_handler(u8 handle_num) -{ +void default_handler(u8 handle_num){ send_eoi(handle_num); - counter += 1; - DEBUGK("[%x] default interrupt called... %d", handle_num, counter); + // 外中断发生就立即调度 + schdule(); } diff --git a/src/kernel/main.c b/src/kernel/main.c index b5bfaf8..3609b98 100644 --- a/src/kernel/main.c +++ b/src/kernel/main.c @@ -10,25 +10,19 @@ void kernel_init(){ // 初始化全局描述符 gdt_init(); - - // 初始化任务 - // task_init(); - + // 初始化中断 interrupt_init(); - asm volatile( - "sti\n" // 打开cpu的中断中断 - "movl %eax, %eax\n" - ); - - // u32 counter = 0; - // while(true){ - // counter += 1; - // DEBUGK("looping in kernel init %d", counter); - // delay(10000000); + // int cnt = 0; + // while (true){ + // cnt += 1; + // printk("%d", 123); // } + // 初始化任务 + task_init(); + } diff --git a/src/kernel/printk.c b/src/kernel/printk.c index 6d5503f..fbbd3de 100644 --- a/src/kernel/printk.c +++ b/src/kernel/printk.c @@ -6,6 +6,7 @@ static u8* buf[1024]; u32 printk(const u8 *fmt, ...){ + va_list args; u32 i; @@ -13,6 +14,9 @@ u32 printk(const u8 *fmt, ...){ i = vsprintf(buf, fmt, args); va_end(args); + + asm volatile("cli\n"); console_write(buf, i); + asm volatile("sti\n"); return i; } \ No newline at end of file diff --git a/src/kernel/schdule.asm b/src/kernel/schdule.asm index d6b020b..b674667 100644 --- a/src/kernel/schdule.asm +++ b/src/kernel/schdule.asm @@ -25,10 +25,11 @@ task_switch: ; next的 的栈顶指针 mov esp, [eax] + ; 在任务第一次进入到这里的时候会把我们设置的寄存器的默认值 pop 到指定寄存器中 pop edi pop esi pop ebx pop ebp - ; 此时 栈顶是 call task_switch 的下一行代码的位置, ret即可 + ; 此时 栈顶是 call task_switch 的下一行代码的位置, ret即可 ret diff --git a/src/kernel/start.asm b/src/kernel/start.asm index c5797f0..7bf7996 100644 --- a/src/kernel/start.asm +++ b/src/kernel/start.asm @@ -9,7 +9,7 @@ global _start _start: call kernel_init - ; int 0x0d + ; int 0x08 ; mov bx, 0 ; div bx diff --git a/src/kernel/task.c b/src/kernel/task.c index 1ff26d5..b10535a 100644 --- a/src/kernel/task.c +++ b/src/kernel/task.c @@ -1,4 +1,5 @@ #include +#include #include extern void task_switch(task_t* next); @@ -33,7 +34,7 @@ task_t* running_task(){ } -void schdule(){ +void _ofp schdule(){ task_t* current = running_task(); // 这里实验的, 如果当前是任务a 就切换到任务b, 如果是任务b 就切换到任务a task_t* next; @@ -46,22 +47,26 @@ void schdule(){ } -void thread_a(){ +void _ofp thread_a(){ + asm volatile("sti\n"); + int c = 0; while(true){ - printk("A"); - schdule(); + printk("A~"); } } -void thread_b(){ +void _ofp thread_b(){ + // 因为是在中断内进行的调度, 而进入中断 if标志位又被置为0, 所以由中断函数进入到当前任务中时, 需要把任务的eflag置为1(打开中断) + // , 这样中断在调用完中断处理函数之后, 会pop保存的寄存器状态, 我们这个函数一进来就把 if标志位置 设置为1了, 也就跟着恢复了 + asm volatile("sti\n"); + int c = 0; while(true){ - printk("B"); - schdule(); + printk("B~"); } } void task_init(){ - // test(); + test(); } @@ -70,7 +75,6 @@ void test(){ task_create(task_a_stack, thread_a); task_create(task_b_stack, thread_b); schdule(); - } /* diff --git a/src/lib/console.c b/src/lib/console.c index eafefee..183dbf9 100644 --- a/src/lib/console.c +++ b/src/lib/console.c @@ -323,4 +323,218 @@ test_console(){ // console_write(message2, strlen(message2)); // console_write(message2, strlen(message2)); // console_write(message2, strlen(message2)); -} \ No newline at end of file +} + + + +// #include +// #include +// #include + +// #define CRT_ADDR_REG 0x3D4 // CRT(6845)索引寄存器 +// #define CRT_DATA_REG 0x3D5 // CRT(6845)数据寄存器 + +// #define CRT_START_ADDR_H 0xC // 显示内存起始位置 - 高位 +// #define CRT_START_ADDR_L 0xD // 显示内存起始位置 - 低位 +// #define CRT_CURSOR_H 0xE // 光标位置 - 高位 +// #define CRT_CURSOR_L 0xF // 光标位置 - 低位 + +// #define MEM_BASE 0xB8000 // 显卡内存起始位置 +// #define MEM_SIZE 0x4000 // 显卡内存大小 +// #define MEM_END (MEM_BASE + MEM_SIZE) // 显卡内存结束位置 +// #define WIDTH 80 // 屏幕文本列数 +// #define HEIGHT 25 // 屏幕文本行数 +// #define ROW_SIZE (WIDTH * 2) // 每行字节数 +// #define SCR_SIZE (ROW_SIZE * HEIGHT) // 屏幕字节数 + +// #define ASCII_NUL 0x00 +// #define ASCII_ENQ 0x05 +// #define ASCII_BEL 0x07 // \a +// #define ASCII_BS 0x08 // \b +// #define ASCII_HT 0x09 // \t +// #define ASCII_LF 0x0A // \n +// #define ASCII_VT 0x0B // \v +// #define ASCII_FF 0x0C // \f +// #define ASCII_CR 0x0D // \r +// #define ASCII_DEL 0x7F + +// static u32 screen; // 当前显示器开始的内存位置 + +// static u32 pos; // 记录当前光标的内存位置 + +// static u32 x, y; // 当前光标的坐标 + +// static u8 attr = 7; // 字符样式 +// static u16 erase = 0x0720; // 空格 + +// // 获得当前显示器的开始位置 +// static void get_screen() +// { +// out_8(CRT_ADDR_REG, CRT_START_ADDR_H); // 开始位置高地址 +// screen = in_8(CRT_DATA_REG) << 8; // 开始位置高八位 +// out_8(CRT_ADDR_REG, CRT_START_ADDR_L); +// screen |= in_8(CRT_DATA_REG); + +// screen <<= 1; // screen *= 2 +// screen += MEM_BASE; +// } + +// // 设置当前显示器开始的位置 +// static void set_screen() +// { +// out_8(CRT_ADDR_REG, CRT_START_ADDR_H); // 开始位置高地址 +// out_8(CRT_DATA_REG, ((screen - MEM_BASE) >> 9) & 0xff); +// out_8(CRT_ADDR_REG, CRT_START_ADDR_L); +// out_8(CRT_DATA_REG, ((screen - MEM_BASE) >> 1) & 0xff); +// } + +// // 获得当前光标位置 +// static void get_cursor() +// { +// out_8(CRT_ADDR_REG, CRT_CURSOR_H); // 高地址 +// pos = in_8(CRT_DATA_REG) << 8; // 高八位 +// out_8(CRT_ADDR_REG, CRT_CURSOR_L); +// pos |= in_8(CRT_DATA_REG); + +// get_screen(); + +// pos <<= 1; // pos *= 2 +// pos += MEM_BASE; + +// u32 delta = (pos - screen) >> 1; +// x = delta % WIDTH; +// y = delta / WIDTH; +// } + +// static void set_cursor() +// { +// out_8(CRT_ADDR_REG, CRT_CURSOR_H); // 光标高地址 +// out_8(CRT_DATA_REG, ((pos - MEM_BASE) >> 9) & 0xff); +// out_8(CRT_ADDR_REG, CRT_CURSOR_L); +// out_8(CRT_DATA_REG, ((pos - MEM_BASE) >> 1) & 0xff); +// } + +// console_clear() +// { +// screen = MEM_BASE; +// pos = MEM_BASE; +// x = y = 0; +// set_cursor(); +// set_screen(); + +// u16 *ptr = (u16 *)MEM_BASE; +// while (ptr < (u16 *)MEM_END) +// { +// *ptr++ = erase; +// } +// } + +// // 向上滚屏 +// static void scroll_up() +// { +// if (screen + SCR_SIZE + ROW_SIZE >= MEM_END) +// { +// memcpy((void *)MEM_BASE, (void *)screen, SCR_SIZE); +// pos -= (screen - MEM_BASE); +// screen = MEM_BASE; +// } + +// u32 *ptr = (u32 *)(screen + SCR_SIZE); +// for (usize i = 0; i < WIDTH; i++) +// { +// *ptr++ = erase; +// } +// screen += ROW_SIZE; +// pos += ROW_SIZE; +// set_screen(); +// } + +// static void command_lf() +// { +// if (y + 1 < HEIGHT) +// { +// y++; +// pos += ROW_SIZE; +// return; +// } +// scroll_up(); +// } + +// static void command_cr() +// { +// pos -= (x << 1); +// x = 0; +// } + +// static void command_bs() +// { +// if (x) +// { +// x--; +// pos -= 2; +// *(u16 *)pos = erase; +// } +// } + +// static void command_del() +// { +// *(u16 *)pos = erase; +// } + +// console_write(u8 *buf, u32 count) +// { +// char ch; +// while (count--) +// { +// ch = *buf++; +// switch (ch) +// { +// case ASCII_NUL: +// break; +// case ASCII_BEL: +// // todo \a +// break; +// case ASCII_BS: +// command_bs(); +// break; +// case ASCII_HT: +// break; +// case ASCII_LF: +// command_lf(); +// command_cr(); +// break; +// case ASCII_VT: +// break; +// case ASCII_FF: +// command_lf(); +// break; +// case ASCII_CR: +// command_cr(); +// break; +// case ASCII_DEL: +// command_del(); +// break; +// default: +// if (x >= WIDTH) +// { +// x -= WIDTH; +// pos -= ROW_SIZE; +// command_lf(); +// } + +// *((char *)pos) = ch; +// pos++; +// *((char *)pos) = attr; +// pos++; + +// x++; +// break; +// } +// } +// set_cursor(); +// } + +// console_init() +// { +// console_clear(); +// } \ No newline at end of file