Page 1 of 1

[Solved]Can't update CS after load gdt

Posted: Wed Aug 10, 2016 8:07 am
by OhShift
Hello guys, I am trying to change gdt from old one which provided by uefi to my own gdt.
After load my gdt, I need to update register cs, but when I put code segment in it, the CPU reset!
I feel confused, hope someone can take me out of this reset loop. :(

Here is my code:

Code: Select all

[bits 64] ;uefi should bring us to long mode
entry:
   cli ;disable interrupt
    lgdt [GDT_R] ;load gdt
    mov rsp, STACK_BOTTOM
    mov rax, 8
    push rax ;push code seg
    push new ;push where we want to go
    retf ;far return
    new:
    mov ax, 16 ;update data seg
    mov es, ax
    mov ss, ax
    mov ds, ax
    mov fs, ax
    mov gs, ax
    call main ;call c code
    jmp HALT
And here is the GDT:

Code: Select all

GDT_R:
	dw (GDT_END - GDT)
	dq GDT
	
GDT:
NULL:
	dw 0x0
	dw 0x0
	db 0x0
	db 0x0
	db 0x0
	db 0x0
CODE:
	dw 0xffff
	dw 0x0
	db 0x0
	db 0x9a
	db 0xaf
	db 0x0
DATA:
	dw 0xffff
	dw 0x0
	db 0x0
	db 0x92
	db 0xcf
	db 0x0
GDT_END:
==== 4-9-2016 ====
I fix it with:

Code: Select all

    cli ;disable interrupt

	lgdt [GDT_R]

	mov eax, 0x10
	push rax
	mov rax, STACK_BOTTOM
	push rax
	push 0x202
	mov eax, 0x8
	push rax
	mov rax, InNewGDT
	push rax
	iretq
InNewGDT:
	mov ax, 0x10
	mov es, ax
	mov ss, ax
	mov ds, ax
	mov fs, ax
	mov gs, ax

	lea rax, [rel main]
	call rax
    jmp HALT

GDT_R:
	dw GDT_END - GDT - 1
	dq GDT
GDT:
NULL: equ $ - GDT
	dq 0x0
CODE: equ $ - GDT
	dw 0xffff
	dw 0x0
	db 0x0
	db 0x9a
	db 0xaf
	db 0x0
DATA: equ $ - GDT
	dw 0xffff
	dw 0x0
	db 0x0
	db 0x92
	db 0xcf
	db 0x0
GDT_END:
retf cant work well, so instead i use iretq and everything works well!

sorry for my poor English.

Re: Can't update CS after load gdt

Posted: Wed Aug 10, 2016 12:11 pm
by Octacone
OhShift wrote:

Code: Select all

[bits 64] ;uefi should bring us to long mode
That is not how long mode works. You cannot enter long mode by doing "BITS 64", you need to set it up.
Take a look at: http://wiki.osdev.org/Setting_Up_Long_Mode You need to set up your GDT for 64 Bit arch.

Re: Can't update CS after load gdt

Posted: Wed Aug 10, 2016 12:42 pm
by SpyderTL
[bits 64] only tells the compiler that it should generate 64-bit long mode byte codes. It doesn't tell the CPU (or UEFI) that it should be in long mode.

When UEFI calls your code, you are probably going to be in 32-bit protected mode. So you should use [Bits 32] until you switch to 64-bit mode yourself.

Re: Can't update CS after load gdt

Posted: Wed Aug 10, 2016 1:41 pm
by Octocontrabass
Are you trying to do this before exiting boot services?

Does the PE header correctly identify your binary as 64-bit? UEFI will load 64-bit binaries in long mode, no matter what the previous posts say. :wink:

Does the assembler you're using assemble "push new" as "push qword new" and not "push dword new"?

Have you tried debugging your code using QEMU? (Bochs doesn't seem to have any UEFI firmware available.)

Re: Can't update CS after load gdt

Posted: Wed Aug 10, 2016 3:36 pm
by iansjack
Octocontrabass wrote:UEFI will load 64-bit binaries in long mode, no matter what the previous posts say. :wink:
That may be so, but the new GDT has to have 64-bit descriptors, unlike those that the OP is trying to use.

Re: Can't update CS after load gdt

Posted: Thu Aug 11, 2016 12:17 am
by OhShift
octacone wrote:
OhShift wrote:

Code: Select all

[bits 64] ;uefi should bring us to long mode
That is not how long mode works. You cannot enter long mode by doing "BITS 64", you need to set it up.
Take a look at: http://wiki.osdev.org/Setting_Up_Long_Mode You need to set up your GDT for 64 Bit arch.
I compiled a 64bit efi file and when the uefi bios support 64bit mode, my bootloader works!
But when i boot it in 32bit bios, it give me error message. So I think i probably is in Long Mode.

btw, thanks for the comment :D

Re: Can't update CS after load gdt

Posted: Thu Aug 11, 2016 12:38 am
by OhShift
Octocontrabass wrote:Are you trying to do this before exiting boot services?
No, i don't. In fact,

Code: Select all

entry
is called by my bootloader, and it is after ExitBootService() and SetVirtualAddress().
Octocontrabass wrote:Does the PE header correctly identify your binary as 64-bit? UEFI will load 64-bit binaries in long mode, no matter what the previous posts say. :wink:
I think so.. My compiler option is set to X64. It should give me an X64 output.
Octocontrabass wrote:Does the assembler you're using assemble "push new" as "push qword new" and not "push dword new"?
But address in Long Mode are 64bit right? I disassembled the binary file, and its show "push qword new".
Octocontrabass wrote:Have you tried debugging your code using QEMU? (Bochs doesn't seem to have any UEFI firmware available.)
Yes, I have been tried. and here is the debug log:

Code: Select all

Triple fault
CPU Reset (CPU 0)
RAX=00000000bff20e18 RBX=00000000bff21f18 RCX=0000000000000000 RDX=00000000bff21f88
RSI=00000000be780698 RDI=00000000bff21f70 RBP=00000000bff94b20 RSP=00000000bff94ab8
R8 =00000000bff94a4c R9 =00000000bff18518 R10=00000000bed02ec8 R11=0000000000000040
R12=0000000000000000 R13=0000000000100000 R14=0000000000090002 R15=0000000000000000
RIP=0000000000100010 RFL=00000046 [---Z-P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0008 0000000000000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
CS =0028 0000000000000000 ffffffff 00af9b00 DPL=0 CS64 [-RA]
SS =0008 0000000000000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
DS =0008 0000000000000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
FS =0008 0000000000000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
GS =0008 0000000000000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
LDT=0000 0000000000000000 0000ffff 00008200 DPL=0 LDT
TR =0000 0000000000000000 0000ffff 00008b00 DPL=0 TSS64-busy
GDT=     0000000000100034 00000018
IDT=     00000000bfc07018 00000fff
CR0=80000033 CR2=0000000000000000 CR3=00000000bff33000 CR4=00000668
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000 
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=0000000000000017 CCD=0000000000000000 CCO=SUBQ    
EFER=0000000000000500
FCW=037f FSW=0000 [ST=0] FTW=00 MXCSR=00001f80
FPR0=0000000000000000 0000 FPR1=0000000000000000 0000
FPR2=0000000000000000 0000 FPR3=0000000000000000 0000
FPR4=0000000000000000 0000 FPR5=0000000000000000 0000
FPR6=0000000000000000 0000 FPR7=0000000000000000 0000
XMM00=00000000000000000000000000000000 XMM01=00000000000000000000000000000000
XMM02=00000000000000000000000000000000 XMM03=00000000000000000000000000000000
XMM04=00000000000000000000000000000000 XMM05=00000000000000000000000000000000
XMM06=00000000000000000000000000000000 XMM07=00000000000000000000000000000000
XMM08=00000000000000000000000000000000 XMM09=00000000000000000000000000000000
XMM10=00000000000000000000000000000000 XMM11=00000000000000000000000000000000
XMM12=00000000000000000000000000000000 XMM13=00000000000000000000000000000000
XMM14=00000000000000000000000000000000 XMM15=00000000000000000000000000000000
Thanks for the comment :D

Re: Can't update CS after load gdt

Posted: Thu Aug 11, 2016 2:14 am
by iansjack
How much RAM are you allocating to the QEMU virtual machine?

Re: Can't update CS after load gdt

Posted: Thu Aug 11, 2016 3:04 am
by OhShift
iansjack wrote:How much RAM are you allocating to the QEMU virtual machine?
4GB

Re: Can't update CS after load gdt

Posted: Thu Aug 11, 2016 4:06 am
by Brendan
Hi,
OhShift wrote:

Code: Select all

    push rax ;push code seg
    push new ;push where we want to go
    retf ;far return
For UEFI, the executable is position independent (could be loaded at any virtual address). For this reason it's impossible for "push new" to work properly because it's impossible for the assembler or linker to guess the address it might be loaded; and you'd have to do something like (e.g. NASM syntax) "lea rax,[rel new]" (to get an RIP relative address into RAX) and "push rax".


Cheers,

Brendan

Re: Can't update CS after load gdt

Posted: Thu Aug 11, 2016 6:09 am
by OhShift
Brendan wrote:Hi,
OhShift wrote:

Code: Select all

    push rax ;push code seg
    push new ;push where we want to go
    retf ;far return
For UEFI, the executable is position independent (could be loaded at any virtual address). For this reason it's impossible for "push new" to work properly because it's impossible for the assembler or linker to guess the address it might be loaded; and you'd have to do something like (e.g. NASM syntax) "lea rax,[rel new]" (to get an RIP relative address into RAX) and "push rax".


Cheers,

Brendan
Thanks for the comment! After i change "push new" to "lea rax, [rel new]" and "push rax", i still got a Triple fault when execute "retf". what should i do?
Should i reset uefi paging before reset GDT?

Re: Can't update CS after load gdt

Posted: Thu Aug 11, 2016 6:37 am
by Brendan
Hi,
OhShift wrote:
Brendan wrote:
OhShift wrote:

Code: Select all

    push rax ;push code seg
    push new ;push where we want to go
    retf ;far return
For UEFI, the executable is position independent (could be loaded at any virtual address). For this reason it's impossible for "push new" to work properly because it's impossible for the assembler or linker to guess the address it might be loaded; and you'd have to do something like (e.g. NASM syntax) "lea rax,[rel new]" (to get an RIP relative address into RAX) and "push rax".
Thanks for the comment! After i change "push new" to "lea rax, [rel new]" and "push rax", i still got a Triple fault when execute "retf". what should i do?
Should i reset uefi paging before reset GDT?
I'd assume you have the same problem everywhere else too.

For example; for the first 6 instructions of the code you posted:
  • your "lgdt [GDT_R]" loads the GDTR from an address that's impossible to know at compile time
  • the GDTR pointer itself (at "GDT_R:") contains an "address of GDT" that's impossible to know at compile time
  • you load RSP with an address that's impossible to know at compile time
  • then you do the "push new" (already mentioned)
It's essentially the same problem repeated 4 times in 6 instructions.


Cheers,

Brendan

Re: Can't update CS after load gdt

Posted: Thu Aug 11, 2016 8:36 am
by OhShift
Brendan wrote:Hi,
I'd assume you have the same problem everywhere else too.

For example; for the first 6 instructions of the code you posted:
  • your "lgdt [GDT_R]" loads the GDTR from an address that's impossible to know at compile time
  • the GDTR pointer itself (at "GDT_R:") contains an "address of GDT" that's impossible to know at compile time
  • you load RSP with an address that's impossible to know at compile time
  • then you do the "push new" (already mentioned)
It's essentially the same problem repeated 4 times in 6 instructions.


Cheers,

Brendan
How can i calculate the address of GDT in GDT_R?