• QEMU裸机开发之S模式中断设置


    1. 中断初始化

    S模式下的中断初始化和M模式基本类似,只不过操作的是S模式下的CSR寄存器,如下所示。

    static void supervisor_trap_init(void)
    {
        //this stack is used for supervisor_trap_entry in entry.S
        sscratch_set((unsigned long)(__stack_start + 4096 * 3));
        // set the supervisor trap handler.
        stvec_set((unsigned long)supervisor_trap_entry);
        // enable supervisor interrupts.
        sstatus_set(sstatus_get() | SSTATUS_SIE);
        // enable supervisor timer and soft interrupts.
        sie_set(sie_get() | SIE_STIE | SIE_SSIE);
    }
    static void main(void)
    {
        printf("%s %d.\r\n", __func__, __LINE__);
        supervisor_trap_init();
        while(1);
    }

    唯一不同的是S模式的中断栈使用的是__stack_start往上第三个4KB的空间,其余的和m模式下都差不多,这里就不在赘述了。

    2. 中断处理

    2.1 中断入口

    在“entry.S”中,我们需要实现s模式中断和异常处理的入口,如下所示。

    #
    # supervisor trap entry.
    #
    .globl supervisor_trap_entry
    .align 4
    supervisor_trap_entry:
        # sscratch holds smode trap stack address,
        # swap current sp and smode trap stack address
        csrrw sp, sscratch, sp
        // call the C trap handler in trap.c
        call supervisor_trap
        # restore old sp value
        csrrw sp, sscratch, sp
        sret

    处理逻辑也和m模式下一样,就是设置好栈地址后,然后调用“supervisor_trap”C函数进行处理。

    2.2 中断C函数处理

    S模式的C处理函数和m模式同样基本类似,只不过将相关CSR寄存器换成S模式下的,如下所示。

    void supervisor_trap(void)
    { 
        unsigned long tval = stval_get();
        unsigned long sepc = sepc_get();
        unsigned long cause = scause_get();
        int is_int = (cause & (1l << 63l)) ? 1 : 0;
        int scode = cause & 0xff;
        if (scode >= 16) {
            printf("%s : %s.\r\n", is_int ? "Interrupt" : "Exception", "Unknown code");
            return;
        }
        if (is_int) {
            printf("Interrupt : %s.\r\n", interrupt_cause[scode]); 
            switch (scode) {
            case S_SOFT_INT:
                break;
            case S_TIMER_INT:
                break;
            }
        } else {
            printf("Exception : %s.\r\n", exception_cause[scode]); 
            switch (scode) {
            case ILLEGAL_INSTRUCTION:
                printf("tval = %p\r\n", tval);
                printf("sepc = %p\r\n", sepc);
                break;
            }
            sepc_set(sepc + 4);
        }
        return;
    }

    3. 测试

    由于需要触发中断才能测试中断处理是不是真的被执行了,所以本章节只是准备好这些设置和处理,通过S模式软中断来一起验证和测试。

    S模式下的软中断我们借助m模式下的定时器中断进行触发,在m模式下设置sip中的soft位域就可以触发S模式下的软中断,如下所示。

    void machine_trap(void)
    { 
        unsigned long cause = mcause_get();
        unsigned long mepc  = mepc_get();
        unsigned long tval  = mtval_get();
        int is_int = (cause & (1l << 63l)) ? 1 : 0;
        int mcode = cause & 0xff;
        if (mcode >= 16) {
            printf("%s : %s.\r\n", is_int ? "Interrupt" : "Exception", "Unknown code");
            return;
        }
        if (is_int) {
            printf("Interrupt : %s.\r\n", interrupt_cause[mcode]); 
            switch (mcode) {
            case M_SOFT_INT:
                msoftint_clear();
                break;
            case M_TIMER_INT:
                timer_set(timer_get() + TIMER_CLK_RATE);
                // raise a supervisor software interrupt.
                sip_set(SIP_SSIP);
                break;
            }
        } else {
            printf("Exception : %s.\r\n", exception_cause[mcode]); 
            switch (mcode) {
            case ILLEGAL_INSTRUCTION:
                printf("tval = %p\r\n", tval);
                printf("mepc = %p\r\n", mepc);
                break;
            case ECALL_FROM_SMODE:
                break;
            }
            mepc_set(mepc + 4);
        }
        return;
    }

    S-Mode中异常开关的寄存器

    对于S-Mode中断的Enable与Pending,还需要关注两个寄存器。siesip

    Supervisor Interrupt Enable(sie)

    5bd3d46061648e635b1bf0a65193c793.png

    Supervisor Interrupt Pending(sip)

    c355b3c323a9d4326b95a1549a7add14.png

    可以看到有三种类型的中断,由芯片厂家进行自定义设计。

    • Supervisor software interrupt
    • Supervisor timer interrupt
    • Supervisor external interrupt

    当m模式中断处理结束时就会立马进入s模式中断处理,在s模式的软中断处理中通过将sip中的soft位域置0来清除s模式软中断状态,如下所示。

    void supervisor_trap(void)
    { 
        unsigned long tval = stval_get();
        unsigned long sepc = sepc_get();
        unsigned long cause = scause_get();
        int is_int = (cause & (1l << 63l)) ? 1 : 0;
        int scode = cause & 0xff;
        if (scode >= 16) {
            printf("%s : %s.\r\n", is_int ? "Interrupt" : "Exception", "Unknown code");
            return;
        }
        if (is_int) {
            printf("Interrupt : %s.\r\n", interrupt_cause[scode]); 
            switch (scode) {
            case S_SOFT_INT:
                // acknowledge the software interrupt by clearing
                // the SSIP bit in sip.
                sip_set(sip_get() & (~SIP_SSIP));
                break;
            case S_TIMER_INT:
                break;
            }
        } else {
            printf("Exception : %s.\r\n", exception_cause[scode]); 
            switch (scode) {
            case ILLEGAL_INSTRUCTION:
                printf("tval = %p\r\n", tval);
                printf("sepc = %p\r\n", sepc);
                break;
            }
            sepc_set(sepc + 4);
        }
        return;
    }

    在“start”中设置定时器以让定时器产生可以产生中断,如下所示。

    void start(void)
    {
        printf("%s %d.\r\n", __func__, __LINE__);
        machine_trap_init();
        //msoftint_make();
        timer_set(timer_get() + TIMER_CLK_RATE);
        machine_switchto_supervisor();
    }

    注意本章节只是为了说明S模式软中断是如何使用的而采用了一种简单方便的测试方法,在实际的软件中,需要借助OpenSBI来进行核间中断设置和处理

    在命令行执行“make qemu”,可以看到先打印m模式下定时器中断信息,然后再打印s模式下的软中断信息,如下所示。

    [root@centos7 lesson9]# make qemu
    qemu-system-riscv64 -machine virt -bios none -kernel kernelimage -m 128M -smp 1 -nographic
    start 76.
    main 44.
    Interrupt : Machine timer interrupt.
    Interrupt : Supervisor software interrupt.
    Interrupt : Machine timer interrupt.
    Interrupt : Supervisor software interrupt.
    Interrupt : Machine timer interrupt.
    Interrupt : Supervisor software interrupt.
    Interrupt : Machine timer interrupt.
    Interrupt : Supervisor software interrupt.
    Interrupt : Machine timer interrupt.
    Interrupt : Supervisor software interrupt.
    Interrupt : Machine timer interrupt.
    Interrupt : Supervisor software interrupt.
    Interrupt : Machine timer interrupt.
    Interrupt : Supervisor software interrupt.
    Interrupt : Machine timer interrupt.
    Interrupt : Supervisor software interrupt.
    Interrupt : Machine timer interrupt.
    Interrupt : Supervisor software interrupt.
    Interrupt : Machine timer interrupt.
    Interrupt : Supervisor software interrupt.
    Interrupt : Machine timer interrupt.
    Interrupt : Supervisor software interrupt.
    Interrupt : Machine timer interrupt.
    Interrupt : Supervisor software interrupt.
    Interrupt : Machine timer interrupt.
    Interrupt : Supervisor software interrupt.
    Interrupt : Machine timer interrupt.
    Interrupt : Supervisor software interrupt.
    make: *** [Makefile:56: qemu] Killed
    [root@centos7 lesson9]# 
    [root@centos7 lesson9]# cat entry.S
    .section .text
    
    .global _entry
    _entry:
    
    # sp = __stack_start + ((hartid + 1) * 4096)
        la sp, __stack_start
        li a0, 4096
        csrr a1, mhartid
        addi a1, a1, 1
        mul a0, a0, a1
        add sp, sp, a0
    
        call start
        
    loop:
        j loop
    
    #
    # machine-mode trap entry.
    #
    .globl machine_trap_entry
    .align 4
    machine_trap_entry:
    
            # mscratch holds mmode trap stack address,
            # swap current sp and mmode trap stack address
            csrrw sp, mscratch, sp
    
            // call the C trap handler in trap.c
            call machine_trap
    
            # restore old sp value
            csrrw sp, mscratch, sp
    
            mret
    
    #
    # supervisor trap entry.
    #
    .globl supervisor_trap_entry
    .align 4
    supervisor_trap_entry:
    
            # sscratch holds smode trap stack address,
            # swap current sp and smode trap stack address
            csrrw sp, sscratch, sp
    
            // call the C trap handler in trap.c
            call supervisor_trap
    
            # restore old sp value
            csrrw sp, sscratch, sp
    
            sret

     屏蔽中断代理

    static void machine_switchto_supervisor(void)
    {
        // set M Previous Privilege mode to Supervisor, for mret.
        unsigned long x = mstatus_get();
        x &= ~MSTATUS_MPP_MASK;
        x |= MSTATUS_MPP_S;
        mstatus_set(x);
    
        // set M Exception Program Counter to main, for mret.
        // requires gcc -mcmodel=medany
        mepc_set((unsigned long)main);
    
        // disable paging for now.
        satp_set(0);
    
        // delegate interrupts and exceptions to supervisor mode.
        medeleg_set(0x0000);
        mideleg_set(0x0000);
        //medeleg_set(0xb109);
        //mideleg_set(0x222);
    
        // switch to supervisor mode and jump to main().
        asm volatile("mret");
    }
    [root@centos7 lesson9]# make qemu
    qemu-system-riscv64 -machine virt -bios none -kernel kernelimage -m 128M -smp 1 -nographic
    start 78.
    main 44.
    Interrupt : Machine timer interrupt.
    Interrupt : Machine timer interrupt.
    Interrupt : Machine timer interrupt.
    Interrupt : Machine timer interrupt.
    Interrupt : Machine timer interrupt.
    Interrupt : Machine timer interrupt.
    Interrupt : Machine timer interrupt.
    Interrupt : Machine timer interrupt.
    Interrupt : Machine timer interrupt.
    Interrupt : Machine timer interrupt.
    Interrupt : Machine timer interrupt.
    Interrupt : Machine timer interrupt.
    Interrupt : Machine timer interrupt.
    Interrupt : Machine timer interrupt.
    Interrupt : Machine timer interrupt.
    Interrupt : Machine timer interrupt.
    Interrupt : Machine timer interrupt.
    Interrupt : Machine timer interrupt.
    Interrupt : Machine timer interrupt.
    Interrupt : Machine timer interrupt.
    Interrupt : Machine timer interrupt.
    Interrupt : Machine timer interrupt.
    Interrupt : Machine timer interrupt.
    make: *** [Makefile:56: qemu] Killed

     没有Supervisor software interrupt.输出

    QEMU裸机开发之S模式中断设置

  • 相关阅读:
    controller接收前台数据—中文乱码问题
    如何将Object类型转换成String类型
    Spring MVC 关于controller的字符编码问题
    互联网金融--支付结算平台的测试
    delphi控件安装(安装ODAC、TeeChart、TServerSocket、TWSocketServer、TComm)
    软件业虽然建立在硬件行业之上,门槛低、更容易,但同时也是食物链的顶端(各行各业都需要信息化,所以就造成良莠不齐。大部分人,甚至大部分公司都是搬砖的)
    要会包装自己,包装自己的第一步就是合理的写简历(适当夸大自己、争取机会)
    OSChina 周三乱弹 —— 致力于做一名优秀的女程序员鼓励师
    35岁不是不行,而是某些公司培养方式让你无论多少岁都是一颗螺丝(要避免碰到这样的公司)
    Web层的搭建
  • 原文地址:https://www.cnblogs.com/dream397/p/15683832.html
Copyright © 2020-2023  润新知