메뉴 바로가기 본문 바로가기

기술자료

Windows CE 6.0 기반 S3C6410 BSP 의 부트로더 코드 분석하기 (I)
관리자 2009-07-29 오후 2:50:53 조회 16051
Windows CE 6.0 기반 S3C6410 BSP 의 부트로더 코드 분석하기 (I)
 
지난 호에서는 기본적인 ARM 아키텍쳐와 소위 Pre-Bootloader 에 해당하는 스텝로더의 스타트업 코드에 대해 간략히 살펴보았다. 실제로 스타트업 코드는 부팅 초기에 실행된다는 점에서 해당 시스템을 이해하는 데 좋은 진입점이기도 하며, 스텝로더와 부트로더 그리고, 운영체제 이미지 각각의 코드에서 많은 부분을 공유하고 있기도 한다. 
이번 호에서는 스텝로더의 남은 C 코드 부분과 함께 eboot 부트로더에 대해 코드 실행 순서대로 개괄적으로 살펴보고자 한다. 그러한 내용을 토대로 다음 호에서는 특정 디바이스를 예로 들어 구체적인 초기화 과정을 다룰 수 있도록 하겠다. Eboot 코드는 스텝로더 코드와는 달리 여러 디렉토리에 걸쳐 비교적 많은 코드를 포함하고 있긴 하지만 전문적인 편집기를 활용하면 손쉬운 로직 파악에 많은 도움이 된다.
 
글 : 백원석 팀장 / ePlatform 사업부, SG 어드밴텍㈜
www.sgadvantech.kr/esg   / joseph@advantech.co.kr
 
 
부트로더의 스타트업 코드 호출
본격적으로 부트로더 코드를 살펴보기 전에 스텝로더의 C 코드로 된 main 함수에서 어떻게 낸드플래쉬에 저장되어 있는 부트 코드를 SDRAM 으로 다운로드하여 로딩함으로써 결과적으로 non-bootable 디바이스인 낸드 플래쉬를 bootable 디바이스로 사용하는지 살펴보자.
스텝로더의 스타트업 코드 실행을 통해 현재 타겟보드는 인스트럭션 캐쉬가 동작중이고, 주변장치용 SFR(Special Function Register)를 설정할 수 있으며, 3개의 PLL 과 클럭이 활성화 되었으며, SDRAM 이 초기화 되어 있는 상태이다. 그 외에도 몇 가지 수행 내용이 있지만 편의상 중략하기로 한다.
 
 
위의 코드 상에서 보듯이 부트로더가 저장되어 있는 낸드플래쉬를 먼저 초기화시켜서 이후에 사용할 수 있도록 한다. 그리고, 부트로더가 저장되어 있는 세번째 블록의 시작 페이지에서부터 부트로더 크기만큼의 영역 내에서 순차적으로 페이지 단위로 읽어들여서 SDRAM 으로 복사한다는 것을 알 수 있다. 현재 이 코드를 실행하고 있는 스텝로더는 타겟 보드에 전원이 인가됨과 동시에 하드웨어적으로 SRAM 으로 다운로드되어 실행되고 있으며, 이 스텝로더에 의해 부트로더가 스토리지 디바이스로부터 코드 실행이 가능한 매체인 SDRAM 으로 복사됨을 알 수 있다.
이후 LOAD_ADDRESS_PHYSICAL 주소로 점프하여 본격적으로 부트로더 코드가 실행되게 된다.
그럼 이제 우리는 부트로더의 엔트리 포인트를 찾아야 할 것이다. 이는 해당 SOURCES 파일을 통해 확인할 수 있다.
 
TARGETNAME=eboot
TARGETTYPE=PROGRAM
RELEASETYPE=PLATFORM
EXEENTRY=StartUp
 
TARGETLIBS= \
           $(_PLATCOMMONLIB)\$(_CPUDEPPATH)\oal_cache_arm920t.lib          \
           $(_PLATCOMMONLIB)\$(_CPUINDPATH)\oal_kitl.lib             \
           $(_PLATCOMMONLIB)\$(_CPUINDPATH)\oal_log.lib             \
           $(_PLATCOMMONLIB)\$(_CPUINDPATH)\oal_blnk.lib           \
           $(_PLATCOMMONLIB)\$(_CPUINDPATH)\oal_blcommon.lib    \
           $(_PLATCOMMONLIB)\$(_CPUINDPATH)\oal_blmemory_arm.lib   \
---- 중 략 ----
 
