Bootloader Only Works On Bochs

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
Xeon

Bootloader Only Works On Bochs

Post by Xeon »

Hello everyone.

I've programmed my own bootloader (please don't ask me why I don't just use GRUB), but I'm having a problem. For some odd reason, it only works on Bochs and not on a real PC. On Bochs it boots up just fine, but on a real PC it only displays the first message ("Booting ToriX OS") and then reboots (which I think means it encountered a triple fault - whatever the heck that is). The flow of my bootloader is:

Set up a stack, enable the A20 gateway, load a temporary GDT, switch to protected mode, switch to unreal mode, step up a stack, load the second half of the bootloader from the floppy, load the kernel from the floppy to the 1MB mark, jump to the second half of the bootloader, detect that the user is using a 486+ CPU (I'm planning on using the invplg instruction), jump back into protected mode, set up a new stack, and finally jumping to the kernel at the 1MB mark.

I believe that the real PC is triple faulting when I jump to unreal mode, but I'm not sure. Please don't flame me for being stupid, I'm an assembly newbie and have just finished reading Assembly Language Step-by-Step (I've just gotten the Intel reference manuals).

Attached is the source code. macros.def contains the print macro which points to print_msg.

Please help me out! I will forever be in debt to you.

-Xeon


[attachment deleted by admin]
distantvoices
Member
Member
Posts: 1600
Joined: Wed Oct 18, 2006 11:59 am
Location: Vienna/Austria
Contact:

Re:Bootloader Only Works On Bochs

Post by distantvoices »

Code: Select all

unreal32:
   ; Load the data selector into ds, es, and ss:
   mov   ax, DATA_SELECTOR --> pls use AX
   mov   ss, ax  SEGMENT Registers are 
   mov   ds, ax  16 bits wide.
   mov   es, ax
   ;mov   fs, ax
   ;mov   gs, ax 

   ; Switch back to real mode (but this time with segments larger than
   ; 64Kb:
                mov eax,cr0 ; -->I think you should do this.
   and   al, 0xFE   ; Unset the protected mode enable bit...
   mov   cr0, eax   ; ...and write it back.

   jmp   dword 0x07C0:unreal-0x7C00   ; Go back to 16-bit mode.

have a look at it. I have added comments and some
recommendations.

And it is ok to write a bootloader :-) It is a learning experience. I have done it too prior to grub.
... the osdever formerly known as beyond infinity ...
BlueillusionOS iso image
Xeon

Re:Bootloader Only Works On Bochs

Post by Xeon »

That might have solved some problems, but it's still not working on a real PC. When I just change the eax's to ax's the real PC still triple faults. When I just add the mov eax, cr0 the real PC no longer triple faults, but hangs at the "Booting ToriX OS" text. When I add both, needless to say, the real PC triple faults. All fixes work fine on Bochs...

What the heck is going on!? :(
slacker

Re:Bootloader Only Works On Bochs

Post by slacker »

i never really liked using bochs unless i encountered a problem that i had no idea how to fix. i think you should take apart your code and put it back together piece by piece or place halts in your code at certain places so you know where the triple fault occurs. if you dont know where the error occurs then you'll spend a longer timer trying to fix it so i say take it apart and put it back together since a bootloader tends to be relatively small....
Xeon

Re:Bootloader Only Works On Bochs

Post by Xeon »

Ok. I've traced the problem down to read_floppy (using none other than 'jmp $'). I also noticed that I hear the floppy try to read something for like half a second and then dies. The code doesn't even get to print a dot if I'm not mistaken. Oi vey.
Curufir

Re:Bootloader Only Works On Bochs

Post by Curufir »

Ok, seeing as you took time out to narrow down the problem area I took time out to glance at the code :).

Remember that BOCHS emulates a perfect floppy drive, ie it will never produce a read/write error from a disk image. Now on a real floppy drive you'll get errors, plenty of them, and you should really try to reset the drive before the first read operation.

In your code you try the read, assuming there's an error you then reset the drive. There's nothing wrong with this scheme (Although I'd be reseting the drive before the read). First time through the read_floppy routine you're using BX to represent start sector, you then push that value (And also put it into CX) before using BX to hold the destination offset for the BIOS read function, 0x8000 in your code.

Now assuming there's an error first time through the code you jump back to .try_again after reseting the drive, once again there's no inherent problem with that, but what value of BX are you using when you go back through the read the second time? Yup, 0x8000 ;). Things will get real wierd at this point.

There may be other errors, but if it works on BOCHS and not real hardware then this is a prime suspect as to why not.

Hope that helps.
User avatar
Pype.Clicker
Member
Member
Posts: 5964
Joined: Wed Oct 18, 2006 2:31 am
Location: In a galaxy, far, far away
Contact:

Re:Bootloader Only Works On Bochs

Post by Pype.Clicker »

btw, i don't see you setting up DS to a known value before you load the GDT. As the bootsector may be loaded at 0x7c0:0000 or 0x0000:0x7c00 depending on the bios you should have

Code: Select all

mov ax,0
mov ds,ax
jmp 0:start
as your very first instructions...

btw, i don't see why you are jumping to 0x7c0:unreal-7c00, as you assumed the code was starting at [org 7c00] ... just use jmp 0x000:unreal :)

btw, i suggest you add a small checksum of your kernel before you jump to it (just to make sure it loaded properly)
Xeon

Re:Bootloader Only Works On Bochs

Post by Xeon »

