1# SPDX-License-Identifier: GPL-2.0 2# 3# gdb helper commands and functions for Linux kernel debugging 4# 5# Kernel proc information reader 6# 7# Copyright (c) 2016 Linaro Ltd 8# 9# Authors: 10# Kieran Bingham <kieran.bingham@linaro.org> 11# 12# This work is licensed under the terms of the GNU GPL version 2. 13# 14 15import gdb 16from linux import constants 17from linux import utils 18from linux import tasks 19from linux import lists 20from linux import vfs 21from struct import * 22 23 24class LxCmdLine(gdb.Command): 25 """ Report the Linux Commandline used in the current kernel. 26 Equivalent to cat /proc/cmdline on a running target""" 27 28 def __init__(self): 29 super(LxCmdLine, self).__init__("lx-cmdline", gdb.COMMAND_DATA) 30 31 def invoke(self, arg, from_tty): 32 gdb.write(gdb.parse_and_eval("saved_command_line").string() + "\n") 33 34 35LxCmdLine() 36 37 38class LxVersion(gdb.Command): 39 """ Report the Linux Version of the current kernel. 40 Equivalent to cat /proc/version on a running target""" 41 42 def __init__(self): 43 super(LxVersion, self).__init__("lx-version", gdb.COMMAND_DATA) 44 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()) 48 49 50LxVersion() 51 52 53# Resource Structure Printers 54# /proc/iomem 55# /proc/ioports 56 57def get_resources(resource, depth): 58 while resource: 59 yield resource, depth 60 61 child = resource['child'] 62 if child: 63 for res, deep in get_resources(child, depth + 1): 64 yield res, deep 65 66 resource = resource['sibling'] 67 68 69def 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']) 75 end = int(res['end']) 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") 80 81 82class LxIOMem(gdb.Command): 83 """Identify the IO memory resource locations defined by the kernel 84 85Equivalent to cat /proc/iomem on a running target""" 86 87 def __init__(self): 88 super(LxIOMem, self).__init__("lx-iomem", gdb.COMMAND_DATA) 89 90 def invoke(self, arg, from_tty): 91 return show_lx_resources("iomem_resource") 92 93 94LxIOMem() 95 96 97class LxIOPorts(gdb.Command): 98 """Identify the IO port resource locations defined by the kernel 99 100Equivalent to cat /proc/ioports on a running target""" 101 102 def __init__(self): 103 super(LxIOPorts, self).__init__("lx-ioports", gdb.COMMAND_DATA) 104 105 def invoke(self, arg, from_tty): 106 return show_lx_resources("ioport_resource") 107 108 109LxIOPorts() 110 111 112# Mount namespace viewer 113# /proc/mounts 114 115def info_opts(lst, opt): 116 opts = "" 117 for key, string in lst.items(): 118 if opt & key: 119 opts += string 120 return opts 121 122 123FS_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"} 128 129MNT_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"} 135 136mount_type = utils.CachedType("struct mount") 137mount_ptr_type = mount_type.get_type().pointer() 138 139 140class LxMounts(gdb.Command): 141 """Report the VFS mounts of the current process namespace. 142 143Equivalent to cat /proc/mounts on a running target 144An integer value can be supplied to display the mount 145values of that process namespace""" 146 147 def __init__(self): 148 super(LxMounts, self).__init__("lx-mounts", gdb.COMMAND_DATA) 149 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) 155 if len(argv) >= 1: 156 try: 157 pid = int(argv[0]) 158 except gdb.error: 159 raise gdb.GdbError("Provide a PID as integer value") 160 else: 161 pid = 1 162 163 task = tasks.get_task_by_pid(pid) 164 if not task: 165 raise gdb.GdbError("Couldn't find a process with PID {}" 166 .format(pid)) 167 168 namespace = task['nsproxy']['mnt_ns'] 169 if not namespace: 170 raise gdb.GdbError("No namespace for current process") 171 172 gdb.write("{:^18} {:^15} {:>9} {} {} options\n".format( 173 "mount", "super_block", "devname", "pathname", "fstype")) 174 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" 179 180 pathname = "" 181 parent = mnt 182 while True: 183 mntpoint = parent['mnt_mountpoint'] 184 pathname = vfs.dentry_name(mntpoint) + pathname 185 if (parent == parent['mnt_parent']): 186 break 187 parent = parent['mnt_parent'] 188 189 if (pathname == ""): 190 pathname = "/" 191 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" 197 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))) 202 203 204LxMounts() 205 206 207class 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""" 211 212 def __init__(self): 213 super(LxFdtDump, self).__init__("lx-fdtdump", gdb.COMMAND_DATA, 214 gdb.COMPLETE_FILENAME) 215 216 def fdthdr_to_cpu(self, fdt_header): 217 218 fdt_header_be = ">IIIIIII" 219 fdt_header_le = "<IIIIIII" 220 221 if utils.get_target_endianness() == 1: 222 output_fmt = fdt_header_le 223 else: 224 output_fmt = fdt_header_be 225 226 return unpack(output_fmt, pack(fdt_header_be, 227 fdt_header['magic'], 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'])) 234 235 def invoke(self, arg, from_tty): 236 237 if not constants.LX_CONFIG_OF: 238 raise gdb.GdbError("Kernel not compiled with CONFIG_OF\n") 239 240 if len(arg) == 0: 241 filename = "fdtdump.dtb" 242 else: 243 filename = arg 244 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() 248 249 fdt_header = self.fdthdr_to_cpu(py_fdt_header) 250 251 if fdt_header[0] != constants.LX_OF_DT_HEADER: 252 raise gdb.GdbError("No flattened device tree magic found\n") 253 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])) 261 262 inf = gdb.inferiors()[0] 263 fdt_buf = utils.read_memoryview(inf, py_fdt_header_ptr, 264 fdt_header[1]).tobytes() 265 266 try: 267 f = open(filename, 'wb') 268 except gdb.error: 269 raise gdb.GdbError("Could not open file to dump fdt") 270 271 f.write(fdt_buf) 272 f.close() 273 274 gdb.write("Dumped fdt blob to " + filename + "\n") 275 276 277LxFdtDump() 278