본문 바로가기

Hacked Brain/embeddedland.net

부트로더의 기본 구현 1

저 자 : 유영창
출판일 : 2003년 12월호

부트로더의 주 임무는 커널이 동작할 수 있는 환경을 구성하고 커널을 시작하는 것이다. 정의는 간단하지만 프로세서를 정상적으로 동작시켜야 하기 때문에 그리 만만한 일이 아니다. PXA255 프로세서에서 동작하는 이지부트 프로그램은 두 부분으로 나눠져 있다. 최소한의 환경 설정을 하는 start 프로그램 부분이 있으며, 커널이 부팅하도록 지원하고 개발의 편리함을 제공하는 main 프로그램이 있다. start 프로그램은 모두 ARM 어셈블러로 구성돼 있고 스택이 없기 때문에 다중 호출도 하지 못하는 가장 열악한 환경의 프로그램이다. 이번 호에는 이지부트의 시작점인 start.S를 집중적으로 살펴보고자 한다.

연+재+순+서
1회 2003.10 | 임베디드 프로그래머와 부트로더
2회 2003.11 | 보드를 살려보자
3회 2003.12 | 부트로더의 기본 구현(1)
4회 | 부트로더의 기본 구현(2)
5회 | 부트로더의 네트워크 구현
6회 | 커널로의 진입

연+재+가+이+드
운영체제 | 리눅스
개발도구 | GNU gcc 크로스 컴파일러
기본지식 | 리눅스 운영
응용분야 | 임베디드 리눅스 기업 애플리케이션간 상호 운용 등

유영창 | frog@falinux.com
필자는 제이닷디앤티의 기술이사로 근무 중이다. 원래는 윈도우 시스템 환경의 시스템 프로그래머였다가 그 악명 높은 파란 화면에 질려서 리눅서로 전향(?)했다. 리눅스를 이용해 공장 자동화에 적용시키려다 임베디드 리눅스에 처음 발을 들여놓게 됐다. 하지만 아직도 리눅스의 vi 에디터에 적응하지 못하는 절름발이 리눅서이기도 하다.
----------------------------------------------------

공개된 부트로더를 살펴보면 주로 두 개의 프로그램으로 나누어진다. 하나는 부팅할 때 동작하는 프로그램이고, 또 하나는 프로세서의 램 컨트롤러 환경 설정이 끝난 후에 동작하는 프로그램이다. 또한 대부분의 부트로더 프로그램은 롬(ROM)에서 동작하지 않고 램(RAM)에서 동작한다.
왜 이렇게 동작하는 구조를 가질까? 이것은 필자가 공개된 부트로더를 처음 보면서 가졌던 의문이다. 롬에서 프로그램이 동작하면 메모리의 사용 효율도 높일 수 있을 것 같은데 굳이 낭비를 하면서까지 램에서 프로그램을 동작시키는 이유가 무엇인가가 의문의 핵심이었다. 하지만 직접 EZ-X5에서 동작하는 부트로더를 만들어가면서 이 질문에 대한 답을 모두 얻었다. 결론부터 이야기하면 이유는 다음의 세 가지이다.

◆ 롬은 처리 속도가 느리다.
◆ 플래시 롬은 해당 장치에 쓰기를 시도하는 순간 롬으로서의 역할을 하지 못한다.
◆ 어셈블러로 작성된 프로그램과 C 함수로 작성되는 프로그램에 차이가 있다.

== 플래시 롬의 특성
요즘 나오는 32비트 프로세서들은 처리 속도가 최소한 50MHz 이상이다. 시중에서 가장 많이 사용되고 있는 프로세서들은 200MHz가 기본이다. 그런데 롬이나 플래시 롬들은 150nsec(나노 초) 정도의 접근 속도를 갖는다. 이것은 최근 프로세서들의 동작 속도에 비하면 현저하게 느린 속도이다. 프로그램을 수행하기 위해서는 롬에서 프로그램 코드를 프로세서로 가져와야 하는데 롬이나 플래시 롬들은 150나노 초 정도의 시간이 소요되기 때문에 프로세서가 캐시를 사용한다고 하더라도 수행 속도는 롬의 처리 속도에 좌우되어 버린다.
반면 롬과는 달리 SRAM이나 SDRAM의 처리 속도는 고속이다. 더욱이 SDRAM의 동작 속도는 100MHz 정도이다. 두 메모리 장치의 속도 차이는 한 사이클만 보면 대단하지 않을 것 같지만 수만 번의 연산을 반복하면 그 차이는 어마어마해진다. 그래서 프로그램의 처리 속도를 올리기 위해서는 롬에서 프로그램을 수행하는 것보다 램에서 수행하는 것이 더 낫다.
지금도 가끔 쓰이지만 예전에 프로그램 롬으로 대표적인 것이 EEPROM이었다. 이 롬은 가격적인 이점 때문에 8비트 프로세서에서 지속적으로 사용되기는 하지만 사용 가능한 공간도 그리 크지 않고 프로그램을 써넣는 장비가 따로 필요한 점 등 무척 불편하기 때문에 최근에는 잘 사용되지 않고 있다. 이와 달리 플래시 롬은 용량도 상당히 크고 써넣는 기능을 소프트웨어로 간단하게 구현할 수 있는 데다 프로그램을 롬에 써넣는 장비가 따로 필요 없기 때문에 점점 많이 사용되는 추세이다(가장 초기에 동작하는 프로그램을 써넣을 경우에는 장비가 필요하거나 JTAG을 이용한 라이팅 프로그램이 필요하다).
하지만 이 플래시 롬이 롬으로서의 역할을 하는 것은 단순하게 읽기만을 수행할 때이다. 플래시 롬에 쓰기를 시도하게 되면 그 순간 플래시 롬은 롬으로서의 역할을 하지 못한다. 그래서 플래시 롬에서 동작하는 프로그램은 자신이 동작하는 플래시 롬에 쓰기를 시도할 수 없다. 쓰기를 시도하는 순간 프로그램은 멈추게 된다. 이런 특성 때문에 플래시 롬에서 동작하는 부트로더는 다운로드 기능을 구현하지 못한다.
꼭 플래시 롬에서 동작하는 프로그램을 제작해야 하는 경우에는 플래시에 쓰는 기능을 수행하는 루틴만 잠시 램으로 옮겨서 수행한 후 플래시에 원하는 데이터를 써넣고 다시 플래시 롬을 롬으로 동작하는 변환 처리를 한 다음 원래의 루틴으로 동작하게 하는 방법을 사용한다. 그러나 이런 식으로 구현하게 되면 생각보다 여러 가지 경우를 고려해야 한다. 가장 대표적인 고려 사항이 복귀 루틴의 고정 처리이다. 복귀된 위치가 다른 루틴으로 대치되어 버리면 문제가 발생할 경우가 있기 때문이다(플래시에 대한 이야기는 이후에 다시 자세히 하게 될 것이므로 여기서는 이 정도로 이해하고 넘어가자).

