Page 1 of 1

Setting up second stage loader

Posted: Sat Mar 06, 2010 4:14 pm
by GenHornet18
Hey, this has likely been asked before (although I couldn't find a response) but I've started building my OS by assembling a two stage bootloader (learned from bits and pieces here and there). Now the first stage works fine (gets control, loads second stage, transfers control), but the second stage not so much. I've created a message processor to output messages corresponding to the progress of the loader, but they don't seem to appear (it's just a black screen the whole time). The code just executes and control eventually gets transferred to the kernel. Now I'm new at OSdev and I'm unsure exactly how to set up the second stage of the loader when control gets transferred there so I was wondering if anyone could offer me insight into how to set things up (memory addressing, a stack?) The second stage loader is incomplete as I'm just trying to get it set up accordingly...

boot sector/first stage

Code: Select all

[ORG 0]
[BITS 16]

jmp 07C0h:StartBoot


StartBoot:
   mov AX, CS
   mov DS, AX
   mov ES, AX

.reset:                      
   mov AX, 00h           
   mov DL, 00h         
   int 13h             
   jc .reset           

.read:
   mov AX, 7E00h     
   mov ES, AX        
   mov BX, 00h          

   mov AH, 02h           
   mov AL, 04h          
   mov CH, 00h         
   mov CL, 02h          
   mov DH, 00h           
   mov DL, 00h          
   int 13h             
   jc .read             
jmp 7E00h:0000h      ;To second stage bootloader


times 510-($-$$) db 0
dw 0AA55h
second stage loader

Code: Select all

[ORG 0]
[BITS 16]

Setup:
   mov AX, 7E00h   ;set new location, and set screen mode
   mov DS, AX
   mov ES, AX
   mov DL, 00h

   mov AH, 00h
   mov AL, 03h
   int 10h

   mov SI, [TITLE]
   call PrintS
jmp Main

Main:
.reset:             ;load kernel to physical address 10000h 
   mov AX, 00h           
   mov DL, 00h         
   int 13h             
   jc .reset           
.read:
   mov AX, 1000h    
   mov ES, AX        
   mov BX, 00h          

   mov AH, 02h       ;start locations for reading    
   mov AL, 40h          
   mov CH, 00h         
   mov CL, 06h          
   mov DH, 00h           
   mov DL, 00h          
   int 13h             
   jc .read 

   mov SI, [KLOAD]
   call PrintS
.pMode:
   cli
   
   mov AX, 2401h     ;enable the A20 line
   int 15h
   jc Error

       
jmp Done

PrintS:
   mov AH, 0Eh
.loop:
   mov AL, [SI]
   cmp AL, 00h
   je .done
   int 10h
   inc SI
   jmp .loop
.done:
   ret

Error:
   cmp AH, 86h
   je .nSupport
   
.unknwon:
   mov SI, [UNERROR]
   call PrintS
   hlt
.nSupport:
   mov SI, [NOSUPPORT]
   call PrintS
   hlt

;****************
; Strings
;****************
TITLE:
   db "-> BootSector found, second stage bootloader started", 0Ah, 0Dh, 00h
KLOAD:
   db "-> Kernel succefully loaded, setting up protected mode", 0Ah, 0Dh, 00h 
NOSUPPORT:
   db "ERROR!! - A20 line function not supported, booting halted", 0Ah, 0Dh, 00h
UNERROR:
   db "ERROR!! - An unknown error has occured, booting halted", 0Ah, 0Dh, 00h

Done:
   jmp 1000h:0000h

times 2048-($-$$) db 0
Thanks in advance for any help you can give

Re: Setting up second stage loader

Posted: Sat Mar 06, 2010 8:26 pm
by Brendan
Hi,

An instruction like "mov SI, [TITLE]" moves the value at the address "TITLE" into the SI register. You want to do "mov SI, TITLE" which moves the address itself into SI.


Cheers,

Brendan

Re: Setting up second stage loader

Posted: Sat Mar 06, 2010 10:52 pm
by GenHornet18
Thank you Sir,

Unbelievable how I missed that mistake, (what i actually should have put there and after that was lea SI, [TITLE]), you just saved me countless hours of frustration. I need to take a break, and actually examine what I type.


thanks,
Hornet

Re: Setting up second stage loader

Posted: Sat Mar 06, 2010 10:55 pm
by SOLeonOS
Hey,

furthermore, in your first stage you load the kernel to segment 0x7E00.
If my assumption is right that you want the kernel being loaded directly after the bootsector you need to put it into segment 0x07E0.

:arrow: good luck!

Re: Setting up second stage loader

Posted: Sat Mar 06, 2010 11:14 pm
by GenHornet18
No actually in the first stage loader I load the second stage loader which then goes to load the kernel, enter protected mode and jump to the kernel. It would do this anyways but as I said it is incomplete. Just a simple question though, when I jump to the second stage loader do I have to update the registers (so the memory location will be set accordingly)?

Re: Setting up second stage loader

Posted: Sun Mar 07, 2010 1:38 am
by GenHornet18
Well on the upside my original error is somewhat resolved. I can get text to be outputted correctly if I move my GDT table and PrintS function around. I'm not exactly sure why but does the GDT need to be somewhere specifically in my coding? The code listed below doesn't work and I'm not exactly sure why, the error bochs spams we with is segment limit violation, which seems to occur somewhere at the start. Any insight you can offer as to how/where this is badly structured/poorly coded would be greatly appreciated.


updated second stage loader (first stage loader still remains the same, and functional)

Code: Select all

[ORG 0]
[BITS 16]

Setup:
   mov AX, 7E00h
   mov DS, AX
   mov ES, AX
   mov DL, 00h

   mov AH, 00h
   mov AL, 03h
   int 10h

   lea SI, [TITLE]
   call PrintS
jmp Main

Main:
.stack:             ;setup stack
   cli
   xor AX, AX
   mov DS, AX
   mov ES, AX
   mov AX, 9000h
   mov SS, AX
   mov SP, 0FFFFh
   sti

.reset:             ;load kernel to physical address 10000h 
   mov AX, 00h           
   mov DL, 00h         
   int 13h             
   jc .reset           
.read:
   mov AX, 1000h    
   mov ES, AX        
   mov BX, 00h          

   mov AH, 02h       ;start locations for reading    
   mov AL, 40h          
   mov CH, 00h         
   mov CL, 06h          
   mov DH, 00h           
   mov DL, 00h          
   int 13h             
   jc .read 

   lea SI, [KLOAD]
   call PrintS
.pMode:
   cli
   
   mov AX, 2401h     ;enable the A20 line
   int 15h
   jc Error

   pusha
   lgdt [GDTTable]   ;load a GDT
   popa

   mov EAX, CR0
   or EAX, 01h
   mov CR0, EAX
   hlt
   jmp 08h:clear_pipe

[BITS 32]
clear_pipe:
   mov AX, 10h         
   mov DS, AX              
   mov SS, AX              
   mov ESP, 90000h         
jmp Done


PrintS:
   mov AH, 0Eh
   mov BH, 00h
.loop:
   mov AL, [SI]
   cmp AL, 00h
   je .done
   int 10h
   inc SI
   jmp .loop
.done:
   ret

Error:
   cmp AH, 86h
   je .nSupport
   
.unknwon:
   lea SI, [UNERROR]
   call PrintS
   hlt
.nSupport:
   lea SI, [NOSUPPORT]
   call PrintS
   hlt

GDT:
null:      
   dd 00h
   dd 00h

code:               
   dw 0FFFFh
   dw 00h
   db 00h
   db 10011010b
   db 11001111b
   db 00h

data:               
   dw 0FFFFh
   dw 00h
   db 00h
   db 10010010b
   db 11001111b
   db 00h
GDTEnd:                

GDTTable:                      
   dw GDTEnd - GDT - 1    
   dd GDT

;****************
; Strings
;****************
TITLE:
   db "-> BootSector found, second stage bootloader started", 0Ah, 0Dh, 00h
KLOAD:
   db "-> Kernel succefully loaded, setting up protected mode", 0Ah, 0Dh, 00h 
NOSUPPORT:
   db "ERROR!! - A20 line function not supported, booting halted", 0Ah, 0Dh, 00h
UNERROR:
   db "ERROR!! - An unknown error has occured, booting halted", 0Ah, 0Dh, 00h

Done:
   jmp 08h:1000h


times 2048-($-$$) db 0

Re: Setting up second stage loader

Posted: Sun Mar 07, 2010 7:23 pm
by madeofstaples
The second stage is using an origin of 0 and the first stage jumps to this code while setting the CS to 0x7E00. Consequently, the calculated address of the GDT related labels will be 0x7E000 too small. The fact that your code seems to work when changing around the location of the GDT is probably only a lucky coincidence, because really, your code causes part of the interrupt vector table (probably, I haven't actually assembled the code to see what the address ends up being to be sure) to be used as if it were the GDT.

Try changing

Code: Select all

GDTTable:                      
   dw GDTEnd - GDT - 1    
   dd GDT
to

Code: Select all

GDTTable:                      
   dw GDTEnd - GDT - 1    
   dd GDT + 0x7E000
Although not required, the GDT should be aligned to an 8-byte boundary for best performance. You probably want to place "align 8" before the "GDT" label.

Edit: whoops, forgot the implicit multiplier. Originally I just added 0x7E00 but I've fixed the above post to add 0x7E000.

Re: Setting up second stage loader

Posted: Mon Mar 08, 2010 3:02 pm
by GenHornet18
Thanks for the advice, the problem actually resided in the way I was reading in the kernel. The GDT has also been corrected, I think there was a problem there. Loads the kernel now though, so I just have to finish writing it.

Thanks for the help,
-Hornet