root/kernel/debug/kdb/kdb_io.c
// SPDX-License-Identifier: GPL-2.0
/*
 * Kernel Debugger Architecture Independent Console I/O handler
 *
 * Copyright (c) 1999-2006 Silicon Graphics, Inc.  All Rights Reserved.
 * Copyright (c) 2009 Wind River Systems, Inc.  All Rights Reserved.
 */

#include <linux/types.h>
#include <linux/ctype.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/kdev_t.h>
#include <linux/console.h>
#include <linux/string.h>
#include <linux/sched.h>
#include <linux/smp.h>
#include <linux/nmi.h>
#include <linux/delay.h>
#include <linux/kgdb.h>
#include <linux/kdb.h>
#include <linux/kallsyms.h>
#include "kdb_private.h"

#define CMD_BUFLEN 256
char kdb_prompt_str[CMD_BUFLEN];

int kdb_trap_printk;
int kdb_printf_cpu = -1;

static int kgdb_transition_check(char *buffer)
{
        if (buffer[0] != '+' && buffer[0] != '$') {
                KDB_STATE_SET(KGDB_TRANS);
                kdb_printf("%s", buffer);
        } else {
                int slen = strlen(buffer);
                if (slen > 3 && buffer[slen - 3] == '#') {
                        kdb_gdb_state_pass(buffer);
                        strcpy(buffer, "kgdb");
                        KDB_STATE_SET(DOING_KGDB);
                        return 1;
                }
        }
        return 0;
}

/**
 * kdb_handle_escape() - validity check on an accumulated escape sequence.
 * @buf:        Accumulated escape characters to be examined. Note that buf
 *              is not a string, it is an array of characters and need not be
 *              nil terminated.
 * @sz:         Number of accumulated escape characters.
 *
 * Return: -1 if the escape sequence is unwanted, 0 if it is incomplete,
 * otherwise it returns a mapped key value to pass to the upper layers.
 */
static int kdb_handle_escape(char *buf, size_t sz)
{
        char *lastkey = buf + sz - 1;

        switch (sz) {
        case 1:
                if (*lastkey == '\e')
                        return 0;
                break;

        case 2: /* \e<something> */
                if (*lastkey == '[')
                        return 0;
                break;

        case 3:
                switch (*lastkey) {
                case 'A': /* \e[A, up arrow */
                        return 16;
                case 'B': /* \e[B, down arrow */
                        return 14;
                case 'C': /* \e[C, right arrow */
                        return 6;
                case 'D': /* \e[D, left arrow */
                        return 2;
                case '1': /* \e[<1,3,4>], may be home, del, end */
                case '3':
                case '4':
                        return 0;
                }
                break;

        case 4:
                if (*lastkey == '~') {
                        switch (buf[2]) {
                        case '1': /* \e[1~, home */
                                return 1;
                        case '3': /* \e[3~, del */
                                return 4;
                        case '4': /* \e[4~, end */
                                return 5;
                        }
                }
                break;
        }

        return -1;
}

/**
 * kdb_getchar() - Read a single character from a kdb console (or consoles).
 *
 * Other than polling the various consoles that are currently enabled,
 * most of the work done in this function is dealing with escape sequences.
 *
 * An escape key could be the start of a vt100 control sequence such as \e[D
 * (left arrow) or it could be a character in its own right.  The standard
 * method for detecting the difference is to wait for 2 seconds to see if there
 * are any other characters.  kdb is complicated by the lack of a timer service
 * (interrupts are off), by multiple input sources. Escape sequence processing
 * has to be done as states in the polling loop.
 *
 * Return: The key pressed or a control code derived from an escape sequence.
 */
