Este no es un sistema de operación real . Es solo un sistema de operación simple creado con fines educativos.
El objetivo principal que estoy siguiendo es aprender cómo el sistema operativo está trabajando desde cero. A partir del propio sector de arranque, hardware e interrupciones de software, controladores propios.
El repositorio se sufre con GCC porque estoy planeando escribir otro sistema operativo simple con óxido. Entonces, espero que habrá Ghaiklor-Os-GCC y Ghaiklor-Os-Rustc .
| Hola Mundo |
|---|
Ghaiklor-OS-GCC consta de dos archivos en formato binario sin procesar: boot.bin y kernel.bin . Están ubicados en Boot/Boot.Bin y Kernel/Kernel.Bin en consecuencia después de la compilación.
Boot.bin se compila a través de NASM . Makefile toma Boot/Boot.asm y llama nasm boot/boot.asm -f bin -o boot/boot.bin . Las manijas de NASM incluyen de los subdueños en sí, por lo que todos los archivos de ensamblaje se compilarán en binario. Nada más, simple.
Kernel.bin se compila a través de GCC y LD de compilador cruzado que debe instalar. Eche un vistazo a la sección de entorno de desarrollo. Una vez instalado el compilador cruzado, podemos tomar fuentes de CPU , controladores , incluir carpetas de núcleo y libc recursivamente. Todos los archivos .c se compilan a través de gcc . Los archivos de objetos compilados se utilizan para compilar kernel.bin luego, a través de ld ld -o kernel/kernel.bin -Ttext 0x1000 <OBJ_FILES> --oformat binary .
OS-Image.Bin es compilado Concatenate de Boot.Bin y Kernel.Bin . Fácilmente logrado con cat boot/boot.bin kernel/kernel.bin > os-image.bin .
Escribí el script bootstrap.sh , que puedes ejecutar. Instalará todas las dependencias necesarias para su máquina host.
bash bootstrap.shCuando se enciende o reinicia una computadora, se ejecuta a través de una serie de diagnósticos llamados Post-Power-on-Self-Test. Esta secuencia culmina en la ubicación de un dispositivo de arranque, como un disquete, CDROM o un disco duro.
Un dispositivo es arrancable si lleva un sector de arranque con la secuencia de bytes 0x55 , 0xAA en bytes 511 y 512 respectivamente. Cuando el BIOS encuentra dicho sector de arranque, se carga en la memoria a 0x0000:0x7C00 .
Una simple implementación del dispositivo de arranque:
jmp $
times 510 - ($ - $$) db 0
dw 0xAA55 $ - $$ Resultados en CURRENT_POINTER - START_POINTER . De esa manera estamos calculando cuánto tiempo es nuestro registro de arranque. Posteriormente, estamos sustrando 510 y llenamos con ceros, obteniendo el registro de arranque de 512 bytes con la firma del sector de arranque.
Por ejemplo, tenemos $ - $$ igual a 100. Entonces, tenemos 510 - 100 = 410 bytes gratuitos. Estamos llenando estos 410 bytes con ceros. Y los dos últimos bytes 511 y 512 son firmas de arranque que estamos llenando con dw 0xAA55 .
¡Hecho! Tenemos nuestro dispositivo de arranque y podemos reemplazar nuestro jmp $ con cualquier código que desee.
Implementación del sector de arranque
Al principio, nuestro código se ejecuta en modo real.
El modo real es un modo simplista de 16 bits que está presente en todos los procesadores X86. El modo real fue el primer diseño del modo X86 y fue utilizado por muchos sistemas operativos tempranos. Para fines de compatibilidad, todos los procesadores X86 comienzan la ejecución en modo real.
¿Qué es malo y bueno en modo real?
Contras
Pros
Debido a las muchas limitaciones y problemas que tiene el modo real, necesitamos cambiar al modo protegido.
El modo protegido es el principal modo de funcionamiento de los procesadores Intel modernos desde el 80286. Permite trabajar con varios espacios de dirección virtuales, cada uno de los cuales tiene un máximo de 4 GB de memoria direccionable.
Dado que la CPU inicializada por el BIOS comienza en modo real, cambiar al modo protegido evita que use la mayoría de las interrupciones del BIOS. Antes de cambiar al modo protegido, debe deshabilitar las interrupciones, incluidas NMI, habilitar la línea A20 y la tabla de descriptor global de carga.
Algoritmo para cambiar al modo protegido:
cli
lgdt [ gdt_descriptor ]
mov eax , cr0
or eax , 0x1
mov cr0 , eax
jmp CODE_SEG:init_pmImplementación para cambiar a PM
Tabla de descriptor global
Pero, podemos ir más allá ...
¿Qué es el modo largo y por qué configurarlo?
Desde la introducción de los procesadores X86-64, también se ha introducido un nuevo modo, que se llama modo largo. El modo largo básicamente consiste en dos submodos que son el modo real de 64 bits y el modo de compatibilidad (32 bits).
Lo que nos interesa es simplemente el modo de 64 bits, ya que este modo proporciona muchas características nuevas, como:
Antes de cambiar al modo largo, debemos verificar si la CPU admite este modo. En caso de que si la CPU no admite el modo largo, necesitamos retroceso al modo protegido.
Detectar si el modo largo es compatible
Si es así, cambie a modo largo
Todos estos modos son geniales, pero no podemos escribir un sistema operativo en 512 bytes. Entonces, nuestro sector de arranque debe saber cómo cargar nuestro núcleo compilado del disco duro.
Cuando estamos en modo real, podemos usar interrupciones de BIOS para leer desde el disco. En nuestro caso, IS INT 13,2 - Read Disk Sectors .
¿Cómo usarlo?
;; al = number of sectors to read (1 - 128)
;; ch = track/cylinder number
;; cl = sector number
;; dh = head number
;; dl = drive number
;; bx = pointer to buffer
mov ah , 0x02
mov al , 15
mov ch , 0x00
mov cl , 0x02
mov dh , 0x00
mov dl , 0
mov bx , KERNEL_OFFSET_IN_MEMORY
int 0x13 Este código resulta en leer del disco duro en la dirección KERNEL_OFFSET_IN_MEMORY . Lee 15 sectores a partir del segundo y lo almacena por dirección KERNEL_OFFSET_IN_MEMORY .
Dado que nuestra imagen compilada del sistema operativo es una concatenación del sector de arranque y el núcleo, y sabemos que nuestro sector de arranque es 512 bytes, podemos estar seguros de que nuestro núcleo comienza en el segundo sector.
Cuando la lectura se completa con éxito, podemos llamar a la instrucción en nuestro KERNEL_OFFSET_IN_MEMORY y dar ejecución al kernel.
call KERNEL_OFFSET_IN_MEMORY
jmp $Implementación para la lectura del disco
Podemos dibujar una línea aquí sobre nuestro sector de arranque. El flujo es simple:
call ;En este paso, nuestro sector de arranque terminó su trabajo y comienza a trabajar con el núcleo.
Puede navegar a través de fuentes de arranque e intentar obtener cómo funciona.
Cuando llamamos a la instrucción por dirección, podemos tener algunos problemas. No podemos estar seguros de que la instrucción por dirección es un kernel_main() . La solución es simple.
Podemos escribir una sub-rutina que se adjunta al inicio del código del núcleo. Esta función externa de llamada sub -rutina de nuestro núcleo - kernel_main() . Cuando los archivos de objetos se vinculen juntos, esta llamada se traducirá a la llamada de nuestro kernel_main() .
global _start
[bits 32]
[extern kernel_main]
_start:
call kernel_main
jmp $Implementación de entrada de kernel
En este paso, tenemos un punto de entrada a nuestro método kernel_main() . Y ese es nuestro punto de entrada para todo el núcleo.
Creo que es aburrido explicar cómo funciona #include y qué sucede en nuestro kernel_main() . Fácilmente puede seguir los métodos que estoy llamando.
Entrada de núcleo en C
Esa es la parte más simple.
Necesitamos construir una imagen boot/boot.bin En formato binario sin procesar. Para hacerlo, llamamos al ensamblador nasm con banderas especiales.
nasm boot/boot.asm -f bin -o boot/boot.binResulta en formato binario sin procesar que puede ejecutar a través de QEMU.
En este paso, tenemos el sector de arranque compilado.
Necesitamos construir todas las fuentes de todas las carpetas de manera recursiva, excepto la carpeta boot .
Todos los archivos C se compilan en archivos de objetos a través de gcc y archivos de ensamblaje a través de nasm :
gcc -g -ffreestanding -Wall -Wextra -fno-exceptions -m32 -std=c11 -c < SOURCE > -o < OBJ_FILE >
nasm < SOURCE > -f elf -o < OBJ_FILE > Resulta en todos los archivos de objetos necesarios para vincular al formato binario sin procesar. Todo lo que queda por hacer es vincularlos a través de ld :
ld -o kernel/kernel.bin -Ttext 0x1000 kernel/kernel_entry.o < OBJ_FILES > --oformat binary Tenga en cuenta que kernel/kernel_entry.o en primer lugar, ya que tenemos un problema para llamar al kernel_main() . De esta manera, garantizamos que la primera instrucción se llamará desde nuestro boot/kernel_entry.asm .
Después de todo, hemos compilado la imagen del núcleo en formato binario bruto.
Dado que nuestro sector de arranque y kernel son formatos binarios crudos, solo podemos concatenarlos.
cat boot/boot.bin kernel/kernel.bin > os-image.bin Ahora, podemos ejecutar os-image.bin a través de qemu-system-i386 . BIOS tratando de localizar el sector de arranque, descubra nuestro boot/boot.bin y ve firma. Comienza a ejecutar nuestro código de ensamblaje en boot/boot.bin que carga nuestro kernel/kernel.bin a través de int 13,2 en la memoria y lo ejecuta.
Así es como todo funciona en conjunto. No dude en navegar a través del proyecto, ¿gracias?
La licencia del MIT (MIT)
Copyright (c) 2016 Eugene Obrezkov
El permiso se otorga, de forma gratuita, a cualquier persona que obtenga una copia de este software y archivos de documentación asociados (el "software"), para tratar en el software sin restricción, incluidos los derechos de los derechos de usar, copiar, modificar, fusionar, publicar, distribuir, sublicense y/o vender copias del software, y para permitir que las personas a quienes se les proporciona el software para hacer, sujeto a las siguientes condiciones: las siguientes condiciones: las siguientes condiciones: las siguientes condiciones:
El aviso de derechos de autor anterior y este aviso de permiso se incluirán en todas las copias o porciones sustanciales del software.
El software se proporciona "tal cual", sin garantía de ningún tipo, expresa o implícita, incluidas, entre otros, las garantías de comerciabilidad, idoneidad para un propósito particular y no infracción. En ningún caso los autores o titulares de derechos de autor serán responsables de cualquier reclamo, daños u otra responsabilidad, ya sea en una acción de contrato, agravio o de otra manera, que surge, de o en relación con el software o el uso u otros tratos en el software.