What to do immediately after entering protected mode?

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
Ziddia
Member
Member
Posts: 38
Joined: Sat Nov 05, 2011 6:28 pm

What to do immediately after entering protected mode?

Post by Ziddia »

Hello,

A while ago, I was writing a bootloader for my hobby OS. After a multitude of errors that I wasn't able to deal with at the time, I just gave up on it and went with GRUB, which has been working perfectly, but I want to have another try at my own bootloader. I dug up my old code, read through it, and managed to fix up a few things. However, I now have a few questions concerning what I'm meant to do immediately after entering protected mode (besides jump to my kernel).

Stage one of my bootloader does nothing but load stage two into memory at 1000h and pass control to it. My stage two bootloader, which I'll post below, enables the A20 line, loads the GDT, and enables protected mode. One thing I'm not sure about is whether the GDT loads correctly; I've used VirtualBox's debugger to dump the contents of the GDT, and it seems to have about a hundred more entries than I wanted (output is here).

My major issue is that my previous code did all of the above, then jumped to a routine called clear_pipe. The original contents of that method are here:

Code: Select all

clear_pipe:
MOV AX, 10h
MOV DS, AX
MOV SS, AX
MOV ESP, 090000h
MOV BYTE [DS:0B8000h], 'P'
MOV BYTE [DS:0B8001h], 1Bh
However, I'm not entirely sure what the point of that is, especially seeing as SS stays at 9000h regardless. Another problem is that, as soon as I move to protected mode, DS changes from 1000h to 0, and stays at 0 no matter what I do. I'm somewhat confused as to the purpose of anything in the clear_pipe method. Finally, the last two lines are meant to print the character 'P' to the screen, but they don't. Could this be because DS is 0 at the time?

Thanks for the time. Here's the current contents of the stage two file (doesn't include the stuff in clear_pipe, as I rewrote the file):

Code: Select all

org 1000h

bits 16 ; 16 bits, as this is a bootloader

%include "enableA20.s" ; contains our routine for enabling the a20 line

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; start.asm: stage 2 of our bl.                                              ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

start:
   push cs
   pop ds				; segments out of order, fix it up
   call check_a20		; check if a20 is enabled
   cmp ax, 0			; compare AX to 0 for error
   je enableA20			; enable the a20 line
   call check_a20		; check again
   cmp ax, 0			; error check
   je hang_error		; hang with error message
   cli				; clear interrupts for gdt
   lgdt[gdt_desc]		; load the gdt
   mov eax, cr0			; get cr0
   or eax, 1			; set the first bit in cr0
   mov cr0, eax			; welcome to pmode
   jmp 08h:clear_pipe		; jump to clear

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Check if A20 is enabled
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
check_a20:
   pushf
   push ds
   push es
   push di
   push si

   cli

   xor ax, ax
   mov es, ax

   not ax
   mov ds, ax

   mov di, 0x0500
   mov si, 0x0510

   mov al, byte [es:di]
   push ax

   mov al, byte [ds:si]
   push ax

   mov byte [es:di], 0x00
   mov byte [ds:si], 0xFF

   cmp byte [es:di], 0xFF

   pop ax
   mov byte [ds:si], al

   pop ax
   mov byte [es:di], al

   mov ax, 0
   je check_a20_exit

   mov ax, 1

check_a20_exit:
   pop si
   pop di
   pop es
   pop ds
   popf
   ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Address for GDT
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
gdt:

gdt_null:
   dd 0
   dd 0

gdt_code:
   dw 0FFFFh
   dw 0
   db 0
   db 10011010b
   db 11001111b
   db 0

gdt_data:
   dw 0FFFFh
   dw 0
   db 0
   db 10010010b
   db 11001111b
   db 0

gdt_end:

gdt_desc:
   dw gdt_end - gdt - 1
   dd gdt


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Prints to the screen
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
print:
   pusha		; Push all pointers and standard registers
   mov ah, 0eh		; set ah to 0eh (this is an 'argument' for int 10h)
   
.loop:
   lodsb
   test al, al		; Make sure al is not zero (zero-terminated string)
   jz .end
   int 10h		; Print contents of al
   jmp .loop

