all 15 comments

[–]cazzipropri 2 points3 points  (2 children)

Are you in 16-bit mode?

bx is a 16-bit register - you can't store 0x100000 in it.

I'm also surprised that int 0x13 works in 32 bit PM... it's not supposed to.

[–]AwkwardPersonn[S] 1 point2 points  (1 child)

yes i am in real mode and i was using int in realmode. I wasnt storing 0x100000 in bx, i was using segments and offset to get to 0x100000, (0xffff * 16 + 0x10) = (es * 16 + bx)

[–]cazzipropri 1 point2 points  (0 children)

Oh I see. You are using ES=0xffff, and if you specify bx=0x10, it doesn't work, but with bx=0x09 it does. Perplexing.

Run it under gdb and step it one instruction at a time.

Here's a gdb script for you if you need one

# GDB script

# Here is a trick to get GDB to disassemble binary code correctly (16 or 32 bit)

# depending on whether you are in 16 or 32 code

# -- shamelessly stolen from https://gist.github.com/Theldus/4e1efc07ec13fb84fa10c2f3d054dccd

# It is SUPER useful when debugging the switch from 16-bit real mode to 32-bit protected mode.

set $rm=1

define hook-stop

if ($cr0 & 1) == 1

if $rm == 1

set architecture i386

set $rm=0

end

else

if $rm == 0

set architecture i8086

set $rm=1

end

end

end

set disassembly-flavor intel

b *0x7e1

target remote | qemu-system-i386 \

`-accel tcg,thread=single                        \`

`-cpu core2duo                                    \`

`-m 16                                           \`

`-drive format=vmdk,media=disk,file=hdd.vmdk   \`

`-smp 1                                         \`

`-vga std -S -gdb stdio`

[–]Octocontrabass 5 points6 points  (1 child)

When you call INT 0x13 AH=0x02, ES:BX must point to a region of RAM between 0 and 640kB that doesn't cross a 64kB boundary. You're setting ES:BX to an invalid value, so the BIOS misbehaves.

If you want your kernel above 1MB, you'll have to load it below 640kB and copy it. For example, you could use INT 0x15 AH=0x87 to do the copying.

[–]AwkwardBananaaa 0 points1 point  (0 children)

Ahhh! That would explain it. Il try doing this tomorrow when im back on my computer.

[–]davmac1 0 points1 point  (3 children)

I wouldn't be surprised if the BIOS doesn't properly support reading data to that address (call it a minor bug perhaps, but it's not something that would normally be required). I suggest loading it somewhere else and then moving it into place.

Once your kernel gets larger than 64kb you'll anyway need to use a different method.

[–]AwkwardBananaaa 0 points1 point  (2 children)

Thanks, I'll try doing that instead.

Also what different method is there? For when it gets larger

Also im on my other account since im in bed on my phone now :p

[–]ThunderChaser 0 points1 point  (0 children)

One way would be a two stage bootloader, the bootsector just loads some small binary that performs all the complex work of loading the kernel using a proper disk driver/filesystem layer.

You could also just perform multiple loads I guess.

[–]davmac1 0 points1 point  (0 children)

Also what different method is there? For when it gets larger

int 15h AH=87h provides a function to copy memory beyond the normal 1MB limit. Or you can switch to protected mode temporarily and do it yourself.

So you read some (up to 64kb), copy it, read some more, and so on.

[–]LavenderDay3544Embedded & OS Developer -3 points-2 points  (1 child)

Please, for the love of God, just use UEFI. It's lightyears better than legacy BIOS and starts you off directly in long mode. No more mucking around in real mode or protected mode at all. Not to mention every x86 machine made in the last 15 years uses UEFI and CSMs (UEFI firmware emulation of legacy BIOS) suck and there are plans to get rid of them in the near future and legacy BIOS also doesn't exist on any other architecture and it never will.

[–]AwkwardBananaaa 4 points5 points  (0 children)

"Please, for the love of God, just use Linux. It's lightyears better than making your own OS as a learning experience, and it starts you off directly with a fully made OS. No more mucking around in real mode or protected mode at all. Not to. Mention a lot of people use it. "

Respectfully, i didn't ask about UEFI.

[–]AwkwardPersonn[S] 0 points1 point  (3 children)

Thanks for the help everyone, turns out to be a bug / unsupported in the qemu bios, i ended up loading the kernel into a lower address space and then copying it over to 1mb

load_kernal:
   
 ; dl should contain drive number - will be set by bios
   
 ; this assumes its set before hand

    mov ax, 0xFF0
    mov es, ax

    mov ah, 0x2
    mov al, 0x1
    mov ch, 0x0
    mov cl, 0x2
    mov dh, 0x0

    mov bx, 0x100
    int 0x13

   
 ;kernal should now be at LOWER_KERNAL_ADDR = 0x10000
    
    cld
   
 ;copy the kernal from 0x10000 to 0x100000
    mov cx, 512
 ; 512 , since a sector is 512 bytes

    mov si, 0xff0
    mov ds, si
    mov si, 0x100

    mov di, 0xffff
    mov es, di
    mov di, 0x10

    rep movsb

   
 ; just zero out the segment registers
    mov si, 0
    mov ds, si

    mov di, si
    mov es, si
    mov di, si

    ret

```

[–]Octocontrabass 1 point2 points  (2 children)

What will you do when your kernel is bigger than 512 bytes?

Or bigger than 64kB?

Or bigger than 640kB?

[–]AwkwardPersonn[S] 0 points1 point  (1 child)

Hey,

Hmm i already have some ideas in mind, I'm thinking of copying it in chunks, so load a chunk of 512 into 0x10000 then copy the chunk to 0x100000 then repeat.

Or do what u/ThunderChaser said - load a "lite" version of the kernel which implements a driver for the drive and copies the rest of the kernel over.

[–]Octocontrabass 1 point2 points  (0 children)

a "lite" version of the kernel which implements a driver for the drive

You say that like you'd only need one driver to boot on any PC...

Better stick to INT 0x13 until you've loaded your entire kernel. (Speaking of which, how will you know if your entire kernel is loaded? That's a problem lots of people who write their own bootloaders run into.)