1#
2# gdb helper commands and functions for Linux kernel debugging
3#
4#  per-cpu tools
5#
6# Copyright (c) Siemens AG, 2011-2013
7#
8# Authors:
9#  Jan Kiszka <jan.kiszka@siemens.com>
10#
11# This work is licensed under the terms of the GNU GPL version 2.
12#
13
14import gdb
15
16from linux import tasks, utils
17
18
19task_type = utils.CachedType("struct task_struct")
20
21
22MAX_CPUS = 4096
23
24
25def get_current_cpu():
26    if utils.get_gdbserver_type() == utils.GDBSERVER_QEMU:
27        return gdb.selected_thread().num - 1
28    elif utils.get_gdbserver_type() == utils.GDBSERVER_KGDB:
29        return gdb.parse_and_eval("kgdb_active.counter")
30    else:
31        raise gdb.GdbError("Sorry, obtaining the current CPU is not yet "
32                           "supported with this gdb server.")
33
34
35def per_cpu(var_ptr, cpu):
36    if cpu == -1:
37        cpu = get_current_cpu()
38    if utils.is_target_arch("sparc:v9"):
39        offset = gdb.parse_and_eval(
40            "trap_block[{0}].__per_cpu_base".format(str(cpu)))
41    else:
42        try:
43            offset = gdb.parse_and_eval(
44                "__per_cpu_offset[{0}]".format(str(cpu)))
45        except gdb.error:
46            # !CONFIG_SMP case
47            offset = 0
48    pointer = var_ptr.cast(utils.get_long_type()) + offset
49    return pointer.cast(var_ptr.type).dereference()
50
51
52cpu_mask = {}
53
54
55def cpu_mask_invalidate(event):
56    global cpu_mask
57    cpu_mask = {}
58    gdb.events.stop.disconnect(cpu_mask_invalidate)
59    if hasattr(gdb.events, 'new_objfile'):
60        gdb.events.new_objfile.disconnect(cpu_mask_invalidate)
61
62
63def cpu_list(mask_name):
64    global cpu_mask
65    mask = None
66    if mask_name in cpu_mask:
67        mask = cpu_mask[mask_name]
68    if mask is None:
69        mask = gdb.parse_and_eval(mask_name + ".bits")
70        if hasattr(gdb, 'events'):
71            cpu_mask[mask_name] = mask
72            gdb.events.stop.connect(cpu_mask_invalidate)
73            if hasattr(gdb.events, 'new_objfile'):
74                gdb.events.new_objfile.connect(cpu_mask_invalidate)
75    bits_per_entry = mask[0].type.sizeof * 8
76    num_entries = mask.type.sizeof * 8 / bits_per_entry
77    entry = -1
78    bits = 0
79
80    while True:
81        while bits == 0:
82            entry += 1
83            if entry == num_entries:
84                return
85            bits = mask[entry]
86            if bits != 0:
87                bit = 0
88                break
89
90        while bits & 1 == 0:
91            bits >>= 1
92            bit += 1
93
94        cpu = entry * bits_per_entry + bit
95
96        bits >>= 1
97        bit += 1
98
99        yield int(cpu)
100
101
102def each_online_cpu():
103    for cpu in cpu_list("__cpu_online_mask"):
104        yield cpu
105
106
107def each_present_cpu():
108    for cpu in cpu_list("__cpu_present_mask"):
109        yield cpu
110
111
112def each_possible_cpu():
113    for cpu in cpu_list("__cpu_possible_mask"):
114        yield cpu
115
116
117def each_active_cpu():
118    for cpu in cpu_list("__cpu_active_mask"):
119        yield cpu
120
121
122class LxCpus(gdb.Command):
123    """List CPU status arrays
124
125Displays the known state of each CPU based on the kernel masks
126and can help identify the state of hotplugged CPUs"""
127
128    def __init__(self):
129        super(LxCpus, self).__init__("lx-cpus", gdb.COMMAND_DATA)
130
131    def invoke(self, arg, from_tty):
132        gdb.write("Possible CPUs : {}\n".format(list(each_possible_cpu())))
133        gdb.write("Present CPUs  : {}\n".format(list(each_present_cpu())))
134        gdb.write("Online CPUs   : {}\n".format(list(each_online_cpu())))
135        gdb.write("Active CPUs   : {}\n".format(list(each_active_cpu())))
136
137
138LxCpus()
139
140
141class PerCpu(gdb.Function):
142    """Return per-cpu variable.
143
144$lx_per_cpu("VAR"[, CPU]): Return the per-cpu variable called VAR for the
145given CPU number. If CPU is omitted, the CPU of the current context is used.
146Note that VAR has to be quoted as string."""
147
148    def __init__(self):
149        super(PerCpu, self).__init__("lx_per_cpu")
150
151    def invoke(self, var, cpu=-1):
152        return per_cpu(var.address, cpu)
153
154
155PerCpu()
156
157def get_current_task(cpu):
158    task_ptr_type = task_type.get_type().pointer()
159
160    if utils.is_target_arch("x86"):
161        if gdb.lookup_global_symbol("cpu_tasks"):
162            # This is a UML kernel, which stores the current task
163            # differently than other x86 sub architectures
164            var_ptr = gdb.parse_and_eval("(struct task_struct *)cpu_tasks[0].task")
165            return var_ptr.dereference()
166        else:
167            var_ptr = gdb.parse_and_eval("&pcpu_hot.current_task")
168            return per_cpu(var_ptr, cpu).dereference()
169    elif utils.is_target_arch("aarch64"):
170        current_task_addr = gdb.parse_and_eval("$SP_EL0")
171        if (current_task_addr >> 63) != 0:
172            current_task = current_task_addr.cast(task_ptr_type)
173            return current_task.dereference()
174        else:
175            raise gdb.GdbError("Sorry, obtaining the current task is not allowed "
176                               "while running in userspace(EL0)")
177    elif utils.is_target_arch("riscv"):
178        current_tp = gdb.parse_and_eval("$tp")
179        scratch_reg = gdb.parse_and_eval("$sscratch")
180
181        # by default tp points to current task
182        current_task = current_tp.cast(task_ptr_type)
183
184        # scratch register is set 0 in trap handler after entering kernel.
185        # When hart is in user mode, scratch register is pointing to task_struct.
186        # and tp is used by user mode. So when scratch register holds larger value
187        # (negative address as ulong is larger value) than tp, then use scratch register.
188        if (scratch_reg.cast(utils.get_ulong_type()) > current_tp.cast(utils.get_ulong_type())):
189            current_task = scratch_reg.cast(task_ptr_type)
190
191        return current_task.dereference()
192    else:
193        raise gdb.GdbError("Sorry, obtaining the current task is not yet "
194                           "supported with this arch")
195
196class LxCurrentFunc(gdb.Function):
197    """Return current task.
198
199$lx_current([CPU]): Return the per-cpu task variable for the given CPU
200number. If CPU is omitted, the CPU of the current context is used."""
201
202    def __init__(self):
203        super(LxCurrentFunc, self).__init__("lx_current")
204
205    def invoke(self, cpu=-1):
206        return get_current_task(cpu)
207
208
209LxCurrentFunc()
210