.end:
   popa			; Restore registers
   ret			; Return to previous instruction

hang_error:
   mov si, msg_a20
   jmp hang

hang:
   jmp hang		; Hang indefinitely

msg_a20 db 'Error while enabling A20', 13, 10, 0

bits 32			; pmode is 32-bit

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; clear stuff, print a p to the screen (???)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
clear_pipe:
   jmp hang_two

hang_two:
   jmp hang_two
User avatar
AJ
Member
Member
Posts: 2646
Joined: Sun Oct 22, 2006 7:01 am
Location: Devon, UK
Contact:

Re: What to do immediately after entering protected mode?

Post by AJ »

Hi,
Ziddia wrote: I've used VirtualBox's debugger to dump the contents of the GDT, and it seems to have about a hundred more entries than I wanted (output is here).
I'm not familiar with VirtualBox's debugger, but that "GDT" looks as though it's simply converting real mode segments to linear offsets (perhaps this is how virtualbox does its segment translation internally?). That is not a 32 bit protected mode GDT.

Also, you %include enablea20.s, but have no jump directive at the start of your stage 2 loader - if this is a flat binary, the first line of 'enableA20.s' will be your entry point, which is perhaps why you never get as far as loading the GDT.

Cheers,
Adam
User avatar
DavidCooper
Member
Member
Posts: 1150
Joined: Wed Oct 27, 2010 4:53 pm
Location: Scotland

Re: What to do immediately after entering protected mode?

Post by DavidCooper »

Ziddia wrote:My major issue is that my previous code did all of the above, then jumped to a routine called clear_pipe. The original contents of that method are here:

Code: Select all

clear_pipe:
MOV AX, 10h
MOV DS, AX
MOV SS, AX
MOV ESP, 090000h
MOV BYTE [DS:0B8000h], 'P'
MOV BYTE [DS:0B8001h], 1Bh
However, I'm not entirely sure what the point of that is, especially seeing as SS stays at 9000h regardless. Another problem is that, as soon as I move to protected mode, DS changes from 1000h to 0, and stays at 0 no matter what I do. I'm somewhat confused as to the purpose of anything in the clear_pipe method. Finally, the last two lines are meant to print the character 'P' to the screen, but they don't. Could this be because DS is 0 at the time?
Once you've switched to protected mode, the values in the segment registers are all holding real mode values which need to be replaced with protected mode values. The first three instructions sort out DS and SS, the fourth sets the stack up for the new segment value, and then the rest print "P" to the screen to show you if the code has run successfully or not. I don't know why you've removed this, so put it back in, and you might as well set ES just after DS while you're about it.

Your GDT code looks fine, so you should maybe add more code in various places before that to print to the screen to find out where it goes wrong. Set ES to B800h, DI to 0, AX to A61h/A62h/A63h/etc. and send to the screen with stosw.

Code: Select all

push cs
pop ds            ; segments out of order, fix it up
I hope you know what's in CS.

By the way, the routine's name "clear-pipe" has absolutely nothing to do with its content, but refers to the jump to the start of it (which loads CS with a protected mode segment value).
Help the people of Laos by liking - https://www.facebook.com/TheSBInitiative/?ref=py_c

MSB-OS: http://www.magicschoolbook.com/computing/os-project - direct machine code programming
Ziddia
Member
Member
Posts: 38
Joined: Sat Nov 05, 2011 6:28 pm

Re: What to do immediately after entering protected mode?

Post by Ziddia »

AJ wrote:Hi,
Ziddia wrote: I've used VirtualBox's debugger to dump the contents of the GDT, and it seems to have about a hundred more entries than I wanted (output is here).
I'm not familiar with VirtualBox's debugger, but that "GDT" looks as though it's simply converting real mode segments to linear offsets (perhaps this is how virtualbox does its segment translation internally?). That is not a 32 bit protected mode GDT.

Also, you %include enablea20.s, but have no jump directive at the start of your stage 2 loader - if this is a flat binary, the first line of 'enableA20.s' will be your entry point, which is perhaps why you never get as far as loading the GDT.

Cheers,
Adam
I'm aware that the GDT probably isn't right, but I plan on recreating it once I get into the C code, where I'm a bit more comfortable.

