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模式中斷設置


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM