[FREELDR] Add support for loading Linux in x64 FreeLdr. Part 1/2: ASM code.
authorHermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
Tue, 1 Oct 2019 01:50:29 +0000 (03:50 +0200)
committerHermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
Mon, 7 Oct 2019 00:57:48 +0000 (02:57 +0200)
Add also ASM implementation for intrinsics that may not be always
present on MSVC (e.g. MSVC 2010).

boot/freeldr/freeldr/CMakeLists.txt
boot/freeldr/freeldr/arch/amd64/entry.S
boot/freeldr/freeldr/arch/amd64/int386.S
boot/freeldr/freeldr/arch/amd64/linux.S [new file with mode: 0644]
boot/freeldr/freeldr/arch/realmode/amd64.S

index 53eaca3..d4086a7 100644 (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)
index 31c6411..24ecc07 100644 (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]
-
-    /* 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
+PUBLIC Relocator16Boot
+Relocator16Boot:
+
+    /* 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
 
-    /*
-     * 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.
-     */
-    // call DiskStopFloppyMotor
+    /* Copy input registers */
+    mov rsi, qword ptr [rsp + 8]
+    mov rdi, BSS_RegisterSet
+    mov rcx, REGS_SIZE / 4
+    rep movsd
 
-    /* Set the function ID */
-    mov bx, FNID_ChainLoadBiosBootSectorCode
+    /* 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
 
-    /* Switch to real mode (we don't return) */
+    /*
+     * 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.
+     */
+    // 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 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:
index dc486ee..f3bd65a 100644 (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
diff --git a/boot/freeldr/freeldr/arch/amd64/linux.S b/boot/freeldr/freeldr/arch/amd64/linux.S
new file mode 100644 (file)
index 0000000..383111a
--- /dev/null
@@ -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
index d6bf02c..0a97afd 100644 (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: