Page 1 of 1

Booting C Kernel with own bootloader

Posted: Wed Feb 02, 2011 2:01 pm
by Whitebird
Hello everyone!

I'm developing a small OS that works in protected mode, but executes entirely below 1mb. For acquire this purpose, I've wrote my own bootloader that loads for now, a small kernel of test that print a 'a' on screen.

The main problem is that when I emulate the "OS Project.img" with Virtual box the character 'a' is not printed on screen and nothing happens!
Can anyone give a hand with this?

I'm developing this OS in a Ubuntu system without a floppy drive, using nasm, a i586-elf-gcc compiler and dd command.

bootloader.asm

Code: Select all

;=====================================NASM HEADERS========================================


%define KSEG 0x0800 ; kernel segment
%define KOFF 0x0000 ; kernel offset
%define KSEC 2      ; kernel sector

%define GDT_LOCATION 0x00007E00
%define GDT_SIZE     0x0100

;=========================================================================================

use16

org 0x7C00
jmp 0x0000:start
start:

sti                          ; enable bios interrupts

; read kernel to memory 
mov ah, 0x02                 ; interrupt subfunction: read disk
mov dl, 0                    ; drive ( 00h = A: )
mov dh, 0                    ; head
mov ch, 0                    ; cylinder
mov cl, KSEC                 ; sector
mov al, 9                    ; number of sectors to be read
mov bx, KSEG                 ; es:bx points to memory buffer's location
mov es, bx      
mov bx, KOFF                 ; 0x0800:0x0000
int 13h                      ; disk interrupt

use32

cli                          ; disable BIOS interrupt

; initializates GDT
mov  ax, GDT_SIZE         
mov  word[cs:gdtr], ax       ; load GDT size
mov  eax, GDT_LOCATION
mov  dword[cs:gdtr + 2], eax ; load GDT offset
lgdt [gdtr]                  ; load GDT register

; Load GDT entries
mov ebx, dword[cs:gdtr + 2]
mov edx, gdt
mov ecx, 6
fillgdt: 
    mov eax, [cs:edx]
    mov [cs:ebx], eax
    add ebx, 4
    add edx, 4
    loop fillgdt

; enter protected mode
mov eax, cr0
or  al, 1
mov cr0, eax

; Load Kernel Code and Data Descriptors
mov ax,0x10
mov ds,ax
mov es,ax
mov fs,ax
mov gs,ax
mov ss,ax

jmp 0x08:0x0000              ; now kernel have control of system

;======================================VARIABLES==========================================


gdtr dw 0x0000     ; GDT size
     dd 0x00000000 ; GDT location

gdt dw 0x0000, 0x0000, 0x0000, 0x0000 ; GDT entry null
    dw 0x004F, 0x9A00, 0x8000, 0xFFFF ; GDT entry kernel code
    dw 0x004F, 0x9200, 0x0500, 0xFFFF ; GDT entry kernel data

;====================================NASM DIRECTIVES======================================

; fill the end of sector with signature's boot
times 510-($-$$) db 0
                 db 0x55
                 db 0xAA
kernel.c

Code: Select all

void kmain ()
{
    unsigned char *videoram = (unsigned char *) 0xb8000;
    videoram[0] = 65;                                     /* character 'A' */
    videoram[1] = 0x07;                                   /* forground, background color. */
    
    for (;;);
}
kernel_linker.ld

Code: Select all

ENTRY (kmain)

SECTIONS
{
    . = 0x00008000;
    .text : { *(.text) }
    . = 0x00000500;
    .data : { *(.data) }
    .bss : { *(.bss) }
}
Compile.sh

Code: Select all

#!/bin/sh
PATH=/usr/local/cross/bin:$PATH

#clean
cd ..
cd Compiled
rm -f "bootloader.bin" "kernel.bin" "kernel.o"
cd ..
cd Image
rm -f "OS Project.img"
cd ..

#make
nasm "Source/boot/bootloader.asm" -f bin -o "Compiled/bootloader.bin"
i586-elf-gcc -o "Compiled/kernel.o" -c "Source/kernel/kernel.c" 
i586-elf-ld -T "Source/kernel/kernel_linker.ld" -o "Compiled/kernel.bin" "Compiled/kernel.o"

dd if="Compiled/bootloader.bin" of="Image/OS Project.img" bs=512 count=1 seek=0
dd if="Compiled/kernel.bin"     of="Image/OS Project.img" bs=512 count=9 seek=1
dd if="/dev/zero"               of="Image/OS Project.img" bs=512 count=2870 seek=10
Thank you for your patience.

Re: Booting C Kernel with own bootloader

Posted: Wed Feb 02, 2011 3:05 pm
by M2004
Just a quick look:
1st: Your bootloader should setup a proper stack pointer and real mode
segment registers for a start.

Code: Select all

jmp 0x08:0x0000              ; now kernel have control of system
2ndly: I would guess that you try load the kernel to adress 0:8000h.
Basically you are not jumping to your kernel at all.

I have a boot code example here:

http://board.flatassembler.net/topic.php?t=6529