Thanks a bunch everyone. All your comments are valid and have helped me, but it still isn't working. I think I might need to completely rewrite read_floppy which was taken from geist's newOS. Whatever I do just isn't cutting it. :P I'm about to go into a series of jmp $'s after each and every instruction and reboot to determine the problem. I promise you, I've been trying to figure out this problem for almost a month. It really is quite discouraging...

The bugfixed code:

Code: Select all

; Read from the floppy (thanks geist):
read_floppy:
   sti
   push   bx
   push   cx
.try_again:
   mov   al, 0x13   ; Read a maximum of 18 sectors.
   sub   al, bl      ; Substract the first sector (to prevent
            ; wrap-around).

   xor   ah, ah      ; Don't read more then required, VMWare doesn't
            ; like that.
   cmp   ax, cx
   jl   .shorten
   mov   ax, cx
.shorten:
;; RELOAD CX AND BX WITH THE INITIAL VALUES BEFORE CONTROL WAS PASSED TO
;; READ_FLOPPY.  THEN PUSH BACK ON THE STACK TO REPOP THEM OFF WHEN DONE:
   pop   cx
   pop   bx
   push   bx
   push   cx
   mov   cx, bx      ; Sector/cylinder number to read from.
   mov   bx, 0x8000   ; Buffer address.
   mov   ah, 0x2      ; Command 'read sectors'.
   push   ax
   int   0x13      ; Invoke the BIOS => The computer reads the
            ; floppy.
   pop   ax      ; Should return number of transferred sectors
            ; in al but VMWare 3 clobbers it, so we
            ; (re-)load al manually.
   jnc   .success   ; No error => proceed as usual.
   dec   byte [retry_count]
   jz   .fail
   xor   ah, ah      ; Reset the disk controller.
   int   0x13      ; Invoke the BIOS => the floppy is read from.

;; CHEAP WAY TO FIX THE "BX'S VALUE STAYS AT 0x8000" (but being used to debug):
   jmp   .fail
   ;jmp   .try_again   ; Retry.
.success:
   mov   byte [retry_count], 3   ; Reload retry_count.
   print   dot
   mov   esi, 0x8000   ; Source.
   xor   ecx, ecx
   mov   cl, al      ; Copy the number of read sectors (al)...
   shl   cx, 0x7      ; ...of size 128*4 bytes...
   rep   a32 movsd   ; ...to destination (edi).
   pop   cx
   pop   bx
   xor   dh, 0x1      ; Read: next head.
   jnz   .bar6
   inc   bh      ; Read: next cylinder.
.bar6:
   mov   bl, 0x1      ; Read: sector 1.
   xor   ah, ah
   sub   cx, ax      ; Substract the number of read sectors.
   jg   read_floppy   ; Sectors left to read?
   cli
   ret
.fail:
   ; Print an error message, wait for a keypress, and reboot:
   print   read_error_msg
   print   reboot_msg

   xor   ax, ax
   int   0x16      ; Invoke the BIOS => the computer waits for a
            ; key.
   int   0x19      ; Invoke the BIOS => the computer reboots
   hlt         ; Halt the CPU (if we even get here, that is).
User avatar
Pype.Clicker
Member
Member
Posts: 5964
Joined: Wed Oct 18, 2006 2:31 am
Location: In a galaxy, far, far away
Contact:

Re:Bootloader Only Works On Bochs

Post by Pype.Clicker »

a few tips:
- make use of the VRAM as copy buffer: it'll be easier to see if loading is made properly
- write a small keyboard-polling function to force breakpoints in your code
Curufir

Re:Bootloader Only Works On Bochs

Post by Curufir »

Ok, you've exacerbated the problem while trying to fix it. All you actually needed to do to remove the BX problem was add two lines before jumping back to the top of the read_floppy procedure.

Code: Select all

   dec   byte [retry_count]
   jz   .fail
   xor   ah, ah      ; Reset the disk controller
   int   0x13

   pop cx            ; Restore cx and bx values from the stack
   pop bx

   jmp   .read_floppy  ; Retry the read
Now that's just solving a general programming fault (Ie losing track of variables you are keeping on the stack), there may be others. Try adding the 2 pops to the original read_floppy procedure you posted and see what happens.

I'm still working on the hypothesis that:
a) The original read_floppy has no other errors (I really don't have time to go through line by line atm).
b) It really is the disk read error that's causing the problem (And it would definitely have caused a problem).

**

Apologies if you read this before the edit, had this nagging feeling I had something to correct today, finally figured it out a few minutes ago ;D.
Xeon

Re:Bootloader Only Works On Bochs

Post by Xeon »

Haha, I did read it before the edit, but I figured out what you were trying to say. :) It didn't work, but thanks anyways. It solves that problem, however...dispite it still not working. I would use prints or some "keyboard-polling function", but I can't fit any more code/data into the first stage without overflowing the 512 byte limit. :(
User avatar
Pype.Clicker
Member
Member
Posts: 5964
Joined: Wed Oct 18, 2006 2:31 am
Location: In a galaxy, far, far away
Contact:

Re:Bootloader Only Works On Bochs

Post by Pype.Clicker »

Shorten your message (after all, everything you need is a proof a stage has been reached), maybe use the "print string" BIOS function instead of a loop of printchars, drop your "unused entry" in your GDT, and maybe you can push your "unreal mode setup" in the second (or third) sector if you're still short of a few bytes. (after all, everything you *really* need in the bootsector is loading the remaining of the bootloader, don't you :)

maybe you can also try
"call near <x>" instead of "call <x>"
and some "mov bx,byte 1" instead of "mov bx,1"
Post Reply