1// SPDX-License-Identifier: GPL-3.0-or-later
2
3#include <dirent.h>
4#include <errno.h>
5#include <fcntl.h>
6#include <readline/libreadline.h>
7#include <stdio.h>
8#include <stdlib.h>
9#include <string.h>
10#include <unistd.h>
11
12#define BUFSIZE 4096
13
14void clear_console(void)
15{
16 printf(format: "\033[2J\033[1;1H");
17}
18
19static void print_file(FILE *f)
20{
21 do
22 {
23 char buffer[BUFSIZE] = { 0 };
24 size_t sz = fread(buffer, size: 1, BUFSIZE, stream: f);
25 if (sz == 0 || sz == (size_t) -1)
26 break;
27
28 fwrite(buffer, size: 1, count: sz, stream: stdout);
29 } while (true);
30}
31
32static void open_and_print_file(const char *path)
33{
34 FILE *f = fopen(filename: path, mode: "r");
35 if (!f)
36 {
37 fprintf(stream: stderr, format: "failed to open file '%s'\n", path);
38 return;
39 }
40
41 print_file(f);
42 fclose(stream: f);
43}
44static void do_prompt_rw(const char *prompt, const char *filename)
45{
46 while (true)
47 {
48 char *line = readline(prompt);
49 if (!line || strlen(s: line) == 0)
50 {
51 puts(string: "leaving...");
52 if (line)
53 free(pointer: line);
54 return;
55 }
56
57 FILE *f = fopen(filename, mode: "r+");
58 setbuf(stream: f, NULL);
59 if (!f)
60 {
61 fprintf(stream: stderr, format: "failed to open '%s'\n", filename);
62 free(pointer: line);
63 return;
64 }
65
66 fprintf(stream: f, format: "%s", line);
67 print_file(f);
68 fclose(stream: f);
69 free(pointer: line);
70 }
71}
72
73static void do_memstat(void)
74{
75 open_and_print_file(path: "/sys/mmstat/stat");
76}
77
78static void do_leave(void)
79{
80 exit(status: 0);
81}
82
83static FILE *open_debug_file(const char *module, const char *mode)
84{
85 char *path;
86 asprintf(&path, "/sys/debug/%s", module);
87 FILE *file = fopen(filename: path, mode);
88 if (!file)
89 fprintf(stream: stderr, format: "failed to open '%s'\n", path);
90 free(pointer: path);
91 return file;
92}
93
94static bool get_debug_value(const char *module)
95{
96 FILE *file = open_debug_file(module, mode: "r");
97 if (!file)
98 {
99 printf(format: "debug: cannot open '%s': %s\n", module, strerror(errno));
100 exit(status: 1);
101 }
102
103 int value;
104 fscanf(stream: file, format: "%d", &value);
105 fclose(stream: file);
106 return value;
107}
108
109static bool set_debug_value(const char *module, bool value)
110{
111 FILE *file = open_debug_file(module, mode: "w");
112 if (!file)
113 {
114 printf(format: "debug: cannot open '%s': %s\n", module, strerror(errno));
115 return false;
116 }
117
118 fprintf(stream: file, format: "%d", value);
119 fclose(stream: file);
120 return true;
121}
122
123static void do_help(void);
124
125static void do_pagetable()
126{
127 do_prompt_rw(prompt: "pid: ", filename: "/sys/mmstat/pagetable");
128}
129
130static void do_vmaps()
131{
132 do_prompt_rw(prompt: "pid: ", filename: "/sys/mmstat/vmaps");
133}
134
135static void do_pstat(void)
136{
137 do_prompt_rw(prompt: "pfn: ", filename: "/sys/mmstat/phyframe_stat");
138}
139
140const struct
141{
142 const char *name;
143 void (*func)(void);
144} actions[] = {
145 { "memstat", do_memstat }, //
146 { "q", do_leave }, //
147 { "leave", do_leave }, //
148 { "pstat", do_pstat }, //
149 { "pagetable", do_pagetable }, //
150 { "vmaps", do_vmaps }, //
151 { "help", do_help }, //
152 { "h", do_help }, //
153 { NULL, NULL }, //
154};
155
156void do_help()
157{
158 puts(string: "KD, the MOS kernel debugger.");
159 puts(string: "Available commands:");
160 for (int i = 0; actions[i].name; i++)
161 printf(format: " %s\n", actions[i].name);
162
163 puts(string: "");
164 puts(string: "Also, you can use 'kd' to enable/disable debug modules.");
165 puts(string: "Usage:");
166 puts(string: " kd -l [<module>] list all, or get the status of a debug module");
167 puts(string: " kd <module> <on|off> enable/disable a debug module");
168}
169
170int main(int argc, const char *argv[])
171{
172 if (argc == 2 && strcmp(a: argv[1], b: "-h") == 0)
173 do_help();
174
175 if (argc == 2 && strcmp(a: argv[1], b: "-l") == 0)
176 {
177 // list all debug modules and their status
178 DIR *dir = opendir("/sys/debug");
179 if (!dir)
180 {
181 printf(format: "debug: cannot open '/sys/debug': %s\n", strerror(errno));
182 return 1;
183 }
184
185 struct dirent *entry;
186 while ((entry = readdir(dir)))
187 {
188 if (entry->d_type != DT_REG)
189 continue;
190
191 const bool on = get_debug_value(module: entry->d_name);
192 printf(format: "%s: %s\n", entry->d_name, on ? "on" : "off");
193 }
194
195 closedir(dir);
196 return 0;
197 }
198
199 if (argc == 3)
200 {
201 if (strcmp(a: argv[1], b: "-l") == 0)
202 {
203 const char *module = argv[2];
204 const bool on = get_debug_value(module);
205 printf(format: "%s: %s\n", module, on ? "on" : "off");
206 }
207 else
208 {
209 const char *module = argv[1], *value = argv[2];
210
211 if (strcmp(a: value, b: "on") == 0 || strcmp(a: value, b: "1") == 0)
212 set_debug_value(module, value: true);
213 else if (strcmp(a: value, b: "off") == 0 || strcmp(a: value, b: "0") == 0)
214 set_debug_value(module, value: false);
215 else
216 printf(format: "debug: invalid value '%s'\n", value);
217 }
218 return 0;
219 }
220
221 // interactive mode
222 while (true)
223 {
224 const char *line = readline(prompt: "kd> ");
225 if (!line)
226 break;
227
228 if (strlen(s: line) == 0)
229 {
230 free(pointer: (void *) line);
231 continue;
232 }
233
234 bool handled = false;
235 for (int i = 0; actions[i].name; i++)
236 {
237 if (!strcmp(a: line, b: actions[i].name))
238 {
239 handled = true;
240 actions[i].func();
241 free(pointer: (void *) line);
242 break;
243 }
244 }
245
246 if (!handled)
247 {
248 if (strlen(s: line))
249 puts(string: "unknown command");
250 free(pointer: (void *) line);
251 }
252 }
253
254 return 0;
255}
256