Arm Linux Kernel Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

1148
469
422441


부트로더 소개! 임베디드 에세이

BSP란 무엇일까?
근래 BSP, BSP라고 많이 말을 하는데 BSP란 대략 bootloader와 driver를 합친 말이며 보통 휴대폰 개발자들 사이에서 많이 사용되는 것 같습니다. 예전 피쳐폰(feature phone, 일반폰) 시절의 펌웨어(Firmware)라고 봐도 무방합니다. 누군가 난 2007년에 firmware를 개발 했었어 라고 말하면, 예전에 BSP를 담당했었어 라고 봐도 무방합니다. 2006년에 난 UI를 했었어 라고 말하면 지금 안드로이드 스마트 폰 시대의 기준으로 application을 했다고 보면 됩니다.

부트로더를 우습게 보는 개발자의 마인드 
모바일 BSP 세계에서는 사실 리눅스 드라이버 그리고 특히 리눅스 커널이 대세입니다. 
이미 10여년 전 안드로이드가 모바일계의 선두 주자로 입성한 후 IoT 생태계에서도 역시 리눅스를 많이 쓰고 있기 때문입니다. 

임베디드 개발자들은 부트로더에 대해 리눅스 커널(리눅스 드라이버 포함)만큼 중요하게 여기지 않는 것 같습니다. 

부트로더에 대한 자료 부족
리눅스 관련 서적들은 대부분 리눅스 시스템 프로그래밍, 디바이스 드라이버 혹은 리눅스 커널을 다루고 있습니다.
인터넷 커뮤니티에 가도 리눅스 커널에 대해서 워크큐, fixmap. ion memory 등등에 대해 난상 토론을 합니다.
지식 검색을 해도 리눅스 커널에 관련된 대한 질문과 답을 많이 볼 수 있구요.

하지만 부트로더에 대한 자료는 디바이스 드라이버에 비해 적은 것은 사실입니다.

부트로더의 역할
부트로더는 말의 어원과 같이 리눅스 커널을 DDR memory에 로딩할 있도록 power rail를 키고 MMU를 키는 동작을 수행합니다. 
물론 커널 이미지를 램에 로딩을 합니다. 이를 위해 flash 메모리 controller는 이전에 초기화가 되어야 합니다. 

보통 부트로더는 소프트웨어 복잡도가 리눅스 커널에 비해 낮습니다.
Feature phone 시절의 실시간 OS(real time OS) 정보의 복잡도(complexity) 입니다. 

부트로더가 중요한 이유
여러가지 이유가 있겠지만 임베디드 세상에서 부트로더는 리눅스 커널 만큼 중요하거나 비중이 높은 SW Component라고 보고 있지 않습니다. 하지만 저는 bootloader는 리눅스 커널과 마찬가지로 중요한 모듈이며 따라서 리눅스 커널만큼 많은 시간동안 철저히 분석을 해야 한다고 봅니다. 휴대폰 임베디드 개발자 관점에서 말이죠. 그 이유에 대해서 살펴보죠.

1. SoC의 아키텍처를 볼 수 있다.
우선 부트로더를 자세히 살펴보면 chip vendor(퀄컴, 인텔, 미디어택, 기타 등등 nvidia) 들이 어떻게 자신의 플렛폼을 구상하고 운용하는 지 대략 밑그림을 볼 수 있습니다. 더 깊게, 이 업체들이 어떤 생각으로 SW을 설계했는지도 파악할 수 있죠. 

모바일 계의 대부인 퀄컴님은 부트로더에 RAM DUMP라는 엄청난 기능을 구현하여 제조사에서 SW이슈가 나올 시 RAM DUMP만 확보가 되면 어떤 이슈라도 쉽게 디버깅을 할 수 있습니다. 기술 지원 비용을 상당히 줄일 수 있었죠. 퀄컴이란 회사가 리누스 토발즈 같이 SW 개발 문화의 패러다임(오픈 소스)를 바꾸기 위해 이와 같은 강력한 RAM DUMP란 툴을 구현한 것은 아니고 효율적으로 기술 지원을 하기 위해서 구현한 것이죠. 

그런데, 퀄컴 SOC(MSM)은 부트로더에 램 덤프를 구현을 하였습니다. 커널 패닉이 발생하고 웜부트(warm boot)가 되고 나서 부트로더에서 램덤프가 되는 것이죠. nvidia SOC(Tegra)도 비슷합니다.

