TITLE LongMode.asm: Assembly code for the entering long mode ;------------------------------------------------------------------------------ ;* ;* Copyright (c) 2006, Intel Corporation ;* All rights reserved. This program and the accompanying materials ;* are licensed and made available under the terms and conditions of the BSD License ;* which accompanies this distribution. The full text of the license may be found at ;* http://opensource.org/licenses/bsd-license.php ;* ;* THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, ;* WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. ;* ;* LongMode.asm ;* ;* Abstract: ;* ;* Transition from 32-bit protected mode EFI environment into x64 ;* 64-bit bit long mode. ;* ;------------------------------------------------------------------------------ .686p .model flat ; ; Create the exception handler code in IA32 C code ; .code .stack .MMX .XMM _LoadGo64Gdt PROC Near Public push ebp ; C prolog push edi mov ebp, esp ; ; Disable interrupts ; cli ; ; Reload the selectors ; Note: ; Make the Selectors 64-bit ready ; mov edi, OFFSET gdtr ; Load GDT register mov ax,cs ; Get the selector data from our code image mov es,ax lgdt FWORD PTR es:[edi] ; and update the GDTR db 067h db 0eah ; Far Jump Offset:Selector to reload CS dd OFFSET DataSelectorRld; Offset is ensuing instruction boundary dw LINEAR_CODE_SEL ; Selector is our code selector, 10h DataSelectorRld:: mov ax, SYS_DATA_SEL ; Update the Base for the new selectors, too mov ds, ax mov es, ax mov fs, ax mov gs, ax mov ss, ax pop edi pop ebp ret _LoadGo64Gdt endp ; VOID ; ActivateLongMode ( ; IN EFI_PHYSICAL_ADDRESS PageTables, ; IN EFI_PHYSICAL_ADDRESS HobStart, ; IN EFI_PHYSICAL_ADDRESS Stack, ; IN EFI_PHYSICAL_ADDRESS PpisNeededByDxeIplEntryPoint, ; IN EFI_PHYSICAL_ADDRESS DxeCoreEntryPoint ; ) ; ; Input: [ebp][0h] = Original ebp ; [ebp][4h] = Return address ; [ebp][8h] = PageTables ; [ebp][10h] = HobStart ; [ebp][18h] = Stack ; [ebp][20h] = CodeEntryPoint1 <--- Call this first (for each call, pass HOB pointer) ; [ebp][28h] = CodeEntryPoint2 <--- Call this second ; ; _ActivateLongMode PROC Near Public push ebp ; C prolog mov ebp, esp ; ; Use CPUID to determine if the processor supports long mode. ; mov eax, 80000000h ; Extended-function code 8000000h. cpuid ; Is largest extended function cmp eax, 80000000h ; any function > 80000000h? jbe no_long_mode ; If not, no long mode. mov eax, 80000001h ; Extended-function code 8000001h. cpuid ; Now EDX = extended-features flags. bt edx, 29 ; Test if long mode is supported. jnc no_long_mode ; Exit if not supported. ; ; Enable the 64-bit page-translation-table entries by ; setting CR4.PAE=1 (this is _required_ before activating ; long mode). Paging is not enabled until after long mode ; is enabled. ; mov eax, cr4 bts eax, 5 mov cr4, eax ; ; Get the long-mode page tables, and initialize the ; 64-bit CR3 (page-table base address) to point to the base ; of the PML4 page table. The PML4 page table must be located ; below 4 Gbytes because only 32 bits of CR3 are loaded when ; the processor is not in 64-bit mode. ; mov eax, [ebp+8h] ; Get Page Tables mov cr3, eax ; Initialize CR3 with PML4 base. ; ; Enable long mode (set EFER.LME=1). ; mov ecx, 0c0000080h ; EFER MSR number. rdmsr ; Read EFER. bts eax, 8 ; Set LME=1. wrmsr ; Write EFER. ; ; Enable paging to activate long mode (set CR0.PG=1) ; mov eax, cr0 ; Read CR0. bts eax, 31 ; Set PG=1. mov cr0, eax ; Write CR0. jmp go_to_long_mode go_to_long_mode: ; ; This is the next instruction after enabling paging. Jump to long mode ; db 067h db 0eah ; Far Jump Offset:Selector to reload CS dd OFFSET in_long_mode; Offset is ensuing instruction boundary dw SYS_CODE64_SEL ; Selector is our code selector, 10h in_long_mode:: mov ax, SYS_DATA64_SEL mov es, ax mov ss, ax mov ds, ax ;; jmp $ ; ; We're in long mode, so marshall the arguments to call the ; passed in function pointers ; Recall ; [ebp][10h] = HobStart ; [ebp][18h] = Stack ; [ebp][20h] = PpisNeededByDxeIplEntryPoint <--- Call this first (for each call, pass HOB pointer) ; [ebp][28h] = DxeCoreEntryPoint <--- Call this second ; db 48h mov ebx, [ebp+18h] ; Setup the stack db 48h mov esp, ebx ; On a new stack now ;; 00000905 FF D0 call rax db 48h mov ecx, [ebp+10h] ; Pass Hob Start in RCX db 48h mov eax, [ebp+28h] ; Get the function pointer for ; DxeCoreEntryPoint into EAX ;; 00000905 FF D0 call rax db 0ffh db 0d0h ; ; WE SHOULD NEVER GET HERE!!!!!!!!!!!!! ; no_long_mode: jmp no_long_mode _ActivateLongMode endp align 16 gdtr dw GDT_END - GDT_BASE - 1 ; GDT limit dd OFFSET GDT_BASE ; (GDT base gets set above) ;-----------------------------------------------------------------------------; ; global descriptor table (GDT) ;-----------------------------------------------------------------------------; align 16 public GDT_BASE GDT_BASE: ; null descriptor NULL_SEL equ $-GDT_BASE ; Selector [0] dw 0 ; limit 15:0 dw 0 ; base 15:0 db 0 ; base 23:16 db 0 ; type db 0 ; limit 19:16, flags db 0 ; base 31:24 ; linear data segment descriptor LINEAR_SEL equ $-GDT_BASE ; Selector [0x8] dw 0FFFFh ; limit 0xFFFFF dw 0 ; base 0 db 0 db 092h ; present, ring 0, data, expand-up, writable db 0CFh ; page-granular, 32-bit db 0 ; linear code segment descriptor LINEAR_CODE_SEL equ $-GDT_BASE ; Selector [0x10] dw 0FFFFh ; limit 0xFFFFF dw 0 ; base 0 db 0 db 09Fh ; present, ring 0, data, expand-up, writable db 0CFh ; page-granular, 32-bit db 0 ; system data segment descriptor SYS_DATA_SEL equ $-GDT_BASE ; Selector [0x18] dw 0FFFFh ; limit 0xFFFFF dw 0 ; base 0 db 0 db 093h ; present, ring 0, data, expand-up, writable db 0CFh ; page-granular, 32-bit db 0 ; system code segment descriptor SYS_CODE_SEL equ $-GDT_BASE ; Selector [0x20] dw 0FFFFh ; limit 0xFFFFF dw 0 ; base 0 db 0 db 09Ah ; present, ring 0, data, expand-up, writable db 0CFh ; page-granular, 32-bit db 0 ; spare segment descriptor SPARE3_SEL equ $-GDT_BASE ; Selector [0x28] dw 0 ; limit 0xFFFFF dw 0 ; base 0 db 0 db 0 ; present, ring 0, data, expand-up, writable db 0 ; page-granular, 32-bit db 0 ; ; system data segment descriptor ; SYS_DATA64_SEL equ $-GDT_BASE ; Selector [0x30] dw 0FFFFh ; limit 0xFFFFF dw 0 ; base 0 db 0 db 092h ; P | DPL [1..2] | 1 | 1 | C | R | A db 0CFh ; G | D | L | AVL | Segment [19..16] db 0 ; ; system code segment descriptor ; SYS_CODE64_SEL equ $-GDT_BASE ; Selector [0x38] dw 0FFFFh ; limit 0xFFFFF dw 0 ; base 0 db 0 db 09Ah ; P | DPL [1..2] | 1 | 1 | C | R | A db 0AFh ; G | D | L | AVL | Segment [19..16] db 0 ; spare segment descriptor SPARE4_SEL equ $-GDT_BASE ; Selector [0x40] dw 0 ; limit 0xFFFFF dw 0 ; base 0 db 0 db 0 ; present, ring 0, data, expand-up, writable db 0 ; page-granular, 32-bit db 0 GDT_END: END