SONY PRS-300/PRS-505 hack

03.03.2010

Я работаю инженером в маленьком стартапе, занимающимся разработкой программного обеспечения для электронных книг. Для популяризации нашей продукции было принято решение о создании порта на некоторые известные платформы. Например - SONY PRS 505/300 очень популярные книги, но их программная “начинка” оставляет желать лучшего, так как отсутствует поддержка разнообразных популярных форматов. да и вообще, признаться, работать с этой книгой не комфортно.

И так, постановка задачи следующая - есть готовая платформа и есть исходный код нашего приложения, необходимо запустить на этой платформе наш код с минимальными затратами.

В качестве жертвы была приобретена книга SONY PRS 300. Важно отметить, что платформа у 505 и 300 абсолютно идентичная. Внутри находится Freescale i.MXL MC9328MXLVP20 (ARM920T core, 200MHz), 64 MB RAM, 512MB Internal.Инструментарий, который понадобился - паяльный инструмент, осциллограф, логический анализатор и, естественно,PC с Linux.

Этап первый. Знакомство.

Книга сделано очень качественно, и разбирать ее было действительно жалко. Большое количество винтов и вспомогательных крепежных элементов. Первый взгляд на книгу - в левой верхней части находится посадочное место под диагностический разъем. Используя осциллограф, был обнаружен консольный выход, с номерами ног X,Y. Подключившись к нему видно старыйMontavista Linux3.0 на ядре 2.4. родной аппликейшен монтируется в /opt, все разделы и ПО лежат в cramfs. Начинаем по порядку. Быстрый взгляд на /dev - есть fb0, подозрительный ttyDB0, rtc0 и npm. больше ничего интересного. Как оказалось в последствии, вся работа с клавиатурой и батарейкой шли через этот ttyDB0, на котором находится сопроцессор, и работа по большей части свелась к разбору интерфейса.

что уже есть готовое?

На сайте сони представлен исходный код ядра. Есть так же исходники тулчейна, но его собрать не удалось, по этому пришлось пользоваться своими, версия libc на книге - XXX.

Экран.

Быстрый взгляд на драйвер фрейм буфера - все просто, с экраном проблем совершенно не оказалось. Фреймбуфер ремапится без проблем, по этому работа с экраном свелась к преобразованию существующих данных в форматAAA00000b, где AAA номер цвета.

int i,*fb,tmp;
int pio_fd = open ( "/dev/fb0", O_RDWR);
int t= ioctl (pio_fd, FBIO_EINK_GET_TEMPERATURE, NULL);
fb= mmap(0, 800*600, PROT_WRITE, MAP_SHARED, pio_fd, 0);
memcpy(fb,image,800*600);
ioctl (pio_fd, FBIO_EINK_DISP_PIC, 0);
close(pio_fd);

коды IOCTL любезно предоставлены драйвером fb0.

Часы.

Как оказалось, устройство /dev/rtc0 лочится единственно выполняющимся соневским апликйшеном. Действительно, в драйвере идет проверка при попытке открыть устройство, и так как родное приложение наотрез отказывается прекращать работу с часами, пришлось их отложить на будущее. таким образом, работа с часами возможно только в случае деактивации родного аплийкейшена.

Клавиатура.

Вот тут начинается самое интересное. клавиатуры как таковой нет, но есть интерфейс ttyDB0, общаясь с которым можно получить доступ к клавиатуре. Подключение клавиатуры следующее - KBD->SUBCPI->CPU. Этот сопроцессор, помимо работы с клавиатурой, так же контролирует батарейку, USB и еще некоторые элементы. После длительных поисков в интернете был обнаружен Service Manual, где можно было узнать что это за сопроцессор и с чем его едят. И, естественно, протокол общения нигде не объявлен. Не понятно какими командами извлекается напряжение батарейки, как доставляются сообщения с клавиатуры и прочее. вторая существенная проблема, это конфигурирование интерфейса. Например, если убить родной аплийкейшен, то настройки интерфейса сбрасывались в дефолтное состояние, и, даже после повторной конфигурации порта, клавиатура не работала. Т.е. работа с клавиатурой была возможна только в случае работы родного аппликейшена.

Сложилась плохая ситуация, и нужно было как-то из нее выходить. Итак, первое что нужно было узнать - протокол передачи данных между CPU и SUBCPU. Для этого был подсоединен анализатор логики прямо на шину, запущен родной аппликейшен, и срисованы диаграммы передачи данных. Кроме этого, были записаны все диаграммы ухода в сон и пробуждения устройства, а так же загрузки и отключения. Как оказалось, протокол был очень прост.

5 байт посылка, сумма первого и последнего 0xff. При получении посылки приемная сторона отвечает своего родаACK.

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

