[FREELDR] Add support for loading Linux in x64 FreeLdr. Part 1/2: ASM code.

Add also ASM implementation for intrinsics that may not be always
present on MSVC (e.g. MSVC 2010).
This commit is contained in:
Hermès Bélusca-Maïto 2019-10-01 03:50:29 +02:00
parent 21c51eed05
commit 25badcfbbd
No known key found for this signature in database
GPG key ID: 3B2539C65E7B93D0
5 changed files with 254 additions and 60 deletions

View file

@ -143,7 +143,8 @@ elseif(ARCH STREQUAL "amd64")
list(APPEND FREELDR_COMMON_ASM_SOURCE
arch/amd64/entry.S
arch/amd64/int386.S
arch/amd64/pnpbios.S)
arch/amd64/pnpbios.S
arch/amd64/linux.S)
list(APPEND FREELDR_NTLDR_SOURCE
ntldr/arch/amd64/winldr.c)

View file

@ -1,6 +1,7 @@
#include <asm.inc>
#include <arch/pc/x86common.h>
#include <arch/pc/pcbios.h>
EXTERN BootMain:PROC
// EXTERN cmdline:DWORD
@ -75,50 +76,71 @@ Reboot:
/* Stop the floppy drive motor */
call DiskStopFloppyMotor
/* Set the function ID */
/* Set the function ID and switch to real mode (we don't return) */
mov bx, FNID_Reboot
/* Switch to real mode (we don't return) */
jmp SwitchToReal
/*
* VOID __cdecl ChainLoadBiosBootSectorCode(
* IN UCHAR BootDrive OPTIONAL,
* IN ULONG BootPartition OPTIONAL);
* VOID __cdecl Relocator16Boot(
* IN REGS* In<rcx>,
* IN USHORT StackSegment<dx>,
* IN USHORT StackPointer<r8w>,
* IN USHORT CodeSegment<r9w>,
* IN USHORT CodePointer<rsp+40>);
*
* RETURNS: Nothing
* RETURNS: Nothing.
*
* NOTE: The implementation of this function is similar to that of Int386(),
* with the proviso that no attempt is done to save the original values of
* the registers since we will not need them anyway, as we do not return back
* to the caller but instead place the machine in a permanent new CPU state.
*/
PUBLIC ChainLoadBiosBootSectorCode
ChainLoadBiosBootSectorCode:
/* Set the boot drive */
mov dl, [esp + 4]
test dl, dl
jnz set_part
mov dl, byte ptr [FrldrBootDrive]
PUBLIC Relocator16Boot
Relocator16Boot:
/* Set the boot partition */
set_part:
mov eax, [esp + 8]
test eax, eax
jnz continue
mov eax, dword ptr [FrldrBootPartition]
continue:
/* Store the 1-byte truncated partition number in DH */
mov dh, al
/* Save home registers */
mov qword ptr [rsp + 8], rcx
mov word ptr [rsp + 16], dx
mov word ptr [rsp + 24], r8w
mov word ptr [rsp + 32], r9w
#if 0
/* Save non-volatile registers */
push rbx
push rsi
push rdi
#endif
/* Copy input registers */
mov rsi, qword ptr [rsp + 8]
mov rdi, BSS_RegisterSet
mov rcx, REGS_SIZE / 4
rep movsd
/* Set the stack segment/offset */
// Since BSS_CallbackReturn contains a ULONG, store in its high word
// the stack segment and in its low word the stack offset.
mov ax, word ptr [rsp + 16]
shl eax, 16
mov ax, word ptr [rsp + 24]
mov dword ptr [BSS_CallbackReturn], eax
/*
* Don't stop the floppy drive motor when we are just booting a bootsector,
* a drive, or a partition. If we were to stop the floppy motor, the BIOS
* wouldn't be informed and if the next read is to a floppy then the BIOS
* will still think the motor is on and this will result in a read error.
* Set the code segment/offset (Copy entry point)
* NOTE: We permanently *ERASE* the contents of ds:[BSS_RealModeEntry]
* but it is not a problem since we are going to place the machine in
* a permanent new CPU state.
*/
// call DiskStopFloppyMotor
// Since BSS_RealModeEntry contains a ULONG, store in its high word
// the code segment and in its low word the code offset.
mov ax, word ptr [rsp + 32]
shl eax, 16
mov ax, word ptr [rsp + 40]
mov dword ptr [BSS_RealModeEntry], eax
/* Set the function ID */
mov bx, FNID_ChainLoadBiosBootSectorCode
/* Switch to real mode (we don't return) */
/* Set the function ID and switch to real mode (we don't return) */
mov bx, FNID_Relocator16Boot
jmp SwitchToReal
@ -166,7 +188,7 @@ jumpvector:
.word CMODE_CS
SwitchToRealCompSegment:
/* Note: In fact the CPU is in 32 bit mode here. But it will interprete
/* Note: In fact the CPU is in 32 bit mode here. But it will interpret
the generated instructions accordingly. rax will become eax */
/* Step 2 - deactivate long mode, by disabling paging */
@ -191,18 +213,30 @@ CallRealMode_return:
/////////////////////////////////////////
//void __lgdt(void *Source);
// void __fastfail(unsigned int Code<rcx>);
PUBLIC __fastfail
__fastfail:
// mov ecx, [rsp + 4]
int HEX(29)
// void __lgdt(void *Source<rcx>);
PUBLIC __lgdt
__lgdt:
lgdt fword ptr [rcx]
ret
//void __ltr(unsigned short Source);
// void __ltr(unsigned short Source<rcx>);
PUBLIC __ltr
__ltr:
ltr cx
ret
// void _sgdt(void *Destination<rcx>);
PUBLIC __sgdt
__sgdt:
sgdt fword ptr [rcx]
ret
/* 64-bit stack pointer */
stack64:

