/* 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,
+ * IN USHORT StackSegment,
+ * IN USHORT StackPointer,
+ * IN USHORT CodeSegment,
+ * IN USHORT CodePointer);
+ *
+ * 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 ds:[_FrldrBootDrive]
-
- /* Set the boot partition */
-set_part:
- mov eax, [esp + 8]
- test eax, eax
- jnz continue
- mov eax, dword ptr ds:[_FrldrBootPartition]
-continue:
- /* Store the 1-byte truncated partition number in DH */
- mov dh, al
+PUBLIC _Relocator16Boot
+_Relocator16Boot:
+
+ /* Copy input registers */
+ mov esi, dword ptr [esp + 4]
+ mov edi, BSS_RegisterSet
+ mov ecx, 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 [esp + 8]
+ shl eax, 16
+ mov ax, word ptr [esp + 12]
+ mov dword ptr ds:[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
-
- /* Set the function ID */
- mov bx, FNID_ChainLoadBiosBootSectorCode
+ // 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 [esp + 16]
+ shl eax, 16
+ mov ax, word ptr [esp + 20]
+ mov dword ptr ds:[BSS_RealModeEntry], eax
- /* 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
WRITE_PORT_UCHAR((PUCHAR)0x42, scale >> 8);
WRITE_PORT_UCHAR((PUCHAR)0x61, READ_PORT_UCHAR((PUCHAR)0x61) | 3);
}
+
+VOID __cdecl ChainLoadBiosBootSectorCode(
+ IN UCHAR BootDrive OPTIONAL,
+ IN ULONG BootPartition OPTIONAL)
+{
+ REGS Regs;
+
+ RtlZeroMemory(&Regs, sizeof(Regs));
+
+ /* Set the boot drive and the boot partition */
+ Regs.b.dl = (UCHAR)(BootDrive ? BootDrive : FrldrBootDrive);
+ Regs.b.dh = (UCHAR)(BootPartition ? BootPartition : FrldrBootPartition);
+
+ /*
+ * 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.
+ */
+ // DiskStopFloppyMotor();
+
+ Relocator16Boot(&Regs,
+ /* Stack segment:pointer */
+ 0x0000, 0x7C00,
+ /* Code segment:pointer */
+ 0x0000, 0x7C00);
+}
#include <asm.inc>
#include <arch/pc/x86common.h>
+#include <arch/pc/pcbios.h>
EXTERN _DiskStopFloppyMotor:PROC
-EXTERN i386CallRealMode:PROC
+EXTERN _Relocator16Boot:PROC
+EXTERN _FrldrBootDrive:BYTE
+EXTERN _FrldrBootPartition:DWORD
.code32
+Regs:
+ .space REGS_SIZE
+
/*
- * VOID BootOldLinuxKernel(ULONG KernelSize);
+ * VOID __cdecl BootLinuxKernel(
+ * IN ULONG KernelSize,
+ * IN PVOID KernelCurrentLoadAddress,
+ * IN PVOID KernelTargetLoadAddress,
+ * IN UCHAR DriveNumber,
+ * IN ULONG PartitionNumber);
*/
-PUBLIC _BootOldLinuxKernel
-_BootOldLinuxKernel:
-
- /* First we have to copy the kernel down from 0x100000 to 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 and move the kernel down */
- mov ecx, [esp + 4]
- mov esi, HEX(100000)
- mov edi, HEX(10000)
- rep movsb
+PUBLIC _BootLinuxKernel
+_BootLinuxKernel:
- /* Fall through */
-
-PUBLIC _BootNewLinuxKernel
-_BootNewLinuxKernel:
/* Stop the floppy drive motor */
call _DiskStopFloppyMotor
- mov bx, FNID_BootLinuxKernel
- call i386CallRealMode
+ /* 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 [esp + 16]
+ test dl, dl
+ jnz set_part
+ mov dl, byte ptr ds:[_FrldrBootDrive]
+
+ /* Set the boot partition */
+set_part:
+ mov eax, dword ptr [esp + 20]
+ 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 */
+ mov ecx, dword ptr [esp + 4]
+ test ecx, ecx // If size is zero, do not perform relocations
+ jz after_reloc
+
+ /* Load the source and target addresses */
+ mov esi, dword ptr [esp + 8] // HEX(100000) // LINUX_KERNEL_LOAD_ADDRESS
+ mov edi, dword ptr [esp + 12] // HEX(10000)
+
+//
+// FIXME: Support relocating *upwards*, overlapping regions, aligned addresses,
+// etc... !! See memmove code.
+//
+ /* Check how we should perform relocation */
+ cmp edi, esi
+ 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 edx, ecx // Copy the total number of bytes in EDX
+ and edx, HEX(0FFFFFFFC) // Number of bytes we copy using DWORDs
+ xor edx, ecx // Number of remaining bytes to copy after the DWORDs
+ shr ecx, 2 // Count number of DWORDs
+ rep movsd // Move DWORDs
+ mov ecx, edx // 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 esi, ecx
+ add edi, ecx
+ dec esi
+ dec edi
+ rep movsb
+ // jmp after_reloc
+
+after_reloc:
+
+ push HEX(0000) // CodePointer
+ push HEX(9020) // CodeSegment
+ push HEX(9000) // StackPointer
+ push HEX(9000) // StackSegment
+ mov eax, offset Regs
+ push eax
+ call _Relocator16Boot
- /* We should never get here */
+ /* We must never get there */
int 3
END
SetPhysByte(((ULONG)Address)+0x80000000, Value);
}
-void BootOldLinuxKernel( unsigned long size ) {
- ofw_exit();
-}
-
-void BootNewLinuxKernel() {
+VOID __cdecl BootLinuxKernel(
+ IN ULONG KernelSize,
+ IN PVOID KernelCurrentLoadAddress,
+ IN PVOID KernelTargetLoadAddress,
+ IN UCHAR DriveNumber,
+ IN ULONG PartitionNumber)
+{
ofw_exit();
}
ljmp16 HEX(0F000), HEX(0FFF0)
-ChainLoadBiosBootSectorCode:
+Relocator16Boot:
cli
/* Disable A20 address line */
mov ax, HEX(0003)
int HEX(10)
- /* Load segment registers */
- xor ax, ax
+ /* Get current EFLAGS and mask CF, ZF and SF */
+ pushf
+ pop cx
+ and cx, not (EFLAGS_CF or EFLAGS_ZF or EFLAGS_SF)
+
+ /* Get flags CF, ZF and SF from the REGS structure */
+ mov ax, word ptr cs:[BSS_RegisterSet + REGS_EFLAGS]
+ and ax, (EFLAGS_CF or EFLAGS_ZF or EFLAGS_SF)
+
+ /* Combine flags and set them */
+ or ax, cx
+ push ax
+ popf
+
+ /* Setup the segment registers */
+ mov ax, word ptr cs:[BSS_RegisterSet + REGS_DS]
mov ds, ax
+ mov ax, word ptr cs:[BSS_RegisterSet + REGS_ES]
mov es, ax
+ mov ax, word ptr cs:[BSS_RegisterSet + REGS_FS]
mov fs, ax
+ mov ax, word ptr cs:[BSS_RegisterSet + REGS_GS]
mov gs, ax
- mov ss, ax
- mov esp, HEX(7C00)
- /* Jump to the bootsector code */
- ljmp16 HEX(0000), HEX(7C00)
+ /* Patch the jump address (segment:offset) */
+ mov eax, dword ptr cs:[BSS_RealModeEntry]
+ mov dword ptr cs:[Relocator16Address], eax
+
+ /* Switch the stack (segment:offset) */
+ mov eax, dword ptr cs:[BSS_CallbackReturn]
+ shr eax, 16
+ mov ss, ax
+ mov eax, dword ptr cs:[BSS_CallbackReturn]
+ and eax, HEX(0FFFF)
+ mov esp, eax
+
+ /* Setup the registers */
+ mov eax, dword ptr cs:[BSS_RegisterSet + REGS_EAX]
+ mov ebx, dword ptr cs:[BSS_RegisterSet + REGS_EBX]
+ mov ecx, dword ptr cs:[BSS_RegisterSet + REGS_ECX]
+ mov edx, dword ptr cs:[BSS_RegisterSet + REGS_EDX]
+ mov esi, dword ptr cs:[BSS_RegisterSet + REGS_ESI]
+ mov edi, dword ptr cs:[BSS_RegisterSet + REGS_EDI]
+ // Don't setup ebp, we only use it as output! <-- FIXME!
+
+ /* Jump to the new CS:IP (e.g. jump to bootsector code...) */
+ .byte HEX(0EA) // ljmp16 segment:offset
+Relocator16Address:
+ .word HEX(7C00) // Default offset
+ .word HEX(0000) // Default segment
+ nop
.code16
-/* fat helper code */
+/* FAT helper code */
#include "fathelp.inc"
.org 512
CallbackTable:
.word Int386
.word Reboot
- .word ChainLoadBiosBootSectorCode
+ .word Relocator16Boot
.word PxeCallApi
.word PnpBiosGetDeviceNodeCount
.word PnpBiosGetDeviceNode
- .word BootLinuxKernel
/* 16-bit stack pointer */
/* Real-mode IDT pointer */
rmode_idtptr:
- .word HEX(3ff) /* Limit */
+ .word HEX(3FF) /* Limit */
.long 0 /* Base Address */
#include "int386.inc"
#include "helpers.inc"
#include "pxe.inc"
#include "pnp.inc"
-#include "linux.inc"
.org (FREELDR_PE_BASE - FREELDR_BASE - 1)
.byte 0
.endcode16
+
END
#include "../../include/arch/pc/pcbios.h"
-#define EFLAGS_CF HEX(01)
-#define EFLAGS_ZF HEX(40)
-#define EFLAGS_SF HEX(80)
-
Int386:
/* Save all registers + segment registers */
push ds
+++ /dev/null
-
-BootLinuxKernel:
- // dl must be set to the boot drive
-
- /* Load segment registers */
- cli
- mov bx, HEX(9000)
- mov ds, bx
- mov es, bx
- mov fs, bx
- mov gs, bx
- mov ss, bx
- mov sp, HEX(9000)
-
- ljmp16 HEX(9020), HEX(0000)
#ifndef _PCBIOS_H_
#define _PCBIOS_H_
+#ifdef __ASM__
+#define EFLAGS_CF HEX(01)
+#define EFLAGS_ZF HEX(40)
+#define EFLAGS_SF HEX(80)
+#endif
+
#ifndef __ASM__
#define MAX_BIOS_DESCRIPTORS 80
// If CF is set then the call failed (usually)
#define INT386_SUCCESS(regs) ((regs.x.eflags & EFLAGS_CF) == 0)
-VOID __cdecl ChainLoadBiosBootSectorCode( // Implemented in boot.S
+VOID __cdecl ChainLoadBiosBootSectorCode(
IN UCHAR BootDrive OPTIONAL,
IN ULONG BootPartition OPTIONAL);
-VOID __cdecl Reboot(VOID); // Implemented in boot.S
-VOID DetectHardware(VOID); // Implemented in hardware.c
+VOID __cdecl Relocator16Boot(
+ IN REGS* In,
+ IN USHORT StackSegment,
+ IN USHORT StackPointer,
+ IN USHORT CodeSegment,
+ IN USHORT CodePointer);
+
+VOID __cdecl Reboot(VOID);
+VOID DetectHardware(VOID);
#endif /* ! __ASM__ */
/* Realmode function IDs */
#define FNID_Int386 0
#define FNID_Reboot 1
-#define FNID_ChainLoadBiosBootSectorCode 2
+#define FNID_Relocator16Boot 2
#define FNID_PxeCallApi 3
-#define FNID_PnpBiosGetDeviceNodeCount 4
-#define FNID_PnpBiosGetDeviceNode 5
-#define FNID_BootLinuxKernel 6
+#define FNID_PnpBiosGetDeviceNodeCount 4
+#define FNID_PnpBiosGetDeviceNode 5
/* Flag Masks */
#define CR0_PE_SET HEX(00000001) /* OR this value with CR0 to enable pmode */
} LINUX_SETUPSECTOR, *PLINUX_SETUPSECTOR;
#include <poppack.h>
-VOID __cdecl BootNewLinuxKernel(VOID); // Implemented in linux.S
-VOID __cdecl BootOldLinuxKernel(ULONG KernelSize); // Implemented in linux.S
+// Implemented in linux.S
+VOID __cdecl BootLinuxKernel(
+ IN ULONG KernelSize,
+ IN PVOID KernelCurrentLoadAddress,
+ IN PVOID KernelTargetLoadAddress,
+ IN UCHAR DriveNumber,
+ IN ULONG PartitionNumber);
ARC_STATUS
LoadAndBootLinux(
}
}
+ /* If we haven't retrieved the BIOS drive and partition numbers above, do it now */
+ if (PartitionNumber == 0)
+ {
+ /* Retrieve the BIOS drive and partition numbers */
+ if (!DissectArcPath(BootPath, NULL, &DriveNumber, &PartitionNumber))
+ {
+ /* This is not a fatal failure, but just an inconvenience: display a message */
+ TRACE("DissectArcPath(%s) failed to retrieve BIOS drive and partition numbers.\n", BootPath);
+ }
+ }
+
/* Get the kernel name */
LinuxKernelName = GetArgumentValue(Argc, Argv, "Kernel");
if (!LinuxKernelName || !*LinuxKernelName)
UiUnInitialize("Booting Linux...");
IniCleanup();
- if (LinuxSetupSector->LoadFlags & LINUX_FLAG_LOAD_HIGH)
- BootNewLinuxKernel();
- else
- BootOldLinuxKernel(LinuxKernelSize);
-
+ BootLinuxKernel(LinuxKernelSize, LinuxKernelLoadAddress,
+ (LinuxSetupSector->LoadFlags & LINUX_FLAG_LOAD_HIGH)
+ ? (PVOID)LINUX_KERNEL_LOAD_ADDRESS /* == 0x100000 */
+ : (PVOID)0x10000,
+ DriveNumber, PartitionNumber);
+ /* Must not return! */
+ return ESUCCESS;
LinuxBootFailed:
RtlStringCbPrintfA(StatusText, sizeof(StatusText), "Loading %s", LinuxKernelName);
UiDrawStatusText(StatusText);
- /* Allocate memory for Linux kernel */
+ /* Try to allocate memory for the Linux kernel; if it fails, allocate somewhere else */
LinuxKernelLoadAddress = MmAllocateMemoryAtAddress(LinuxKernelSize, (PVOID)LINUX_KERNEL_LOAD_ADDRESS, LoaderSystemCode);
if (LinuxKernelLoadAddress != (PVOID)LINUX_KERNEL_LOAD_ADDRESS)
{
- return FALSE;
+ /* It's OK, let's allocate again somewhere else */
+ LinuxKernelLoadAddress = MmAllocateMemoryWithType(LinuxKernelSize, LoaderSystemCode);
+ if (LinuxKernelLoadAddress == NULL)
+ {
+ TRACE("Failed to allocate 0x%lx bytes for the kernel image.\n", LinuxKernelSize);
+ return FALSE;
+ }
}
LoadAddress = LinuxKernelLoadAddress;