OS를 만듭시다. 어때요~ 참 쉽죠? (3)

나빌레라의 이미지

OS. 영어로 풀어서 Operating System. 한자어로 번역해서 운영체제. 이것을 만든다고 하면 사람들은 굉장히 어렵게 생각한다. 리눅스나 윈도우같은 OS를 비교대상으로 본다면 OS를 만드는것은 정말 어렵고 힘들고, 개인이 만들기엔 어쩌면 불가능에 가까운 도전일지도 모른다. 하지만 OS의 기본 개념들은 대학교 학부과정에서 가르칠 정도로 이미 보편화 되어 있고 그 개념 자체들은 그다지 어렵지 않다. 개념 구현을 중심으로 동작하는 것 자체에 의미를 둔 OS를 만드는 것은 어쩌면 도전 해 볼만 한 가치가 있는 시도가 아닐까.

3. ARM exception vector table 구성하기

ARM프로세서에는 7가지의 Exception Mode가 존재 한다. ARM프로세서는 메모리주소 0x00000000 번지를 이 7가지 Exception 을 처리하기 위한 벡터 테이블로 예약해 놓고 있다. 7가지 Exception 은 다음과 같다.

이름 벡터주소
Reset 0x00000000
Undefined instruction 0x00000004
Software Instruction 0x00000008
Prefetch Abort 0x0000000c
Data Abort 0x00000010
Reserved 0x00000014
Interrupt Request 0x00000018
Fast Intrrupt Request 0x0000001c

각 Exception 이 어떨때 발생하는 지는 인터넷을 찾아보면 아주 많은 아주 잘 정리된 자료가 많이 나온다. 여기서 필자가 다시 정리할 필요가 없을 정도로. 우리가 알아야 할것은 저것이 과연 무엇을 위해 존재 하는 가 이다.

저 표 자체를 벡터 테이블이라고 부른다. 즉, ARM프로세서는 위 표에 해당하는 7개의 Exception 중 하나가 발생하면 자동으로 옆에 있는 벡터주소에 명시된 주소로 PC를 점프 한다. 예를 들면 프로그램 실행도중 IRQ가 발생하면 당장에 0x00000018 번지로 점프한다는 것이다.

벡터주소를 잘 보면 각 주소가 4바이트씩 떨어져 있음을 알 수 있다. 알다 시피 ARM에서 어셈블리 명령어 하나는 각각 4바이트를 차지 한다. 그러면 저 벡터테이블에는 각 Exception 별로 명령어 한개 씩 밖에 들어가지 못한다는 것이다. 그래서 보통 저 벡터 테이블에는 브렌치 명령이 들어가 있고, 브렌치해서 다시 점프해간 위치에 제대로 된 Exception Handling 코드가 들어가 있다.

그럼 우리가 수정해야 할 이지부트의 소스를 보자, 이지부트 소스의 start 디렉토리에 보면 start.S 가 있다. 이 어셈블리 소스 코드가 이지부트의 엔트리 포인트. 즉 0x00000000 번지부터 작성된 코드이다. 주석이 좀 있고, 바로 아래에 위에서 서술한 Exception 벡터 테이블이 보일 것이다.

.globl      _start

_start:	     b   reset      
       		 b   undefined_instruction 
     		 b   software_interrupt    
        	 b   prefetch_abort        
        	 b   data_abort            
        	 b   not_used              
        	 b   IRQ                   
        	 b   FIQ

이지부트는 7개의 Exception 을 모두 처리하지 않는다. 즉, 어떤 Exception 이 발생하건 간에 그냥 보드를 리셋 해 버린다. 이것은 이지부트 자체가 리눅스를 올리기 위해 최적화된 부트로더 이고, 리눅스가 올라가서 부팅이 되고 리눅스 부트코드가 실행되고 나면 Exception 벡터 테이블이 재설정되기 때문이다. 하지만 나빌눅스는 리눅스가 아니다. 즉 할수 있다면 부트로더에서 최대한 많은 것을 해주면 해줄 수록 커널에서 해야 할일이 많이 줄어 든다. 그럼 계속 해서 코드를 훑어 보자

