Міжнародна конференція розробників
і користувачів вільного програмного забезпечення

Портирование линукс на КПК на базе WinMobile

Василий Хоружик, Минск, Беларусь, anarsoul@gmail.com

В докладе рассказывается о запуске Linux на устройствах с ARM-процессорами, работающих под управлением ОС WinMobile. Рассмотрена утилита Haret и особенности её использования для reverse engineering.

0. Мотивация

Так уж исторически сложилось, что основной операционной системой для меня является GNU/Linux, и нравится то, что если в ней что-то сломалось (или не работало изначально) – можно попробовать это починить.

1. Linux на КПК

Предысторией данной публикации послужило приобретение осенью 2006 года наладонного компьютера HP iPAQ RX1950 с WinMobile на борту. Предварительно, я изучил вопрос запуска любимой ОС на данном КПК, и ситуация выглядела неплохо – из WinMobile можно было загрузится до консоли, выводимой на UART (COM-порт), с корневой файловой системой на initramfs (в ОЗУ) или nfs (через USB-сеть).

После изготовления кабеля для общения с КПК через UART обнаружился ворох проблем: попытки доступа к карте памяти приводили к зависанию КПК; USB-сетью через некоторое время становилось невозможно пользоваться, т.к. задержки ping возрастали до 1-2 секунд; отсутствовала поддержка звука, WiFi, IrDA, PM и др. Драйвера были только для видео, CPU, UART, NAND-контроллера и USB-device.

2. Чтение документации

Как показывает практика, решить значительную часть проблем поддержки устройств можно на этапе изучения документации. Вооружившись спецификациями на System-on-Chip (данный КПК сделан на базе чипа S3C2442 от Samsung), первым делом я решил проблему с драйвером карты памяти. Оказалось, что несмотря на незначительные различия SD-контроллеров S3C2410 и S3C244X, драйвер для S3C2410 не распознавал контроллер на S3C2442. В сети была найдена версия, которая должна была работать на S3C244X, однако карта «отваливалась» при попытке драйвера тактировать ее на максимальной заявленной карточкой частоте. Исследование исходных кодов драйвера и спецификаций показало, что драйвер пытается задать частоту в 2 раза больше поддерживаемой. Таким образом, вскоре заработал драйвер s3c2440-mci, однако в последствии он не пригодился, т.к. в рамках проекта OpenMoko (http://openmoko.org) был разработан драйвер s3cmci, поддерживающий s3c24xx.

Чтением документации была также решена проблема выхода КПК из спящего режима: как оказалось, некоторые GPIO (интерфейсы ввода/вывода) нужно конфигурировать особым образом, чтобы КПК мог загрузится с NAND после пробуждения).

3. Реверс-инжиниринг #1. objdump

Однако нередки также случаи, когда необходимая документация недоступна или отсутствует, и приходится изучать поведение аппаратных компонент на действующем объекте.

На ARM-устройствах нет PnP, поэтому ядро не может определить где какое устройство находится, как его включать, куда разведены его GPIO (вроде детектирования наличия карты памяти). Эту информацию нужно указывать драйверам, иначе их функционал будет ограничен (возмножны ситуации когда драйвер вообще работать не будет).

У пользователей WinMobile есть как минимум два способа узнать подробности аппаратной начинки своего КПК. Первый, более сложный – дизассемблировать загрузчики и драйвера от WinMobile для прояснения ситуации. Перед разбором дизассемблированных драйверов имеет смысл обзавестись картой MMU – узнать что и как WinMobile отображает в своё адресное пространство. Второй способ – использование утилиты Haret для того, чтобы отслеживать, что и когда WinMobile пишет/читает в/из регистров контроллеров вашего System-on-Chip.

Первый способ хорош там, где нет возможности использовать Haret (в моем случае это были проблемы со спящим режимом КПК). Для дизассемблирования можно использовать objdump из toolchain или IDA от Windows.

Второй способ хорошо подходит для мониторинга состояния GPIO-пинов, отслеживания прерываний, а также значений регистров контроллеров. Рассмотрим этот способ подробнее.

4. Реверс-инжиниринг #2. Handhelds advanced reverse engineering tool

В первую очередь, Haret – это загрузчик Linux под WinMobile, но не только. Haret умеет перехватывать прерывания, отлавливать обращения по определённым адресам, включать\выключать экран КПК, а также проигрывать звук и мигать индикаторами.

Для того, чтобы воспользоваться этими дополнительными возможностями, нужен доступ к КПК по сети. Подходит usb-сеть, WiFi, Bluetooth или что-нибудь еще (перечисленные интерфейсы лично опробованы).

Для удобства общения с Haret требуется скачать haretconsole — набор скриптов на питоне, подставляющих имена регистров вместо их адресов и т.д. После настройки сети между ПК и КПК необходимо запустить Haret на КПК и нажать в нём “Listen for connection”, а на ПК запустить haretconsole:

./console ip-адрес-нашего-КПК

Наиболее частыми используемыми командами будут:

  • addlist <список> <значение> – пополнение списков (например, списка адресов GPIO)
  • ibit номера_битов – добавление в список игнорирования отдельных битов GPIO либо отдельных прерываний
  • watch <список> [<время>] – следить за изменением значений из списка адресов
  • wirq <время> – отлавливать прерывания заданное время

Полный список команд можно посмотреть введя в консоли help.

Пример использования Haret:

========  Connecting to haret  ========
Minimal virtual address: 00010000, maximal virtual address: 7FFFFFFF
Detected machine RX1950/s3c2442 (Plat='PocketPC' OEM='HP iPAQ rx1950')
CPU is ARM ARM arch 4T stepping 0 running in system mode
Enter 'HELP' for a short command summary.

HaRET(1)# wirq 1
000.001 IRQS INTPND=02000000: INT_USBD=1
000.001 IRQS EINTPEND=00000000:
000.001 IRQS INTPND: INT_USBD(25)=1
000.000 IRQS INTPND: INT_USBD(25)=1
000.000 IRQS INTPND: INT_USBD(25)=1
000.039 IRQS INTPND: INT_TIMER2(12)=1


Видно, что непрерывно идут прерывания от контроллера USB (в примере используется USB-сеть) и от таймера. Для отсечения лишней информации их можно маскировать:
HaRET(3)# ibit irqs 12 25

После выполнения команды вывод этих сообщений не производится:
HaRET(4)# wirq 10

Нажатия на кнопки показывают следующее:
001.879     IRQS EINTPEND: EINT18(50)=1 # Нужные номера GPIO
002.050     IRQS EINTPEND: EINT18(50)=1
003.047     IRQS EINTPEND: EINT19(51)=1
003.227     IRQS EINTPEND: EINT19(51)=1
004.506     IRQS EINTPEND: EINT14(46)=1
004.643     IRQS EINTPEND: EINT14(46)=1
006.318     IRQS EINTPEND: EINT12(44)=1
006.481     IRQS EINTPEND: EINT12(44)=1
Restoring windows exception handlers...
Finished restoring windows exception handlers.
Handled 413 irq, 3856 abort, 2029 prefetch, 0 lost, 0 errors

Для мониторинга GPIO лучше замаскировать заведомо ненужные биты:
HaRET(5)# ibit gpios 32 33 65 66 67 68 75 76 77 78 79 98 99 100 101 102 \
103 107 108 109 110 111
HaRET(6)# watch gpios 5

Извлечение карты памяти показывает:
002.930    GPIOS   GPFDAT: GPF5(165)=1
002.930    GPIOS   GPHDAT: GPH8(232)=1
002.960    GPIOS   GPEDAT: GPE5(133)=0 GPE6(134)=0 GPE7(135)=0 \
GPE8(136)=0 GPE9(137)=0 GPE10(138)=0
002.960    GPIOS   GPJDAT: GPJ1(257)=0
002.960    GPIOS   GPECON: GDE5(426 427)=1 GDE6(428 429)=1 \
GDE7(430 431)=1 GDE8(432 433)=1 GDE9(434 435)=1 GDE10(436 437)=1

Как видно, изменились какие-то биты GPIO. Изучив вывод Haret перед командой watch gpios (в примере выше он не приведен), можно обнаружить, что GPF5 и GPH8 работают на ввод, GPJ1 на вывод, а GPE5-10 – это выводы контроллера карты памяти, и их можно игнорировать. Из этого можно сделать вывод, что GPJ1 управляет питанием, а GPF5 и GPH8 отвечают за детектирование наличия карты и переключатель RO/RW на карте. Что из них что можно выяснить опытным путём, поменяв положение переключателя RO/RW на карточке. Аналогичным образом можно узнать, какой из выводов отвечает за детектирование подключения USB кабеля, наушников, кабеля зарядки и т.д.

Данный способ (wirq и watch <список>) подходит лишь для медленно изменяющихся значений. Для захвата быстрых последовательностей, например, последовательности выключения экрана, стоит использовать mmutrace:
<preHaRET(1)# addlist mmutrace p2v(0x56000010) # p2v преобразует HaRET(2)# addlist mmutrace p2v(0x56000014) # физический адрес HaRET(3)# addlist mmutrace p2v(0x56000018) # в виртуальный
Этими тремя командами мы добавили мониторинг обращения по 3 адресам (можно добавлять не только отдельные адреса, но и регионы).

HaRET(4)# wirq 5

Теперь выключаем экран:
001.308 078713f0: ldr    r3, [r2]       #   GPBDAT==00000124
001.308 078713f8: str    r3, [r2]       #   GPBDAT =00000124
001.308 078713fc: ldr    r3, [r4]       #   GPBCON==0015545a
001.308 07871408: str    r3, [r4]       #   GPBCON =00155459
001.358 0b8c40b0: ldr    r3, [r2, #20]  #   GPBDAT==00000126
001.358 0b8c40b8: str    r3, [r2, #20]  #   GPBDAT =00000124
001.358 0b8c40c0: ldr    r3, [r2, #16]  #   GPBCON==00155459
001.358 0b8c40cc: str    r3, [r2, #16]  #   GPBCON =00155455

Таким образом, можно получить последовательность конфигурации GPIO для операций, критичных ко времени – включение/выключение экрана, мониторинг батареи, управление питанием WiFi и др.

5. Результат

На сегодняшний день у порта Linux на RX1950 остались нерешёнными две проблемы: управление питанием звукового кодека и управление питанием WiFi, всё остальное работает. Базовая поддержка RX1950 попадёт в версию 2.6.35 ядра (соответствующие патчи были приняты в upstream в мае 2010), драйвера батареи и светодиодов – в последующие версии.

Ведутся работы по исправлению имеющихся проблем.

Материалы к докладу