reactos/hal/halx86/generic/x86bios.c
Hermès Bélusca-Maïto 46831c537f
[HAL] HalInitializeBios() should not explicitly call HalpBiosDisplayReset().
While the BIOS support for INT10h emulation has been set up, any
modification of the display should wait until the kernel requests
video initialization with INBV/bootvid.
The latter will call HalResetDisplay() (that calls HalpBiosDisplayReset())
at the correct time.
This avoids an useless video mode change and reset when booting ReactOS.
2024-02-11 18:51:40 +01:00

524 lines
14 KiB
C

/*
* PROJECT: ReactOS HAL
* LICENSE: GPL, See COPYING in the top level directory
* FILE: hal/halx86/amd64/x86bios.c
* PURPOSE:
* PROGRAMMERS: Timo Kreuzer (timo.kreuzer@reactos.org)
*/
/* INCLUDES ******************************************************************/
#include <hal.h>
//#define NDEBUG
#include <debug.h>
#include <fast486.h>
/* GLOBALS *******************************************************************/
/* This page serves as fallback for pages used by Mm */
PFN_NUMBER x86BiosFallbackPfn;
BOOLEAN x86BiosIsInitialized;
LONG x86BiosBufferIsAllocated = 0;
PUCHAR x86BiosMemoryMapping;
/* This the physical address of the bios buffer */
ULONG64 x86BiosBufferPhysical;
VOID
NTAPI
DbgDumpPage(PUCHAR MemBuffer, USHORT Segment)
{
ULONG x, y, Offset;
for (y = 0; y < 0x100; y++)
{
for (x = 0; x < 0x10; x++)
{
Offset = Segment * 16 + y * 16 + x;
DbgPrint("%02x ", MemBuffer[Offset]);
}
DbgPrint("\n");
}
}
VOID
NTAPI
HalInitializeBios(
_In_ ULONG Phase,
_In_ PLOADER_PARAMETER_BLOCK LoaderBlock)
{
PPFN_NUMBER PfnArray;
PFN_NUMBER Pfn, Last;
PMEMORY_ALLOCATION_DESCRIPTOR Descriptor;
PLIST_ENTRY ListEntry;
PMDL Mdl;
ULONG64 PhysicalAddress;
if (Phase == 0)
{
/* Allocate one page for a fallback mapping */
PhysicalAddress = HalpAllocPhysicalMemory(LoaderBlock,
0x100000,
1,
FALSE);
if (PhysicalAddress == 0)
{
ASSERT(FALSE);
}
x86BiosFallbackPfn = PhysicalAddress / PAGE_SIZE;
ASSERT(x86BiosFallbackPfn != 0);
/* Allocate a page for the buffer allocation */
x86BiosBufferPhysical = HalpAllocPhysicalMemory(LoaderBlock,
0x100000,
1,
FALSE);
if (x86BiosBufferPhysical == 0)
{
ASSERT(FALSE);
}
}
else
{
/* Allocate an MDL for 1MB */
Mdl = IoAllocateMdl(NULL, 0x100000, FALSE, FALSE, NULL);
if (!Mdl)
{
ASSERT(FALSE);
}
/* Get pointer to the pfn array */
PfnArray = MmGetMdlPfnArray(Mdl);
/* Fill the array with the fallback page */
for (Pfn = 0; Pfn < 0x100; Pfn++)
{
PfnArray[Pfn] = x86BiosFallbackPfn;
}
/* Loop the memory descriptors */
for (ListEntry = LoaderBlock->MemoryDescriptorListHead.Flink;
ListEntry != &LoaderBlock->MemoryDescriptorListHead;
ListEntry = ListEntry->Flink)
{
/* Get the memory descriptor */
Descriptor = CONTAINING_RECORD(ListEntry,
MEMORY_ALLOCATION_DESCRIPTOR,
ListEntry);
/* Check if the memory is in the low 1 MB range */
if (Descriptor->BasePage < 0x100)
{
/* Check if the memory type is firmware */
if ((Descriptor->MemoryType == LoaderFirmwarePermanent) ||
(Descriptor->MemoryType == LoaderSpecialMemory))
{
/* It's firmware, so map it! */
Last = min(Descriptor->BasePage + Descriptor->PageCount, 0x100);
for (Pfn = Descriptor->BasePage; Pfn < Last; Pfn++)
{
/* Set each physical page in the MDL */
PfnArray[Pfn] = Pfn;
}
}
}
}
/* Map this page proper, too */
Pfn = x86BiosBufferPhysical / PAGE_SIZE;
PfnArray[Pfn] = Pfn;
Mdl->MdlFlags = MDL_PAGES_LOCKED;
/* Map the MDL to system space */
x86BiosMemoryMapping = MmGetSystemAddressForMdlSafe(Mdl, HighPagePriority);
ASSERT(x86BiosMemoryMapping);
DPRINT1("*x86BiosMemoryMapping: %p, %p\n",
*(PVOID*)x86BiosMemoryMapping, *(PVOID*)(x86BiosMemoryMapping + 8));
//DbgDumpPage(x86BiosMemoryMapping, 0xc351);
x86BiosIsInitialized = TRUE;
}
}
NTSTATUS
NTAPI
x86BiosAllocateBuffer(
_In_ ULONG *Size,
_In_ USHORT *Segment,
_In_ USHORT *Offset)
{
/* Check if the system is initialized and the buffer is large enough */
if (!x86BiosIsInitialized || (*Size > PAGE_SIZE))
{
/* Something was wrong, fail! */
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Check if the buffer is already allocated */
if (InterlockedBitTestAndSet(&x86BiosBufferIsAllocated, 0))
{
/* Buffer was already allocated, fail */
return STATUS_INSUFFICIENT_RESOURCES;
}
/* The buffer is sufficient, return hardcoded address and size */
*Size = PAGE_SIZE;
*Segment = x86BiosBufferPhysical / 16;
*Offset = 0;
return STATUS_SUCCESS;
}
NTSTATUS
NTAPI
x86BiosFreeBuffer(
_In_ USHORT Segment,
_In_ USHORT Offset)
{
/* Check if the system is initialized and if the address matches */
if (!x86BiosIsInitialized || (Segment != 0x2000) || (Offset != 0))
{
/* Something was wrong, fail */
return STATUS_INVALID_PARAMETER;
}
/* Check if the buffer was allocated */
if (!InterlockedBitTestAndReset(&x86BiosBufferIsAllocated, 0))
{
/* It was not, fail */
return STATUS_INVALID_PARAMETER;
}
/* Buffer is freed, nothing more to do */
return STATUS_SUCCESS;
}
NTSTATUS
NTAPI
x86BiosReadMemory(
_In_ USHORT Segment,
_In_ USHORT Offset,
_Out_writes_bytes_(Size) PVOID Buffer,
_In_ ULONG Size)
{
ULONG_PTR Address;
/* Calculate the physical address */
Address = (Segment << 4) + Offset;
/* Check if it's valid */
if (!x86BiosIsInitialized || ((Address + Size) > 0x100000))
{
/* Invalid */
return STATUS_INVALID_PARAMETER;
}
/* Copy the memory to the buffer */
RtlCopyMemory(Buffer, x86BiosMemoryMapping + Address, Size);
/* Return success */
return STATUS_SUCCESS;
}
NTSTATUS
NTAPI
x86BiosWriteMemory(
_In_ USHORT Segment,
_In_ USHORT Offset,
_In_reads_bytes_(Size) PVOID Buffer,
_In_ ULONG Size)
{
ULONG_PTR Address;
/* Calculate the physical address */
Address = (Segment << 4) + Offset;
/* Check if it's valid */
if (!x86BiosIsInitialized || ((Address + Size) > 0x100000))
{
/* Invalid */
return STATUS_INVALID_PARAMETER;
}
/* Copy the memory from the buffer */
RtlCopyMemory(x86BiosMemoryMapping + Address, Buffer, Size);
/* Return success */
return STATUS_SUCCESS;
}
static
VOID
FASTCALL
x86MemRead(
PFAST486_STATE State,
ULONG Address,
PVOID Buffer,
ULONG Size)
{
/* Validate the address range */
if (((ULONG64)Address + Size) < 0x100000)
{
RtlCopyMemory(Buffer, x86BiosMemoryMapping + Address, Size);
}
else
{
RtlFillMemory(Buffer, Size, 0xCC);
DPRINT1("x86MemRead: invalid read at 0x%lx (size 0x%lx)\n", Address, Size);
}
}
static
VOID
FASTCALL
x86MemWrite(
PFAST486_STATE State,
ULONG Address,
PVOID Buffer,
ULONG Size)
{
/* Validate the address range */
if (((ULONG64)Address + Size) < 0x100000)
{
RtlCopyMemory(x86BiosMemoryMapping + Address, Buffer, Size);
}
else
{
DPRINT1("x86MemWrite: invalid write at 0x%lx (size 0x%lx)\n", Address, Size);
}
}
static
BOOLEAN
ValidatePort(
USHORT Port,
UCHAR Size,
BOOLEAN IsWrite)
{
switch (Port)
{
// VGA: https://wiki.osdev.org/VGA_Hardware#Port_0x3C0
case 0x3C0: return (Size == 1) && IsWrite;
case 0x3C1: return (Size == 1) && !IsWrite;
case 0x3C2: return (Size == 1) && IsWrite;
case 0x3C4: return IsWrite;
case 0x3C5: return (Size <= 2);
case 0x3C7: return (Size == 1) && IsWrite;
case 0x3CC: return (Size == 1) && !IsWrite;
case 0x3CE: return IsWrite;
case 0x3CF: return (Size <= 2);
case 0x3D4: return IsWrite;
case 0x3D5: return (Size <= 2);
case 0x3C6: return (Size == 1);
case 0x3C8: return (Size == 1) && IsWrite;
case 0x3C9: return (Size == 1);
case 0x3DA: return (Size == 1) && !IsWrite;
// OVMF debug messages used by VBox / QEMU
// https://www.virtualbox.org/svn/vbox/trunk/src/VBox/Devices/EFI/Firmware/OvmfPkg/README
case 0x402: return (Size == 1) && IsWrite;
// BOCHS VBE: https://forum.osdev.org/viewtopic.php?f=1&t=14639
case 0x1CE: return (Size == 1) && IsWrite;
case 0x1CF: return (Size == 1);
// CHECKME!
case 0x3B6: return (Size <= 2);
}
/* Allow but report unknown ports, we trust the BIOS for now */
DPRINT1("Unknown port 0x%x, size %d, write %d\n", Port, Size, IsWrite);
return TRUE;
}
static
VOID
FASTCALL
x86IoRead(
PFAST486_STATE State,
USHORT Port,
PVOID Buffer,
ULONG DataCount,
UCHAR DataSize)
{
/* Validate the port */
if (!ValidatePort(Port, DataSize, FALSE))
{
DPRINT1("Invalid IO port read access (port: 0x%x, count: 0x%x)\n", Port, DataSize);
}
switch (DataSize)
{
case 1: READ_PORT_BUFFER_UCHAR((PUCHAR)(ULONG_PTR)Port, Buffer, DataCount); return;
case 2: READ_PORT_BUFFER_USHORT((PUSHORT)(ULONG_PTR)Port, Buffer, DataCount); return;
case 4: READ_PORT_BUFFER_ULONG((PULONG)(ULONG_PTR)Port, Buffer, DataCount); return;
}
}
static
VOID
FASTCALL
x86IoWrite(
PFAST486_STATE State,
USHORT Port,
PVOID Buffer,
ULONG DataCount,
UCHAR DataSize)
{
/* Validate the port */
if (!ValidatePort(Port, DataSize, TRUE))
{
DPRINT1("Invalid IO port write access (port: 0x%x, count: 0x%x)\n", Port, DataSize);
}
switch (DataSize)
{
case 1: WRITE_PORT_BUFFER_UCHAR((PUCHAR)(ULONG_PTR)Port, Buffer, DataCount); return;
case 2: WRITE_PORT_BUFFER_USHORT((PUSHORT)(ULONG_PTR)Port, Buffer, DataCount); return;
case 4: WRITE_PORT_BUFFER_ULONG((PULONG)(ULONG_PTR)Port, Buffer, DataCount); return;
}
}
static
VOID
FASTCALL
x86BOP(
PFAST486_STATE State,
UCHAR BopCode)
{
ASSERT(FALSE);
}
static
UCHAR
FASTCALL
x86IntAck (
PFAST486_STATE State)
{
ASSERT(FALSE);
return 0;
}
BOOLEAN
NTAPI
x86BiosCall(
_In_ ULONG InterruptNumber,
_Inout_ PX86_BIOS_REGISTERS Registers)
{
const ULONG StackBase = 0x2000;
FAST486_STATE EmulatorContext;
ULONG FlatIp;
PUCHAR InstructionPointer;
/* Initialize the emulator context */
Fast486Initialize(&EmulatorContext,
x86MemRead,
x86MemWrite,
x86IoRead,
x86IoWrite,
x86BOP,
x86IntAck,
NULL, // FpuCallback,
NULL); // Tlb
/* Copy the registers */
EmulatorContext.GeneralRegs[FAST486_REG_EAX].Long = Registers->Eax;
EmulatorContext.GeneralRegs[FAST486_REG_EBX].Long = Registers->Ebx;
EmulatorContext.GeneralRegs[FAST486_REG_ECX].Long = Registers->Ecx;
EmulatorContext.GeneralRegs[FAST486_REG_EDX].Long = Registers->Edx;
EmulatorContext.GeneralRegs[FAST486_REG_ESI].Long = Registers->Esi;
EmulatorContext.GeneralRegs[FAST486_REG_EDI].Long = Registers->Edi;
EmulatorContext.SegmentRegs[FAST486_REG_DS].Selector = Registers->SegDs;
EmulatorContext.SegmentRegs[FAST486_REG_ES].Selector = Registers->SegEs;
/* Set Eflags */
EmulatorContext.Flags.Long = 0;
EmulatorContext.Flags.AlwaysSet = 1;
EmulatorContext.Flags.If = 1;
/* Set up the INT stub */
FlatIp = StackBase - 4;
InstructionPointer = x86BiosMemoryMapping + FlatIp;
InstructionPointer[0] = 0xCD; // INT instruction
InstructionPointer[1] = (UCHAR)InterruptNumber;
InstructionPointer[2] = 0x90; // NOP. We will stop at this address.
/* Set the stack pointer */
Fast486SetStack(&EmulatorContext, 0, StackBase - 8);
/* Start execution at the INT stub */
Fast486ExecuteAt(&EmulatorContext, 0x00, FlatIp);
while (TRUE)
{
/* Get the current flat IP */
FlatIp = (EmulatorContext.SegmentRegs[FAST486_REG_CS].Selector << 4) +
EmulatorContext.InstPtr.Long;
/* Make sure we haven't left the allowed memory range */
if (FlatIp >= 0x100000)
{
DPRINT1("x86BiosCall: invalid IP (0x%lx) during BIOS execution\n", FlatIp);
return FALSE;
}
/* Check if we returned from our int stub */
if (FlatIp == (StackBase - 2))
{
/* We are done! */
break;
}
/* Emulate one instruction */
Fast486StepInto(&EmulatorContext);
}
/* Copy the registers back */
Registers->Eax = EmulatorContext.GeneralRegs[FAST486_REG_EAX].Long;
Registers->Ebx = EmulatorContext.GeneralRegs[FAST486_REG_EBX].Long;
Registers->Ecx = EmulatorContext.GeneralRegs[FAST486_REG_ECX].Long;
Registers->Edx = EmulatorContext.GeneralRegs[FAST486_REG_EDX].Long;
Registers->Esi = EmulatorContext.GeneralRegs[FAST486_REG_ESI].Long;
Registers->Edi = EmulatorContext.GeneralRegs[FAST486_REG_EDI].Long;
Registers->SegDs = EmulatorContext.SegmentRegs[FAST486_REG_DS].Selector;
Registers->SegEs = EmulatorContext.SegmentRegs[FAST486_REG_ES].Selector;
return TRUE;
}
#ifdef _M_AMD64
BOOLEAN
NTAPI
HalpBiosDisplayReset(VOID)
{
#if 0
X86_BIOS_REGISTERS Registers;
ULONG OldEflags;
/* Save flags and disable interrupts */
OldEflags = __readeflags();
_disable();
/* Set AH = 0 (Set video mode), AL = 0x12 (640x480x16 vga) */
Registers.Eax = 0x12;
/* Call INT 0x10 */
x86BiosCall(0x10, &Registers);
// FIXME: check result
/* Restore previous flags */
__writeeflags(OldEflags);
return TRUE;
#else
/* This x64 HAL does NOT currently handle display reset (TODO) */
return FALSE;
#endif
}
#endif // _M_AMD64