Problemas con bootstrap/kernel de C simple

Recientemente me he interesado en escribir mi propio sistema operativo realmente básico. Escribí (bueno, copié) un ensamblaje básico que establece una pila y hace algunas cosas básicas y esto pareció funcionar bien, sin embargo, intentar introducir C en la mezcla lo arruinó todo.

Tengo dos archivos de proyecto principales: loader.s, que es un NASM que crea la pila y llama a mi función C, y kernel.c, que contiene la función C básica.

Mi problema en este momento es esencialmente que QEMU se congela cuando ejecuto mi archivo kernel.bin. Supongo que mi código tiene varios problemas; quizás esta pregunta no sea realmente apropiada para un formato StackOverflow debido a su extrema especificidad. Los archivos de mi proyecto son los siguientes:

cargador.s:

BITS 16                         ; 16 Bits

extern kmain                    ; Our 'proper' kernel function in C

loader:
    mov ax, 07C0h           ; Move the starting address [7C00h] into 'ax'
    add ax, 32              ; Leave 32 16 byte blocks [200h] for the 512 code segment
    mov ss, ax              ; Set 'stack segment' to the start of our stack
    mov sp, 4096            ; Set the stack pointer to the end of our stack [4096 bytes in size]

    mov ax, 07C0h           ; Use 'ax' to set 'ds'
    mov ds, ax              ; Set data segment to where we're loaded
    mov es, ax              ; Set our extra segment

    call kmain              ; Call the kernel proper

    cli                     ; Clear ints

    jmp $                   ; Hang


; Since putting these in and booting the image without '-kernel' can't find
; a bootable device, we'll comment these out for now and run the ROM with
; the '-kernel' flag in QEMU
        ;times 510-($-$$) db 0          ; Pad remained of our boot sector with 0s
        ;dw 0xAA55                      ; The standard 'magic word' boot sig

núcleo.c:

#include <stdint.h>

void kmain(void)
{
        unsigned char *vidmem = (char*)0xB8000; //Video memory address
        vidmem[0] = 65; //The character 'A'
        vidmem[1] = 0x07; //Light grey (7) on black (0)
}

Compilo todo así:

nasm -f elf -o cargador.o cargador.s

i386-elf-gcc -I/usr/include -o kernel.o -c kernel.c -Wall -nostdlib -fno-builtin -nostartfiles -nodefaultlibs

i386-elf-ld -T enlazador.ld -o kernel.bin loader.o kernel.o

Y luego prueba así:

qemu-sistema-x86_64 -kernel kernel.bin

Con suerte, alguien puede echarle un vistazo a esto por mí: los fragmentos de código no son muy largos.

Gracias.

preguntado el 12 de junio de 12 a las 14:06

¿No se supone que eso es binario plano? ¿Qué analiza/carga tu archivo ELF? -

BIN es un formato binario plano, así que supongo que lo es. No sé. Simplemente lanzo mi BIN directamente a QEMU, le digo que es un kernel que usa '-kernel' (porque tratarlo como un kernel completo y un gestor de arranque sin usar '-kernel' y usar el inicio mágico sig a un segmento de 512 bytes no fue hacer ejercicio por alguna razón, aunque en mi opinión debería ser así) y esperar lo mejor (que aparentemente no me está funcionando). -

Este código parece familiar, cualquier posibilidad de que sea parte de wiki.osdev.org ¿Tutorial de barebones? -

Parte de eso se basó en ese código, sí. [Como nota al margen: si no recuerdo mal, usar el código exacto de allí realmente creó el mismo problema o uno similar al que estoy experimentando con este código, pero dudo que este sea un problema ambiental como eso puede sugerir, he probé el BIN bien (y el IMG en el caso del código de ese tutorial) en otras máquinas y tiene los mismos problemas] -

@joesavage: El tutorial de OSDev Bare Bones se basa en la suposición de que está utilizando el cargador de arranque GRUB para la primera y la segunda etapa (como se detalla en el propio tutorial). Como tal, no hay código de 16 bits involucrado allí, y la secuencia de comandos del enlazador tiene como objetivo generar un archivo ELF, es decir, ese tutorial tiene como objetivo un completamente entorno diferente como su enfoque de "primera etapa". -

1 Respuestas

Dios, ¿por dónde empezar? (Rughes, ¿eres tú?)

