Second-Stage Bootloader Bug

Programming, for all ages and all languages.
TS
Posts: 3
Joined: Fri Sep 07, 2012 6:27 pm

Re: Second-Stage Bootloader Bug

Post by TS »

In case you were using my assembler, there is no ORG. My executable file format has a list of absolute addresses that must be patched. My Loader takes care of that, normally, except in my OSMain module. It must patch itself. The start-up assembly code and my boot-loaders must deal with almost no support for data segment variables. It's basically like an ORG 0. What they do is look at the code segment and figure-out where they were loaded. My boot-loaders actually copy themselves to just under 0x80000 in real mode and load the next stage into 0x7C00.
User avatar
Love4Boobies
Member
Member
Posts: 2111
Joined: Fri Mar 07, 2008 5:36 pm
Location: Bucharest, Romania

Re: Second-Stage Bootloader Bug

Post by Love4Boobies »

Rolice wrote:Thank you for you explanation. Now I verified the source of the problem. Yes, in the second boot loader I have some short jumps, that is possibly why the code did not brake. :)
If you show us your code, we can tell you for sure where the problem lies.
"Computers in the future may weigh no more than 1.5 tons.", Popular Mechanics (1949)
[ Project UDI ]
User avatar
Rolice
Posts: 5
Joined: Thu Sep 06, 2012 2:40 pm
Location: Sofia, Bulgaria
Contact:

Re: Second-Stage Bootloader Bug

Post by Rolice »

Ok I will. The second stage was problematic. I have placed a ;;; NOTE on the keypoints - where I inverted the values from 1000h -> 0h...

Here goes the first stage...

Code: Select all

bits 16
org 07c00h

loader:
	jmp short start			; go to start
	nop

; -------------------------------------------- CODE SEGMENT --------------------------------------------

%include "include/print.inc.asm"

ResetFloppy:
	mov ah, 0h				; Reset the floppy drive
	mov dl, 0h				; Drive number 0 for floppy drive
	int 13h					; Call BIOS to do this thing for us
	
	jc ErrorHandler			; Problem with resetting - Carry Flag (CF) - try again
	
	mov ax, 1000h 			; Setting the address where to store the read data - 0x1000:0 - THIS IS LIKE IT HAS TO BE
	mov es, ax
	xor bx, bx
	
LoadSecondLoader:
	mov  ah, 02h				; BIOS function no. 2 - read from floppy
	mov al, 1h				; Read 1 sector
	mov ch, 0h				; We are reading the next sector that this one, so it is on the same track (1) IMPORTANT THE TRACK IS 0!!! NOT 1
	mov cl, 2h				; The second (2) sector to be read from
	mov dh, 0h				; Head number 0
	mov dl, 0h				; Drive number - 0 - floppy drive
	int 13h					; Call the BIOS to do the reading for us
	
	jc ErrorHandler
	
	jmp 1000h:0000h
	
ErrorHandler:
	mov bl, 07h
	mov si, error
	call PrintString
	
	cli
	hlt

start:
	cli

	mov ax,cs				; Setup segment registers
	mov ds, ax				; Make DS correct
	mov es, ax				; Make ES correct
	mov ss, ax				; Make SS correct

	mov bp, 7c00h
	mov sp, 7c00h			; Setup a stack

	sti

	mov bl, 0ch
	mov si, title
	call PrintString
	
	
	mov bl, 02h
	mov si, sep
	call PrintString
	
	mov bl, 04h
	mov si, info
	call PrintString
	
	call ResetFloppy
	
	cli
	hlt
	
title db "Some title", 0dh, 0ah, 0
sep db "============================================", 0dh, 0ah, 0
info db "Bootloader entered stage 1, loading stage 2.", 0dh, 0ah, 0
error db "An unrecoverable error occurred. System will now halt.", 0dh, 0ah, 0

times 510-($-$$) db 0			; Rest of the bootloader is filled with zeros
dw 0xAA55					; Last bootloader signature 2 bytes
Now we have the second stage - the problem was lying here:

Code: Select all

org 0h				;;; NOTE: Here instead of 1000h I use 0h now
bits 16

loader:
	jmp short start			; go to start
	
%include "include/print.inc.asm"
%include "include/prepare.inc.asm"
	
