Interrupt handling routines are architecture-dependent. The way interrupts are managed and processed can vary significantly between different computer architectures. Interrupt handling routines can be different between RISC-V and Armv8.

Understanding interrupt handling routines is crucial when dealing with real-time operating systems (RTOS) and the Linux kernel. Since RTOS and Linux kernel rely heavily on interrupt handling to efficiently manage and respond to events in a timely manner.

Here's a general outline of how interrupt handling might be implemented in Linux kernel based on RISC-V architecture:


[1] When interrupt is triggered from peripheral like device signals interrupt, interrupt handling routine is performed to handle interrupt request. Based on RISC-V architecture, 'handle_exception' is exception handler.

arch/riscv/kernel/entry.S
SYM_CODE_START(handle_exception)
...
la ra, ret_from_exception

/*
 * MSB of cause differentiates between
 * interrupts and exceptions
 */
bge s4, zero, 1f

/* Handle interrupts */
tail do_irq

[2] The call do_irq() function is made inside handle_exception as above.

do_irq() -> handle_riscv_irq() -> riscv_intc_irq() -> generic_handle_domain_irq()

[3] If IRQ is considered to be inter-processor interrupt, sbi_ipi_handle() is called.
Where sbi_ipi_handle() is set to chained handler in arch/riscv/kernel/sbi-ipi.c
 
[4] If IRQ is triggered from peripheral device(like sensor, touch, etc), plic_handle_irq() is called.
Where plic_handle_irq() is set to chained handler in drivers/irqchip/irq-sifive-plic.c.

[5] It is known that timer interrupt is handled in RISC-V in different ways. If timer IRQ is triggered, handle_percpu_devid_irq () is called.

The subroutine of [3],[4][5] will call the corresponding interrupt handler to take care of interrupt handling.

          <idle>-0       [000] d.h..   643.355357: irq_handler_entry: irq=16 name=virtio2
          <idle>-0       [000] d.h..   643.355397: <stack trace>
 => trace_event_raw_event_irq_handler_entry+0x96/0xf6
 => __traceiter_irq_handler_entry+0x2e/0x44
 => __handle_irq_event_percpu+0x13a/0x196
 => handle_irq_event+0x78/0xd2
 => handle_fasteoi_irq+0xa8/0x236
 => generic_handle_domain_irq+0x28/0x36
 => plic_handle_irq+0x86/0xfe
 => generic_handle_domain_irq+0x28/0x36
 => riscv_intc_irq+0x34/0x4c
 => do_irq+0x56/0x8e
 => ret_from_exception+0x0/0x64
 => default_idle_call+0x3a/0xd0
 => do_idle+0x214/0x23e
 => cpu_startup_entry+0x2e/0x30
 => kernel_init+0x0/0x168
 => arch_post_acpi_subsys_init+0x0/0x28
 => console_on_rootfs+0x0/0x70

'RISC-V' 카테고리의 다른 글

[RISC-V] system call return (s mode -> u mode)  (0) 2024.02.29
[RISC-V]: Interrupt handling - code walkthrough  (0) 2024.01.29

...
MP:FFFFFFFF80002FEA|                    csrrc      x9,sstatus,x5  // atomic read and clear bits in CSR.
MP:FFFFFFFF80002FEE|                    csrr       x18,sepc
MP:FFFFFFFF80002FF2|                    csrr       x19,stval
MP:FFFFFFFF80002FF6|                    csrr       x20,scause
MP:FFFFFFFF80002FFA|                    csrr       x21,sscratch

void handle_exception() 
{
   word scause;
   word offset_vect;
   void *exception_func;
   bool interrupt_status;

   scause = sys_csr_reg_read(scause); // csrr       x20,scause

   interrupt_status = scause >> (SXLEN -1); // 

   if ( interrupt_status == true) {
       la_reg = ret_from_exception;  // la ra, ret_from_exception
       handle_arch_irq(); 
   }
   else {
      la_reg = ret_from_exception;  // la ra, ret_from_exception
      if (scause & 0b1000) {
         handle_syscall();
      }
      else {
         offset_vect = get_exception_reason(scause);
         exception_func = excp_vect_table + offset_vect;
         (void*)exception_func; // c.jr x5
      }
   }
}

