OSDev.org

The Place to Start for Operating System Developers
It is currently Thu Mar 28, 2024 5:11 am

All times are UTC - 6 hours




Post new topic Reply to topic  [ 15 posts ] 
Author Message
 Post subject: UEFI loader, jump to protected mode environment?
PostPosted: Thu Mar 09, 2017 9:04 am 
Offline

Joined: Wed Feb 01, 2017 8:09 am
Posts: 10
Location: Germany, Paderborn
Hi there,

I'm currently writing an UEFI OS loader. My goal is to load a 32-bit multiboot compliant kernel. Loading the kernel to memory and relocating it to its final destination is not a big deal using UEFI boot-services.

However, jumping to the kernels start address (after exiting boot services) failes on qemu (TianoCore Firmware-Image) with a "general protection fault".

My entry address is "0x01302298". The code just before jumping to the start address looks like this:

Code:
Dump of assembler code for function call_kernel:
   0x000000001ead3b31 <+0>:   push   %rbp
   0x000000001ead3b32 <+1>:   mov    %rsp,%rbp
   0x000000001ead3b35 <+4>:   sub    $0x8,%rsp
   0x000000001ead3b39 <+8>:   mov    %rdi,-0x8(%rbp)
=> 0x000000001ead3b3d <+12>:   mov    -0x8(%rbp),%rax
   0x000000001ead3b41 <+16>:   jmpq   *%rax
with RAX=0000000001302298

The next instruction (which is expect) would be:
Code:
a3 bc 7b 31 01          mov    %eax,0x1317bbc

Instead gdb tells me, that the next instruction at this address is
Quote:
=> 0x1302298: movabs %eax,0x8ed88c6601317bbc

I've some theories (but no clue) what happens:
- The firmware puts the cpu into x86_64-"long mode", this leads to miss-interpretation of the next instruction (which is a 32-bit instruction)
- The last jump instruction itself is wrong: -somehow- a 32-bit jump instruction has to be used.

Can someone give me a hint?

Best regards


Top
 Profile  
 
 Post subject: Re: UEFI loader, jump to protected mode environment?
PostPosted: Thu Mar 09, 2017 9:56 am 
Offline
Member
Member

Joined: Mon Feb 02, 2015 7:11 pm
Posts: 898
You firmware is running in long mode (64-bit). If you want to run a 32-bit kernel, you will have to disable it and go to 32-bit protected mode before jumping to your kernel.

_________________
https://github.com/kiznit/rainbow-os


Top
 Profile  
 
 Post subject: Re: UEFI loader, jump to protected mode environment?
PostPosted: Thu Mar 09, 2017 9:59 am 
Offline
Member
Member
User avatar

Joined: Sat Jan 15, 2005 12:00 am
Posts: 8561
Location: At his keyboard!
Hi,

vollkorn wrote:
- The firmware puts the cpu into x86_64-"long mode", this leads to miss-interpretation of the next instruction (which is a 32-bit instruction)


Yes.

To fix this you have to switch to a 16-bit or 32-bit code segment (in long mode), make sure your code is in an identity mapped page (for UEFI it should be anyway), then disable long mode and paging.

Note that most multiboot stuff assumes that there's usable RAM at physical address 0x00100000, and UEFI makes no guarantee about the physical address space layout (and doesn't guarantee that there's usable RAM at physical address 0x00100000, even though it's "relatively likely"). This is unsolvable; which means that there's a (relatively tiny) chance that your code can't work.


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.


Top
 Profile  
 
 Post subject: Re: UEFI loader, jump to protected mode environment?
PostPosted: Fri Mar 10, 2017 6:56 am 
Offline

Joined: Wed Feb 01, 2017 8:09 am
Posts: 10
Location: Germany, Paderborn
Brendan wrote:
To fix this you have to switch to a 16-bit or 32-bit code segment (in long mode)


Okay I did this, and it seems that qemu switches to 32-bit mode (e.g. register is now eax instead of rax).
I modified an already present gdt entry by resetting the "L" bit in the "flags" part of the entry and then reloaded the %cs register with appropriate gdt index. BUT it seems to reset all other registers, including the instruction pointer. Why?
Quote:
store_gdt_desc(&gdt_desc);

