Problem jumping to 32 bit code after protected mode enabled.

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
psimonson1988
Member
Member
Posts: 40
Joined: Tue Jul 16, 2019 8:40 pm

Problem jumping to 32 bit code after protected mode enabled.

Post by psimonson1988 »

In my second stage, "stage2.asm" there is a problem with the far jump to 32 bit code. But for some reason or another it doesn't want to jump to that code, not sure what is wrong. But if you put the hlt instruction right before the far jump everything works and it halts the system in protected mode. If said hlt instruction is removed then it triple faults, I'm not really sure about what's going on or how to debug it other than what I've already done. This is my first time trying to go into 32 bit protected mode, so please be gentle. By the way, the code in question is displayed below...

If you want to clone so you can test things, here is my github: boot32-barebones

UPDATE: Redone a few things and removed 'load_gdt' subroutine and now I have the debug and normal log files from bochs.

debug.log
normal.log

stage1.asm:

Code: Select all

; Stage 1 bootloader by Philip Simonson.

[org 0x7c00]
[bits 16]

[section .text]

global _start
_start:
	jmp 0:main
	nop

; put BPB here

main:
	xor ax, ax
	mov ds, ax
	mov es, ax
	mov ss, ax
	mov sp, 0x7c00
	cld

	mov [iBootDrive], dl
	call reset_disk

	mov si, op_loading
	call print

	mov ax, 1
	mov cl, 2
	xor bx, bx
	mov es, bx
	mov bx, load_segment
	call read_disk

	jmp 0:load_segment

%include "common.inc"
%include "disk.inc"

op_loading db "Loading stage 2, please wait",0
op_ferror db 10,13,"Disk error!",13,10,0
op_fdone db "success!",13,10,0
op_progress db 0x2e,0
iBootDrive db 0
load_segment dw 0x07e0

; padding and magic number
times 510-($-$$) db 0
dw 0xaa55
stage2.asm:

Code: Select all

; stage 2 boot loader.
; by Philip Simonson.
; =======================

[org 0x7e00]
[bits 16]

start:
	xor ax, ax
	mov ds, ax
	mov es, ax

	call a20_bios
	call check_a20

	mov si, op_pmode
	call print

	; switch on protected mode
	cli
	lgdt [gdt.pointer]
	mov eax, cr0
	or eax, 1
	mov cr0, eax

;	hlt ; uncomment to test before jump instruction
	jmp dword 0x08:INIT_PM

%include "common.inc"
%include "a20.inc"
%include "gdt.inc"

[bits 32]

INIT_PM:
	mov ax, 0x10
	mov ds, ax
	mov es, ax
	mov fs, ax
	mov gs, ax
	mov ss, ax
	mov ebp, 0x90000
	mov esp, ebp

	call BEGIN_PM
	hlt

BEGIN_PM:
	ret


op_pmode db "Entering protected mode...",0
op_pmode2 db "done!",13,10,0
op_a20yes db "A20 is enabled.",13,10,0
op_a20no db "A20 is disabled.",13,10,0
gdt.inc:

Code: Select all

; Global Descriptor Table
gdt:
.null: equ $-gdt
	dd 0
	dd 0
.code: equ $-gdt
	dw 0xffff
	dw 0
	db 0
	db 10011010b
	db 11001111b
	db 0
.data: equ $-gdt
	dw 0xffff
	dw 0
	db 0
	db 10010010b
	db 11001111b
	db 0
.pointer:
	dw $-gdt-1
	dd gdt
Last edited by psimonson1988 on Sat Jun 27, 2020 10:22 pm, edited 1 time in total.
Octocontrabass
Member
Member
Posts: 5882
Joined: Mon Mar 25, 2013 7:01 pm

Re: Problem jumping to 32 bit code after protected mode enab

Post by Octocontrabass »

The GDTR needs to contain a linear address, but you have told the assembler to generate offsets relative to a nonzero segment base, so the resulting address is not a linear address. (When the segment base is zero, logical addresses are equivalent to linear addresses.)

Set all of the segment bases to zero when you transfer control from stage 1 to stage 2, and change your org statement in stage 2 as appropriate.

Alternatively, you could modify your GDT descriptor to take into account the difference between logical and linear addresses. Since your GDT entries all use a base address of 0, and the assembler does not know that you're changing the segment base address, you will need to make similar adjustments elsewhere in your code to take into account the changing segment base address. This is a lot more work than just leaving all of the segment bases set to 0 while you're in real mode.
User avatar
bzt
Member
Member
Posts: 1584
Joined: Thu Oct 13, 2016 4:55 pm
Contact:

Re: Problem jumping to 32 bit code after protected mode enab

Post by bzt »

Hi,

Maybe @Octocontrabass is on the right track, however i cannot confirm because we don't see your segment registers, we only see ORG 0x0 in the code, that's not enough to be sure. But if so, then you should not load the stage2 code at linear address 0.

You should not mess around with CLI/STI, this entire code should run with interrupts disabled (I bet you haven't set up IDT yet). And another thing, there's no need for PUSHA/POPA in load_gdt, because lgdt does not change registers. That being said there's really no need for load_gdt function (since you don't need neither CLI/STI nor PUSHA/POPA), you could just put that single lgdt instruction in your code.
psimonson1988 wrote:I'm not really sure about what's going on or how to debug it
I don't know what vm software you use, but you could try to add "-d int" to the qemu command line, that should print the exceptions too. With VirtualBox, check the logs, it will contain the info (although it is VERY verbose, you'll need to find the corresponding lines in a lot of noise).

But my advice is, use bochs. Not only will it tell you exactly why the vm triple faults (and what exceptions are raised), but it will drop you into an interactive debug prompt, where you can view the actual values in the segment registers ("sreg") and check the gdt ("info gdt") as well. It will decode the gdt table, so you'll see if you misplaced a bit or something, or if the gdt_desc has a bad address, etc.

And the last thing: I'm not sure about this, because I haven't used protmode for a long time, but if I were you, I'd align that gdt table just to be on the safe side.

Cheers,
bzt
psimonson1988
Member
Member
Posts: 40
Joined: Tue Jul 16, 2019 8:40 pm

Re: Problem jumping to 32 bit code after protected mode enab

Post by psimonson1988 »

Okay updated the original post with the modifications and also posted everything that should be needed to figure it out.. if you need more information let me know and I'll post that as well. I had to post bochs logs on pastebin because they were too large to fit on here. Hopefully, you guys can spot what I did wrong am totally lost in switching to protected mode. Like I stated before never done it I was always into boot sectors and loaders (16 bit) ofcourse. Thanks in advance for any help resolving this issue.

EDIT: It says that the GDT code segment isn't valid using bochs. Which is probably why the triple fault, right?

-Phil
PeterX
Member
Member
Posts: 590
Joined: Fri Nov 22, 2019 5:46 am

Re: Problem jumping to 32 bit code after protected mode enab

Post by PeterX »

This is wrong:

Code: Select all

global _start
_start:
   jmp 0:main
   nop

; put BPB here

main:
Before the BPB you have only 3 bytes. You can either do a short JMP and a NOP or a near JMP without NOP. But you do a far JMP which is 5 bytes long (and a NOP).
Do the far JMP after the BPB. Or else the BPB will overwrite your JMP instruction partially.

Like this:

Code: Select all

start: jmp short main
    nop

;BPB
bpb: times 93 db 0

main: jmp 0:.skip
.skip: ...
Note that in a flat binary (like a bootsector) there is no special/dedicated entry point (it's simply starting at offset 0), no symbol export ("global _start") and no section (like ".text" etc.).

--------------------------------------------------
Can someone who is better versed at pmode confirm or deny:
It seems to me that in the Access Byte the "Descriptor type" bit is set to 0 and should be set to 1. Am I wrong here?

Greetings
Peter
Schmidtzilla
Posts: 4
Joined: Sat Jun 06, 2020 2:02 pm

Re: Problem jumping to 32 bit code after protected mode enab

Post by Schmidtzilla »

Have you figured it out yet? I think you may have your pointer wrong when loading the gdt table? Your

Code: Select all

lgdt [gdt.pointer]
but your pointer should be

Code: Select all

lgdt [pointer]
maybe?
Schmidtzilla
Posts: 4
Joined: Sat Jun 06, 2020 2:02 pm

Re: Problem jumping to 32 bit code after protected mode enab

Post by Schmidtzilla »

Code: Select all

   xor bx, bx
   mov es, bx
   mov bx, load_segment
Can be changed to a simple

Code: Select all

   les bx, [load_segment]
instruction as well as your load_segment variable being set to 0x07e0 instead of 0x7e00 where you have your orgin set to on the second stage bootloader. I believe if you set your orgin on your second stage bootloader to 0x7E0 or change your variable to 0x7E00 it should work.
Octocontrabass
Member
Member
Posts: 5882
Joined: Mon Mar 25, 2013 7:01 pm

Re: Problem jumping to 32 bit code after protected mode enab

Post by Octocontrabass »

psimonson1988 wrote:

Code: Select all

	xor bx, bx
	mov es, bx
	mov bx, load_segment
	call read_disk

	jmp 0:load_segment
It's not a segment anymore, it's an offset. Perhaps change the name to "load_offset" to avoid further confusion.
psimonson1988 wrote:

Code: Select all

load_segment dw 0x07e0

Code: Select all

[org 0x7e00]
Your load offset disagrees with your org statement, so you're still not loading the GDTR correctly. (You probably intended to set the load offset to 0x7e00.)
psimonson1988 wrote:It says that the GDT code segment isn't valid using bochs. Which is probably why the triple fault, right?
Where are you seeing that? The logs you posted are from before any code has run!
PeterX wrote:It seems to me that in the Access Byte the "Descriptor type" bit is set to 0 and should be set to 1. Am I wrong here?
Once the addresses are all fixed, the descriptor type bit will be set to 1.
MichaelPetch
Member
Member
Posts: 832
Joined: Fri Aug 26, 2016 1:41 pm
Libera.chat IRC: mpetch

Re: Problem jumping to 32 bit code after protected mode enab

Post by MichaelPetch »

A number of things. You define load_segment as label to a word containing 0x07e0. You should have used EQU instead. You can load sectors using 0x07e0:0x0000 but you save yourself a lot of hassles if you launch stage2 with 0x0000:0x7e00 instead. This is important if stage1 didn't enter protected mode. LGDT requires a GDTR that takes a linear address. If you aren't using a segment of 0x0000 when you load your GDT then you will have a mismatch between the offsets relative to the beginning of a segment and offsets relative to the beginning of memory (which the GDTR expects). If you fix up these things your code should work:

stage1.asm

Code: Select all

; Stage 1 boot loader by Philip Simonson.

[org 0x7c00]
[bits 16]

[section .text]

global _start
_start:
	jmp 0:main
	nop

; put BPB here

main:
	xor ax, ax
	mov ds, ax
	mov es, ax
	mov ss, ax
	mov sp, 0x7c00
	cld

	mov [iBootDrive], dl
	call reset_disk

	mov si, op_loading
	call print

	mov ax, 1
	mov cl, 2
	mov bx, load_segment
	mov es, bx
	xor bx, bx
	call read_disk

	mov ax, run_segment
	mov ds, ax
	mov es, ax
	jmp run_segment:run_offset

%include "common.inc"
%include "disk.inc"

op_loading db "Loading stage 2, please wait",0
op_ferror db 10,13,"Disk error!",13,10,0
op_fdone db "success!",13,10,0
op_progress db 0x2e,0
iBootDrive db 0
load_segment EQU 0x07e0
run_offset   EQU 0x7e00
run_segment  EQU 0x0000

; padding and magic number
times 510-($-$$) db 0
dw 0xaa55
stage2.asm:

Code: Select all

; stage 2 boot loader.
; by Philip Simonson.
; =======================

[org 0x7e00]
[bits 16]

start:
	call a20_bios
	call check_a20

	mov si, op_pmode
	call print

	; switch on protected mode
	cli
	lgdt [gdt.pointer]
	mov eax, cr0
	or eax, 1
	mov cr0, eax

;	hlt ; uncomment to test before jump instruction
	jmp dword 0x08:INIT_PM

%include "common.inc"
%include "a20.inc"
%include "gdt.inc"

[bits 32]

INIT_PM:
	mov ax, 0x10
	mov ds, ax
	mov es, ax
	mov fs, ax
	mov gs, ax
	mov ss, ax
	mov ebp, 0x90000
	mov esp, ebp

	call BEGIN_PM
	hlt

BEGIN_PM:
	ret


op_pmode db "Entering protected mode...",0
op_pmode2 db "done!",13,10,0
op_a20yes db "A20 is enabled.",13,10,0
op_a20no db "A20 is disabled.",13,10,0
If you don't use a segment of 0x0000 the FAR JMP to protected mode has to be adjusted to take account for the difference and the address of the GDT inside the GDTR (GDT record) has to be adjusted as well. That makes things messy. What I've done in the code above is simplified things so you don't have to worry about such fixups. It will just work.
psimonson1988
Member
Member
Posts: 40
Joined: Tue Jul 16, 2019 8:40 pm

Re: Problem jumping to 32 bit code after protected mode enab

Post by psimonson1988 »

Big thanks to all of you it works now.
Post Reply