From 9ab3246d4368eb8498ccb01a4359161e4c36ef7a Mon Sep 17 00:00:00 2001 From: Timo Kreuzer Date: Sun, 11 Feb 2018 19:21:01 +0100 Subject: [PATCH] [HAL] Implement amd64 BIOS call support The code uses FAST486 to emulate the BIOS code. --- hal/hal.spec | 14 +- hal/halx86/CMakeLists.txt | 4 +- hal/halx86/amd64/x86bios.c | 278 +++++++++++++++++++++++++++++------ hal/halx86/generic/halinit.c | 4 + hal/halx86/include/halp.h | 8 + 5 files changed, 254 insertions(+), 54 deletions(-) diff --git a/hal/hal.spec b/hal/hal.spec index bfe55fa6b8e..4f8e0bec939 100644 --- a/hal/hal.spec +++ b/hal/hal.spec @@ -6,10 +6,10 @@ @ fastcall -arch=i386 ExTryToAcquireFastMutex(ptr) ntoskrnl.ExiTryToAcquireFastMutex @ stdcall HalAcquireDisplayOwnership(ptr) @ stdcall HalAdjustResourceList(ptr) +@ stdcall HalAllProcessorsStarted() @ stdcall HalAllocateAdapterChannel(ptr ptr long ptr) @ stdcall HalAllocateCommonBuffer(ptr long ptr long) @ stdcall HalAllocateCrashDumpRegisters(ptr ptr) -@ stdcall HalAllProcessorsStarted() @ stdcall HalAssignSlotResources(ptr ptr ptr ptr long long long ptr) @ stdcall -arch=i386,arm HalBeginSystemInterrupt(long long ptr) @ stdcall HalCalibratePerformanceCounter(ptr long long) @@ -29,8 +29,8 @@ @ stdcall HalGetInterruptVector(long long long long ptr ptr) ;@ stdcall -arch=x86_64 HalHandleMcheck() @ stdcall -arch=i386,x86_64 HalHandleNMI(ptr) -@ stdcall HalInitializeProcessor(long ptr) @ stdcall HalInitSystem(long ptr) +@ stdcall HalInitializeProcessor(long ptr) ;@ stdcall -arch=x86_64 HalIsHyperThreadingEnabled() @ stdcall HalMakeBeep(long) @ stdcall HalProcessorIdle() @@ -56,7 +56,6 @@ @ fastcall -arch=arm HalSweepIcache() @ fastcall -arch=arm HalSweepDcache() @ fastcall HalSystemVectorDispatchEntry(long long long) -;@ stdcall -arch=x86_64 HalSystemVectorDispatchEntry() @ stdcall HalTranslateBusAddress(long long long long ptr ptr) @ stdcall -arch=i386,x86_64 IoAssignDriveLetters(ptr str ptr ptr) HalpAssignDriveLetters @ stdcall IoFlushAdapterBuffers(ptr ptr ptr ptr long long) @@ -102,7 +101,8 @@ @ stdcall -arch=i386,arm WRITE_PORT_UCHAR(ptr long) @ stdcall -arch=i386,arm WRITE_PORT_ULONG(ptr long) @ stdcall -arch=i386,arm WRITE_PORT_USHORT(ptr long) -@ stdcall -arch=x86_64 HalInitializeBios(long ptr) -;@ stdcall -arch=x86_64 x86BiosExecuteInterrupt() -;@ stdcall -arch=x86_64 x86BiosInitializeBiosEx() -;@ stdcall -arch=x86_64 x86BiosTranslateAddress() +@ stdcall -arch=x86_64 x86BiosAllocateBuffer() +@ stdcall -arch=x86_64 x86BiosCall() +@ stdcall -arch=x86_64 x86BiosFreeBuffer() +@ stdcall -arch=x86_64 x86BiosReadMemory() +@ stdcall -arch=x86_64 x86BiosWriteMemory() diff --git a/hal/halx86/CMakeLists.txt b/hal/halx86/CMakeLists.txt index 831131494ec..f4148eb5ec0 100644 --- a/hal/halx86/CMakeLists.txt +++ b/hal/halx86/CMakeLists.txt @@ -5,7 +5,8 @@ add_definitions( include_directories( include - ${REACTOS_SOURCE_DIR}/ntoskrnl/include) + ${REACTOS_SOURCE_DIR}/ntoskrnl/include + ${REACTOS_SOURCE_DIR}/sdk/include/reactos/libs/fast486) function(add_hal _halname) cmake_parse_arguments(_haldata "" "" "SOURCES;COMPONENTS" ${ARGN}) @@ -76,5 +77,6 @@ elseif(ARCH STREQUAL "amd64") amd64/processor.c) add_hal(hal SOURCES ${HAL_SOURCE} COMPONENTS generic acpi apic) + target_link_libraries(hal fast486) endif() diff --git a/hal/halx86/amd64/x86bios.c b/hal/halx86/amd64/x86bios.c index 88f52421b7b..a8b7d94bc6c 100644 --- a/hal/halx86/amd64/x86bios.c +++ b/hal/halx86/amd64/x86bios.c @@ -12,7 +12,7 @@ //#define NDEBUG #include -//#include "x86emu.h" +#include /* This page serves as fallback for pages used by Mm */ #define DEFAULT_PAGE 0x21 @@ -43,7 +43,9 @@ DbgDumpPage(PUCHAR MemBuffer, USHORT Segment) VOID NTAPI -HalInitializeBios(ULONG Unknown, PLOADER_PARAMETER_BLOCK LoaderBlock) +HalInitializeBios( + _In_ ULONG Unknown, + _In_ PLOADER_PARAMETER_BLOCK LoaderBlock) { PPFN_NUMBER PfnArray; PFN_NUMBER Pfn, Last; @@ -112,12 +114,12 @@ HalInitializeBios(ULONG Unknown, PLOADER_PARAMETER_BLOCK LoaderBlock) NTSTATUS NTAPI x86BiosAllocateBuffer( - ULONG *Size, - USHORT *Segment, - USHORT *Offset) + _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) + if (!x86BiosIsInitialized || (*Size > PAGE_SIZE)) { /* Something was wrong, fail! */ return STATUS_INSUFFICIENT_RESOURCES; @@ -141,11 +143,11 @@ x86BiosAllocateBuffer( NTSTATUS NTAPI x86BiosFreeBuffer( - USHORT Segment, - USHORT Offset) + _In_ USHORT Segment, + _In_ USHORT Offset) { /* Check if the system is initialized and if the address matches */ - if (!x86BiosIsInitialized || Segment != 0x2000 || Offset != 0) + if (!x86BiosIsInitialized || (Segment != 0x2000) || (Offset != 0)) { /* Something was wrong, fail */ return STATUS_INVALID_PARAMETER; @@ -165,10 +167,10 @@ x86BiosFreeBuffer( NTSTATUS NTAPI x86BiosReadMemory( - USHORT Segment, - USHORT Offset, - PVOID Buffer, - ULONG Size) + _In_ USHORT Segment, + _In_ USHORT Offset, + _Out_writes_bytes_(Size) PVOID Buffer, + _In_ ULONG Size) { ULONG_PTR Address; @@ -176,7 +178,7 @@ x86BiosReadMemory( Address = (Segment << 4) + Offset; /* Check if it's valid */ - if (!x86BiosIsInitialized || Address + Size > 0x100000) + if (!x86BiosIsInitialized || ((Address + Size) > 0x100000)) { /* Invalid */ return STATUS_INVALID_PARAMETER; @@ -192,10 +194,10 @@ x86BiosReadMemory( NTSTATUS NTAPI x86BiosWriteMemory( - USHORT Segment, - USHORT Offset, - PVOID Buffer, - ULONG Size) + _In_ USHORT Segment, + _In_ USHORT Offset, + _In_reads_bytes_(Size) PVOID Buffer, + _In_ ULONG Size) { ULONG_PTR Address; @@ -203,7 +205,7 @@ x86BiosWriteMemory( Address = (Segment << 4) + Offset; /* Check if it's valid */ - if (!x86BiosIsInitialized || Address + Size > 0x100000) + if (!x86BiosIsInitialized || ((Address + Size) > 0x100000)) { /* Invalid */ return STATUS_INVALID_PARAMETER; @@ -216,53 +218,237 @@ x86BiosWriteMemory( return STATUS_SUCCESS; } -#if 0 +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)", 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)", 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; + + // CHECKME! + case 0x1CE: return (Size == 1) && IsWrite; + case 0x1CF: return (Size == 1); + case 0x3B6: return (Size <= 2); + } + + return FALSE; +} + +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( - ULONG InterruptNumber, - X86_BIOS_REGISTERS *Registers) + _In_ ULONG InterruptNumber, + _Inout_ PX86_BIOS_REGISTERS Registers) { - X86_VM_STATE VmState; + FAST486_STATE EmulatorContext; struct { USHORT Ip; USHORT SegCs; - } *InterrupTable; + } *Ivt; + ULONG FlatIp; + PUCHAR InstructionPointer; - /* Zero the VmState */ - RtlZeroMemory(&VmState, sizeof(VmState)); + /* Initialize the emulator context */ + Fast486Initialize(&EmulatorContext, + x86MemRead, + x86MemWrite, + x86IoRead, + x86IoWrite, + x86BOP, + x86IntAck, + NULL, // FpuCallback, + NULL); // Tlb + +//RegisterBop(BOP_UNSIMULATE, CpuUnsimulateBop); /* Copy the registers */ - VmState.BiosRegisters = *Registers; - - /* Set the physical memory buffer */ - VmState.MemBuffer = x86BiosMemoryMapping; + 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 */ - VmState.Registers.Eflags.Long = 0; // FIXME + EmulatorContext.Flags.Long = 0; + EmulatorContext.Flags.AlwaysSet = 1; + EmulatorContext.Flags.If = 1; - /* Setup stack */ - VmState.Registers.SegSs = 0; // FIXME - VmState.Registers.Sp = 0x2000 - 2; // FIXME + /* Set the stack pointer */ + Fast486SetStack(&EmulatorContext, 0, 0x2000 - 2); // FIXME - /* Initialize IP from the interrupt vector table */ - InterrupTable = (PVOID)x86BiosMemoryMapping; - VmState.Registers.SegCs = InterrupTable[InterruptNumber].SegCs; - VmState.Registers.Eip = InterrupTable[InterruptNumber].Ip; - - /* Make the function return on IRET */ - VmState.Flags.ReturnOnIret = 1; + /* Set CS:EIP from the IVT entry */ + Ivt = (PVOID)x86BiosMemoryMapping; + Fast486ExecuteAt(&EmulatorContext, + Ivt[InterruptNumber].SegCs, + Ivt[InterruptNumber].Ip); - /* Call the x86 emulator */ - x86Emulator(&VmState); + while (TRUE) + { + /* Step one instruction */ + Fast486StepInto(&EmulatorContext); - /* Copy registers back to caller */ - *Registers = VmState.BiosRegisters; + /* Check for iret */ + FlatIp = (EmulatorContext.SegmentRegs[FAST486_REG_CS].Selector << 4) + + EmulatorContext.InstPtr.Long; + if (FlatIp >= 0x100000) + { + DPRINT1("x86BiosCall: invalid IP (0x%lx) during BIOS execution", FlatIp); + return FALSE; + } + + /* Read the next instruction and check if it's IRET */ + InstructionPointer = x86BiosMemoryMapping + FlatIp; + if (*InstructionPointer == 0xCF) + { + /* We are done! */ + break; + } + } + + /* 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; } -#endif BOOLEAN NTAPI diff --git a/hal/halx86/generic/halinit.c b/hal/halx86/generic/halinit.c index 002605deb9f..90183d1fb4f 100644 --- a/hal/halx86/generic/halinit.c +++ b/hal/halx86/generic/halinit.c @@ -156,6 +156,10 @@ HalInitSystem(IN ULONG BootPhase, /* Do some HAL-specific initialization */ HalpInitPhase1(); + +#ifdef _M_AMD64 + HalInitializeBios(0, LoaderBlock); +#endif } /* All done, return */ diff --git a/hal/halx86/include/halp.h b/hal/halx86/include/halp.h index 190bfe84636..9c03a96f43d 100644 --- a/hal/halx86/include/halp.h +++ b/hal/halx86/include/halp.h @@ -871,6 +871,14 @@ HalpInitProcessor( ); #ifdef _M_AMD64 + +VOID +NTAPI +HalInitializeBios( + _In_ ULONG Unknown, + _In_ PLOADER_PARAMETER_BLOCK LoaderBlock +); + #define KfLowerIrql KeLowerIrql #define KiEnterInterruptTrap(TrapFrame) /* We do all neccessary in asm code */ #define KiEoiHelper(TrapFrame) return /* Just return to the caller */