Thanks for the info on the inclusion - I do get further than that file, but I will put a jump directive in to start it off.
Ziddia
Member
Member
Posts: 38
Joined: Sat Nov 05, 2011 6:28 pm

Re: What to do immediately after entering protected mode?

Post by Ziddia »

DavidCooper wrote:
Ziddia wrote:My major issue is that my previous code did all of the above, then jumped to a routine called clear_pipe. The original contents of that method are here:

Code: Select all

clear_pipe:
MOV AX, 10h
MOV DS, AX
MOV SS, AX
MOV ESP, 090000h
MOV BYTE [DS:0B8000h], 'P'
MOV BYTE [DS:0B8001h], 1Bh
However, I'm not entirely sure what the point of that is, especially seeing as SS stays at 9000h regardless. Another problem is that, as soon as I move to protected mode, DS changes from 1000h to 0, and stays at 0 no matter what I do. I'm somewhat confused as to the purpose of anything in the clear_pipe method. Finally, the last two lines are meant to print the character 'P' to the screen, but they don't. Could this be because DS is 0 at the time?
Once you've switched to protected mode, the values in the segment registers are all holding real mode values which need to be replaced with protected mode values. The first three instructions sort out DS and SS, the fourth sets the stack up for the new segment value, and then the rest print "P" to the screen to show you if the code has run successfully or not. I don't know why you've removed this, so put it back in, and you might as well set ES just after DS while you're about it.

Your GDT code looks fine, so you should maybe add more code in various places before that to print to the screen to find out where it goes wrong. Set ES to B800h, DI to 0, AX to A61h/A62h/A63h/etc. and send to the screen with stosw.

Code: Select all

push cs
pop ds            ; segments out of order, fix it up
I hope you know what's in CS.

By the way, the routine's name "clear-pipe" has absolutely nothing to do with its content, but refers to the jump to the start of it (which loads CS with a protected mode segment value).
The only issue I have, then, is that the P isn't printing to the screen. I'll do some debuggy stuff and take a look.
Ziddia
Member
Member
Posts: 38
Joined: Sat Nov 05, 2011 6:28 pm

Re: What to do immediately after entering protected mode?

Post by Ziddia »

Ziddia wrote:
DavidCooper wrote:
Ziddia wrote:My major issue is that my previous code did all of the above, then jumped to a routine called clear_pipe. The original contents of that method are here:

Code: Select all

clear_pipe:
MOV AX, 10h
MOV DS, AX
MOV SS, AX
MOV ESP, 090000h
MOV BYTE [DS:0B8000h], 'P'
MOV BYTE [DS:0B8001h], 1Bh
However, I'm not entirely sure what the point of that is, especially seeing as SS stays at 9000h regardless. Another problem is that, as soon as I move to protected mode, DS changes from 1000h to 0, and stays at 0 no matter what I do. I'm somewhat confused as to the purpose of anything in the clear_pipe method. Finally, the last two lines are meant to print the character 'P' to the screen, but they don't. Could this be because DS is 0 at the time?
Once you've switched to protected mode, the values in the segment registers are all holding real mode values which need to be replaced with protected mode values. The first three instructions sort out DS and SS, the fourth sets the stack up for the new segment value, and then the rest print "P" to the screen to show you if the code has run successfully or not. I don't know why you've removed this, so put it back in, and you might as well set ES just after DS while you're about it.

Your GDT code looks fine, so you should maybe add more code in various places before that to print to the screen to find out where it goes wrong. Set ES to B800h, DI to 0, AX to A61h/A62h/A63h/etc. and send to the screen with stosw.

Code: Select all

push cs
pop ds            ; segments out of order, fix it up
I hope you know what's in CS.

By the way, the routine's name "clear-pipe" has absolutely nothing to do with its content, but refers to the jump to the start of it (which loads CS with a protected mode segment value).
The only issue I have, then, is that the P isn't printing to the screen. I'll do some debuggy stuff and take a look.
It seems that I don't get past loading the gdt, as everything before that is executed, but nothing afterwards is.

I tested it by placing a line to jump to the routine 'failed' right after loading the gdt; the code for 'failed' is here, just in case I did something wrong. Interrupts are still off at this point.

