以rt-smart在全志D1上的代碼為例,主要注釋了rt-smart在riscv64上的系統(tǒng)初始化和異常處理的代碼
啟動
代碼路徑
libcpurisc-vt-headc906startup_gcc.S
/*
Copyright (c) 2006-2018, RT-Thread Development Team
SPDX-License-Identifier: Apache-2.0
Change Logs:
 Date Author Notes
 2018/10/01 Bernard The first version
 2018/12/27 Jesven Add SMP support
 2020/6/12 Xim Port to QEMU and remove SMP support
/
 #define ASSEMBLY
 #define SSTATUS_FS 0x00006000U / initial state of FPU, clear to disable /
 #include
 .global _start
 .section ".start", "ax"
 _start:
 j 1f
 .word 0xdeadbeef
 .align 3
 .global g_wake_up
 g_wake_up:
 .dword 1
 .dword 0
 1:
 csrw sie, 0 / 超級用戶模式中斷使能關(guān)閉 /
 csrw sip, 0 / 超級用戶模式中斷等待關(guān)閉 /
 la t0, trap_entry / 將trap_entry的地址放入t0寄存器 /
 csrw stvec, t0 / 配置異常服務(wù)程序的入口地址 /
 li x1, 0
 / .........../ / 初始化通用寄存器 /
 li x31,0
 / set to disable FPU */
 li t0, SSTATUS_FS / 將FS的bit位寫入t0寄存器 /
 csrc sstatus, t0 / 清除sstatus中的FS bit,關(guān)閉浮點單元 /
 li t0, 0x40000 / 當 SUM=1 時,超級用戶模式下,加載、存儲和取指令請求可以訪問標記為用戶態(tài)的虛擬內(nèi)存空間 /
 csrs sstatus, t0 / 置位sstatus中的SUM位 /
 .option push
 .option norelax
 la gp, __global_pointer$
 .option pop
 // removed SMP support here
 la sp, stack_start / 棧指針的值來自于鏈接腳本中的__stack_start /
 li t0, STACKSIZE
 add sp, sp, t0 / 棧自上到下增長 /
 csrw sscratch, sp /*sscratch存儲棧頂?shù)牡刂?*/
 j primary_cpu_entry / 跳轉(zhuǎn)到board中的C程序入口 /
 //BSP的C入口
 void primary_cpu_entry(void)
 {
 extern void entry(void);
 //初始化BSS
 init_bss();
 //關(guān)中斷
 rt_hw_interrupt_disable();
 rt_assert_set_hook(__rt_assert_handler);
 //啟動RT-Thread Smart內(nèi)核
 entry();
 }
異常處理
異常處理流程圖

