1// SPDX-License-Identifier: GPL-3.0-or-later
2
3#include "mos/assert.hpp"
4#include "mos/misc/kallsyms.hpp"
5#include "mos/platform/platform.hpp"
6#include "mos/tasks/task_types.hpp"
7
8#include <mos/types.hpp>
9#include <mos_stdio.hpp>
10
11static size_t do_print_vmflags(char *buf, size_t size, VMFlags flags)
12{
13 return snprintf(str: buf, size, format: "%c%c%c", flags.test(b: VM_READ) ? 'r' : '-', flags.test(b: VM_WRITE) ? 'w' : '-', flags.test(b: VM_EXEC) ? 'x' : '-');
14}
15
16/**
17 * @brief Kernel's extension to vsnprintf, '%p' format specifier.
18 *
19 * @details Supported extensions are listed below:
20 *
21 * '%ps' prints a kernel symbol name, with offset if applicable.
22 * e.g. "do_fork (+0x123)"
23 * '%pt' prints a thread_t object.
24 * e.g.: "[p123:my_process]"
25 * '%pp' prints a process_t object.
26 * e.g.: "[t123:my_thread]"
27 * '%pvf' prints VMFlags_t flag, only the r/w/x bits are printed for general purpose.
28 * e.g.: "rwx" / "r--" / "rw-" / "--x"
29 * '%pvm' prints a vmap_t object.
30 * e.g.: "{ 0x123000-0x123fff, rwx, on_fault=0x12345678 }"
31 * '%pio' prints an IO object.
32 * e.g.: "{ 'file.txt', offset=0x12345678 }"
33 *
34 * @returns true if the format specifier is handled, false otherwise, the
35 * caller should fallback to the default implementation. (i.e. %p)
36 *
37 */
38
39size_t vsnprintf_do_pointer_kernel(char *buf, size_t *size, const char **pformat, ptr_t ptr)
40{
41 size_t ret = 0;
42#define current (**pformat)
43#define peek_next (*(*pformat + 1))
44#define shift_next ((void) ((*pformat)++))
45#define unshift_next ((void) ((*pformat)--))
46
47#define wrap_print(...) wrap_printed(snprintf(buf, *size, __VA_ARGS__))
48#define wrap_printed(x) \
49 do \
50 { \
51 const size_t _printed = x; \
52 buf += _printed, ret += _printed; \
53 } while (0)
54
55 if (current != 'p')
56 goto done;
57
58#define null_check() \
59 do \
60 { \
61 if (unlikely(ptr == 0)) \
62 { \
63 wrap_print("(null)"); \
64 goto done; \
65 } \
66 } while (0)
67
68 switch (peek_next)
69 {
70 case 'i':
71 {
72 shift_next;
73 switch (peek_next)
74 {
75 case 'o':
76 {
77 shift_next;
78 // IO
79 const IO *io = (const IO *) ptr;
80 if (io == NULL)
81 {
82 wrap_print("(null)");
83 goto done;
84 }
85
86 const auto name = io->name();
87 wrap_print("{ '%s'", name.c_str());
88 if (!IO::IsValid(io))
89 wrap_print(", invalid");
90 wrap_print(" }");
91 goto done;
92 }
93 default: return false;
94 }
95
96 MOS_UNREACHABLE();
97 }
98 case 's': // %ps
99 {
100 shift_next;
101 // kernel symbol address
102 const kallsyms_t *sym = kallsyms_get_symbol(addr: ptr);
103 if (!sym)
104 {
105 wrap_print("(unknown)");
106 goto done;
107 }
108
109 const off_t off = ptr - sym->address;
110 if (off)
111 wrap_print("%s (+0x%zx)", sym ? sym->name : "(unknown)", off);
112 else
113 wrap_print("%s", sym ? sym->name : "(unknown)");
114 goto done;
115 }
116 case 't': // %pt
117 {
118 shift_next;
119 // thread_t
120 null_check();
121 const auto thread = (Thread *) ptr;
122 if (thread == NULL)
123 {
124 wrap_print("(null)");
125 goto done;
126 }
127 MOS_ASSERT_X(Thread::IsValid(thread), "thread is invalid");
128 wrap_print("[t%d:%s]", thread->tid, thread->name.empty() ? "<no name>" : thread->name.data());
129 goto done;
130 }
131 case 'p': // %pp
132 {
133 shift_next;
134 // process_t
135 null_check();
136 const Process *process = (const Process *) ptr;
137 if (process == NULL)
138 {
139 wrap_print("(null)");
140 goto done;
141 }
142 MOS_ASSERT_X(Process::IsValid(process), "process is invalid");
143 wrap_print("[p%d:%s]", process->pid, process->name.empty() ? "<no name>" : process->name.data());
144 goto done;
145 }
146 case 'v': // %pv
147 {
148 shift_next;
149
150 // vmap-related types
151 switch (peek_next)
152 {
153 case 'f': // %pvf
154 {
155 shift_next;
156 // VMFlags, only r/w/x are supported
157 const VMFlags flags = *(VMFlags *) ptr;
158 wrap_printed(do_print_vmflags(buf, *size, flags));
159 goto done;
160 }
161 case 'm': // %pvm
162 {
163 shift_next;
164 // vmap_t *, print the vmap's range and flags
165 const vmap_t *vmap = (const vmap_t *) ptr;
166 if (vmap == NULL)
167 {
168 wrap_print("(null)");
169 goto done;
170 }
171
172 wrap_print("{ [" PTR_VLFMT " - " PTR_VLFMT "], ", vmap->vaddr, vmap->vaddr + vmap->npages * MOS_PAGE_SIZE - 1);
173 wrap_printed(do_print_vmflags(buf, *size, vmap->vmflags));
174 wrap_print(", fault: %ps", (void *) (ptr_t) vmap->on_fault);
175
176 if (vmap->io)
177 {
178 const auto name = vmap->io->name();
179 wrap_print(", io: '%s', offset: 0x%zx", name.c_str(), vmap->io_offset);
180 }
181
182 wrap_print(" }");
183 goto done;
184 }
185 default: return false;
186 }
187 }
188 default: return false;
189 }
190
191done:
192 return ret;
193}
194