reset:
		... 어쩌고 저쩌고 ...
data_abort:
        mov r5, #DEBUG_DATA_ABORT
        bl  led_out
        b   error_loop
undefined_instruction:
software_interrupt:
prefetch_abort:
not_used:
IRQ:
FIQ:
        mov r5, #DEBUG_OTHER_EXCEPT
        bl  led_out
        b   error_loop

같은 소스 파일의 아래쪽에 보면 Exception 벡터테이블로 부터 분기되어 가는 목표지점들의 코드가 보인다. 이지부트에서는 reset 외에는 별다른 처리를 해주지 않고 있다. 즉 어떤 Exception 이 발생하더라도 결국 error_loop 부분으로 점프해서 reset되어 버린다. 나빌눅스에서 사용할 Exception 은 software_interrupt 와 IRQ 두개이다. software_interrupt 는 SWI 명령을 사용하여 시스템콜 및 컨텍스트 스위칭등을 구현하는데 사용하고, IRQ 는 외부 장치들의 인터럽트 처리, 내부 타이머의 인터럽트 처리등에 사용 할 것이다. IRQ와 FIQ의 차이에 대해서도 여기서 굳이 설명하지 않겠다. 인터넷에 찾아보면 훌륭한 문서들이 아주 많이 있다.

위 코드에 있는 software_interrupt 레이블과 IRQ레이블의 아랫부분에 인터럽트 핸들러 코드를 써 주면 된다. 하지만 여기서 생각 해 보아야 할 것이 있다. 부트로더를 이지보드의 플래시 메모리에 쓸(writing)할려면 JTAG를 이용해서 라이팅 해야 한다. 이지부트의 크기는 몇십Kbyte 정도이다. 하지만 그것을 JTAG로 쓰는데는 대략 40분정도가 걸린다. 만약 인터럽트 핸들러 부분에 오타라던지 로직 에러가 있어서 수정하고 다시 JTAG로 써야 한다면... 매번 할때마다 40분이 걸리게 된다. 아무리 최대한으로 생각해 봤자 하루종일 밥도 안먹고 잠도 안자고 씻지도 않고 코딩하고, 컴파일하고, JTAG writing 한다 해도 30번정도 밖에 하지 못한다.

앞에서 썻듯이 부트로더는 최소한의 수정만 해서 사용한다고 했다. 부트로더를 자주수정하는 것은 심한 정신적 피로를 가져다 준다. 방법은 없을까...

조용히 눈을 감고 학부컴퓨터구조수업때 (안배웠다면 할 수 없다.) 배운 내용을 떠 올려 보자. CPU 입장에서 프로그램이란 OS 건 펌웨어건 어플이건 관계없이 그냥 메모리상에 존재하는 인스트럭션의 연속일 뿐이다. 그것들이 CPU로 불러들여 져서 CPU는 명령을 해석 처리하고, 필요에 따라 결과를 메모리에 쓰기도 한다.

그럼 부트로더와 커널간의 관계를 생각 해보자. ARM 입장에서는 부트로더건 커널이건 관계없이 그냥 명령어 집합일 뿐이다. MMU를 사용하지 않는 이상 ARM 입장에서는 명령어가 메모리 주소 어디에 존재하건 간에 상관없이 불러들여서 실행 할 수 있다. 즉, PC(Program Counter)는 접근가능한 메모리 주소값 어디라도 값으로 가질 수 있다.

필자의 마음을 헤아리는 사람이라면 여기서 무릎을 탁 치며 앞으로 어떻게 Exception Handler를 구현할지 알아 낼 것이고 어떤 분은 아직도 어떻게 할지 예상이 안될 것이다. 괜찮다. 설명 할 것이다.

