xref: /xnu-8020.121.3/tools/lldbmacros/kdp.py (revision fdd8201d7b966f0c3ea610489d29bd841d358941)
1from __future__ import absolute_import, print_function
2
3from xnu import *
4from utils import *
5import sys
6
7current_KDP_mode = "swhosted"
8
9def GetKDPPacketHeaderInt(request=0, is_reply=False, seq=0, length=0, key=0):
10    """ create a 64 bit number that could be saved as pkt_hdr_t
11        params:
12            request:int   - 7 bit kdp_req_t request type
13            is_reply:bool - False => request, True => reply
14            seq: int      - 8  sequence number within session
15            length: int   - 16 bit length of entire pkt including hdr
16            key: int      - session key
17        returns:
18            int - 64 bit number to be saved in memory
19    """
20    retval = request
21    if is_reply:
22        retval = 1<<7 |retval
23    retval = (seq << 8) | retval
24    retval = (length << 16) | retval
25    #retval = (retval << 32) | key
26    retval = (key << 32) | retval
27    return retval
28
29
30def KDPDumpInfo(subcmd, file_name="", dest_ip="", router_ip="", port=0):
31    """ Setup the state for DUMP INFO commands for sending coredump etc
32    """
33    if "kdp" != GetConnectionProtocol():
34        print("Target is not connected over kdp. Nothing to do here.")
35        return False
36    input_address = unsigned(addressof(kern.globals.manual_pkt.input))
37    len_address = unsigned(addressof(kern.globals.manual_pkt.len))
38    data_address = unsigned(addressof(kern.globals.manual_pkt.data))
39    if not WriteInt32ToMemoryAddress(0, input_address):
40        return False
41
42    kdp_pkt_size = GetType('kdp_dumpinfo_req_t').GetByteSize()
43    if not WriteInt32ToMemoryAddress(kdp_pkt_size, len_address):
44        return False
45
46    data_addr = int(addressof(kern.globals.manual_pkt))
47    pkt = kern.GetValueFromAddress(data_addr, 'kdp_dumpinfo_req_t *')
48    if len(file_name) > 49:
49        file_name = file_name[:49]
50    if len(dest_ip) > 15:
51        dest_ip = dest_ip[:15]
52    if len(router_ip) > 15:
53        router_ip = router_ip[:15]
54
55    header_value =GetKDPPacketHeaderInt(request=GetEnumValue('kdp_req_t::KDP_DUMPINFO'), length=kdp_pkt_size)
56    # 0x1f is same as KDP_DUMPINFO
57    if ( WriteInt64ToMemoryAddress((header_value), int(addressof(pkt.hdr))) and
58         WriteInt32ToMemoryAddress(subcmd, int(addressof(pkt.type))) and
59         WriteStringToMemoryAddress(file_name, int(addressof(pkt.name))) and
60         WriteStringToMemoryAddress(dest_ip, int(addressof(pkt.destip))) and
61         WriteStringToMemoryAddress(router_ip, int(addressof(pkt.routerip)))
62         ):
63         #We have saved important data successfully
64        if port > 0:
65            if not WriteInt32ToMemoryAddress(port, int(addressof(pkt.port))):
66                return False
67        if WriteInt32ToMemoryAddress(1, input_address):
68            return True
69    return False
70
71@lldb_command('sendcore')
72def KDPSendCore(cmd_args=None):
73    """  Configure kernel to send a coredump to the specified IP
74    Syntax: sendcore <IP address> [filename]
75    Configure the kernel to transmit a kernel coredump to a server (kdumpd)
76    at the specified IP address. This is useful when the remote target has
77    not been previously configured to transmit coredumps, and you wish to
78    preserve kernel state for later examination. NOTE: You must issue a "continue"
79    command after using this macro to trigger the kernel coredump. The kernel
80    will resume waiting in the debugger after completion of the coredump. You
81    may disable coredumps by executing the "disablecore" macro. You can
82    optionally specify the filename to be used for the generated core file.
83
84    """
85    if cmd_args == None or len(cmd_args) < 1:
86        print(KDPSendCore.__doc__)
87        return False
88    ip_address = cmd_args[0]
89    filename=""
90    if len(cmd_args) >=2:
91        filename = cmd_args[1].strip()
92    retval = KDPDumpInfo(GetEnumValue('kdp_dumpinfo_t::KDP_DUMPINFO_CORE'), file_name=filename, dest_ip=ip_address)
93    if retval:
94        print("Remote system has been setup for coredump. Please detach/continue the system. ")
95        return True
96    else:
97        print("Something went wrong. Failed to setup the coredump on the target.")
98        return False
99
100
101@lldb_command('sendsyslog')
102def KDPSendSyslog(cmd_args=None):
103    """ Configure kernel to send a system log to the specified IP
104        Syntax: sendsyslog <IP address> [filename]
105        Configure the kernel to transmit a kernel system log to a server (kdumpd)
106        at the specified IP address. NOTE: You must issue a "continue"
107        command after using this macro to trigger the kernel system log. The kernel
108        will resume waiting in the debugger after completion. You can optionally
109        specify the name to be used for the generated system log.
110    """
111    if cmd_args == None or len(cmd_args) < 1:
112        print(KDPSendSyslog.__doc__)
113        return False
114    ip_address = cmd_args[0]
115    filename =""
116    if len(cmd_args) >=2:
117        filename = cmd_args[1].strip()
118    retval = KDPDumpInfo(GetEnumValue('kdp_dumpinfo_t::KDP_DUMPINFO_SYSTEMLOG'), file_name = filename, dest_ip = ip_address)
119    if retval:
120        print("Remote system has been setup to send system log. please detach/continue the system.")
121        return True
122    else:
123        print("Something went wrong. Failed to setup the systemlog on the target.")
124        return False
125
126@lldb_command('sendpaniclog')
127def KDPSendPaniclog(cmd_args=None):
128    """ Configure kernel to send a panic log to the specified IP
129        Syntax: sendpaniclog <IP address> [filename]
130        Configure the kernel to transmit a kernel paniclog to a server (kdumpd)
131        at the specified IP address. NOTE: You must issue a "continue"
132        command after using this macro to trigger the kernel panic log. The kernel
133        will resume waiting in the debugger after completion. You can optionally
134        specify the name to be used for the generated panic log.
135    """
136    if cmd_args == None or len(cmd_args) < 1:
137        print(KDPSendPaniclog.__doc__)
138        return False
139    ip_address = cmd_args[0]
140    filename =""
141    if len(cmd_args) >=2:
142        filename = cmd_args[1].strip()
143    retval = KDPDumpInfo(GetEnumValue('kdp_dumpinfo_t::KDP_DUMPINFO_PANICLOG'), file_name = filename, dest_ip = ip_address)
144    if retval:
145        print("Remote system has been setup to send panic log. please detach/continue the system.")
146        return True
147    else:
148        print("Something went wrong. Failed to setup the paniclog on the target.")
149        return False
150
151
152@lldb_command('disablecore')
153def KDPDisableCore(cmd_args=None):
154    """ Configure the kernel to disable coredump transmission
155        Reconfigures the kernel so that it no longer transmits kernel coredumps. This
156        complements the "sendcore" macro, but it may be used if the kernel has been
157        configured to transmit coredumps through boot-args as well.
158
159    """
160    retval = KDPDumpInfo(GetEnumValue('kdp_dumpinfo_t::KDP_DUMPINFO_DISABLE'))
161    if retval :
162        print("Disabled coredump functionality on remote system.")
163    else:
164        print("Failed to disable coredump functionality.")
165    return retval
166
167@lldb_command('resume_on')
168def KDPResumeON(cmd_args=None):
169    """ The target system will resume when detaching  or exiting from lldb.
170        This is the default behavior.
171    """
172    subcmd = GetEnumValue('kdp_dumpinfo_t::KDP_DUMPINFO_SETINFO') | GetEnumValue('kdp_dumpinfo_t::KDP_DUMPINFO_RESUME')
173    retval = KDPDumpInfo(subcmd)
174    if retval :
175        print("Target system will resume on detaching from lldb.")
176    else:
177        print("Failed to enable resume functionality.")
178    return retval
179
180@lldb_command('resume_off')
181def KDPResumeOFF(cmd_args=None):
182    """ The target system will not resume when detaching  or exiting from lldb.
183    """
184    subcmd = GetEnumValue('kdp_dumpinfo_t::KDP_DUMPINFO_SETINFO') | GetEnumValue('kdp_dumpinfo_t::KDP_DUMPINFO_NORESUME')
185    retval = KDPDumpInfo(subcmd)
186    if retval :
187        print("Target system will not resume on detaching from lldb.")
188    else:
189        print("Failed to disable resume functionality.")
190    return retval
191
192
193
194@lldb_command('getdumpinfo')
195def KDPGetDumpInfo(cmd_args=None):
196    """ Retrieve the current remote dump settings.
197    """
198    if not KDPDumpInfo(GetEnumValue('kdp_dumpinfo_t::KDP_DUMPINFO_GETINFO')):
199        print("Failed to get dump settings.")
200        return False
201    dumpinfo = Cast(addressof(kern.globals.manual_pkt.data), 'kdp_dumpinfo_reply_t *')
202    target_dump_type = int(dumpinfo.type)
203    if target_dump_type & GetEnumValue('kdp_dumpinfo_t::KDP_DUMPINFO_REBOOT'):
204        print("System will reboot after kernel info gets dumped.")
205    else:
206        print("System will not reboot after kernel info gets dumped.")
207    if target_dump_type & GetEnumValue('kdp_dumpinfo_t::KDP_DUMPINFO_RESUME'):
208        print("System will allow a re-attach after KDP disconnect.")
209    else:
210        print("System will not allow a re-attach after KDP disconnect.")
211    target_dump_type = target_dump_type & GetEnumValue('kdp_dumpinfo_t::KDP_DUMPINFO_MASK')
212    if target_dump_type == GetEnumValue('kdp_dumpinfo_t::KDP_DUMPINFO_DISABLE'):
213        print("Kernel not setup for remote dumps.")
214    else:
215        kern_dump_type = ''
216        if target_dump_type == GetEnumValue('kdp_dumpinfo_t::KDP_DUMPINFO_CORE'):
217            kern_dump_type = "Core File"
218        elif target_dump_type == GetEnumValue('kdp_dumpinfo_t::KDP_DUMPINFO_PANICLOG'):
219            kern_dump_type = "Panic Log"
220        elif target_dump_type == GetEnumValue('kdp_dumpinfo_t::KDP_DUMPINFO_SYSTEMLOG'):
221            kern_dump_type = "System Log"
222        print("Kernel dump type:" + kern_dump_type)
223        fname = "(autogenerated)"
224        if int(dumpinfo.name[0]) != 0:
225            fname = str(dumpinfo.name)
226        print("Filename: " + fname)
227        print("Network Info: {:s} [{:d}] , Router: {:s}".format(dumpinfo.destip, dumpinfo.port, dumpinfo.routerip))
228    # end of get dump info
229
230
231@lldb_command('kdp-reenter')
232def KDPReenter(cmd_args=None):
233    """ Schedules reentry into the debugger
234        after <seconds> seconds, and resumes the target.
235        usage: kdp-reenter <seconds>
236    """
237    if len(cmd_args) < 1:
238        print("Please provide valid time in seconds")
239        print(KDPReenter.__doc__)
240        return False
241
242    if "kdp" != GetConnectionProtocol():
243        print("Target is not connected over kdp. Nothing to do here.")
244        return False
245
246    num_seconds = ArgumentStringToInt(cmd_args[0])
247    milliseconds_to_sleep = num_seconds * 1000
248    if WriteInt32ToMemoryAddress(milliseconds_to_sleep, addressof(kern.globals.kdp_reentry_deadline)):
249        lldb.debugger.HandleCommand('process continue')
250        return True
251    print("Failed to setup kdp-reentry.")
252    return False
253
254@lldb_command('kdp-reboot')
255def KDPReboot(cmd_args=None):
256    """ Restart the remote target
257    """
258    if "kdp" != GetConnectionProtocol():
259        print("Target is not connected over kdp. Nothing to do here.")
260        return False
261
262    print("Rebooting the remote machine.")
263    lldb.debugger.HandleCommand('process plugin packet send --command 0x13')
264    lldb.debugger.HandleCommand('detach')
265    return True
266
267@lldb_command('setdumpinfo')
268def KDPSetDumpInfo(cmd_args=None):
269    """ Configure the current remote dump settings.
270        Specify "" if you want to use the defaults (filename) or previously configured
271        settings (ip/router). Specify 0 for the port if you wish to
272        use the previously configured/default setting for that.
273        Syntax: setdumpinfo <filename> <ip> <router> <port>
274    """
275    if not cmd_args:
276        print(KDPSetDumpInfo.__doc__)
277        return False
278    if len(cmd_args) < 4:
279        print("Not enough arguments.")
280        print(KDPSetDumpInfo.__doc__)
281        return False
282    portnum = ArgumentStringToInt(cmd_args[3])
283    retval = KDPDumpInfo(GetEnumValue('kdp_dumpinfo_t::KDP_DUMPINFO_SETINFO'), cmd_args[0], cmd_args[1], cmd_args[2], portnum)
284    if retval:
285        print("Successfully saved the dumpinfo.")
286    else:
287        print("Failed to save the dumpinfo.")
288    return retval
289
290@lldb_command('kdpmode')
291def KDPMode(cmd_args=None):
292    """
293    Change KDP mode between software hosted and hardware probe.
294    When lldb is connected to a KDP server backed by a hardware debug tool
295    setting this to 'hwprobe' enables physical memory access.
296
297    swhosted: LLDB is connected to the target using a serial or socket connection.
298    hwprobe: LLDB is connected to the target using a hardware probe.
299
300    usage: kdpmode <mode>
301    mode: 'swhosted' or 'hwprobe'
302    """
303    global current_KDP_mode
304
305    if cmd_args == None or len(cmd_args) == 0:
306        return current_KDP_mode
307    if len(cmd_args) > 1 or cmd_args[0] not in {'swhosted', 'hwprobe'}:
308        print("Invalid Arguments", KDPMode.__doc__)
309    else:
310        current_KDP_mode = cmd_args[0]
311    return
312
313