Problem with my OS Skeleton

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
spectre1989
Posts: 3
Joined: Tue Jan 27, 2009 2:17 am

Problem with my OS Skeleton

Post by spectre1989 »

Hi all, this is my first post so please be gentle :)

So I've been interested in doing this for a while, and I have quite a bit of C/C++ experience, and a little assembly. I didn't really want to use GRUB to boot my OS, as I want it to be entirely "my OS" rather than grub booting a kernel. Anywho, so at the moment my kernel is very simple:

Code: Select all

// kernel.c
void kmain( void )
{
	unsigned char * vram = (unsigned char *)0xb8000;
	int i;
	for( i = 0; i < 20; ++i )
	{
		if( i % 2 == 0 ) vram[i] = 65;
		else vram[i] = 0x07;
	}
}

Code: Select all

; and asmkernel.asm
%define STACKSIZE 0x4000
global load_kernel
extern _kmain

section .text
align 4

load_kernel:
	mov esp, stack+STACKSIZE	; Set stack pointer so we can safely 
								; push back into the stack

	call _kmain					; Call kmain() from kernel.c
	hlt							; halt should it return
	
section .bss
align 32
stack:
	resb STACKSIZE
compiled like so...

Code: Select all

//Linker script here
ENTRY (load_kernel)

SECTIONS{
    . = 0x00100000;

    .text :{
        *(.text)
    }

    .rodata ALIGN (0x1000) : {
        *(.rodata)
    }

    .data ALIGN (0x1000) : {
        *(.data)
    }

    .bss : {
        sbss = .;
        *(COMMON)
        *(.bss)
        ebss = .;
    }
}

// And then compiled with the following commands
nasm.exe -f elf -o asmkernel.o asmkernel.asm
gcc.exe -o kernel.o -c kernel.c -Wall -Wextra -Werror -nostdlib -nostartfiles -nodefaultlibs
ld.exe -T linker.ld -o kernel.bin asmkernel.o kernel.o
The bootloader code is:

Code: Select all

[BITS 16]
[ORG 0x7C00]

; Start the code
section .text

jmp 0x0000:load_kernel
	
load_kernel:		; use the int 13h bios call to read more data from floppy
	mov bx, 0x2000	; segment to read into
	mov es, bx		; can't read directly into segment registers damn them
	mov bx, 0x0000	; offset to read into
	mov ah, 0x02	; BIOS read sector function code
	mov al, 0x05	; read 5 sectors (this is because the kernel is 2.35kb, rounds up to 5*512)
	mov ch, 0x01	; read track 1, won't need to worry about track 2 unless the kernel gets pretty big
	mov cl, 0x02	; start reading at sector 2
	mov dh, 0x00	; head 0
	mov dl, 0x00	; Drive 0 (floppy 1)
	
attempt_load:
	int 0x13
	jc load_fail	; if there was an error carry flag will be set, say so and try again
	
load_success:
	mov ax, bSuccess
	call print_string
	mov ax, 0x2000
	mov ds, ax
        mov ax, 0x0000
        mov es, ax
	
	jmp 0x2000:0x0000	; jump to kernel
	
load_fail:
	mov ax, bFailure
	call print_string
	mov ah, 0x02 ; restore ax register
	mov al, 0x05 ;
	jmp attempt_load
	
print_string:
	.init:
		mov si, ax		; move parameter into si
		mov ax, 0000h	; set up ds with 0, so we'll have 0000:string
		mov ds, ax
		mov ah, 0eh		; char printing bios function code
		mov bh, 00h		; page number.. Dunno, just leave 0 I think =/
		mov bl, 07h		; text attribute, white text black bg
	.put_char:
		lodsb			; ok so in case I forget, this loads the byte at DS:SI 
						; into AL and incs SI, which is what the bios 
						; char printing function uses to store the character
		cmp al, 0		; if char is 0, end of string
		je .return
		
		int 10h			; else print char and jump back
		jmp .put_char
	.return:
		ret

section .data 	; stick any initialised data here
bFailure db "Kernel failed to load, trying again...", 0
bSuccess db "Successfully read kernel, booting...", 0
; Pad the end
times 430-($-$$) db 0
; Bootloader signature, probably not needed on modern machines
dw 0xAA55
compiled with

Code: Select all

nasm.exe boot.asm
I then copy the compiled boot file (512 bytes) followed immediately by the kernel.bin file, and then I mount this image in Virtual PC (for virtual PC the image file needs to be the full 1.44 megs so I have a custom program which fills in the extra space with 0s).

But it just seems to successfully read the floppy and then stop...

Any ideas?
Thanks in advance
Spec

EDIT: Updated code
Last edited by spectre1989 on Tue Jan 27, 2009 5:47 am, edited 1 time in total.
User avatar
Love4Boobies
Member
Member
Posts: 2111
Joined: Fri Mar 07, 2008 5:36 pm
Location: Bucharest, Romania

Re: Problem with my OS Skeleton

Post by Love4Boobies »

I didn't actually read your code but something caught my attention: the boot signature needs to be at offset 510. That means, you need to do this:

Code: Select all

times 510-($-$$) db 00h
dw 0aa55h
Your offset is 430.
"Computers in the future may weigh no more than 1.5 tons.", Popular Mechanics (1949)
[ Project UDI ]
User avatar
AJ
Member
Member
Posts: 2646
Joined: Sun Oct 22, 2006 7:01 am
Location: Devon, UK
Contact:

Re: Problem with my OS Skeleton

Post by AJ »

Hi,

I haven't had a chance to dissect the code fully, but a few things stand out:

Code: Select all

section .text
align 4

; reserve us a stack
STACKSIZE equ 0x4000

load_kernel:
   mov esp, stack+STACKSIZE   
Here, the stack is actually the entry point of your kernel. Either stick the stack at the end of the asm file, or start this stub with a "jmp load_kernel" instruction.

Next, you are in real mode when you jump to the kernel. Is that intentional?

I also don't know what's going on with your segment registers when you enter the boot loader. It would be a good idea to start with a "jmp 0x0000:load_kernel" to make sure that CS is correct.

I also see a CALL instruction before you have set up any stack (either SS or SP). You use a "mov ax, bSuccess" before having set up DS too (you've only initialised ES - DS doesn't get set until just before you JMP to the kernel). Is it really your intention to load ES and DS with 0x2000? I think at the start of your boot loader, you should set both of these to 0x0000 (assuming an ORG 0x7C00 and a CS of 0x0000).

Code: Select all

times 430-($-$$) db 0
Shouldn't that be times 510?

I suggest you run in bochs, and place a strategic "JMP $" at the start of your boot loader. When the machine halts, check the Bochs register dump is what you expect. Then, gradually move the position of the JMP $ so that you are happy with each unit of code before you go on to the next.

Cheers,
Adam

Edit: fixed code tag.
spectre1989
Posts: 3
Joined: Tue Jan 27, 2009 2:17 am

Re: Problem with my OS Skeleton

Post by spectre1989 »

AJ wrote: Here, the stack is actually the entry point of your kernel. Either stick the stack at the end of the asm file, or start this stub with a "jmp load_kernel" instruction.
Ah ha, I wasn't aware of that. I replaced it with %define so it won't be in the final program
AJ wrote: Next, you are in real mode when you jump to the kernel. Is that intentional?
I thought I'd get it working in real mode then switch to protected mode, could that be causing the error?
AJ wrote: I also don't know what's going on with your segment registers when you enter the boot loader. It would be a good idea to start with a "jmp 0x0000:load_kernel" to make sure that CS is correct.
OK I stuck a jmp 0x0000:load_kernel at the start for good measure.
AJ wrote: I also see a CALL instruction before you have set up any stack (either SS or SP). You use a "mov ax, bSuccess" before having set up DS too (you've only initialised ES - DS doesn't get set until just before you JMP to the kernel).
I wasn't aware I needed a stack as I don't push any values.. Is that wrong?
AJ wrote: Is it really your intention to load ES and DS with 0x2000? I think at the start of your boot loader, you should set both of these to 0x0000 (assuming an ORG 0x7C00 and a CS of 0x0000).
Well the kernel is in theory loaded into 0x2000:0x0000 by the floppy reading routine, so I do (I think) want to load DS with 0x2000 and ES with 0x0000. I added the setting of ES as I realised I hadn't done that.
AJ wrote:

Code: Select all

times 430-($-$$) db 0
Shouldn't that be times 510?
I know it's weird but for some reason when I use 510-($-$$) db 0 it generates a file greater than 512 bytes which changes depending on how much code there is in the program. Is it possible I'm not assembling it correctly? I use a simple:

Code: Select all

