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