xref: /xnu-12377.1.9/tools/lldbmacros/kdp.py (revision f6217f891ac0bb64f3d375211650a4c1ff8ca1ea)
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) == 0:
84        raise ArgumentError()
85
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) == 0:
110        raise ArgumentError()
111
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) == 0:
135        raise ArgumentError()
136
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        raise ArgumentError("Please provide valid time in seconds")
237
238    if "kdp" != GetConnectionProtocol():
239        print("Target is not connected over kdp. Nothing to do here.")
240        return False
241
242    num_seconds = ArgumentStringToInt(cmd_args[0])
243    milliseconds_to_sleep = num_seconds * 1000
244    if WriteInt32ToMemoryAddress(milliseconds_to_sleep, addressof(kern.globals.kdp_reentry_deadline)):
245        lldb.debugger.HandleCommand('process continue')
246        return True
247    print("Failed to setup kdp-reentry.")
248    return False
249
250@lldb_command('kdp-reboot')
251def KDPReboot(cmd_args=None):
252    """ Restart the remote target
253    """
254    if "kdp" != GetConnectionProtocol():
255        print("Target is not connected over kdp. Nothing to do here.")
256        return False
257
258    print("Rebooting the remote machine.")
259    lldb.debugger.HandleCommand('process plugin packet send --command 0x13')
260    lldb.debugger.HandleCommand('detach')
261    return True
262
263@lldb_command('setdumpinfo')
264def KDPSetDumpInfo(cmd_args=None):
265    """ Configure the current remote dump settings.
266        Specify "" if you want to use the defaults (filename) or previously configured
267        settings (ip/router). Specify 0 for the port if you wish to
268        use the previously configured/default setting for that.
269        Syntax: setdumpinfo <filename> <ip> <router> <port>
270    """
271    if cmd_args is None or len(cmd_args) == 0:
272        raise ArgumentError()
273
274    if len(cmd_args) < 4:
275        raise ArgumentError("Not enough arguments.")
276
277    portnum = ArgumentStringToInt(cmd_args[3])
278    retval = KDPDumpInfo(GetEnumValue('kdp_dumpinfo_t::KDP_DUMPINFO_SETINFO'), cmd_args[0], cmd_args[1], cmd_args[2], portnum)
279    if retval:
280        print("Successfully saved the dumpinfo.")
281    else:
282        print("Failed to save the dumpinfo.")
283    return retval
284
285@lldb_command('kdpmode')
286def KDPMode(cmd_args=None):
287    """
288    Change KDP mode between software hosted and hardware probe.
289    When lldb is connected to a KDP server backed by a hardware debug tool
290    setting this to 'hwprobe' enables physical memory access.
291
292    swhosted: LLDB is connected to the target using a serial or socket connection.
293    hwprobe: LLDB is connected to the target using a hardware probe.
294
295    usage: kdpmode <mode>
296    mode: 'swhosted' or 'hwprobe'
297    """
298    global current_KDP_mode
299
300    if cmd_args is None or len(cmd_args) == 0:
301        return current_KDP_mode
302    if len(cmd_args) > 1 or cmd_args[0] not in {'swhosted', 'hwprobe'}:
303        raise ArgumentError()
304    else:
305        current_KDP_mode = cmd_args[0]
306    return
307
308