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,還需要關注兩個寄存器。sie
與sip
。
Supervisor Interrupt Enable(sie)
Supervisor Interrupt Pending(sip)
可以看到有三種類型的中斷,由芯片廠家進行自定義設計。
- 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.輸出