2. SoC SW 전체 구조를 알 수 있다.
부트로더를 통해 chipset vendor(퀄컴, 인텔)의 SOC SW에 대한 전반적인 구조를 파악할 수 있는 구체적인 예를 들자면 아래와 같습니다.

   - PMIC(power management를 하는 부품, 모든 mobile chip에 존재함) power rail를 설정한 다음에 얼마만큼의 delay를 주는 지. Chipset vendor의 data sheet에도 있겠죠.
   - Memory controller를 초기화하는 방식 LPDDR2방식인지 여부를 파악할 수 있겠죠.
   - 부트 이미지(커널 이미지)를 emmc를 통해 어떻게 로딩을 하는지.
   - 다른 IP(processor)를 어떻게 리셋을 치며(bring out of reset) 얼마만큼의 delay를 주는 지.
   - Display controller를 어떤 핀(gpio)으로 초기화를 하는 지
   - 타겟이 (이전 동작에서) 어떻게 리셋이 되었는 지 파악하는 루틴.
   - 어떤 방식으로 커널의 bootstrap 코드로 점프를 하는 지.

이와 더불어 architecture(ARM, x86 등등)의 구조에 대해서도 알 수 있습니다.

   - (BSP개발자가 에러 코드를 부트로더에 추가할 경우) Exception vector table로 어떻게 점프를 하는지. 
   - Exception vector를 초기화하는 방식은 어떤지. ARM과 x86는 매우 상이합니다.
   - 각 architecture 별로 stack을 어떻게 설정하는 지.
   - Segment register를 어떻게 초기화하는 지(x86 인 경우)
   - Linker script 파일을 통해 각각의 section(data, code, bss 등등)들이 어떻게 재배치되는 지.
   - 인터럽트는 어떻게 처리(handle)하는 지.

화가가 그림(SOC)을 그리기 전에 스케치(부트로더)한 것을 보는 것이라고 봐도 좋습니다. 너무 BSP를 낭만적으로 이야기를 하는 것 같군요. 

이와 더불어 부트로더를 잘 알아야 하는 가장 중요한 이유에 대해서 추가로 설명을 하고자 합니다.

부트로더는 디버깅하기 어렵다!
부트로더는 커널에 비해 디버깅하기 매우 어렵습니다. 유식하게 말하자면 디버깅 환경이 좋지 않다라고 말할 수 있죠. 보통 디바이스는 부팅하는 과정에서 로그 서비스가 시작됩니다. 이후 아이들화면(배경화면)에 까지 진입을 하고 난 다음 부터 각 로그들이 특정 파일 시스템에 자동으로 저장이 됩니다. 로그 서비스가 특정 디바이스 파일을 통해 주기적으로 로그 스트링을 저장하는 것이죠. 그래서 문제가 발생하면 디바이스를 재부팅 시킨 다음에 adb push와 같은 명령어로 로그(커널, 메인, 시스템)를 추출할 수 있습니다. 

그런데 부트로더에 이슈는 부팅 애니메이션에서 화면이 멈추어져 있는 경우가 대부분이라 FPCB UART Cable를 연결해야 부트로더 로그를 볼 수가 있습니다. 심지어는 유와트 케이블(UART Cable)를 연결해도 로그를 볼 수 없는 경우도 있습니다. 이럴 때 부트로더의 구조를 명확하게 파악한 다음 문제가 발생한 환경과 재현 경로를 바탕으로 가설을 세운 다음에 추가 분석(테스트를 진행, 방어 코드 삽입)을 진행해야 합니다.

예를 들어 특정 상황에서 부트로더가 USB 인식을 못할 경우에, 바로 부트로더에서 USB를 인식하는 루틴을 중심으로 코드를 파악해야 하는 것이죠. 예를 들면, 특정 상황에서 ADC(Analog to Digital Convertor)를 통해- 보통 USB 타입은 PMIC의 ADC 값을 통해서 알아냄- USB type를 가져오는 코드에 문제가 있는 지. 혹시 이 코드가 호출이 안된 건 아닌지.

부트로더는 디버깅을 못하면 어떻게 될까?
이제 개발자의 스트레스 관리(?) 관점에서 부트로더 이슈 해결이 얼마나 중요한 지 설명을 하겠습니다. 물론 저의 경험에서 우러 나온 진솔한 생각입니다.

