Table of Contents

SCV/OS 를 개발하면서 삽질한 것들, 새롭게 알게된 것에 대해서 정리했다.

링크 시에 warning 이 발생하는 경우

어셈파일로 된 부트로더(.S) 를 컴파일하고, elf 포맷으로 만들기 위해 ld 를 사용해서 링킹을 하는데,

arm-linux-ld : warning: cannot find entry symbol reset; defaulting to 00000000

위와 같은 에러가 발생했다. 다행히 warning 이고 entry 주소가 0x00000000 으로 지정이 되었기 때문에 동작하는 데는 문제가 없었다.
하지만, 컴파일 마다 보이는 warning 메세지는 보기가 좋지 않았다.
분명히 ld-script 파일에 아래와 같이 적었음에도 불구하고

ENTRY(reset)

문제는 사라지지 않았다. 몇번의 삽질 끝에 원인은 entry 함수명(?) 이 전역변수(global) 로 선언해주지 않았기 때문이었다.
해당 reset 함수(?)를

.global reset        // 추가
 
reset:
 b start

위와 같이 수정한 결과, warning 는 발생하지 않았다.

링크 시에 undefined reference to `__divsi3` 에러가 발생하는 경우

링크 시에 아래와 같은 오류가 난다면,

stdio.o(.text+0x5f0):/opt/scvos/kernel/stdio.c:137: undefined reference to `__divsi3`
stdio.o(.text+0x608):/opt/scvos/kernel/stdio.c:138: undefined reference to `__modsi3`
...

아마도, 나눗셈과 관련한 라이브러리가 없어서 일 것이다. 실제로 오류 상에 표시된 소스코드를 보면, /, % 과 같은 나눗셈 연산이 사용된다.
다음과 같이 문제를 해결할 수 있다. 링커가 실행되는 Makefile 을 아래와 같이 수정한다.

SRCS = kernel_start.o kernel_main.o stdio.o serial.o
 
all:
	$(LD) $(KLDFLAGS) -o $(TARGET_KERNEL_ELF) $(SRCS) -L/korea-dokdo/aesoptool/gcc-3.3.4-glibc-2.3.3/arm-linux/lib/gcc-lib/arm-linux/3.3.4 -lgcc -lc   <<----- 다음을 추가한다
 
	$(OC) $(OCFLAGS) $(TARGET_KERNEL_ELF) $(TARGET_KERNEL_BIN)
	mv $(TARGET_KERNEL_ELF) $(TARGET_KERNEL_BIN) ../../image
 
clean:
	rm -f *.o

링크 시에 크로스 컴파일러의 라이브러리 경로를 추가시켜주면 된다.

시리얼로 문자가 출력되지 않을 때