異常處理上半部分
/ libcpurisc-vt-headc906interrupt_gcc.S /
 #define ASSEMBLY
 #include "cpuport.h"
 #include "encoding.h"
 #include "stackframe.h"
 .section .text.entry
 .align 2
 .global trap_entry
 .extern __stack_cpu0
 .extern get_current_thread_kernel_stack_top
 trap_entry: / 異常處理函數(shù)的入口 /
 //backup sp
 csrrw sp, sscratch, sp / 將當前棧與sscratch做交換 /
 //load interrupt stack
 la sp, __stack_cpu0 / sp指向cpu0的中斷棧的棧頂 /
 //backup context
 SAVE_ALL / CPU寄存器入棧,使能浮點的情況下浮點相關(guān)的寄存器也要入棧 并且要保存sstatus中浮點的運算狀態(tài) /
 RESTORE_SYS_GP / gp操作不用了解 /
 //check syscall
 csrr t0, scause / 讀取scaue到t0 /
 li t1, 8 //environment call from u-mode / 用戶模式環(huán)境調(diào)用異常 /
 beq t0, t1, syscall_entry / 如果是系統(tǒng)調(diào)用則跳轉(zhuǎn)到系統(tǒng)調(diào)用處理函數(shù),這個函數(shù)最終會調(diào)用sret /
 csrr a0, scause / 讀取scause到a0,機器模式異常事件向量寄存器(MCAUSE)用于保存觸發(fā)異常的異常事件向量號,用于在異常服務(wù)程序中處理對應(yīng)事件 /
 csrrc a1, stval, zero / 讀取stval到a1,發(fā)生異?;蛘咧袛?,且在機器模式響應(yīng)時,處理器會更新 pc 到 MEPC,并根據(jù)異常類型更新 MTVAL /
 csrr a2, sepc / 讀取sepc到a2, 超級用戶模式異常保留程序計數(shù)器(SEPC)用于存儲程序從異常服務(wù)程序退出時的程序計數(shù)器值(即
 PC 值) /
 mv a3, sp / 讀取sp的值到a3 /
 /* scause, stval, sepc, sp /
 call handle_trap / 進行中斷處理 /
 中斷處理
 / libcpurisc-vt-headc906trap.c /
 / Trap entry /
 void handle_trap(rt_size_t scause,rt_size_t stval,rt_size_t sepc,struct rt_hw_stack_frame sp)
 {
 /
 SCAUSE
 bit63 Interrupt-中斷標記位
 當 Interrupt 位為 0 時,表示觸發(fā)異常的來源不是中斷, Exception Code 按照異常解析。當 Interrupt 位為 1 時,表示觸發(fā)異常的來源是中斷, Exception Code 按照中斷解析。該位會被 reset 置為 1’ b0。
 bit04 Exception Code-異常向量號位4是9超級用戶模式外部中斷 /
 在處理器響應(yīng)異?;蛑袛鄷r,該域會被更新為對應(yīng)異常號,具體請參考 表 3.9 異常和中斷向量分
 配。該位會被 reset 置為 5’ b0。
 */
 / 我理解這里是想獲取Exception Code,但是Exception Code是bit0 ~ bit4,這里用__MASK(5UL)更合適吧 /
 rt_size_t id = __MASKVALUE(scause,__MASK(63UL));
 const char msg;
 / supervisor external interrupt */
 /*如果scause的bit63是1,scause的bit0
 if ((SCAUSE_INTERRUPT & scause) && SCAUSE_S_EXTERNAL_INTR == (scause & 0xff))
 {
 rt_interrupt_enter();
 plic_handle_irq();
 rt_interrupt_leave();
 return;
 } / 如果scause的bit63是1,scause的bit0~4是超級用戶模式計時器中斷 /
 else if ((SCAUSE_INTERRUPT | SCAUSE_S_TIMER_INTR) == scause)
 {
 /* supervisor timer /
 rt_interrupt_enter();
 tick_isr();
 rt_interrupt_leave();
 return;
 } / 其他中斷 /
 else if (SCAUSE_INTERRUPT & scause)
 {
 if(id < sizeof(Interrupt_Name) / sizeof(const char ))
 {
 msg = Interrupt_Name[id];
 }
 else
 {
 msg = "Unknown Interrupt";
 }
 LOG_E("Unhandled Interrupt %ld:%sn",id,msg);
 }
 else / 異常處理 /
 {
 #ifdef RT_USING_USERSPACE
 / page fault 缺頁異常處理 /
 if (id == EP_LOAD_PAGE_FAULT ||
 id == EP_STORE_PAGE_FAULT)
 {
 arch_expand_user_stack((void *)stval);
 return;
 }
 #endif / 其他異常處理,走到這里后打印一些必要信息,最終會走到while(1),進入死循環(huán) /
 if(id < sizeof(Exception_Name) / sizeof(const char *))
 {
 msg = Exception_Name[id];
 }
 else
 {
 msg = "Unknown Exception";
 }
 rt_kprintf("Unhandled Exception %ld:%sn",id,msg);
 }
 rt_kprintf("scause:0x%p,stval:0x%p,sepc:0x%pn",scause,stval,sepc);
 dump_regs(sp);
 while(1);
 }
在rt-smart中任務(wù)切換有三個相關(guān)的線程函數(shù)
rt_hw_context_switch_to():沒有來源線程,切換到目標線程,在調(diào)度器啟動第一個線程的時候 被調(diào)用
 rt_hw_context_switch():在線程環(huán)境下,從當前線程切換到目標線程
 rt_hw_context_switch_interrupt ():在中斷環(huán)境下,從當前線程切換到目標線程。
 rt_hw_context_switch_interrupt ()會將rt_thread_switch_interrupt_flag置為1,真正的線程切換動作在異常處理函數(shù)中完成。
異常處理下半部分
/* need to switch new thread 查詢線程切換的flag是否被置位為1*/
 la s0, rt_thread_switch_interrupt_flag / 讀取rt_thread_switch_interrupt_flag /
 lw s2, 0(s0)
 beqz s2, spurious_interrupt / rt_thread_switch_interrupt_flag如果為0那么直接跳轉(zhuǎn)到spurious_interrupt進行寄存器恢復,并調(diào)用sret回到異常之前的狀態(tài) /
 sw zero, 0(s0) / rt_thread_switch_interrupt_flag = 0 /
 .global rt_hw_context_switch_interrupt_do
 rt_hw_context_switch_interrupt_do:
 //swap to thread kernel stack
 csrr t0, sstatus / 讀取sstatus到t0 /
 andi t0, t0, 0x100 / bit8 超級用戶模式保留特權(quán)狀態(tài)位 /
 /*
 該位用于保存處理器在降級到超級用戶模式進入異常服務(wù)程序前的特權(quán)狀態(tài)。
 ? 當 SPP 為 2’ b00 時,表示處理器進入異常服務(wù)程序前處于用戶模式;
 ? 當 SPP 為 2’ b01 時,表示處理器進入異常服務(wù)程序前處于超級用戶模式;
 該位會被 reset 置 2’ b01。
/
 beqz t0, __restore_sp_from_tcb_interrupt / 如果是內(nèi)核態(tài)發(fā)生異常 /
 __restore_sp_from_sscratch_interrupt:
 csrr t0, sscratch / 獲取發(fā)生異常時的上下文數(shù)據(jù) /
 j __move_stack_context_interrupt / 如果是用戶態(tài)發(fā)生異常 /
 / 獲取當前線程的棧頂位置存到t0中 /
 __restore_sp_from_tcb_interrupt:
 la s0, rt_interrupt_from_thread
 LOAD a0, 0(s0)
 jal rt_thread_sp_to_thread
 jal get_thread_kernel_stack_top
 mv t0, a0
 __move_stack_context_interrupt:
 mv t1, sp//src / 當前棧,當前棧存儲的是發(fā)生異常時的通用寄存器信息 /
 mv sp, t0//switch stack / 將發(fā)生異常時的棧的值寫回到sp寄存器 */
 addi sp, sp, -CTX_REG_NR * REGBYTES / 棧指針向下移動CTX_REG_NR * REGBYTES /
 //copy context
 li s0, CTX_REG_NR//cnt / 需要恢復的寄存器的個數(shù)加載到s0 /
 mv t2, sp//dst / 棧指針加載到t2 /
 / 總結(jié)就是,當前CPU的中斷棧存儲了當前線程的通用寄存器的信息,如果發(fā)生任務(wù)切換,需要把這些信息拷貝到線程的棧里 /
 copy_context_loop_interrupt:
 LOAD t0, 0(t1) / t1的值放到t0 /
 STORE t0, 0(t2) / t0的值放到t2 /
 addi s0, s0, -1 / 要恢復的寄存器個數(shù)-1 /
 addi t1, t1, 8 / t1的地址加8 /
 addi t2, t2, 8 / t2的地址加8 /
 bnez s0, copy_context_loop_interrupt / 如果s0不為0就重復拷貝 /
 la s0, rt_interrupt_from_thread
 LOAD s1, 0(s0)
 STORE sp, 0(s1) / 更新from線程的sp指針 /
 la s0, rt_interrupt_to_thread
 LOAD s1, 0(s0)
 LOAD sp, 0(s1) / 恢復to線程的sp /
 #ifdef RT_USING_USERSPACE
 mv a0, s1
 jal rt_thread_sp_to_thread
 jal lwp_mmu_switch / 切換mmu,函數(shù)內(nèi)部會判斷from線程和to線程是不是在同一個lwp中,不是的話就會切換MMU /
 #endif
 spurious_interrupt:
 RESTORE_ALL / 恢復寄存器 /
 sret / 超級用戶模式異常返回指令 /