Code: Select all

failed:
   ; Clear screen, set attributes to white text on red background.
   mov ax, 0x4f20
   mov cx, 2000
   push WORD 0xb800
   pop es
   xor di, di
   rep stosw
User avatar
turdus
Member
Member
Posts: 496
Joined: Tue Feb 08, 2011 1:58 pm

Re: What to do immediately after entering protected mode?

Post by turdus »

DavidCooper wrote:Once you've switched to protected mode, the values in the segment registers are all holding real mode values which need to be replaced with protected mode values. The first three instructions sort out DS and SS, the fourth sets the stack up for the new segment value, and then the rest print "P" to the screen to show you if the code has run successfully or not. I don't know why you've removed this, so put it back in, and you might as well set ES just after DS while you're about it.
Correct.
Your GDT code looks fine, so you should maybe add more code in various places before that to print to the screen to find out where it goes wrong. Set ES to B800h, DI to 0, AX to A61h/A62h/A63h/etc. and send to the screen with stosw.
You're mixing modes, ES cannot be 0B800h in protected mode.

Code: Select all

push cs
pop ds            ; segments out of order, fix it up
I hope you know what's in CS.
Wrong, CS should point to a valid code segment, but DS should select a valid data segment from GDT/LDT. So their value should never be identical in protmode (save some brainf*cking low-level isr code maybe).
By the way, the routine's name "clear-pipe" has absolutely nothing to do with its content, but refers to the jump to the start of it (which loads CS with a protected mode segment value).
Correct again.

@OP: As David pointed out, when you enable protmode, your segment registers became invalid. So the first thing to do is fix it (do not even call a routine, since SS is invalid too). CS register is special, as you cannot load it directly, you'll need a far jump for that. Assuming your GDT is as follows:
1. null
2. code segment
3. data segment
you'll need something right after enabling:

Code: Select all

jmp 8:protstart ;load CS to select code segment
protstart:
mov ax, 16 ;load the data selector into ax
mov ds, ax ;load the data selector to all segment registers
mov es, ax
mov fs, ax
mov gs, ax
Now if you want to plot a character on screen, use base address of 0 (in segment descriptor) and offset of 0xB80000 (your real mode ES*16). If you don't understand why, I suggest to read our wiki or other documents on segmented real mode addressing.
User avatar
DavidCooper
Member
Member
Posts: 1150
Joined: Wed Oct 27, 2010 4:53 pm
Location: Scotland

Re: What to do immediately after entering protected mode?

Post by DavidCooper »

turdus wrote:
DavidCooper wrote:Your GDT code looks fine, so you should maybe add more code in various places before that to print to the screen to find out where it goes wrong. Set ES to B800h, DI to 0, AX to A61h/A62h/A63h/etc. and send to the screen with stosw.
You're mixing modes, ES cannot be 0B800h in protected mode.
Check the bold print above. My concern was that his A20 setting code might be faulty as we haven't seen it yet. It may be though that it's all fine and he just isn't printing to the screen the right way at the end, so your advice on that score should provide answers.

Code: Select all

push cs
pop ds            ; segments out of order, fix it up
I hope you know what's in CS.
Wrong, CS should point to a valid code segment, but DS should select a valid data segment from GDT/LDT. So their value should never be identical in protmode (save some brainf*cking low-level isr code maybe).
Again this is while still way back when it's in real mode - I don't like seeing other segments taking their value from CS, but it's possible that CS was set up carefully in the first stage which we haven't seen, so it may not result in a problem.
Help the people of Laos by liking - https://www.facebook.com/TheSBInitiative/?ref=py_c

MSB-OS: http://www.magicschoolbook.com/computing/os-project - direct machine code programming
User avatar
Kazinsal
Member
Member
Posts: 559
Joined: Wed Jul 13, 2011 7:38 pm
Libera.chat IRC: Kazinsal
Location: Vancouver
Contact:

Re: What to do immediately after entering protected mode?

Post by Kazinsal »

Are you being loaded to 0000:1000 or 1000:0000? Your post isn't entirely clear and I suspect that your real mode segments and offsets are behind this.
Post Reply