mirror of
https://github.com/HyperDbg/HyperDbg.git
synced 2026-07-18 05:24:47 +00:00
468 lines
11 KiB
C
468 lines
11 KiB
C
/**
|
|
* @file Apic.c
|
|
* @author Sina Karvandi (sina@hyperdbg.org)
|
|
* @brief Routines for Advanced Programmable Interrupt Controller (APIC)
|
|
* @details The code is derived from (https://www.cpl0.com/blog/?p=46) and the code
|
|
* for showing the APIC details in the XAPIC is derived from bitdefender/napoca project
|
|
* which is enhanced to support X2APIC mode in HyperDbg
|
|
*
|
|
* @version 0.1
|
|
* @date 2020-12-31
|
|
*
|
|
* @copyright This project is released under the GNU Public License v3.
|
|
*
|
|
*/
|
|
#include "pch.h"
|
|
|
|
/**
|
|
* @brief Perform read I/O APIC
|
|
*
|
|
* @param IoApicBaseVa
|
|
* @param Reg
|
|
*
|
|
* @return UINT64
|
|
*/
|
|
UINT64
|
|
IoApicRead(volatile IO_APIC_ENT * IoApicBaseVa, UINT32 Reg)
|
|
{
|
|
UINT32 High = 0, Low;
|
|
|
|
IoApicBaseVa->Reg = Reg;
|
|
Low = IoApicBaseVa->Data;
|
|
|
|
if (Reg >= IOAPIC_REDTBL(0) && Reg < IOAPIC_REDTBL(IOAPIC_REDTBL_MAX))
|
|
{
|
|
// ASSERT(Reg % 2 == 0);
|
|
Reg++;
|
|
IoApicBaseVa->Reg = Reg;
|
|
High = IoApicBaseVa->Data;
|
|
return IOAPIC_APPEND_QWORD(High, Low);
|
|
}
|
|
else
|
|
{
|
|
return Low;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Perform write to I/O APIC
|
|
*
|
|
* @param IoApicBaseVa
|
|
* @param Reg
|
|
* @param Data
|
|
*
|
|
* @return VOID
|
|
*/
|
|
VOID
|
|
IoApicWrite(volatile IO_APIC_ENT * IoApicBaseVa, UINT32 Reg, UINT64 Data)
|
|
{
|
|
IoApicBaseVa->Reg = Reg;
|
|
IoApicBaseVa->Data = IOAPIC_LOW_DWORD(Data);
|
|
|
|
if (Reg >= IOAPIC_REDTBL(0) && Reg < IOAPIC_REDTBL(IOAPIC_REDTBL_MAX))
|
|
{
|
|
// ASSERT(Reg % 2 == 0);
|
|
Reg++;
|
|
IoApicBaseVa->Reg = Reg;
|
|
IoApicBaseVa->Data = IOAPIC_HIGH_DWORD(Data);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Dump I/O APIC
|
|
* @param IoApicPackets
|
|
*
|
|
* @return VOID
|
|
*/
|
|
VOID
|
|
ApicDumpIoApic(IO_APIC_ENTRY_PACKETS * IoApicPackets)
|
|
{
|
|
UINT32 Index = 0;
|
|
UINT32 Max;
|
|
UINT64 Ll, Lh;
|
|
|
|
UINT64 ApicBasePa = IO_APIC_DEFAULT_BASE_ADDR;
|
|
|
|
Ll = IoApicRead(g_IoApicBase, IO_VERS_REGISTER),
|
|
|
|
Max = (Ll >> 16) & 0xff;
|
|
|
|
// Log("IoApic @ %08x ID:%x (%x) Arb:%x\n",
|
|
// ApicBasePa,
|
|
// IoApicRead(g_IoApicBase, IO_ID_REGISTER) >> 24,
|
|
// Ll & 0xFF,
|
|
// IoApicRead(g_IoApicBase, IO_ARB_ID_REGISTER));
|
|
|
|
//
|
|
// Fill the I/O APIC
|
|
//
|
|
IoApicPackets->ApicBasePa = (UINT32)ApicBasePa;
|
|
IoApicPackets->ApicBaseVa = (UINT64)g_IoApicBase;
|
|
IoApicPackets->IoIdReg = (UINT32)IoApicRead(g_IoApicBase, IO_ID_REGISTER);
|
|
IoApicPackets->IoLl = (UINT32)Ll;
|
|
IoApicPackets->IoArbIdReg = (UINT32)IoApicRead(g_IoApicBase, IO_ARB_ID_REGISTER);
|
|
|
|
//
|
|
// Dump inti table
|
|
//
|
|
Max *= 2;
|
|
|
|
for (Index = 0; Index <= Max; Index += 2)
|
|
{
|
|
if (Index >= MAX_NUMBER_OF_IO_APIC_ENTRIES)
|
|
{
|
|
//
|
|
// To prevent overflow of the target buffer
|
|
//
|
|
return;
|
|
}
|
|
|
|
Ll = IoApicRead(g_IoApicBase, IO_REDIR_BASE + Index + 0);
|
|
Lh = IoApicRead(g_IoApicBase, IO_REDIR_BASE + Index + 1);
|
|
|
|
IoApicPackets->LlLhData[Index] = Ll;
|
|
IoApicPackets->LlLhData[Index + 1] = Lh;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Trigger NMI on XAPIC
|
|
* @param Low
|
|
* @param High
|
|
*
|
|
* @return VOID
|
|
*/
|
|
VOID
|
|
XApicIcrWrite(UINT32 Low, UINT32 High)
|
|
{
|
|
*(UINT32 *)((uintptr_t)g_ApicBase + ICROffset + 0x10) = High;
|
|
*(UINT32 *)((uintptr_t)g_ApicBase + ICROffset) = Low;
|
|
}
|
|
|
|
/**
|
|
* @brief Trigger NMI on X2APIC
|
|
* @param Low
|
|
* @param High
|
|
*
|
|
* @return VOID
|
|
*/
|
|
VOID
|
|
X2ApicIcrWrite(UINT32 Low, UINT32 High)
|
|
{
|
|
CpuWriteMsr(X2_MSR_BASE + TO_X2(ICROffset), ((UINT64)High << 32) | Low);
|
|
}
|
|
|
|
/**
|
|
* @brief Read x2APIC mode
|
|
* @param Offset
|
|
*
|
|
* @return UINT64
|
|
*/
|
|
UINT64
|
|
X2ApicRead(UINT32 Offset)
|
|
{
|
|
return CpuReadMsr(X2_MSR_BASE + TO_X2(Offset));
|
|
}
|
|
|
|
/**
|
|
* @brief Store the local APIC in XAPIC mode
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
ApicStoretLocalApicInXApicMode(PLAPIC_PAGE LApicBuffer)
|
|
{
|
|
volatile LAPIC_PAGE * LocalApicVa = g_ApicBase;
|
|
|
|
//
|
|
// Check if the base virtual address of local APIC is valid or not
|
|
//
|
|
if (!g_ApicBase)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Store fields
|
|
//
|
|
LApicBuffer->Id = LocalApicVa->Id;
|
|
LApicBuffer->Version = LocalApicVa->Version;
|
|
LApicBuffer->SpuriousInterruptVector = LocalApicVa->SpuriousInterruptVector;
|
|
LApicBuffer->TPR = LocalApicVa->TPR;
|
|
LApicBuffer->ProcessorPriority = LocalApicVa->ProcessorPriority;
|
|
LApicBuffer->LogicalDestination = LocalApicVa->LogicalDestination;
|
|
LApicBuffer->ErrorStatus = LocalApicVa->ErrorStatus;
|
|
LApicBuffer->LvtLINT0 = LocalApicVa->LvtLINT0;
|
|
LApicBuffer->LvtLINT1 = LocalApicVa->LvtLINT1;
|
|
LApicBuffer->LvtCmci = LocalApicVa->LvtCmci;
|
|
LApicBuffer->LvtPerfMonCounters = LocalApicVa->LvtPerfMonCounters;
|
|
LApicBuffer->LvtTimer = LocalApicVa->LvtTimer;
|
|
LApicBuffer->LvtThermalSensor = LocalApicVa->LvtThermalSensor;
|
|
LApicBuffer->LvtError = LocalApicVa->LvtError;
|
|
LApicBuffer->InitialCount = LocalApicVa->InitialCount;
|
|
LApicBuffer->CurrentCount = LocalApicVa->CurrentCount;
|
|
LApicBuffer->DivideConfiguration = LocalApicVa->DivideConfiguration;
|
|
|
|
//
|
|
// Save the ISR, TMR and IRR
|
|
//
|
|
for (UINT32 i = 0; i < 8; i++)
|
|
{
|
|
LApicBuffer->ISR[i * 4] = LocalApicVa->ISR[i * 4];
|
|
}
|
|
|
|
for (UINT32 i = 0; i < 8; i++)
|
|
{
|
|
LApicBuffer->TMR[i * 4] = LocalApicVa->TMR[i * 4];
|
|
}
|
|
|
|
for (UINT32 i = 0; i < 8; i++)
|
|
{
|
|
LApicBuffer->IRR[i * 4] = LocalApicVa->IRR[i * 4];
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Store the local APIC in X2APIC mode
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
ApicStoreLocalApicInX2ApicMode(PLAPIC_PAGE LApicBuffer)
|
|
{
|
|
//
|
|
// Store fields
|
|
//
|
|
LApicBuffer->Id = (UINT32)X2ApicRead(APIC_ID);
|
|
LApicBuffer->Version = (UINT32)X2ApicRead(APIC_VERSION);
|
|
LApicBuffer->SpuriousInterruptVector = (UINT32)X2ApicRead(APIC_SPURIOUS_INTERRUPT_VECTOR);
|
|
LApicBuffer->TPR = (UINT32)X2ApicRead(APIC_TASK_PRIORITY);
|
|
LApicBuffer->ProcessorPriority = (UINT32)X2ApicRead(APIC_PROCESSOR_PRIORITY);
|
|
LApicBuffer->LogicalDestination = (UINT32)X2ApicRead(APIC_LOGICAL_DESTINATION);
|
|
LApicBuffer->ErrorStatus = (UINT32)X2ApicRead(APIC_ERROR_STATUS);
|
|
LApicBuffer->LvtLINT0 = (UINT32)X2ApicRead(APIC_LVT_LINT0);
|
|
LApicBuffer->LvtLINT1 = (UINT32)X2ApicRead(APIC_LVT_LINT1);
|
|
LApicBuffer->LvtCmci = (UINT32)X2ApicRead(APIC_LVT_CORRECTED_MACHINE_CHECK_INTERRUPT);
|
|
LApicBuffer->LvtPerfMonCounters = (UINT32)X2ApicRead(APIC_LVT_PERFORMANCE_MONITORING_COUNTERS);
|
|
LApicBuffer->LvtTimer = (UINT32)X2ApicRead(APIC_LVT_TIMER);
|
|
LApicBuffer->LvtThermalSensor = (UINT32)X2ApicRead(APIC_LVT_THERMAL_SENSOR);
|
|
LApicBuffer->LvtError = (UINT32)X2ApicRead(APIC_LVT_ERROR);
|
|
LApicBuffer->InitialCount = (UINT32)X2ApicRead(APIC_INITIAL_COUNT);
|
|
LApicBuffer->CurrentCount = (UINT32)X2ApicRead(APIC_CURRENT_COUNT);
|
|
LApicBuffer->DivideConfiguration = (UINT32)X2ApicRead(APIC_DIVIDE_CONFIGURATION);
|
|
|
|
//
|
|
// Save the ISR, TMR and IRR
|
|
//
|
|
for (UINT32 i = 0; i < 8; i++)
|
|
{
|
|
LApicBuffer->ISR[i * 4] = (UINT32)X2ApicRead(APIC_IN_SERVICE_BITS_31_0 + (0x10 * i));
|
|
}
|
|
|
|
for (UINT32 i = 0; i < 8; i++)
|
|
{
|
|
LApicBuffer->TMR[i * 4] = (UINT32)X2ApicRead(APIC_TRIGGER_MODE_BITS_31_0 + (0x10 * i));
|
|
}
|
|
|
|
for (UINT32 i = 0; i < 8; i++)
|
|
{
|
|
LApicBuffer->IRR[i * 4] = (UINT32)X2ApicRead(APIC_INTERRUPT_REQUEST_BITS_31_0 + (0x10 * i));
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Trigger NMI on X2APIC or APIC based on Current system
|
|
*
|
|
* @return VOID
|
|
*/
|
|
VOID
|
|
ApicTriggerGenericNmi()
|
|
{
|
|
if (g_CompatibilityCheck.IsX2Apic)
|
|
{
|
|
X2ApicIcrWrite((4 << 8) | (1 << 14) | (3 << 18), 0);
|
|
}
|
|
else
|
|
{
|
|
XApicIcrWrite((4 << 8) | (1 << 14) | (3 << 18), 0);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Trigger NMI on X2APIC or APIC based on Current system
|
|
*
|
|
* @return VOID
|
|
*/
|
|
VOID
|
|
ApicTriggerGenericSmi()
|
|
{
|
|
LogInfo("Generating SMIs");
|
|
if (g_CompatibilityCheck.IsX2Apic)
|
|
{
|
|
// X2ApicIcrWrite((4 << 8) | (1 << 14) | (3 << 18), 0);
|
|
X2ApicIcrWrite((2 << 8) | (1 << 14) | (3 << 18), 0);
|
|
}
|
|
else
|
|
{
|
|
// XApicIcrWrite((4 << 8) | (1 << 14) | (3 << 18), 0);
|
|
XApicIcrWrite((2 << 8) | (1 << 14) | (3 << 18), 0);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Store the details of APIC in xAPIC and x2APIC mode
|
|
* @param LApicBuffer
|
|
* @param IsUsingX2APIC
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
ApicStoreLocalApicFields(PLAPIC_PAGE LApicBuffer, PBOOLEAN IsUsingX2APIC)
|
|
{
|
|
if (g_CompatibilityCheck.IsX2Apic)
|
|
{
|
|
*IsUsingX2APIC = TRUE;
|
|
return ApicStoreLocalApicInX2ApicMode(LApicBuffer);
|
|
}
|
|
else
|
|
{
|
|
*IsUsingX2APIC = FALSE;
|
|
return ApicStoretLocalApicInXApicMode(LApicBuffer);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Store the details of I/O APIC
|
|
* @param IoApicPackets
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
ApicStoreIoApicFields(IO_APIC_ENTRY_PACKETS * IoApicPackets)
|
|
{
|
|
//
|
|
// Dump I/O APIC Entries
|
|
// Note that I/O APIC is not accessed through MSRs (e.g., X2APIC)
|
|
// So, it's all about the physical memory
|
|
//
|
|
ApicDumpIoApic(IoApicPackets);
|
|
|
|
//
|
|
// There is not error defined for it at the moment
|
|
//
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Initialize APIC
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
ApicInitialize()
|
|
{
|
|
UINT64 ApicBaseMSR;
|
|
PHYSICAL_ADDRESS PaApicBase;
|
|
PHYSICAL_ADDRESS PaIoApicBase;
|
|
|
|
ApicBaseMSR = CpuReadMsr(IA32_APIC_BASE);
|
|
|
|
if (!(ApicBaseMSR & (1 << 11)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Map I/O APIC default base address
|
|
//
|
|
// The exact APIC base address should be read from MADT table (ACPI)
|
|
// However, we don't have an ACPI parser right now, but the address
|
|
// is proved to stay at this (default) physical address since Intel
|
|
// recommends OS/BIOS to not relocate it, but it could be relocated
|
|
// however, this address is valid for almost all of the systems
|
|
//
|
|
PaIoApicBase.QuadPart = IO_APIC_DEFAULT_BASE_ADDR & 0xFFFFFF000;
|
|
g_IoApicBase = MmMapIoSpace(PaIoApicBase, 0x1000, MmNonCached);
|
|
|
|
if (!g_IoApicBase)
|
|
{
|
|
//
|
|
// Not gonna fail the initialization since the IOAPIC might be relocated by
|
|
// either OS/BIOS
|
|
//
|
|
|
|
// return FALSE;
|
|
}
|
|
|
|
if (ApicBaseMSR & (1 << 10))
|
|
{
|
|
g_CompatibilityCheck.IsX2Apic = TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
PaApicBase.QuadPart = ApicBaseMSR & 0xFFFFFF000;
|
|
g_ApicBase = MmMapIoSpace(PaApicBase, 0x1000, MmNonCached);
|
|
|
|
if (!g_ApicBase)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
g_CompatibilityCheck.IsX2Apic = FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Uninitialize APIC
|
|
*
|
|
* @return VOID
|
|
*/
|
|
VOID
|
|
ApicUninitialize()
|
|
{
|
|
//
|
|
// Unmap Local APIC base
|
|
//
|
|
if (g_ApicBase)
|
|
{
|
|
MmUnmapIoSpace(g_ApicBase, 0x1000);
|
|
}
|
|
|
|
//
|
|
// Unmap I/O APIC base
|
|
//
|
|
if (g_IoApicBase)
|
|
{
|
|
MmUnmapIoSpace(g_IoApicBase, 0x1000);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Self IPI the current core
|
|
*
|
|
* @param Vector
|
|
* @return VOID
|
|
*/
|
|
VOID
|
|
ApicSelfIpi(UINT32 Vector)
|
|
{
|
|
//
|
|
// Check and apply self-IPI to x2APIC and xAPIC
|
|
//
|
|
if (g_CompatibilityCheck.IsX2Apic)
|
|
{
|
|
X2ApicIcrWrite(APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED | Vector, 0);
|
|
}
|
|
else
|
|
{
|
|
XApicIcrWrite(APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED | Vector, 0);
|
|
}
|
|
}
|