start:
	;push cs
	;pop ds
	
	cli

	xor ax, ax
	mov ax, 1000h				;;; NOTE: Here instead of moving cs into ax (mov ax, cs) or just clearing it with the xor above I put 1000h which fixed the problem
	mov ds, ax				; Make DS correct
	mov es, ax				; Make ES correct
	mov ss, ax				; Make SS correct

	;mov bp, 1000h
	;mov sp, 1200h			; Setup a stack

	sti
	cld

	;mov ah, 0eh
;	mov al, 'H'
;	xor bx, bx
;	int 10h
	
	mov si, info
	call PrintString
	
	jmp $					; Endless loop in order to ensure a control point
	
	cli
	hlt


info db "Bootloader entered stage 2, setting up general environment and loading kernel.", 0dh, 0ah, 0

times 512-($-$$) db 0
The other files are separated as includes for the common routines.

This is the prepare.inc.asm:

Code: Select all

bits 16

EnableA20:
	mov al, 02h				; set bit 2 (enable a20)
	out 92h, al				; writing a byte to port 0x92
	ret
... and print.inc.asm:

Code: Select all

bits 16

PrintCharacter:								; Print Character on the screen
	mov ah, 0eh								; Tell the BIOS we will print char
	mov bh, 0h								; Page 0
	mov bl, 07h								; Lightgray color
	
	int 10h
	ret

PrintString:
	lodsb									; Grab a byte from SI
	
	or al, al									; Logical or of AL itself
	jz PrintString_Return						; End of the string - RETURN
	
	mov ah, 0eh								; Tell the BIOS we will print char
	int 10h									; Print the character					

	jmp PrintString							; Next symbol

	PrintString_Return:
	ret
	
HexToStr:
	xor bx, bx								; Setting bx to 0
	mov bl, al								; Move the decimal value in bl
	mov al, [HEXIDECIMAL_CHARACTERS + bx]		; Getting the corresponding character of hex value in al, back
	mov bl, 17h								; Setting color style for output
	call PrintCharacter							; Print the character representing the hex value
	ret						
	

HEXIDECIMAL_CHARACTERS db "01234567890ABCDEF"
This way the loader is quite working. :D
I have prepared kernel entry linked with ld, containing the linked preparational assembly and C void kmain function, which I have to load now... step forward, which of course will include some reading about file systems, before acting. :)
User avatar
JAAman
Member
Member
Posts: 879
Joined: Wed Oct 27, 2004 11:00 pm
Location: WA

Re: Second-Stage Bootloader Bug

Post by JAAman »

well, the first thing that comes into my mind, is you are either constantly repeating the same typo, or you don't understand segmentation
the segment is not the same as the offset, it is shifted by 4bits (1 hex digit) before adding it to the offset to create the address
1000:0 is not the same as 0:1000 -- those are 2 different addresses

if you set org=0, and DS = 1000, you are using absolute address 1_0000 (65536 decimal)
if you set org=1000 and DS = 0, you are using absolute address 0_1000 (4096 decimal)


so basically, you are loading the code to address 1_0000 (ES=1000, BX=0), and then trying to use it at address 0_1000 (DS=0, org=1000), so obviously this isn't going to work, since those are 2 different addresses

Code: Select all

mov ax, 1000h            ;;; NOTE: Here instead of moving cs into ax (mov ax, cs) or just clearing it with the xor above I put 1000h which fixed the problem
this should be unnecessary, since CS actually contains 1000 already -- but your org definitely needs to be 0 (same as your jump offset from your stage1)

there really isn't any reason to reset your stack again, unless you want to be able to access data from your new section using SS... but I'm not sure why you would, but it is causing a bug in your code:

you disable interrupts (CLI) before adjusting SS but then re-enable them without setting SP -- this is never a good idea (usually you should always pair them together, and thus never need to disable interrupts since they are automatically disabled for 1 instruction following any modification of SS) thus you can fix this one of 3 ways: either un-comment the line setting SP, remove the cli/sti and put a mov SP line immediately after the mov SS line, or eliminate the SS/SP setting altogether (leave it as you already set it up in the first stage)
User avatar
Rolice
Posts: 5
Joined: Thu Sep 06, 2012 2:40 pm
Location: Sofia, Bulgaria
Contact:

Re: Second-Stage Bootloader Bug

Post by Rolice »

