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