이번 포스트에서는 RISC-V 툴체인을 설치해 리눅스 커널을 빌드하는 방법을 소개합니다.

먼저 RISC-V 툴체인을 설치하는 명령어를 입력합시다.

RISC-V 툴체인 소스를 내려받기  

다음 명령어를 입력해 RISC-V 툴체인 소스를 내려받습니다.

$ git clone --recursive https://github.com/riscv/riscv-gnu-toolchain
 
아래는 리눅스 터미널에서 위 명령어를 입력한 후의 출력 결과입니다. 소스를 내려받는데 1시간 정도 걸리네요.

austindh.kim:~/src/risc-v_toolchain$ git clone --recursive https://github.com/riscv/riscv-gnu-toolchain
Cloning into 'riscv-gnu-toolchain'...
remote: Enumerating objects: 21, done.
remote: Counting objects: 100% (21/21), done.
remote: Compressing objects: 100% (14/14), done.
remote: Total 7795 (delta 7), reused 16 (delta 4), pack-reused 7774
Receiving objects: 100% (7795/7795), 4.75 MiB | 970.00 KiB/s, done.
Resolving deltas: 100% (3940/3940), done.
Submodule 'qemu' (https://git.qemu.org/git/qemu.git) registered for path 'qemu'
Submodule 'riscv-binutils' (https://github.com/riscv/riscv-binutils-gdb.git) registered for path 'riscv-binutils'
Submodule 'riscv-dejagnu' (https://github.com/riscv/riscv-dejagnu.git) registered for path 'riscv-dejagnu'
Submodule 'riscv-gcc' (https://github.com/riscv/riscv-gcc.git) registered for path 'riscv-gcc'
Submodule 'riscv-gdb' (https://github.com/riscv/riscv-binutils-gdb.git) registered for path 'riscv-gdb'
...

시간이 오래 걸리니 밥 먹기 전에 명령어를 입력해 놔야 겠습니다.

RISC-V 툴체인 소스 빌드하고 설치하기 

소스를 전부 내려받은 다음에 아래와 같이 디렉터리를 생성합니다.

$ mkdir -p opt/riscv
$ mkdir build
$ cd build
$ ../configure --prefix=/home001/austindh.kim/src/risc-v_toolchain/riscv-gnu-toolchain/opt/riscv --enable-multilib

혹은

$ ../configure --prefix=/home001/austindh.kim/src/risc-v_toolchain/riscv-gnu-toolchain/opt/riscv  



아래는 리눅스 터미널에서 위 명령어를 실행한 후 출력 결과입니다. 빌드되는데 2시간 정도가 소요됩니다.

austindh.kim:~/src/risc-v_toolchain$ mkdir -p opt/riscv
austindh.kim:~/src/risc-v_toolchain$ mkdir build
austindh.kim:~/src/risc-v_toolchain$ cd build
austindh.kim:~/src/risc-v_toolchain/build$ ../configure --prefix=/home001/austindh.kim/src/risc-v_toolchain/riscv-gnu-toolchain/opt/riscv 
...

 

이번에는 다음 명령어를 실행해서 빌드를 수행합니다.

 

austindh.kim:~/src/risc-v_toolchain/build$ make linux


빌드가 끝나고 "~src/risc-v_toolchain/riscv-gnu-toolchain/opt/riscv/bin" 디렉터리로 이동하니, 다음과 같은 파일이 생성됐음을 확인할 수 있습니다.

austindh.kim:~/src/risc-v_toolchain/riscv-gnu-toolchain/opt/riscv/bin$ ls
riscv64-unknown-linux-gnu-addr2line  riscv64-unknown-linux-gnu-gcc         riscv64-unknown-linux-gnu-gdb            riscv64-unknown-linux-gnu-objcopy  ... 
riscv64-unknown-linux-gnu-elfedit    riscv64-unknown-linux-gnu-gcov-dump   riscv64-unknown-linux-gnu-lto-dump       riscv64-unknown-linux-gnu-strings
riscv64-unknown-linux-gnu-g++        riscv64-unknown-linux-gnu-gcov-tool   riscv64-unknown-linux-gnu-nm             riscv64-unknown-linux-gnu-strip

이제 RISC-V 툴체인 소스 빌드를 마무리했습니다.

 RISC-V 툴체인으로 리눅스 커널 빌드하기 

다음 명령어를 입력해 리눅스 커널 소스 코드를 내려 받습니다.

$ git clone https://kernel.googlesource.com/pub/scm/linux/kernel/git/next/linux-next

소스를 내려 받은 다음에 확인한 디렉터리 정보는 다음과 같습니다.

austindh.kim:~/src/dev_kernel/59_linux_kernel$ ls
linux-next

'riscv_kernel_build.sh' 이름으로 셸 스크립트 파일을 생성한 다음에, 아래 빌드 스크립트를 입력합니다.

 

#!/bin/bash

export PATH=$PATH:/home001/austindh.kim/src/risc-v_toolchain/riscv-gnu-toolchain/opt/riscv/bin

echo "configure build output path"
TOP_PATH=$( cd "$(dirname "$0")" ; pwd )
OUTPUT="$TOP_PATH/out"

BUILD_LOG="$TOP_PATH/rpi_build_log.txt"

OUTPUT_PATH=$( cd "$(dirname "$0")" ; pwd )
OUTPUT="$OUTPUT_PATH/out"

pushd linux-next > /dev/null

# configure defconfig
make ARCH=riscv O=$OUTPUT CROSS_COMPILE=riscv64-unknown-linux-gnu- defconfig -j16  2>&1

# build kernel with riscv tool-chain
make ARCH=riscv O=$OUTPUT CROSS_COMPILE=riscv64-unknown-linux-gnu- modules dtbs -j16  2>&1 | tee $BUILD_LOG

popd > /dev/null


위 빌드 스크립트에서 가장 중요한 명령어는 아래와 같습니다.


# configure defconfig
make ARCH=riscv O=$OUTPUT CROSS_COMPILE=riscv64-unknown-linux-gnu- defconfig -j16  2>&1

# build kernel with riscv tool-chain
make ARCH=riscv O=$OUTPUT CROSS_COMPILE=riscv64-unknown-linux-gnu- modules dtbs -j16  2>&1 | tee $BUILD_LOG



'arch/riscv/configs/defconfig' 파일을 다음과 같이 수정해 vmlinux에 디버깅 심벌이 추가되도록 합시다.

diff --git a/arch/riscv/configs/defconfig b/arch/riscv/configs/defconfig
index d58c93e..6ea8e83 100644
--- a/arch/riscv/configs/defconfig
+++ b/arch/riscv/configs/defconfig
@@ -20,6 +20,7 @@ CONFIG_SMP=y
 CONFIG_JUMP_LABEL=y
 CONFIG_MODULES=y
 CONFIG_MODULE_UNLOAD=y
+CONFIG_DEBUG_INFO=y
 CONFIG_NET=y
 CONFIG_PACKET=y
 CONFIG_UNIX=y


'riscv_kernel_build.sh' 빌드 셸 스크립트를 실행해, RISCV 툴 체인으로 커널을 빌드합니다.

아래는 'riscv_kernel_build.sh' 빌드 셸 스크립트를 터미널에서 실행한 출력 화면입니다.
austindh.kim:~/src/dev_kernel/59_linux_kernel$ ./riscv_kernel_build.sh
configure build output path
make[1]: Entering directory '/home001/austindh.kim/src/dev_kernel/59_linux_kernel/out'
  GEN     Makefile
  HOSTCC  scripts/basic/fixdep
  HOSTCC  scripts/kconfig/conf.o
...
  KSYMS   .tmp_vmlinux.kallsyms2.S
  AS      .tmp_vmlinux.kallsyms2.S
  LD      vmlinux
  SYSMAP  System.map
  MODPOST Module.symvers
  CC [M]  fs/nfs/flexfilelayout/nfs_layout_flexfiles.mod.o
  LD [M]  fs/nfs/flexfilelayout/nfs_layout_flexfiles.ko


'out' 폴더를 가니 제대로 커널이 빌드됐음을 확인할 수 있습니다.

austindh.kim:~/src/dev_kernel/59_linux_kernel/out$ ls
arch   crypto   include  kernel    mm                       modules.order   scripts   source      virt       vmlinux.symvers
block  drivers  init     lib       modules.builtin          Module.symvers  security  System.map  vmlinux
certs  fs       ipc      Makefile  modules.builtin.modinfo  net             sound     usr         vmlinux.o


RISC-V 바이너리 유틸리티 사용해보기 

이번엔 다음 명령어를 입력해 vmlinux의 셉션 정보를 확인해보겠습니다.

  
$ ./riscv64-unknown-linux-gnu-objdump -x vmlinux | more


아래는 './riscv64-unknown-linux-gnu-objdump -x vmlinux | more' 빌드 셸 스크립트를 터미널에서 실행한 출력 화면입니다.

austindh.kim:~/src/dev_kernel/59_linux_kernel/out$ ./riscv64-unknown-linux-gnu-objdump -x vmlinux | more

vmlinux:     file format elf64-littleriscv
vmlinux
architecture: riscv:rv64, flags 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
start address 0xffffffe000000000

Program Header:
    LOAD off    0x0000000000001000 vaddr 0xffffffe000000000 paddr 0x0000000000000000 align 2**12
         filesz 0x0000000000025af4 memsz 0x0000000000025af4 flags r-x
    LOAD off    0x0000000000027000 vaddr 0xffffffe000026000 paddr 0x0000000000026000 align 2**12
         filesz 0x0000000000019de8 memsz 0x0000000000019de8 flags rwx
    LOAD off    0x0000000000041000 vaddr 0xffffffe000200000 paddr 0x0000000000200000 align 2**12
         filesz 0x00000000006ce0a2 memsz 0x00000000006ce0a2 flags r-x
    LOAD off    0x0000000000710000 vaddr 0xffffffe000a00000 paddr 0x0000000000a00000 align 2**12
         filesz 0x000000000022a9fc memsz 0x000000000022a9fc flags rw-
    LOAD off    0x000000000093b000 vaddr 0xffffffe000e00000 paddr 0x0000000000e00000 align 2**12
         filesz 0x0000000000002400 memsz 0x0000000000002400 flags r--
    LOAD off    0x000000000093e000 vaddr 0xffffffe001000000 paddr 0x0000000001000000 align 2**12
         filesz 0x00000000000e19b4 memsz 0x0000000000131920 flags rw-
    NOTE off    0x000000000093a9c0 vaddr 0xffffffe000c2a9c0 paddr 0x0000000000c2a9c0 align 2**2
         filesz 0x000000000000003c memsz 0x000000000000003c flags r--
   STACK off    0x0000000000000000 vaddr 0x0000000000000000 paddr 0x0000000000000000 align 2**4
         filesz 0x0000000000000000 memsz 0x0000000000000000 flags rw-

  
출력 결과를 보니 아키텍처의 정보는 'riscv:rv64'이고 스타트업 코드의 위치는 0xffffffe000000000 주소임을 알 수 있습니다.

+ Recent posts