303 lines
10 KiB
C
303 lines
10 KiB
C
#include <defs.h>
|
||
#include <mmu.h>
|
||
#include <memlayout.h>
|
||
#include <clock.h>
|
||
#include <trap.h>
|
||
#include <x86.h>
|
||
#include <stdio.h>
|
||
#include <assert.h>
|
||
#include <console.h>
|
||
#include <kdebug.h>
|
||
#include <string.h>
|
||
#define TICK_NUM 1
|
||
|
||
static void print_ticks() {
|
||
cprintf("%d ticks\n",TICK_NUM);
|
||
#ifdef DEBUG_GRADE
|
||
cprintf("End of Test.\n");
|
||
panic("EOT: kernel seems ok.");
|
||
#endif
|
||
}
|
||
|
||
/* *
|
||
* Interrupt descriptor table:
|
||
*
|
||
* Must be built at run time because shifted function addresses can't
|
||
* be represented in relocation records.
|
||
* */
|
||
static struct gatedesc idt[256] = {{0}};
|
||
|
||
static struct pseudodesc idt_pd = {
|
||
sizeof(idt) - 1, (uintptr_t)idt
|
||
};
|
||
|
||
/* idt_init - initialize IDT to each of the entry points in kern/trap/vectors.S */
|
||
void
|
||
idt_init(void) {
|
||
/* LAB1 YOUR CODE : STEP 2 */
|
||
/* (1) Where are the entry addrs of each Interrupt Service Routine (ISR)?
|
||
* All ISR's entry addrs are stored in __vectors. where is uintptr_t __vectors[] ?
|
||
* __vectors[] is in kern/trap/vector.S which is produced by tools/vector.c
|
||
* (try "make" command in lab1, then you will find vector.S in kern/trap DIR)
|
||
* You can use "extern uintptr_t __vectors[];" to define this extern variable which will be used later.
|
||
* (2) Now you should setup the entries of ISR in Interrupt Description Table (IDT).
|
||
* Can you see idt[256] in this file? Yes, it's IDT! you can use SETGATE macro to setup each item of IDT
|
||
* (3) After setup the contents of IDT, you will let CPU know where is the IDT by using 'lidt' instruction.
|
||
* You don't know the meaning of this instruction? just google it! and check the libs/x86.h to know more.
|
||
* Notice: the argument of lidt is idt_pd. try to find it!
|
||
*/
|
||
extern uintptr_t __vectors[];
|
||
int i;
|
||
for (i = 0; i < sizeof(idt) / sizeof(struct gatedesc); i ++) {
|
||
SETGATE(idt[i], 0, GD_KTEXT, __vectors[i], DPL_KERNEL);
|
||
}
|
||
// set for switch from user to kernel
|
||
SETGATE(idt[T_SWITCH_TOK], 0, GD_KTEXT, __vectors[T_SWITCH_TOK], DPL_USER);
|
||
// load the IDT
|
||
lidt(&idt_pd);
|
||
}
|
||
|
||
static const char *
|
||
trapname(int trapno) {
|
||
static const char * const excnames[] = {
|
||
"Divide error",
|
||
"Debug",
|
||
"Non-Maskable Interrupt",
|
||
"Breakpoint",
|
||
"Overflow",
|
||
"BOUND Range Exceeded",
|
||
"Invalid Opcode",
|
||
"Device Not Available",
|
||
"Double Fault",
|
||
"Coprocessor Segment Overrun",
|
||
"Invalid TSS",
|
||
"Segment Not Present",
|
||
"Stack Fault",
|
||
"General Protection",
|
||
"Page Fault",
|
||
"(unknown trap)",
|
||
"x87 FPU Floating-Point Error",
|
||
"Alignment Check",
|
||
"Machine-Check",
|
||
"SIMD Floating-Point Exception"
|
||
};
|
||
|
||
if (trapno < sizeof(excnames)/sizeof(const char * const)) {
|
||
return excnames[trapno];
|
||
}
|
||
if (trapno >= IRQ_OFFSET && trapno < IRQ_OFFSET + 16) {
|
||
return "Hardware Interrupt";
|
||
}
|
||
return "(unknown trap)";
|
||
}
|
||
|
||
/* trap_in_kernel - test if trap happened in kernel */
|
||
bool
|
||
trap_in_kernel(struct trapframe *tf) {
|
||
return (tf->tf_cs == (uint16_t)KERNEL_CS);
|
||
}
|
||
|
||
static const char *IA32flags[] = {
|
||
"CF", NULL, "PF", NULL, "AF", NULL, "ZF", "SF",
|
||
"TF", "IF", "DF", "OF", NULL, NULL, "NT", NULL,
|
||
"RF", "VM", "AC", "VIF", "VIP", "ID", NULL, NULL,
|
||
};
|
||
|
||
void
|
||
print_trapframe(struct trapframe *tf) {
|
||
cprintf("trapframe at %p\n", tf);
|
||
print_regs(&tf->tf_regs);
|
||
cprintf(" ds 0x----%04x\n", tf->tf_ds);
|
||
cprintf(" es 0x----%04x\n", tf->tf_es);
|
||
cprintf(" fs 0x----%04x\n", tf->tf_fs);
|
||
cprintf(" gs 0x----%04x\n", tf->tf_gs);
|
||
cprintf(" trap 0x%08x %s\n", tf->tf_trapno, trapname(tf->tf_trapno));
|
||
cprintf(" err 0x%08x\n", tf->tf_err);
|
||
cprintf(" eip 0x%08x\n", tf->tf_eip);
|
||
cprintf(" cs 0x----%04x\n", tf->tf_cs);
|
||
cprintf(" flag 0x%08x ", tf->tf_eflags);
|
||
|
||
int i, j;
|
||
for (i = 0, j = 1; i < sizeof(IA32flags) / sizeof(IA32flags[0]); i ++, j <<= 1) {
|
||
if ((tf->tf_eflags & j) && IA32flags[i] != NULL) {
|
||
cprintf("%s,", IA32flags[i]);
|
||
}
|
||
}
|
||
cprintf("IOPL=%d\n", (tf->tf_eflags & FL_IOPL_MASK) >> 12);
|
||
|
||
if (!trap_in_kernel(tf)) {
|
||
cprintf(" esp 0x%08x\n", tf->tf_esp);
|
||
cprintf(" ss 0x----%04x\n", tf->tf_ss);
|
||
}
|
||
}
|
||
|
||
void
|
||
print_regs(struct pushregs *regs) {
|
||
cprintf(" edi 0x%08x\n", regs->reg_edi);
|
||
cprintf(" esi 0x%08x\n", regs->reg_esi);
|
||
cprintf(" ebp 0x%08x\n", regs->reg_ebp);
|
||
cprintf(" oesp 0x%08x\n", regs->reg_oesp);
|
||
cprintf(" ebx 0x%08x\n", regs->reg_ebx);
|
||
cprintf(" edx 0x%08x\n", regs->reg_edx);
|
||
cprintf(" ecx 0x%08x\n", regs->reg_ecx);
|
||
cprintf(" eax 0x%08x\n", regs->reg_eax);
|
||
}
|
||
|
||
/* temporary trapframe or pointer to trapframe */
|
||
struct trapframe switchk2u, *switchu2k;
|
||
|
||
/* trap_dispatch - dispatch based on what type of trap occurred */
|
||
static void
|
||
trap_dispatch(struct trapframe *tf) {
|
||
volatile char c;
|
||
// 有限状态机?
|
||
static enum {STARTED=0, STOPPED=1, WAITING_FOR_INPUT, READY_TO_OUTPUT} state = STOPPED;
|
||
// 唉不能long long,看来很有可能会溢出啊
|
||
static long milliseconds = 0;
|
||
static enum {COUNTDOWN=-1, COUNTUP=1} mode = COUNTUP;
|
||
|
||
switch (tf->tf_trapno) {
|
||
case IRQ_OFFSET + IRQ_TIMER:
|
||
/* LAB1 YOUR CODE : STEP 3 */
|
||
/* handle the timer interrupt */
|
||
/* (1) After a timer interrupt, you should record this event using a global variable (increase it), such as ticks in kern/driver/clock.c
|
||
* (2) Every TICK_NUM cycle, you can print some info using a funciton, such as print_ticks().
|
||
* (3) Too Simple? Yes, I think so!
|
||
*/
|
||
switch (state) {
|
||
case STARTED:
|
||
if (ticks % TICK_NUM == 0) {
|
||
// 10毫秒触发一次时钟中断,TICK_NUM次中断触发一次更新,所以毫秒数应该乘TICK_NUM * 10
|
||
milliseconds += mode * TICK_NUM * 10;
|
||
// 当时间倒计时到0时转成停止状态
|
||
// 而正计时加了mode之后至少为1所以不会转换状态
|
||
// 使用小于等于0而不是等于0是为了防止万一出现异常导致小于0的情况
|
||
state = READY_TO_OUTPUT * (milliseconds <= 0); // 为了减少分支才写成这样
|
||
// print_ticks();
|
||
cprintf("\r%d.%03d\t", milliseconds / 1000, milliseconds % 1000);
|
||
}
|
||
break;
|
||
case READY_TO_OUTPUT:
|
||
state = STOPPED;
|
||
mode = COUNTUP;
|
||
if (milliseconds < 0) milliseconds = 0; // 防止出现异常小于0
|
||
cprintf(MSG_COUNTDOWN_STOP);
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
ticks ++;
|
||
break;
|
||
case IRQ_OFFSET + IRQ_COM1:
|
||
c = cons_getc();
|
||
cprintf("serial [%03d] %c\n", c, c);
|
||
break;
|
||
case IRQ_OFFSET + IRQ_KBD:
|
||
c = cons_getc();
|
||
if (state == WAITING_FOR_INPUT) {
|
||
long seconds = milliseconds / 1000;
|
||
// if (c != '\0') cprintf("%c", c);
|
||
// 如果是正在输入状态,此时不能键入字母或其他各种字符
|
||
// 在这里不能直接break不然就直接跳出中断处理程序了
|
||
// 所以用c=0来防止执行后面的转换状态
|
||
if (c == '\b' || c == '\n' || ('0' <= c && c <= '9')) {
|
||
cprintf("%c", c);
|
||
} else {
|
||
c = '\0';
|
||
}
|
||
if (c == '\b') {
|
||
seconds /= 10;
|
||
}
|
||
if ('0' <= c && c <= '9') {
|
||
seconds *= 10;
|
||
seconds += c - '0';
|
||
}
|
||
milliseconds = seconds * 1000;
|
||
if (c == '\n') {
|
||
state = STARTED;
|
||
}
|
||
c = '\0'; // 如果是正在输入状态,此时不能根据字母转换状态
|
||
}
|
||
switch (c) {
|
||
case 'a':
|
||
mode = COUNTUP;
|
||
cprintf(MSG_COUNTUP_START);
|
||
break;
|
||
case 'b':
|
||
mode = COUNTDOWN;
|
||
cprintf(MSG_COUNTDOWN_START);
|
||
state = WAITING_FOR_INPUT;
|
||
milliseconds = 0;
|
||
break;
|
||
case 's':
|
||
case 'c':
|
||
state = STARTED;
|
||
cprintf(MSG_START);
|
||
break;
|
||
case 'p':
|
||
state = STOPPED;
|
||
cprintf(MSG_PAUSE, milliseconds / 1000, milliseconds % 1000);
|
||
break;
|
||
case 'e':
|
||
state = STOPPED;
|
||
mode = COUNTUP;
|
||
cprintf(MSG_STOP, milliseconds / 1000, milliseconds % 1000);
|
||
milliseconds = 0;
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
// cprintf("kbd [%03d] %c\n", c, c);
|
||
break;
|
||
//LAB1 CHALLENGE 1 : YOUR CODE you should modify below codes.
|
||
case T_SWITCH_TOU:
|
||
if (tf->tf_cs != USER_CS) {
|
||
switchk2u = *tf;
|
||
switchk2u.tf_cs = USER_CS;
|
||
switchk2u.tf_ds = switchk2u.tf_es = switchk2u.tf_ss = USER_DS;
|
||
switchk2u.tf_esp = (uint32_t)tf + sizeof(struct trapframe) - 8;
|
||
|
||
// set eflags, make sure ucore can use io under user mode.
|
||
// if CPL > IOPL, then cpu will generate a general protection.
|
||
switchk2u.tf_eflags |= FL_IOPL_MASK;
|
||
|
||
// set temporary stack
|
||
// then iret will jump to the right stack
|
||
*((uint32_t *)tf - 1) = (uint32_t)&switchk2u;
|
||
}
|
||
break;
|
||
case T_SWITCH_TOK:
|
||
if (tf->tf_cs != KERNEL_CS) {
|
||
tf->tf_cs = KERNEL_CS;
|
||
tf->tf_ds = tf->tf_es = KERNEL_DS;
|
||
tf->tf_eflags &= ~FL_IOPL_MASK;
|
||
switchu2k = (struct trapframe *)(tf->tf_esp - (sizeof(struct trapframe) - 8));
|
||
memmove(switchu2k, tf, sizeof(struct trapframe) - 8);
|
||
*((uint32_t *)tf - 1) = (uint32_t)switchu2k;
|
||
}
|
||
break;
|
||
case IRQ_OFFSET + IRQ_IDE1:
|
||
case IRQ_OFFSET + IRQ_IDE2:
|
||
/* do nothing */
|
||
break;
|
||
default:
|
||
// in kernel, it must be a mistake
|
||
if ((tf->tf_cs & 3) == 0) {
|
||
print_trapframe(tf);
|
||
panic("unexpected trap in kernel.\n");
|
||
}
|
||
}
|
||
}
|
||
|
||
/* *
|
||
* trap - handles or dispatches an exception/interrupt. if and when trap() returns,
|
||
* the code in kern/trap/trapentry.S restores the old CPU state saved in the
|
||
* trapframe and then uses the iret instruction to return from the exception.
|
||
* */
|
||
void
|
||
trap(struct trapframe *tf) {
|
||
// dispatch based on what type of trap occurred
|
||
trap_dispatch(tf);
|
||
}
|
||
|