char kdb_getchar(void)
{
#define ESCAPE_UDELAY 1000
#define ESCAPE_DELAY (2*1000000/ESCAPE_UDELAY) /* 2 seconds worth of udelays */
        char buf[4];    /* longest vt100 escape sequence is 4 bytes */
        char *pbuf = buf;
        int escape_delay = 0;
        get_char_func *f, *f_prev = NULL;
        int key;
        static bool last_char_was_cr;

        for (f = &kdb_poll_funcs[0]; ; ++f) {
                if (*f == NULL) {
                        /* Reset NMI watchdog once per poll loop */
                        touch_nmi_watchdog();
                        f = &kdb_poll_funcs[0];
                }

                key = (*f)();
                if (key == -1) {
                        if (escape_delay) {
                                udelay(ESCAPE_UDELAY);
                                if (--escape_delay == 0)
                                        return '\e';
                        }
                        continue;
                }

                /*
                 * The caller expects that newlines are either CR or LF. However
                 * some terminals send _both_ CR and LF. Avoid having to handle
                 * this in the caller by stripping the LF if we saw a CR right
                 * before.
                 */
                if (last_char_was_cr && key == '\n') {
                        last_char_was_cr = false;
                        continue;
                }
                last_char_was_cr = (key == '\r');

                /*
                 * When the first character is received (or we get a change
                 * input source) we set ourselves up to handle an escape
                 * sequences (just in case).
                 */
                if (f_prev != f) {
                        f_prev = f;
                        pbuf = buf;
                        escape_delay = ESCAPE_DELAY;
                }

                *pbuf++ = key;
                key = kdb_handle_escape(buf, pbuf - buf);
                if (key < 0) /* no escape sequence; return best character */
                        return buf[pbuf - buf == 2 ? 1 : 0];
                if (key > 0)
                        return key;
        }

        unreachable();
}

/**
 * kdb_position_cursor() - Place cursor in the correct horizontal position
 * @prompt: Nil-terminated string containing the prompt string
 * @buffer: Nil-terminated string containing the entire command line
 * @cp: Cursor position, pointer the character in buffer where the cursor
 *      should be positioned.
 *
 * The cursor is positioned by sending a carriage-return and then printing
 * the content of the line until we reach the correct cursor position.
 *
 * There is some additional fine detail here.
 *
 * Firstly, even though kdb_printf() will correctly format zero-width fields
 * we want the second call to kdb_printf() to be conditional. That keeps things
 * a little cleaner when LOGGING=1.
 *
 * Secondly, we can't combine everything into one call to kdb_printf() since
 * that renders into a fixed length buffer and the combined print could result
 * in unwanted truncation.
 */
static void kdb_position_cursor(char *prompt, char *buffer, char *cp)
{
        kdb_printf("\r%s", prompt);
        if (cp > buffer)
                kdb_printf("%.*s", (int)(cp - buffer), buffer);
}

/*
 * kdb_read
 *
 *      This function reads a string of characters, terminated by
 *      a newline, or by reaching the end of the supplied buffer,
 *      from the current kernel debugger console device.
 * Parameters:
 *      buffer  - Address of character buffer to receive input characters.
 *      bufsize - size, in bytes, of the character buffer
 * Returns:
 *      Returns a pointer to the buffer containing the received
 *      character string.  This string will be terminated by a
 *      newline character.
 * Locking:
 *      No locks are required to be held upon entry to this
 *      function.  It is not reentrant - it relies on the fact
 *      that while kdb is running on only one "master debug" cpu.
 * Remarks:
 *      The buffer size must be >= 2.
 */

static char *kdb_read(char *buffer, size_t bufsize)
{
        char *cp = buffer;
        char *bufend = buffer+bufsize-2;        /* Reserve space for newline
                                                 * and null byte */
        char *lastchar;
        char *p_tmp;
        char tmp;
        static char tmpbuffer[CMD_BUFLEN];
        int len = strlen(buffer);
        int len_tmp;
        int tab = 0;
        int count;
        int i;
        int diag, dtab_count;
        int key, ret;

        diag = kdbgetintenv("DTABCOUNT", &dtab_count);
        if (diag)
                dtab_count = 30;

        if (len > 0) {
                cp += len;
                if (*(buffer+len-1) == '\n')
                        cp--;
        }

        lastchar = cp;
        *cp = '\0';
        kdb_printf("%s", buffer);
poll_again:
        key = kdb_getchar();
        if (key != 9)
                tab = 0;
        switch (key) {
        case 8: /* backspace */
                if (cp > buffer) {
                        memmove(cp-1, cp, lastchar - cp + 1);
                        lastchar--;
                        cp--;
                        kdb_printf("\b%s ", cp);
                        kdb_position_cursor(kdb_prompt_str, buffer, cp);
                }
                break;
        case 10: /* linefeed */
        case 13: /* carriage return */
                *lastchar++ = '\n';
                *lastchar++ = '\0';
                if (!KDB_STATE(KGDB_TRANS)) {
                        KDB_STATE_SET(KGDB_TRANS);
                        kdb_printf("%s", buffer);
                }
                kdb_printf("\n");
                return buffer;
        case 4: /* Del */
                if (cp < lastchar) {
                        memmove(cp, cp+1, lastchar - cp);
                        lastchar--;
                        kdb_printf("%s ", cp);
                        kdb_position_cursor(kdb_prompt_str, buffer, cp);
                }
                break;
        case 1: /* Home */
                if (cp > buffer) {
                        cp = buffer;
                        kdb_position_cursor(kdb_prompt_str, buffer, cp);
                }
                break;
        case 5: /* End */
                if (cp < lastchar) {
                        kdb_printf("%s", cp);
                        cp = lastchar;
                }
                break;
        case 2: /* Left */
                if (cp > buffer) {
                        kdb_printf("\b");
                        --cp;
                }
                break;
        case 14: /* Down */
        case 16: /* Up */
                kdb_printf("\r%*c\r",
                           (int)(strlen(kdb_prompt_str) + (lastchar - buffer)),
                           ' ');
                *lastchar = (char)key;
                *(lastchar+1) = '\0';
                return lastchar;
        case 6: /* Right */
                if (cp < lastchar) {
                        kdb_printf("%c", *cp);
                        ++cp;
                }
                break;
        case 9: /* Tab */
                if (tab < 2)
                        ++tab;

                tmp = *cp;
                *cp = '\0';
                p_tmp = strrchr(buffer, ' ');
                p_tmp = (p_tmp ? p_tmp + 1 : buffer);
                strscpy(tmpbuffer, p_tmp);
                *cp = tmp;

                len = strlen(tmpbuffer);
                count = kallsyms_symbol_complete(tmpbuffer, sizeof(tmpbuffer));
                if (tab == 2 && count > 0) {
                        kdb_printf("\n%d symbols are found.", count);
                        if (count > dtab_count) {
                                count = dtab_count;
                                kdb_printf(" But only first %d symbols will"
                                           " be printed.\nYou can change the"
                                           " environment variable DTABCOUNT.",
                                           count);
                        }
                        kdb_printf("\n");
                        for (i = 0; i < count; i++) {
                                ret = kallsyms_symbol_next(tmpbuffer, i, sizeof(tmpbuffer));
                                if (WARN_ON(!ret))
                                        break;
                                if (ret != -E2BIG)
                                        kdb_printf("%s ", tmpbuffer);
                                else
                                        kdb_printf("%s... ", tmpbuffer);
                                tmpbuffer[len] = '\0';
                        }
                        if (i >= dtab_count)
                                kdb_printf("...");
                        kdb_printf("\n");
                        kdb_printf("%s",  kdb_prompt_str);
                        kdb_printf("%s", buffer);
                        if (cp != lastchar)
                                kdb_position_cursor(kdb_prompt_str, buffer, cp);
                } else if (tab != 2 && count > 0) {
                        /* How many new characters do we want from tmpbuffer? */
                        len_tmp = strlen(tmpbuffer) - len;
                        if (lastchar + len_tmp >= bufend)
                                len_tmp = bufend - lastchar;

                        if (len_tmp) {
                                /* + 1 ensures the '\0' is memmove'd */
                                memmove(cp+len_tmp, cp, (lastchar-cp) + 1);
                                memcpy(cp, tmpbuffer+len, len_tmp);
                                kdb_printf("%s", cp);
                                cp += len_tmp;
                                lastchar += len_tmp;
                                if (cp != lastchar)
                                        kdb_position_cursor(kdb_prompt_str,
                                                            buffer, cp);
                        }
                }
                kdb_nextline = 1; /* reset output line number */
                break;
        default:
                if (key >= 32 && lastchar < bufend) {
                        if (cp < lastchar) {
                                memmove(cp+1, cp, lastchar - cp + 1);
                                lastchar++;
                                *cp = key;
                                kdb_printf("%s", cp);
                                ++cp;
                                kdb_position_cursor(kdb_prompt_str, buffer, cp);
                        } else {
                                *++lastchar = '\0';
                                *cp++ = key;
                                /* The kgdb transition check will hide
                                 * printed characters if we think that
                                 * kgdb is connecting, until the check
                                 * fails */
                                if (!KDB_STATE(KGDB_TRANS)) {
                                        if (kgdb_transition_check(buffer))
                                                return buffer;
                                } else {
                                        kdb_printf("%c", key);
                                }
                        }
                        /* Special escape to kgdb */
                        if (lastchar - buffer >= 5 &&
                            strcmp(lastchar - 5, "$?#3f") == 0) {
                                kdb_gdb_state_pass(lastchar - 5);
                                strcpy(buffer, "kgdb");
                                KDB_STATE_SET(DOING_KGDB);
                                return buffer;
                        }
                        if (lastchar - buffer >= 11 &&
                            strcmp(lastchar - 11, "$qSupported") == 0) {
                                kdb_gdb_state_pass(lastchar - 11);
                                strcpy(buffer, "kgdb");
                                KDB_STATE_SET(DOING_KGDB);
                                return buffer;
                        }
                }
                break;
        }
        goto poll_again;
}

/*
 * kdb_getstr
 *
 *      Print the prompt string and read a command from the
 *      input device.
 *
 * Parameters:
 *      buffer  Address of buffer to receive command
 *      bufsize Size of buffer in bytes
 *      prompt  Pointer to string to use as prompt string
 * Returns:
 *      Pointer to command buffer.
 * Locking:
 *      None.
 * Remarks:
 *      For SMP kernels, the processor number will be
 *      substituted for %d, %x or %o in the prompt.
 */

char *kdb_getstr(char *buffer, size_t bufsize, const char *prompt)
{
        if (prompt && kdb_prompt_str != prompt)
                strscpy(kdb_prompt_str, prompt);
        kdb_printf("%s", kdb_prompt_str);
        kdb_nextline = 1;       /* Prompt and input resets line number */
        return kdb_read(buffer, bufsize);
}

/*
 * kdb_input_flush
 *
 *      Get rid of any buffered console input.
 *
 * Parameters:
 *      none
 * Returns:
 *      nothing
 * Locking:
 *      none
 * Remarks:
 *      Call this function whenever you want to flush input.  If there is any
 *      outstanding input, it ignores all characters until there has been no
 *      data for approximately 1ms.
 */

static void kdb_input_flush(void)
{
        get_char_func *f;
        int res;
        int flush_delay = 1;
        while (flush_delay) {
                flush_delay--;
empty:
                touch_nmi_watchdog();
                for (f = &kdb_poll_funcs[0]; *f; ++f) {
                        res = (*f)();
                        if (res != -1) {
                                flush_delay = 1;
                                goto empty;
                        }
                }
                if (flush_delay)
                        mdelay(1);
        }
}

/*
 * kdb_printf
 *
 *      Print a string to the output device(s).
 *
 * Parameters:
 *      printf-like format and optional args.
 * Returns:
 *      0
 * Locking:
 *      None.
 * Remarks:
 *      use 'kdbcons->write()' to avoid polluting 'log_buf' with
 *      kdb output.
 *
 *  If the user is doing a cmd args | grep srch
 *  then kdb_grepping_flag is set.
 *  In that case we need to accumulate full lines (ending in \n) before
 *  searching for the pattern.
 */

static char kdb_buffer[256];    /* A bit too big to go on stack */
static char *next_avail = kdb_buffer;
static int  size_avail;
static int  suspend_grep;

/*
 * search arg1 to see if it contains arg2
 * (kdmain.c provides flags for ^pat and pat$)
 *
 * return 1 for found, 0 for not found
 */
