CloverBootloader/MemoryFix/OsxAptioFixDrv/X64/TestAsm32.asm

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