대 전제는 Exception Handler 의 코드수정과 부트로더의 리빌딩을 상호간에 독립시키는 것이다. 즉, Exception Handler의 코드를 바꾼다 하더라도 이미 빌드해서 JTAG를 통해 플래시메모리에 올라가 있는 부트로더 코드는 수정하지 않아야 한다는 것이다. 그럴려면 어떻게 해야 하는가. Exception Handler 의 코드를 수시로 리빌드 해서 테스트해보는 커널코드 안에 넣는 것이다. 위에서도 썻듯이 ARM 은 실행하는 코드가 부트로더영역에 있는지 커널영역에 있는지를 구분하지 않는다. 다만 PC가 가르키고 있는 주소에 있는 명령어를 실행 할 뿐이다.

부트로더 쪽에서 Exception Vector Table 로 부터 넘어온곳에서 다시 커널영역주소로 브랜치를 해준다. 그리고 커널 영역쪽에서 Exception Handler 코드를 써주면 된다. 하지만 부트로더 쪽에서 넘어가는 커널영역의 Exception Handler의 주소 역시 불변이어야 한다. 그쪽의 주소가 바뀌면 부트로더쪽에서 브랜치하는 주소를 바꿔주고 부트로더를 다시 빌드한다음 JTAG로 써야 하기 때문이다. 그러면 커널영역으로 가서 브랜치를 한번 더 해준다. 그렇게 해서 두번점프해서 도착한곳에서야 비로소 핸들러 코드가 위치 한다.

위 한 단락을 읽고 나면 대부분의 사람들은 이렇게 생각하거나 중얼 거릴 것이다. '뭔소리야...' 자! 이제 코드가 나간다. 누가 말했지 않는가. 개발자는 코드로 말한다고...

우선 커널(이라기엔 아직은 뭐 하지만..)쪽 소스를 보자. main.c 였는데 이번회 부터 이름을 navilnux.c 로 바꾸었다.

#include 

void swiHandler(unsigned int syscallnum)
{
    printf("system call %d\n", syscallnum);
}

void irqHandler(void)
{

}

int main(void)
{
    __asm__("swi 77");
    return 0;
}	

잡다구레한 헤더파일들은 navilnux.h 에 모두 통합해서 넣어 놨다. 지난 강좌에 썻던 코드가 바뀌었다. LED를 껏다 켯다 하던 무한루프는 사라졌고, 인라인 어셈블리 명령한줄만 덩그러니 main() 함수에 있다. 그리고 두개의 함수가 추가 되었다. 이름만 봐도 알 것이다. 하나는 Software interrupt 의 핸들러 함수이고, 하나는 IRQ 핸들러 함수이다. 부트로더에 있는 Exception Vector Table로 부터 시작한 예외처리 루틴은 마지막으로 이 두함수로 들어가서 우리가 의도하는 동작을 수행 하게 되는 것이다.

다음은 entry.S 를 보자. 커널의 시작 코드는 entry.S 라고 하였다. 그렇다면 다른 부분의 코드 수정에 의해 링크된 명령어의 주소 변화가 가장 작은 부분역시 entry.S 일것이다. 점점 베일이 밝혀 지고 있다.

.globl _ram_entry
_ram_entry:
    bl  main
	b	_ram_entry
    b   Core_swiHandler
    b   Core_irqHandler


.global Core_swiHandler
Core_swiHandler:
    bl      swiHandler

.global Core_irqHandler
Core_irqHandler:    
    bl      irqHandler

의미상의 커널 시작은 navilnux.c 에 있는 main() 함수 인데 실질적인 시작은 entry.S 에 있는 _ram_entry 레이블이다. 그걸 어떻게 아느냐.. 전회에 나왔던 main-ld-script 파일의 내용을 다시 한번 보자

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_ram_entry)
SECTIONS
{
	. = 0xA0008000;

	. = ALIGN(4);
	.text : { *(.text) }

	. = ALIGN(4);
	.rodata : { *(.rodata) }

	. = ALIGN(4);
	.data : { *(.data) }

	. = ALIGN(4);
	.got : { *(.got) }

	. = ALIGN(4);
	.bss : { *(.bss) }
}