EXEENTRY 라는 매크로는 해당 코드에서 가장 먼저 실행되는 함수의 이름을 지정하는 것이다.
실제로 스텝로더에서와 마찬가지로 부트로더의 Startup 함수 역시 어셈블리어로 구현되어 있는 것을 알 수 있고, 많은 부분 코드를 공유하고 있다. 단지 부트로더에서는 MMU 설정을 위한 코드 정도가 추가되어 있을 뿐이다. 이미 시스템 상에 적용되어 있는 설정을 왜 굳이 반복해서 수행하는지 의문이 들 것이다. 이는 코드의 독립성 때문이라고 표현하고 싶다. 부트로더는 본인 스스로 실행 상태로 진입했는지, 혹은 어떤 Pre-bootloader 에 의해 기본적인 시스템 초기화를 거친 후 구동되었는지 알지 못한다. 스텝로더가 해당 사실을 알려 주지도 않았을 뿐더러 어떠한 부분이 이미 초기화 되었는지 검사하려고 하지도 않는다. 차라리 부트로더 스스로가 ‘자기 힘으로’ 필요한 초기화를 ‘책임지고’ 수행하는 것이 오히려 더 수월하다. 이러한 이유로 부트로더 뿐만 아니라 커널의 구동시 실행되는 스타트업 코드에서도 역시 중복되는 작업을 반복해서 수행하는 것이다.
 
 
부트로더 코드 시작
부트로더의 스타트업 코드는 기본적인 하드웨어 초기화와 함께 MMU 초기화를 거친 후 C main 함수를 호출하여 Win CE OS 이미지 구동을 위한 본격적인 작업에 착수하게 된다.
위에서 살펴본 부트로더의 SOURCES 파일에서 알 수 있듯이 일반적으로 부트로더를 구성하는 라이브러리에는 칩벤더가 제공하는 것 뿐만 아니라 마이크로소프트에 의해 이미 구현되어 있는 것들도 상당부분 존재한다. $(_PLATCOMMONLIB) 경로 상에 위치하는 라이브러리들이 그것이다.
전체 부트로더의 실행 흐름을 통제하는 코드 역시 oal_blcommon.lib 라는 공유 라이브러리이다.
 
[ 그림 1 ] Windows CE 부트로더의 구조
 
위의 [그림 1] 에서 보여지는 함수들의 이름은 마이크로소프트에 의해 미리 정의되어져 있다.
칩벤더들은 이와 같이 MS가 미리 만들어 놓은 코드를 최대한 재사용 함으로써 시간을 단축시킬
수 있음은 물론 검증된 코드 사용을 통한 안정성 확보라는 두 가지 긍정적 효과를 거둘 수 있게 된다. 수 백만 라인의 제품 수준의 샘플 코드들은 윈도우 임베디드 CE 가 갖는 주요 장점 중의 하나이다.
 
주요 부트로더 함수
대부분의 경우 디버그 LED 나 디버그 시리얼은 초기화 코드 중에서도 우선적으로 초기화 되는 디바이스들이다. 개발 초기에는 여러가지 원인에 의해 예기치 않은 오류에 빠지기 십상이기 때문에 디버그 포트를 우선적으로 활성화 시켜야 하기 때문이다. 이 글에서 사용하는 타겟보드인 AT-6400 은 4개의 디버그용 LED 와 한 개의 디버그 시리얼 포트를 제공하고 있으며, 지난 연재에서 살펴보았듯이 이미 스텝로더에서 두 장치를 모두 활성화 시켜서 주요 부팅 스텝마다 적절하게 사용하고 있다.
부트로더의 OEMDebugInit() 함수에서는 BSP의 배치파일에서 설정된 디버그용 UART 장치에 대해 초기화 과정을 수행한다. 이를 위해서는 UART 관련 레지스터와 UART 클럭 및 GPIO 핀 설정을 해야 한다.
 
[ 표 1 ] UART 메모리 맵
 
실제로 UART 의 펑션 레지스터에는 이 외에도 몇 가지가 더 있지만, 현재 시점에서 단순히 시리얼 메시지 입출력을 위해서는 위의 6가지 정도만 해 주면 되겠다. 각각의 레지스터의 설정 방법은 데이터시트에 나와 있으며 해당 정보를 토대로 설정한 아래 코드를 보도록 하자.
 
 
 