- 
                                寄存器
                                +關(guān)注
關(guān)注
31文章
5522瀏覽量
128514 - 
                                中斷處理
                                +關(guān)注
關(guān)注
0文章
94瀏覽量
11409 - 
                                RT-Thread
                                +關(guān)注
關(guān)注
32文章
1514瀏覽量
43995 - 
                                RISC-V
                                +關(guān)注
關(guān)注
48文章
2718瀏覽量
51355 
發(fā)布評論請先 登錄
RT-Smart的資料合集
如何在RT-Thread Smart下使用gcc交叉編譯工具鏈呢
在虛擬機Ubuntu18.04中配置RT-Smart RISC-V64報unknown arch錯誤?
RT-Smart在riscv64上的系統(tǒng)初始化和異常處理的代碼注釋
rt-smart在riscv64上的系統(tǒng)初始化和異常處理的代碼注釋
怎么解決rt-smart在適配riscv64虛擬地址和物理地址映射問題?
riscv64裸機編程實踐與分析
    
樹莓派上rt-smart的應(yīng)用編程入門
    
第一屆RISC-V中國峰會最新內(nèi)容:RV64上的微內(nèi)核操作系統(tǒng)rt-smart介紹
    
rt-smart移植分析:從樹莓派3b入手
    
          
        
        
RT-Smart riscv64匯編注釋
                
 
    
           
            
            
                
            
評論