flat assembler
Message board for the users of flat assembler.

Index > OS Construction > Switching back to RM after UEFI/EFI initialization?

Author
Thread Post new topic Reply to topic
sid123



Joined: 30 Jul 2013
Posts: 339
Location: Asia, Singapore
sid123 28 Dec 2013, 05:49
Has anyone tried to switch back to Real Mode after UEFI/EFI initialization?
In theory I think it should be possible since UEFI/EFI based machines are simply x86 machines with a Firmware that does switch to Protected Mode after initialization (Maybe? or Maybe Not?). UEFI/EFI also uses POST from the BIOS-era.
So do you think this is possible?
I don't have a UEFI machine. Sad

_________________
"Those who can make you believe in absurdities can make you commit atrocities" -- Voltaire https://github.com/Benderx2/R3X
XD
Post 28 Dec 2013, 05:49
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20430
Location: In your JS exploiting you and your system
revolution 28 Dec 2013, 05:58
I don't see any reason why this won't still work:
Code:
mov eax,cr0
and eax,not 1
mov cr0,eax    
Post 28 Dec 2013, 05:58
View user's profile Send private message Visit poster's website Reply with quote
sid123



Joined: 30 Jul 2013
Posts: 339
Location: Asia, Singapore
sid123 28 Dec 2013, 08:19
How about this :
Take a BIOS Machine.
Copy the whole IVT and write it to disk.
Then put the file in the UEFI Machine.
Load the file.
Switch back to RM.
Load the IVT.
Use BIOS ints.
Image
Unless UEFI has introduced some sort of protection mechanism to prevent the processor from switching to RM.

_________________
"Those who can make you believe in absurdities can make you commit atrocities" -- Voltaire https://github.com/Benderx2/R3X
XD
Post 28 Dec 2013, 08:19
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20430
Location: In your JS exploiting you and your system
revolution 29 Dec 2013, 06:29
sid123 wrote:
Unless UEFI has introduced some sort of protection mechanism to prevent the processor from switching to RM.
that is only possible if UEFI does not give you ring 0 execution.
Post 29 Dec 2013, 06:29
View user's profile Send private message Visit poster's website Reply with quote
Feryno



Joined: 23 Mar 2005
Posts: 514
Location: Czech republic, Slovak republic
Feryno 30 Dec 2013, 12:43
sid123 wrote:
Has anyone tried to switch back to Real Mode after UEFI/EFI initialization?
In theory I think it should be possible since UEFI/EFI based machines are simply x86 machines with a Firmware that does switch to Protected Mode after initialization (Maybe? or Maybe Not?). UEFI/EFI also uses POST from the BIOS-era.
So do you think this is possible?
I don't have a UEFI machine. Sad
Yes, it is possible. Expect that RM 16-bit IDT is empty so you can't enable interrupts until you set some entries in 16-bit real mode IDT and execute LIDT (the 16-bit RM IDT is expected to be placed at 0000:0000 but LIDT is maybe capable to load if from any nonstandard position)

sid123 wrote:
Unless UEFI has introduced some sort of protection mechanism to prevent the processor from switching to RM.
There is no way to prevent the processor from switching to RM. The only one solution for me is running hypevisor which prevents to erase bit 31. and bit 0. of CR0
If you have full control over CPU nothing can prevent that.

