exercise 13:
在inc/trap.h 给出了几种 hardware IRQ numbers
#define IRQ_OFFSET 32 // IRQ 0 corresponds to int IRQ_OFFSET // Hardware IRQ numbers. We receive these as (IRQ_OFFSET+IRQ_WHATEVER) #define IRQ_TIMER 0 #define IRQ_KBD 1 #define IRQ_SERIAL 4 #define IRQ_SPURIOUS 7 #define IRQ_IDE 14 #define IRQ_ERROR 19
在idt中的偏移量由 idt[IRQ_OFFSET+IRQNumber]来决定
在inc/mmu.h中定义了SETGATE:
// Set up a normal interrupt/trap gate descriptor. // - istrap: 1 for a trap (= exception) gate, 0 for an interrupt gate. // see section 9.6.1.3 of the i386 reference: "The difference between // an interrupt gate and a trap gate is in the effect on IF (the // interrupt-enable flag). An interrupt that vectors through an // interrupt gate resets IF, thereby preventing other interrupts from // interfering with the current interrupt handler. A subsequent IRET // instruction restores IF to the value in the EFLAGS image on the // stack. An interrupt through a trap gate does not change IF." // - sel: Code segment selector for interrupt/trap handler // - off: Offset in code segment for interrupt/trap handler // - dpl: Descriptor Privilege Level - // the privilege level required for software to invoke // this interrupt/trap gate explicitly using an int instruction. #define SETGATE(gate, istrap, sel, off, dpl) { (gate).gd_off_15_0 = (uint32_t) (off) & 0xffff; (gate).gd_sel = (sel); (gate).gd_args = 0; (gate).gd_rsv1 = 0; (gate).gd_type = (istrap) ? STS_TG32 : STS_IG32; (gate).gd_s = 0; (gate).gd_dpl = (dpl); (gate).gd_p = 1; (gate).gd_off_31_16 = (uint32_t) (off) >> 16; }
注册外部中断与lab3中注册trap是一样的。
在kern/traentry.S中添加 entry points
TRAPHANDLER_NOEC(irq_error_handler, IRQ_OFFSET+IRQ_ERROR); TRAPHANDLER_NOEC(irq_ide_handler, IRQ_OFFSET+IRQ_IDE); TRAPHANDLER_NOEC(irq_kbd_handler, IRQ_OFFSET+IRQ_KBD); TRAPHANDLER_NOEC(irq_serial_handler, IRQ_OFFSET+IRQ_SERIAL); TRAPHANDLER_NOEC(irq_spurious_handler, IRQ_OFFSET+IRQ_SPURIOUS); TRAPHANDLER_NOEC(irq_timer_handler, IRQ_OFFSET+IRQ_TIMER);
在inc/trap.h中添加函数定义:
void irq_error_handler(); void irq_kbd_handler(); void irq_ide_handler(); void irq_timer_handler(); void irq_spurious_handler(); void irq_serial_handler();
在kern/trap.c中添加以下代码:
//在inc/trap.h中给出了Hardware IRQ numbers. //外部设备中断在内核中禁用的,在用户态下启用,因此dpl应该是3 SETGATE(idt[IRQ_OFFSET + IRQ_ERROR], 0, GD_KT, irq_error_handler, 3); SETGATE(idt[IRQ_OFFSET + IRQ_IDE], 0, GD_KT, irq_ide_handler, 3); SETGATE(idt[IRQ_OFFSET + IRQ_KBD], 0, GD_KT, irq_kbd_handler, 3); SETGATE(idt[IRQ_OFFSET + IRQ_SERIAL], 0, GD_KT, irq_serial_handler, 3); SETGATE(idt[IRQ_OFFSET + IRQ_SPURIOUS], 0, GD_KT, irq_spurious_handler, 3); SETGATE(idt[IRQ_OFFSET + IRQ_TIMER], 0, GD_KT, irq_timer_handler, 3);
修改kern/env.c中的env_alloc()确保用户进程在开中断状态下运行.
外部中断由eflags寄存器的FL_IF标志位控制(inc/mmu.h),当该位被设置时,打开外部中断。
//在文件inc/mmu.h中Eflags registers #define FL_IF 0x00000200 // Interrupt Flag
因此我们要设置这个标志位:
//在kern/env.c // Enable interrupts while in user mode. // LAB 4: Your code here. e->env_tf.tf_eflags |= FL_IF;
取消对sched_halt()中sti指令的注释.这样空闲的cpu也不会屏蔽中断
// Reset stack pointer, enable interrupts and then halt. asm volatile ( "movl $0, %%ebp " "movl %0, %%esp " "pushl $0 " "pushl $0 " // Uncomment the following line after completing exercise 13 "sti " "1: " "hlt " "jmp 1b " : : "a" (thiscpu->cpu_ts.ts_esp0));
在终端运行一个测试程序:
make run-spin
得到结果:
SMP: CPU 0 found 1 CPU(s) enabled interrupts: 1 2 [00000000] new env 00001000 I am the parent. Forking the child... [00001000] new env 00001001 I am the parent. Running the child... I am the child. Spinning... I am the parent. Killing the child... [00001000] destroying 00001001 [00001000] free env 00001001 [00001000] exiting gracefully [00001000] free env 00001000 No runnable environments in the system!
exercise 13 完成
Exercise 14:
首先看看 lapic_eoi,这个函数位于kern/lpic.c中
// Acknowledge interrupt. void lapic_eoi(void) { if (lapic) lapicw(EOI, 0); }
修改trap_dispatch(在文件kern/trap.c中):
else if(tf->tf_trapno == IRQ_OFFSET + IRQ_TIMER){ lapic_eoi(); sched_yield(); return; }
再次运行spin应该出现以下结果
SMP: CPU 0 found 1 CPU(s) enabled interrupts: 1 2 [00000000] new env 00001000 I am the parent. Forking the child... [00001000] new env 00001001 I am the parent. Running the child... I am the child. Spinning... I am the parent. Killing the child... [00001000] destroying 00001001 [00001000] free env 00001001 [00001000] exiting gracefully [00001000] free env 00001000 No runnable environments in the system!
exercise 15:
----在kern / syscall.c中实现sys_ipc_recv和sys_ipc_try_send。调用envid2env时,应将checkperm标志设置为0,这意味着允许任何进程将IPC消息发送到任何其他进程,并且除了验证目标envid合法之外,内核不执行任何特殊权限检查。
static int
sys_ipc_try_send(envid_t envid, uint32_t value, void *srcva, unsigned perm)
{
int r;
pte_t *pte;
struct PageInfo *pp;
struct Env *env;
if ((r = envid2env(envid, &env, 0)) < 0)
return -E_BAD_ENV;
if (env->env_ipc_recving != true || env->env_ipc_from != 0)
return -E_IPC_NOT_RECV;
if (srcva < (void *)UTOP && PGOFF(srcva))
return -E_INVAL;
if (srcva < (void *)UTOP) {
if ((perm & PTE_P) == 0 || (perm & PTE_U) == 0)
return -E_INVAL;
if ((perm & ~(PTE_P | PTE_U | PTE_W | PTE_AVAIL)) != 0)
return -E_INVAL;
}
if (srcva < (void *)UTOP && (pp = page_lookup(curenv->env_pgdir, srcva, &pte)) == NULL)
return -E_INVAL;
if (srcva < (void *)UTOP && (perm & PTE_W) != 0 && (*pte & PTE_W) == 0)
return -E_INVAL;
if (srcva < (void *)UTOP && env->env_ipc_dstva != 0) {
if ((r = page_insert(env->env_pgdir, pp, env->env_ipc_dstva, perm)) < 0)
return -E_NO_MEM;
env->env_ipc_perm = perm;
}
env->env_ipc_from = curenv->env_id;
env->env_ipc_recving = false;
env->env_ipc_value = value;
env->env_status = ENV_RUNNABLE;
env->env_tf.tf_regs.reg_eax = 0;
return 0;
}
static int sys_ipc_recv(void* dstva) { // LAB 4: Your code here. //-E_INVAL if dstva < UTOP but dstva is not page-aligned. if ((uintptr_t)dstva < UTOP) { if ((uintptr_t)dstva % PGSIZE) return -E_INVAL; } curenv->env_ipc_dstva = dstva; curenv->env_ipc_from = 0; //证明现在还没有收到任何信息 curenv->env_ipc_recving = 1; //1-block, 0-unblock // mark yourself not runnable, and then give up the CPU. curenv->env_status = ENV_NOT_RUNNABLE; //不需要自己unblock,sys_ipc_try_send成功会把这设0 sched_yield(); //panic("sys_ipc_recv not implemented"); //return 0; }
----在lib / ipc.c中实现ipc_recv和ipc_send函数。
void ipc_send(envid_t to_env, uint32_t val, void *pg, int perm) { // LAB 4: Your code here. int r; while(1){ if(pg) r=sys_ipc_try_send(to_env, val, pg, perm); else r=sys_ipc_try_send(to_env, val, (void *)UTOP, perm); if(r!=0){ if(r!=-E_IPC_NOT_RECV) panic("ipc send fault:%e",r); else sys_yield(); }else return; } panic("ipc_send not implemented"); }
int32_t ipc_recv(envid_t *from_env_store, void *pg, int *perm_store) { // LAB 4: Your code here. //// If 'pg' is nonnull, then any page sent by the sender will be mapped at // that address. int r; if (pg != NULL) r = sys_ipc_recv(pg); else//????? r = sys_ipc_recv((void*)UTOP); // If 'from_env_store' is nonnull, then store the IPC sender's envid in // *from_env_store. if (from_env_store != NULL) *from_env_store = thisenv->env_ipc_from; // If 'perm_store' is nonnull, then store the IPC sender's page permission // in *perm_store (this is nonzero iff a page was successfully // transferred to 'pg'). if (perm_store != NULL) *perm_store = thisenv->env_ipc_perm; // If the system call fails, then store 0 in *fromenv and *perm (if // they're nonnull) and return the error. if (from_env_store != NULL && perm_store != NULL && r < 0) { *from_env_store = 0; *perm_store = 0; return r; } if (r < 0) return r; return thisenv->env_ipc_value; //panic("ipc_recv not implemented"); //return 0; }
在syscall.c中进行设置
case (SYS_ipc_recv): return sys_ipc_recv((void *)a1); case (SYS_ipc_try_send): return sys_ipc_try_send(a1, a2, (void *)a3, a4);
结束