1. 12시 퇴근은 가능
위에서 언급된 USB를 인식 못하는 정도의 이슈는 집에 못 갈 정도는 아닌 것 같습니다.(12시 퇴근은 가능) 그런데 부팅 도중에 애니메이션이 멈추는 심각한 이슈가 나올 경우 문제의 대략적인 문제 원인을 좁히기 전까지 집에 못 갑니다. 부팅이 안되면 다른 개발자들이 일을 못하고 또한 검증 부서도 놀 수 밖에 없죠. 특정 소프트웨어 이미지를 이전 버젼으로 바꾸어 다른 개발자들이 일을 할 수 있게 시간을 벌 수는 있죠. 하지만, 이렇게 시간을 버는 동안 부트로더 담당자는 최단기간 내에 문제를 해결해야 합니다. 그런데 운이 안 좋게도 이런 이슈는 디버깅할 수 있는 시간이 없는 급박한 상황에서 나오는 경우가 많은 것 같습니다. 이러하니 임베디드 개발자는 극한의 스트레스를 받으면서 디버깅을 할 수 밖에 없죠. 일을 즐겁게 할 수 없는 상황인 거죠.

2. 대머리가 됨
예전에 모델 출시 5일 전 부팅이 안 되는 초대형 이슈가  나왔는데 그 당시 도저히 디버깅을 할 수 있는 막막한 상황이 었습니다. 담당자들은 식음을 전폐하고 머리를 쥐어 뜯었죠.(거의 대머리가 되었다고 함). 한 4일 동안 집에 못가고 밥도 거의 못 먹었고. 그런데 그 당시 옆에서 대머리가 되어 가는 동료를 지켜본 어떤 고수 BSP 개발자가 혜성 같이 나타나 정확히 문제의 원인될 만한 코드의 함수 이름과 라인 수까지 예측을 하였습니다. 물론 저는 아니였구요. 그런데 그 예측 디버깅이 정확히 맞아 들어갔어요. 그분이 말한 코드를 추가하였더니 정상적으로 부팅을 했답니다. 주위 동료들은 눈물을 흘리며 그 분께 신내림 디버거이라는 별명을 만들어서 고마워 했습니다. 제가 봐도 그 분은 정말 찬양 받을 만 합니다. 개발자들이 포기할 정도의 상황이란 정말로 절망의 순간이거든요. 

임베디드 개발을 안 하신 분이면 그 상황이 그려지지는 않을 겁니다. 대신 뭐 그리 대단한 것도 아닌데 서로 난리를 친다고 생각할 수도 있겠죠. 일반인들도 이해할 수 있을 만한 예를 들자면. 뭐, 월드컵 축구 결승전 경기에서 2명의 수비수가 거머리같이 마크를 해서 후반전 46분까지 공을 한번도 발로 못 건드려본 적이 없는 스트라이커가 후반 47분에 딱 한 번의 기회에 결승골을 넣는 정도라고 볼 수 있겠습니다.

얼마나 부트로더에 대해서 잘 알아야 할까?
부트로더에 대한 임베디드 개발자들의 마음가짐에 대해서 할 말이 많지만 제 생각을 아래 정도로 정리하려고 합니다. 부트로더를 담당하는 임베디드 개발자는 (이 정도 신내림 디버깅까지는 아니더라도) 부트로더에 문제가 생기면 2~3가지 정도로 구체적으로 문제의 원인을 바로 좁힐(narrow down) 수 있어야 합니다. BSP 개발자는 이 정도의 포스는 갖고 있어야 개발자의 아우라를 풍길 수 있죠. 이 때 이슈에 대해서 피하거나 다른 부서 혹은 담당자에게 책임을 전가하려는 비겁한 모습을 보여서는 절대 안됩니다. 자신이 알고 있는 내용과 바탕으로 명확히 자신의 의견을 정리해서 관리자들 혹은 동료들에게 대안을 제시해야 합니다. 개발에도 멘탈이 중요하죠.
절대! 절대! 도망가면 안됩니다. (안타깝지만, 생각보다 임베디드 세상에서도 비겁하면서도 굴욕적인 개발자들이 많은 것도 사실입니다.)

결국 부트로더 구조에 대해서 제대로 알고 있으면 3일 동안 집에 못 들어갈 만한 상황을 8시 퇴근을 할 수 있는 well-being으로 바꿀 수 있습니다. 이런 스스로 환경을 만드는 것은 우리 BSP 개발자들의 몫입니다. 