View file

@ -20,6 +20,7 @@
#include <asm.inc>
#include <arch/pc/x86common.h>
#include <arch/pc/pcbios.h>
.code64
EXTERN CallRealMode:PROC
@ -40,7 +41,7 @@ Int386:
push rsi
push rdi
/* Alloc stack space for home registers */
/* Alloc stack space for home registers (+ alignment) */
sub rsp, 40
//.ENDPROLOG
@ -68,11 +69,11 @@ int386_2:
/* Copy output registers */
mov rsi, BSS_RegisterSet
mov rdi, [r11 + 24]
mov rdi, qword ptr [r11 + 24]
mov rcx, REGS_SIZE / 4
rep movsd
/* cleanup and return */
/* Cleanup and return */
add rsp, 40
pop rdi
pop rsi

View file

@ -0,0 +1,159 @@
/*
* FreeLoader
* Copyright (C) 1998-2002 Brian Palmer <brianp@sginet.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <asm.inc>
#include <arch/pc/x86common.h>
#include <arch/pc/pcbios.h>
EXTERN DiskStopFloppyMotor:PROC
EXTERN Relocator16Boot:PROC
EXTERN FrldrBootDrive:BYTE
EXTERN FrldrBootPartition:DWORD
.code64
Regs:
.space REGS_SIZE
/*
* VOID __cdecl BootLinuxKernel(
* IN ULONG KernelSize<ecx>,
* IN PVOID KernelCurrentLoadAddress<rdx>,
* IN PVOID KernelTargetLoadAddress<r8>,
* IN UCHAR DriveNumber<r9b>,
* IN ULONG PartitionNumber<rsp+40>);
*/
PUBLIC BootLinuxKernel
BootLinuxKernel:
/* Save home registers */
mov r11, rsp
mov dword ptr [r11 + 8], ecx
mov qword ptr [r11 + 16], rdx
mov qword ptr [r11 + 24], r8
mov byte ptr [r11 + 32], r9b
/* Save non-volatile registers */
push rsi
push rdi
/* Allocate stack space for home registers (+ alignment) */
sub rsp, (8*4 + 8)
//.ENDPROLOG
/* Stop the floppy drive motor */
call DiskStopFloppyMotor
/* Set all segment registers to 0x9000 */
mov ax, HEX(9000)
mov word ptr [Regs + REGS_DS], ax
mov word ptr [Regs + REGS_ES], ax
mov word ptr [Regs + REGS_FS], ax
mov word ptr [Regs + REGS_GS], ax
/* Set the boot drive */
xor edx, edx
mov dl, byte ptr [r11 + 32]
test dl, dl
jnz set_part
mov dl, byte ptr /*ds:*/[FrldrBootDrive]
/* Set the boot partition */
set_part:
mov eax, dword ptr [r11 + 40]
test eax, eax
jnz continue
mov eax, dword ptr /*ds:*/[FrldrBootPartition]
continue:
/* Store the 1-byte truncated partition number in DH */
mov dh, al
mov dword ptr [Regs + REGS_EDX], edx
/*
* Relocate the kernel image to its final destination (can be as low as 0x10000).
* The reason we can overwrite low memory is because this code executes
* between 0000:8000 and 0000:FFFF. That leaves space for 32k of code
* before we start interfering with Linux kernel address space.
*/
/* Get KernelSize in ECX */
xor rcx, rcx // Put the 64..32 higher bits to zero
mov ecx, dword ptr [r11 + 8]
test rcx, rcx // If size is zero, do not perform relocations
jz after_reloc
/* Load the source and target addresses */
mov rsi, qword ptr [r11 + 16] // HEX(100000) // LINUX_KERNEL_LOAD_ADDRESS
mov rdi, qword ptr [r11 + 24] // HEX(10000)
//
// FIXME: Support relocating *upwards*, overlapping regions, aligned addresses,
// etc... !! See memmove code.
//
/* Check how we should perform relocation */
cmp rdi, rsi
je after_reloc // target == source: do not perform relocations
ja reloc_up // target > source: relocate up
// jb reloc_down // target < source: relocate down (default)
reloc_down:
/* Move the kernel down - Start with low addresses and increment them */
cld
#if 0
rep movsb
#else
mov rdx, rcx // Copy the total number of bytes in EDX
and rdx, HEX(0FFFFFFFC) // Number of bytes we copy using DWORDs
xor rdx, rcx // Number of remaining bytes to copy after the DWORDs
shr rcx, 2 // Count number of DWORDs
rep movsd // Move DWORDs
mov rcx, rdx // Count number of remaining bytes
rep movsb // Move bytes
#endif
jmp after_reloc
reloc_up:
/* Move the kernel up - Start with high addresses and decrement them */
std
add rsi, rcx
add rdi, rcx
dec rsi
dec rdi
rep movsb
// jmp after_reloc
after_reloc:
mov word ptr [rsp-8 + 40], HEX(0000) // CodePointer
mov r9w, HEX(9020) // CodeSegment
mov r8w, HEX(9000) // StackPointer
mov dx, HEX(9000) // StackSegment
mov rcx, offset Regs
call Relocator16Boot
/* Cleanup and return */
add rsp, (8*4 + 8)
pop rdi
pop rsi
/* We must never get there */
int 3
END