세번째 줄에 ENTRY(_ram_entry) 라고 써 있는 것이 보일 것이다. _ram_entry 레이블에 해당하는 위치를 섹션 시작 주소 0xA0008000 으로 링킹한다는 의미이다. 그럼 다시 entry.S 를 보자.

시작하자마자 main 이라는 레이브로 브랜치 한다. 저 main 이란 레이블은 바로 navilnux.c 에 있는 main() 함수이다. 어셈블리에서 C언어로 된 코드로 들어갈때는 저렇게 bl 명령을 사용해서 함수이름을 레이블로 사용하면 된다. 실제 OS 는 main() 함수로 진입한 다음에 무한루프를 돈다. main()함수가 리턴한다는 것은 곧 OS 가 종료된다는 의미이다. 종료되는 OS 봤는가? (강제종료나, OS 끄는것 말고...) 그래서 어떤식으로든 main() 함수가 리턴해서 종료 하게 되면 다시 _ram_entry 로 뛰어서 main() 함수를 다시 실행하는 브랜치 명령이 바로 아래 있고, 이후 두 줄이 부트로더로 부터 점핑해서 도착하는 곳이다. 이름만 봐도 역시나 뭔지 알 것이다.

Software_interrupt 예외 핸들러가 브랜치해서 도착하는 곳은 b Core_swiHandler 이고, IRQ 예외 핸들러가 브랜치 해서 도착하는 곳은 b Core_irqHandler 이다. 이곳에서 한번더 브랜치 해서 아랫쪽에 실제로 존재 하는 핸들러 코드가 있다.

왜 이렇게 두번 뛰어야 하느냐는 위에서 아주 이해 안가게 설명했다. 다시 설명하자면, b Core_swiHandler가 있는 곳에 Software_interrupt 의 핸들러 코드를 넣어도 상관은 없다. 하지만 OS를 만들면서 예외 핸들러 코드를 한번에 뚝딱 완성 하고 다시는 수정이나 디버그를 안하게 코드를 짜는 개발자는 지구상에 손가락에 꼽을 정도로 적을 것이다. 어쩌면 아예 없을 수도 있다. 그래서 Software_interrupt 핸들러의 코드를 수정하게 되면, 특히 명령어를 삽입하거나 삭제 하게 되면 아래 쪽에 있는 b Core_irqHandler의 위치. 즉 IRQ 핸들러가 점핑하게 될 명령어의 주소가 계속 바뀌게 된다. 바뀌게 되면 부트로더를 수정해야 한다. 그러면 40분을 낭비한다.

그래서 부트로더에서 점핑하는 커널 영역의 주소를 고정하기 위해 저곳에 다시 벡터테이블 처럼 브렌치명령만 넣어놓고, 아랫쪽에 주소가 변경되어도 상관없는 지점에 실제 코드를 넣어 놓는 것이다.

일단 저렇게 코드를 수정하고 빌드를 한다. 파일명이 좀 바뀌었으므로 Makefile 도 바뀌었다. 많이 바뀌진 않았다. 그래도 어딜 고쳐야 할지 모르는 독자들을 위해 Makefile 코드는 다음과 같이 바뀐다.

CFLAGS   += -mcpu=xscale -mshort-load-bytes -msoft-float -fno-builtin

LDFLAGS   = -static -nostdlib -nostartfiles -nodefaultlibs -p -X -T ./main-ld-script
                
OCFLAGS = -O binary -R .note -R .comment -S