static int kdb_search_string(char *searched, char *searchfor)
{
        char firstchar, *cp;
        int len1, len2;

        /* not counting the newline at the end of "searched" */
        len1 = strlen(searched)-1;
        len2 = strlen(searchfor);
        if (len1 < len2)
                return 0;
        if (kdb_grep_leading && kdb_grep_trailing && len1 != len2)
                return 0;
        if (kdb_grep_leading) {
                if (!strncmp(searched, searchfor, len2))
                        return 1;
        } else if (kdb_grep_trailing) {
                if (!strncmp(searched+len1-len2, searchfor, len2))
                        return 1;
        } else {
                firstchar = *searchfor;
                cp = searched;
                while ((cp = strchr(cp, firstchar))) {
                        if (!strncmp(cp, searchfor, len2))
                                return 1;
                        cp++;
                }
        }
        return 0;
}

static void kdb_msg_write(const char *msg, int msg_len)
{
        struct console *c;
        const char *cp;
        int cookie;
        int len;

        if (msg_len == 0)
                return;

        cp = msg;
        len = msg_len;

        while (len--) {
                dbg_io_ops->write_char(*cp);
                cp++;
        }

        /*
         * The console_srcu_read_lock() only provides safe console list
         * traversal. The use of the ->write() callback relies on all other
         * CPUs being stopped at the moment and console drivers being able to
         * handle reentrance when @oops_in_progress is set.
         *
         * There is no guarantee that every console driver can handle
         * reentrance in this way; the developer deploying the debugger
         * is responsible for ensuring that the console drivers they
         * have selected handle reentrance appropriately.
         */
        cookie = console_srcu_read_lock();
        for_each_console_srcu(c) {
                short flags = console_srcu_read_flags(c);

                if (!console_is_usable(c, flags, true))
                        continue;
                if (c == dbg_io_ops->cons)
                        continue;

                if (flags & CON_NBCON) {
                        struct nbcon_write_context wctxt = { };

                        /*
                         * Do not continue if the console is NBCON and the context
                         * can't be acquired.
                         */
                        if (!nbcon_kdb_try_acquire(c, &wctxt))
                                continue;

                        nbcon_write_context_set_buf(&wctxt, (char *)msg, msg_len);

                        c->write_atomic(c, &wctxt);
                        nbcon_kdb_release(&wctxt);
                } else {
                        /*
                         * Set oops_in_progress to encourage the console drivers to
                         * disregard their internal spin locks: in the current calling
                         * context the risk of deadlock is a bigger problem than risks
                         * due to re-entering the console driver. We operate directly on
                         * oops_in_progress rather than using bust_spinlocks() because
                         * the calls bust_spinlocks() makes on exit are not appropriate
                         * for this calling context.
                         */
                        ++oops_in_progress;
                        c->write(c, msg, msg_len);
                        --oops_in_progress;
                }
                touch_nmi_watchdog();
        }
        console_srcu_read_unlock(cookie);
}

