Решил написать историю о том, как я дебрикал Нук (восстанавливал убитый прошивкой до версии 1.0.0), дабы не было мыслей вроде "гад Номад нашел какие кнопки надо зажать для дебрика, а делиться не хочет".
Далее следует стена текста, интересная только железячникам и линуксоидам. Нормальным людям лучше не читать :)
Дебют
Началось все с того, что приехал мне брикнутый девайс с просьбой попробовать его починить. Симптомы классические - надпись "Restoring to Factory update" и тишина.. Где-то через три дня пайки, собирания шлейфов и медитации над логами и сорцами, я осознал, как именно выглядит наша проблема. Девайс грузит u-boot, которому вообще все-равно с какой памятью работать, затем должен грузиться turboboot (промежуточное ядро, которое и умеет делать апдейт/откат), а затем уже полноценное ядро и система. Так вот, turboboot от версии 1.0.0 (а за ним и основное ядро) при инициализации MMC ругается и не подхватывают NAND память - она использует протокол MMC 4.3 или 4.4, поддержка которых появилась лишь в ядре Линукса 2.6.36, а для B&N прошивок 1.4 и 1.5 была бекпортнута на наше ядро. Кстати, именно по-этому и не стартует 1.5.0k на новых Нуках - там та же ошибка.
К этому же времени правдами и неправдами мне удалось выполнить собственный код на брикнутом Нуке, но вышла глупая ситуация - в системе не создалось устройство для NAND, потому и починить ничего нельзя. Возможно, будь я супер-спецом по процессорам и железу, смог бы написать код для обращения к NAND напрямую, но для меня это темный лес.. Другой идеей было взять код UBoot, выкусить работу с MMC и написать свою программу. Примерная оценка трудозатрат на это - не меньше недели фулл-тайм работы, включая изучение чужого кода, идеологии его работы, отладку и тестирование. Много часов я пытался собрать драйвер MMC в виде модуля, который бы выгрузил имеющийся в ядре код и заменил его (это не бред, в драйвере
тачскрина я так и сделал), но оказалось, что поддержка нашего железа прибита гвоздями к внутренностям ядра и сделать это очень и очень тяжело (даже если откинуть в сторону всякие "мелочи" вроде невозможности поиска и выгрузки шины по имени, т.к. код
find_bus закомментирован в линуксе очень давно).
Миттельшпиль
На другой день я узнал о такой сказочной фиче как kexec. В двух словах, это возможность запустить еще одно ядро прямо из выполняющегося. Я думал, сам turboboot так и работает, но был жестоко разочарован - там какой-то проприетарный код для перехода на другое ядро, а само ядро скомпилировано без поддержки kexec.. Перерыв кучу форумов, я нашел проект, в котором люди пытались сделать kexec в виде
подгружаемого модуля. Это была смелая идея, но как я понял, проект таки не был доведен до ума и работал только на Motorola Milestone и DroidX. Самое неприятное, что сам модуль собирали для другого ядра (2.6.29 или старше), да еще и с ARMv7 кодом (у нас ARMv6 процессор). Не говоря о том, что поиск таблицы системных вызовов там был закоменчен и стоял адрес, к нам отношения не имеющий, а сам код после раскомментирования толком ничего и не искал. Уже не скажу, сколько пришлось его бекпортить и дорабатывать
подручными средствами, но таки модуль собрался, научился искать syscall_table и его .ko файл мне удалось подгрузить прямо в работающее ядро turboboot.
На этом этапе еще немного пришлось повозиться (снова подручные средства) с исполняемым кодом kexec-tools, дабы его таки запустить, и оказалось, что попытка загрузки любого ядра приводит к перезагрузке устройства... По-сути, модуль сыроват и не дописан, направление бесперспективное. С этой пессимистичной мыслью я и пошел спать.
На следующий день, занимаясь основной работой я вдруг подумал - а если ребут не результат бага, а запланированное поведение? Так и оказалось - один из драйверов WM8350 при попытке выгрузки считал, что время перезапускать систему и отключал питание :) Повлиять на сам драйвер не получилось, потому пришлось переделывать код выгрузки всех драйверов, чтобы именно этот игнорировался. Я чувствовал, что от этого еще будут проблемы, но решил рискнуть. Как результат - устройство стало не перегружаться, а просто сказало "Bye!" и зависло :)
Пат
Расстраивался я не долго и перебором нашел, что не смотря на поддержку uImage и zImage, kexec грузит только не арихвированные ядра. Тут бы и истории конец, но оказалось, что все самосборные ядра, а заодно и заводские 1.4-1.5 доходят до инициализации GPIO и зависают.. Чуть дальше загружалось ядро, выкушенное из turboboot, но потом валилось при загрузке драйвера LCD экрана, да и в целом пользы от него было бы не много. Следующие день или два (казалось, что прошла целая вечность) я собирал собственные ядра, выкидывал оттуда все, что можно (зачем нам e-ink или звук, когда надо только перепрошиться?) и комментировал места, где происходили зависания. В общих случаях так делать нельзя - ядро без GPIO и работать-то не должно, но тут мне таки повезло и я собрал супер-обрезанное ядро, которое умело буквально только работать с MMC (поддержку протокола 4.3 я уже бекпортнул из старших версий ядра). Заодно я сделал собственный образ рам диска для этого ядра и написал скрипт, который при загрузке зальет на MMC карту turboboot и остановится. Тут еще оказался мелкий казус с размещением turboboot - из-за каких-то багов скрипт не мог узнать размер MMC карты (кривой бекпорт? особенность запуска из-под kexec?), потому я рассчитал точное размещение и вбил в скрипт фиксированное число. Это было очень опасно - смещение хоть на один байт привело бы к полному окирпичиванию, но все прошло успешно. На устройство залился новый turboboot, который видел внутреннюю память. Это была почти полная победа - оставалось лишь убедить нового turboboot сделать откат на заводскую прошивку. Он теперь видел NAND и корректно развернул туда заводской образ.. только вот этот образ оказался версии 1.0.0..
Эндшпиль
Не знаю, как туда попала 1.0.0 (видимо, ошибка B&N при изготовлении Refurbished нука), но получилось, что загружается новый turboboot, а затем грузит старое ядро, которое все еще не видит NAND. Более того, на этом этапе я уже не мог выполнить свой код, а нук не загружался и периодически откатывался на прошивку 1.0.0. Причем, в этой заводской прошивке не было turboboot.img, потому и нельзя было вернуться к предыдущему состоянию, потому ситуация выглядела откровенно плохо. Решив не расстраиваться, я вчитался в логи и заметил, что связка turboboot от 1.5 и kernel и 1.0.0 при каждом старте пытается загрузить файл /init (загрузчик Android), причем если вставить SD карточку, то файл ищется и на ней. Издав ликующий звук я записал туда скрипт для загрузки модуля kexec_load.ko и вызова kexec, но снова уперся головой в стену багов: ядро 1.0.0 отличалось от того, что внутри turboboot 1.0.0 и не имеет поддержки /proc/iomem. Это устройство должно сообщать, по каким адресам расположена физическая память, без чего kexec работать не может. Адреса этой памяти далеко не 0-0x1000000, как можно подумать, но к счастью в тоннах логов нужные адреса нашлись и я прописал их в коде самого kexec.
Загрузилось мое мини-ядро, но теперь я заливал на NAND не turboboot, а целиком bravo_update.dat от
прошивки 1.5.0n. Конечно, адреса пришлось заранее высчитать на калькуляторе, но тут ошибка уже не была так страшна, да и в целом все прошло успешно - устройство откатилось на "заводскую" 1.5.0n и стало полностью работоспособным.
Вывод
Как видите, ничего особо сложного в этой операции не было, и если кто-то захочет, то без проблем сможет пройти мой путь, написать нужные патчи, собрать модуль kexec, нужные рамдиски и скрипты, научиться выполнять код на брикнутом нуке. Я только буду этому рад :)
А пока этого не произошло, я готов восстановить любое количество брикнутых нуков за небольшое вознаграждение. Пишите на
g.a@ua.fm, договоримся.
Когда же мне это надоест - я выложу свои исходники kexec-mod, kexec-tools, модуля mmc и пр. Может кому-то пригодится.