operating system - ¿Cómo cargar el cargador de arranque de la segunda etapa desde la primera etapa?
operating-system bootloader (3)
En x86 harías lo siguiente (simplificado):
- Haga que el gestor de arranque cargue el sector n-ésimo del disco / disquete (desde donde esté arrancando) a la memoria y ejecútelo (es decir, cargue el segmento / desplazamiento y haga
retf
). Una mejor alternativa es buscar en el sistema de archivos un nombre de archivo determinado (p. Ej., KERNEL.BIN), pero necesitaría conocer el tipo de sistema de archivos (por ejemplo, FAT12 si está probando desde una imagen de disquete). - El kernel comenzaría entonces en modo real. Configura descriptores de código, GDT, etc., activa el direccionamiento de 32 bits (debe haber oído hablar de "A20") y finalmente ingresa al modo protegido. Entonces necesita un salto lejano a un segmento de código de 32 bits (el archivo kernel debe estar vinculado de forma que el código de 32 bits esté en una posición absoluta, por ejemplo, en el desplazamiento 512, justo después del modo de 16 bits en modo real) .
- El ensamblaje kernel de 32 bits, entonces, simplemente define
EXTERN _mykernel
(por ejemplo) y llama a ese símbolo. - Entonces puede comenzar a escribir su kernel como función C
mykernel
.
Bueno, eso fue una breve descripción de lo que hice hace unos años (con muchos copiar y pegar de Internet;). Si eso no es útil, aquí hay algunos buenos recursos web sobre el desarrollo del sistema operativo:
- http://www.brokenthorn.com/Resources/OSDevIndex.html
- http://wiki.osdev.org/Main_Page
- http://lowlevel.brainsware.org/wiki/index.php/Hauptseite (wiki con muchos desarrolladores de OS aficionados, solo en alemán ...)
Espero que ayude ^^
Escribí el gestor de arranque de primera etapa simple que muestra "Hola mundo" usando la interrupción de la BIOS. Ahora, como un próximo paso obvio para escribir una segunda etapa, pero ¿dónde debería existir el código para eso y cómo cargarlo desde la primera etapa?
Aquí hay un programa para la primera etapa
[BITS 16] ;Tells the assembler that its a 16 bit code
[ORG 0x7C00] ;Origin, tell the assembler that where the code will
;be in memory after it is been loaded
MOV SI, HelloString ;Store string pointer to SI
CALL PrintString ;Call print string procedure
JMP $ ;Infinite loop, hang it here.
PrintCharacter: ;Procedure to print character on screen
;Assume that ASCII value is in register AL
MOV AH, 0x0E ;Tell BIOS that we need to print one charater on screen.
MOV BH, 0x00 ;Page no.
MOV BL, 0x07 ;Text attribute 0x07 is lightgrey font on black background
INT 0x10 ;Call video interrupt
RET ;Return to calling procedure
PrintString: ;Procedure to print string on screen
;Assume that string starting pointer is in register SI
next_character: ;Lable to fetch next character from string
MOV AL, [SI] ;Get a byte from string and store in AL register
INC SI ;Increment SI pointer
OR AL, AL ;Check if value in AL is zero (end of string)
JZ exit_function ;If end then return
CALL PrintCharacter ;Else print the character which is in AL register
JMP next_character ;Fetch next character from string
exit_function: ;End label
RET ;Return from procedure
;Data
HelloString db ''Hello World'', 0 ;HelloWorld string ending with 0
TIMES 510 - ($ - $$) db 0 ;Fill the rest of sector with 0
DW 0xAA55 ;Add boot signature at the end of bootloader
Mire la implementación de GRUB aquí (etapa 1):
http://src.illumos.org/source/xref/illumos-gate/usr/src/grub/grub-0.97/stage1/stage1.S
Primero notó el punto de partida en 0x7c00 y la firma final de 0xaa55 para este primer sector. Desde dentro del desmontaje, puedes ver esto:
349 copy_buffer: 350 movw ABS(stage2_segment), %es 351 352 /* 353 * We need to save %cx and %si because the startup code in 354 * stage2 uses them without initializing them. 355 */ 356 pusha 357 pushw %ds 358 359 movw $0x100, %cx 360 movw %bx, %ds 361 xorw %si, %si 362 xorw %di, %di 363 364 cld 365 366 rep 367 movsw 368 369 popw %ds 370 popa 371 372 /* boot stage2 */ 373 jmp *(stage2_address) 374 375 /* END OF MAIN LOOP */ 376
Básicamente, la lógica es copiar el código de la etapa 2 en otra parte de la memoria, y luego saltar directamente allí, y eso es "boot stage2". En otras palabras, "boot stage1" se desencadena de manera efectiva desde el BIOS después de haber cargado el sector en la memoria, mientras que stage2 es donde se puede saltar, puede estar en cualquier lugar.
Ejemplo de BIOS de NASM mínimamente ejecutable que carga la etapa 2 y salta a ella
use16
org 0x7C00
; You should do further initializations here
; like setup the stack and segment registers.
; Load stage 2 to memory.
mov ah, 0x02
; Number of sectors to read.
mov al, 1
; This may not be necessary as many BIOS set it up as an initial state.
mov dl, 0x80
; Cylinder number.
mov ch, 0
; Head number.
mov dh, 0
; Starting sector number. 2 because 1 was already loaded.
mov cl, 2
; Where to load to.
mov bx, stage2
int 0x13
jmp stage2
; Magic bytes.
times ((0x200 - 2) - ($ - $$)) db 0x00
dw 0xAA55
stage2:
; Print ''a''.
mov ax, 0x0E61
int 0x10
cli
hlt
; Pad image to multiple of 512 bytes.
times ((0x400) - ($ - $$)) db 0x00
Compilar y ejecutar:
nasm -f bin -o main.img main.asm
qemu-system-i386 main.img
Resultado esperado: a
se imprime en la pantalla, y luego el programa se detiene.
Probado en Ubuntu 14.04.
Ejemplo de Saner GAS utilizando un script del enlazador y una inicialización más correcta (registros de segmentos, pila) en mi GitHub .