
Ingeniería inversa de la ROM de arranque del TI AM3358
Probablemente han pasado dieciocho meses desde que conseguí unas cuantas placas Beaglebone Black, salvadas de la basura. Desafortunadamente, las placas no funcionaron de inmediato. Ahora bien, era la primera vez que trabajaba con una de estas placas, o con cualquier computadora de placa única, para el caso, así que no estaba seguro de si el problema era algo que yo estaba haciendo o algo mal con las propias placas (quizás por eso estaban destinadas a la basura en primer lugar). Me tomó bastante tiempo y una gran cantidad de esfuerzo lograr que estas placas realmente comenzaran a arrancar, pero demonios, lo logré. Y esto es lo que aprendí en el camino.
He incluido algunas utilidades en este repositorio, junto con un archivo xml exportado desde Ghidra que tiene todos los símbolos que he obtenido hasta ahora mediante la ingeniería inversa. Usé esta publicación para exportar sin el firmware real, para evitar cualquier problema de derechos de autor, por si acaso. Si quieres depurar la ROM de arranque por ti mismo, ya tendrás el JTAG conectado, así que puedes volcar la ROM de arranque (de 0x20000 a 0x2BFFF) por tu cuenta.
Para cargar los símbolos:
0x20000; también puedes establecer el nombre del bloque como bootrom.main(), o al manejador de arranque por tarjeta MMC/SD.Para empezar, sabía que estas eran versiones personalizadas del beaglebone black estándar, así que al principio determiné que podría ser algo que faltaba en la propia placa, como un identificador de placa. Lo que vi al arrancar una tarjeta SD estándar formateada con balenaEtcher fue simplemente nada. Esperaba que los LEDs de la placa comenzaran a parpadear, y esperaba que conectar un cable UART a USB me permitiera ver el proceso de U-Boot. Sin embargo, la UART estaba en silencio. Si retiraba la tarjeta SD, emitía la letra C una y otra vez, lo cual es un comportamiento esperado para un arranque por UART/serie. Definitivamente estaba intentando arrancar, y la tarjeta SD estaba alterando este comportamiento, pero no tenía más visibilidad. La mayoría de las guías de solución de problemas en la web tomaban la salida de U-Boot como punto de partida para diagnosticar problemas. Supongo que no iba a tener ese lujo.
En ese punto, pensé que valdría la pena conectar una sonda de depuración. Desafortunadamente, no tenía un conector compatible con el footprint existente, así que hice uno propio.
La placa beaglebone tiene un conector con la designación P2 que expone las conexiones JTAG. Conecté algunos cables desde este a un conector hembra para poder comunicarme con él a través de mi J-Link.



Al lanzar Ozone (el depurador de Segger), configuré el J-Link y comencé simplemente intentando encontrar el punto de entrada. Había pensado que un reset-halt me colocaría donde necesitaba estar, que fue como llegué a la suposición (incorrecta) de que el punto de entrada era 0x2148a, aunque ciertamente noté que esto no era consistente. Más tarde, me di cuenta de que las placas AM335x no se llevan bien con el reset-halt del J-Link, por lo que en realidad había un retraso de quizás unos cientos de ciclos de reloj, aterrizando en algún lugar dentro de un manejador de arranque, de manera indeterminista. (Finalmente lo solucioné escribiendo un archivo GEL para Code Composer Studio de TI, que soporta depuración con J-Link: en el reset, el registro PC se establece en el manejador de reset, los registros se limpian y el modo de instrucción se fuerza a ARM).
De un hilo en los foros de TI (AM335x: empleados de TI, ¿dónde puedo obtener el código fuente/símbolos del ROM Bootloader?) obtuve un par de símbolos de depuración: SPI Initialize en 0x231e0, SPI ReadSectors en 0x23230, y 0x24bfa es una rutina que realiza una lectura por UART. Eso es una buena ayuda, supongo. Noté que el arranque fallaba terminando en un bucle infinito en 0x402f0440, un bucle sin salida. Mmm, bastante lejos del resto de la ROM de arranque; debe estar en la RAM o algo así. ¡Probablemente sea hora de acudir al manual de referencia técnica (TRM)!
El Capítulo 26 del TRM contiene muchísima información sobre el arranque. Obtenemos la siguiente vista de la ROM de arranque:

Descripción:
La arquitectura del código ROM público se muestra en la Figura 26-1. Está dividida en tres capas principales con un enfoque de arriba hacia abajo: alto nivel, controladores y capa de abstracción de hardware (HAL). Cada capa se comunica con una capa de nivel inferior a través de una interfaz unificada. La capa de alto nivel está a cargo de las tareas principales del código ROM público: configuración del watchdog y de los relojes, y la rutina principal de arranque. La capa de controladores implementa los protocolos lógicos y de comunicación para cualquier dispositivo de arranque de acuerdo con la especificación de la interfaz. Finalmente, la HAL implementa el código de más bajo nivel para interactuar con las IPs de infraestructura de hardware. Los dispositivos de arranque finales están conectados a los pads de E/S del dispositivo.

La Figura 26-2 ilustra el flujo de alto nivel del procedimiento de arranque del código ROM público. En este dispositivo, el código ROM público comienza al completarse el inicio seguro (realizado por el código ROM seguro). El código ROM realiza entonces la configuración e inicialización de la plataforma como parte del procedimiento de inicio público. La lista de dispositivos de arranque se crea basándose en los pines SYSBOOT. Un dispositivo de arranque puede ser un dispositivo de arranque por memoria (memoria flash soldada o un dispositivo de arranque temporal, como una tarjeta de memoria) o una interfaz periférica conectada a un host. El bucle principal del procedimiento de arranque recorre la lista de dispositivos de arranque e intenta buscar una imagen en el dispositivo de arranque seleccionado actualmente. El bucle se abandona si se encuentra una imagen de arranque válida y se ejecuta correctamente, o si vence el watchdog. El procedimiento de autenticación de imagen se realiza antes de la ejecución de la imagen en un dispositivo HS. Un fallo en el procedimiento de autenticación provoca una ramificación a un “bucle infinito” en la ROM segura (a la espera de un reset por watchdog).