View file

@ -8,7 +8,7 @@
.code16
/* fat helper code */
/* FAT helper code */
#include "fathelp.inc"
.org 512
@ -107,20 +107,20 @@ Msg_SwitchToLongMode:
gdt:
.word HEX(0000), HEX(0000), HEX(0000), HEX(0000) /* 00: NULL descriptor */
.word HEX(0000), HEX(0000), HEX(0000), HEX(0000) /* 08: */
.word HEX(0000), HEX(0000), HEX(9800), HEX(0020) /* 10: long mode cs */
.word HEX(ffff), HEX(0000), HEX(f300), HEX(00cf) /* 18: long mode ds */
.word HEX(0000), HEX(0000), HEX(9800), HEX(0020) /* 10: long mode CS */
.word HEX(FFFF), HEX(0000), HEX(F300), HEX(00CF) /* 18: long mode DS */
.word HEX(FFFF), HEX(0000), HEX(9E00), HEX(0000) /* 20: 16-bit real mode CS */
.word HEX(FFFF), HEX(0000), HEX(9200), HEX(0000) /* 28: 16-bit real mode DS */
.word HEX(FFFF), HEX(0000), HEX(9B00), HEX(00CF) /* 30: compat mode cs */
.word HEX(FFFF), HEX(0000), HEX(9B00), HEX(00CF) /* 30: compat mode CS */
/* GDT table pointer */
gdtptr:
.word HEX(37) /* Limit */
.long offset gdt /* Base Address */
.word HEX(37) /* Limit */
.long offset gdt /* Base Address */
CheckFor64BitSupport:
/* Check if CPU supports CPUID */
/* Check whether the CPU supports CPUID */
pushad
pushfd
pop eax
@ -130,7 +130,7 @@ CheckFor64BitSupport:
popfd
pushfd
pop eax
cmp eax,ebx
cmp eax, ebx
jnz .CheckForPAE
mov si, offset .Msg_NoCpuidSupport
@ -144,10 +144,10 @@ CheckFor64BitSupport:
.CheckForPAE:
/* CPUID support detected - getting the PAE/PGE */
mov eax,1 // Fn0000_0001 - PAE in EDX[6]
mov eax, 1 // Fn0000_0001 - PAE in EDX[6]
cpuid
and edx, HEX(00a0)
cmp edx, HEX(00a0)
and edx, HEX(00A0)
cmp edx, HEX(00A0)
je .CheckForLongMode
mov si, offset .Msg_NoPAE
@ -164,7 +164,7 @@ CheckFor64BitSupport:
mov eax, HEX(80000001)
cpuid
and edx, HEX(20000000)
test edx,edx
test edx, edx
jnz .Success
mov si, offset .Msg_NoLongMode
@ -195,7 +195,7 @@ BuildPageTables:
/* One entry in the PML4 pointing to PDP */
mov eax, PDP_ADDRESS
or eax, HEX(0f)
or eax, HEX(0F)
stosd
/* clear rest */
@ -205,7 +205,7 @@ BuildPageTables:
/* One entry in the PDP pointing to PD */
mov eax, PD_ADDRESS
or eax, HEX(0f)
or eax, HEX(0F)
stosd
/* clear rest */
@ -218,8 +218,8 @@ BuildPageTables:
mov eax, HEX(008f)
.Bpt2:
mov es: [di], eax
mov dword ptr es: [di + 4], 0
mov es:[di], eax
mov dword ptr es:[di + 4], 0
add eax, 512 * 4096 // add 512 4k pages
add di, 8
@ -242,7 +242,7 @@ BuildPageTables:
RealModeEntryPoint:
/* Disable Protected Mode */
mov eax, cr0
and eax, HEX(0fffffffe) // ~0x00000001
and eax, HEX(0FFFFFFFE) // ~0x00000001
mov cr0, eax
/* Clear prefetch queue & correct CS */
@ -295,7 +295,7 @@ ExitToLongMode:
mov word ptr ds:[stack16], sp
/* Set PAE and PGE: 10100000b */
mov eax, HEX(00a0)
mov eax, HEX(00A0)
mov cr4, eax
/* Point cr3 at the PML4 */
@ -322,7 +322,7 @@ InLongMode:
//DB 66h, 66h, 0C7h, 04h, 25h, 00h, 80h, 0Bh, 00h, 31h, 0Eh
//mov word ptr [HEX(b8000)], HEX(0e00) + '1'
.byte HEX(0ff), HEX(25) // opcode of 64bit indirect jump
.byte HEX(0FF), HEX(25) // opcode of 64bit indirect jump
.long 1 // relative address of LongModeEntryPoint
nop
LongModeEntryPoint:
@ -334,11 +334,10 @@ LongModeEntryPoint:
CallbackTable:
.word Int386
.word Reboot
.word ChainLoadBiosBootSectorCode
.word Relocator16Boot
.word PxeCallApi
.word PnpBiosGetDeviceNodeCount
.word PnpBiosGetDeviceNode
.word 0 // BootLinuxKernel
/* 16-bit stack pointer */
stack16: