7c6c86b36a
Since some time now, printk() adds carriage return, leading to
unusable xmon output if there is no udbg backend available:
[ 54.288722] sysrq: Entering xmon
[ 54.292209] Vector: 0 at [cace3d2c]
[ 54.292274] pc:
[ 54.292331] c0023650
[ 54.292468] : xmon+0x28/0x58
[ 54.292519]
[ 54.292574] lr:
[ 54.292630] c0023724
[ 54.292749] : sysrq_handle_xmon+0xa4/0xfc
[ 54.292801]
[ 54.292867] sp: cace3de8
[ 54.292931] msr: 9032
[ 54.292999] current = 0xc28d0000
[ 54.293072] pid = 377, comm = sh
[ 54.293157] Linux version 5.10.0-rc6-s3k-dev-01364-gedf13f0ccd76-dirty (root@po17688vm.idsi0.si.c-s.fr) (powerpc64-linux-gcc (GCC) 10.1.0, GNU ld (GNU Binutils) 2.34) #4211 PREEMPT Fri Dec 4 09:32:11 UTC 2020
[ 54.293287] enter ? for help
[ 54.293470] [cace3de8]
[ 54.293532] c0023724
[ 54.293654] sysrq_handle_xmon+0xa4/0xfc
[ 54.293711] (unreliable)
...
[ 54.296002]
[ 54.296159] --- Exception: c01 (System Call) at
[ 54.296217] 0fd4e784
[ 54.296303]
[ 54.296375] SP (7fca6ff0) is in userspace
[ 54.296431] mon>
[ 54.296484] <no input ...>
Use pr_cont() instead.
Fixes: 4bcc595ccd
("printk: reinstate KERN_CONT for printing continuation lines")
Cc: stable@vger.kernel.org # v4.9+
Signed-off-by: Christophe Leroy <christophe.leroy@csgroup.eu>
[mpe: Mention that it only happens when udbg is not available]
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/c8a6ec704416ecd5ff2bd26213c9bc026bdd19de.1607077340.git.christophe.leroy@csgroup.eu
189 lines
3.1 KiB
C
189 lines
3.1 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* Copyright (C) 1996-2005 Paul Mackerras.
|
|
*/
|
|
#include <linux/string.h>
|
|
#include <asm/udbg.h>
|
|
#include <asm/time.h>
|
|
#include "nonstdio.h"
|
|
|
|
static bool paginating, paginate_skipping;
|
|
static unsigned long paginate_lpp; /* Lines Per Page */
|
|
static unsigned long paginate_pos;
|
|
|
|
void xmon_start_pagination(void)
|
|
{
|
|
paginating = true;
|
|
paginate_skipping = false;
|
|
paginate_pos = 0;
|
|
}
|
|
|
|
void xmon_end_pagination(void)
|
|
{
|
|
paginating = false;
|
|
}
|
|
|
|
void xmon_set_pagination_lpp(unsigned long lpp)
|
|
{
|
|
paginate_lpp = lpp;
|
|
}
|
|
|
|
static int xmon_readchar(void)
|
|
{
|
|
if (udbg_getc)
|
|
return udbg_getc();
|
|
return -1;
|
|
}
|
|
|
|
static int xmon_write(const char *ptr, int nb)
|
|
{
|
|
int rv = 0;
|
|
const char *p = ptr, *q;
|
|
const char msg[] = "[Hit a key (a:all, q:truncate, any:next page)]";
|
|
|
|
if (nb <= 0)
|
|
return rv;
|
|
|
|
if (paginating && paginate_skipping)
|
|
return nb;
|
|
|
|
if (paginate_lpp) {
|
|
while (paginating && (q = strchr(p, '\n'))) {
|
|
rv += udbg_write(p, q - p + 1);
|
|
p = q + 1;
|
|
paginate_pos++;
|
|
|
|
if (paginate_pos >= paginate_lpp) {
|
|
udbg_write(msg, strlen(msg));
|
|
|
|
switch (xmon_readchar()) {
|
|
case 'a':
|
|
paginating = false;
|
|
break;
|
|
case 'q':
|
|
paginate_skipping = true;
|
|
break;
|
|
default:
|
|
/* nothing */
|
|
break;
|
|
}
|
|
|
|
paginate_pos = 0;
|
|
udbg_write("\r\n", 2);
|
|
|
|
if (paginate_skipping)
|
|
return nb;
|
|
}
|
|
}
|
|
}
|
|
|
|
return rv + udbg_write(p, nb - (p - ptr));
|
|
}
|
|
|
|
int xmon_putchar(int c)
|
|
{
|
|
char ch = c;
|
|
|
|
if (c == '\n')
|
|
xmon_putchar('\r');
|
|
return xmon_write(&ch, 1) == 1? c: -1;
|
|
}
|
|
|
|
static char line[256];
|
|
static char *lineptr;
|
|
static int lineleft;
|
|
|
|
static int xmon_getchar(void)
|
|
{
|
|
int c;
|
|
|
|
if (lineleft == 0) {
|
|
lineptr = line;
|
|
for (;;) {
|
|
c = xmon_readchar();
|
|
if (c == -1 || c == 4)
|
|
break;
|
|
if (c == '\r' || c == '\n') {
|
|
*lineptr++ = '\n';
|
|
xmon_putchar('\n');
|
|
break;
|
|
}
|
|
switch (c) {
|
|
case 0177:
|
|
case '\b':
|
|
if (lineptr > line) {
|
|
xmon_putchar('\b');
|
|
xmon_putchar(' ');
|
|
xmon_putchar('\b');
|
|
--lineptr;
|
|
}
|
|
break;
|
|
case 'U' & 0x1F:
|
|
while (lineptr > line) {
|
|
xmon_putchar('\b');
|
|
xmon_putchar(' ');
|
|
xmon_putchar('\b');
|
|
--lineptr;
|
|
}
|
|
break;
|
|
default:
|
|
if (lineptr >= &line[sizeof(line) - 1])
|
|
xmon_putchar('\a');
|
|
else {
|
|
xmon_putchar(c);
|
|
*lineptr++ = c;
|
|
}
|
|
}
|
|
}
|
|
lineleft = lineptr - line;
|
|
lineptr = line;
|
|
}
|
|
if (lineleft == 0)
|
|
return -1;
|
|
--lineleft;
|
|
return *lineptr++;
|
|
}
|
|
|
|
char *xmon_gets(char *str, int nb)
|
|
{
|
|
char *p;
|
|
int c;
|
|
|
|
for (p = str; p < str + nb - 1; ) {
|
|
c = xmon_getchar();
|
|
if (c == -1) {
|
|
if (p == str)
|
|
return NULL;
|
|
break;
|
|
}
|
|
*p++ = c;
|
|
if (c == '\n')
|
|
break;
|
|
}
|
|
*p = 0;
|
|
return str;
|
|
}
|
|
|
|
void xmon_printf(const char *format, ...)
|
|
{
|
|
va_list args;
|
|
static char xmon_outbuf[1024];
|
|
int rc, n;
|
|
|
|
va_start(args, format);
|
|
n = vsnprintf(xmon_outbuf, sizeof(xmon_outbuf), format, args);
|
|
va_end(args);
|
|
|
|
rc = xmon_write(xmon_outbuf, n);
|
|
|
|
if (n && rc == 0) {
|
|
/* No udbg hooks, fallback to printk() - dangerous */
|
|
pr_cont("%s", xmon_outbuf);
|
|
}
|
|
}
|
|
|
|
void xmon_puts(const char *str)
|
|
{
|
|
xmon_write(str, strlen(str));
|
|
}
|