关于Lab1 Challenge的几个小问题
匿名2023/07/31 19:52:03提问
    lab1student
311

老师,助教,同学们好,我对Lab1的Challenge有一些不懂的地方:

在Lab1的Challenge2中要求:

用键盘实现用户模式内核模式切换。具体目标是:“键盘输入3时切换到用户模式,键盘输入0时切换到内核模式”。

答案(github branch lab1-X)的实现为:

     case IRQ_OFFSET + IRQ_KBD:
        c = cons_getc();
        cprintf("kbd [%03d] %c\n", c, c);

        /*********************/
        //Hardware Interrupt is different with software trap, so no need use temp stack
        if ( c =='3'){
            tf->tf_eflags |= 0x3000;
            if (tf->tf_cs != USER_CS) {

                tf->tf_cs = USER_CS;
                tf->tf_ds = tf->tf_es = tf->tf_ss = USER_DS;
                tf->tf_eflags |= FL_IOPL_MASK;
        }
        print_trapframe(tf);
}

按照我的理解,在内核态时触发键盘中断,此时是从内核态转换为内核态,故栈中不会先压入ss和esp。此时通过修改栈帧再iret的方法进行状态切换,

直接修改tf->tf_ss为USER_DS,会不会有越过当前栈帧的问题?因为此时栈帧中并没有ss这一项。答案中强调了硬中断和软中断的不同所以处理方式不同,这个不同是什么呢?

另外,我猜想在Challenge1的答案中可能会有点小问题:
(1)在内核态转换到用户态时: 

case T_SWITCH_TOU:
        tf->tf_eflags |= 0x3000;
        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;


switchk2u.tf_esp = (uint32_t)tf + sizeof(struct trapframe) - 8;我觉得这里不应该加上-8,在init.c中


static void lab1_switch_to_user(void) {
//LAB1 CHALLENGE 1 : TODO
    asm volatile (
        "sub $0x8, %%esp \n"
        "int %0 \n"
        "movl %%ebp, %%esp" //can cancel
        :         : "i"(T_SWITCH_TOU)     ); }


已经提前把esp减了8再调用软中断,此时再到上面的case中处理时,新建栈帧的esp若要指向上一个栈帧的结尾,值应为(uint32_t)tf + sizeof(struct trapframe)。
经测试,若去掉这个-8,可在lab1_switch_to_user中去掉"movl %%ebp, %%esp",此时仍正常运行,因为iret时esp已修正为正确值。

(2)在用户态转化到内核态时:

     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;

if{}中的后三句似乎没有存在的必要诶,直接改当前tf好像就可以了?

我的理解可能有些浅薄,希望老师同学一起讨论一下,谢谢!

回答(1
    推荐问答
      Simple Empty
      暂无数据