regards
Mac2004

Re: Booting C Kernel with own bootloader

Posted: Wed Feb 02, 2011 5:34 pm
by Chandra
mac2004 wrote:Just a quick look:
1st: Your bootloader should setup a proper stack pointer and real mode
segment registers for a start.

Code: Select all

jmp 0x08:0x0000              ; now kernel have control of system
2ndly: I would guess that you try load the kernel to adress 0:8000h.
Basically you are not jumping to your kernel at all.



He was trying
to set the base of the code segment in the GDT to 0x8000. So the jump is Ok.
In fact, I see the following source of errors (listed in descending order of possibility):
1 No far jump before loading the segment registers after entering protected mode.
2. GDT discriptors out of order.
3. Using 32 bit code before actually entering 32 bit protected mode.
4. No drive reset before loading sectors.
5. No provision of saving the Boot Device no. You might be booting from one drive and trying to load sectors from other (1st floppy in your case).

Re: Booting C Kernel with own bootloader

Posted: Wed Feb 02, 2011 6:48 pm
by bewing
Well, as Chandra says, your "use32" is definitely in the wrong place. Should be after the "MOV CR0, EAX".
It's very hard to tell if a bunch of GDT bits are correct just by staring at them. So I hope those are right.
The thing that I noticed is that your GDT_SIZE is illegal. You need to subtract 1. This may also cause a failure.

Re: Booting C Kernel with own bootloader

Posted: Thu Feb 03, 2011 4:46 am
by Combuster
bewing wrote:Well, as Chandra says, your "use32" is definitely in the wrong place. Should be after the "MOV CR0, EAX".
Even after the jump, because that's the instruction where the processor enters 32-bits mode.

Re: Booting C Kernel with own bootloader

Posted: Thu Feb 03, 2011 7:52 am
by Whitebird
Thank you for replying me!

Well, I did those modification in bootloader (implement drive reset, far jump, use32 setup correctly, change GDT descriptors order) and also wrote a simple kernel in assembly for testing if this error was in compiling and linking 'kernel.c', but seems it does not, nothing still happening. So, I've tried using a bootloader found at http://www.osdever.net/tutorials/view/mixing-assembly-c, modifying some parametres and, guess what? Still nothing happening.... I think that is an Virtual Box's error emulating. I will try emulate it on Bonch or qemu, because it seems to be more trusty. This is my code with modifications:

Code: Select all

;=====================================NASM HEADERS========================================


%define KSEG 0x0800 ; kernel segment
%define KOFF 0x0000 ; kernel offset
%define KSEC 2      ; kernel sector

%define GDT_LOCATION 0x00007E00
%define GDT_SIZE     0x00FF

;=========================================================================================

use16

org 0x7C00
jmp 0x0000:start
start:

sti                          ; enable bios interrupts

; Set stacks
mov ax, 0x0500
mov ss, ax
mov sp, 0x7BFF              

; Driver reset
mov ah,0x00 
mov dl,0x00
int 0x13

; read kernel to memory 
mov ah, 0x02                 ; interrupt subfunction: read disk
mov dl, 0                    ; drive ( 00h = A: )
mov dh, 0                    ; head
mov ch, 0                    ; cylinder
mov cl, KSEC                 ; sector
mov al, 1                    ; number of sectors to be read
mov bx, KSEG                 ; es:bx points to memory buffer's location
mov es, bx      
mov bx, KOFF                 ; 0x0800:0x0000
int 13h                      ; disk interrupt

cli                          ; disable BIOS interrupt

push cs
pop  ds

; initializates GDT
mov  ax, GDT_SIZE         
mov  word[gdtr], ax          ; load GDT size
mov  eax, GDT_LOCATION
mov  dword[gdtr + 2], eax    ; load GDT offset

; Load GDT entries
mov ebx, dword[gdtr + 2]
mov edx, gdt
mov ecx, 6
fillgdt: 
    mov eax, dword[edx]
    mov dword[ebx], eax
    add ebx, 4
    add edx, 4
    loop fillgdt


xor  ax, ax
mov  ds, ax
lgdt [gdtr]                  ; load GDT register

; enter protected mode
mov eax, cr0
or  al, 1
mov cr0, eax

jmp 0x08:pmode

use32

pmode:

; Load Kernel Code and Data Descriptors
mov ax,0x10
mov ds,ax
mov es,ax
mov fs,ax
mov gs,ax
mov ss,ax

jmp 0x08:0x8000              ; now kernel have control of system

;======================================VARIABLES==========================================


gdtr dw 0x0000     ; GDT size
     dd 0x00000000 ; GDT location

gdt dw 0x0000, 0x0000, 0x0000, 0x0000 ; GDT entry null
    dw 0xFFFF, 0x0000, 0x9A00, 0x00CF ; GDT entry kernel code
    dw 0xFFFF, 0x0000, 0x9200, 0x00CF ; GDT entry kernel data

;====================================NASM DIRECTIVES======================================

; fill the end of sector with signature's boot
times 510-($-$$) db 0
                 db 0x55
                 db 0xAA