El código de loader.s entra en el registro de arranque maestro (MBR). El MBR, sin embargo, también contiene la tabla de particiones del disco duro. Entonces, una vez que ensamblada el loader.s, tienes que unir con el MBR: el código de loader.s, la tabla de particiones del MBR. Si solo copia el loader.s código en el MBR, eliminó la partición de su disco duro. Para realizar correctamente la fusión, debe saber dónde se encuentra exactamente la tabla de particiones en el MBR...

La salida de loader.s, que va al MBR, se denomina "cargador de arranque de primera etapa". Debido a las cosas descritas anteriormente, solo tiene 436 bytes en esa primera etapa. una cosa tu no pueden hacer en este punto es abofetear algo de salida del compilador C encima de eso (es decir, hacer que su binario sea más grande que un sector, el MBR) y copiarlo en el disco duro. mientras que podría trabajar temporalmente en un disco duro antiguo, los modernos llevan aún más información de partición en el sector 1 en adelante, que sería destruida por su copia.

La idea es que compiles kernel.c en un separado binario, la "segunda etapa". El en el primer etapa, en los 436 bytes disponibles, luego usa el BIOS (o EFI) para carga la segunda etapa desde un punto específico en el disco duro (porque no podrá agregar la tabla de particiones y el análisis del sistema de archivos a la primera etapa), luego saltar a ese código recién cargado. Dado que la segunda etapa no está bajo el mismo tipo de limitación de tamaño, puede seguir adelante para hacer lo mismo. apropiado cosa, es decir, analice la información de partición, encuentre la partición "inicio", analice su sistema de archivos, luego cargue y analice el real binario del núcleo.

Espero que sepas que estoy mirando todo esto desde la órbita terrestre baja. La carga de arranque es un proceso complicado, y nadie puede esperar detallarlo en una publicación de SO. Por lo tanto, hay sitios web dedicados a estos temas, como OSDev. Pero tenga cuidado: este tipo de desarrollo requiere experimentado programadores, personas capaces de realizar investigaciones de nivel profesional, hacer preguntas de manera inteligente y llevar su propio peso. Dado que estas habilidades están en declive general en estos días, los sitios web de desarrollo de sistemas operativos tienen una tendencia a reacciones de mal humor si lo aborda de manera incorrecta. (*)

(*): O te arrojan una fuente sin comentar, como hizo Dwalter justo cuando terminé esta publicación. ;-)

Edit: Por supuesto, nada de esto es la razón real por la que el emulador se congela. i386-elf-gcc es un compilador que genera código para el modo protegido de 32 bits, asumiendo un modelo de memoria "plano", es decir, segmentos de código/datos que comienzan en cero. Tu loader.s es un código de modo real de 16 bits (según lo establecido por el BITS 16 parte), que no activar el modo protegido, y no inicializa los registros del segmento a los valores esperados por GCC, y luego procede a saltar al código generado por GCC bajo suposiciones falsas... BAM.

Respondido el 12 de junio de 12 a las 15:06

@DevSolar: tiene razón, el código no documentado de mi parte no fue tan útil. Lo eliminaré y tal vez lo vuelva a publicar con la documentación. O OP debería echar un vistazo a wiki.osdev.org. - morador

@Joesavage1: La recomendación de recursos está en mi publicación, con un hipervínculo para su conveniencia. - DevSolar

@joesavage Un cargador de arranque se inicia en modo real de 16 bits y luego cambia al modo protegido de 32 bits. Pero al pasar a qemu el indicador -kernel, está utilizando el gestor de arranque integrado de qemu. Por lo tanto, su loader.s no es el cargador de arranque real sino el pegamento entre el cargador de arranque y el kernel. Como dice DevSolar, el emulador se congela porque loader.s está en código de 16 bits (modo real) y kernel.c está en modo de 32 bits. - Hophat Abc

Un cargador de arranque está limitado a 512 bytes y, por lo general, cargará el núcleo en la memoria y hará algunas cosas que requieren que se realice el modo real. Algunos cargadores de arranque de varias etapas (GRUB) cargarán la segunda etapa, que luego la cambiará al modo protegido y cargará el kernel. Pero al pasar qemu la bandera -kernel, hace todo esto por usted. Por lo tanto, su código debe estar formateado para el modo protegido. - Hophat Abc

La times y dw 0xAA55 se usaría para un gestor de arranque real. Pero dado que asumo que desea que loader.s sea el pegamento entre el cargador de arranque y el núcleo, no lo usaría. - Hophat Abc

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.