Page 1 of 1

Changing into PMode [NOT quite solved]

Posted: Sat Apr 18, 2009 4:00 pm
by kay10
Hello everybody, it's me again... :roll:

I've got a problem while changing into protected mode with my 2 stage loader.
Bochs is telling me the following message every time when it should do the far jump to finally change into pmode:
check_cs(0x0008): not a valid code segment!
That's weird for me because the 0x8 should have been the code descriptor and not a code segment. :?

I've already used Bochs internal debugger and it told me that the GDT table should have been loaded correctly.

I'm new to protected mode, so every help will be appreciated. :)

Here are the important parts of my code:

Code: Select all

[BITS 16]
[ORG 0x0000]

MAIN:
MOV AX, CS					; Startsegment des Kernels in AX schreiben... (0x100)
MOV DS, AX					; ... und danach in die folgenden Register
MOV ES, AX
MOV FS, AX
MOV GS, AX
XOR AX, AX
MOV SS, AX
MOV SP, 0xFFFF

MOV SI, AktivatorExec
CALL PRINT_STRING
MOV SI, PModeStart
CALL PRINT_STRING
XOR AX, AX					; AX = 0 -> A20 Gate aktivieren
CALL CheckA20				; A20 Gate de-/aktivieren

CLI
PUSHA
LGDT [GDT]
POPA

MOV EAX, CR0
OR EAX, 1
MOV CR0, EAX
JMP 0x8:PMode				; 0x8 -> 2. Deskriptor (CODE_Desc)

[...]               ; UNimportant parts of my code (variables and funtions like enabling A20 or printing a string)

;************************************************;
;******************** GDT ***********************;
;************************************************;
GDT:
DW GDTEnd - NULL_Desc - 1	; GDT limit
DD NULL_Desc				; GDT base
NULL_Desc:
DD 0
DD 0
CODE_Desc:
DW 0xFFFF
DW 0x0000
DB 0x00
DB 10011010b
DB 11001111b
DB 0x00
DATA_Desc:
DW 0xFFFF
DW 0x0000
DB 0x00
DB 10011010b
DB 11001111b
DB 0x00
GDTEnd:

[BITS 32]

PMode:
MOV AX, 0x10				; AX -> 3. Deskriptor (DATA_Desc)
MOV DS, AX
MOV ES, AX
MOV FS, AX
MOV GS, AX
MOV SS, AX
MOV ESP, 0x90000
JMP $
I can post the whole code if you need it.
Thanks.

Re: Changing into PMode

