1 # SPDX-License-Identifier: GPL-2.0
3 # gdb helper commands and functions for Linux kernel debugging
5 # Kernel proc information reader
7 # Copyright (c) 2016 Linaro Ltd
10 # Kieran Bingham <kieran.bingham@linaro.org>
12 # This work is licensed under the terms of the GNU GPL version 2.
16 from linux import constants
17 from linux import utils
18 from linux import tasks
19 from linux import lists
24 class LxCmdLine(gdb.Command):
25 """ Report the Linux Commandline used in the current kernel.
26 Equivalent to cat /proc/cmdline on a running target"""
29 super(LxCmdLine, self).__init__("lx-cmdline", gdb.COMMAND_DATA)
31 def invoke(self, arg, from_tty):
32 gdb.write(gdb.parse_and_eval("saved_command_line").string() + "\n")
38 class LxVersion(gdb.Command):
39 """ Report the Linux Version of the current kernel.
40 Equivalent to cat /proc/version on a running target"""
43 super(LxVersion, self).__init__("lx-version", gdb.COMMAND_DATA)
45 def invoke(self, arg, from_tty):
46 # linux_banner should contain a newline
47 gdb.write(gdb.parse_and_eval("(char *)linux_banner").string())
53 # Resource Structure Printers
57 def get_resources(resource, depth):
61 child = resource['child']
63 for res, deep in get_resources(child, depth + 1):
66 resource = resource['sibling']
69 def show_lx_resources(resource_str):
70 resource = gdb.parse_and_eval(resource_str)
71 width = 4 if resource['end'] < 0x10000 else 8
72 # Iterate straight to the first child
73 for res, depth in get_resources(resource['child'], 0):
74 start = int(res['start'])
76 gdb.write(" " * depth * 2 +
77 "{0:0{1}x}-".format(start, width) +
78 "{0:0{1}x} : ".format(end, width) +
79 res['name'].string() + "\n")
82 class LxIOMem(gdb.Command):
83 """Identify the IO memory resource locations defined by the kernel
85 Equivalent to cat /proc/iomem on a running target"""
88 super(LxIOMem, self).__init__("lx-iomem", gdb.COMMAND_DATA)
90 def invoke(self, arg, from_tty):
91 return show_lx_resources("iomem_resource")
97 class LxIOPorts(gdb.Command):
98 """Identify the IO port resource locations defined by the kernel
100 Equivalent to cat /proc/ioports on a running target"""
103 super(LxIOPorts, self).__init__("lx-ioports", gdb.COMMAND_DATA)
105 def invoke(self, arg, from_tty):
106 return show_lx_resources("ioport_resource")
112 # Mount namespace viewer
115 def info_opts(lst, opt):
117 for key, string in lst.items():
123 FS_INFO = {constants.LX_SB_SYNCHRONOUS: ",sync",
124 constants.LX_SB_MANDLOCK: ",mand",
125 constants.LX_SB_DIRSYNC: ",dirsync",
126 constants.LX_SB_NOATIME: ",noatime",
127 constants.LX_SB_NODIRATIME: ",nodiratime"}
129 MNT_INFO = {constants.LX_MNT_NOSUID: ",nosuid",
130 constants.LX_MNT_NODEV: ",nodev",
131 constants.LX_MNT_NOEXEC: ",noexec",
132 constants.LX_MNT_NOATIME: ",noatime",
133 constants.LX_MNT_NODIRATIME: ",nodiratime",
134 constants.LX_MNT_RELATIME: ",relatime"}
136 mount_type = utils.CachedType("struct mount")
137 mount_ptr_type = mount_type.get_type().pointer()
140 class LxMounts(gdb.Command):
141 """Report the VFS mounts of the current process namespace.
143 Equivalent to cat /proc/mounts on a running target
144 An integer value can be supplied to display the mount
145 values of that process namespace"""
148 super(LxMounts, self).__init__("lx-mounts", gdb.COMMAND_DATA)
150 # Equivalent to proc_namespace.c:show_vfsmnt
151 # However, that has the ability to call into s_op functions
152 # whereas we cannot and must make do with the information we can obtain.
153 def invoke(self, arg, from_tty):
154 argv = gdb.string_to_argv(arg)
159 raise gdb.GdbError("Provide a PID as integer value")
163 task = tasks.get_task_by_pid(pid)
165 raise gdb.GdbError("Couldn't find a process with PID {}"
168 namespace = task['nsproxy']['mnt_ns']
170 raise gdb.GdbError("No namespace for current process")
172 gdb.write("{:^18} {:^15} {:>9} {} {} options\n".format(
173 "mount", "super_block", "devname", "pathname", "fstype"))
175 for mnt in lists.list_for_each_entry(namespace['list'],
176 mount_ptr_type, "mnt_list"):
177 devname = mnt['mnt_devname'].string()
178 devname = devname if devname else "none"
183 mntpoint = parent['mnt_mountpoint']
184 pathname = vfs.dentry_name(mntpoint) + pathname
185 if (parent == parent['mnt_parent']):
187 parent = parent['mnt_parent']
192 superblock = mnt['mnt']['mnt_sb']
193 fstype = superblock['s_type']['name'].string()
194 s_flags = int(superblock['s_flags'])
195 m_flags = int(mnt['mnt']['mnt_flags'])
196 rd = "ro" if (s_flags & constants.LX_SB_RDONLY) else "rw"
198 gdb.write("{} {} {} {} {} {}{}{} 0 0\n".format(
199 mnt.format_string(), superblock.format_string(), devname,
200 pathname, fstype, rd, info_opts(FS_INFO, s_flags),
201 info_opts(MNT_INFO, m_flags)))
207 class LxFdtDump(gdb.Command):
208 """Output Flattened Device Tree header and dump FDT blob to the filename
209 specified as the command argument. Equivalent to
210 'cat /proc/fdt > fdtdump.dtb' on a running target"""
213 super(LxFdtDump, self).__init__("lx-fdtdump", gdb.COMMAND_DATA,
214 gdb.COMPLETE_FILENAME)
216 def fdthdr_to_cpu(self, fdt_header):
218 fdt_header_be = ">IIIIIII"
219 fdt_header_le = "<IIIIIII"
221 if utils.get_target_endianness() == 1:
222 output_fmt = fdt_header_le
224 output_fmt = fdt_header_be
226 return unpack(output_fmt, pack(fdt_header_be,
228 fdt_header['totalsize'],
229 fdt_header['off_dt_struct'],
230 fdt_header['off_dt_strings'],
231 fdt_header['off_mem_rsvmap'],
232 fdt_header['version'],
233 fdt_header['last_comp_version']))
235 def invoke(self, arg, from_tty):
237 if not constants.LX_CONFIG_OF:
238 raise gdb.GdbError("Kernel not compiled with CONFIG_OF\n")
241 filename = "fdtdump.dtb"
245 py_fdt_header_ptr = gdb.parse_and_eval(
246 "(const struct fdt_header *) initial_boot_params")
247 py_fdt_header = py_fdt_header_ptr.dereference()
249 fdt_header = self.fdthdr_to_cpu(py_fdt_header)
251 if fdt_header[0] != constants.LX_OF_DT_HEADER:
252 raise gdb.GdbError("No flattened device tree magic found\n")
254 gdb.write("fdt_magic: 0x{:02X}\n".format(fdt_header[0]))
255 gdb.write("fdt_totalsize: 0x{:02X}\n".format(fdt_header[1]))
256 gdb.write("off_dt_struct: 0x{:02X}\n".format(fdt_header[2]))
257 gdb.write("off_dt_strings: 0x{:02X}\n".format(fdt_header[3]))
258 gdb.write("off_mem_rsvmap: 0x{:02X}\n".format(fdt_header[4]))
259 gdb.write("version: {}\n".format(fdt_header[5]))
260 gdb.write("last_comp_version: {}\n".format(fdt_header[6]))
262 inf = gdb.inferiors()[0]
263 fdt_buf = utils.read_memoryview(inf, py_fdt_header_ptr,
264 fdt_header[1]).tobytes()
267 f = open(filename, 'wb')
269 raise gdb.GdbError("Could not open file to dump fdt")
274 gdb.write("Dumped fdt blob to " + filename + "\n")