На этом этапе была сделана первая сборка нашего приложения - клавиатура и экран работают, а что еще нужно ).

Клавиатура 2.

Клавиатура по-прежнему отказывалась работать самостоятельно, хотя были старательно восстановлены настройки порта. Забегая вперед, расскажу в чем же проблема. Дело в том, что если убивать родное приложение в определенные моменты, до того как книга уходила в сон, то после этого никакими средствами оживать клавиатуру не удавалось, даже повторно запустив родное приложение. Это, к сожалению, породило ложную мысль, что SUBCPUкак-то дополнительно настраивается после выхода из сна, из-за чего повторно были пересмотрены все графики уходов и пробуждения. Таким образом, спустя некоторое время, клавиатура работала без родного аппликейшена, и был осуществлен возврат к часам, кои были еще не настроены.

Часы 2.

С часами все просто. Стандартный интерфейс. Абсолютно стандартный и без изысков.

Клавиатура 3.

Для того, что бы узнать, как правильно конфигурировать ttyDB0 интерфейс, на родной аппликейшен был натравленstrace. К сожалению, strace затыкался примерно на 20% запуска приложения. И никаким образом не хотел дальше его грузить. Этот факт, конечно, не мог не расстраивать - ведь нужно еще отладить Power Managment, а до него даже не доходила загрузка. Главное, первая попытка натравить strace выдала полную конфигурацию ttyDB0 интерфейса:

01:05:28.260000 ioctl(12, SNDCTL_TMR_START or TCSETS, {c_iflags=0x14, c_oflags=0, c_cflags=0x9bf, c_lflags=0, c_line=0, c_cc[VMIN]=5, c_cc[VTIME]=0,c_cc="\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"}) = 0

Так образом, к этому моменту, работал наш собственный аппликейшен на SONY PRS300, и в целом можно было уже ощутить разницу, между родным и не родным.

Power managment and strace.

По какой-то непонятно причине, один раз strace все-таки смог уйти в сон с запущенным аппликейшеном, и стало видно как ведется работа с питанием. Рабочим устройством является /dev/npm, к которому уходит в общей сложности 6 запросов. Strace показал с какими номерами ioctl были сделаны эти запросы. После открытия исходников npm драйвера, стало понятно что одних номеров тут мало.

А суть работы npm в следующем - есть набор устройств, драйверов, и у каждого есть свой PM вызов. В пользовательском аппликейшене создаются структуры под каждое устройство и под каждый его режим работы. После этого структуры объединяются в массив структур, соответствующий определенной совокупности состояний устройств. например - у нас есть 3 устройства, каждое из которых может работать и не работать. и мы создаем 3 режима работы - (0,0,0) - (1,1,1) - (1,0,1). Добавление каждого режима - syscall.

Таким образом, необходимо было узнать, в какой последовательности и какие структуры уходили в ядро. Для этого был создан свой npm драйвер, сделан был chroot на внутренней памяти, создан inod для работы с драйвером и сам драйвер был загружен в ядро. После этого запустили родной аппликейшен, который как ни в чем не бывало стал работать с /dev/npm, даже не подозревая что это не родной драйвер. Содержимое всех структур было выловлено, на основе чего было сделано apm приложение.

NPM and RTC

Как оказалось, сон у книжки настолько глубокий, что прерывание с часов не выбивает книгу из сна. По этому ежеминутного обновления часов нет.

А теперь все вместе.

А теперь осталось это все вместе заставить работать. Если с часами проблем не было, то NPM наотрез отказывался работать одновременно с клавиатурой. Книга просто не уходила в сон без нашего chroot. Поковыряв некоторое время родной аппликейшен в IDA (дизассемблер), я попытался снова натравить на него strace. В этот раз strace натравливался не на аппликейшен целиком, а на один из процессов, висящих в памяти. И, действительно, мне повезло - один из потоков непосредственно отвечал за уход книги в сон, а главное, в нем так же производилась конфигурация ttyDB0 интерфейса каждый раз по пробуждению и сну.

Но! Создав абсолютно идентичное по strace приложение, я все еще не мог заставить книгу уходить в сон. Проблема оказалась смешная - избыточное количество диагностики, после отключения которой, моя книга начала засыпать.

Заключение.

В заключении я хотел бы пояснить, зачем я написал эту статейку. В процессе работы я несколько раз сталкивался с проблемами, которые не только было не понятно как решать, но и вообще не ясно в каком направлении копать. И находясь в конечной точке, я уже вижу, что, например, можно было не использовать логический анализатор, но в тот момент никаких других инструментов не было. По совокупности же, ничего экстраординарного не сделано, а просто применены некоторые всем известные механизмы в определенной последовательности. Задача действительно была сложная, и я рад, что довел ее до конца. Надеюсь описанный выше процесс может быть для кого-то полезен.