관리자들이 받는 압박감
관리자 입장에서 부트로더 이슈에 대한 에피소드(?) 말씀을 드리면.
부트로더 이슈가 나왔는데 전혀 진척이 안되면 임베디드 개발 관리자들은 엄청난 스트레스를 받습니다. 개발실 임원들에게 불려가서 날라차기, 싸대기를 맞기도 합니다.(피하면 화내며 더 강하게 때린다고 함)  검증 부서에 내려가서 직속 상사에게 싸대기를 맞은 흔적, 폐암 말기 환자 같이 창백한 아련한 얼굴,  일주일 동안 감지 않은 머리카락과 다크서클이 얼굴 전체로 덮힌 처철한 모습을 검증부서 담당자에게 보여주면서 무릎을 꿇습니다. 그러면서 디버깅 할 수 있는 시간을 달라고 비는 거죠.(결국 마지막에는 휴대폰을 얼굴에 던짐). 그런데 이렇게 관리자들에게 쌓인 분노가 불교의 돈오점수를 깨달은 분들에게는 몸 속의 사리가 될 수가 있고, 혹은 독실한 크리스찬이라면 “이 모든 게 하나님이 주신 역경이다. 일정 연기는 하나님의 뜻이다”라고 신앙의 힘으로 마음을 돌릴 수 있겠죠. 하지만 임베디드 관리자들에게 이런 도덕적인 같은 모습을 기대하기는 사실상 힘듭니다. 이런 화산과 같은 극한의 분노가 터지게 되면 관리자들은 폐암 말기 환자가 피를 토하듯 개발자들에게 절규(소새끼!, 말새끼!, 뛰어내려!, 죽어!, 매장시킨다!)를 하기 마련이죠. 

이런 극한 상황을 저는 옆에서 직접 본적이 있습니다. 너무 처절해서 이어폰을 꼽고 소리를 크게 틀었는데 그게 더 견딜 수 없었습니다. 사람의 목소리가 마치 동물들이 죽기 직전의 울부짖음으로 이어폰으로 들렸기 때문입니다.

그런 상황을 막기 위해서는 임베디드 개발자들께서는 평소에 부트로더 코드를 많이 보면서 열심히 분석을 해야 합니다.


# Reference: For more information on 'Linux Kernel';

디버깅을 통해 배우는 리눅스 커널의 구조와 원리. 1

디버깅을 통해 배우는 리눅스 커널의 구조와 원리. 2





덧글

  • 희나람 2014/07/18 20:31 # 답글

    ㅋㅋ 글 내용이 재밋네요ㅎ
  • 헤드위그 2014/07/27 20:35 # 답글

    잘 읽었습니다 ㅎㅎ 남의 일이 아니라서 재밌네요.
  • aaa 2020/11/03 18:39 # 삭제 답글

    뒷부분 실화입니까... omg
  • AustinKim 2020/11/03 18:45 #

    실화입니다.

    참고로 저는 건물의 옥상에 잘 안 올라갑니다.
    예전에 옥상에서 관리자에게 다음과 같이 뛰어내리라면서 욕을 하도 많이 먹어서요. ):

    "와치독 이슈 해결 못하면 뛰어 내려라!"
  • 2020/11/03 18:45 # 답글 비공개

    비공개 덧글입니다.
  • 영인 2022/04/13 20:12 # 삭제 답글

    하나 여쭤보고 싶은 부분이 있어요!...
    제가 "임베디드 OS 개발 프로젝트"라는 책을 통해 학습을 진행하고 있는데... 이 책에서는 libvirt에서 올리는 형식으로 되어 있어 실제 보드에는 어떻게 하면 올릴 수 있을까 ? 라는 질문에
    빠져 헤어나오질 못하고 있습니다.

    만약 책을 보고 개발한 OS가 있을 때, NAND FLASH 기반 ARM 기반 보드에 OS를 포팅해 보고 싶다고 한다면 대략적인 절차가 어떻게 되는 것인지 여쭤보고 싶습니다.
    부트로더 자체부터 제작을 직접해야 되는 것인지 아니면 개발되어 있는 부트로더를 보드에 업로드하고 내가 만든 OS를 JTAG를 활용해서 NAND FLASH 메모리에 업로드하면 되는 것인지...
    너무 무지한 영역이라서 접근이 힘들어 문의드립니다...
  • AustinKim 2022/04/13 20:22 #

    아래 링크에 가시면 "임베디드 OS 개발 프로젝트" 책의 저자께서 운영하시는 블로그에 접근할 수 있습니다.

    https://kldp.org/node/162560

    저자님께 직접 문의하시면 명확한 답변을 들으실 수 있을 것 같은데요.

    감사합니다.
댓글 입력 영역