mirror of
https://github.com/reactos/reactos.git
synced 2024-11-01 12:26:32 +00:00
46831c537f
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.
525 lines
14 KiB
C
525 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
|