learningOS开源操作系统社区
  • 首页
  • 训练营
  • 明星学员
  • 共建单位
  • 项目实习
  • 问答论坛
登录
    Copyright © 2024 opencamp.ai All rights reserved.
    关于Lab1 Challenge的几个小问题
    匿名2023/07/31 19:52:03提问
      lab1student
    625

    老师,助教,同学们好,我对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
        暂无数据