flat assembler
Message board for the users of flat assembler.
Index
> OS Construction > Switching back to RM after UEFI/EFI initialization? |
Author |
|
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. _________________ "Those who can make you believe in absurdities can make you commit atrocities" -- Voltaire https://github.com/Benderx2/R3X XD |
|||
28 Dec 2013, 05:49 |
|
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 |
|||
28 Dec 2013, 05:58 |
|
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. 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 |
|||
28 Dec 2013, 08:19 |
|
Feryno 30 Dec 2013, 12:43
sid123 wrote: Has anyone tried to switch back to Real Mode after UEFI/EFI initialization? sid123 wrote: Unless UEFI has introduced some sort of protection mechanism to prevent the processor from switching to RM. 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). |
|||
30 Dec 2013, 12:43 |
|
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.) |
|||
31 Dec 2013, 11:41 |
|
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. |
|||
31 Dec 2013, 12:51 |
|
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. |
|||
31 Dec 2013, 13:05 |
|
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 |
|||
02 Jan 2014, 10:11 |
|
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 |
|||
03 Jan 2014, 13:02 |
|
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). It works . 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 |
|||
05 Jan 2014, 03:31 |
|
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 |
|||
05 Jan 2014, 03:40 |
|
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 |
|||
06 Jan 2014, 14:33 |
|
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 |
|||
08 Jan 2014, 10:39 |
|
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).
|
|||||||||||
10 Jan 2014, 06:47 |
|
< Last Thread | Next Thread > |
Forum Rules:
|
Copyright © 1999-2024, Tomasz Grysztar. Also on GitHub, YouTube.
Website powered by rwasa.