[ 그림 2 ] UART 클럭 제너레이션
 
 
6400 CPU 의 시스템 컨트롤러는 ARM11 코어를 비롯한 SoC 내의 모든 주변장치에 공급되는 클럭들과 전원관리 기능을 제공한다. UART 클럭 설정을 위해 시스템 컨트롤러 중에서 4개의 레지스터를 사용하고 있음을 볼 수 있다.
 
 
[표 2] GPACON 레지스터 정의
 
 
그리고 GPA0 와 GPA1 을 UART0 의 TX 와 RX 핀으로 설정하였다.
또한 부트로더의 디버그용 시리얼 초기화 로직에서는 조건부 메시지 출력을 위한 디버그존 역시 사용하고 있다.
이후 Blcommon 은 OEMPlatformInit() 를 호출하여 부트로더에서 사용하는 모든 하드웨어에 대한초기화가 이루어 질 수 있도록 한다.
먼저 디스플레이 컨트롤러를 초기화한다. 이 작업에는 LCD 모듈에 관련된 설정과 프레임 버퍼 생성 및 클럭 설정 등의 작업이 포함된다.
두번째로는 인터럽트 컨트롤러 초기화 작업이 수행된다. ARM11 CPU 에서는 기존보다 더욱 늘어난 파이프라인으로 인해 성능 향상 효과도 있지만 인터럽트 발생시나 기타 서브루틴으로의 분기가 일어나는 경우 초기 지연이 훨씬 늘어나는 부작용도 안고 있다. 이를 최소화하기 위해 대안으로 마련한 것 중의 하나가 분기 예측이나 벡터 방식 인터럽트 컨트롤러(Vectored Interrupt Controller, 이하 VIC)라고 할 수 있겠다. VIC 는 기존 전통적인 인터럽트 컨트롤러에 비해 인터럽트 핸들러가 호출되는 과정을 더욱 간소화 함으로써 인터럽트 처리에 드는 비용을 줄이는 효과가 있다. 삼성의 S3C6400 CPU 는 두 개의 VIC 를 내장하고 있으며 각각은 32개씩의 인터럽트 소스에 매핑되어 있다.
 
[표 3] 인터럽트 소스
 
BootloaderMain 에서 세 번째로 호출하는 InitializeInterrupt 함수에서는 이 64개의 벡터 인터럽트 테이블을 구성하고, 그 중 58번째 인터럽트 소스인 OTG 인터럽트를 활성화 하는 역할을 한다. 이 OTG 포트는 부트로더에서 USB 를 통해 OS image 를 다운로드 할 수 있도록 하는 데 사용된다.
단, 여기서 활성화 하는 인터럽트 핸들러는 이후 OS 에서 사용하는 ISR / IST 구조와는 동일하지 않은 것임을 알아야 한다. 원래 부트로더에서 인터럽트 처리는 불필요하기 때문에 인터럽트 컨트롤러를 비활성화 하고 OS 측에서 이를 활성화 하고 여러가지 인터럽트 핸들러를 추가하여 사용하는 것이 일반적이지만, 특별히 USB 부팅이라는 기능을 위해 아주 간단한 인터럽트 핸들링 로직을 부트로더 상에 구현한 것 뿐이다. 이를 위해 지난 호에서 살펴보았던 것처럼 0x18 번지에 위치하고 있는 IRQ Handler 부분에 USB OTG 용 인터럽트 핸들러를 추가하였다.
[표 4] ARM 동작모드와 인터럽트 핸들러의 주소
 
세번째로는 낸드플래쉬를 초기화 하고 TOC 에 저장되어 있는 부트로더의 환경설정 정보들을 읽어들인다. 그리고 기 설정되어 있는 부트 지연 시간 만큼 시리얼 입력을 모니터링 하면서 사용자가 스페이스 키를 입력하는지 감시한다.
만약 정해진 시간 동안 아무런 입력도 없다면 BootConfig 설정에 따라 로컬 부팅 혹은 신규다운로드를 위한 절차를 수행한다. 만약 디폴트 부트 모드가 로컬 부팅(Launch Existing) 이라면 ReadOSImageFromBootMedia 를 통해 로컬 저장된 이미지를 실제로 불러들여서 정상적인 OS 이미지가 들어있는지를 판단하며, 부트 모드가 새로운 OS 이미지를 다운로드 하도록 되어 있다면 이더넷 디바이스 초기화를 수행한다. 하지만, 사용자가 스페이스 키를 입력한다면 부트로더는 부트 메뉴 상태로 진입하여 여러가지 설정 작업을 할 수 있도록 한다. 이 때 디폴트 설정 값은 TOC 에 저장되어 있는 값들이 반영되어 표시된다.
계속해서 BootloaderMain 은 OEMPreDownload 함수를 호출함으로써, 사용자 설정에 따라 로컬 부팅 혹은 신규 OS 다운로드를 수행한다. OS를 새로 다운로드 하는 방식에 있어서도 USB와 이더넷을 지원하므로, USB 다운로드 모드라면, DNW 프로그램을 통해 이미지 업데이트를 시작하도록 메시지를 출력하고, 이더넷 모드인 경우에는 장치에 IP 주소나 서브넷 마스크 등을 설정하고 이더넷 디바이스를 초기화한다. 디바이스가 브로드캐스트하는 BootME 패킷을 통해 비주얼 스튜디오에서 디바이스의 이름을 확인할 수 있는 것도 이 부분에서 수행하는 내용이다.
 
 
 
맺음말
여기까지 Windows CE 의 부트로더를 실행 순서 순으로 개괄적으로 살펴보았다. 큰 틀에서 보자면 OS 이미지를 다운로드 할 수 있도록 필요한 디바이스를 초기화 하고 일부 인터럽트 라인을 사용한 것 뿐이다. 이외에 특정 디바이스 초기화에 대한 세부적인 로직은 다음 호에서 다루기로 한다.
 

답변 목록

회원가입