nasm.exe boot.asm
Process to assemble it, I then put this 512 byte file and the kernel together like so:
[Boot][Kernel][Padding]

Thanks a lot for the speedy replies in helping a newbie out :)
User avatar
AJ
Member
Member
Posts: 2646
Joined: Sun Oct 22, 2006 7:01 am
Location: Devon, UK
Contact:

Re: Problem with my OS Skeleton

Post by AJ »

Hi,

Firstly, to anyone reading this, sorry to use the "flame quote style" but I think it's best for answering these questions :)
spectre1989 wrote: Ah ha, I wasn't aware of that. I replaced it with %define so it won't be in the final program
That looks much better - the stack will now not get executed :)
I thought I'd get it working in real mode then switch to protected mode, could that be causing the error?
Chances are that your C compiler (GCC?) outputs 32 bit code. This will cause a problem, but I don't think your execution is getting that far yet.
OK I stuck a jmp 0x0000:load_kernel at the start for good measure.
That's better - you can now guarantee the value of CS and the assembler knows where it is.
I wasn't aware I needed a stack as I don't push any values.. Is that wrong?
CALL pushes the return value on to the stack - RET pops the return value. You should have a stack before using CALL.
Well the kernel is in theory loaded into 0x2000:0x0000 by the floppy reading routine, so I do (I think) want to load DS with 0x2000 and ES with 0x0000. I added the setting of ES as I realised I hadn't done that.
That's for your kernel, which is fine. However, your boot loader is not executing from 0x20000. Things like loading your strings will use DS - DS therefore needs to be set to 0x0000 for the majority of your boot loader code. IIRC, int 0x13 uses ES:BX, so as you say, ES does indeed need to be 0x2000 for the floppy read routine.
I know it's weird but for some reason when I use 510-($-$$) db 0 it generates a file greater than 512 bytes which changes depending on how much code there is in the program. Is it possible I'm not assembling it correctly? I use a simple:

Code: Select all

nasm.exe boot.asm
I would explicitly use "-f bin" just to make sure you are creating a flat binary. If it fails with the value 510, you need to check your nasm parameters.

If you havent done the Bochs thing, trust me - get it! You need to check exactly where IP is when the boot loader fails. Also, adding a JMP $ (or CLI, HLT) at any point will enable you to check the exact state of the registers at any point.

Cheers,
Adam
spectre1989
Posts: 3
Joined: Tue Jan 27, 2009 2:17 am

Re: Problem with my OS Skeleton

Post by spectre1989 »

AJ wrote: Chances are that your C compiler (GCC?) outputs 32 bit code. This will cause a problem, but I don't think your execution is getting that far yet.
OK I'll bear that in mind, but for now as you say I need it to get to the kernel first. For now I've just stuck an int 10h before calling _kmain so I know if it's getting that far.
AJ wrote: CALL pushes the return value on to the stack - RET pops the return value. You should have a stack before using CALL.
Ah ha, thanks for clearing that up. For now I've just commented out the calls to print_string, I'll implement a proper stack later.
AJ wrote:That's for your kernel, which is fine. However, your boot loader is not executing from 0x20000. Things like loading your strings will use DS - DS therefore needs to be set to 0x0000 for the majority of your boot loader code. IIRC, int 0x13 uses ES:BX, so as you say, ES does indeed need to be 0x2000 for the floppy read routine.
Ah ok, well I set it so that both DS and ES get reset to 0x0000 before jumping to the kernel.
AJ wrote: I would explicitly use "-f bin" just to make sure you are creating a flat binary. If it fails with the value 510, you need to check your nasm parameters.
Well I changed it to -f bin but still the same, which is very odd.. Hope to work that out later on.

I'll be trying the bochs thing, though I just had a weird result. I just realised that the entry for the kernel is called load_kernel, as is the first label on the boot loader. I changed the entry for the kernel to "begin", and now I can see it's trying to write something after the first string, and the cursor is zipping around like crazy.. Progress!
User avatar
Love4Boobies
Member
Member
Posts: 2111
Joined: Fri Mar 07, 2008 5:36 pm
Location: Bucharest, Romania

Re: Problem with my OS Skeleton

Post by Love4Boobies »

The reason it's doing that is because you've defined multiple sections there and they need to be aligned somehow. Better just delete all sections, it's a flat binary... That way everything should be just fine.
"Computers in the future may weigh no more than 1.5 tons.", Popular Mechanics (1949)
[ Project UDI ]
Post Reply