visopsys mikeos menuetos kolibrios assembler assembly operating-system

assembly - menuetos - mikeos



¿Cómo ejecutar un programa sin un sistema operativo? (2)

¿Cómo se ejecuta un programa por sí mismo sin un sistema operativo en ejecución? ¿Puede crear programas de ensamblaje que la computadora puede cargar y ejecutar al inicio, por ejemplo, iniciar la computadora desde una unidad flash y ejecuta el programa que está en la CPU?


¿Cómo se ejecuta un programa por sí mismo sin un sistema operativo en ejecución?

Coloca el código binario en un lugar donde el procesador busca después de reiniciar (por ejemplo, la dirección 0 en ARM).

¿Puede crear programas de ensamblaje que la computadora puede cargar y ejecutar al inicio (por ejemplo, iniciar la computadora desde una unidad flash y ejecuta el programa que está en la unidad)?

Respuesta general a la pregunta: se puede hacer. A menudo se denomina "programación bare metal". Para leer desde la unidad flash, desea saber qué es USB y desea tener algún controlador para trabajar con este USB. El programa en este disco también debería estar en algún formato particular. En algún sistema de archivos en particular ... Esto es algo que generalmente hacen los cargadores de arranque. Muchos tableros ARM te permiten hacer algunas de esas cosas. Algunos tienen gestor de arranque para ayudarlo con la configuración básica.

Here puede encontrar un gran tutorial de cómo hacer un sistema operativo básico en Raspberry PI.

Editar: Este artículo y todo el wiki.osdev.org responderá a la mayoría de sus preguntas http://wiki.osdev.org/Introduction

Además, si no desea experimentar directamente en el hardware, puede ejecutarlo como una máquina virtual utilizando hipervisores como qemu. Vea cómo ejecutar "hello world" directamente en el hardware ARM virtualizado here .


Ejemplos ejecutables

Técnicamente, un programa que se ejecuta sin un sistema operativo, es un sistema operativo. Así que veamos cómo crear y ejecutar minúsculos sistemas operativos hello world.

El código de todos los ejemplos a continuación está presente en este repositorio de GitHub . Todo fue probado en Ubuntu 14.04 AMD64 QEMU y hardware real ThinkPad T430.

Sector de arranque

En x86, lo más simple y lo más bajo que puede hacer es crear un sector de inicio maestro (MBR) , que es un tipo de sector de arranque , y luego instalarlo en un disco.

Aquí creamos uno con una sola llamada printf :

printf ''/364%509s/125/252'' > main.img sudo apt-get install qemu-system-x86 qemu-system-x86_64 -hda main.img

main.img contiene lo siguiente:

  • /364 en octal == 0xf4 en hexadecimal: la codificación de una instrucción hlt , que le dice a la CPU que deje de funcionar.

    Por lo tanto, nuestro programa no hará nada: solo comienza y se detiene.

    Usamos octal porque /x números hexadecimales no están especificados por POSIX.

    Podríamos obtener esta codificación fácilmente con:

    echo hlt > a.asm nasm -f bin a.asm hd a

    pero la codificación 0xf4 también está documentada en el manual de Intel, por supuesto.

  • %509s producen 509 espacios. Necesario para completar el archivo hasta el byte 510.

  • /125/252 en octal == 0x55 seguido de 0xaa : bytes mágicos requeridos por el hardware. Deben ser los bytes 511 y 512.

    Si no está presente, el hardware no lo tratará como un disco de arranque.

Tenga en cuenta que incluso sin hacer nada, algunos caracteres ya están impresos en la pantalla. Esos son impresos por el firmware y sirven para identificar el sistema.

Ejecutar en hardware real

