1"""
2Miscellaneous (Intel) platform-specific commands.
3"""
4
5from xnu import *
6import xnudefines
7
8@lldb_command('showmcastate')
9def showMCAstate(cmd_args=None):
10    """
11    Print machine-check register state after MC exception.
12    """
13    if kern.arch != 'x86_64':
14        print "Not available for current architecture."
15        return
16
17    present = ["not present", "present"]
18    print 'MCA {:s}, control MSR {:s}, threshold status {:s}'.format(
19    present[int(kern.globals.mca_MCA_present)],
20    present[int(kern.globals.mca_control_MSR_present)],
21    present[int(kern.globals.mca_threshold_status_present)])
22    print '{:d} error banks, family code {:#0x}, machine-check dump state: {:d}'.format(
23        kern.globals.mca_error_bank_count,
24        kern.globals.mca_dump_state,
25        kern.globals.mca_family)
26    cpu = 0
27    while kern.globals.cpu_data_ptr[cpu]:
28        cd = kern.globals.cpu_data_ptr[cpu]
29        mc = cd.cpu_mca_state
30        if mc:
31            print 'CPU {:d}: mca_mcg_ctl: {:#018x} mca_mcg_status {:#018x}'.format(cpu, mc.mca_mcg_ctl, mc.mca_mcg_status.u64)
32            hdr = '{:<4s} {:<18s} {:<18s} {:<18s} {:<18s}'
33            val = '{:>3d}: {:#018x} {:#018x} {:#018x} {:#018x}'
34            print hdr.format('bank',
35                    'mca_mci_ctl',
36                    'mca_mci_status',
37                    'mca_mci_addr',
38                    'mca_mci_misc')
39            for i in range(int(kern.globals.mca_error_bank_count)):
40                bank = mc.mca_error_bank[i]
41                print val.format(i,
42                    bank.mca_mci_ctl,
43                    bank.mca_mci_status.u64,
44                    bank.mca_mci_addr,
45                    bank.mca_mci_misc)
46        print 'register state:'
47        reg = cd.cpu_desc_index.cdi_ktss.ist1 - sizeof('x86_saved_state_t')
48        print lldb_run_command('p/x *(x86_saved_state_t *) ' + hex(reg))
49        cpu = cpu + 1
50
51def dumpTimerList(anchor):
52    """
53    Utility function to dump the timer entries in list (anchor).
54    """
55    entry = Cast(anchor.head, 'queue_t')
56    if entry == addressof(anchor):
57        print '(empty)'
58        return
59
60    thdr = ' {:<22s}{:<17s}{:<16s} {:<14s} {:<18s}'
61    print thdr.format('entry:','deadline','soft_deadline','to go','(*func)(param0,param1')
62    while entry != addressof(anchor):
63        timer_call = Cast(entry, 'timer_call_t')
64        call_entry = Cast(entry, 'struct call_entry *')
65        debugger_entry = kern.globals.debugger_entry_time
66        if (debugger_entry < call_entry.deadline):
67            delta_sign = ' '
68            timer_fire = call_entry.deadline - debugger_entry
69        else:
70            delta_sign = '-'
71            timer_fire = debugger_entry - call_entry.deadline
72        tval = ' {:#018x}: {:16d} {:16d} {:s}{:3d}.{:09d}  ({:#018x})({:#018x},{:#018x})'
73        print tval.format(entry,
74            call_entry.deadline,
75            timer_call.soft_deadline,
76            delta_sign,
77            timer_fire/1000000000,
78            timer_fire%1000000000,
79            call_entry.func,
80            call_entry.param0,
81            call_entry.param1)
82        entry = entry.next
83
84@lldb_command('longtermtimers')
85def longtermTimers(cmd_args=None):
86    """
87    Print details of long-term timers and stats.
88    """
89    if kern.arch != 'x86_64':
90        print "Not available for current architecture."
91        return
92
93    lt = kern.globals.timer_longterm
94    ltt = lt.threshold
95    EndofAllTime = -1
96    if ltt.interval == EndofAllTime:
97        print "Longterm timers disabled"
98        return
99
100    if lt.escalates > 0:
101        ratio = lt.enqueues / lt.escalates
102    else:
103        ratio = lt.enqueues
104    print     'Longterm timer object: {:#018x}'.format(addressof(lt))
105    print     ' queue count         : {:d}'    .format(lt.queue.count)
106    print     ' number of enqueues  : {:d}'    .format(lt.enqueues)
107    print     ' number of dequeues  : {:d}'    .format(lt.dequeues)
108    print     ' number of escalates : {:d}'    .format(lt.escalates)
109    print     ' enqueues/escalates  : {:d}'    .format(ratio)
110    print     ' threshold.interval  : {:d}'    .format(ltt.interval)
111    print     ' threshold.margin    : {:d}'    .format(ltt.margin)
112    print     ' scan_time           : {:d}'    .format(lt.scan_time)
113    if ltt.preempted == EndofAllTime:
114        print ' threshold.preempted : None'
115    else:
116        print ' threshold.preempted : {:d}'    .format(ltt.preempted)
117    if ltt.deadline == EndofAllTime:
118        print ' threshold.deadline  : None'
119    else:
120        print ' threshold.deadline  : {:d}'    .format(ltt.deadline)
121        print ' threshold.call      : {:#018x}'.format(ltt.call)
122        print ' actual deadline set : {:d}'    .format(ltt.deadline_set)
123    print     ' threshold.scans     : {:d}'    .format(ltt.scans)
124    print     ' threshold.preempts  : {:d}'    .format(ltt.preempts)
125    print     ' threshold.latency   : {:d}'    .format(ltt.latency)
126    print     '               - min : {:d}'    .format(ltt.latency_min)
127    print     '               - max : {:d}'    .format(ltt.latency_max)
128    dumpTimerList(lt.queue)
129
130
131@lldb_command('processortimers')
132def processorTimers(cmd_args=None):
133    """
134    Print details of processor timers, noting anything suspicious
135    Also include long-term timer details
136    """
137    hdr = '{:<32s}{:<18s} {:<18s} {:<18s}'
138    print hdr.format('Processor','Last dispatch','Next deadline','difference')
139    p = kern.globals.processor_list
140    while p:
141        cpu = p.cpu_id
142        rt_timer = kern.globals.cpu_data_ptr[cpu].rtclock_timer
143        diff = p.last_dispatch - rt_timer.deadline
144        tmr = 'Processor {:d}: {:#018x} {:#018x} {:#018x} {:#018x} {:s}'
145        print tmr.format(cpu,
146            p,
147            p.last_dispatch,
148            rt_timer.deadline,
149            diff,
150            ['probably BAD', '(ok)'][int(diff < 0)])
151        if kern.arch == 'x86_64':
152            print 'Next deadline set at: {:#018x}. Timer call list:'.format(rt_timer.when_set)
153            dumpTimerList(rt_timer.queue)
154        p = p.processor_list
155    longtermTimers()
156
157
158@lldb_command('showtimerwakeupstats')
159def showTimerWakeupStats(cmd_args=None):
160    """
161    Displays interrupt and platform idle wakeup frequencies
162    associated with each thread, timer time-to-deadline frequencies, and
163    CPU time with user/system break down where applicable, with thread tags.
164    """
165    for task in kern.tasks:
166        proc = Cast(task.bsd_info, 'proc_t')
167        print dereference(task)
168        print '{:d}({:s}), terminated thread timer wakeups: {:d} {:d} 2ms: {:d} 5ms: {:d} UT: {:d} ST: {:d}'.format(
169            proc.p_pid,
170            proc.p_comm,
171# Commented-out references below to be addressed by rdar://13009660.
172            0, #task.task_interrupt_wakeups,
173            0, #task.task_platform_idle_wakeups,
174            task.task_timer_wakeups_bin_1,
175            task.task_timer_wakeups_bin_2,
176            task.total_user_time,
177            task.total_system_time)
178        tot_wakes = 0 #task.task_interrupt_wakeups
179        tot_platform_wakes = 0 #task.task_platform_idle_wakeups
180        for thread in IterateQueue(task.threads, 'thread_t', 'task_threads'):
181#           if thread.thread_interrupt_wakeups == 0:
182#               continue
183            print '\tThread ID 0x{:x}, Tag 0x{:x}, timer wakeups: {:d} {:d} {:d} {:d} <2ms: {:d}, <5ms: {:d} UT: {:d} ST: {:d}'.format(
184                thread.thread_id,
185                thread.thread_tag,
186                0, #thread.thread_interrupt_wakeups,
187                0, #thread.thread_platform_idle_wakeups,
188                0, #thread.thread_callout_interrupt_wakeups,
189                0, #thread.thread_callout_platform_idle_wakeups,
190                0,0,0,0,
191                thread.thread_timer_wakeups_bin_1,
192                thread.thread_timer_wakeups_bin_2,
193                thread.user_timer.all_bits,
194                thread.system_timer.all_bits)
195            tot_wakes += 0 #thread.thread_interrupt_wakeups
196            tot_platform_wakes += 0 #thread.thread_platform_idle_wakeups
197        print 'Task total wakeups: {:d} {:d}'.format(
198            tot_wakes, tot_platform_wakes)
199
200def DoReadMsr64(msr_address, lcpu):
201    """ Read a 64-bit MSR from the specified CPU
202        Params:
203            msr_address: int - MSR index to read from
204            lcpu: int - CPU identifier
205        Returns:
206            64-bit value read from the MSR
207    """
208    result = 0xbad10ad
209
210    if "kdp" != GetConnectionProtocol():
211        print "Target is not connected over kdp. Cannot read MSR."
212        return result
213
214    input_address = unsigned(addressof(kern.globals.manual_pkt.input))
215    len_address = unsigned(addressof(kern.globals.manual_pkt.len))
216    data_address = unsigned(addressof(kern.globals.manual_pkt.data))
217    if not WriteInt32ToMemoryAddress(0, input_address):
218        print "DoReadMsr64() failed to write 0 to input_address"
219        return result
220
221    kdp_pkt_size = GetType('kdp_readmsr64_req_t').GetByteSize()
222    if not WriteInt32ToMemoryAddress(kdp_pkt_size, len_address):
223        print "DoReadMsr64() failed to write kdp_pkt_size"
224        return result
225
226    kgm_pkt = kern.GetValueFromAddress(data_address, 'kdp_readmsr64_req_t *')
227    header_value = GetKDPPacketHeaderInt(
228        request=GetEnumValue('kdp_req_t::KDP_READMSR64'),
229        length=kdp_pkt_size)
230
231    if not WriteInt64ToMemoryAddress(header_value, int(addressof(kgm_pkt.hdr))):
232        print "DoReadMsr64() failed to write header_value"
233        return result
234    if not WriteInt32ToMemoryAddress(msr_address, int(addressof(kgm_pkt.address))):
235        print "DoReadMsr64() failed to write msr_address"
236        return result
237    if not WriteInt16ToMemoryAddress(lcpu, int(addressof(kgm_pkt.lcpu))):
238        print "DoReadMsr64() failed to write lcpu"
239        return result
240    if not WriteInt32ToMemoryAddress(1, input_address):
241        print "DoReadMsr64() failed to write to input_address"
242        return result
243
244    result_pkt = Cast(addressof(kern.globals.manual_pkt.data),
245        'kdp_readmsr64_reply_t *')
246    if (result_pkt.error == 0):
247        result = dereference(Cast(addressof(result_pkt.data), 'uint64_t *'))
248    else:
249        print "DoReadMsr64() result_pkt.error != 0"
250    return result
251
252def DoWriteMsr64(msr_address, lcpu, data):
253    """ Write a 64-bit MSR
254        Params:
255            msr_address: int - MSR index to write to
256            lcpu: int - CPU identifier
257            data: int - value to write
258        Returns:
259            True upon success, False if error
260    """
261    if "kdp" != GetConnectionProtocol():
262        print "Target is not connected over kdp. Cannot write MSR."
263        return False
264
265    input_address = unsigned(addressof(kern.globals.manual_pkt.input))
266    len_address = unsigned(addressof(kern.globals.manual_pkt.len))
267    data_address = unsigned(addressof(kern.globals.manual_pkt.data))
268    if not WriteInt32ToMemoryAddress(0, input_address):
269        print "DoWriteMsr64() failed to write 0 to input_address"
270        return False
271
272    kdp_pkt_size = GetType('kdp_writemsr64_req_t').GetByteSize()
273    if not WriteInt32ToMemoryAddress(kdp_pkt_size, len_address):
274        print "DoWriteMsr64() failed to kdp_pkt_size"
275        return False
276
277    kgm_pkt = kern.GetValueFromAddress(data_address, 'kdp_writemsr64_req_t *')
278    header_value = GetKDPPacketHeaderInt(
279        request=GetEnumValue('kdp_req_t::KDP_WRITEMSR64'),
280        length=kdp_pkt_size)
281
282    if not WriteInt64ToMemoryAddress(header_value, int(addressof(kgm_pkt.hdr))):
283        print "DoWriteMsr64() failed to write header_value"
284        return False
285    if not WriteInt32ToMemoryAddress(msr_address, int(addressof(kgm_pkt.address))):
286        print "DoWriteMsr64() failed to write msr_address"
287        return False
288    if not WriteInt16ToMemoryAddress(lcpu, int(addressof(kgm_pkt.lcpu))):
289        print "DoWriteMsr64() failed to write lcpu"
290        return False
291    if not WriteInt64ToMemoryAddress(data, int(addressof(kgm_pkt.data))):
292        print "DoWriteMsr64() failed to write data"
293        return False
294    if not WriteInt32ToMemoryAddress(1, input_address):
295        print "DoWriteMsr64() failed to write to input_address"
296        return False
297
298    result_pkt = Cast(addressof(kern.globals.manual_pkt.data),
299        'kdp_writemsr64_reply_t *')
300    if not result_pkt.error == 0:
301        print "DoWriteMsr64() error received in reply packet"
302        return False
303
304    return True
305
306@lldb_command('readmsr64')
307def ReadMsr64(cmd_args=None):
308    """ Read the specified MSR. The CPU can be optionally specified
309        Syntax: readmsr64 <msr> [lcpu]
310    """
311    if cmd_args == None or len(cmd_args) < 1:
312        print ReadMsr64.__doc__
313        return
314
315    msr_address = ArgumentStringToInt(cmd_args[0])
316    if len(cmd_args) > 1:
317        lcpu = ArgumentStringToInt(cmd_args[1])
318    else:
319        lcpu = int(xnudefines.lcpu_self)
320
321    msr_value = DoReadMsr64(msr_address, lcpu)
322    print "MSR[{:x}]: {:#016x}".format(msr_address, msr_value)
323
324@lldb_command('writemsr64')
325def WriteMsr64(cmd_args=None):
326    """ Write the specified MSR. The CPU can be optionally specified
327        Syntax: writemsr64 <msr> <value> [lcpu]
328    """
329    if cmd_args == None or len(cmd_args) < 2:
330        print WriteMsr64.__doc__
331        return
332    msr_address = ArgumentStringToInt(cmd_args[0])
333    write_val = ArgumentStringToInt(cmd_args[1])
334    if len(cmd_args) > 2:
335        lcpu = ArgumentStringToInt(cmd_args[2])
336    else:
337        lcpu = xnudefines.lcpu_self
338
339    if not DoWriteMsr64(msr_address, lcpu, write_val):
340        print "writemsr64 FAILED"
341
342