struct gdt_t* gdt = (struct gdt_t*) gdt_desc.base;

gdt[7].flags = 0x8; /* Reset 'L' bit in x86-64 descriptor */

asm volatile ("mov $0x38, %%ax\n\t"
"mov %%ax, %%cs"
""
: /* no output */
: /* no input */
: /* no clobber */
);


Top
 Profile  
 
 Post subject: Re: UEFI loader, jump to protected mode environment?
PostPosted: Fri Mar 10, 2017 6:59 am 
Offline
Member
Member

Joined: Mon Mar 25, 2013 7:01 pm
Posts: 5099
vollkorn wrote:
Code:
"mov %%ax, %%cs"

That is not a valid instruction. How did you compile this without errors?


Top
 Profile  
 
 Post subject: Re: UEFI loader, jump to protected mode environment?
PostPosted: Fri Mar 10, 2017 7:37 am 
Offline

Joined: Wed Feb 01, 2017 8:09 am
Posts: 10
Location: Germany, Paderborn
I just double checked: it compiles, but qemu resets all register. Maybe a hard reset due to an illegal instruction / a bug? I'm using gcc 4.6.4 on ubuntu.

How to load cs using inline assembly instead? Google does not want to give me a hint.


Top
 Profile  
 
 Post subject: Re: UEFI loader, jump to protected mode environment?
PostPosted: Fri Mar 10, 2017 8:15 am 
Offline
Member
Member

Joined: Mon Mar 25, 2013 7:01 pm
Posts: 5099
There are a few ways to load CS in inline assembly, but you can't use any of them to switch from 64-bit mode to 32-bit mode in inline assembly because your compiler can't switch from 64-bit to 32-bit in the middle of a function.

You can reload CS using a far return ("retf") instruction.


Top
 Profile  
 
 Post subject: Re: UEFI loader, jump to protected mode environment?
PostPosted: Fri Mar 10, 2017 10:22 am 
Offline

