operating-system bootloader

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:

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 .