]>
Commit | Line | Data |
---|---|---|
e3b3d0f5 | 1 | // SPDX-License-Identifier: GPL-2.0 |
f2d937f3 JW |
2 | /* |
3 | * Based on the same principle as kgdboe using the NETPOLL api, this | |
4 | * driver uses a console polling api to implement a gdb serial inteface | |
5 | * which is multiplexed on a console port. | |
6 | * | |
7 | * Maintainer: Jason Wessel <jason.wessel@windriver.com> | |
8 | * | |
9 | * 2007-2008 (c) Jason Wessel - Wind River Systems, Inc. | |
f2d937f3 | 10 | */ |
39724d56 HZ |
11 | |
12 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | |
13 | ||
f2d937f3 JW |
14 | #include <linux/kernel.h> |
15 | #include <linux/ctype.h> | |
16 | #include <linux/kgdb.h> | |
ada64e4c | 17 | #include <linux/kdb.h> |
f2d937f3 | 18 | #include <linux/tty.h> |
efe2f29e | 19 | #include <linux/console.h> |
408a4be1 | 20 | #include <linux/vt_kern.h> |
111c1823 | 21 | #include <linux/input.h> |
578b9ce0 | 22 | #include <linux/module.h> |
f2d937f3 JW |
23 | |
24 | #define MAX_CONFIG_LEN 40 | |
25 | ||
26 | static struct kgdb_io kgdboc_io_ops; | |
27 | ||
28 | /* -1 = init not run yet, 0 = unconfigured, 1 = configured. */ | |
29 | static int configured = -1; | |
30 | ||
31 | static char config[MAX_CONFIG_LEN]; | |
32 | static struct kparam_string kps = { | |
33 | .string = config, | |
34 | .maxlen = MAX_CONFIG_LEN, | |
35 | }; | |
36 | ||
408a4be1 | 37 | static int kgdboc_use_kms; /* 1 if we use kernel mode switching */ |
f2d937f3 JW |
38 | static struct tty_driver *kgdb_tty_driver; |
39 | static int kgdb_tty_line; | |
40 | ||
ada64e4c | 41 | #ifdef CONFIG_KDB_KEYBOARD |
111c1823 DT |
42 | static int kgdboc_reset_connect(struct input_handler *handler, |
43 | struct input_dev *dev, | |
44 | const struct input_device_id *id) | |
45 | { | |
46 | input_reset_device(dev); | |
47 | ||
9e03aa2f | 48 | /* Return an error - we do not want to bind, just to reset */ |
111c1823 DT |
49 | return -ENODEV; |
50 | } | |
51 | ||
52 | static void kgdboc_reset_disconnect(struct input_handle *handle) | |
53 | { | |
54 | /* We do not expect anyone to actually bind to us */ | |
55 | BUG(); | |
56 | } | |
57 | ||
58 | static const struct input_device_id kgdboc_reset_ids[] = { | |
59 | { | |
60 | .flags = INPUT_DEVICE_ID_MATCH_EVBIT, | |
61 | .evbit = { BIT_MASK(EV_KEY) }, | |
62 | }, | |
63 | { } | |
64 | }; | |
65 | ||
66 | static struct input_handler kgdboc_reset_handler = { | |
67 | .connect = kgdboc_reset_connect, | |
68 | .disconnect = kgdboc_reset_disconnect, | |
69 | .name = "kgdboc_reset", | |
70 | .id_table = kgdboc_reset_ids, | |
71 | }; | |
72 | ||
73 | static DEFINE_MUTEX(kgdboc_reset_mutex); | |
74 | ||
75 | static void kgdboc_restore_input_helper(struct work_struct *dummy) | |
76 | { | |
77 | /* | |
78 | * We need to take a mutex to prevent several instances of | |
79 | * this work running on different CPUs so they don't try | |
80 | * to register again already registered handler. | |
81 | */ | |
82 | mutex_lock(&kgdboc_reset_mutex); | |
83 | ||
84 | if (input_register_handler(&kgdboc_reset_handler) == 0) | |
85 | input_unregister_handler(&kgdboc_reset_handler); | |
86 | ||
87 | mutex_unlock(&kgdboc_reset_mutex); | |
88 | } | |
89 | ||
90 | static DECLARE_WORK(kgdboc_restore_input_work, kgdboc_restore_input_helper); | |
91 | ||
92 | static void kgdboc_restore_input(void) | |
93 | { | |
8863ada9 JW |
94 | if (likely(system_state == SYSTEM_RUNNING)) |
95 | schedule_work(&kgdboc_restore_input_work); | |
111c1823 DT |
96 | } |
97 | ||
ada64e4c JW |
98 | static int kgdboc_register_kbd(char **cptr) |
99 | { | |
24b8592e JW |
100 | if (strncmp(*cptr, "kbd", 3) == 0 || |
101 | strncmp(*cptr, "kdb", 3) == 0) { | |
ada64e4c JW |
102 | if (kdb_poll_idx < KDB_POLL_FUNC_MAX) { |
103 | kdb_poll_funcs[kdb_poll_idx] = kdb_get_kbd_char; | |
104 | kdb_poll_idx++; | |
105 | if (cptr[0][3] == ',') | |
106 | *cptr += 4; | |
107 | else | |
108 | return 1; | |
109 | } | |
110 | } | |
111 | return 0; | |
112 | } | |
113 | ||
114 | static void kgdboc_unregister_kbd(void) | |
115 | { | |
116 | int i; | |
117 | ||
118 | for (i = 0; i < kdb_poll_idx; i++) { | |
119 | if (kdb_poll_funcs[i] == kdb_get_kbd_char) { | |
120 | kdb_poll_idx--; | |
121 | kdb_poll_funcs[i] = kdb_poll_funcs[kdb_poll_idx]; | |
122 | kdb_poll_funcs[kdb_poll_idx] = NULL; | |
123 | i--; | |
124 | } | |
125 | } | |
43829731 | 126 | flush_work(&kgdboc_restore_input_work); |
ada64e4c JW |
127 | } |
128 | #else /* ! CONFIG_KDB_KEYBOARD */ | |
129 | #define kgdboc_register_kbd(x) 0 | |
130 | #define kgdboc_unregister_kbd() | |
111c1823 | 131 | #define kgdboc_restore_input() |
ada64e4c JW |
132 | #endif /* ! CONFIG_KDB_KEYBOARD */ |
133 | ||
ada64e4c JW |
134 | static void cleanup_kgdboc(void) |
135 | { | |
0c57dfcc AV |
136 | if (kgdb_unregister_nmi_console()) |
137 | return; | |
ada64e4c JW |
138 | kgdboc_unregister_kbd(); |
139 | if (configured == 1) | |
140 | kgdb_unregister_io_module(&kgdboc_io_ops); | |
141 | } | |
142 | ||
f2d937f3 JW |
143 | static int configure_kgdboc(void) |
144 | { | |
145 | struct tty_driver *p; | |
146 | int tty_line = 0; | |
2dd45316 | 147 | int err = -ENODEV; |
ada64e4c | 148 | char *cptr = config; |
efe2f29e | 149 | struct console *cons; |
f2d937f3 | 150 | |
2dd45316 | 151 | if (!strlen(config) || isspace(config[0])) |
f2d937f3 JW |
152 | goto noconfig; |
153 | ||
efe2f29e | 154 | kgdboc_io_ops.is_console = 0; |
ada64e4c JW |
155 | kgdb_tty_driver = NULL; |
156 | ||
408a4be1 JW |
157 | kgdboc_use_kms = 0; |
158 | if (strncmp(cptr, "kms,", 4) == 0) { | |
159 | cptr += 4; | |
160 | kgdboc_use_kms = 1; | |
161 | } | |
162 | ||
ada64e4c JW |
163 | if (kgdboc_register_kbd(&cptr)) |
164 | goto do_register; | |
f2d937f3 | 165 | |
ada64e4c | 166 | p = tty_find_polling_driver(cptr, &tty_line); |
f2d937f3 JW |
167 | if (!p) |
168 | goto noconfig; | |
169 | ||
efe2f29e JW |
170 | cons = console_drivers; |
171 | while (cons) { | |
172 | int idx; | |
173 | if (cons->device && cons->device(cons, &idx) == p && | |
174 | idx == tty_line) { | |
175 | kgdboc_io_ops.is_console = 1; | |
176 | break; | |
177 | } | |
178 | cons = cons->next; | |
179 | } | |
180 | ||
f2d937f3 JW |
181 | kgdb_tty_driver = p; |
182 | kgdb_tty_line = tty_line; | |
183 | ||
ada64e4c | 184 | do_register: |
f2d937f3 JW |
185 | err = kgdb_register_io_module(&kgdboc_io_ops); |
186 | if (err) | |
187 | goto noconfig; | |
188 | ||
0c57dfcc AV |
189 | err = kgdb_register_nmi_console(); |
190 | if (err) | |
191 | goto nmi_con_failed; | |
192 | ||
f2d937f3 JW |
193 | configured = 1; |
194 | ||
195 | return 0; | |
196 | ||
0c57dfcc AV |
197 | nmi_con_failed: |
198 | kgdb_unregister_io_module(&kgdboc_io_ops); | |
f2d937f3 | 199 | noconfig: |
0c57dfcc | 200 | kgdboc_unregister_kbd(); |
f2d937f3 JW |
201 | config[0] = 0; |
202 | configured = 0; | |
ada64e4c | 203 | cleanup_kgdboc(); |
f2d937f3 JW |
204 | |
205 | return err; | |
206 | } | |
207 | ||
208 | static int __init init_kgdboc(void) | |
209 | { | |
210 | /* Already configured? */ | |
211 | if (configured == 1) | |
212 | return 0; | |
213 | ||
214 | return configure_kgdboc(); | |
215 | } | |
216 | ||
f2d937f3 JW |
217 | static int kgdboc_get_char(void) |
218 | { | |
ada64e4c JW |
219 | if (!kgdb_tty_driver) |
220 | return -1; | |
f34d7a5b AC |
221 | return kgdb_tty_driver->ops->poll_get_char(kgdb_tty_driver, |
222 | kgdb_tty_line); | |
f2d937f3 JW |
223 | } |
224 | ||
225 | static void kgdboc_put_char(u8 chr) | |
226 | { | |
ada64e4c JW |
227 | if (!kgdb_tty_driver) |
228 | return; | |
f34d7a5b AC |
229 | kgdb_tty_driver->ops->poll_put_char(kgdb_tty_driver, |
230 | kgdb_tty_line, chr); | |
f2d937f3 JW |
231 | } |
232 | ||
e4dca7b7 KC |
233 | static int param_set_kgdboc_var(const char *kmessage, |
234 | const struct kernel_param *kp) | |
f2d937f3 | 235 | { |
dada6a43 | 236 | size_t len = strlen(kmessage); |
c191e5ad JW |
237 | |
238 | if (len >= MAX_CONFIG_LEN) { | |
39724d56 | 239 | pr_err("config string too long\n"); |
f2d937f3 JW |
240 | return -ENOSPC; |
241 | } | |
242 | ||
243 | /* Only copy in the string if the init function has not run yet */ | |
244 | if (configured < 0) { | |
245 | strcpy(config, kmessage); | |
246 | return 0; | |
247 | } | |
248 | ||
249 | if (kgdb_connected) { | |
39724d56 | 250 | pr_err("Cannot reconfigure while KGDB is connected.\n"); |
f2d937f3 JW |
251 | |
252 | return -EBUSY; | |
253 | } | |
254 | ||
255 | strcpy(config, kmessage); | |
c191e5ad | 256 | /* Chop out \n char as a result of echo */ |
dada6a43 | 257 | if (len && config[len - 1] == '\n') |
c191e5ad | 258 | config[len - 1] = '\0'; |
f2d937f3 JW |
259 | |
260 | if (configured == 1) | |
261 | cleanup_kgdboc(); | |
262 | ||
263 | /* Go and configure with the new params. */ | |
264 | return configure_kgdboc(); | |
265 | } | |
266 | ||
408a4be1 JW |
267 | static int dbg_restore_graphics; |
268 | ||
f2d937f3 JW |
269 | static void kgdboc_pre_exp_handler(void) |
270 | { | |
408a4be1 JW |
271 | if (!dbg_restore_graphics && kgdboc_use_kms) { |
272 | dbg_restore_graphics = 1; | |
273 | con_debug_enter(vc_cons[fg_console].d); | |
274 | } | |
f2d937f3 JW |
275 | /* Increment the module count when the debugger is active */ |
276 | if (!kgdb_connected) | |
277 | try_module_get(THIS_MODULE); | |
278 | } | |
279 | ||
280 | static void kgdboc_post_exp_handler(void) | |
281 | { | |
282 | /* decrement the module count when the debugger detaches */ | |
283 | if (!kgdb_connected) | |
284 | module_put(THIS_MODULE); | |
408a4be1 JW |
285 | if (kgdboc_use_kms && dbg_restore_graphics) { |
286 | dbg_restore_graphics = 0; | |
287 | con_debug_leave(); | |
288 | } | |
111c1823 | 289 | kgdboc_restore_input(); |
f2d937f3 JW |
290 | } |
291 | ||
292 | static struct kgdb_io kgdboc_io_ops = { | |
293 | .name = "kgdboc", | |
294 | .read_char = kgdboc_get_char, | |
295 | .write_char = kgdboc_put_char, | |
296 | .pre_exception = kgdboc_pre_exp_handler, | |
297 | .post_exception = kgdboc_post_exp_handler, | |
298 | }; | |
299 | ||
9731191f | 300 | #ifdef CONFIG_KGDB_SERIAL_CONSOLE |
1cd25cbb LA |
301 | static int kgdboc_option_setup(char *opt) |
302 | { | |
303 | if (!opt) { | |
304 | pr_err("config string not provided\n"); | |
305 | return -EINVAL; | |
306 | } | |
307 | ||
308 | if (strlen(opt) >= MAX_CONFIG_LEN) { | |
309 | pr_err("config string too long\n"); | |
310 | return -ENOSPC; | |
311 | } | |
312 | strcpy(config, opt); | |
313 | ||
314 | return 0; | |
315 | } | |
316 | ||
317 | __setup("kgdboc=", kgdboc_option_setup); | |
318 | ||
319 | ||
9731191f | 320 | /* This is only available if kgdboc is a built in for early debugging */ |
91b152aa | 321 | static int __init kgdboc_early_init(char *opt) |
9731191f JW |
322 | { |
323 | /* save the first character of the config string because the | |
324 | * init routine can destroy it. | |
325 | */ | |
326 | char save_ch; | |
327 | ||
328 | kgdboc_option_setup(opt); | |
329 | save_ch = config[0]; | |
330 | init_kgdboc(); | |
331 | config[0] = save_ch; | |
332 | return 0; | |
333 | } | |
334 | ||
335 | early_param("ekgdboc", kgdboc_early_init); | |
336 | #endif /* CONFIG_KGDB_SERIAL_CONSOLE */ | |
337 | ||
f2d937f3 JW |
338 | module_init(init_kgdboc); |
339 | module_exit(cleanup_kgdboc); | |
340 | module_param_call(kgdboc, param_set_kgdboc_var, param_get_string, &kps, 0644); | |
341 | MODULE_PARM_DESC(kgdboc, "<serial_device>[,baud]"); | |
342 | MODULE_DESCRIPTION("KGDB Console TTY Driver"); | |
343 | MODULE_LICENSE("GPL"); |