Los emuladores son divertidos, pero el hardware es el verdadero negocio.

  • Grabe la imagen en una memoria USB (¡destruirá sus datos!):

    sudo dd if=main.img of=/dev/sdX

  • Enchufe el USB en una computadora

  • encenderlo

  • dígale que arranque desde el USB.

    Esto significa hacer que el firmware seleccione USB antes del disco duro.

    Si ese no es el comportamiento predeterminado de su máquina, siga presionando Enter, F12, ESC u otras teclas extrañas después del encendido hasta que obtenga un menú de inicio donde puede seleccionar iniciar desde el USB.

    A menudo es posible configurar el orden de búsqueda en esos menús.

Hola Mundo

Ahora que hemos hecho un programa mínimo, pasémonos a un mundo de hola.

La pregunta obvia es: ¿cómo hacer IO? Algunas opciones:

  • pregunte al firmware, por ejemplo, BIOS o UEFI, si es necesario para nosotros
  • VGA: región de memoria especial que se imprime en la pantalla si está escrita. Se puede usar en modo protegido.
  • escribe un controlador y habla directamente con el hardware de la pantalla. Esta es la forma "correcta" de hacerlo: más poderosa, pero más compleja.

Aquí vamos a hacer un ejemplo de BIOS ya que es más simple. Pero tenga en cuenta que no es el método más robusto.

Aquí está el código GAS:

.code16 .global _start _start: cli mov $msg, %si mov $0x0e, %ah loop: lodsb or %al, %al jz halt int $0x10 jmp loop halt: hlt msg: .asciz "hello world" .org 510 .word 0xaa55

Además de las instrucciones de ensamblaje de usuario estándar, tenemos:

  • .code16 : le dice a GAS que .code16 código de 16 bits

  • cli : deshabilita las interrupciones de software. Esos podrían hacer que el procesador empiece a funcionar nuevamente después del hlt

  • int $0x10 : hace una llamada BIOS. Esto es lo que imprime los personajes uno por uno.

  • .org 510 y .word 0xaa55 : coloque los bytes mágicos al final

Una manera rápida y sucia de compilar esto es con:

as -o main.o main.S ld --oformat binary -o main.ing -Ttext 0x7C00 main.o

y ejecuta main.img como antes.

Aquí hay dos banderas importantes:

  • --oformat binary : --oformat binary código de ensamblado binario en bruto, no lo deforma dentro de un archivo ELF como es el caso para los archivos ejecutables regulares de usuario.

  • -Ttext 0x7C00 : necesitamos decirle al vinculador ld dónde se colocará el código para que pueda acceder a la memoria.

    En particular, esto se usa durante la fase de reubicación. Lea más sobre esto here .

La mejor forma de compilar es usar un script de enlazador limpio como este . La secuencia de comandos del enlazador también puede colocar los bytes mágicos para nosotros.

Firmware

En verdad, su sector de arranque no es el primer software que se ejecuta en la CPU del sistema.

Lo que realmente se ejecuta primero es el llamado firmware , que es un software:

  • hecho por los fabricantes de hardware
  • típicamente de fuente cerrada pero probablemente basada en C
  • almacenado en la memoria de solo lectura, y por lo tanto, más difícil / imposible de modificar sin el consentimiento del proveedor.

Los firmwares bien conocidos incluyen:

  • BIOS : antiguo firmware x86 omnipresente. SeaBIOS es la implementación de código abierto predeterminada utilizada por QEMU.
  • UEFI : sucesor del BIOS, mejor estandarizado, pero más capaz e increíblemente hinchado.
  • Coreboot : el noble arco cruzado de código abierto

El firmware hace cosas como:

  • bucle sobre cada disco duro, USB, red, etc. hasta que encuentre algo de arranque.

    Cuando ejecutamos QEMU, -hda dice que main.img es un disco duro conectado al hardware, y

    hda es el primero en ser probado, y se usa.

  • cargue los primeros 512 bytes en la dirección de memoria RAM 0x7c00 , ponga allí el RIP de la CPU y déjelo funcionar

  • mostrar cosas como el menú de inicio o las llamadas de impresión del BIOS en la pantalla

El firmware ofrece una funcionalidad tipo OS de la que dependen la mayoría de los OS-es. Por ejemplo, un subconjunto de Python se ha portado para ejecutarse en BIOS / UEFI: https://www.youtube.com/watch?v=bYQ_lq5dcvM