Thanks, yes the conceptions is known to me, but to be honest I got confused. However, I have uptated the loader, since then, and now I load it on 0000:8000h, where the ORG of the second part is 8000h.
Yes the bootloader gave a bunch of bugs, which ate me yesterday, but I find the solution after all. The code for second stage, was also incomplete, due to the time of posting. However I solve this and the next furious problem with entering protected mode (processor-flag+GDT), which one was because of corrupted GDT - the access part of the Data Descriptor (executable bit) was saying it was another code selector.

I am still confused with these segment pointers, cs, ds, es, ss. However I know their purpose, but I got lost within the meaning, and thanks to this:
if you set org=0, and DS = 1000, you are using absolute address 1_0000 (65536 decimal)
if you set org=1000 and DS = 0, you are using absolute address 0_1000 (4096 decimal)
I think I got the link in between (segment pointer after all), with the offset, it got sense. #-o
I forgot for the 4 bit offset, which of couse lead me to wrong usage of the segmentation, and the wrong questions, which ate my day. :o

For the bp, sp and ss, I am still suspicious. I know that by defining bp and sp I would define the bottom of the stack and the upper limit, ie the stack bounds. But, how ss is related to the sp? As I understand the principle:

First I have to reserve a space in memory for the stack, so SS would point the segment of the area where is the stack (currently it would be on the same segment 0h for 0000:8000h). Then for base (BP) I point the address of that space and SP will contain the upper corner i.e. the stack size (or it was the base + stacksize?).

However I did not set up a stack in stage 2. Would it be a problem, as far as I know it should be, however in some of the includes (including a lot of new ones) I have push/pop, but it is working normally both 16 and 32bit?
For the 32bit I have moved the ESP to 90000h, which is far away from the current location... while SS is 0x10 (the address of the data segment).

I have listened to your advice and I have commented the lines for the setup of the segment registers, all of them, because now I use the 0000:8000h and they sould be all the same, right?

Thank you for your post and explanation

Off topic:
However, I am glad for the excercises I have played. They say, the experience is comming with the bad decisions, whereas good decisions come with experience... :D
So for other people entered in such black hole area, I would suggest not to quit, but to keep thinking, inspecting and analyzing (keep trying), its not unresolvable.
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: Second-Stage Bootloader Bug

Post by Brendan »

Hi,
Rolice wrote:First I have to reserve a space in memory for the stack, so SS would point the segment of the area where is the stack (currently it would be on the same segment 0h for 0000:8000h). Then for base (BP) I point the address of that space and SP will contain the upper corner i.e. the stack size (or it was the base + stacksize?).
SS defines where the stack segment starts (and the stack segment's limit). SP determines where the current "top of stack" is within the stack segment.

BP is just another register, like AX, BX, etc. It doesn't have a "hard wired" role like IP or SP or FLAGS.

However, minor differences between various registers make different registers a little better for certain things (e.g. CX tends to be a little better for loop counts, SI and DI tend to be a little better for addresses, AX tends to be a little better for multiplication, etc). For BP the only minor difference is that memory accesses that use BP will use SS as the default segment (e.g. "mov ax,[bp]" really means "mov ax,[ss:bp]" and not "mov ax,[ds:bp]"), which makes it a little better to use for "addresses within the stack" (it saves a segment override prefix). For this reason compilers tend to use EBP/BP as a frame pointer.


Cheers,

Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
User avatar
Rolice
Posts: 5
Joined: Thu Sep 06, 2012 2:40 pm
Location: Sofia, Bulgaria
Contact:

Re: Second-Stage Bootloader Bug

Post by Rolice »

Thanks, now this makes some more sense. Yes I have some knowledge of principal register beneficial usage for AX (accumolator - math), CX (cx counters, usage with REG), SI and DI (strings).
I have read some theory long ago and I am primary working in 32/64-bit mode as developer of standard userland apps, some debugging and disassembling, but on low level I had written one very simple bootloader.

Now with this deep (far) jump in low-level I am filling a missing holes of information, for myself, thats why some parts of the code may seem not logical or broken. :)

Another question, I am already in 32-bit environment, as I mentioned with the previous post and I have GDT, as far as I understand in 64-bit, the GDT is set up, but seems to be unsed, because of the returning to flat model for accessing memory, right? ... which means there are no segment:selectors anymore, but just a direct addresses like 0x0000000000000000? :o

Thanks
Post Reply