all: navilnux.c
	$(CC) -c $(CFLAGS) -o entry.o entry.S
	$(CC) -c $(CFLAGS) -o gpio.o gpio.c
	$(CC) -c $(CFLAGS) -o time.o time.c
	$(CC) -c $(CFLAGS) -o vsprintf.o vsprintf.c
	$(CC) -c $(CFLAGS) -o printf.o printf.c
	$(CC) -c $(CFLAGS) -o string.o string.c
	$(CC) -c $(CFLAGS) -o serial.o serial.c
	$(CC) -c $(CFLAGS) -o lib1funcs.o lib1funcs.S
	$(CC) -c $(CFLAGS) -o navilnux.o navilnux.c
	$(LD) $(LDFLAGS) -o navilnux_elf entry.o gpio.o time.o vsprintf.o printf.o string.o serial.o lib1funcs.o navilnux.o 
	$(OC) $(OCFLAGS) navilnux_elf navilnux_img

clean:
	rm *.o
	rm navilnux_elf
	rm navilnux_img

make 를 실행하면 빌드가 된다. 부트로더를 수정해야 하고, 수정을 최소화 해야 한다면서 아직까지도 부트로더는 건들지도 않고 왜 다짜고짜 커널을 빌드 하는가. 다 이유가 있다. 필자를 믿어라. 일단 빌드가 끝나면 navilnux_img 와 navilnux_elf 가 생긴다. 아래와 같은 명령으로 커널의 역어셈블 한 결과를 본다.

$ arm-linux-objdump -D navilnux_elf | more

arm-linux-objdump 는 이전강좌에서 설치했던 arm-linux-gcc 패키지 안에 포함되어 있는 유틸리티 이다. elf 형식으로 된 파일을 입력으로 받아서 역어셈블한 결과를 출력으로 보여준다. 별것도 없는 프로그램이지만 그래도 어셈블리어로 보면 그 양이 꽤 되므로 more 로 한페이지씩 보자. 필요한 정보는 첫부분에 있으므로 more 는 필요하다.

navilnux_elf:     file format elf32-littlearm

Disassembly of section .text:

a0008000 <_ram_entry>:
a0008000:       eb0006a3        bl      a0009a94 
a0008004: eafffffd b a0008000 <_ram_entry> a0008008: ea000000 b a0008010 a000800c: ea000000 b a0008014 a0008010 : a0008010: eb00069a bl a0009a80 a0008014 : a0008014: eb00069d bl a0009a90 : :

역어셈블 출력 화면이다. 앞에는 해당 명령이 위치하는 메모리 주소, 그다음에는 해당명령의 기계어, 그 다음에는 해당 명령의 어셈블명령어, 그다음에는 레이블의 명칭이 괄호 안에 나온다.

첫줄을 보면 주소가 0xA0008000 에서 시작함을 알 수 있다. 강좌내내 그렇게 떠들어 댔던 엔트리 포인트의 주소가 제대로 지정되었음을 알 수 있다. 그리고, b Core_swiHandlerb Core_irqHandler 가 각각 0xA0008008 번지와 0xA000800C 번지에 위치함을 알 수 있다. 부트로더는 건들지도 않고, 커널을 먼저 빌드한건 바로 이 두개의 주소를 얻어내기 위함이었다. 물론 굳이 이렇게 빌드해서 역어셈블 하지 않고, 순수하게 머리로 계산해서 "음.. 시작은 0xA0008000 이고, 그 아래 아래 명령인데 b 명령은 크기가 4바이트니깐, 아래 아래는 8바이트 그담 4바이트니깐 각각 0xA0008008, 0xA000800C 겠군.." 하고 계산해 낼 수도 있다. 하지만 어느정도 개발을 해본 개발자라면 알 것이다. 저렇게 계산에 의존해서 제대로 동작한 경험은 실패한경험에 비하면 없다라고 말하는게 좋을 만큼 미비하다.

이제 점프해야 할 주소를 알았으니깐 드디어! 부트로더를 수정한다. 아래와 같이 수정 한다.

data_abort:
        mov r5, #DEBUG_DATA_ABORT
        bl  led_out
        b   error_loop

undefined_instruction:
software_interrupt:
        ldr r0, =0xa0008008 
        mov pc, r0