어셈블러 구현 루틴과 C 함수 구현 루틴의 차이
부트로더를 어떤 언어로 구현하는가는 프로그래머가 어떤 언어를 더 자유롭게 구현하는가에 달린 문제이다. 꼭 C 언어로 부트로더를 구현할 이유는 없다. 하지만 부트로더를 C 언어로 구현하면 어느 정도 아키텍처에 의존적인 부분을 줄여서 작성할 수 있다. 필자는 ARM에서 사용한 부트로더인 ‘이지부트’를 i386EX에도 적용한 적이 있다. 이때 ARM에서 사용된 C로 작성된 부분 중 초기화 부분 이외에는 그대로 사용이 가능했다.
초기에 부트로더를 만들기 위해서는 다른 부트로더의 소스에서 루틴을 가져오거나 커널 소스에 있는 루틴을 가져오게 된다. 이렇게 다른 소스를 가져와 사용할 때는 아무래도 어셈블러보다 C 언어가 유리하다. 결국 C 언어로 부트로더를 작성하는 것이 여러모로 유리하기 때문에 대부분의 공개된 부트로더는 C 언어로 사용된다. 그런데 C 언어는 함수 호출 구조상 스택(stack)이라는 것이 필요하다. 그러나 스택은 롬 시스템에서는 원초적으로 사용이 불가능하다. 따라서 C 언어로 작성된 프로그램은 램에서 동작하여야 한다. 부트로더는 이런 언어적인 차이도 롬으로 동작하는 것과 램에서 동작하는 것으로 나누어야 하는 이유가 된다.
이런 이유로 EZ-X5에서 동작하는 이지부트 프로그램은 start 프로그램과 main 프로그램으로 나누어져 있다. start 프로그램은 어셈블러로 작성되어 있고 아주 기초적인 부분을 수행한다. main은 C 언어로 작성되어 있고 start 프로그램에서 구현한 것 외의 나머지 대부분을 구현한다. 각각 만들어진 두 개의 프로그램 소스는 컴파일되고 실행 이미지가 따로 만들어진다. 이후에 하나의 이미지로 합쳐져 플래시 롬에 써넣게 된다. 이렇게 합쳐진 프로그램 중에서 리셋 후에 처음 동작하는 것을 이지부트에서는 start란 프로그램이 담당하고, 스택이 설정되어 C 함수 호출이 가능한 램 공간에서 수행되는 대부분의 작업은 main 프로그램이 담당한다.
이지부트가 어떻게 동작하는지 <그림 1>을 가지고 설명하겠다. 그림에서 보듯이 EZ-X5의 플래시 롬은 0x00000000에 위치한다. start 프로그램은 2KB 이내로 매우 작은 크기를 갖고 있다. main 프로그램은 그 나머지를 차지한다. 플래시 롬에는 이 두 개의 프로그램이 연속되어 있는 하나의 이미지로 만들어져 배치된다. 이 이미지는 리셋 후 start 프로그램에 의해서 <그림 1>의 (1)에서 보듯이 램의 0xA0F00000에 그대로 복사된다. 램은 0xA000000부터 시작하는데 나중에 배치되는 커널을 고려해서 램의 뒤쪽에 부트로더를 둔다.

사용자 삽입 이미지

<그림 1> 이지부트의 동작 방식

main 프로그램은 수행 시작 위치가 0xA0F00800로 고정된다. 이 위치는 램에 같이 복사된 2KB를 차지하는 start 이미지 바로 뒤쪽이다(예전에 어떤 사람이 필자에게 2KB를 제외한 크기를 램에서 복사하지 않는가 물어본 적이 있다. 하지만 이렇게 하는 특별한 이유가 있는 것은 아니다. 단지 필자가 게으르기 때문인데, 2KB를 제외하면 실제 크기를 계산해서 프로그래밍해야 하지만 그대로 하는 것이 정신건강(?)에 유리하기 때문이다). 부트로더는 부팅이 끝나면 그대로 사라지는 프로그램이기 때문에 부득이 램의 크기가 작은 시스템이 아니라면 굳이 메모리를 아낄 필요는 없다.
롬에서 동작하는 start 프로그램은 초기화를 마치고 main으로 이동해야 하는데 두 프로그램은 같은 프로그램이 아니기 때문에 소스상의 심볼릭 참조가 불가능하다. 즉 소스에서 어떤 함수로 이동하라는 식의 기술이 곤란하다. 그래서 절대 위치로 이동하는 명령을 사용하는데 이동 위치가 <그림 1>의 (2)와 같이 0xA0F00800이 된다.