Joined: Wed Feb 01, 2017 8:09 am
Posts: 10
Location: Germany, Paderborn
Okay, I tried it in a different way. But this does not work either... :(

Quote:
make_lm_pm_transition:
.code64
push %rbp
mov %rsp,%rbp
push $0x7
lea (%rip), %rax <-- how can i know the offset to label ".inprotectedmode"?
add $0x8, %rax
push %rax
lret

.inProtectedMode:
.code32
nop
...
nop


Top
 Profile  
 
 Post subject: Re: UEFI loader, jump to protected mode environment?
PostPosted: Fri Mar 10, 2017 10:32 am 
Offline
Member
Member

Joined: Mon Feb 02, 2015 7:11 pm
Posts: 898
Code:
    ...
    push $0x08     ; GDT selector
    push $target    ; jump target
    retf

target:
    ...


or

Code:
    ...
    ljmpl $0x08, $target

target:
    ...

_________________
https://github.com/kiznit/rainbow-os


Top
 Profile  
 
 Post subject: Re: UEFI loader, jump to protected mode environment?
PostPosted: Fri Mar 10, 2017 12:34 pm 
Offline
Member
Member

Joined: Mon Mar 25, 2013 7:01 pm
Posts: 5099
Far jumps are not valid in 64-bit mode. Only a far return will work.


Top
 Profile  
 
 Post subject: Re: UEFI loader, jump to protected mode environment?
PostPosted: Sat Mar 11, 2017 4:01 am 
Offline
Member
Member

Joined: Sat Nov 21, 2009 5:11 pm
Posts: 852
What you mean is:
Code:
push $0x8
lea .inProtectedMode(%rip), %rax
push %rax
lretq

Push $.inProtectedMode might work, but only when the address is less than 0x80000000.

Or, you could simply set the machine type to 0x14c (EFI_IMAGE_MACHINE_IA32) in the PE image.


Top
 Profile  
 
 Post subject: Re: UEFI loader, jump to protected mode environment?
PostPosted: Mon Mar 13, 2017 2:28 am 
Offline

Joined: Wed Feb 01, 2017 8:09 am
Posts: 10
Location: Germany, Paderborn
Gigasoft wrote:
What you mean is:
Code:
push $0x8
lea .inProtectedMode(%rip), %rax
push %rax
lretq

Push $.inProtectedMode might work, but only when the address is less than 0x80000000.

Or, you could simply set the machine type to 0x14c (EFI_IMAGE_MACHINE_IA32) in the PE image.


This did the trick for me, at least for the jumping part. But still, qemu resets all register (see below). Did I miss something?

Quote:
EAX=00000000 EBX=00000000 ECX=00000000 EDX=00000663
ESI=00000000 EDI=00000000 EBP=00000000 ESP=00000000
EIP=0000fff1 EFL=00000002 [-------] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0000 00000000 0000ffff 00009300
CS =f000 ffff0000 0000ffff 00009b00
SS =0000 00000000 0000ffff 00009300
DS =0000 00000000 0000ffff 00009300
FS =0000 00000000 0000ffff 00009300
GS =0000 00000000 0000ffff 00009300
LDT=0000 00000000 0000ffff 00008200
TR =0000 00000000 0000ffff 00008b00
GDT= 00000000 0000ffff
IDT= 00000000 0000ffff
CR0=60000010 CR2=00000000 CR3=00000000 CR4=00000000
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
DR6=00000000ffff0ff0 DR7=0000000000000400
EFER=0000000000000000
FCW=037f FSW=0000 [ST=0] FTW=00 MXCSR=00001f80

=> 0x1ead3dcf <make_lm_pm_transition+6>:
lea 0x2(%rip),%rax # 0x1ead3dd8 <make_lm_pm_transition+15>
(gdb) nexti
14 push %rax
1: x/i $pc
=> 0x1ead3dd6 <make_lm_pm_transition+13>: push %rax
(gdb) nexti
15 lret
1: x/i $pc
=> 0x1ead3dd7 <make_lm_pm_transition+14>: lret
(gdb) nexti
0x0000000000000000 in ?? ()
1: x/i $pc


Top
 Profile  
 
 Post subject: Re: UEFI loader, jump to protected mode environment?
PostPosted: Mon Mar 13, 2017 5:50 am 
Offline

Joined: Wed Feb 01, 2017 8:09 am
Posts: 10
Location: Germany, Paderborn
Haha, I'm making some progress...the trick was - like you said - use lretq instread of lret. Also: gdb must be set to a different target architecture. Otherwise instructions get misinterpreted.

Gigasoft wrote:
What you mean is:
Code:
push $0x8
lea .inProtectedMode(%rip), %rax
push %rax
lretq

Push $.inProtectedMode might work, but only when the address is less than 0x80000000.

Or, you could simply set the machine type to 0x14c (EFI_IMAGE_MACHINE_IA32) in the PE image.


Thanks for your help btw ;-)


Top
 Profile  
 
 Post subject: Re: UEFI loader, jump to protected mode environment?
PostPosted: Mon Mar 13, 2017 1:00 pm 
Offline
Member
Member

Joined: Sat Nov 21, 2009 5:11 pm
Posts: 852
Well, but what is the machine type in the PE image set to? I have no experience with UEFI, but I thought that if you set it to 0x14c rather than 0x200, it should start in 32 bit mode automatically.


Top
 Profile  
 
 Post subject: Re: UEFI loader, jump to protected mode environment?
PostPosted: Tue Mar 14, 2017 7:27 am 
Offline

Joined: Wed Feb 01, 2017 8:09 am
Posts: 10
Location: Germany, Paderborn
Gigasoft wrote:
Well, but what is the machine type in the PE image set to? I have no experience with UEFI, but I thought that if you set it to 0x14c rather than 0x200, it should start in 32 bit mode automatically.


The machine type should be x86_64. I followed the the instructions listed here http://wiki.osdev.org/UEFI#Developing_with_GNU-EFI to setup my toolchain.

How can I read the machine type from my efi application?

Maybe the "jump to protected mode" thing wouldn't be necessary, if the efi-application could be 32-bit from the beginning. However, learning something new is always good :)


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 15 posts ] 

All times are UTC - 6 hours


Who is online

Users browsing this forum: No registered users and 66 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group