prefetch_abort:
not_used:
IRQ:
        ldr r0, =0xa000800c
        mov pc, r0
FIQ:
        mov r5, #DEBUG_OTHER_EXCEPT
        bl  led_out
        b   error_loop

software_interrupt 레이블과 IRQ 레이블 아랫부분에 정말 지금까지 저걸 설명 하려고 이렇게 장문의 글을 썻나 싶게 달랑 두줄씩 넣었다. 0xA0008008 과 0xA000800C 로 점프하는 명령이다. 주소값의 크기가 꽤 커서 직접 b 명령의 인자로는 넣지 못하고 r0 레지스터에 넣어서 pc 에 값을 mov 하는 형식으로 점프한다. 위와 같이 이지부트 소스를 고치고 빌드한다음, JTAG을 연결하고 JTAG을 이용해서 보드에 writing 한다 라고 쓰고 '굽는다' 라고 읽는다.

그렇게 보드에 부트로더가 올라가고 나면 전회강좌에 썻던 것 처럼 zfk를 이용해 커널 이미지를 로딩하고 재부팅 해본다. 뭔가 부팅을 하고 나면 아래와 같은 메시지가 터미널에 찍힐 것이다.

system call 2684387336

메시지 뒤에 찍히는 숫자는 신경쓰지 말자. 아마 쓰레기 값 찍혔을 것이다. 어찌 되었던 main 함수에서는 swi 명령을 사용해서 software_interrupt를 발생시켰고, 그로 인해 ARM 프로세서는 Exception vector table 에 있는 브렌치 주소로 점프를 했고, 그 자리에는 다시 0xA0008008 로 뛰라는 코드가 있고, 0xA0008008로 갔더니 다시 밑에 있는 Core_swiHandler 로 가라고 하고, 갔더니 swiHandler()함수로 가라고 하고, 갔더니 printf() 로 뭘 찍으라 해서 찍고 났더니 리턴하고 갈곳을 잃어서 커널이 멈춰 버렸다. 아무튼 swi 명령을 이용한 software_interrupt 발생 및 핸들러 사용은 성공한것이다.

다음 회에는 software_interrupt 를 발생시키고 나서 커널이 멈춰버리는 것이 아니라 계속해서 돌 수 있게끔 코드를 수정 해 보겠다.

이 글은 http://raonlife.com/navilera/blog/view/77/에 동시 연재 됩니다.

댓글

jachin의 이미지

잇힝... 저보다 훨씬 더 대단하세용. T^T 나빌옹 쵝오!
====
( - -)a 이제는 학생으로 가장한 백수가 아닌 진짜 백수가 되어야겠다.

나빌레라의 이미지

아핫~
야친옹.. 읽어주셔서 감사합니다..(굽신굽신..)

글이 내용이 길어서 자세한것 같긴 하지만 뭔가 설명을 어렵게 한것 같아서

맘에 안들어요...ㅠ.ㅠ

-----
얇은 사 하이얀 고깔은 고이 접어서 나빌레라

----------------------
얇은 사 하이얀 고깔은 고이 접어서 나빌레라

쑈를하라의 이미지

앞으로도 좋은 내용 많이 부탁드립니다.

bookgekgom의 이미지

진짜 OS 를 만들수있는겁니까?

아니면 제가 초보라서 진짜 뜻을 이해못하는겁니까?

---------------------------------------------------------------------

허접한 페도라 가이드 http://oniichan.shii.org

---------------------------------------------------------------------------------------------------------------
루비 온 레일즈로 만들고 있는 홈페이지 입니다.

http://jihwankim.co.nr

여러 프로그램 소스들이 있습니다.

필요하신분은 받아가세요.

나빌레라의 이미지

만들 수 있어요..

허접하긴 하지만... 분명 OS로 불리우기 초큼 부끄러운 정도 수준의 OS는 만들어 집니다.

