Найти в Дзене
mamaich

Откуда в ARM процессоре взялось ядро RISC-V?

Ковыряясь с Яндекс ТВ Станцией я почему-то игнорировал строки лога про AOCPU: В определенный момент в голове созрела мысль - а вдруг там есть что-то полезное? Решил поковырять, что же это такое, так что опишу здесь найденную информацию, просто чтобы не забылось. Спойлер: секреты RSA/AES через AOCPU выдернуть не получится, но я все-равно не считаю время ковыряния потраченным зря - может этот опыт выстрелит в будущем. AOCPU расшифровывается как Always On CPU - энергоэффективное процессорное ядро (или сопроцессор), работающее, когда основной процессор спит. Этот сопроцессор реагирует на нажатие кнопок пульта, аппаратные кнопки, команды HDMI CEC, звуковые сигналы и наверняка на что-то еще. Он слишком маломощен, чтобы распознать команду "Алиса", но его достаточно, чтобы разбудить основной процессор для этого. А еще он имеет, хоть и ограниченный по диапазону адресов, но read-write доступ к основной оперативной памяти. Область памяти 0xf7000000 на процессорах Amlogic T3 у Яндекс ТВ Станций ча

Ковыряясь с Яндекс ТВ Станцией я почему-то игнорировал строки лога про AOCPU:

строки handler= содержат какие-то адреса после 0xf7000000
строки handler= содержат какие-то адреса после 0xf7000000

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

Спойлер: секреты RSA/AES через AOCPU выдернуть не получится, но я все-равно не считаю время ковыряния потраченным зря - может этот опыт выстрелит в будущем.

AOCPU расшифровывается как Always On CPU - энергоэффективное процессорное ядро (или сопроцессор), работающее, когда основной процессор спит. Этот сопроцессор реагирует на нажатие кнопок пульта, аппаратные кнопки, команды HDMI CEC, звуковые сигналы и наверняка на что-то еще. Он слишком маломощен, чтобы распознать команду "Алиса", но его достаточно, чтобы разбудить основной процессор для этого. А еще он имеет, хоть и ограниченный по диапазону адресов, но read-write доступ к основной оперативной памяти.

Область памяти 0xf7000000 на процессорах Amlogic T3 у Яндекс ТВ Станций частично доступна на чтение из Exception Level 0, в котором выполняется ядро Android, так что ее можно сдампить моей утилитой https://github.com/mamaich/physmem-dump. Не вся память доступна на чтение, моя утилита недоступные адреса выводит на экран и записывает в файл 0xFF:

-2

Но адреса, которые мы видим при загрузке (0xf701d548) и тд читаются и содержат внятный текст:

-3

Однако, если загрузить этот дамп в IDA и попробовать декомпилировать - внятный код не получится ни как ARM64, ни как ARM32, и даже ни как THUMB2. А всё потому, что этот сопроцессор вовсе не ARM!

Если вбить в github.com в поиск строку "Response AOCPU", которую видно в середине скриншота выше, то можно найти исходники:

-4

и в этих исходниках мы видим, что процессор - 32-битный RISC-V:

-5

Если немного покурить интернет и попинать ИИ, то можно узнать, что данный сопроцессор является вспомогательным, его код загружается BL2 (скорее всего лежит где-то рядом, если не внутри, BL31). Сопроцессор не имеет своего MMU, и вся доступная ему оперативная память задается кодом в BL2 (который, увы, в Яндекс ТВ Станции нам недоступен).

С этой информацией можем успешно декомпилировать код обработчиков, которые видели в логе при загрузке. Например, cmd=6 handler=f701d548:

cmd=6 это MBX_CMD_RPCUINTREE_TEST
cmd=6 это MBX_CMD_RPCUINTREE_TEST

Си-код функции xMboxUintReeTestCase приведен на скриншоте гита выше.

Функции RISC-V сопроцессора вызываются ARM ядром через mailbox. Исходники работы с mailbox есть в репозитории Khadas (и не только в нем), см функцию:

int scpi_send_data(void *data, int size, int channel, int cmd, void *revdata, int revsize);

Примеры использования - мой исходник, код u-boot (исходников команды "aocpu - Call AOCPU command through SCPI-over-Mailbox", доступной в u-boot телевизора, там нет, но функция scpi_send_data там в наличии), код ядра Linux.

Область памяти с кодом функций AOCPU доступна не только на чтение, но и на запись. Так что я не долго думая модифицировал функцию xMboxUintReeTestCase таким образом, чтобы она принимаемый параметр msg воспринимала как массив из трёх DWORD: адрес откуда читать, адрес, куда положить прочитанные данные, и длину. Тем самым я получил возможность сдампить всю память сопроцессора для дальнейшего анализа.

Код простой:

https://godbolt.org компилятор RISC-V 32-бит (в исходнике на github код без -O2)
https://godbolt.org компилятор RISC-V 32-бит (в исходнике на github код без -O2)

Для компиляции asm кода в бинарник RISC-V можно использовать сайт https://riscvasm.lucasteske.dev/

Далее я модифицировал уже упомянутый выше драйвер physmem_dump, переименовав его в risc_access, вызывая в нем пропатченный MBX_CMD_RPCUINTREE_TEST через scpi_send_data. Исходники выложил сюда: https://github.com/mamaich/ya-tv-aocpu

Дамп памяти идет довольно долго, все 4 гб делаются порядка 8 часов (рекомендую бить на части по 1-2 гб за раз). Главное, что телевизор не зависает/не перезагружается при обращении к "неправильным" участкам памяти, например, принадлежащим устройствам или TEE.

Память практически полностью совпадает с обычной оперативкой, за исключением блоков с 0x10000000 (очень похоже на область SRAM этого RISC-V сопроцессора), область 0x30000000 (похоже на код DSP сопроцессоров). Наверняка есть и другие несовпадения - я смотрел не сильно внимательно.

Интересно, что область помеченная основным процессором как Secure (например, TEE), недоступна, но не как обычно (не читается как 00 или FF и система не виснет/крашится). При обращении к данной памяти команда чтения просто пропускается, и в регистре, куда читали, остается его прежнее значение. В моем случае этот регистр ранее содержал счетчик, поэтому недоступная память в дампе выглядит как набор байтов с последовательно возрастающими значениями, например:

-8

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