Linux Kernel(4.14) Hacks

rousalome.egloos.com

포토로그 Kernel Crash




[라즈베리파이]인터럽트(4) - 인터럽트 발생을 막고 싶을 때 [라즈베리파이][커널]인터럽트

인터럽트가 발생하면 어떤 프로세스도 동작을 멈추고 인터럽트 벡터로 점프합니다. 그런데 어떤 코드 흐름에서는 인터럽트가 발생하면 안 될 경우가 있습니다.
- SoC에서 정의한 하드웨어 블락에 시퀀스(Sequence)을 줘야 할 경우.
- 시스템이 슬립에 진입하기 직전 핀 먹스 값을 저장하는 도중
- 각 디바이스 드라이버가 서스팬드 모드로 진입할 때 디바이스 드라이버에 데이터 시트에서 명시한 대로 특정 시퀀스를 줘야 할 경우
- 예외(Exception)이 발생해서 시스템 리셋을 시키기 전

예를 들면 프로세서가 슬립에 진입할 때 각 디바이스 드라이버를 서스팬스 시키는 도중 인터럽트가 발생하면 안 됩니다. 왜냐면 슬립에 진입할 때는 적절한 시퀀스(파형)을 해당 하드웨어에 전달하는 코드를 실행하는 경우가 많은데 이 때 타이밍이 중요합니다. 보통 데이터 시트에서 이런 정보를 담고 있죠. 그런데 이럴 때 인터럽트가 뜨면 데이터 시트에서 정해진 시퀀스를 줄 수 없습니다.

만약에 LCD 드라이버가 A란 파형이 하이에서 로우로 떨어질 때 B란 파형이 하이로 유지되어야 슬립에 들어간다고 가정하겠습니다. 그런데 이 구간에서 인터럽트가 발생하면 어떤 일이 발생할까요? 당연히 오동작하겠죠. 이럴 때 local_irq_disable 함수를 호출해야 합니다.

예외(Exception)가 발생해서 시스템 리셋을 시키기 전에도 인터럽트 발생을 막습니다.
아래는 어셈블코드에서 호출되는 코드인데, 유저 모드에서 권한 없이 메모리 공간에 접근하면 ARM 프로세스가 이를 감지하고 예외(Exception)을 발생시킵니다. 이때 bad_mode 함수가 호출되는데, 결국 panic 함수를 호출해서 커널 패닉을 유발시킵니다.   

커널 패닉을 시키기 바로 전 local_irq_disable 함수를 호출해서 인터럽트를 아예 막아 버립니다.
[arch/arm/kernel/traps.c]
asmlinkage void bad_mode(struct pt_regs *regs, int reason)
{
console_verbose();

pr_crit("Bad mode in %s handler detected\n", handler[reason]);

die("Oops - bad mode", regs, 0);
local_irq_disable();
panic("bad mode");
}

그럼 local_irq_disable 함수의 실제 구현부를 알아보기 위해 전처리 파일(linux/arch/arm/kernel/.tmp_traps.i)를 열어서 확인해보면, 아래 코드와 같이  arch_local_irq_disable() 코드를 확인할 수 있습니다.
void bad_mode(struct pt_regs *regs, int reason)
{
 console_verbose();

 printk("\001" "2" "Bad mode in %s handler detected\n", handler[reason]);

 die("Oops - bad mode", regs, 0);
 do { arch_local_irq_disable(); trace_hardirqs_off(); } while (0);
 panic("bad mode");
}

arch_local_irq_disable 함수를 열어서 보면 인라인 어셈블 코드로 "cpsid i" 이란 ARM 명령어를 실행합니다. 이 명령어를 실행하면 인터럽트가 발생해도 인터럽트 벡터가 실행되지 않습니다.
static inline __attribute__((always_inline)) __attribute__((no_instrument_function)) void arch_local_irq_disable(void)
 {
  asm volatile(
   "     cpsid i                 @ arch_local_irq_disable"
   :
   :
   : "memory", "cc");
 }
 # 155 "./arch/arm/include/asm/irqflags.h"

정말로 ARM "cpsid i" 명령어가 bad_mode 함수에서 호출됐는지 확인할까요?
"./arm-linux-androideabi-objdump -d vmlinux" 란 명령어로 바이너리 유틸리티를 써서 어셈블 코드를 확인할 수 있습니다. 정말로 해당 “cpsid" 명령어가 어셈블리 코드로 들어가 있네요. 
8010c260 <bad_mode>:
8010c260:       e1a0c00d        mov     ip, sp
8010c264:       e92dd800        push    {fp, ip, lr, pc}
// .. 생략 ..
8010c2a4:       e3a02000        mov     r2, #0
8010c2a8:       ebfffe9c        bl      8010bd20 <die>
8010c2ac:       f10c0080        cpsid   I // 
8010c2b0:       eb034511        bl      801dd6fc <trace_hardirqs_off>
8010c2b4:       e59f0010        ldr     r0, [pc, #16]   ; 8010c2cc <bad_mode+0x6c>
8010c2b8:       eb03db7a        bl      802030a8 <panic>


핑백

덧글

댓글 입력 영역