Problems with simple C bootstrap/kernel
c, kernel, nasm, osdev, x86
Solution
Gosh, where to begin? (rhughes, is that you?)
The code from `loader.s` goes into the Master Boot Record (MBR). The MBR, however, also holds the partition table of the hard drive. So, once you assembled the `loader.s`, you have to merge it with the MBR: The code from `loader.s`, the partition table from the MBR. If you just copy the `loader.s` code into the MBR, you killed your hard drive's partitioning. To properly do the merge, you have to know where exactly the partition table is located in the MBR...
The output from `loader.s`, which goes into the MBR, is called a "first stage bootloader". Due to the things described above, you only have 436 bytes in that first stage. One thing you cannot do at this point is slapping some C compiler output on top of that (i.e. making your binary larger than one sector, the MBR) and copying that to the hard drive. While it might work temporarily on an old hard drive, modern ones carry yet more partitioning information in sector 1 onward, which would be destroyed by your copying.
The idea is that you compile `kernel.c` into a separate binary, the "second stage". The first stage, in the 436 bytes available, then uses the BIOS (or EFI) to load the second stage from a specific point on the hard drive (because you won't be able to add partition table and file system parsing to the first stage), then jump to that just-loaded code. Since the second stage isn't under the same kind of size limitation, it can then go ahead to do the proper thing, i.e. parse the partitioning information, find the "home" partition, parse its file system, then load and parse the actual kernel binary.
I hope you are aware that I am looking at all this from low-earth orbit. Bootloading is one heck of an involved process, and no-one can hope to detail it in one SO posting. Hence, there are websites dedicated to these subjects, like OSDev. But be warned: This kind of development takes experienced programmers, people capable of doing professional-grade research, asking questions the smart way, and carrying their own weight. Since these skills are on a general decline these days, OS development websites have a tendency for grumpy reactions if you approach it wrongly.(*)
(*): Or they toss uncommented source at you, like dwalter did just as I finished this post. ;-)
Edit: Of course, none of this is the actual reason why the emulator freezes. i386-elf-gcc is a compiler generating code for 32-bit protected mode, assuming a "flat" memory model, i.e. code / data segments beginning at zero. Your `loader.s` is 16-bit real mode code (as stated by the `BITS 16` part), which does not activate protected mode, and does not initialize the segment registers to the values expected by GCC, and then proceeds to jump to the code generated by GCC under false assumptions... BAM.
Problem
Recently I've become interested in writing my own really really basic OS. I wrote (well, copied) some basic Assembly that establishes a stack and does some basic things and this seemed to work fine, however attempting to introduce C into the mix has screwed everything up. I have two main project files: loader.s which is some NASM that creates the stack and calls my C function, and kernel.c which contains the basic C function. My issue at the moment is essentially that QEMU freezes up when I run my kernel.bin file. I'm guessing there are any number of things wrong with my code -- perhaps this question isn't really appropriate for a StackOverflow format due to its extreme specificity. My project files are as follows: loader.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 ``` kernel.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) } ``` I compile everything like so: nasm -f elf -o loader.o loader.s i386-elf-gcc -I/usr/include -o kernel.o -c kernel.c -Wall -nostdlib -fno-builtin -nostartfiles -nodefaultlibs i386-elf-ld -T linker.ld -o kernel.bin loader.o kernel.o And then test like so: qemu-system-x86_64 -kernel kernel.bin Hopefully someone can have a look over this for me -- the code snippets aren't massively long. Thanks.