After all, i have some questions about loading kernel and GDT descriptors:

- Have I need to explicitly load all GDT descriptors, like I did in my code? Because I saw that a lot of people do something like:

Code: Select all

xor ax, ax
mov ds, ax              ; Set DS-register to 0 - used by lgdt

lgdt [gdt_desc]         ; Load the GDT descriptor

...
...

gdt:                    ; Address for the GDT

gdt_null:               ; Null Segment
        dd 0
        dd 0

gdt_code:               ; Code segment, read/execute, nonconforming
        dw 0FFFFh
        dw 0
        db 0
        db 10011010b
        db 11001111b
        db 0

gdt_data:               ; Data segment, read/write, expand down
        dw 0FFFFh
        dw 0
        db 0
        db 10010010b
        db 11001111b
        db 0

gdt_end:                ; Used to calculate the size of the GDT

gdt_desc:                       ; The GDT descriptor
        dw gdt_end - gdt - 1    ; Limit (size)
        dd gdt                  ; Address of the GDT
- When GDT is setup correctly and protected mode was loaded properly, data will be stored in segment that I've specified right? Ok, and if this is true, what is stack pointer function in this story (still the same)?

Re: Booting C Kernel with own bootloader

Posted: Thu Feb 03, 2011 9:13 am
by Combuster
Have I need to explicitly load all GDT descriptors, like I did in my code? Because I saw that a lot of people do something like:
Did you try to observe the differences?
http://www.osdever.net/tutorials/view/mixing-assembly-c
I see you found the tutorial from hell. KEEP OUT - there is just too much broken code and lies around it.
I think that is an Virtual Box's error emulating
Learn not to blame your tools but yourself. That said, Bochs will be able to give you much better information about which things go wrong.

Re: Booting C Kernel with own bootloader

Posted: Fri Feb 04, 2011 3:42 am
by Solar
Combuster wrote:
I think that is an Virtual Box's error emulating
Learn not to blame your tools but yourself.
Seconding this. The chances of some problem being due to an error in your tools is very, very slim. The chance that you are the first to find a given bug in a mainstream tool is even less.

(You did try to google for any known errors related to your problem, did you?)

Always assume the problem is in your code, unless you have proof that the tool is to blame. Don't waste your time trying to prove the tool incorrect before you have thoroughly and systematically proven your code to be correct.

Re: Booting C Kernel with own bootloader

Posted: Fri Feb 04, 2011 10:26 am
by Whitebird
ty guys

Re: Booting C Kernel with own bootloader

Posted: Fri Feb 04, 2011 3:49 pm
by LegendDairy
Don't you need Grub to print on screen like you do, and as far as I see I don't see a multiboot-header so you I assume you aren't using it. If I'm not mistaking you're just placing "65" at 0xb8000 the RAM but you graphics card has no idea that you are trying to print something on the screen. If you would use GRUB, it would place it in the VRAM for you and give instructions to the GPU.

My advise: writing you're own bootloader is cool but It'll will get very tricky when you'll try to print stuff, because you will need a gpu-driver or something. So I advise you to use GRUB it's almost the same but you'll have the possibility to easily print on screen.

Re: Booting C Kernel with own bootloader

Posted: Fri Feb 04, 2011 3:58 pm
by Tosi
GRUB is a bootloader. It's sole purpose is to load the operating system with the machine in a specified state. It provides no functionality for writing to the screen or other BIOS-like functionality, except providing structures containing important system information to the loaded operating system. On a standard PC, unless you put commands in your multiboot header telling it otherwise, GRUB starts the OS in 80 by 25 text mode with the framebuffer at 0x000B8000. Even without GRUB, the BIOS puts the card in that mode before loading the bootloader any way. If the VGA registers are untouched, you can write to the screen much the same.

Re: Booting C Kernel with own bootloader

Posted: Fri Feb 04, 2011 4:03 pm
by LegendDairy
Tosi wrote:GRUB is a bootloader. It's sole purpose is to load the operating system with the machine in a specified state. It provides no functionality for writing to the screen or other BIOS-like functionality, except providing structures containing important system information to the loaded operating system. On a standard PC, unless you put commands in your multiboot header telling it otherwise, GRUB starts the OS in 80 by 25 text mode with the framebuffer at 0x000B8000. Even without GRUB, the BIOS puts the card in that mode before loading the bootloader any way. If the VGA registers are untouched, you can write to the screen much the same.
Offtoppic: Really? People always told me that 0xB8000 was because of GRUB. Have you got some docs about all that for me?

Ontopic: Then I don't directly see what's wrong. But then I do advise you to use Bochs, it's easier for debugging maybe you could find something there?

Re: Booting C Kernel with own bootloader

Posted: Fri Feb 04, 2011 4:21 pm
by Tosi
VGA Graphics register 0x06 - bits 2 and 3 choose how memory is mapped. For 80x25 text mode this is 011b which maps the display memory to the 64 kb region beginning at 0x000B8000. All of this is independent of the bootloader used.