from xnu import * from misc import DoReadMsr64, DoWriteMsr64 ###################################### # Globals ###################################### lapic_base_addr = 0xfee00000 ioapic_base_addr = 0xfec00000 ioapic_index_off = 0x0 ioapic_data_off = 0x10 ###################################### # LAPIC Helper functions ###################################### def IsArchX86_64(): """ Determines if target machine is x86_64 Returns: True if running on x86_64, False otherwise """ return kern.arch == "x86_64" @static_var('x2apic_enabled', -1) def IsX2ApicEnabled(): """ Reads the APIC configuration MSR to determine if APIC is operating in x2APIC mode. The MSR is read the first time this function is called, and the answer is remembered for all subsequent calls. Returns: True if APIC is x2APIC mode False if not """ apic_cfg_msr = 0x1b apic_cfg_msr_x2en_mask = 0xc00 if IsX2ApicEnabled.x2apic_enabled < 0: if (int(DoReadMsr64(apic_cfg_msr, xnudefines.lcpu_self)) & apic_cfg_msr_x2en_mask == apic_cfg_msr_x2en_mask): IsX2ApicEnabled.x2apic_enabled = 1 else: IsX2ApicEnabled.x2apic_enabled = 0 return IsX2ApicEnabled.x2apic_enabled == 1 def DoLapicRead32(offset, cpu): """ Read the specified 32-bit LAPIC register Params: offset: int - index of LAPIC register to read cpu: int - cpu ID Returns: The 32-bit LAPIC register value """ if IsX2ApicEnabled(): return DoReadMsr64(offset >> 4, cpu) else: return ReadPhysInt(lapic_base_addr + offset, 32, cpu) def DoLapicWrite32(offset, val, cpu): """ Write the specified 32-bit LAPIC register Params: offset: int - index of LAPIC register to write val: int - write value cpu: int - cpu ID Returns: True if success, False if error """ if IsX2ApicEnabled(): return DoWriteMsr64(offset >> 4, cpu, val) else: return WritePhysInt(lapic_base_addr + offset, val, 32) ###################################### # LAPIC Register Print functions ###################################### def GetLapicVersionFields(reg_val): """ Helper function for DoLapicDump that prints the fields of the version register. Params: reg_val: int - the value of the version register to print Returns: string showing the fields """ lvt_num = (reg_val >> 16) + 1 version = reg_val & 0xff return "[VERSION={:d} MaxLVT={:d}]".format(lvt_num, version) def GetLapicSpuriousVectorFields(reg_val): """ Helper function for DoLapicDump that prints the fields of the spurious vector register. Params: reg_val: int - the value of the spurious vector registre to print Returns: string showing the fields """ vector = reg_val & 0xff enabled = (reg_val & 0x100) >> 8 return "[VEC={:3d} ENABLED={:d}]".format(vector, enabled) def GetLapicIcrHiFields(reg_val): """ Helper function for DoLapicDump that prints the fields of the upper 32-bits of the Interrupt Control Register (ICR). Params: reg_val: int - the value of the ICR to show Returns: string showing the fields """ dest = reg_val >> 24 return "[DEST={:d}]".format(dest) def GetLapicTimerDivideFields(reg_val): """ Helper function for DoLapicDump that prints the fields of the timer divide register. Params: reg_val: int - the value of the timer divide register Returns: string showing the fields """ divide_val = ((reg_val & 0x8) >> 1) | (reg_val & 0x3) if divide_val == 0x7: divide_by = 1 else: divide_by = 2 << divide_val return "[Divide by {:d}]".format(divide_by) def GetApicFields(reg_val): """ Helper function for DoLapicDump and DoIoapicDump that prints the fields of the APIC register. Params: reg_val: int - the value of the APIC register to print Returns: string showing the fields """ vector = reg_val & 0xff tsc_deadline = reg_val & 0x40000 periodic = reg_val & 0x20000 masked = reg_val & 0x10000 trigger = reg_val & 0x8000 polarity = reg_val & 0x2000 pending = reg_val & 0x1000 ret_str = "[VEC={:3d} MASK={:3s} TRIG={:5s} POL={:4s} PEND={:3s}".format( vector, "no" if masked == 0 else "yes", "edge" if trigger == 0 else "level", "low" if polarity == 0 else "high", "no" if pending == 0 else "yes") if not periodic == 0: ret_str += " PERIODIC" if not tsc_deadline == 0: ret_str += " TSC_DEADLINE" ret_str += "]" return ret_str def DoLapicDump(): """ Prints all LAPIC registers """ print("LAPIC operating mode: {:s}".format( "x2APIC" if IsX2ApicEnabled() else "xAPIC")) # LAPIC register offset, register name, field formatting function lapic_dump_table = [ (0x020, "ID", None), (0x030, "VERSION", GetLapicVersionFields), (0x080, "TASK PRIORITY", None), (0x0A0, "PROCESSOR PRIORITY", None), (0x0D0, "LOGICAL DEST", None), (0x0E0, "DEST FORMAT", None), (0x0F0, "SPURIOUS VECTOR", GetLapicSpuriousVectorFields), (0x100, "ISR[031:000]", None), (0x110, "ISR[063:032]", None), (0x120, "ISR[095:064]", None), (0x130, "ISR[127:096]", None), (0x140, "ISR[159:128]", None), (0x150, "ISR[191:160]", None), (0x160, "ISR[223:192]", None), (0x170, "ISR[225:224]", None), (0x180, "TMR[031:000]", None), (0x190, "TMR[063:032]", None), (0x1A0, "TMR[095:064]", None), (0x1B0, "TMR[127:096]", None), (0x1C0, "TMR[159:128]", None), (0x1D0, "TMR[191:160]", None), (0x1E0, "TMR[223:192]", None), (0x1F0, "TMR[225:224]", None), (0x200, "IRR[031:000]", None), (0x210, "IRR[063:032]", None), (0x220, "IRR[095:064]", None), (0x230, "IRR[127:096]", None), (0x240, "IRR[159:128]", None), (0x250, "IRR[191:160]", None), (0x260, "IRR[223:192]", None), (0x270, "IRR[225:224]", None), (0x280, "ERROR STATUS", None), (0x300, "Interrupt Command LO", GetApicFields), (0x310, "Interrupt Command HI", GetLapicIcrHiFields), (0x320, "LVT Timer", GetApicFields), (0x350, "LVT LINT0", GetApicFields), (0x360, "LVT LINT1", GetApicFields), (0x370, "LVT Error", GetApicFields), (0x340, "LVT PerfMon", GetApicFields), (0x330, "LVT Thermal", GetApicFields), (0x3e0, "Timer Divide", GetLapicTimerDivideFields), (0x380, "Timer Init Count", None), (0x390, "Timer Cur Count", None)] for reg in lapic_dump_table: reg_val = DoLapicRead32(reg[0], xnudefines.lcpu_self) if reg[2] == None: print("LAPIC[{:#05x}] {:21s}: {:#010x}".format(reg[0], reg[1], reg_val)) else: print("LAPIC[{:#05x}] {:21s}: {:#010x} {:s}".format(reg[0], reg[1], reg_val, reg[2](reg_val))) ###################################### # IOAPIC Helper functions ###################################### def DoIoApicRead(offset): """ Read the specified IOAPIC register Params: offset: int - index of IOAPIC register to read Returns: int 32-bit read value """ WritePhysInt(ioapic_base_addr + ioapic_index_off, offset, 8) return ReadPhysInt(ioapic_base_addr + ioapic_data_off, 32) def DoIoApicWrite(offset, val): """ Write the specified IOAPIC register Params: offset: int - index of IOAPIC register to write Returns: True if success, False if error """ WritePhysInt(ioapic_base_addr + ioapic_index_off, offset, 8) return WritePhysInt(ioapic_base_addr + ioapic_data_off, val, 32) def DoIoApicDump(): """ Prints all IOAPIC registers """ # Show IOAPIC ID register ioapic_id = DoIoApicRead(0) print("IOAPIC[0x00] {:9s}: {:#010x}".format("ID", ioapic_id)) # Show IOAPIC Version register ioapic_ver = DoIoApicRead(1) maxredir = ((ioapic_ver >> 16) & 0xff) + 1 print("IOAPIC[0x01] {:9s}: {:#010x}".format("VERSION", ioapic_ver) +\ " [MAXREDIR={:02d} PRQ={:d} VERSION={:#04x}]".format( maxredir, ioapic_ver >> 15 & 0x1, ioapic_ver & 0xff)) # Show IOAPIC redirect regsiters for redir in range(maxredir): redir_val_lo = DoIoApicRead(0x10 + redir * 2) redir_val_hi = DoIoApicRead(0x10 + (redir * 2) + 1) print("IOAPIC[{:#04x}] IOREDIR{:02d}: {:#08x}{:08x} {:s}".format( 0x10 + (redir * 2), redir, redir_val_hi, redir_val_lo, GetApicFields(redir_val_lo))) ###################################### # LLDB commands ###################################### @lldb_command('lapic_read32') def LapicRead32(cmd_args=None): """ Read the LAPIC register at the specified offset. The CPU can be optionally specified Syntax: lapic_read32 [lcpu] """ if cmd_args is None or len(cmd_args) < 1: print(LapicRead32.__doc__) return if not IsArchX86_64(): print("lapic_read32 not supported on this architecture.") return lcpu = xnudefines.lcpu_self if len(cmd_args) > 1: lcpu = ArgumentStringToInt(cmd_args[1]) offset = ArgumentStringToInt(cmd_args[0]) read_val = DoLapicRead32(offset, lcpu) print("LAPIC[{:#05x}]: {:#010x}".format(offset, read_val)) @lldb_command('lapic_write32') def LapicWrite32(cmd_args=None): """ Write the LAPIC register at the specified offset. The CPU can be optionally specified. Prints an error message if there was a failure. Prints nothing upon success. Syntax: lapic_write32 [lcpu] """ if cmd_args is None or len(cmd_args) < 2: print(LapicWrite32.__doc__) return if not IsArchX86_64(): print("lapic_write32 not supported on this architecture.") return offset = ArgumentStringToInt(cmd_args[0]) write_val = ArgumentStringToInt(cmd_args[1]) lcpu = xnudefines.lcpu_self if len(cmd_args) > 2: lcpu = ArgumentStringToInt(cmd_args[2]) if not DoLapicWrite32(offset, write_val, lcpu): print("lapic_write32 FAILED") @lldb_command('lapic_dump') def LapicDump(cmd_args=None): """ Prints all LAPIC entries """ if not IsArchX86_64(): print("lapic_dump not supported on this architecture.") return DoLapicDump() @lldb_command('ioapic_read32') def IoApicRead32(cmd_args=None): """ Read the IOAPIC register at the specified offset. Syntax: ioapic_read32 """ if cmd_args is None or len(cmd_args) < 1: print(IoApicRead32.__doc__) return if not IsArchX86_64(): print("ioapic_read32 not supported on this architecture.") return offset = ArgumentStringToInt(cmd_args[0]) read_val = DoIoApicRead(offset) print("IOAPIC[{:#04x}]: {:#010x}".format(offset, read_val)) @lldb_command('ioapic_write32') def IoApicWrite32(cmd_args=None): """ Write the IOAPIC register at the specified offset. Syntax: ioapic_write32 """ if cmd_args is None or len(cmd_args) < 2: print(IoApicWrite32.__doc__) return if not IsArchX86_64(): print("ioapic_write32 not supported on this architecture.") return offset = ArgumentStringToInt(cmd_args[0]) write_val = ArgumentStringToInt(cmd_args[1]) if not DoIoApicWrite(offset, write_val): print("ioapic_write32 FAILED") return @lldb_command('ioapic_dump') def IoApicDump(cmd_args=None): """ Prints all IOAPIC entries """ if not IsArchX86_64(): print("ioapic_dump not supported on this architecture.") return DoIoApicDump()