int vkdb_printf(enum kdb_msgsrc src, const char *fmt, va_list ap)
{
        int diag;
        int linecount;
        int colcount;
        int logging, saved_loglevel = 0;
        int retlen = 0;
        int fnd, len;
        int this_cpu, old_cpu;
        char *cp, *cp2, *cphold = NULL, replaced_byte = ' ';
        char *moreprompt = "more> ";
        unsigned long flags;

        /* Serialize kdb_printf if multiple cpus try to write at once.
         * But if any cpu goes recursive in kdb, just print the output,
         * even if it is interleaved with any other text.
         */
        local_irq_save(flags);
        this_cpu = smp_processor_id();
        for (;;) {
                old_cpu = cmpxchg(&kdb_printf_cpu, -1, this_cpu);
                if (old_cpu == -1 || old_cpu == this_cpu)
                        break;

                cpu_relax();
        }

        diag = kdbgetintenv("LINES", &linecount);
        if (diag || linecount <= 1)
                linecount = 24;

        diag = kdbgetintenv("COLUMNS", &colcount);
        if (diag || colcount <= 1)
                colcount = 80;

        diag = kdbgetintenv("LOGGING", &logging);
        if (diag)
                logging = 0;

        if (!kdb_grepping_flag || suspend_grep) {
                /* normally, every vsnprintf starts a new buffer */
                next_avail = kdb_buffer;
                size_avail = sizeof(kdb_buffer);
        }
        vsnprintf(next_avail, size_avail, fmt, ap);

        /*
         * If kdb_parse() found that the command was cmd xxx | grep yyy
         * then kdb_grepping_flag is set, and kdb_grep_string contains yyy
         *
         * Accumulate the print data up to a newline before searching it.
         * (vsnprintf does null-terminate the string that it generates)
         */

        /* skip the search if prints are temporarily unconditional */
        if (!suspend_grep && kdb_grepping_flag) {
                cp = strchr(kdb_buffer, '\n');
                if (!cp) {
                        /*
                         * Special cases that don't end with newlines
                         * but should be written without one:
                         *   The "[nn]kdb> " prompt should
                         *   appear at the front of the buffer.
                         *
                         *   The "[nn]more " prompt should also be
                         *     (MOREPROMPT -> moreprompt)
                         *   written *   but we print that ourselves,
                         *   we set the suspend_grep flag to make
                         *   it unconditional.
                         *
                         */
                        if (next_avail == kdb_buffer) {
                                /*
                                 * these should occur after a newline,
                                 * so they will be at the front of the
                                 * buffer
                                 */
                                cp2 = kdb_buffer;
                                len = strlen(kdb_prompt_str);
                                if (!strncmp(cp2, kdb_prompt_str, len)) {
                                        /*
                                         * We're about to start a new
                                         * command, so we can go back
                                         * to normal mode.
                                         */
                                        kdb_grepping_flag = 0;
                                        goto kdb_printit;
                                }
                        }
                        /* no newline; don't search/write the buffer
                           until one is there */
                        len = strlen(kdb_buffer);
                        next_avail = kdb_buffer + len;
                        size_avail = sizeof(kdb_buffer) - len;
                        goto kdb_print_out;
                }

                /*
                 * The newline is present; print through it or discard
                 * it, depending on the results of the search.
                 */
                cp++;                /* to byte after the newline */
                replaced_byte = *cp; /* remember what it was */
                cphold = cp;         /* remember where it was */
                *cp = '\0';          /* end the string for our search */

                /*
                 * We now have a newline at the end of the string
                 * Only continue with this output if it contains the
                 * search string.
                 */
                fnd = kdb_search_string(kdb_buffer, kdb_grep_string);
                if (!fnd) {
                        /*
                         * At this point the complete line at the start
                         * of kdb_buffer can be discarded, as it does
                         * not contain what the user is looking for.
                         * Shift the buffer left.
                         */
                        *cphold = replaced_byte;
                        len = strlen(cphold);
                        /* Use memmove() because the buffers overlap */
                        memmove(kdb_buffer, cphold, len + 1);
                        next_avail = kdb_buffer + len;
                        size_avail = sizeof(kdb_buffer) - len;
                        goto kdb_print_out;
                }
                if (kdb_grepping_flag >= KDB_GREPPING_FLAG_SEARCH) {
                        /*
                         * This was a interactive search (using '/' at more
                         * prompt) and it has completed. Replace the \0 with
                         * its original value to ensure multi-line strings
                         * are handled properly, and return to normal mode.
                         */
                        *cphold = replaced_byte;
                        kdb_grepping_flag = 0;
                }
                /*
                 * at this point the string is a full line and
                 * should be printed, up to the null.
                 */
        }
kdb_printit:

        /*
         * Write to all consoles.
         */
        retlen = strlen(kdb_buffer);
        cp = (char *) printk_skip_headers(kdb_buffer);
        if (!dbg_kdb_mode && kgdb_connected)
                gdbstub_msg_write(cp, retlen - (cp - kdb_buffer));
        else
                kdb_msg_write(cp, retlen - (cp - kdb_buffer));

        if (logging) {
                saved_loglevel = console_loglevel;
                console_loglevel = CONSOLE_LOGLEVEL_SILENT;
                if (printk_get_level(kdb_buffer) || src == KDB_MSGSRC_PRINTK)
                        printk("%s", kdb_buffer);
                else
                        pr_info("%s", kdb_buffer);
        }

        if (KDB_STATE(PAGER)) {
                /*
                 * Check printed string to decide how to bump the
                 * kdb_nextline to control when the more prompt should
                 * show up.
                 */
                int got = 0;
                len = retlen;
                while (len--) {
                        if (kdb_buffer[len] == '\n') {
                                kdb_nextline++;
                                got = 0;
                        } else if (kdb_buffer[len] == '\r') {
                                got = 0;
                        } else {
                                got++;
                        }
                }
                kdb_nextline += got / (colcount + 1);
        }

        /* check for having reached the LINES number of printed lines */
        if (kdb_nextline >= linecount) {
                char ch;

                /* Watch out for recursion here.  Any routine that calls
                 * kdb_printf will come back through here.  And kdb_read
                 * uses kdb_printf to echo on serial consoles ...
                 */
                kdb_nextline = 1;       /* In case of recursion */

                /*
                 * Pause until cr.
                 */
                moreprompt = kdbgetenv("MOREPROMPT");
                if (moreprompt == NULL)
                        moreprompt = "more> ";

                kdb_input_flush();
                kdb_msg_write(moreprompt, strlen(moreprompt));

                if (logging)
                        printk("%s", moreprompt);

                ch = kdb_getchar();
                kdb_nextline = 1;       /* Really set output line 1 */

                /* empty and reset the buffer: */
                kdb_buffer[0] = '\0';
                next_avail = kdb_buffer;
                size_avail = sizeof(kdb_buffer);
                if ((ch == 'q') || (ch == 'Q')) {
                        /* user hit q or Q */
                        KDB_FLAG_SET(CMD_INTERRUPT); /* command interrupted */
                        KDB_STATE_CLEAR(PAGER);
                        /* end of command output; back to normal mode */
                        kdb_grepping_flag = 0;
                        kdb_printf("\n");
                } else if (ch == ' ') {
                        kdb_printf("\r");
                        suspend_grep = 1; /* for this recursion */
                } else if (ch == '\n' || ch == '\r') {
                        kdb_nextline = linecount - 1;
                        kdb_printf("\r");
                        suspend_grep = 1; /* for this recursion */
                } else if (ch == '/' && !kdb_grepping_flag) {
                        kdb_printf("\r");
                        kdb_getstr(kdb_grep_string, KDB_GREP_STRLEN,
                                   kdbgetenv("SEARCHPROMPT") ?: "search> ");
                        *strchrnul(kdb_grep_string, '\n') = '\0';
                        kdb_grepping_flag += KDB_GREPPING_FLAG_SEARCH;
                        suspend_grep = 1; /* for this recursion */
                } else if (ch) {
                        /* user hit something unexpected */
                        suspend_grep = 1; /* for this recursion */
                        if (ch != '/')
                                kdb_printf(
                                    "\nOnly 'q', 'Q' or '/' are processed at "
                                    "more prompt, input ignored\n");
                        else
                                kdb_printf("\n'/' cannot be used during | "
                                           "grep filtering, input ignored\n");
                } else if (kdb_grepping_flag) {
                        /* user hit enter */
                        suspend_grep = 1; /* for this recursion */
                        kdb_printf("\n");
                }
                kdb_input_flush();
        }

        /*
         * For grep searches, shift the printed string left.
         *  replaced_byte contains the character that was overwritten with
         *  the terminating null, and cphold points to the null.
         * Then adjust the notion of available space in the buffer.
         */
        if (kdb_grepping_flag && !suspend_grep) {
                *cphold = replaced_byte;
                len = strlen(cphold);
                /* Use memmove() because the buffers overlap */
                memmove(kdb_buffer, cphold, len + 1);
                next_avail = kdb_buffer + len;
                size_avail = sizeof(kdb_buffer) - len;
        }

kdb_print_out:
        suspend_grep = 0; /* end of what may have been a recursive call */
        if (logging)
                console_loglevel = saved_loglevel;
        /* kdb_printf_cpu locked the code above. */
        smp_store_release(&kdb_printf_cpu, old_cpu);
        local_irq_restore(flags);
        return retlen;
}

int kdb_printf(const char *fmt, ...)
{
        va_list ap;
        int r;

        va_start(ap, fmt);
        r = vkdb_printf(KDB_MSGSRC_INTERNAL, fmt, ap);
        va_end(ap);

        return r;
}
EXPORT_SYMBOL_GPL(kdb_printf);