1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * KGDB NMI serial console
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright 2010 Google, Inc.
6*4882a593Smuzhiyun * Arve Hjønnevåg <arve@android.com>
7*4882a593Smuzhiyun * Colin Cross <ccross@android.com>
8*4882a593Smuzhiyun * Copyright 2012 Linaro Ltd.
9*4882a593Smuzhiyun * Anton Vorontsov <anton.vorontsov@linaro.org>
10*4882a593Smuzhiyun */
11*4882a593Smuzhiyun
12*4882a593Smuzhiyun #include <linux/kernel.h>
13*4882a593Smuzhiyun #include <linux/module.h>
14*4882a593Smuzhiyun #include <linux/compiler.h>
15*4882a593Smuzhiyun #include <linux/slab.h>
16*4882a593Smuzhiyun #include <linux/errno.h>
17*4882a593Smuzhiyun #include <linux/atomic.h>
18*4882a593Smuzhiyun #include <linux/console.h>
19*4882a593Smuzhiyun #include <linux/tty.h>
20*4882a593Smuzhiyun #include <linux/tty_driver.h>
21*4882a593Smuzhiyun #include <linux/tty_flip.h>
22*4882a593Smuzhiyun #include <linux/serial_core.h>
23*4882a593Smuzhiyun #include <linux/interrupt.h>
24*4882a593Smuzhiyun #include <linux/hrtimer.h>
25*4882a593Smuzhiyun #include <linux/tick.h>
26*4882a593Smuzhiyun #include <linux/kfifo.h>
27*4882a593Smuzhiyun #include <linux/kgdb.h>
28*4882a593Smuzhiyun #include <linux/kdb.h>
29*4882a593Smuzhiyun
30*4882a593Smuzhiyun static int kgdb_nmi_knock = 1;
31*4882a593Smuzhiyun module_param_named(knock, kgdb_nmi_knock, int, 0600);
32*4882a593Smuzhiyun MODULE_PARM_DESC(knock, "if set to 1 (default), the special '$3#33' command " \
33*4882a593Smuzhiyun "must be used to enter the debugger; when set to 0, " \
34*4882a593Smuzhiyun "hitting return key is enough to enter the debugger; " \
35*4882a593Smuzhiyun "when set to -1, the debugger is entered immediately " \
36*4882a593Smuzhiyun "upon NMI");
37*4882a593Smuzhiyun
38*4882a593Smuzhiyun static char *kgdb_nmi_magic = "$3#33";
39*4882a593Smuzhiyun module_param_named(magic, kgdb_nmi_magic, charp, 0600);
40*4882a593Smuzhiyun MODULE_PARM_DESC(magic, "magic sequence to enter NMI debugger (default $3#33)");
41*4882a593Smuzhiyun
42*4882a593Smuzhiyun static atomic_t kgdb_nmi_num_readers = ATOMIC_INIT(0);
43*4882a593Smuzhiyun
kgdb_nmi_console_setup(struct console * co,char * options)44*4882a593Smuzhiyun static int kgdb_nmi_console_setup(struct console *co, char *options)
45*4882a593Smuzhiyun {
46*4882a593Smuzhiyun arch_kgdb_ops.enable_nmi(1);
47*4882a593Smuzhiyun
48*4882a593Smuzhiyun /* The NMI console uses the dbg_io_ops to issue console messages. To
49*4882a593Smuzhiyun * avoid duplicate messages during kdb sessions we must inform kdb's
50*4882a593Smuzhiyun * I/O utilities that messages sent to the console will automatically
51*4882a593Smuzhiyun * be displayed on the dbg_io.
52*4882a593Smuzhiyun */
53*4882a593Smuzhiyun dbg_io_ops->cons = co;
54*4882a593Smuzhiyun
55*4882a593Smuzhiyun return 0;
56*4882a593Smuzhiyun }
57*4882a593Smuzhiyun
kgdb_nmi_console_write(struct console * co,const char * s,uint c)58*4882a593Smuzhiyun static void kgdb_nmi_console_write(struct console *co, const char *s, uint c)
59*4882a593Smuzhiyun {
60*4882a593Smuzhiyun int i;
61*4882a593Smuzhiyun
62*4882a593Smuzhiyun for (i = 0; i < c; i++)
63*4882a593Smuzhiyun dbg_io_ops->write_char(s[i]);
64*4882a593Smuzhiyun }
65*4882a593Smuzhiyun
66*4882a593Smuzhiyun static struct tty_driver *kgdb_nmi_tty_driver;
67*4882a593Smuzhiyun
kgdb_nmi_console_device(struct console * co,int * idx)68*4882a593Smuzhiyun static struct tty_driver *kgdb_nmi_console_device(struct console *co, int *idx)
69*4882a593Smuzhiyun {
70*4882a593Smuzhiyun *idx = co->index;
71*4882a593Smuzhiyun return kgdb_nmi_tty_driver;
72*4882a593Smuzhiyun }
73*4882a593Smuzhiyun
74*4882a593Smuzhiyun static struct console kgdb_nmi_console = {
75*4882a593Smuzhiyun .name = "ttyNMI",
76*4882a593Smuzhiyun .setup = kgdb_nmi_console_setup,
77*4882a593Smuzhiyun .write = kgdb_nmi_console_write,
78*4882a593Smuzhiyun .device = kgdb_nmi_console_device,
79*4882a593Smuzhiyun .flags = CON_PRINTBUFFER | CON_ANYTIME,
80*4882a593Smuzhiyun .index = -1,
81*4882a593Smuzhiyun };
82*4882a593Smuzhiyun
83*4882a593Smuzhiyun /*
84*4882a593Smuzhiyun * This is usually the maximum rate on debug ports. We make fifo large enough
85*4882a593Smuzhiyun * to make copy-pasting to the terminal usable.
86*4882a593Smuzhiyun */
87*4882a593Smuzhiyun #define KGDB_NMI_BAUD 115200
88*4882a593Smuzhiyun #define KGDB_NMI_FIFO_SIZE roundup_pow_of_two(KGDB_NMI_BAUD / 8 / HZ)
89*4882a593Smuzhiyun
90*4882a593Smuzhiyun struct kgdb_nmi_tty_priv {
91*4882a593Smuzhiyun struct tty_port port;
92*4882a593Smuzhiyun struct timer_list timer;
93*4882a593Smuzhiyun STRUCT_KFIFO(char, KGDB_NMI_FIFO_SIZE) fifo;
94*4882a593Smuzhiyun };
95*4882a593Smuzhiyun
96*4882a593Smuzhiyun static struct tty_port *kgdb_nmi_port;
97*4882a593Smuzhiyun
kgdb_tty_recv(int ch)98*4882a593Smuzhiyun static void kgdb_tty_recv(int ch)
99*4882a593Smuzhiyun {
100*4882a593Smuzhiyun struct kgdb_nmi_tty_priv *priv;
101*4882a593Smuzhiyun char c = ch;
102*4882a593Smuzhiyun
103*4882a593Smuzhiyun if (!kgdb_nmi_port || ch < 0)
104*4882a593Smuzhiyun return;
105*4882a593Smuzhiyun /*
106*4882a593Smuzhiyun * Can't use port->tty->driver_data as tty might be not there. Timer
107*4882a593Smuzhiyun * will check for tty and will get the ref, but here we don't have to
108*4882a593Smuzhiyun * do that, and actually, we can't: we're in NMI context, no locks are
109*4882a593Smuzhiyun * possible.
110*4882a593Smuzhiyun */
111*4882a593Smuzhiyun priv = container_of(kgdb_nmi_port, struct kgdb_nmi_tty_priv, port);
112*4882a593Smuzhiyun kfifo_in(&priv->fifo, &c, 1);
113*4882a593Smuzhiyun }
114*4882a593Smuzhiyun
kgdb_nmi_poll_one_knock(void)115*4882a593Smuzhiyun static int kgdb_nmi_poll_one_knock(void)
116*4882a593Smuzhiyun {
117*4882a593Smuzhiyun static int n;
118*4882a593Smuzhiyun int c = -1;
119*4882a593Smuzhiyun const char *magic = kgdb_nmi_magic;
120*4882a593Smuzhiyun size_t m = strlen(magic);
121*4882a593Smuzhiyun bool printch = false;
122*4882a593Smuzhiyun
123*4882a593Smuzhiyun c = dbg_io_ops->read_char();
124*4882a593Smuzhiyun if (c == NO_POLL_CHAR)
125*4882a593Smuzhiyun return c;
126*4882a593Smuzhiyun
127*4882a593Smuzhiyun if (!kgdb_nmi_knock && (c == '\r' || c == '\n')) {
128*4882a593Smuzhiyun return 1;
129*4882a593Smuzhiyun } else if (c == magic[n]) {
130*4882a593Smuzhiyun n = (n + 1) % m;
131*4882a593Smuzhiyun if (!n)
132*4882a593Smuzhiyun return 1;
133*4882a593Smuzhiyun printch = true;
134*4882a593Smuzhiyun } else {
135*4882a593Smuzhiyun n = 0;
136*4882a593Smuzhiyun }
137*4882a593Smuzhiyun
138*4882a593Smuzhiyun if (atomic_read(&kgdb_nmi_num_readers)) {
139*4882a593Smuzhiyun kgdb_tty_recv(c);
140*4882a593Smuzhiyun return 0;
141*4882a593Smuzhiyun }
142*4882a593Smuzhiyun
143*4882a593Smuzhiyun if (printch) {
144*4882a593Smuzhiyun kdb_printf("%c", c);
145*4882a593Smuzhiyun return 0;
146*4882a593Smuzhiyun }
147*4882a593Smuzhiyun
148*4882a593Smuzhiyun kdb_printf("\r%s %s to enter the debugger> %*s",
149*4882a593Smuzhiyun kgdb_nmi_knock ? "Type" : "Hit",
150*4882a593Smuzhiyun kgdb_nmi_knock ? magic : "<return>", (int)m, "");
151*4882a593Smuzhiyun while (m--)
152*4882a593Smuzhiyun kdb_printf("\b");
153*4882a593Smuzhiyun return 0;
154*4882a593Smuzhiyun }
155*4882a593Smuzhiyun
156*4882a593Smuzhiyun /**
157*4882a593Smuzhiyun * kgdb_nmi_poll_knock - Check if it is time to enter the debugger
158*4882a593Smuzhiyun *
159*4882a593Smuzhiyun * "Serial ports are often noisy, especially when muxed over another port (we
160*4882a593Smuzhiyun * often use serial over the headset connector). Noise on the async command
161*4882a593Smuzhiyun * line just causes characters that are ignored, on a command line that blocked
162*4882a593Smuzhiyun * execution noise would be catastrophic." -- Colin Cross
163*4882a593Smuzhiyun *
164*4882a593Smuzhiyun * So, this function implements KGDB/KDB knocking on the serial line: we won't
165*4882a593Smuzhiyun * enter the debugger until we receive a known magic phrase (which is actually
166*4882a593Smuzhiyun * "$3#33", known as "escape to KDB" command. There is also a relaxed variant
167*4882a593Smuzhiyun * of knocking, i.e. just pressing the return key is enough to enter the
168*4882a593Smuzhiyun * debugger. And if knocking is disabled, the function always returns 1.
169*4882a593Smuzhiyun */
kgdb_nmi_poll_knock(void)170*4882a593Smuzhiyun bool kgdb_nmi_poll_knock(void)
171*4882a593Smuzhiyun {
172*4882a593Smuzhiyun if (kgdb_nmi_knock < 0)
173*4882a593Smuzhiyun return true;
174*4882a593Smuzhiyun
175*4882a593Smuzhiyun while (1) {
176*4882a593Smuzhiyun int ret;
177*4882a593Smuzhiyun
178*4882a593Smuzhiyun ret = kgdb_nmi_poll_one_knock();
179*4882a593Smuzhiyun if (ret == NO_POLL_CHAR)
180*4882a593Smuzhiyun return false;
181*4882a593Smuzhiyun else if (ret == 1)
182*4882a593Smuzhiyun break;
183*4882a593Smuzhiyun }
184*4882a593Smuzhiyun return true;
185*4882a593Smuzhiyun }
186*4882a593Smuzhiyun
187*4882a593Smuzhiyun /*
188*4882a593Smuzhiyun * The tasklet is cheap, it does not cause wakeups when reschedules itself,
189*4882a593Smuzhiyun * instead it waits for the next tick.
190*4882a593Smuzhiyun */
kgdb_nmi_tty_receiver(struct timer_list * t)191*4882a593Smuzhiyun static void kgdb_nmi_tty_receiver(struct timer_list *t)
192*4882a593Smuzhiyun {
193*4882a593Smuzhiyun struct kgdb_nmi_tty_priv *priv = from_timer(priv, t, timer);
194*4882a593Smuzhiyun char ch;
195*4882a593Smuzhiyun
196*4882a593Smuzhiyun priv->timer.expires = jiffies + (HZ/100);
197*4882a593Smuzhiyun add_timer(&priv->timer);
198*4882a593Smuzhiyun
199*4882a593Smuzhiyun if (likely(!atomic_read(&kgdb_nmi_num_readers) ||
200*4882a593Smuzhiyun !kfifo_len(&priv->fifo)))
201*4882a593Smuzhiyun return;
202*4882a593Smuzhiyun
203*4882a593Smuzhiyun while (kfifo_out(&priv->fifo, &ch, 1))
204*4882a593Smuzhiyun tty_insert_flip_char(&priv->port, ch, TTY_NORMAL);
205*4882a593Smuzhiyun tty_flip_buffer_push(&priv->port);
206*4882a593Smuzhiyun }
207*4882a593Smuzhiyun
kgdb_nmi_tty_activate(struct tty_port * port,struct tty_struct * tty)208*4882a593Smuzhiyun static int kgdb_nmi_tty_activate(struct tty_port *port, struct tty_struct *tty)
209*4882a593Smuzhiyun {
210*4882a593Smuzhiyun struct kgdb_nmi_tty_priv *priv =
211*4882a593Smuzhiyun container_of(port, struct kgdb_nmi_tty_priv, port);
212*4882a593Smuzhiyun
213*4882a593Smuzhiyun kgdb_nmi_port = port;
214*4882a593Smuzhiyun priv->timer.expires = jiffies + (HZ/100);
215*4882a593Smuzhiyun add_timer(&priv->timer);
216*4882a593Smuzhiyun
217*4882a593Smuzhiyun return 0;
218*4882a593Smuzhiyun }
219*4882a593Smuzhiyun
kgdb_nmi_tty_shutdown(struct tty_port * port)220*4882a593Smuzhiyun static void kgdb_nmi_tty_shutdown(struct tty_port *port)
221*4882a593Smuzhiyun {
222*4882a593Smuzhiyun struct kgdb_nmi_tty_priv *priv =
223*4882a593Smuzhiyun container_of(port, struct kgdb_nmi_tty_priv, port);
224*4882a593Smuzhiyun
225*4882a593Smuzhiyun del_timer(&priv->timer);
226*4882a593Smuzhiyun kgdb_nmi_port = NULL;
227*4882a593Smuzhiyun }
228*4882a593Smuzhiyun
229*4882a593Smuzhiyun static const struct tty_port_operations kgdb_nmi_tty_port_ops = {
230*4882a593Smuzhiyun .activate = kgdb_nmi_tty_activate,
231*4882a593Smuzhiyun .shutdown = kgdb_nmi_tty_shutdown,
232*4882a593Smuzhiyun };
233*4882a593Smuzhiyun
kgdb_nmi_tty_install(struct tty_driver * drv,struct tty_struct * tty)234*4882a593Smuzhiyun static int kgdb_nmi_tty_install(struct tty_driver *drv, struct tty_struct *tty)
235*4882a593Smuzhiyun {
236*4882a593Smuzhiyun struct kgdb_nmi_tty_priv *priv;
237*4882a593Smuzhiyun int ret;
238*4882a593Smuzhiyun
239*4882a593Smuzhiyun priv = kzalloc(sizeof(*priv), GFP_KERNEL);
240*4882a593Smuzhiyun if (!priv)
241*4882a593Smuzhiyun return -ENOMEM;
242*4882a593Smuzhiyun
243*4882a593Smuzhiyun INIT_KFIFO(priv->fifo);
244*4882a593Smuzhiyun timer_setup(&priv->timer, kgdb_nmi_tty_receiver, 0);
245*4882a593Smuzhiyun tty_port_init(&priv->port);
246*4882a593Smuzhiyun priv->port.ops = &kgdb_nmi_tty_port_ops;
247*4882a593Smuzhiyun tty->driver_data = priv;
248*4882a593Smuzhiyun
249*4882a593Smuzhiyun ret = tty_port_install(&priv->port, drv, tty);
250*4882a593Smuzhiyun if (ret) {
251*4882a593Smuzhiyun pr_err("%s: can't install tty port: %d\n", __func__, ret);
252*4882a593Smuzhiyun goto err;
253*4882a593Smuzhiyun }
254*4882a593Smuzhiyun return 0;
255*4882a593Smuzhiyun err:
256*4882a593Smuzhiyun tty_port_destroy(&priv->port);
257*4882a593Smuzhiyun kfree(priv);
258*4882a593Smuzhiyun return ret;
259*4882a593Smuzhiyun }
260*4882a593Smuzhiyun
kgdb_nmi_tty_cleanup(struct tty_struct * tty)261*4882a593Smuzhiyun static void kgdb_nmi_tty_cleanup(struct tty_struct *tty)
262*4882a593Smuzhiyun {
263*4882a593Smuzhiyun struct kgdb_nmi_tty_priv *priv = tty->driver_data;
264*4882a593Smuzhiyun
265*4882a593Smuzhiyun tty->driver_data = NULL;
266*4882a593Smuzhiyun tty_port_destroy(&priv->port);
267*4882a593Smuzhiyun kfree(priv);
268*4882a593Smuzhiyun }
269*4882a593Smuzhiyun
kgdb_nmi_tty_open(struct tty_struct * tty,struct file * file)270*4882a593Smuzhiyun static int kgdb_nmi_tty_open(struct tty_struct *tty, struct file *file)
271*4882a593Smuzhiyun {
272*4882a593Smuzhiyun struct kgdb_nmi_tty_priv *priv = tty->driver_data;
273*4882a593Smuzhiyun unsigned int mode = file->f_flags & O_ACCMODE;
274*4882a593Smuzhiyun int ret;
275*4882a593Smuzhiyun
276*4882a593Smuzhiyun ret = tty_port_open(&priv->port, tty, file);
277*4882a593Smuzhiyun if (!ret && (mode == O_RDONLY || mode == O_RDWR))
278*4882a593Smuzhiyun atomic_inc(&kgdb_nmi_num_readers);
279*4882a593Smuzhiyun
280*4882a593Smuzhiyun return ret;
281*4882a593Smuzhiyun }
282*4882a593Smuzhiyun
kgdb_nmi_tty_close(struct tty_struct * tty,struct file * file)283*4882a593Smuzhiyun static void kgdb_nmi_tty_close(struct tty_struct *tty, struct file *file)
284*4882a593Smuzhiyun {
285*4882a593Smuzhiyun struct kgdb_nmi_tty_priv *priv = tty->driver_data;
286*4882a593Smuzhiyun unsigned int mode = file->f_flags & O_ACCMODE;
287*4882a593Smuzhiyun
288*4882a593Smuzhiyun if (mode == O_RDONLY || mode == O_RDWR)
289*4882a593Smuzhiyun atomic_dec(&kgdb_nmi_num_readers);
290*4882a593Smuzhiyun
291*4882a593Smuzhiyun tty_port_close(&priv->port, tty, file);
292*4882a593Smuzhiyun }
293*4882a593Smuzhiyun
kgdb_nmi_tty_hangup(struct tty_struct * tty)294*4882a593Smuzhiyun static void kgdb_nmi_tty_hangup(struct tty_struct *tty)
295*4882a593Smuzhiyun {
296*4882a593Smuzhiyun struct kgdb_nmi_tty_priv *priv = tty->driver_data;
297*4882a593Smuzhiyun
298*4882a593Smuzhiyun tty_port_hangup(&priv->port);
299*4882a593Smuzhiyun }
300*4882a593Smuzhiyun
kgdb_nmi_tty_write_room(struct tty_struct * tty)301*4882a593Smuzhiyun static int kgdb_nmi_tty_write_room(struct tty_struct *tty)
302*4882a593Smuzhiyun {
303*4882a593Smuzhiyun /* Actually, we can handle any amount as we use polled writes. */
304*4882a593Smuzhiyun return 2048;
305*4882a593Smuzhiyun }
306*4882a593Smuzhiyun
kgdb_nmi_tty_write(struct tty_struct * tty,const unchar * buf,int c)307*4882a593Smuzhiyun static int kgdb_nmi_tty_write(struct tty_struct *tty, const unchar *buf, int c)
308*4882a593Smuzhiyun {
309*4882a593Smuzhiyun int i;
310*4882a593Smuzhiyun
311*4882a593Smuzhiyun for (i = 0; i < c; i++)
312*4882a593Smuzhiyun dbg_io_ops->write_char(buf[i]);
313*4882a593Smuzhiyun return c;
314*4882a593Smuzhiyun }
315*4882a593Smuzhiyun
316*4882a593Smuzhiyun static const struct tty_operations kgdb_nmi_tty_ops = {
317*4882a593Smuzhiyun .open = kgdb_nmi_tty_open,
318*4882a593Smuzhiyun .close = kgdb_nmi_tty_close,
319*4882a593Smuzhiyun .install = kgdb_nmi_tty_install,
320*4882a593Smuzhiyun .cleanup = kgdb_nmi_tty_cleanup,
321*4882a593Smuzhiyun .hangup = kgdb_nmi_tty_hangup,
322*4882a593Smuzhiyun .write_room = kgdb_nmi_tty_write_room,
323*4882a593Smuzhiyun .write = kgdb_nmi_tty_write,
324*4882a593Smuzhiyun };
325*4882a593Smuzhiyun
kgdb_register_nmi_console(void)326*4882a593Smuzhiyun int kgdb_register_nmi_console(void)
327*4882a593Smuzhiyun {
328*4882a593Smuzhiyun int ret;
329*4882a593Smuzhiyun
330*4882a593Smuzhiyun if (!arch_kgdb_ops.enable_nmi)
331*4882a593Smuzhiyun return 0;
332*4882a593Smuzhiyun
333*4882a593Smuzhiyun kgdb_nmi_tty_driver = alloc_tty_driver(1);
334*4882a593Smuzhiyun if (!kgdb_nmi_tty_driver) {
335*4882a593Smuzhiyun pr_err("%s: cannot allocate tty\n", __func__);
336*4882a593Smuzhiyun return -ENOMEM;
337*4882a593Smuzhiyun }
338*4882a593Smuzhiyun kgdb_nmi_tty_driver->driver_name = "ttyNMI";
339*4882a593Smuzhiyun kgdb_nmi_tty_driver->name = "ttyNMI";
340*4882a593Smuzhiyun kgdb_nmi_tty_driver->num = 1;
341*4882a593Smuzhiyun kgdb_nmi_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
342*4882a593Smuzhiyun kgdb_nmi_tty_driver->subtype = SERIAL_TYPE_NORMAL;
343*4882a593Smuzhiyun kgdb_nmi_tty_driver->flags = TTY_DRIVER_REAL_RAW;
344*4882a593Smuzhiyun kgdb_nmi_tty_driver->init_termios = tty_std_termios;
345*4882a593Smuzhiyun tty_termios_encode_baud_rate(&kgdb_nmi_tty_driver->init_termios,
346*4882a593Smuzhiyun KGDB_NMI_BAUD, KGDB_NMI_BAUD);
347*4882a593Smuzhiyun tty_set_operations(kgdb_nmi_tty_driver, &kgdb_nmi_tty_ops);
348*4882a593Smuzhiyun
349*4882a593Smuzhiyun ret = tty_register_driver(kgdb_nmi_tty_driver);
350*4882a593Smuzhiyun if (ret) {
351*4882a593Smuzhiyun pr_err("%s: can't register tty driver: %d\n", __func__, ret);
352*4882a593Smuzhiyun goto err_drv_reg;
353*4882a593Smuzhiyun }
354*4882a593Smuzhiyun
355*4882a593Smuzhiyun register_console(&kgdb_nmi_console);
356*4882a593Smuzhiyun
357*4882a593Smuzhiyun return 0;
358*4882a593Smuzhiyun err_drv_reg:
359*4882a593Smuzhiyun put_tty_driver(kgdb_nmi_tty_driver);
360*4882a593Smuzhiyun return ret;
361*4882a593Smuzhiyun }
362*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(kgdb_register_nmi_console);
363*4882a593Smuzhiyun
kgdb_unregister_nmi_console(void)364*4882a593Smuzhiyun int kgdb_unregister_nmi_console(void)
365*4882a593Smuzhiyun {
366*4882a593Smuzhiyun int ret;
367*4882a593Smuzhiyun
368*4882a593Smuzhiyun if (!arch_kgdb_ops.enable_nmi)
369*4882a593Smuzhiyun return 0;
370*4882a593Smuzhiyun arch_kgdb_ops.enable_nmi(0);
371*4882a593Smuzhiyun
372*4882a593Smuzhiyun ret = unregister_console(&kgdb_nmi_console);
373*4882a593Smuzhiyun if (ret)
374*4882a593Smuzhiyun return ret;
375*4882a593Smuzhiyun
376*4882a593Smuzhiyun ret = tty_unregister_driver(kgdb_nmi_tty_driver);
377*4882a593Smuzhiyun if (ret)
378*4882a593Smuzhiyun return ret;
379*4882a593Smuzhiyun put_tty_driver(kgdb_nmi_tty_driver);
380*4882a593Smuzhiyun
381*4882a593Smuzhiyun return 0;
382*4882a593Smuzhiyun }
383*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(kgdb_unregister_nmi_console);
384