Posted: Sat Apr 18, 2009 4:30 pm
by kmtdk
hi
it is due to 1 simple fact.
the org .
you know that you are loaded at another address than 0x0000
the problem is this:
you say "we start at 0x0000"
at the gdt the compliler will make it 0x0000 + the size of the code so far ( so when you use LGDT, it loads [ds:offset], so if ds is set currectly it shuld however work a little more, except for the jump, since it will not end at where you expect, since it is 32 bit.
so when you are loaded at where it is, it will not be correct.

another problem, it the data des, since it is like the code des, and that is not legal !

( the access is set wrong)

KMT dk

Re: Changing into PMode

Posted: Sat Apr 18, 2009 4:50 pm
by kay10
Thanks for your answer.
DS should have been set correctly at the beginning of the code.
I'm getting a new message since I changed the data descriptor:
jump_protected: gate type 0 unsupported
So the only problem now is the far jump because the ORG directive isn't set correctly, right?
Is it possible to make a correct far jump without setting the ORG directive?
Maybe adding the current value of CS shifted 4 bits left would be enough?
Something like this? (theoretical)

Code: Select all

MOV AX, CS
SHL AX, 4
JMP 0x8:AX+PMode
I'll test it.

Re: Changing into PMode

Posted: Sat Apr 18, 2009 4:53 pm
by kmtdk
to tell you the answerd, i need to know
what is cs in the start of the code, and where are the code supossed to be loaded

KMT dk

Re: Changing into PMode

Posted: Sat Apr 18, 2009 5:13 pm
by kay10
The second stage loader gets loaded by the bootloader at 0x1000:0x0000,
so CS is 0x1000 and it doesn't get changed in the second stage (It applys the value of CS to every other segment register).
I think that's what you wanted to know, if I understood you right.

EDIT: But it isn't difficult for me to load the second stage at a different address (1 line of code in my bootloader),
so, if it isn't possible with the current address, I can change it to 0x0800:0x0000, when I want to.

Re: Changing into PMode

Posted: Sat Apr 18, 2009 5:26 pm
by kmtdk
oh
my bad:..
LGDT does not use DS:offset ( just cheked intel manual ..)
so in order to fix the problem , you will have to write :

dd NULL_Desc + 0x100000 ; GDT base

the "0x100000" is just the linary address of 0x1000:0x0000

( seg *10 + offset)


KMT dk

Re: Changing into PMode

Posted: Sat Apr 18, 2009 5:52 pm
by kay10
kmtdk wrote:LGDT does not use DS:offset ( just cheked intel manual ..)
Ooops, Intel manual vs. Bochs internal debugger :mrgreen:
(0) [0x00010024] 1000:0024 (unk. ctxt): lgdt ds:0x31b
I think Intel knows better what their processors do. :wink:

But this
kmtdk wrote:dd NULL_Desc + 0x100000 ; GDT base
didn't solve my problem :(

Bochs prints exactly the same message as before...
How can I know whether the 0x100000 are correctly added or not?

Re: Changing into PMode

Posted: Sun Apr 19, 2009 3:09 am
by Combuster
There's a math error
0x1000:0x0000 = 64k
0x100000 = 1M; compare that to
0x10000 = 0x1000 * 16

Also, LGDT does use seg:offset, although the DS is left out in common notation because its implicit, just as it is for practically all other memory-accessing instructions. The manuals do tell you that.

Re: Changing into PMode

Posted: Sun Apr 19, 2009 4:59 am
by kay10
Combuster wrote:There's a math error
0x1000:0x0000 = 64k
0x100000 = 1M; compare that to
0x10000 = 0x1000 * 16
You're right, I was really tired yesterday so I don't saw it anymore.

I think there's only a little mistake in my code because I fixed the math error
and it still don't work. It's the same as before. :(
It looks like it adds the address correctly now, a register dump from bochs shows it:
<bochs:28> sreg
cs:s=0x1000, dl=0x0000ffff, dh=0x00009301, valid=1
ds:s=0x1000, dl=0x0000ffff, dh=0x00009301, valid=3
ss:s=0x0000, dl=0x0000ffff, dh=0x00009300, valid=7
es:s=0x1000, dl=0x0000ffff, dh=0x00009301, valid=1
fs:s=0x1000, dl=0x0000ffff, dh=0x00009301, valid=1
gs:s=0x1000, dl=0x0000ffff, dh=0x00009301, valid=1
ldtr:s=0x0000, dl=0x00000000, dh=0x00000000, valid=0
tr:s=0x0000, dl=0x00000000, dh=0x00000000, valid=0
gdtr:base=0x00010321, limit=0x17
idtr:base=0x00000000, limit=0x3ff
I made this dump directly after activating protected mode in CR0 and before far jumping to the new label.

I don't want to copy the whole protected mode process from a tutorial,
so it would be nice, if someone had a closer look at my code.

EDIT: I think it executes some garbage code because I get the following message now, when it tries to do the far jump :
00008703576i[CPU0 ] LOCK prefix unallowed (op1=0x53, attr=0x0, mod=0x0, nnn=0)
00008703576e[CPU0 ] write_virtual_checks(): write beyond limit, r/w
00008703576i[CPU0 ] CPU is in protected mode (active)

00008703576i[CPU0 ] CS.d_b = 32 bit
00008703576i[CPU0 ] SS.d_b = 16 bit
00008703576i[CPU0 ] EFER = 0x00000000
00008703576i[CPU0 ] | RAX=0000000060000011 RBX=0000000000000007
00008703576i[CPU0 ] | RCX=0000000000000000 RDX=0000000000000001
00008703576i[CPU0 ] | RSP=000000000000ffff RBP=0000000000000000
00008703576i[CPU0 ] | RSI=00000000ffff02b4 RDI=0000000000080004
00008703576i[CPU0 ] | R8=0000000000000000 R9=0000000000000000
00008703576i[CPU0 ] | R10=0000000000000000 R11=0000000000000000
00008703576i[CPU0 ] | R12=0000000000000000 R13=0000000000000000
00008703576i[CPU0 ] | R14=0000000000000000 R15=0000000000000000
00008703576i[CPU0 ] | IOPL=0 id vip vif ac vm RF nt of df if tf sf zf af PF cf
00008703576i[CPU0 ] | SEG selector base limit G D
00008703576i[CPU0 ] | SEG sltr(index|ti|rpl) base limit G D
00008703576i[CPU0 ] | CS:0008( 0001| 0| 0) 00000000 000fffff 1 1
00008703576i[CPU0 ] | DS:1000( 0005| 0| 0) 00010000 0000ffff 0 0
00008703576i[CPU0 ] | SS:0000( 0005| 0| 0) 00000000 0000ffff 0 0
00008703576i[CPU0 ] | ES:1000( 0005| 0| 0) 00010000 0000ffff 0 0
00008703576i[CPU0 ] | FS:1000( 0005| 0| 0) 00010000 0000ffff 0 0
00008703576i[CPU0 ] | GS:1000( 0005| 0| 0) 00010000 0000ffff 0 0
00008703576i[CPU0 ] | MSR_FS_BASE:0000000000010000
00008703576i[CPU0 ] | MSR_GS_BASE:0000000000010000
00008703576i[CPU0 ] | RIP=0000000000000339 (0000000000000339)
00008703576i[CPU0 ] | CR0=0x60000011 CR1=0x0 CR2=0x0000000000000000
00008703576i[CPU0 ] | CR3=0x00000000 CR4=0x00000000

00008703576i[CPU0 ] >> inc dword ptr ds:[eax] : FF00
00008703576e[CPU0 ] exception(): 3rd (13) exception with no resolution, shutdown
status is 00h, resetting
But I've never used any LOCK prefixes or "inc dword ptr ds:[eax]". :?

Re: Changing into PMode

Posted: Sun Apr 19, 2009 5:52 am
by kmtdk
well
look at the RIP (i hope the snap is just after the jump, :P )
it says:

Code: Select all

00008703576i[CPU0 ] | RIP=0000000000000339 (0000000000000339)
so
this has to be currected
since you might be at:
0x0000000000000339+0x100000 = 100339

@combuster:
my bad.. :S

KMT dk

Re: Changing into PMode

Posted: Sun Apr 19, 2009 6:17 am
by kay10
kmtdk wrote:well
look at the RIP (i hope the snap is just after the jump, :P )
it says:

Code: Select all

00008703576i[CPU0 ] | RIP=0000000000000339 (0000000000000339)

so
this has to be currected
since you might be at:
0x0000000000000339+0x100000 = 100339
That's it! It works now! :)
I had to change this line

Code: Select all

JMP 0x8:PMode
into this

Code: Select all

JMP DWORD 0x8:0x10000+PMode
Thank you kmtdk and Combuster. :)

My next goal would be to make it more flexible because when I gets loaded at a different address, it doesn't work anymore, cause of the 0x10000.
So I have to patch the GDT base "on-the-fly", but I think that's easier than modifying the far jump.
Is this possible? Doing a indirect far jump?
Wait, I think someone told me in another thread...

EDIT:
If I'm right, this is what I needed:

Code: Select all

FF /5 	 JMP m16:32 	 Jump far, absolute indirect, address given in m16:32
But how do I use it correctly?
This isn't right, it doesn't work:

Code: Select all

MOV WORD [JMPPointer+4], 0x0008
MOV DWORD [JMPPointer], 0x10000
ADD DWORD [JMPPointer], PMode
JMP DWORD [JMPPointer]
EDIT 2: I've already figured out how to patch the GDT base

Code: Select all

MOV AX, CS
SHL EAX, 4					; Multiplikation mit 16 bzw. 0x10
ADD DWORD [GDT+2], EAX		; Patchen der "Base" der GDT
Works like a charm.

Now I only need to know how to modify the far jump.