diff options
Diffstat (limited to 'scripts/gdb/linux/proc.py')
-rw-r--r-- | scripts/gdb/linux/proc.py | 277 |
1 files changed, 277 insertions, 0 deletions
diff --git a/scripts/gdb/linux/proc.py b/scripts/gdb/linux/proc.py new file mode 100644 index 000000000..43c687e7a --- /dev/null +++ b/scripts/gdb/linux/proc.py @@ -0,0 +1,277 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# gdb helper commands and functions for Linux kernel debugging +# +# Kernel proc information reader +# +# Copyright (c) 2016 Linaro Ltd +# +# Authors: +# Kieran Bingham <kieran.bingham@linaro.org> +# +# This work is licensed under the terms of the GNU GPL version 2. +# + +import gdb +from linux import constants +from linux import utils +from linux import tasks +from linux import lists +from linux import vfs +from struct import * + + +class LxCmdLine(gdb.Command): + """ Report the Linux Commandline used in the current kernel. + Equivalent to cat /proc/cmdline on a running target""" + + def __init__(self): + super(LxCmdLine, self).__init__("lx-cmdline", gdb.COMMAND_DATA) + + def invoke(self, arg, from_tty): + gdb.write(gdb.parse_and_eval("saved_command_line").string() + "\n") + + +LxCmdLine() + + +class LxVersion(gdb.Command): + """ Report the Linux Version of the current kernel. + Equivalent to cat /proc/version on a running target""" + + def __init__(self): + super(LxVersion, self).__init__("lx-version", gdb.COMMAND_DATA) + + def invoke(self, arg, from_tty): + # linux_banner should contain a newline + gdb.write(gdb.parse_and_eval("(char *)linux_banner").string()) + + +LxVersion() + + +# Resource Structure Printers +# /proc/iomem +# /proc/ioports + +def get_resources(resource, depth): + while resource: + yield resource, depth + + child = resource['child'] + if child: + for res, deep in get_resources(child, depth + 1): + yield res, deep + + resource = resource['sibling'] + + +def show_lx_resources(resource_str): + resource = gdb.parse_and_eval(resource_str) + width = 4 if resource['end'] < 0x10000 else 8 + # Iterate straight to the first child + for res, depth in get_resources(resource['child'], 0): + start = int(res['start']) + end = int(res['end']) + gdb.write(" " * depth * 2 + + "{0:0{1}x}-".format(start, width) + + "{0:0{1}x} : ".format(end, width) + + res['name'].string() + "\n") + + +class LxIOMem(gdb.Command): + """Identify the IO memory resource locations defined by the kernel + +Equivalent to cat /proc/iomem on a running target""" + + def __init__(self): + super(LxIOMem, self).__init__("lx-iomem", gdb.COMMAND_DATA) + + def invoke(self, arg, from_tty): + return show_lx_resources("iomem_resource") + + +LxIOMem() + + +class LxIOPorts(gdb.Command): + """Identify the IO port resource locations defined by the kernel + +Equivalent to cat /proc/ioports on a running target""" + + def __init__(self): + super(LxIOPorts, self).__init__("lx-ioports", gdb.COMMAND_DATA) + + def invoke(self, arg, from_tty): + return show_lx_resources("ioport_resource") + + +LxIOPorts() + + +# Mount namespace viewer +# /proc/mounts + +def info_opts(lst, opt): + opts = "" + for key, string in lst.items(): + if opt & key: + opts += string + return opts + + +FS_INFO = {constants.LX_SB_SYNCHRONOUS: ",sync", + constants.LX_SB_MANDLOCK: ",mand", + constants.LX_SB_DIRSYNC: ",dirsync", + constants.LX_SB_NOATIME: ",noatime", + constants.LX_SB_NODIRATIME: ",nodiratime"} + +MNT_INFO = {constants.LX_MNT_NOSUID: ",nosuid", + constants.LX_MNT_NODEV: ",nodev", + constants.LX_MNT_NOEXEC: ",noexec", + constants.LX_MNT_NOATIME: ",noatime", + constants.LX_MNT_NODIRATIME: ",nodiratime", + constants.LX_MNT_RELATIME: ",relatime"} + +mount_type = utils.CachedType("struct mount") +mount_ptr_type = mount_type.get_type().pointer() + + +class LxMounts(gdb.Command): + """Report the VFS mounts of the current process namespace. + +Equivalent to cat /proc/mounts on a running target +An integer value can be supplied to display the mount +values of that process namespace""" + + def __init__(self): + super(LxMounts, self).__init__("lx-mounts", gdb.COMMAND_DATA) + + # Equivalent to proc_namespace.c:show_vfsmnt + # However, that has the ability to call into s_op functions + # whereas we cannot and must make do with the information we can obtain. + def invoke(self, arg, from_tty): + argv = gdb.string_to_argv(arg) + if len(argv) >= 1: + try: + pid = int(argv[0]) + except gdb.error: + raise gdb.GdbError("Provide a PID as integer value") + else: + pid = 1 + + task = tasks.get_task_by_pid(pid) + if not task: + raise gdb.GdbError("Couldn't find a process with PID {}" + .format(pid)) + + namespace = task['nsproxy']['mnt_ns'] + if not namespace: + raise gdb.GdbError("No namespace for current process") + + gdb.write("{:^18} {:^15} {:>9} {} {} options\n".format( + "mount", "super_block", "devname", "pathname", "fstype")) + + for mnt in lists.list_for_each_entry(namespace['list'], + mount_ptr_type, "mnt_list"): + devname = mnt['mnt_devname'].string() + devname = devname if devname else "none" + + pathname = "" + parent = mnt + while True: + mntpoint = parent['mnt_mountpoint'] + pathname = vfs.dentry_name(mntpoint) + pathname + if (parent == parent['mnt_parent']): + break + parent = parent['mnt_parent'] + + if (pathname == ""): + pathname = "/" + + superblock = mnt['mnt']['mnt_sb'] + fstype = superblock['s_type']['name'].string() + s_flags = int(superblock['s_flags']) + m_flags = int(mnt['mnt']['mnt_flags']) + rd = "ro" if (s_flags & constants.LX_SB_RDONLY) else "rw" + + gdb.write("{} {} {} {} {} {}{}{} 0 0\n".format( + mnt.format_string(), superblock.format_string(), devname, + pathname, fstype, rd, info_opts(FS_INFO, s_flags), + info_opts(MNT_INFO, m_flags))) + + +LxMounts() + + +class LxFdtDump(gdb.Command): + """Output Flattened Device Tree header and dump FDT blob to the filename + specified as the command argument. Equivalent to + 'cat /proc/fdt > fdtdump.dtb' on a running target""" + + def __init__(self): + super(LxFdtDump, self).__init__("lx-fdtdump", gdb.COMMAND_DATA, + gdb.COMPLETE_FILENAME) + + def fdthdr_to_cpu(self, fdt_header): + + fdt_header_be = ">IIIIIII" + fdt_header_le = "<IIIIIII" + + if utils.get_target_endianness() == 1: + output_fmt = fdt_header_le + else: + output_fmt = fdt_header_be + + return unpack(output_fmt, pack(fdt_header_be, + fdt_header['magic'], + fdt_header['totalsize'], + fdt_header['off_dt_struct'], + fdt_header['off_dt_strings'], + fdt_header['off_mem_rsvmap'], + fdt_header['version'], + fdt_header['last_comp_version'])) + + def invoke(self, arg, from_tty): + + if not constants.LX_CONFIG_OF: + raise gdb.GdbError("Kernel not compiled with CONFIG_OF\n") + + if len(arg) == 0: + filename = "fdtdump.dtb" + else: + filename = arg + + py_fdt_header_ptr = gdb.parse_and_eval( + "(const struct fdt_header *) initial_boot_params") + py_fdt_header = py_fdt_header_ptr.dereference() + + fdt_header = self.fdthdr_to_cpu(py_fdt_header) + + if fdt_header[0] != constants.LX_OF_DT_HEADER: + raise gdb.GdbError("No flattened device tree magic found\n") + + gdb.write("fdt_magic: 0x{:02X}\n".format(fdt_header[0])) + gdb.write("fdt_totalsize: 0x{:02X}\n".format(fdt_header[1])) + gdb.write("off_dt_struct: 0x{:02X}\n".format(fdt_header[2])) + gdb.write("off_dt_strings: 0x{:02X}\n".format(fdt_header[3])) + gdb.write("off_mem_rsvmap: 0x{:02X}\n".format(fdt_header[4])) + gdb.write("version: {}\n".format(fdt_header[5])) + gdb.write("last_comp_version: {}\n".format(fdt_header[6])) + + inf = gdb.inferiors()[0] + fdt_buf = utils.read_memoryview(inf, py_fdt_header_ptr, + fdt_header[1]).tobytes() + + try: + f = open(filename, 'wb') + except gdb.error: + raise gdb.GdbError("Could not open file to dump fdt") + + f.write(fdt_buf) + f.close() + + gdb.write("Dumped fdt blob to " + filename + "\n") + + +LxFdtDump() |