Also remember that when any CPU is running in long mode and it receives INIT that resets the target CPU to 16-bit real mode (that is also capable to kill hypervisor if it doesn't care - erasing some necessary bits in control register and in EFER MSR) and then the CPU is expecting to receive SIPI which just gives it the CS:IP (only CS as IP is reset to 0 at INIT) from which it starts to execute real mode 16-bit code.

UEFI machine??? There are some emulators for UEFI (qemu + OVMF is about 5-10 MB only, then VMware about 500 MB, VirtualBox about 100 MB).

UEFI uses CSM (Compatibility support module) when it falls back to BIOS legacy boot mode (that is loading MBR via int 19h).
Post 30 Dec 2013, 12:43
View user's profile Send private message Visit poster's website ICQ Number Reply with quote
sid123



Joined: 30 Jul 2013
Posts: 339
Location: Asia, Singapore
sid123 31 Dec 2013, 11:41
So switching to RM is possible.
Great! That's all what I need. I am gonna start reading about UEFI.
@Feryno Thanks for the info man. I would try to see the source of a BIOS
and then load that IDT after switching to RM. However I am pretty sure that things
like VBE, would not be possible since UEFI uses GOP. Also, who knows VGA might be removed
from future graphic cards. The best way I can see is to emulate these video modes.
Looks like we have finally been able to revive RM after it was killed by UEFI.
(although most CPU manufacturers promise that they will have a legacy BIOS mode, but when I saw my friends computer it didn't even have a legacy boot option, and it was a UEFI machine.)
Post 31 Dec 2013, 11:41
View user's profile Send private message Reply with quote
Feryno



Joined: 23 Mar 2005
Posts: 514
Location: Czech republic, Slovak republic
Feryno 31 Dec 2013, 12:51
Hi sid123
RM is still alive. Even UEFI firmware starts always from RM at phys. mem FFFFFFF0. It quickly switches into protected 32 bit mode or into long mode in about first 10-20 instructions.
Even in UEFI based systems and OS-es need to activate all CPUs in SMP. OS initializes them by sending INIT-SIPI so all application CPUs starts to run in 16 bit RM and OS then switches them into long mode (or only protected mode).
Code:
; some example how UEFI is started, this is disasm from a file containing UEFI firmware - code is located at physical memory address FFFFFFF0 and at offset 7FFFF0 in the file:
007FFFF0:i0F09                           wbinvd
007FFFF2:iE90BFB                         jmpn      file:007FFB00

007FFB00:i0F6EC0                         movd      mm0,ax
007FFB03:iB91B00                         mov       cx,001B
007FFB06:i0F32                           rdmsr
007FFB08:iF6C401                         test (b)  ah,01
007FFB0B:i7438                           je        file:007FFB45 ; check bootstrap CPU or application CPU
; this is executed at bootstrap CPU
007FFB0D:i66BEC0FCFFFF                   mov       esi,FFFFFCC0
007FFB13:i662E0F0114                     lgdt (p)  [si]
007FFB18:i0F20C0                         mov       eax,cr0
007FFB1B:i6683C803                       or (d)    eax,+03
007FFB1F:i0F22C0                         mov       cr0,eax
007FFB22:i0F20E0                         mov       eax,cr4
007FFB25:i660D00060000                   or        eax,00000600
007FFB2B:i0F22E0                         mov       cr4,eax
007FFB2E:iB81800                         mov       ax,0018
007FFB31:i8ED8                           mov       ds,ax
007FFB33:i8EC0                           mov       es,ax
007FFB35:i8EE0                           mov       fs,ax
007FFB37:i8EE8                           mov       gs,ax
007FFB39:i8ED0                           mov       ss,ax
007FFB3B:i66BEC6FCFFFF                   mov       esi,FFFFFCC6
007FFB41:i662EFF2C                       jmp (p)   [si]

; this is executed at application CPU - don't do anything, just halt the CPU (no worry it will be waken up later using INIT-SIPI coming from bootstrap)
007FFB45:iFA                             cli
007FFB46:iF4                             hlt
007FFB47:iEBFC                           jmps      file:007FFB45    


when switching from long mode to real mode I'm doing this (stripped code from my hypervisor project)
Code:
;9.9.2 Switching Back to Real-Address Mode
;The processor switches from protected mode back to real-address mode if software
;clears the PE bit in the CR0 register with a MOV CR0 instruction. A procedure that reenters
;real-address mode should perform the following steps:
;1. Disable interrupts. A CLI instruction disables maskable hardware interrupts. NMI
;interrupts can be disabled with external circuitry.
;2. If paging is enabled, perform the following operations:
;- Transfer program control to linear addresses that are identity mapped to
;physical addresses (that is, linear addresses equal physical addresses).
;- Insure that the GDT and IDT are in identity mapped pages.
;- Clear the PG bit in the CR0 register.
;- Move 0H into the CR3 register to flush the TLB.
;3. Transfer program control to a readable segment that has a limit of 64 KBytes
;(FFFFH). This operation loads the CS register with the segment limit required in
;real-address mode.

use64
        cli
        mov     eax,page1_mem
        mov     cr3,rax

        lgdt    [GDTR_init]
        push    DATA16_SELECTOR_init            ; target SS
        push    top_of_startup_stack            ; target RSP !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! N.B. the same stack will be shared among all CPUs in the system !!!!!!!!!!!!!!!!!!!!!
                                                ; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! it is possible to survive that only in case only one CPU is allowed to use stack and all others must have disabled interrupts and can't use stack like not doing call push pop instruction etc, disabled interrupts prevent interrupts from changing stack)
        pushf                                   ; target RFLAGS
        push    CODE16_SELECTOR_init            ; target CS
        push    return_back                     ; target RIP
        iretq                                   ; update CS and SS as well

return_back:
use16                   ; yeah, 16 bit code, but still long mode...
; we are in identity-mapped memory at this point, running 16 bit code under compatibility submode of long mode
; interrupts are disabled (so no external interrupt hit and we also never do software interrupts) which prevents to accessing stack (the same stack is shared among all CPUs, if they would arbitrary access the stack it will be overwritten for another CPUs and lead into big fucking mess)
        mov     ax,DATA16_SELECTOR_init
        mov     ds,ax
        mov     es,ax
        mov     fs,ax
        mov     gs,ax
; CS, SS already set by the iretq

        mov     eax,cr0
        and     eax,not ((1 shl 31) or (1 shl 0))
        mov     cr0,eax                 ; disable paging (bit 31.) and protected mode (bit 0.)
                                ; instruction must be located in an identity-mapped page

; at this point, EFER.LMA=0 (long mode is not active), but still LME=1
        mov     ecx,MSR_IA32_EFER
        rdmsr
        and     ah,not (1 shl (EFER_LME_bit-8)) ; disable long mode
        wrmsr

        mov     eax,cr4
;;      and     al,not ((1 shl 7) or (1 shl 5)) ; PGE bit 7., PAE bit 5.
;;      mov     cr4,eax                 ; disable global pages and physical-address extensions
;       and     al,not ((1 shl 6) or (1 shl 5)) ; MCE bit 6., PAE bit 5.
;       mov     cr4,eax                 ; disable physical-address extensions
; no, let #MC enabled
        and     al,not (1 shl 5)        ; PAE bit 5.
        mov     cr4,eax                 ; disable physical-address extensions

        xor     eax,eax
        mov     cr3,eax                 ; flush TLB

;       xor     ax,ax                   ; no need again, already zeroed
        mov     ds,ax
        mov     es,ax
        mov     fs,ax
        mov     gs,ax
        mov     ss,ax

        jmp     far 0 : real_mode

rmidt:  dw      3FFh    ; limit
        dd      0       ; base

real_mode:

        lidt    [rmidt]    


tables at page1_mem are constructed before switching by a code like
Code:
use16
; initialize paging tables
page_size                                       =       0000000000200000h
page_size_power                                 =       21      ; page_size = 2 ^ page_size_power
boot_identity_mapped_physical_virtual_memory    =       0       ; we map virtual memory at identical physical memory (RAM)
boot_physical_memory                            =       boot_identity_mapped_physical_virtual_memory and 11111111111111111111b
boot_virtual_memory                             =       boot_identity_mapped_physical_virtual_memory
a = (boot_virtual_memory shr 39) and 111111111b
b = (boot_virtual_memory shr 30) and 111111111b
c = (boot_virtual_memory shr 21) and 111111111b
; we construct paging tables for 2 MB paging at linear address boot_translation_tables_linear_address
        mov     di,page1_mem            ; this is physical address (in RAM)
; clear 3 pages
        mov     cx,6 * 1000h / 2        ; clear all 6 pages used for constructing paging tables
        xor     ax,ax
        cld
        repz    stosw                   ; clear the page tables at memory 1000h-3FFFh
                                        ; another benefit is scasw trick used later
        mov     word [page1_mem + a*8],page2_mem + 111b ; field 0. of of PML4 = pointer to first PDP table
                                        ; upper dword was set 0 at the above repz stosw
        mov     word [page2_mem + b*8],page3_mem + 111b ; field 0. of PDP table = pointer to first page directory table
; now we map 2 MB of memory, the memory will be identity-mapped (physical address in RAM = virtual address), when paging will be enabled, physical address switches into the same virtual address thanks to identity-mapping
        mov     word [page3_mem + c*8],boot_physical_memory + 110000011b        ; field 0. of page directory table (identically mapping of memory 00000-1FFFF)
                                        ; note, the above entries have bit 2. (U/S) set to 1, this last has set it to 0, so only supervisor (ring0, 1, 2) is allowed to access memory page mapped by last entry in PD (the entry has name PDE)
                                        ; N.B. this is 2 MB mapping (bit 7. = PDE.PSE = 1) and pages are global (bit 8. = 1)    


these pages are at these offsets
Code:
virtual at 1000h
        page1_mem       rb      1000h
        page2_mem       rb      1000h
        page3_mem       rb      1000h
        page4_mem       rb      1000h
        page5_mem       rb      1000h
        page6_mem       rb      1000h
        startup_stack   rb      0C00h
        top_of_startup_stack:
end virtual    


GTDT is like this
Code:
align 10h
GDTR_init:                              ; Global Descriptors Table Register
        dw      size_of_GDT_init - 1    ; limit of GDT (size minus one)
        dq      GDT_init                ; linear address of GDT

align 10h
GDT_init:
.null                   dq      0               ; null selector
CODE64_SELECTOR_init    =       $ - .null
                        dq      CODE64_DESCRIPTOR_init
DATA64_SELECTOR_init    =       $ - .null
                        dq      DATA64_DESCRIPTOR_init  ; used for DS, ES, FS, GS
STACK64_SELECTOR_init   =       $ - .null
                        dq      STACK64_DESCRIPTOR_init
TASK64_SELECTOR_init            =       $ - .null
                        dw      sizeof.TSS-1    ; limit
                        dw      0
                        db      0
                        db      TSS_ATTRIBUTES
                        dw      0
                        dd      0
                        dd      0
CODE16_SELECTOR_init    =       $ - .null
                        dw      0FFFFh,0,9A00h,0        ; 16-bit code descriptor to return to 16-bit real mode
                                        ; 9A present, ring 0, code, readable
                                        ; 0 granularity=0 Setting a limit of 0 indicates a 1-byte segment limit when G = 0.
DATA16_SELECTOR_init    =       $ - .null
                        dw      0FFFFh,0,9200h,0        ; 16-bit data descriptor to return to 16-bit real mode
                                        ; 92 present, ring 0, data, expand-up, writable
size_of_GDT_init        =       $ - .null    


I'm doing that quite wildly (all app CPUs at the same time, not one after one)

when you want to play with UEFI I did some command line based debugger few years ago you can download it with the whole qemu and OVMF in one file, just upack and run one *.bat file
http://fdbg.x86asm.net/fdbg.uefi.0002.qemu.zip
the debugger is unable to survive such switch from long mode to real mode

Your friend should enable CSM in UEFI setup and disable Secure boot.

At me it is at pages 3-44 and 3-45 in this MB manual:
http://dlcdnet.asus.com/pub/ASUS/mb/socket1150/P9D_WS/Manual&QVL/E8141_P9D_WS.pdf

Even if your friend doesn't do that - it is still possible to write a uefi executable which switches the CPU in RM. As I wrote in previous post, only some running hypervisor may prevent to do that and there is not known UEFI for me which would run under hypervisor. I'm doing such things just now and that is not part of any UEFI firmware. Real UEFI vendor wouldn't do that.
Post 31 Dec 2013, 12:51
View user's profile Send private message Visit poster's website ICQ Number Reply with quote
sid123



Joined: 30 Jul 2013
Posts: 339
Location: Asia, Singapore
sid123 31 Dec 2013, 13:05
@Feryno EPIICCCCC. You guys are great and polite especially to newbies like me.
Glad to see that some people have really tried this.
Post 31 Dec 2013, 13:05
View user's profile Send private message Reply with quote
Feryno



Joined: 23 Mar 2005
Posts: 514
Location: Czech republic, Slovak republic
Feryno 02 Jan 2014, 10:11
Here you have some crap which I did in a hurry for you.
Ran fine at me and turned on all PS2 keyboard LEDs as a sign that it successfully reached the necessary point.
It switches from long mode to real mode, checks whether it is 16 bit real mode, turns on all LEDs on PS2 keyboard (won't work with USB keyboard !!!) and halts (requires reboot by button on PC case)
From that point instead of hard reboot by PC button - it is possible to restore long mode and UEFI but that requires about 5 times much more work that this simple thing I did for you.
It is really crap and only for demonstration.
If it runs at virt. address above 4 GB it very probably crashes but most of UEFI runs in long mode below these 4 GB.

copy it to FAT formatted USB flash drive, launch uefi shell, go to the flash drive (command like fs0: etc, at me it is fs6: depends how many drives/fat partitions you have)
and then execute:
lm2rm.efi

What the program does:
finds memory in low 1 MB, copies real mode code there, swiches to 16 bit submode of compatiblity mode of long mode, jumps to the low 1 MB and finishes switching to real mode by turning off long mode and setting selectors limit to 64 kB and then turning off protected mode

edit 2014-jan-06
deleted attachment, final version available


Last edited by Feryno on 06 Jan 2014, 14:35; edited 1 time in total
Post 02 Jan 2014, 10:11
View user's profile Send private message Visit poster's website ICQ Number Reply with quote
Feryno



Joined: 23 Mar 2005
Posts: 514
Location: Czech republic, Slovak republic
Feryno 03 Jan 2014, 13:02
This is quite good exercise. I like it, so I improved it further. I stripped some part from my other project and added it here - to return from real mode to UEFI long mode (UEFI runs at only 1 CPU and doesn't support SMP yet, in the past I needed to activate all CPUs in SMP and run UEFI long mode at all of them - I managed to activate all sleeping application CPUs in parallel at that my project - not one after one as is done usually).
After stripping that part and adding it into this exercise it worked fine.
Later I decided to try to put the real mode chunk to memory not only below 100000h but also above 10000h and a problem appeared immediately, the real mode chunk was never reached. I had to improve the old part which I posted yesterday. Now it works fine again.
Everybody having UEFI x64 please test it. All my 3 MBs are from ASUS (ASUS M5A99X EVO, ASUS P8Z77-V LX2, ASUS P9D WS) and all have UEFI by AMI.
Very probably it is still possible to find few things to improve in the exercise, suggestions are welcome, please post them here !

edit 2014-jan-06
deleted attachment, final version available


Last edited by Feryno on 06 Jan 2014, 14:35; edited 1 time in total
Post 03 Jan 2014, 13:02
View user's profile Send private message Visit poster's website ICQ Number Reply with quote
sid123



Joined: 30 Jul 2013
Posts: 339
Location: Asia, Singapore
sid123 05 Jan 2014, 03:31
Feryno wrote:
This is quite good exercise. I like it, so I improved it further. I stripped some part from my other project and added it here - to return from real mode to UEFI long mode (UEFI runs at only 1 CPU and doesn't support SMP yet, in the past I needed to activate all CPUs in SMP and run UEFI long mode at all of them - I managed to activate all sleeping application CPUs in parallel at that my project - not one after one as is done usually).
After stripping that part and adding it into this exercise it worked fine.
Later I decided to try to put the real mode chunk to memory not only below 100000h but also above 10000h and a problem appeared immediately, the real mode chunk was never reached. I had to improve the old part which I posted yesterday. Now it works fine again.
Everybody having UEFI x64 please test it. All my 3 MBs are from ASUS (ASUS M5A99X EVO, ASUS P8Z77-V LX2, ASUS P9D WS) and all have UEFI by AMI.
Very probably it is still possible to find few things to improve in the exercise, suggestions are welcome, please post theme !

It works Very Happy. However the LEDs dont flash maybe because I have a USB Keyboard.
I think this is good enough to be on the FASM Examples page. I see there is none for UEFI Programming.

_________________
"Those who can make you believe in absurdities can make you commit atrocities" -- Voltaire https://github.com/Benderx2/R3X
XD
Post 05 Jan 2014, 03:31
View user's profile Send private message Reply with quote
HaHaAnonymous



Joined: 02 Dec 2012
Posts: 1178
Location: Unknown
HaHaAnonymous 05 Jan 2014, 03:40
[ Post removed by author. ]


Last edited by HaHaAnonymous on 28 Feb 2015, 18:36; edited 1 time in total
Post 05 Jan 2014, 03:40
View user's profile Send private message Reply with quote
Feryno



Joined: 23 Mar 2005
Posts: 514
Location: Czech republic, Slovak republic
Feryno 06 Jan 2014, 14:33
I found 2 serious bugs in the code (silent) and fixed them. Also cleaned up everything I found. I did few revisions + looked into it by 2 various disassemblers. I can't improve it anymore.
If anybody has questions about UEFI I'll help with them - post them here on the forum.
Yes USB keyboard doesn't have ports 60h, 64h so the example can't turn on USB keyboard LEDs. It can do that only with PS2 keyboard. But that doesn't hurt, if the application returns into UEFI and prints that such situation happened then it is enough. If keyboard LEDs turned on and then a halt occured (without returning into UEFI and printing such message) that would indicate bug in my code (so it helped me to find where is the problem).
Here the last version and I'm also deleting old developmental versions.

edit 2014-jan-10
deleted attachment, newer version available


Last edited by Feryno on 10 Jan 2014, 06:48; edited 1 time in total
Post 06 Jan 2014, 14:33
View user's profile Send private message Visit poster's website ICQ Number Reply with quote
Feryno



Joined: 23 Mar 2005
Posts: 514
Location: Czech republic, Slovak republic
Feryno 08 Jan 2014, 10:39
Things that were done manually may be done using CSM - a document is available here
http://www.intel.com/content/dam/doc/reference-guide/efi-compatibility-support-module-specification-v097.pdf
Post 08 Jan 2014, 10:39
View user's profile Send private message Visit poster's website ICQ Number Reply with quote
Feryno



Joined: 23 Mar 2005
Posts: 514
Location: Czech republic, Slovak republic
Feryno 10 Jan 2014, 06:47
I still found errors and corrected them (most of - or maybe all UEFIs have TR=0 so the error doesn't happen).


Description: UEFI long mode -> Real Mode -> UEFI long mode (single CPU)
Download
Filename: UEFI.RealMode.zip
Filesize: 16.55 KB
Downloaded: 536 Time(s)

Post 10 Jan 2014, 06:47
View user's profile Send private message Visit poster's website ICQ Number Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  


< Last Thread | Next Thread >
Forum Rules:
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 vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Copyright © 1999-2024, Tomasz Grysztar. Also on GitHub, YouTube.

Website powered by rwasa.