제 강좌는 저도 같이 코딩을 하면서 쓰고 있기 때문에 같이 코딩하면서 따라 오시면

마지막에 OS를 볼 수 있을겁니다...^^;

------
얇은 사 하이얀 고깔은 고이 접어서 나빌레라

----------------------
얇은 사 하이얀 고깔은 고이 접어서 나빌레라

bookgekgom의 이미지

대단하군요.

처음부터 보기로하곘습니다.

물론 이해는 못합니다.

------------------------------------------------------------

허접한 페도라 가이드 http://oniichan.shii.org

---------------------------------------------------------------------------------------------------------------
루비 온 레일즈로 만들고 있는 홈페이지 입니다.

http://jihwankim.co.nr

여러 프로그램 소스들이 있습니다.

필요하신분은 받아가세요.

namhw의 이미지

강좌 잘 보고 있습니다. 그런데 부트로더를 수정하지 않고 직접 메모리상에 Interrupt Vector Table을 수정하면 안될까?라는 생각으로 어셈코드를 몇 줄 추가해서 테스트해보는데 잘 안되네요. 그래서 검색을 하다보니 http://kkamagui.tistory.com/324 와 같은 일을 제가 하려고 하는것 같은데요/-_-;;; 단순히 str 명령어로 0x00000008번지에 원하는 swiHandler로 점프하는 opcode를 박아놓을려고 하는데 안될까요? 그러면 부트로더를 수정해서 업로드하지 않아도 되니 좋을것 같습니다. 그럼 조언 부탁 드리겠습니다.

촌놈.

촌놈.

나빌레라의 이미지

저도 맨처음 나빌눅스 코딩할때 그생각을 했었는데요.

0x0000 0000 영역은 flash 영역입니다.

그냥 str 명령으로 write 를 시도하면 당연히 되지 않습니다.

flash 에 어떤 값을 쓸려면 flash 컨트롤러에 관련되는 아주 복잡미묘한 작업을 해야 하기 때문이지요.

MMU 를 통해서 가상메모리로 Exception vector 영역을 어떻게 remapping 하면 된다는 소리도 들은것(본것) 같은데, MMU 자체를 이해 못해서 포기하고,

결국 그냥 부트로더 수정해버렸죠..

이와 같은 경우엔 부트로더 수정이 가장 쉬운 방법이니깐요.

언제가 가장 쉬운 방법으로만 코딩하는 나빌레라 였습니다...^^;

답변이 되었으면 좋겠네요.
----
얇은 사 하이얀 고깔은 고이 접어서 나빌레라

----------------------
얇은 사 하이얀 고깔은 고이 접어서 나빌레라

namhw의 이미지

역시 쉬운 작업이 아니었군요.^^;;; 어셈블러 메뉴얼 보면 삽질하다가 포기하고 있었습니다. 그냥 강좌대로 부트로더를 수정해서 해야겠네요~

촌놈.

촌놈.

ohhara의 이미지

혹시 모르는 사람을 위해 약간의 보충설명...

arm에서 instruction을 직접 write해서 수정하기 위해서는 약간의 과정이 필요합니다.

일단 보통의 경우에는 data cache와 instruction cache가 on인 상태에서 프로그램을 실행합니다. (default는 cache가 off이지만 보통 프로그램을 작동시킬 때 빠르게 작동하도록 보통 cache를 on합니다.)

그런 이유로 일단 0x00000000에 write를 하게 되면 실제 memory에 write되지 않고 data cache에 write됩니다. 그리고 instruction cache상에서는 data cache에 write된 내용이 보이지가 않습니다. 그렇기 때문에 data로 write한 내용을 instruction으로 인식해서 보이게 만들어 줘야 되는데 그렇게 하기 위해서는 몇가지 작업이 필요합니다.