putc() 라는 함수를 사용해서 uart 포트 출력 테스트를 해보려고 시도했지만, 번번히 실패했다. 기존의 vpos 의 설정을 그대로 했음에도 별다른 특이한 점을 발견하지 못했다. 몇 번의 삽질 끝에, 부트로더 쪽에서 문제가 있음을 발견했다. 아래와 같이 해결했다.

	mov	r1, #LOCKTIME
	ldr	r2, =vh_vMPLLCON_200  	
	str	r2, [r1, #vh_oMPLLCON]   

위의 부분은 CPU Core 의 클럭을 설정하는 부분이다. 바로 이 부분이 엉뚱한 값으로 설정되어 있었다. 이 설정이 문제가 된 이유는 uart 의 bit rate 를 설정할 때, cpu clock 을 이용해서 계산을 하기 때문이다. 그래서 정확히 맞지 않으면 시리얼 포트로 문자를 출력할 수 없다.

터미널의 문자가 깨지거나 갑자기(?) 출력이 되지 않을 때

uart 출력이 잘 되다가, 몇 가지 함수를 소스코드에 추가하고 컴파일을 했다. 물론 에러없이 바이너리 파일까지 생성했다. 그런데 문자 출력이 안된다. 디버깅 장비를 이용해서 확인해봐도 뚜렸한 문제가 보이지 않았다.
결국, 문제는 다른 곳에 있었다. 처음에 부트로더가 커널부분을 메모리 상으로 복사를 하는데, 그 주소범위를 너무 작게 잡은 것이었다.
처음에는 충분했으나, 소스 코드 크기가 증가하면서 전체 커널 코드의 일부분만 복사하는 문제가 발생한 것이다.
cpu.h 파일에서 다음을 수정한다.

#define BOOT_LOADER_SIZE (1024*20)   // 수정

복사 영역 사이즈를 수정했더니 문제는 해결되었다.

BIN 파일의 사이즈가 너무니 없이 클 때

elf 파일을 생성하고 arm-linux-objcopy 명령어를 사용해서 bin 파일을 생성하는데, 이때 bin 파일의 사이즈가 elf 파일에 비해서 크다면, 뭔가 문제가 있는 것이다. 원래 bin 파일은 elf 파일에서 elf 정보를 제거한 상태로 만들어지기 때문에 기본적으로 elf 파일 보다 사이즈가 작아야 한다.
내가 지금 까지 확인한 것은 소스 파일 내에서 전역변수로 선언하면, 이런 문제가 발생했다.
이런 문제를 해결하는 방법은 'static' 명령어를 붙여주는 것이다. 아래의 예제 코드 처럼 말이다.

#include <stdio.h>
 
int a = 0;    // 문제의 원인이 되는 부분
 
static int a = 0;     // 이렇게 수정하면, 원래대로 작은 사이즈의 bin 파일이 생성된다
 
int main(void)
{
int b;
b = a;
return 0;
}

최종 해결 방법

기존의 vpos 의 HAL_arch_startup.S 파일과 비교해본 결과, 현재 scv/os 에서 문제는 kernel_start.S 에서 각 모드별 스택의 사이즈 만큼을 모두 더한 크기로 bin 파일이 생성된다는 것을 알았다.
곰곰히 생각해보면, 이해가 가는 부분이다. flash 에 구울 bin 파일을 만들 때, 각 모드별 스택 공간을 남겨두어야 하기 때문에 이 공간을 감안하여 지정한 사이즈만큼 간격을 둬서 만든 것이다. 나름대로는 배려(?)를 해준다고 이렇게 해준 것이겠지만, 어짜피 flash 상에서 실행을 할 것이 아니기 때문에 또한 스택이 flash 상에 잡히는 것은 더더욱 아니기 때문에, 이렇게 할 필요가 없다. 오히려 아까운 flash 영역만 낭비하는 셈이 된다.
결국, 우리가 실행할 공간은 메모리(sd memory) 이기 때문이다.
일일이 비교해본 결과, kernel_start.S 파일에서 아래의 루틴이 문제가 되었다.

         /* 예전에는 아래의 사이즈 크기 만큼 bin 파일이 생성되었다 */
         .equ user_stack_size,	0x30000
	.equ	svc_stack_size,	0x10000
	.equ undef_stack_size,	0x10000
	.equ abort_stack_size,	0x10000
	.equ	irq_stack_size,	0x10000
	.equ	fiq_stack_size,	0x10000
 
          /* 바로 이 부분이 문제가 되는 부분이다 */
	.comm	user_stack, user_stack_size
	.comm	svc_stack, svc_stack_size
	.comm	under_stack, undef_stack_size
	.comm	abort_stack, abort_stack_size
	.comm	irq_stack, irq_stack_size
	.comm	fiq_stack, fiq_stack_size

다른 방법을 사용해야 한다. 먼저 .comm 명령어에 대해서 알아보면, 아래의 표와 같다.

이름 .comm
사용법 .comm symbol , lenght
설명 .comm 은 bss 섹션의 공통되는 영역을 선언한다. 보통은 ld 는 링크 작업을 할 동안 .comm 을 위해 메모리 주소를 예약하는 데, 부분적이 아닌 프로그램은 심볼의 위치를 정의한다. .comm 을 사용하면 ld 에게 적어도 lenght 만큼의 바이트가 필요함을 알릴 수 있다. ld 는 각각의 .comm 심볼을 위해서 적어도 링크된 부분적인 프로그램들의 어디에서나 .comm 이 요구한 크기 만큼을 할당할 것이다. length 는 절대적인 표현이다.

문제를 해결하기 위해서 아래와 같이 수정한다.

	.equ USERMODE, 		0x10
	.equ FIQMODE,    		0x11
	.equ IRQMODE,    		0x12
	.equ SVCMODE,    		0x13
	.equ ABORTMODE,  	0x17
	.equ UNDEFMODE,  	0x1b
	.equ MODEMASK,   	0x1f
	.equ NOINT,      		0xc0  
 
 
	.equ user_stack_size,	0x30600000   /* 수정 */
	.equ	svc_stack_size,	0x30800000   /* 수정 */
	.equ undef_stack_size,	0x31000000   /* 수정 */
	.equ abort_stack_size,	0x31000000   /* 수정 */
	.equ	irq_stack_size,	0x30900000   /* 수정 */
	.equ	fiq_stack_size,	0x31100000   /* 수정 */
 
	.global user_stack
	.global svc_stack
	.global under_stack
	.global abort_stack
	.global irq_stack
	.global fiq_stack
	.global kernel_reset
 
	.text
 
kernel_reset:
	b scv_start
	b scv_undefined_instruction
	b scv_swi_handler
	b scv_abort_prefetch
	b scv_abort_data
	b scv_not_used
	b scv_irq_handler
	b scv_fiq_handler
 
scv_start:
 
	/* stack area setting */
 
	/* first change svc mode */
	bic r0,r0,#MODEMASK|NOINT
    	orr r1,r0,#SVCMODE|NOINT
    	msr cpsr_cxsf,r1
 
	mrc	p15, 0, r1, c1, c0, 0
	orr	r1, r1, #0xc0000000
	mcr	p15, 0, r1, c1, c0, 0
 
 
	/* cache lock down */
	mrs	r0, cpsr
	orr	r0, r0, #0xc0
	msr	cpsr, r0
       mov     r0, #0
       mcr     p15, 0, r0, c7, c7, 0   
       mcr     p15, 0, r0, c8, c7, 0   
 
       mrc     p15, 0, r0, c1, c0, 0
       bic     r0, r0, #0x00002300     
       bic     r0, r0, #0x00000087     
       orr     r0, r0, #0x00000002    
       orr     r0, r0, #0x00001000   
       mcr     p15, 0, r0, c1, c0, 0
 
       mov     r0,#0x0
       mrc     p15,0,r0,c1,c0,0
       orr     r0,r0,#(1<<12)
       mcr     p15,0,r0,c1,c0,0
       NOP
       NOP
       NOP
	/* under mode */
	mrs r0,cpsr
    	bic r0,r0,#MODEMASK|NOINT
    	orr r1,r0,#UNDEFMODE|NOINT
    	msr cpsr_cxsf,r1                
    	ldr sp,=undef_stack_size         /* 수정 */
 
	/* abort mode */    
    	bic r0,r0,#MODEMASK|NOINT
    	orr r1,r0,#ABORTMODE|NOINT
    	msr cpsr_cxsf,r1               
    	ldr sp,=abort_stack_size         /* 수정 */
 
	/* irq mode */
    	bic r0,r0,#MODEMASK|NOINT
    	orr r1,r0,#IRQMODE|NOINT
    	msr cpsr_cxsf,r1              
    	ldr sp,=irq_stack_size          /* 수정 */
 
	/* fiq mode */
    	bic r0,r0,#MODEMASK|NOINT
    	orr r1,r0,#FIQMODE|NOINT
    	msr cpsr_cxsf,r1             
    	ldr sp,=fiq_stack_size          /* 수정 */
 
	/* svc mode */
    	bic r0,r0,#MODEMASK|NOINT
    	orr r1,r0,#SVCMODE|NOINT
    	msr cpsr_cxsf,r1            
    	ldr sp,=svc_stack_size          /* 수정 */
 
	/* user mode */
	bic r0,r0,#MODEMASK|NOINT
    	orr r1,r0,#USERMODE|NOINT    
    	msr cpsr_cxsf,r1           
    	ldr sp,=user_stack_size         /* 수정 */
 
    	b	SCV_Main
 
scv_undefined_instruction:
	b	HaltUndef
scv_swi_handler:
	b	HaltSwi
scv_abort_prefetch:
	b	HaltPabort
scv_abort_data:
	b	HaltDabort
scv_not_used:
	b	HaltNouse
scv_irq_handler:
	b	HaltIrq
scv_fiq_handler:
	b 	HaltFiq	

이제 아무 소스 코드에 평소와 같이 전역변수를 선언하고 bin 파일을 만들어보자! 사이즈가 어떤가?

정말 최종 해결 방법

위의 해결 방법의 효과는 그리 오래가지 않았다. 다른 소스 파일에서 전역 변수를 선언하자, 같은 문제가 또 발생했다.
여러가지 고심 끝에 정말 최종(?) 해결 방법을 알아낼 수 있었다.
문제는 elf 파일에서 bin 파일을 생성할 때 뿐만 아니라, 원래 부터 elf 파일을 만드는 데서 부터 문제 였다.
정상적인 elf 파일과 비정상적인 elf 파일을 비교해보면, 거의 2 배(1.7 배) 정도 더 큰 것을 볼 수 있다. 문제는 ld 의 옵션 때문이었다.
makefile 을 아래와 같이 수정한다.

...
KLDFLAGS = -p -X -T ./kernel-ld-script -Bstatic    // -Bstatic
...

추가한 옵션의 의미는 아마도 일일이 static 으로 선언을 해주어야만 문제가 해결되었던 것 처럼, 자동으로 static 으로 만들어주는(?) 것 같다.
이제 다시 컴파일 해보자! 어떤가? 이제 문제가 완전히(?) 해결되었다.

컴파일 시에 warning : type mismatch with previous implicit declaration 발생 시

이 경고(warning) 는 흔히 함수의 인자와 이 함수를 호출하는 부분에서의 인자가 같지 않을 때 발생하는 증상이다.
만일 코드를 비교해봐도, 특별한 문제점을 찾을 수 없다면, 함수의 body 부분을 호출하는 루틴 보다 위로 옮겨보자. 그리고 컴파일해보자!
나의 경우, 경고 메세지가 더이상 출력되지 않았다.

타이머 인터럽트가 발생하지 않는 경우

개인적을 이 문제 때문에 많은 시간을 소비(?)했었다. 결국 정말로 어이없는 곳에서 찾긴 했지만…
문제는 발단은 이렇다. 타이머를 동작하는 지 여부를 판단하기 위해, 타이머를 10 ms 마다 발생시키도록 설정하고, 간단한 인터럽트 루틴을 작성했었다.
하지만 어떻게 된 일인지 아무 것도 출력하지 않았다. 이상하다 싶어 t32 를 이용해서 확인해봤지만, 인터럽트 조차 발생하지 않았다.
인터럽트가 발생하면, 하드웨어적으로 0x18 번지로 점프를 해야 하지만, 이쪽에다가 아무리 breakpoint 를 지정해도 멈추지 않았다.
삼성에서 나온 2410 테스트 프로그램을 이용해서 테스트를 해보면, 제대로 원하는 결과값이 나옴에도 불구하고, 이상하게 scv/os 에서는 발생하지 않았다.
문제의 원인은 간단했다. 아래와 같이 타이머를 초기화 해주는 루틴이 있다고 하자.

void vh_timer_init(void)
{
	int timer_load_val;
	int TIMER = 14;
 
	vu_register_dev(0, "Timer", &timer_device_driver_fops, 14);
	vh_interrupt_unmask(TIMER);
 
	vh_rTCFG0 = 0x0f00;  
	vh_rTCFG1 = 0x0000;	
	timer_load_val = 50000000/(2*16*100); 
	vh_rTCNTB = timer_load_val;  
	vh_rTCON = (vh_rTCON & ~0x0700000) | 0x600000; 
	vh_rTCON = (vh_rTCON & ~0x0700000) | 0x500000; 
}

여기서는 여러개의 타이머 중에서 timer4 를 사용한다.

  1. 먼저 인터럽트 레지스터의 마스크를 해제 시킨다.
  2. timer4 에 대한 설정(카운터 버퍼값)을 하고 나서, 타이머를 시작시킨다.
  3. 타이머 카운터 값이 0 이 되면, 인터럽트가 발생하면서 자동으로 0x18 번지로 점프한다.

위의 루틴에서 빠진 것이 있다. '그것은 바로 CPSR 레지스터의 인터럽트 마스크를 해제하는 것이다. 내가 간과했던 것이 바로 이것이었다!!!'
해당 비트를 1 로 설정하면, IRQ 가 마스크되고, 0 으로 설정하면 언마스크 된다. 어셈코드를 이용해서 해당 비트를 0 으로 설정하면 된다.

앞의 내용을 다시금 정리하자면, 타이머 인터럽트를 발생시키기 위해서는 다음과 같은 절차가 필요하다.

  1. 먼저 인터럽트 레지스터의 마스크를 해제 시킨다.
  2. timer4 에 대한 설정(카운터 버퍼값)을 하고 나서, 타이머를 시작시킨다.
  3. CPSR 레지스터의 IRQ 비트를 0 으로 설정한다.
  4. 타이머 카운터 값이 0 이 되면, 인터럽트가 발생하면서 자동으로 0x18 번지로 점프한다.

Trace32 에서 타이머 인터럽트 잡아내기

기본적으로 타이머 인터럽트는 너무나 자주 발생하기 때문에, 디버깅 장비에서는 기본적으로 무시하도록 설정된다.
T32 역시 마찬가지이며, 이를 확인하기 위해서는 'Break → OnChip Trigger…' 를 선택한다.
창이 뜨는데, 여기서 왼쪽 아래를 보면 exception 항목들이 나열되어 있다. 여기서 체크가 된 것은 해당 exception 이 발생했을 경우, 실제로 해당 정해진 주소로 점프를 디버거가 따라가겠다는 것이다. 체크가 안된 것은 cpu core 에서 해당 exception 이 발생하더라도, 이를 무시하고 현재 디버깅을 계속하겠다는 의미이다.

만일 내가 타이머 인터럽트를 발생시키는 것을 확인하고 싶다고 할 때, 0x18 번지에 breakpoint 를 잡고 인터럽트가 발생하는 지를 보고 싶다면 IRQ 를 반드시 체크해줘야 한다.
IRQ 를 체크하지 않아도 확인할 수 있는 방법이 있는데, 그것은 0x18 이 아닌 remapping 된 SDRAM 영역에다가 breakpoint 를 잡으면 확인할 수 있다 .

Flash 에 write 한 후에, 보드를 부팅이나 리셋마다 실행결과가 달라질 때

참으로 난감한 경우가 아닐 수 없다. 플래시에 write 하고나서 점퍼셋팅을 바꾸고 보드 부팅하면, 내가 원하는 결과가 나온다. 하지만, 리셋버튼을 누르거나, 바로 스위치를 off 했다가 on 시키면 방금 전에 봤던 결과가 나오지 않는다. 문제는 맨 처음의 결과 값이 나올 때가 있고 안 나올 때가 있다는 것이다.
심지어는 점퍼 셋팅만을 바꿨다가 다시 원래대로 점퍼 셋팅을 하는 것만으로도 출력 결과가 달랐다.

보드의 전원 플러그를 완전히 뽑고, 몇 분을 기다린 후에 보드를 on 시키면, 처음에는 역시 내가 예상하는 결과가 나온다. 하지만, 그 이후로는 같은 문제가 반복된다.

나는 처음 하드웨어 문제로 착각했었다. 하지만, 기존의 vpos 나 리눅스의 경우에는 동일한 보드에서 제대로 동작하기 때문에 하드웨어 문제일 가능성은 거의 없다. 그렇다면, 결국 소프트웨어 문제다.
고민 끝에 나는 먼저 일단 현재 프로그램에서 경고(warning) 이 발생하는 것들을 모두 잡았다.
참고로 컴파일 옵션에서 '-Wall' 을 추가하면, 가장 자세하게 컴파일시 발생하는 모든 경고나 오류를 볼 수 있다.
모두 잡았더니, 놀랍게도(?) 문제가 해결되었다.

추가 사항

모든 에러를 잡았더니, SCV_Malloc() 함수 내의 freep 구조체의 값이 0xffffffff 으로 고정되었다.
예전 문제가 발생했을 때는 컴파일 시마다, 값이 바뀌는 문제가 있었다.

컨텍스트 스위칭이 제대로 안될 때

아래와 같이 두 개의 쓰레드가 번갈아 가면서 printf 문을 출력한다고 했을 때, 각각 쓰레드가 처음에 한번만 컨텍스트 스위칭이 되고 그 이후로 제대로 수행이 안되는 문제가 발생했다.

...
...
	pthread_create(&p_thread, NULL, Help1, (void *)NULL);
	pthread_create(&p_thread1, NULL, Help2, (void *)NULL);
...
...
void *Help1(void *arg)
{
	int i =0;
 
	for(i=0; i<10; i++)
	{
		printf("\nCommand Help11111111111\n");
	udelay(10000);
	}
 
  	return (void *)0;
}
 
void *Help2(void *arg)
{
 
	int j=0;
 
	for(j=0; j<10; j++)
	{
		printf("\nCommand Help2222222222\n");
		udelay(5000);
	}
 
	return (void *)0;
}

T32 를 이용해서 오랜 시간 끝에 원인을 찾을 수 있었다.

해결 방법

컨텍스트 스위칭의 핵심은 kernel_start.S 파일에 선언된 scv_swi_handler, scv_irq_handler, vh_restore_thread_ctx, vh_save_thread_ctx 함수들이다.
이 파일에 대한 자세한 설명은 다른 문서들을 참고하기 바란다. 여기서는 기존의 문서에 언급하지 않았던 내용만을 다루기로 한다.
먼저 버그가 존재하는 vpos 루틴부터 살펴보자!

vh_software_interrupt:
vh_entering_swi:
	str	sp, =vk_save_swi_mode_stack_ptr  // gcc 3.x 로 컴파일 시 에러
	stmfd	sp!, {r0-r14}^
	mrs	r0, spsr_all
	stmfd	sp!, {r0, lr}
	mrs 	r0, cpsr_all
	orr	r0, r0, #0x80
	msr	cpsr_all, r0
	str	sp, =vk_save_swi_current_tcb_bottom       // gcc 3.x 로 컴파일 시 에러
	ldr	r0, [sp, #8]
	bl	vk_swi_classifier
 
vh_leaving_swi:
	ldmfd	sp!, {r0, lr}
//	msr	spsr_all, r0
	ldmfd	sp!, {r0-r14}^
	movs	pc, lr
 
vh_irq:
vh_entering_interrupt:
	sub	lr, lr, #4
	str	sp, =vk_save_irq_mode_stack_ptr   // gcc 3.x 로 컴파일 시 에러
	stmfd	sp!, {r0-r14}^
	mrs	r0, spsr_all
	stmfd	sp!, {r0, lr}
	str	sp, =vk_save_irq_current_tcb_bottom     // gcc 3.x 로 컴파일 시 에러
	bl	vh_hwi_classifier
 
vh_leaving_interrupt:
	ldmfd	sp!, {r0, lr}
	msr	spsr_all, r0
	ldmfd	sp!, {r0-r14}^
	movs	pc, lr
 
vh_restore_thread_ctx:
	mrs	r1, cpsr_all
	bic	r1, r1, #0xfffffff0 
        mov	r2, #0x2
	cmps     r1, r2
	mov	sp, r0
	ldmfd	sp!, {r0, lr}
	msr	spsr_all, r0
	ldmfd	sp!, {r0-r14}^
	ldreq	sp, =vk_save_irq_mode_stack_ptr     // 주의 할 것
	ldrne	sp, =vk_save_swi_mode_stack_ptr     // 주의 할 것
	movs	pc, lr
 
vh_save_thread_ctx:
	mrs	r1, cpsr_all
	bic	r1, r1, #0xfffffff0 
        mov	r2, #0x2
	cmps     r1, r2
        ldreq	r2, =vk_save_irq_current_tcb_bottom   // 주의할 것
	ldrne	r2, =vk_save_swi_current_tcb_bottom   // 주의할 것
	.rept	17
	ldr	r1, [r2], #4
	str	r1, [r0], #4
	.endr
	mov	pc, lr
 
vk_save_swi_mode_stack_ptr:	    --------------------- 1
				.long 0
vk_save_swi_current_tcb_bottom:
				.long 0
vk_save_irq_mode_stack_ptr:	
				.long 0
vk_save_irq_current_tcb_bottom:
				.long 0

위의 루틴은 kernel_start.S 에 선언되어 있다. 코드의 주석을 잘 보기 바란다. 이제부터 하나씩 주석에 대한 설명을 해보이겠다.

주석 이유
gcc 3.x 로 컴파일 시 에러 기존의 vpos 의 경우, 2.95.3 을 제외한 상위 버전의 컴파일러의 경우, 에러를 발생시켰다. 예를 들어 3.2.2 버전으로 vpos 를 컴파일하려 한다면, 주석에 위치한 코드에 에러가 있다고 컴파일러가 알려줄 것이다
주의할 것 vpos 기존의 코드를 수정하지 않고, 3.2.2 버전으로 컴파일을 되지만, 컨텍스트 스위칭은 제대로 안될 것이다
1 컨텍스트 스위칭의 결정적인 역할을 해주는 부분으로서, 각 모드에 대한 spsr 이나 r13 레지스터의 값을 저장한다

1 에 대해서 부연적인 설명을 덧붙이자면, 저 루틴들은 컨텍스트 스위칭에서 각 모드의 특정 레지스터의 값들을 저장하고 이를 넘겨주는 역할을 한다. 이 버그의 결론 부터 얘기하자면, 버그의 원인은 코드에서 저장하는 주소와 이 값을 넘겨받는 주소가 다르면서 발생한다. t32 로 디스어셈블링 해서 보는 것이 이해가 빠를 것이다.
위의 표에서 상위 버전의 컴파일러로 컴파일시에 에러가 발생한다고 했다.
scvos 의 경우, 컴파일러 버전이 3.2.2 이기 때문에 이로 부터 발생하는 에러를 없애기 위해서 아래와 같이 코드를 수정했다.

vh_software_interrupt:
vh_entering_swi:
	str	sp, vk_save_swi_mode_stack_ptr  // 코드 수정
	stmfd	sp!, {r0-r14}^
	mrs	r0, spsr_all
	stmfd	sp!, {r0, lr}
	mrs 	r0, cpsr_all
	orr	r0, r0, #0x80
	msr	cpsr_all, r0
	str	sp, vk_save_swi_current_tcb_bottom       // 코드 수정
	ldr	r0, [sp, #8]
	bl	vk_swi_classifier
 
vh_leaving_swi:
	ldmfd	sp!, {r0, lr}
//	msr	spsr_all, r0
	ldmfd	sp!, {r0-r14}^
	movs	pc, lr
 
vh_irq:
vh_entering_interrupt:
	sub	lr, lr, #4
	str	sp, vk_save_irq_mode_stack_ptr   // 코드 수정
	stmfd	sp!, {r0-r14}^
	mrs	r0, spsr_all
	stmfd	sp!, {r0, lr}
	str	sp, vk_save_irq_current_tcb_bottom     // 코드 수정
	bl	vh_hwi_classifier
 
vh_leaving_interrupt:
	ldmfd	sp!, {r0, lr}
	msr	spsr_all, r0
	ldmfd	sp!, {r0-r14}^
	movs	pc, lr
 
vh_restore_thread_ctx:
	mrs	r1, cpsr_all
	bic	r1, r1, #0xfffffff0 
        mov	r2, #0x2
	cmps     r1, r2
	mov	sp, r0
	ldmfd	sp!, {r0, lr}
	msr	spsr_all, r0
	ldmfd	sp!, {r0-r14}^
	ldreq	sp, =vk_save_irq_mode_stack_ptr    
	ldrne	sp, =vk_save_swi_mode_stack_ptr     
	movs	pc, lr
 
vh_save_thread_ctx:
	mrs	r1, cpsr_all
	bic	r1, r1, #0xfffffff0 
        mov	r2, #0x2
	cmps     r1, r2
        ldreq	r2, =vk_save_irq_current_tcb_bottom   
	ldrne	r2, =vk_save_swi_current_tcb_bottom   
	.rept	17
	ldr	r1, [r2], #4
	str	r1, [r0], #4
	.endr
	mov	pc, lr
 
vk_save_swi_mode_stack_ptr:	   
				.long 0
vk_save_swi_current_tcb_bottom:
				.long 0
vk_save_irq_mode_stack_ptr:	
				.long 0
vk_save_irq_current_tcb_bottom:
				.long 0

컴파일을 하기 위해서 임기응변(?)으로 주석이 붙은 곳의 코드를 수정한 것이 빌미가 되어 이번 버그를 발생시킨 셈이다. 단지 '=' 차이로 인해서 말이다.
t32 디버거로 확인을 해보면, 엉뚱한 주소로 sp 레지스터의 값을 저장하는 것을 볼 수 있다.
이제 버그가 패치된 코드를 볼 차례다.

.equ bottom, 0x30000000
...
...
scv_swi_handler:
	mov r3, #bottom                // 추가
	str	sp, [r3, #0x234]      // 추가
	stmfd	sp!, {r0-r14}^       
	mrs	r0, spsr_all
	stmfd	sp!, {r0, lr}
	mrs 	r0, cpsr_all
	orr	r0, r0, #0x80
	msr	cpsr_all, r0
	mov r3, #bottom               // 추가
	str	sp, [r3, #0x238]     // 추가
	ldr	r0, [sp, #8]
	bl	vk_swi_classifier
 
vh_leaving_swi:
	ldmfd	sp!, {r0, lr}
//	msr	spsr_all, r0
	ldmfd	sp!, {r0-r14}^
	movs	pc, lr
 
scv_irq_handler:
	sub	lr, lr, #4
	mov	r3, #bottom       // 추가
	str	sp, [r3, #0x23c]  // 추가
	stmfd	sp!, {r0-r14}^
	mrs	r0, spsr_all
	stmfd	sp!, {r0, lr}
	mov	r3, #bottom       // 추가
	str	sp, [r3, #0x240]  // 추가
	bl	vh_hwi_classifier
 
vh_leaving_interrupt:
	ldmfd	sp!, {r0, lr}
	msr	spsr_all, r0
	ldmfd	sp!, {r0-r14}^
	movs	pc, lr
 
vh_restore_thread_ctx:
	mrs	r1, cpsr_all
	bic	r1, r1, #0xfffffff0 
        mov	r2, #0x2
	cmps     r1, r2
	mov	sp, r0
	ldmfd	sp!, {r0, lr}
	msr	spsr_all, r0
	ldmfd	sp!, {r0-r14}^
	mov	r3, #bottom      // 추가
	ldreq	sp, [r3, #0x23c]    // 추가
	ldrne	sp, [r3, #0x234]    // 추가
 
	movs	pc, lr
 
vh_save_thread_ctx:
	mrs	r1, cpsr_all
	bic	r1, r1, #0xfffffff0 
        mov	r2, #0x2
	cmps     r1, r2
	mov	r3, #bottom        // 추가
	 ldreq	r2, [r3, #0x240]   // 추가
	 ldrne	r2, [r3, #0x238]   // 추가
	.rept	17
	ldr	r1, [r2], #4
	str	r1, [r0], #4
	.endr
	mov	pc, lr
 
vk_save_swi_mode_stack_ptr:
				.long 0
vk_save_swi_current_tcb_bottom:
				.long 0
vk_save_irq_mode_stack_ptr:	
				.long 0
vk_save_irq_current_tcb_bottom:
				.long 0

추가한 부분을 잘 보기 바란다. 부득이 하게, 기존의 코드에서 해당 명령어들을

str	sp, =vk_save_swi_mode_stack_ptr  --- 2
ldreq	sp, =vk_save_irq_mode_stack_ptr  --- 3
ldrne	sp, =vk_save_swi_mode_stack_ptr  --- 4
번호 설명
2 sp 레지스터의 값을 vk_save_swi_mode_stack_ptr 가 가리키는 메모리 주소에 저장
3 앞에서 cmp 명령어의 결과가 참이라면, vk_save_irq_mode_stack_ptr 가 가리키는 메모리 주소에 저장된 값을 sp 에 저장
4 앞에서 cmp 명령어의 결과가 거짓이라면, vk_save_swi_mode_stack_ptr 가 가리키는 메모리 주소에 저장된 값을 sp 에 저장

각각 바꾼 것들이다. 비록 1 line 이 늘어나긴 했지만, 문제없이 동작했다.
만일 앞에서 비슷한 버그가 발생한다면, 주소값을 비교해봄으로서 디버깅할 수 있다.