[App 개발] OS X 를 지원하지 않는 기종을 위한 커널 해킹 (1)
본문
Ryan Rempel (Other World Computing)
- 요약
OS X 가 더 이상 지원하지 않는 기종에서 OS X 를 사용하기 위해서는 커널과 몇 가지 IOKit 익스텐션을 변경해야 합니다. 관련된 대부분이 오픈 소스이므로 해당 부분에 대해서만 애플이 제공하는 파일 대신 커스텀 컴포넌트로 대치해 주면 됩니다. 하지만 이 작업은 OS X 가 업데이트될 때마다 반복되어야 합니다. 다행히도 IOKit 은 애플 코드를 상속받는 부분만 고쳐주면 동작이 가능한 런타임 환경으로 만들어져 있으며, 따라서 수정해 주어야 할 부분만 간단히 대체하게끔 되어 있습니다. 이 작업에 필요한 기술은 디바이스 드라이버 개발자들에게도 유용할 것입니다.
- 서론
애플이 처음 Mac OS X 를 발표했던 당시 필자의 파워맥 7300 에서는 동작하지 않았습니다. 하지만 베타판에서는 7300 도 지원을 했습니다만, 필요한 장치 드라이버가 정식 버젼에서는 제외되었더군요. 하지만 애플은 해당 드라이버를 오픈 소스(다윈 커널) 버젼으로 배포를 했습니다. 따라서, 드라이버만 넣어주면 OS X 가 지원하지 않는 기종을 살릴 수 있을 것 같았습니다.
하지만 삭제된 드라이버만 업데이트 해 주는 것만으로는 일이 성사되지 않았습니다. OS X 커널 및 커널 익스텐션에 작업을 해 주어야 하는 몇 가지 문제가 남아있었습니다. 이 작업에 필요한 기술은 정교한 Mac OS X 의 장치 드라이버 시스템 (IOKit 시스템) 활용에 유용할 것입니다.
경우에 따라서는, 커널 내에 드라이버가 이미 있더라도, 애플 드라이버를 우회하여 여러분의 코드를 넣는 것도 가능합니다. 어떤 경우는 애플의 드라이버를 수퍼클래스로 놓아서, 여러분이 문제 해결에 필요로 하는 몇 가지 부분만 손을 볼 수도 있습니다. 왜냐하면 Mac OS X 장치 드라이버를 구성하는 데 이미 활용되어 온 기술이기 때문입니다. 꼭 맞아 떨어지는 부분을 찾지 못하더라도, 대부분은 장치 드라이버를 우회할 적절한 부분을 찾을 수 있을 것입니다.
- 애플 디바이스 드라이버 우회: 읽을 수 없는 NVRAM 을 처리하는 경우
Mac OS X 초기버젼에서는 NVRAM 읽기와 쓰기 기능에 버그가 있었습니다. 사실 그 버그는 지금까지 전해져 내려오고 있지요. 한 번 찾아보세요:
IOReturn AppleNVRAM::read (IOByteCount offset, UInt8 *buffer,
IOByteCount length)
{
…
case kNVRAMTypePort:
for (cnt = 0; cnt < length; cnt++) {
*_nvramPort = (offset + length) >> 5;
eieio();
buffer[cnt] = _nvramData[((offset + length) & 0x1F) << 4];
}
break;
…
}
Listing 1. Extract from
[hongjuny 주석: 위의 경우 length 가 아니라, 증가 변수 cnt 가 들어가야 되겠지요? 따라서 (offset + length) 가 아니라, (offset + cnt) 를 넣어주어야 합니다. 페이퍼의 주석에는 (offset + count) 라고 나와있는데, 오타인 것 같습니다.]
필자의 경험상 대부분의 커널 버그와 마찬가지로, 코드의 어떤 부분이 문제를 일으키는지만 알면 쉽게 찾을 수 있는 문제입니다. 물론 그 과정은 쉽지 않지요.
이 문제 때문에 파워맥 7300 부터 9600 기종의 NVRAM 을 억세스할 수 없습니다. 그리고 해당 기종들을 OS X 가 지원하지 않기 때문에 애플 엔지니어들이 이 문제를 찾아내지 못한 이유가 되기도 하지요. 따라서, 이 부분이 OS X 를 7300 기종에서 구동하기 위해 고쳐주어야 할 부분입니다.
만약 유사한 문제가 리눅스나 FreeBSD 에서 발견되었다면, 문제를 고치는 방법은 다음과 같습니다:
* 버그 픽스를 만들고 시험한다 (위의 경우는 두 줄만 고쳐주면 된다.)
* 공식 소스 배포판에 대응하는 패치를 만든다.
* 프로젝트에 등록한다.
* 새 배포판에 픽스가 반영될 때까지 관계자를 졸라댄다.
Mac OS X 의 경우는 약간 다릅니다. 물론 패치를 애플에 보낼 수도 있고, 애플이 그것을 반영할 수도 있겠죠. 하지만, 이렇게 지원하지 않는 기종에서만 발생하는 경우에 해당하는 문제는 거의 그렇지 않을 것이라 생각합니다 (지원하지 않는 기종이라는 말이 결국 그 뜻인거죠). 애플이 패치를 받아준다 하더라도 그것이 문제 해결의 끝은 아닙니다. 버그는 이미 배포된 인스톨 CD 에 상존해 있으며, 사용자들은 리눅스나 FreeBSD 의 새버젼 CD 를 다운로드 받듯 새 Mac OS X CD 를 다운로드 받을 수 없기 때문입니다. 따라서 문제 해결은 새 OS 가 배포되는 것과는 무관하게 독립적으로 수행됩니다.
Mac OS X 커널의 대부분은 오픈 소스이므로 문제를 해결한 커널의 재컴파일 및 배포는 충분히 가능합니다만, 까다로운 작업이 될 것입니다. 왜냐하면 애플이 자그마한 부분이라도 커널 업데이트를 할 때마다 해당 패치를 재배포해야 하기 때문이지요. 애플이 제공하는 커널을 변경하지 않고 동적으로 해결한다면 문제는 훨씬 간단할 것입니다. 다행히도 Mac OS X 에는 커널에 동적으로 코드를 추가할 수 있는 커널 익스텐션을 작성하는 기능이 있습니다.
커널 익스텐션이라는 개념은 Mac OS X 에만 있는 것이 아닙니다. 다른 OS 에도 비슷한 구조가 있습니다. 하지만 Mac OS X 에는 현 OS 의 문제를 해결할 커널 익스텐션을 쉽게 배치할 수 있게끔 되어있다는 점이 특징입니다. 핵심은 OS X 가 드라이버 모델에 대한 수용성이 높다는 점입니다. 드라이버는 공통 수퍼클래스 (IOService) 를 공유하는 객체로서, 어떤 드라이버가 어떤 장비를 관리할 것인지를 쉽게 배치할 수 있습니다.
부팅시 커널은 메인보드와 PCI 슬롯에 설치된 장치에 대한 정보를 담고 있는 "디바이스 트리" 를 만듭니다. 정보의 종류는 장비에 따라 다르지만, 대체로 이름, 모델명, 장치의 종류, 인터럽트, 메모리 할당 등, 드라이버가 필요로 하는 정보를 공통으로 갖고 있습니다. 장치 정보는 물리적(혹은 논리적)으로 연결된 상태에 따라 트리 구조로 정리됩니다.
그 다음 커널은 디바이스 트리 내의 장치들을 훑으면서 어떤 드라이버가 이 장치들을 관리할 것인지를 결정합니다. 이 과정에서 어떤 드라이버가 어떤 장치를 관리하는지에 대한 정보를 저장한 레지스트리 구조를 커널 내에서 만듭니다. 이것은 드라이버를 장치에 붙이고 그 다음 트리 구조내의 장치 밑에 각 디바이스를 대표하는 딱지를 발부하는 방식입니다. 커널은 트리 내에 있는 모든 장치에 드라이버를 연결하는 작업을 진행합니다.
자, 이제 NVRAM 억세스를 고쳐 봅시다. 먼저 NVRAM 드라이버를 작성한 다음, NVRAM 드라이버가 커널에서 올바르게 선택되는지를 확인하는 것입니다.
첫 번째 작업은 NVRAM 드라이버가 장치 트리 내 어디에 있는지를 알아내야 합니다. 우리는 먼저번 소스에서 해당 클래스가 AppleNVRAM 이라는 C++ 클래스라는 것을 알았습니다. 그렇다면, 해당 클래스가 어디 위치하는지를 IORegistryExplorer 를 이용해서 장치 트리를 검색하거나 터미널에서 ioreg 명령을 이용해서 알아낼 수 있습니다. 다음의 ioreg 명령 출력은 레지스트리 구조에서 NVRAM 장치와 드라이버가 어디 있는지를 나타냅니다.
+-o Root
+-o AAPL,7500
+-o ApplePowerSurgePE
| +-o bandit@F2000000
| | +-o AppleMacRiscPCI
| | +-o gc@10
| | | +-o AppleGrandCentral
| | | +-o nvram@1D000
| | | | | {
| | | | | "IODeviceMemory" = …
| | | | | "reg" = <0001d000000000100001f00000000200>
| | | | | "name" = <"nvram">
| | | | | "existing" = <0000000000002000>
| | | | | "device_type" = <"nvram">
| | | | | "AAPL,phandle" =
| | | | | }
| | | | |
| | | | +-o AppleNVRAM
| | | | {
| | | | "IOClass" = "AppleNVRAM"
| | | | "IOProviderClass" = "AppleMacIODevice"
| | | | "IONameMatched" = "nvram"
| | | | "IONameMatch" = ("nvram")
| | | | }
Listing 2. Partial output from ioreg -S -l
위에 보시면 장치와 그에 해당하는 드라이버의 연결을 볼 수 있습니다. IOPlatformExpertDevice 클래스는 전체 기계를 대표하고, ApplePowerSurgePE 클래스는 또 그에 해당하는 장치에 대응합니다 (이것은 "플랫폼 엑스퍼트" 라는 것으로서 장치 간의 전체 배열을 담당합니다). ApplePowerSurgePE 클래스는 "bandit" IOPlatformDevice 클래스를 생성하고, AppleMacRiscPCI 클래스는 "bandit" 장치의 드라이버로 선택하고, 그렇게 되어 있습니다.
최종적으로 NVRAM 장치의 이름, 장치 종류, 메모리 내 억세스 정보를 담고 있는 "nvram" 장치에 도달합니다. AppleNVRAM 클래스가 해당 장치에 할당되어 있습니다. 이제 할 일은 이 클래스의 개정판인 커널 익스텐션을 작성하고, 커널 내에 있는 AppleNVRAM 대신 부팅중에 커널 익스텐션이 선택되도록 설정하는 일이었습니다.
... 다음에 계속 ...
최신글이 없습니다.
최신글이 없습니다.
댓글목록 0