Se puede argumentar que los firmwares son indistinguibles de los sistemas operativos, y que el firmware es la única programación "verdadera" de metal desnudo que uno puede hacer.

Como dice este desarrollador de CoreOS :

La parte difícil

Cuando enciende una PC, las fichas que componen el chipset (northbridge, southbridge y SuperIO) aún no se han inicializado correctamente. Aunque la ROM del BIOS está lo más alejada posible de la CPU, la CPU puede acceder a ella, porque debe ser así, de lo contrario la CPU no tendría instrucciones para ejecutar. Esto no significa que la ROM del BIOS esté completamente mapeada, generalmente no. Pero lo suficiente está mapeado para que el proceso de arranque funcione. Cualquier otro dispositivo, solo olvídalo.

Cuando ejecuta Coreboot en QEMU, puede experimentar con las capas superiores de Coreboot y con cargas útiles, pero QEMU ofrece pocas oportunidades de experimentar con el código de inicio de bajo nivel. Por un lado, la RAM solo funciona desde el principio.

Estado inicial del BIOS posterior

Como muchas otras cosas en el hardware, la estandarización es débil, y una de las cosas con las que no debe confiar es en el estado inicial de los registros cuando el código comienza a ejecutarse después del BIOS.

Así que hágase un favor y use un código de inicialización como el siguiente: https://.com/a/32509555/895245

Los registros como %ds y %es tienen efectos secundarios importantes, por lo que debe ponerlos a cero incluso si no los está usando explícitamente.

Tenga en cuenta que algunos emuladores son más agradables que el hardware real y le dan un buen estado inicial. Luego, cuando ejecutas hardware real, todo se rompe.

Multiboot GRUB

Los sectores de arranque son simples, pero no son muy convenientes:

  • solo puedes tener un sistema operativo por disco
  • el código de carga tiene que ser realmente pequeño y encajar en 512 bytes. Esto podría resolverse con la llamada int 0x13 BIOS .
  • tienes que hacer mucho arranque, como pasar al modo protegido

Es por esas razones que GRUB creó un formato de archivo más conveniente llamado multiboot.

Ejemplo de trabajo mínimo: https://github.com/cirosantilli/x86-bare-metal-examples/tree/d217b180be4220a0b4a453f31275d38e697a99e0/multiboot/hello-world

Si prepara su sistema operativo como un archivo de arranque múltiple, GRUB puede encontrarlo dentro de un sistema de archivos normal.

Esto es lo que la mayoría de las distribuciones hacen, colocando imágenes de sistema operativo bajo /boot .

Los archivos de arranque múltiple son básicamente un archivo ELF con un encabezado especial. Están especificados por GRUB en: https://www.gnu.org/software/grub/manual/multiboot/multiboot.html

Puede convertir un archivo de arranque múltiple en un disco de inicio con grub-mkrescue .

El Torito

Formato que se puede grabar en CD: https://en.wikipedia.org/wiki/El_Torito_%28CD-ROM_standard%29

También es posible producir una imagen híbrida que funciona en ISO o USB. Esto se puede hacer con grub-mkrescue ( example ), y también lo hace el kernel de Linux en make isoimage usando isohybrid .

BRAZO

ARM-land tiene sus propias convenciones (o más falta de convenciones, ya que ARM otorga licencias de IP a los proveedores que lo modifican), pero las ideas generales son las mismas:

  • escribes el código en una dirección mágica en la memoria
  • haces IO con direcciones mágicas

Algunas diferencias incluyen:

  • IO se hace escribiendo a direcciones mágicas directamente, no hay instrucciones de out y out
  • necesita agregar blobs de fuente cerrada compilados mágicamente proporcionados por proveedores a su imagen. Esas son algo así como BIOS, y eso es algo bueno, ya que hace que la actualización de ese firmware sea más transparente.

Aquí hay unos ejemplos:

Recursos