본문 바로가기

RISC-V/인터럽트 콘트롤러

[RISC-V] 리눅스 커널 - local_irq_enable() 함수의 역할과 sstatus.sie (로컬 인터럽트 설정)

리눅스 커널에서는 로컬 인터럽트(local interrupt)를 활성화하거나 비활성화할 때 사용하는 API 함수들이 있습니다:

 - local_irq_enable()
 -  local_irq_disable()

코드 분석: preempt_schedule_irq() 함수


이제 preempt_schedule_irq() 함수의 일부 코드를 살펴보겠습니다:

asmlinkage __visible void __sched preempt_schedule_irq(void)
{
	enum ctx_state prev_state;

	/* Catch callers which need to be fixed */
	BUG_ON(preempt_count() || !irqs_disabled());

	prev_state = exception_enter();

	do {
		preempt_disable();
		local_irq_enable();
		__schedule(SM_PREEMPT);
		local_irq_disable();
		sched_preempt_enable_no_resched();
	} while (need_resched());

	exception_exit(prev_state);
}

 

이 코드에서는 __schedule() 함수가 실행되는 동안 local_irq_enable() 함수가 호출되어 로컬 인터럽트를 활성화합니다.
그 후, __schedule() 함수가 종료되면 다시 local_irq_disable() 함수가 호출되어 로컬 인터럽트를 비활성화합니다.

디스어셈블리(Disassembly) 결과 분석


이제 이 코드를 RISC-V 기반 컴파일러로 빌드한 다음, 디스어셈블리(disassembly) 결과를 확인해 보겠습니다:

SP:FFFFFFFF80D8E526|preempt_schedule_irq:  lw         a5,0x8(tp)    ; a5,8(tp)  
SP:FFFFFFFF80D8E52A| c.bnez     a5,0xFFFFFFFF80D8E55E  
[...]  
SP:FFFFFFFF80D8E53C| csrsi      sstatus,0x2  
SP:FFFFFFFF80D8E540| c.li       a0,0x1  
SP:FFFFFFFF80D8E542| auipc      ra,0xFFFFF    ; ra,1048575  
SP:FFFFFFFF80D8E546| jalr       ra,0x516(ra)   ; ra,1302(ra) ; __schedule  
SP:FFFFFFFF80D8E54A| csrci      sstatus,0x2


이 코드를 보면 csrsi sstatus,0x2 명령어와 csrci sstatus,0x2 명령어가 사용됩니다.
이 명령어들은 각각 sstatus 레지스터의 2번째 비트를 설정(1)하거나 해제(0)하는 역할을 수행합니다. 
 
local_irq_enable() 함수의 내부 구현

 

local_irq_enable() 함수는 아키텍처에 의존적인 함수입니다. 코드를 단계별로 분석하면서 함수 내부 동작을 파악해 보겠습니다.

먼저 local_irq_enable() 함수의 구현부를 보겠습니다:

// include/linux/irqflags.h  
#define local_irq_enable() do { raw_local_irq_enable(); } while (0)


이 함수는 raw_local_irq_enable() 함수로 치환됩니다.

raw_local_irq_enable() 함수를 봅시다:

// include/linux/irqflags.h  
#define raw_local_irq_enable() arch_local_irq_enable()

 

이제 arch_local_irq_enable() 함수로 치환됩니다. 리눅스 커널에서는 매크로를 사용해 특정 함수가 아키텍처에 맞춰 동작하도록 하는 방식이 자주 사용됩니다.

arch_local_irq_enable() 함수의 구현부를 보겠습니다.

// arch/riscv/include/asm/irqflags.h  
static inline void arch_local_irq_enable(void)  
{  
	csr_set(CSR_STATUS, SR_IE);  
}

 

이 함수는 RISC-V 아키텍처에 맞춰 구현된 함수입니다. 여기서 SSTATUS 레지스터의 SR_IE 비트를 1로 설정하는 동작입니다. 

SR_IE 매크로와 SR_SIE 매크로 정의를 알아 봅시다.

// arch/riscv/include/asm/csr.h  
#define SR_IE  SR_SIE  
[...]  
#define SR_SIE _AC(0x00000002, UL)  /* Supervisor Interrupt Enable */


여기서 SR_IE는 0x2 값과 동일합니다. 이는 csrsi sstatus,0x2 명령어가 sstatus 레지스터의 2번째 비트를 설정한다는 의미입니다.

분석 결과 요약

 

분석한 내용을 정리하면 다음과 같습니다:

 - local_irq_enable() 함수는 로컬 인터럽트를 활성화하는 역할을 합니다.
-  RISC-V 아키텍처 기반에서는 csrsi sstatus,0x2 명령어가 해당 함수의 핵심 동작입니다.
-  이 명령어는 sstatus 레지스터의 2번째 비트를 1로 설정합니다.

하드웨어 디버깅: sstatus 레지스터 확인

 

아래는 하드웨어 디버깅 과정에서 확인한 결과입니다.

csrsi sstatus,0x2 명령어 실행 후 결과:


sstatus 레지스터 값이 0x2로 변경되었습니다.

csrci sstatus,0x2 명령어 실행 후 결과:


sstatus 레지스터 값이 0x0으로 변경되었습니다.

디버깅 결과로, 이제 local_irq_enable() 함수의 동작 원리와 sstatus 레지스터의 역할에 대해 명확히 이해했을 것입니다.
이를 통해 로컬 인터럽트를 활성화하고 비활성화하는 과정이 소프트웨어와 하드웨어 레벨에서 어떻게 연동되는지도 알 수 있습니다.