mit 6.828学习笔记不知道几--lab4 partc

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\n"
        "movl %0, %%esp\n"
        "pushl $0\n"
        "pushl $0\n"
        // Uncomment the following line after completing exercise 13
        "sti\n"
        "1:\n"
        "hlt\n"
        "jmp 1b\n"
    : : "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);

结束

 

posted @ 2020-11-17 22:05  落鹤  阅读(214)  评论(0编辑  收藏  举报