¿Por qué necesitamos un programa separado en la misma memoria de programa flash de un microcontrolador, específicamente STM32F103, que se llama gestor de arranque?
¿Qué tiene de especial para mantenerlo separado del programa de aplicación principal?
En términos generales, ¿un cargador de arranque de un sistema basado en microprocesador (por ejemplo, PowerPC MPC8270) hace el mismo trabajo que el de un microcontrolador (por ejemplo, ARM STM32F103) o realizan trabajos fundamentalmente diferentes entre sí y, sin embargo, ambos se denominan 'cargador de arranque'? ?
Un cargador de arranque en un microcontrolador es responsable de actualizar el firmware principal a través de un canal de comunicación que no sea el encabezado de programación. Esto es útil para actualizar el firmware en el campo a través de BLE, UART, I2C, tarjetas SD, USB, etc. Sería extremadamente inconveniente pedir a los clientes que compren programadores solo para actualizar el firmware en sus dispositivos.
La razón por la que el cargador de arranque se mantiene separado es por confiabilidad. El cargador de arranque y el código de la aplicación se colocan en secciones separadas de la memoria flash, de modo que el cargador de arranque pueda borrar y volver a escribir el código de la aplicación sin cambiar nada relacionado con el código del cargador de arranque.
Si el cargador de arranque y la aplicación se mantuvieran juntos, el código del cargador de arranque tendría que copiarse en la RAM antes de que pudiera ejecutarse, ya que cualquier actualización de firmware borraría el código del cargador de arranque en la memoria flash. Si se cortara la energía con el código del cargador de arranque en la RAM y se borrara el flash, el dispositivo se bloquearía.
main()
función. Al encender, el código de inicio del cargador de arranque se ejecuta y llama al archivo main()
. El programa del cargador de arranque busca un programa de aplicación válido y luego salta al código de inicio del programa de aplicación que llama al archivo main()
. El código de inicio de cada programa inicializa el entorno de tiempo de ejecución de C para el programa respectivo (es decir, inicializa variables, pila, etc.) y, por lo general, ninguno de los programas main()
vuelve nunca al código de inicio.main
en absoluto.Para que el proceso de carga pueda recuperarse de errores. Supongamos que hay un error de comunicación o se desconecta la alimentación durante una actualización. Si el cargador de arranque fuera parte de la aplicación que estaba actualizando, el usuario no podría volver a intentarlo sin usar un hardware especial para actualizar el cargador de arranque.
Algunos microcontroladores no pueden ejecutar código desde la RAM. Si el cargador de arranque se mezcló con el resto del software, entonces no podría actualizar su software porque no puede borrar las páginas de flash que está ejecutando actualmente. La solución consiste en grabar primero el código nuevo en la segunda mitad de la memoria flash y luego saltar a ella. Luego, el nuevo código se copia a sí mismo en la primera mitad de la memoria flash. Por supuesto, la desventaja es que la grabación del flash suele ser lenta y ahora que tiene que hacerlo dos veces, el proceso de carga puede demorar hasta el doble. Además, esta solución limita el tamaño de su aplicación para que no supere la mitad de su flash total.
Los cargadores de arranque bien escritos intentan verificar que existe un código válido en el dispositivo antes de intentar ejecutarlo. Si el cargador de arranque y otro código se mezclaron, ¿cómo podría estar seguro de que su rutina de validación funcionaría si todo el código no se cargara?
Autenticación. Los cargadores de arranque seguros intentan verificar que la aplicación cargada coincida con una firma digital antes de ejecutarse. Pero si el cargador de arranque y otro código se mezclaron, entonces no puede controlar lo que se ejecuta en el dispositivo porque una vez que el usuario carga el código nuevo, no puede controlar lo que sucede al inicio.
Por lo general, están allí para permitirle actualizar su programa de aplicación principal.
Necesita algún código que sepa cómo borrar y reprogramar parte del flash interno, ese no puede ser el programa principal, ya que cuando se borra no podría reprogramarse.
El cargador de arranque permite que la MCU se comunique con otra cosa para aceptar un nuevo programa, almacenarlo y ejecutarlo después de un reinicio. Si no tenía un gestor de arranque, se necesita un programador para acceder a la memoria y poner el programa en su lugar.
Además de las otras respuestas correctas sobre permitir la reprogramación del firmware principal desde el cargador de arranque, otro beneficio de tener el cargador de arranque separado es que puede separar lógicamente las tareas "hacer una vez en el arranque" del código que necesita durante el tiempo de ejecución. Luego, después de que el cargador de arranque termine sus tareas de configuración inicial, el firmware principal puede desalojar el cargador de arranque con todo su código que ya no se necesita de la memoria, lo que ahorra un espacio de memoria RAM significativo. Es posible lograr esto de otras maneras, pero la división del cargador de arranque/firmware lo hace mucho más fácil en muchas arquitecturas.
La respuesta corta es porque el software es asombroso.
Podría tener todo lo que hace el cargador de arranque como "hardware puro". Pero es mucho, mucho, mucho más fácil hacer que las tareas del cargador de arranque se escriban como software y luego se interpreten por hardware.
Estas tareas pueden implicar configurar el hardware para que se ejecute el software "real" (por ejemplo, en una Raspberry Pi (a través de @ErikF)), tener un protocolo para reemplazar el programa "real" antes de que se ejecute (verifique un pin, si ese pin se configura y luego vuelve a actualizar el programa real), o incluso configurar el entorno de software para el programa "real".
En software de menor escala, cuando ejecuta un ejecutable, el cargador de aplicaciones se mueve, hace cosas como cargar partes de sus datos en la memoria, a veces corrige direcciones, configura argumentos para principal u otras cosas globales, activa las bibliotecas proporcionadas por su sistema operativo y luego salta al inicio del _main
código. Algunas de estas cosas se pueden hacer con un gestor de arranque.
En un microcontrolador, algunas de las tareas que realiza un cargador de arranque podrían dividirse en el programa. El compilador de su plataforma podría inyectar automáticamente el código de "configuración" en cada ejecutable.
Pero tenerlo en el gestor de arranque significa que el mismo compilador podría funcionar en hardware diferente, ya que el gestor de arranque puede "ocultar" la diferencia entre las plataformas.
Para colmo, el hecho de que un flash del programa principal no pone en riesgo el cargador de arranque (y la capacidad de volver a flashear el programa principal), y tener un cargador de arranque no trivial es una gran cosa.
Una parte de la pregunta que no ha sido respondida hasta ahora es la diferencia entre cargadores de arranque en microcontroladores y sistemas de microprocesadores.
microcontrolador
La mayoría de los microcontroladores tienen una memoria ROM incorporada que contiene su código de programa. Cambiar este código generalmente requiere un dispositivo programador que se conecta a la interfaz de programación del microcontrolador (por ejemplo, ISP en ATMega). Pero estas interfaces de programación generalmente no son muy convenientes de usar, en comparación con otras interfaces, ya que es posible que no estén disponibles en el contexto dado. Entonces, por ejemplo, aunque casi todas las computadoras cuentan con puertos USB, la interfaz SPI necesaria para ISP es mucho más rara, y otras interfaces como la interfaz PID utilizada en ATXMega solo son compatibles con hardware de programación dedicado.
Entonces, por ejemplo, si desea actualizar el software desde una computadora normal sin ningún hardware externo, puede usar un gestor de arranque que lea desde un tipo diferente de interfaz (por ejemplo, RS232, USB o RS232 a través de USB como en Arduino) para programar el dispositivo sobre interfaces comunes.
Dicho esto, si no necesita esta funcionalidad, el cargador de arranque es completamente opcional. El microcontrolador aún puede ejecutar su código completamente sin el gestor de arranque.
Microprocesador
En un microprocesador las cosas son un poco diferentes. Si bien la mayoría de los microprocesadores cuentan con una ROM que es lo suficientemente grande para un cargador de arranque, esas ROM no son lo suficientemente grandes para contener un sistema operativo completo. Entonces, el propósito del cargador de arranque es inicializar el hardware, buscar un sistema operativo de arranque, cargarlo y ejecutarlo. Por lo tanto, el cargador de arranque es fundamental para cada arranque.
En los sistemas x86/x64, este cargador de arranque es el BIOS o el UEFI (básicamente, una versión más nueva de un BIOS).
A veces, incluso puede tener varios cargadores de arranque ejecutándose en una cadena. Por ejemplo, si tiene un sistema de arranque dual con Windows y Linux, podría terminar con lo siguiente:
Entonces, en este caso, había tres piezas de software que pueden considerarse un gestor de arranque. Tanto GRUB como el cargador de arranque de Windows están principalmente ahí para brindarle al usuario una opción de selección de arranque más conveniente que la que les daría el BIOS/UEFI. También permite que se inicien varios sistemas operativos desde el mismo disco duro o incluso desde la misma partición.
TLDR
Entonces, mientras que en ambos sistemas el cargador de arranque hace cosas similares (ayuda al usuario a elegir qué código arrancar), ambos difieren mucho en cómo logran eso y qué hacen exactamente.
Una respuesta que no se ha cubierto es la necesidad de separar las preocupaciones con respecto al código de inicio.
Esto se hace para configurar ciertas cosas como:
Esta es una aproximación muy aproximada de los pasos tomados y estoy describiendo el proceso de arranque ARM, es diferente nuevamente para x86 y otras arquitecturas.
Sin embargo, la razón principal sigue siendo la misma: la asignación de la pila C debe realizarse desde el ensamblaje.
Emobe
Lame caliente
ghelquist