1) 0x00000000에 write한 내용이 data cache에 남아 있을텐데 이것을 memory에 write해 줍니다.(cache clean)
2) arm에서 cache clean은 async하게 동작합니다. 즉 cache clean가 다 될 때까지 기다려 줘야 됩니다. 여기에 data synchronization barrier를 사용합니다.
3) 0x00000000에 해당하는 곳에 저장되어 있는 instruction cache를 invalidate를 해 줍니다.
4) arm에서는 instruction을 미리 읽어오는 특징이 있습니다.(일명 prefetch) 그렇기 때문에 instruction cache를 invalidate를 하기 전에 이미 arm이 이미 0x00000000에 있는 instruction을 prefetch했을 가능성이 있습니다. 이 문제를 막기 위해 보통 instruction cache를 invalidate를 해 준 뒤에 prefetch buffer flush를 넣어 줍니다. instruction cache를 invalidate하기 전에 0x00000000의 내용을 prefetch하지 않게 됩니다.

이 기능들은 arm안쪽에 대개(?) 붙어 있는 cp15 coprocessor를 사용해서 control합니다.

일단 여기까지는 0x00000000에 write가 가능한 memory가 붙어 있을 때의 이야기입니다.

보통 arm soc들을 보면(종류가 워낙 많아서 뭐라 딱 말하기는 뭐하지만), memory remapping기능을 제공하는데 이것은 주로 MMU와는 상관없이 물리적으로 arm 바깥에서 아예 switch를 바꾸는 식으로 이루어집니다. (arm의 바깥이지만 arm soc의 안쪽)

그래서 보통 bootloader들이 하는 행동이 초기에 flash로 boot해서 dram controller를 init해 주고 memory remapping을 하면 0x00000000이 될 dram의 장소에 interrupt table을 write하고 program(예를들어 OS)의 code, data등등을 dram의 적절한 위치에 flash로부터 copy한 다음에 dram의 code상으로 jump한 다음에 memory remapping을 해 줘서 flash의 내용을 dram의 내용으로 바꿔치기하게 됩니다. 즉 memory remapping을 하게 되면 0x00000000으로 access할 때 flash로 access하는 것이 아니라 dram으로 access하게 되고 flash는 다른 address로 이동됩니다. 예를 들어 0x00000000:flash, 0x10000000:dram이 memory remapping을 하면 0x00000000:dram, 0x10000000:flash와 같이 위치가 바뀌게 됩니다. 설명은 생략했지만 중간중간에 필요에 따라 위에서 언급한 cache관련 처리를 해 줘야 될 필요가 있습니다.

manual안 보고 기억에 의존해서 대충대충 적은거라 중간에 잘못된 내용이 있을지도 모릅니다. 죄송 ^^

Taeho Oh ( ohhara@postech.edu , ohhara@plus.or.kr ) http://ohhara.sarang.net
Postech ( Pohang University of Science and Technology ) http://www.postech.edu
Digital Media Professionals Inc. http://www.dmprof.com

Taeho Oh ( ohhara@postech.edu ) http://ohhara.sarang.net
Postech ( Pohang University of Science and Technology ) http://www.postech.edu
Alticast Corp. http://www.alticast.com

wsmrdo의 이미지

펌웨어만 계속해온 사람입니다.
리눅스야 뭐 가져다 시킨대로 깔아놓는 정도..

남들은 16비트에서 32비트로 갈때 거꾸로 8비트 4비트로 내려간 불행한 과거가 있엇죠.

어떻든 도스에서 윈도그 환경으로 넘어갈때 포기하시고.
아직도 필요한 툴 있으면 도스 프로그램으로 만들어 쓰고 있는 구가다 입니다.
뭐 남이 만들어 놓은 윈도그 소스있으면 고쳐쓰긴 합니다만.....

OS는 언젠가 한 번 해보고 싶었던 것인데...

많은 도움이 될거 같습니다.
부디 끝까지 ... 가능하면 빠른 강좌를 부탁드립니다.

빨리 보고 싶네요.
사진이 너무 이뻐요... 우리 딸 만큼이나. ^^*

새해 복 많이 받으세요.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.