한 줄 요약: 닌텐도 Wii에서 Mac OS X를 구동하는 포팅 프로젝트가 실존한다 — Wii의 PowerPC 계열 CPU 덕에 가능했고, 실행된 버전은 릴스가 말한 10.4 타이거가 아니라 10.0 치타다.
최근 한 릴스가 화제가 되었습니다. 닌텐도 위(Wii)로 맥북을 만든 개발자의 이야기입니다. 거실에서 볼링을 치던 그 게임기에서 애플의 Mac OS X이 실행된다는 내용이었습니다. 릴스는 길이가 짧아서 ‘PowerPC라서 가능했다’, ‘부트로더와 커널 패치, 드라이버를 수정했다’ 정도만 짚고 넘어갑니다. 그 안에 어떤 기술이 있었는지 풀어 설명하겠습니다.
정정: 릴스에서는 ’Mac OS X 10.4 타이거’가 실행되었다고 했지만, 원본 프로젝트가 포팅한 것은 10.0 치타(Cheetah)입니다. 타이거는 아직 포팅되지 않았습니다. 릴스 대사에 약간 부정확한 부분이 있었습니다.
왜 위에서 Mac OS X이 실행될 수 있었는가: PowerPC 아키텍처
핵심은 CPU 아키텍처에 있었습니다. 닌텐도 위의 CPU인 ’브로드웨이(Broadway)’는 IBM이 제작한 PowerPC 750CL로, 729MHz로 동작합니다. 이 칩은 애플이 과거 G3 아이북과 아이맥에 사용했던 PowerPC 750CXe의 후속 모델입니다. 즉 위와 그 시절 Mac은 같은 PowerPC 계열의 CPU를 사용했던 것입니다.
애플은 2005년부터 2006년까지 Intel로 전환하기 전까지 Mac을 PowerPC 아키텍처로 구동했습니다. 그래서 PowerPC용으로 제작된 옛 Mac OS X을 위의 PowerPC CPU에서 실행할 수 있는 가능성이 존재했던 것입니다. CPU가 같은 명령어 집합을 사용하기 때문입니다.
위의 하드웨어, 현실은 어땠는가
릴스에서 ’처참한 스펙’이라고 언급한 부분은 사실입니다.
- CPU: 브로드웨이(PowerPC 750CL) 729MHz, 요즘 스마트워치보다 성능이 낮은 단일 코어
- RAM: 24MB(1T-SRAM, MEM1) + 64MB(GDDR3, MEM2)를 합쳐 총 88MB
- GPU: ’할리우드(Hollywood)’라는 시스템 칩(SoC). 그 안에 ARM 기반 보조 프로세서인 ’스타렛(Starlet)’도 포함되어 있습니다
가장 큰 장벽은 메모리였습니다. Mac OS X 10.0 치타의 공식 최소 메모리는 128MB인데, 위는 88MB가 전부입니다. 개발자 Bryan Keller는 QEMU(에뮬레이터)로 64MB에서도 치타가 부팅되는지 먼저 검증한 뒤, 시스템에서 불필요한 구성 요소를 제거하는 방식으로 88MB에 맞췄습니다.
부트로더(Bootloader): 컴퓨터가 켜질 때 가장 먼저 실행되는 코드
부트로더는 전원을 켰을 때 운영체제를 메모리에 올리고 실행을 넘겨주는 첫 번째 프로그램입니다.
옛 PowerPC Mac의 부팅 순서는 다음과 같았습니다: Open Firmware(펌웨어)가 BootX(Mac OS X 부트로더)를 거쳐 XNU(커널)로 제어를 넘깁니다. Keller는 여기서 Open Firmware와 BootX를 건너뛰고, 위 전용 부트로더를 처음부터 직접 작성했습니다(이름은 wiiMac). 위 하드웨어는 고정되어 있으므로 하드웨어를 일일이 스캔할 필요 없이, 최소한의 초기화와 커널 로드, 디바이스 트리 생성만 수행하면 되었습니다.
커널 패치: Mac OS X의 핵심을 수정하여 끼워 넣다
Mac OS X의 코어(Darwin)는 오픈소스이므로, XNU라는 커널의 소스 코드를 직접 수정할 수 있습니다. Keller는 위에 맞게 커널을 패치했습니다.
- 커널은 Mach-O라는 실행 파일 포맷으로 저장됩니다. 부트로더는 이 포맷을 해독하여 커널을 메모리에 올립니다.
- 위의 메모리 배치(MEM1은 0x00000000, MEM2는 0x10000000)가 Mac OS X가 기대하는 배치와 달라서, 메모리 주소 변환(BAT, Block Address Translation) 설정을 수정했습니다.
- 디버깅은 위 앞면 LED를 깜빡이게 하는 방식으로 수행했습니다. 직렬 디버그 출력이 불가능하므로, 커널 안의 특정 위치에 있는 명령을 ‘LED 켜기’ 명령으로 교체(바이너리 패치)하면서 진행 상황을 추적했습니다. 디스어셈블러(Hopper)로 커널 이진 파일을 분석했습니다.
디바이스 트리(Device Tree): 하드웨어 목록을 운영체제에 알려주기
운영체제는 ’이 기기에 어떤 하드웨어가 연결되어 있다’는 정보를 알아야 합니다. 실제 Mac에서는 부트로더가 하드웨어를 스캔하여 이 목록(디바이스 트리)을 생성하지만, 위는 하드웨어가 항상 동일하므로 Keller는 트리를 코드에 하드코딩했습니다.
IOKit 드라이버: 운영체제와 하드웨어를 잇는 다리
드라이버는 운영체제가 하드웨어를 제어할 수 있게 해주는 코드입니다. Mac OS X은 IOKit이라는 드라이버 모델을 사용하며, C++로 작성되고 ‘제공자와 클라이언트(provider-client)’ 구조로 연결됩니다. 특정 장치 드라이버가 ’눕(nub)’이라는 연결점에 부착되고, 그 눕이 다시 상위 드라이버와 통신하는 방식입니다.
Keller가 위에 맞춰 작성한 드라이버들이 핵심입니다.
- 할리우드(Hollywood) 드라이버: 위는 Mac처럼 PCI 버스를 사용하지 않고, ’할리우드’라는 전용 SoC로 하드웨어를 연결합니다. 그 안의 ARM 코프로세서인 ’스타렛’이 실제 하드웨어 접근을 담당하며, PowerPC 코어와는 IPC(프로세서 간 통신)로 대화합니다. Mac용 PCI 드라이버를 그대로 사용할 수 없으므로 위 전용 드라이버를 새로 작성했습니다.
- SD 카드 드라이버: 시스템이 설치된 SD 카드를 읽기 위해 필요합니다. 스타렛(ARM)에서 실행되는 MINI라는 프로그램에 IPC로 명령을 보내 SD 카드를 읽고 썼습니다. 까다로운 부분은 ’캐시 일관성’입니다. ARM이 작성한 데이터를 PowerPC가 읽을 때 캐시된 이전 값이 나오지 않도록, 캐시가 적용되지 않는(uncached) 메모리를 사용했습니다.
- 프레임버퍼 드라이버: 화면을 표시하려면 픽셀 데이터가 들어 있는 메모리(프레임버퍼)가 필요합니다. 문제는 위의 영상 하드웨어가 YUV 포맷을 요구하는데 Mac OS X은 RGB를 사용한다는 점이었습니다. 그래서 RGB 프레임버퍼와 YUV 프레임버퍼 두 개를 준비하고, 1초에 60번 RGB를 YUV로 변환하여 출력하는 방식으로 해결했습니다.
- USB 드라이버: 키보드와 마우스를 사용하려면 위의 USB 포트가 작동해야 합니다. Mac OS X의 USB 드라이버(AppleUSBOHCI)는 ’리틀 엔디안’을 가정하지만, 위는 ’역 리틀 엔디안(reversed-little-endian)’이라는 특수한 방식이라 바이트 순서가 맞지 않았습니다. 게다가 당시 USB 스택 소스 코드가 없어서 IRC에서 누군가가 공유해 준 과거 소스를 구하여 수정했고, 역어셈블러(Ghidra)로 바이트 스왑 명령을 찾아 패치하기도 했습니다.
홈브류와 BootMii: 위에서 임의 코드를 실행하기 위해
이 모든 작업의 전제 조건은 ’위에서 자신의 코드를 실행할 수 있어야 한다’는 것이었습니다. 위는 ’탈옥(jailbreak)’이 가능한 기기이므로, Homebrew Channel과 BootMii 같은 도구로 누구나 하드웨어에 직접 접근하여 임의 프로그램을 실행할 수 있습니다. Keller의 부트로더도 이 경로를 통해 실행되었습니다.
어떻게 연결되는가
한 줄로 요약하면: 같은 PowerPC 계열 CPU라는 공통분모 덕분에 Mac OS X을 위에서 실행할 ’가능성’이 존재했고, 그 가능성을 직접 작성한 부트로더와 패치한 XNU 커널, 위 전용 IOKit 드라이버로 현실로 만든 프로젝트입니다. 핵심은 Mac OS X의 오픈소스 코어(Darwin/XNU/IOKit)를 수정할 수 있었다는 점입니다. 닫힌 부분(상위 GUI)은 그대로 실행되게 두고, 열린 부분만 위에 맞춰 수정한 것입니다.
릴스가 말한 ’하드웨어의 한계는 생각보다 유연하다’는 것은, 결국 ’같은 아키텍처와 오픈소스 코어, 집요한 패치’가 만나야 비로소 가능한 이야기였습니다.
출처: Bryan Keller의 블로그 “Porting Mac OS X to the Nintendo Wii” · GitHub wiiMac · GitHub wii-macosx-cheetah-drivers · MacRumors 보도.
