mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-12-10 14:23:31 +01:00
323 lines
8.1 KiB
NASM
323 lines
8.1 KiB
NASM
|
;------------------------------------------------------------------------------
|
||
|
;
|
||
|
; This is used just to produce 32 bit opcode and compare it with 64 bit.
|
||
|
;
|
||
|
; in Win SDK prompt:
|
||
|
; SetEnv /x86
|
||
|
; ml /c /Fl TestAsm32.asm
|
||
|
;------------------------------------------------------------------------------
|
||
|
|
||
|
XDTR STRUCT
|
||
|
Limit DW ?
|
||
|
Base DQ ?
|
||
|
XDTR ENDS
|
||
|
|
||
|
EXTERN gKernelEntry: dword
|
||
|
|
||
|
.model flat
|
||
|
.data
|
||
|
|
||
|
; variables accessed from both 32 and 64 bit code
|
||
|
; need to have this exactly in this order
|
||
|
DataBase LABEL PTR
|
||
|
|
||
|
|
||
|
; 64 bit state
|
||
|
SavedGDTROff EQU $-DataBase
|
||
|
SavedGDTR XDTR <?>
|
||
|
|
||
|
SavedIDTROff EQU $-DataBase
|
||
|
SavedIDTR XDTR <?>
|
||
|
|
||
|
SavedCR3Off EQU $-DataBase
|
||
|
SavedCR3 DQ ?
|
||
|
|
||
|
SavedCSOff EQU $-DataBase
|
||
|
SavedCS DW ?
|
||
|
|
||
|
SavedDSOff EQU $-DataBase
|
||
|
SavedDS DW ?
|
||
|
|
||
|
; 32 bit state
|
||
|
SavedGDTR32Off EQU $-DataBase
|
||
|
SavedGDTR32 XDTR <?>
|
||
|
|
||
|
SavedIDTR32Off EQU $-DataBase
|
||
|
SavedIDTR32 XDTR <?>
|
||
|
|
||
|
SavedCS32Off EQU $-DataBase
|
||
|
SavedCS32 DW ?
|
||
|
|
||
|
SavedDS32Off EQU $-DataBase
|
||
|
SavedDS32 DW ?
|
||
|
|
||
|
SavedESP32Off EQU $-DataBase
|
||
|
SavedESP32 DD ?
|
||
|
|
||
|
; kernel entry - 32 bit
|
||
|
AsmKernelEntryOff EQU $-DataBase
|
||
|
AsmKernelEntry DD ?
|
||
|
|
||
|
|
||
|
;
|
||
|
; for copying kernel image from reloc block to proper mem place
|
||
|
;
|
||
|
|
||
|
; kernel image start in reloc block (source) - 32 bit
|
||
|
AsmKernelImageStartRelocOff EQU $-DataBase
|
||
|
AsmKernelImageStartReloc DD ?
|
||
|
|
||
|
; kernel image start (destination) - 32 bit
|
||
|
AsmKernelImageStartOff EQU $-DataBase
|
||
|
AsmKernelImageStart DD ?
|
||
|
|
||
|
; kernel image size - 32 bit
|
||
|
AsmKernelImageSizeOff EQU $-DataBase
|
||
|
AsmKernelImageSize DD ?
|
||
|
|
||
|
; address of relocated MyAsmCopyAndJumpToKernel32 - 32 bit
|
||
|
MyAsmCopyAndJumpToKernel32AddrOff EQU $-DataBase
|
||
|
MyAsmCopyAndJumpToKernel32Addr DD ?
|
||
|
|
||
|
.code
|
||
|
|
||
|
MyAsmEntryPatchCodeSample32 PROC
|
||
|
mov ecx, 011223344h
|
||
|
;push ecx
|
||
|
;ret
|
||
|
jmp ecx
|
||
|
MyAsmEntryPatchCodeSample32 ENDP
|
||
|
|
||
|
|
||
|
;------------------------------------------------------------------------------
|
||
|
; MyAsmJumpFromKernel32
|
||
|
;
|
||
|
;------------------------------------------------------------------------------
|
||
|
MyAsmJumpFromKernel32 PROC
|
||
|
|
||
|
;hlt ; uncomment to stop here for test
|
||
|
; save bootArgs pointer to edi
|
||
|
mov edi, eax
|
||
|
|
||
|
; load ebx with DataBase - we'll access our saved data with it
|
||
|
db 0BBh ; mov ebx, OFFSET DataBase
|
||
|
DataBaseAdr dd 0
|
||
|
|
||
|
; let's find out kernel entry point - we'll need it to jump back.
|
||
|
; we are called with
|
||
|
; mov ecx, 0x11223344
|
||
|
; call ecx
|
||
|
; and that left return addr on stack. those instructions
|
||
|
; are 7 bytes long, and if we take address from stack and
|
||
|
; substitute 7 from it, we will get kernel entry point.
|
||
|
pop ecx ; 32 bit: pop ecx
|
||
|
sub ecx, 7
|
||
|
; and save it
|
||
|
mov DWORD PTR [ebx + AsmKernelEntryOff], ecx
|
||
|
|
||
|
; lets save 32 bit state to be able to recover it later
|
||
|
; rbx is ebx in 32 bit
|
||
|
sgdt FWORD PTR [ebx + SavedGDTR32Off]
|
||
|
sidt FWORD PTR [ebx + SavedIDTR32Off]
|
||
|
mov WORD PTR [ebx + SavedCS32Off], cs
|
||
|
mov WORD PTR [ebx + SavedDS32Off], ds
|
||
|
mov DWORD PTR [ebx + SavedESP32Off], esp
|
||
|
|
||
|
;
|
||
|
; move to 64 bit mode ...
|
||
|
;
|
||
|
|
||
|
; load saved UEFI GDT, IDT
|
||
|
; will become active after code segment is changed in long jump
|
||
|
; rbx is ebx in 32 bit
|
||
|
lgdt FWORD PTR [ebx + SavedGDTROff]
|
||
|
lidt FWORD PTR [ebx + SavedIDTROff]
|
||
|
|
||
|
; enable the 64-bit page-translation-table entries by setting CR4.PAE=1
|
||
|
mov eax, cr4
|
||
|
bts eax, 5
|
||
|
mov cr4, eax
|
||
|
|
||
|
; set the long-mode page tables - reuse saved UEFI tables
|
||
|
mov eax, DWORD PTR [ebx +SavedCR3Off]
|
||
|
mov cr3, eax
|
||
|
|
||
|
; 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.
|
||
|
|
||
|
; jump to the 64-bit code segment
|
||
|
mov ax, WORD PTR [ebx + SavedCSOff]
|
||
|
push eax
|
||
|
call _RETF32
|
||
|
|
||
|
;
|
||
|
; aloha!
|
||
|
; if there is any luck, we are in 64 bit mode now
|
||
|
;
|
||
|
;hlt ; uncomment to stop here for test
|
||
|
|
||
|
; set segmens
|
||
|
;mov ax, WORD PTR [ebx + SavedDSOff]
|
||
|
;mov ds, ax
|
||
|
; set up stack ...
|
||
|
; not sure if needed, but lets set ss to ds
|
||
|
;mov ss, ax
|
||
|
; lets align the stack
|
||
|
;mov rax, rsp
|
||
|
;and rax, 0xfffffffffffffff0
|
||
|
;mov rsp, rax
|
||
|
|
||
|
; call our C code with bootArgs as first arg (in rcx)
|
||
|
;mov rcx, rdi
|
||
|
;push rcx
|
||
|
; KernelEntryPatchJumpBack should be EFIAPI
|
||
|
; and rbx should not be changed by EFIAPI calling convention
|
||
|
;call KernelEntryPatchJumpBack
|
||
|
;hlt ; uncomment to stop here for test
|
||
|
; return value in rax is bootArgs pointer
|
||
|
;mov rdi, rax
|
||
|
|
||
|
;
|
||
|
; time to go back to 32 bit
|
||
|
;
|
||
|
|
||
|
; load saved 32 bit gdtr
|
||
|
;lgdt FWORD PTR [ebx + SavedGDTR32Off]
|
||
|
; push saved cs and rip (with call) to stack and do retf
|
||
|
;mov ax, WORD PTR [ebx + SavedCS32Off]
|
||
|
;push rax
|
||
|
;call _RETF64
|
||
|
|
||
|
;
|
||
|
; ok, 32 bit opcode again from here
|
||
|
;
|
||
|
|
||
|
; disable paging (set CR0.PG=0)
|
||
|
mov eax, cr0 ; Read CR0.
|
||
|
btr eax, 31 ; Set PG=0.
|
||
|
mov cr0, eax ; Write CR0.
|
||
|
|
||
|
; disable long mode (set EFER.LME=0).
|
||
|
mov ecx, 0c0000080h ; EFER MSR number.
|
||
|
rdmsr ; Read EFER.
|
||
|
btr eax, 8 ; Set LME=0.
|
||
|
wrmsr ; Write EFER.
|
||
|
jmp toNext
|
||
|
toNext:
|
||
|
;
|
||
|
; we are in 32 bit protected mode, no paging
|
||
|
;
|
||
|
|
||
|
; now reload saved 32 bit state data
|
||
|
lidt FWORD PTR [ebx + SavedIDTR32Off]
|
||
|
mov ax, WORD PTR [ebx + SavedDS32Off]
|
||
|
mov ss, ax
|
||
|
mov ds, ax
|
||
|
mov es, ax
|
||
|
mov fs, ax
|
||
|
mov gs, ax
|
||
|
mov esp, DWORD PTR [ebx + SavedESP32Off]
|
||
|
|
||
|
;
|
||
|
; prepare vars for copying kernel to proper mem
|
||
|
; and jump to kernel: set registers as needed
|
||
|
; by MyAsmCopyAndJumpToKernel32
|
||
|
;
|
||
|
|
||
|
; boot args back from edi
|
||
|
mov eax, edi
|
||
|
; kernel entry point
|
||
|
mov edx, DWORD PTR [ebx + AsmKernelEntryOff]
|
||
|
|
||
|
; source, destination and size for kernel copy
|
||
|
mov esi, DWORD PTR [ebx + AsmKernelImageStartRelocOff]
|
||
|
mov edi, DWORD PTR [ebx + AsmKernelImageStartOff]
|
||
|
mov ecx, DWORD PTR [ebx + AsmKernelImageSizeOff]
|
||
|
|
||
|
; address of relocated MyAsmCopyAndJumpToKernel32
|
||
|
mov ebx, DWORD PTR [ebx + MyAsmCopyAndJumpToKernel32AddrOff]
|
||
|
; note: ebx not valid as a pointer to DataBase any more
|
||
|
|
||
|
;
|
||
|
; jump to MyAsmCopyAndJumpToKernel32
|
||
|
;
|
||
|
jmp DWORD PTR ebx ; jmp DWORD PTR ebx in 32 bit
|
||
|
|
||
|
|
||
|
_RETF64:
|
||
|
DB 048h
|
||
|
_RETF32:
|
||
|
retf
|
||
|
|
||
|
; the following is not used - it's here just for a reference
|
||
|
; jump to the 64-bit code segment
|
||
|
; as xnu kernel does it
|
||
|
; example:
|
||
|
; 07f8437a4: 68 28 00 00 cb = push 0cb000008h - last word on stack is segment 0x0008, 07f8437a8h contains CB opcode which is retf
|
||
|
; 07f8437a9: e8 fa ff ff ff = call 07f8437a8h - push EIP on stack and continue with 07f8437a8h which is retf
|
||
|
; retf does far return to next instr after 'call $-1'
|
||
|
|
||
|
;push ((0cbh SHL 24) OR CODE64_SEL)
|
||
|
;call $-1
|
||
|
|
||
|
|
||
|
MyAsmJumpFromKernel32 ENDP
|
||
|
|
||
|
|
||
|
;------------------------------------------------------------------------------
|
||
|
; MyAsmCopyAndJumpToKernel32
|
||
|
;
|
||
|
; This is the last part of the code - it will copy kernel image from reloc
|
||
|
; block to proper mem place and jump to kernel.
|
||
|
; It's 32 bit code and runs after switching back to 32 bit.
|
||
|
; This code will be relocated (copied) to higher mem by PrepareJumpFromKernel().
|
||
|
;
|
||
|
; Expects:
|
||
|
; EAX = address of boot args (proper address, not from reloc block)
|
||
|
; EDX = kernel entry point
|
||
|
; ESI = start of kernel image in reloc block (source)
|
||
|
; EDI = proper start of kernel image (destination)
|
||
|
; ECX = kernel image size in bytes
|
||
|
;------------------------------------------------------------------------------
|
||
|
align 08h
|
||
|
MyAsmCopyAndJumpToKernel32 PROC
|
||
|
|
||
|
;
|
||
|
; we will move double words (4 bytes)
|
||
|
; so ajust ECX to number of double words.
|
||
|
; just in case ECX is not multiple of 4 - inc by 1
|
||
|
;
|
||
|
shr ecx, 2
|
||
|
inc ecx
|
||
|
|
||
|
;
|
||
|
; copy kernel image from reloc block to proper mem place.
|
||
|
; all params should be already set:
|
||
|
; ECX = number of double words
|
||
|
; DS:ESI = source
|
||
|
; ES:EDI = destination
|
||
|
;
|
||
|
cld ; direction is up
|
||
|
rep movsd
|
||
|
|
||
|
;
|
||
|
; and finally jump to kernel:
|
||
|
; EAX already contains bootArgs pointer,
|
||
|
; and EDX contains kernel entry point
|
||
|
;
|
||
|
;hlt
|
||
|
jmp DWORD PTR edx ; jmp DWORD PTR edx in 32 bit
|
||
|
MyAsmCopyAndJumpToKernel32 ENDP
|
||
|
MyAsmCopyAndJumpToKernel32End:
|
||
|
|
||
|
|
||
|
END
|