1
0
Files
kernel-49/kernel/debug/kdb/kdb_io.c
Greg Kroah-Hartman 49f4e44fac Merge 4.9.241 into android-4.9-q
Changes in 4.9.241
	ibmveth: Identify ingress large send packets.
	tipc: fix the skb_unshare() in tipc_buf_append()
	net/ipv4: always honour route mtu during forwarding
	r8169: fix data corruption issue on RTL8402
	ALSA: bebob: potential info leak in hwdep_read()
	net: hdlc: In hdlc_rcv, check to make sure dev is an HDLC device
	net: hdlc_raw_eth: Clear the IFF_TX_SKB_SHARING flag after calling ether_setup
	nfc: Ensure presence of NFC_ATTR_FIRMWARE_NAME attribute in nfc_genl_fw_download()
	tcp: fix to update snd_wl1 in bulk receiver fast path
	icmp: randomize the global rate limiter
	cifs: remove bogus debug code
	KVM: x86/mmu: Commit zap of remaining invalid pages when recovering lpages
	ima: Don't ignore errors from crypto_shash_update()
	crypto: algif_aead - Do not set MAY_BACKLOG on the async path
	EDAC/i5100: Fix error handling order in i5100_init_one()
	crypto: ixp4xx - Fix the size used in a 'dma_free_coherent()' call
	media: Revert "media: exynos4-is: Add missed check for pinctrl_lookup_state()"
	media: m5mols: Check function pointer in m5mols_sensor_power
	media: omap3isp: Fix memleak in isp_probe
	crypto: omap-sham - fix digcnt register handling with export/import
	media: tc358743: initialize variable
	media: platform: fcp: Fix a reference count leak.
	media: ti-vpe: Fix a missing check and reference count leak
	regulator: resolve supply after creating regulator
	ath10k: provide survey info as accumulated data
	ath6kl: prevent potential array overflow in ath6kl_add_new_sta()
	ath9k: Fix potential out of bounds in ath9k_htc_txcompletion_cb()
	wcn36xx: Fix reported 802.11n rx_highest rate wcn3660/wcn3680
	ASoC: qcom: lpass-platform: fix memory leak
	mwifiex: Do not use GFP_KERNEL in atomic context
	drm/gma500: fix error check
	scsi: qla4xxx: Fix an error handling path in 'qla4xxx_get_host_stats()'
	scsi: csiostor: Fix wrong return value in csio_hw_prep_fw()
	backlight: sky81452-backlight: Fix refcount imbalance on error
	VMCI: check return value of get_user_pages_fast() for errors
	tty: serial: earlycon dependency
	tty: hvcs: Don't NULL tty->driver_data until hvcs_cleanup()
	pty: do tty_flip_buffer_push without port->lock in pty_write
	drivers/virt/fsl_hypervisor: Fix error handling path
	video: fbdev: vga16fb: fix setting of pixclock because a pass-by-value error
	video: fbdev: sis: fix null ptr dereference
	HID: roccat: add bounds checking in kone_sysfs_write_settings()
	ath6kl: wmi: prevent a shift wrapping bug in ath6kl_wmi_delete_pstream_cmd()
	misc: mic: scif: Fix error handling path
	ALSA: seq: oss: Avoid mutex lock for a long-time ioctl
	quota: clear padding in v2r1_mem2diskdqb()
	net: enic: Cure the enic api locking trainwreck
	mfd: sm501: Fix leaks in probe()
	iwlwifi: mvm: split a print to avoid a WARNING in ROC
	usb: gadget: f_ncm: fix ncm_bitrate for SuperSpeed and above.
	usb: gadget: u_ether: enable qmult on SuperSpeed Plus as well
	nl80211: fix non-split wiphy information
	scsi: be2iscsi: Fix a theoretical leak in beiscsi_create_eqs()
	mwifiex: fix double free
	net: korina: fix kfree of rx/tx descriptor array
	IB/mlx4: Fix starvation in paravirt mux/demux
	IB/mlx4: Adjust delayed work when a dup is observed
	powerpc/pseries: Fix missing of_node_put() in rng_init()
	powerpc/icp-hv: Fix missing of_node_put() in success path
	mtd: lpddr: fix excessive stack usage with clang
	mtd: mtdoops: Don't write panic data twice
	ARM: 9007/1: l2c: fix prefetch bits init in L2X0_AUX_CTRL using DT values
	RDMA/qedr: Fix use of uninitialized field
	powerpc/tau: Use appropriate temperature sample interval
	powerpc/tau: Remove duplicated set_thresholds() call
	powerpc/tau: Disable TAU between measurements
	perf intel-pt: Fix "context_switch event has no tid" error
	RDMA/hns: Set the unsupported wr opcode
	kdb: Fix pager search for multi-line strings
	overflow: Include header file with SIZE_MAX declaration
	powerpc/perf: Exclude pmc5/6 from the irrelevant PMU group constraints
	powerpc/perf/hv-gpci: Fix starting index value
	cpufreq: powernv: Fix frame-size-overflow in powernv_cpufreq_reboot_notifier
	IB/rdmavt: Fix sizeof mismatch
	lib/crc32.c: fix trivial typo in preprocessor condition
	rapidio: fix error handling path
	rapidio: fix the missed put_device() for rio_mport_add_riodev
	clk: at91: clk-main: update key before writing AT91_CKGR_MOR
	clk: bcm2835: add missing release if devm_clk_hw_register fails
	vfio/pci: Clear token on bypass registration failure
	Input: imx6ul_tsc - clean up some errors in imx6ul_tsc_resume()
	Input: ep93xx_keypad - fix handling of platform_get_irq() error
	Input: omap4-keypad - fix handling of platform_get_irq() error
	Input: twl4030_keypad - fix handling of platform_get_irq() error
	Input: sun4i-ps2 - fix handling of platform_get_irq() error
	KVM: x86: emulating RDPID failure shall return #UD rather than #GP
	memory: omap-gpmc: Fix a couple off by ones
	memory: fsl-corenet-cf: Fix handling of platform_get_irq() error
	arm64: dts: qcom: msm8916: Fix MDP/DSI interrupts
	arm64: dts: zynqmp: Remove additional compatible string for i2c IPs
	powerpc/powernv/dump: Fix race while processing OPAL dump
	nvmet: fix uninitialized work for zero kato
	NTB: hw: amd: fix an issue about leak system resources
	crypto: ccp - fix error handling
	media: firewire: fix memory leak
	media: ati_remote: sanity check for both endpoints
	media: exynos4-is: Fix several reference count leaks due to pm_runtime_get_sync
	media: exynos4-is: Fix a reference count leak due to pm_runtime_get_sync
	media: exynos4-is: Fix a reference count leak
	media: vsp1: Fix runtime PM imbalance on error
	media: platform: s3c-camif: Fix runtime PM imbalance on error
	media: platform: sti: hva: Fix runtime PM imbalance on error
	media: bdisp: Fix runtime PM imbalance on error
	media: media/pci: prevent memory leak in bttv_probe
	media: uvcvideo: Ensure all probed info is returned to v4l2
	mmc: sdio: Check for CISTPL_VERS_1 buffer size
	media: saa7134: avoid a shift overflow
	fs: dlm: fix configfs memory leak
	ntfs: add check for mft record size in superblock
	PM: hibernate: remove the bogus call to get_gendisk() in software_resume()
	scsi: mvumi: Fix error return in mvumi_io_attach()
	scsi: target: core: Add CONTROL field for trace events
	mic: vop: copy data to kernel space then write to io memory
	misc: vop: add round_up(x,4) for vring_size to avoid kernel panic
	usb: gadget: function: printer: fix use-after-free in __lock_acquire
	udf: Limit sparing table size
	udf: Avoid accessing uninitialized data on failed inode read
	USB: cdc-acm: handle broken union descriptors
	ath9k: hif_usb: fix race condition between usb_get_urb() and usb_kill_anchored_urbs()
	misc: rtsx: Fix memory leak in rtsx_pci_probe
	reiserfs: only call unlock_new_inode() if I_NEW
	xfs: make sure the rt allocator doesn't run off the end
	usb: ohci: Default to per-port over-current protection
	Bluetooth: Only mark socket zapped after unlocking
	scsi: ibmvfc: Fix error return in ibmvfc_probe()
	brcmsmac: fix memory leak in wlc_phy_attach_lcnphy
	rtl8xxxu: prevent potential memory leak
	Fix use after free in get_capset_info callback.
	tty: ipwireless: fix error handling
	ipvs: Fix uninit-value in do_ip_vs_set_ctl()
	reiserfs: Fix memory leak in reiserfs_parse_options()
	brcm80211: fix possible memleak in brcmf_proto_msgbuf_attach
	usb: core: Solve race condition in anchor cleanup functions
	ath10k: check idx validity in __ath10k_htt_rx_ring_fill_n()
	net: korina: cast KSEG0 address to pointer in kfree
	usb: cdc-acm: add quirk to blacklist ETAS ES58X devices
	USB: cdc-wdm: Make wdm_flush() interruptible and add wdm_fsync().
	eeprom: at25: set minimum read/write access stride to 1
	usb: gadget: f_ncm: allow using NCM in SuperSpeed Plus gadgets.
	Linux 4.9.241

Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
Change-Id: Ie59605b312e5d0314299cad46ab57df803070564
2020-11-10 11:35:46 +03:00

890 lines
21 KiB
C

/*
* Kernel Debugger Architecture Independent Console I/O handler
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (c) 1999-2006 Silicon Graphics, Inc. All Rights Reserved.
* Copyright (c) 2009 Wind River Systems, Inc. All Rights Reserved.
*/
#include <linux/module.h>
#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;
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;
}
static int kdb_read_get_key(char *buffer, size_t bufsize)
{
#define ESCAPE_UDELAY 1000
#define ESCAPE_DELAY (2*1000000/ESCAPE_UDELAY) /* 2 seconds worth of udelays */
char escape_data[5]; /* longest vt100 escape sequence is 4 bytes */
char *ped = escape_data;
int escape_delay = 0;
get_char_func *f, *f_escape = NULL;
int key;
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];
}
if (escape_delay == 2) {
*ped = '\0';
ped = escape_data;
--escape_delay;
}
if (escape_delay == 1) {
key = *ped++;
if (!*ped)
--escape_delay;
break;
}
key = (*f)();
if (key == -1) {
if (escape_delay) {
udelay(ESCAPE_UDELAY);
--escape_delay;
}
continue;
}
if (bufsize <= 2) {
if (key == '\r')
key = '\n';
*buffer++ = key;
*buffer = '\0';
return -1;
}
if (escape_delay == 0 && key == '\e') {
escape_delay = ESCAPE_DELAY;
ped = escape_data;
f_escape = f;
}
if (escape_delay) {
*ped++ = key;
if (f_escape != f) {
escape_delay = 2;
continue;
}
if (ped - escape_data == 1) {
/* \e */
continue;
} else if (ped - escape_data == 2) {
/* \e<something> */
if (key != '[')
escape_delay = 2;
continue;
} else if (ped - escape_data == 3) {
/* \e[<something> */
int mapkey = 0;
switch (key) {
case 'A': /* \e[A, up arrow */
mapkey = 16;
break;
case 'B': /* \e[B, down arrow */
mapkey = 14;
break;
case 'C': /* \e[C, right arrow */
mapkey = 6;
break;
case 'D': /* \e[D, left arrow */
mapkey = 2;
break;
case '1': /* dropthrough */
case '3': /* dropthrough */
/* \e[<1,3,4>], may be home, del, end */
case '4':
mapkey = -1;
break;
}
if (mapkey != -1) {
if (mapkey > 0) {
escape_data[0] = mapkey;
escape_data[1] = '\0';
}
escape_delay = 2;
}
continue;
} else if (ped - escape_data == 4) {
/* \e[<1,3,4><something> */
int mapkey = 0;
if (key == '~') {
switch (escape_data[2]) {
case '1': /* \e[1~, home */
mapkey = 1;
break;
case '3': /* \e[3~, del */
mapkey = 4;
break;
case '4': /* \e[4~, end */
mapkey = 5;
break;
}
}
if (mapkey > 0) {
escape_data[0] = mapkey;
escape_data[1] = '\0';
}
escape_delay = 2;
continue;
}
}
break; /* A key to process */
}
return key;
}
/*
* 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. A buffer size of 2 means that the caller only
* wants a single key.
*
* 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 and by the need to sometimes
* return after just one key. Escape sequence processing has to be done as
* states in the polling loop.
*/
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, buf_size, ret;
static int last_crlf;
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_read_get_key(buffer, bufsize);
if (key == -1)
return buffer;
if (key != 9)
tab = 0;
if (key != 10 && key != 13)
last_crlf = 0;
switch (key) {
case 8: /* backspace */
if (cp > buffer) {
if (cp < lastchar) {
memcpy(tmpbuffer, cp, lastchar - cp);
memcpy(cp-1, tmpbuffer, lastchar - cp);
}
*(--lastchar) = '\0';
--cp;
kdb_printf("\b%s \r", cp);
tmp = *cp;
*cp = '\0';
kdb_printf(kdb_prompt_str);
kdb_printf("%s", buffer);
*cp = tmp;
}
break;
case 10: /* new line */
case 13: /* carriage return */
/* handle \n after \r */
if (last_crlf && last_crlf != key)
break;
last_crlf = key;
*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) {
memcpy(tmpbuffer, cp+1, lastchar - cp - 1);
memcpy(cp, tmpbuffer, lastchar - cp - 1);
*(--lastchar) = '\0';
kdb_printf("%s \r", cp);
tmp = *cp;
*cp = '\0';
kdb_printf(kdb_prompt_str);
kdb_printf("%s", buffer);
*cp = tmp;
}
break;
case 1: /* Home */
if (cp > buffer) {
kdb_printf("\r");
kdb_printf(kdb_prompt_str);
cp = buffer;
}
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 */
memset(tmpbuffer, ' ',
strlen(kdb_prompt_str) + (lastchar-buffer));
*(tmpbuffer+strlen(kdb_prompt_str) +
(lastchar-buffer)) = '\0';
kdb_printf("\r%s\r", tmpbuffer);
*lastchar = (char)key;
*(lastchar+1) = '\0';
return lastchar;
case 6: /* Right */
if (cp < lastchar) {
kdb_printf("%c", *cp);
++cp;
}
break;
case 16: /* Up */
memset(tmpbuffer, ' ',
strlen(kdb_prompt_str) + (lastchar-buffer));
*(tmpbuffer+strlen(kdb_prompt_str) +
(lastchar-buffer)) = '\0';
kdb_printf("\r%s\r", tmpbuffer);
*lastchar = (char)key;
*(lastchar+1) = '\0';
return lastchar;
case 9: /* Tab */
if (tab < 2)
++tab;
p_tmp = buffer;
while (*p_tmp == ' ')
p_tmp++;
if (p_tmp > cp)
break;
memcpy(tmpbuffer, p_tmp, cp-p_tmp);
*(tmpbuffer + (cp-p_tmp)) = '\0';
p_tmp = strrchr(tmpbuffer, ' ');
if (p_tmp)
++p_tmp;
else
p_tmp = tmpbuffer;
len = strlen(p_tmp);
buf_size = sizeof(tmpbuffer) - (p_tmp - tmpbuffer);
count = kallsyms_symbol_complete(p_tmp, buf_size);
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(p_tmp, i, buf_size);
if (WARN_ON(!ret))
break;
if (ret != -E2BIG)
kdb_printf("%s ", p_tmp);
else
kdb_printf("%s... ", p_tmp);
*(p_tmp + len) = '\0';
}
if (i >= dtab_count)
kdb_printf("...");
kdb_printf("\n");
kdb_printf(kdb_prompt_str);
kdb_printf("%s", buffer);
} else if (tab != 2 && count > 0) {
len_tmp = strlen(p_tmp);
strncpy(p_tmp+len_tmp, cp, lastchar-cp+1);
len_tmp = strlen(p_tmp);
strncpy(cp, p_tmp+len, len_tmp-len + 1);
len = len_tmp - len;
kdb_printf("%s", cp);
cp += len;
lastchar += len;
}
kdb_nextline = 1; /* reset output line number */
break;
default:
if (key >= 32 && lastchar < bufend) {
if (cp < lastchar) {
memcpy(tmpbuffer, cp, lastchar - cp);
memcpy(cp+1, tmpbuffer, lastchar - cp);
*++lastchar = '\0';
*cp = key;
kdb_printf("%s\r", cp);
++cp;
tmp = *cp;
*cp = '\0';
kdb_printf(kdb_prompt_str);
kdb_printf("%s", buffer);
*cp = tmp;
} 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)
strncpy(kdb_prompt_str, prompt, CMD_BUFLEN);
kdb_printf(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;
}
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 saved_trap_printk;
int got_printf_lock = 0;
int retlen = 0;
int fnd, len;
char *cp, *cp2, *cphold = NULL, replaced_byte = ' ';
char *moreprompt = "more> ";
struct console *c = console_drivers;
static DEFINE_SPINLOCK(kdb_printf_lock);
unsigned long uninitialized_var(flags);
preempt_disable();
saved_trap_printk = kdb_trap_printk;
kdb_trap_printk = 0;
/* 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.
*/
if (!KDB_STATE(PRINTF_LOCK)) {
KDB_STATE_SET(PRINTF_LOCK);
spin_lock_irqsave(&kdb_printf_lock, flags);
got_printf_lock = 1;
atomic_inc(&kdb_event);
} else {
__acquire(kdb_printf_lock);
}
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/where it was */
cphold = cp;
*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;
strcpy(kdb_buffer, cphold);
len = strlen(kdb_buffer);
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_level(kdb_buffer);
if (!dbg_kdb_mode && kgdb_connected) {
gdbstub_msg_write(cp, retlen - (cp - kdb_buffer));
} else {
if (dbg_io_ops && !dbg_io_ops->is_console) {
len = retlen - (cp - kdb_buffer);
cp2 = cp;
while (len--) {
dbg_io_ops->write_char(*cp2);
cp2++;
}
}
while (c) {
c->write(c, cp, retlen - (cp - kdb_buffer));
touch_nmi_watchdog();
c = c->next;
}
}
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 buf1[16] = "";
/* 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();
c = console_drivers;
if (dbg_io_ops && !dbg_io_ops->is_console) {
len = strlen(moreprompt);
cp = moreprompt;
while (len--) {
dbg_io_ops->write_char(*cp);
cp++;
}
}
while (c) {
c->write(c, moreprompt, strlen(moreprompt));
touch_nmi_watchdog();
c = c->next;
}
if (logging)
printk("%s", moreprompt);
kdb_read(buf1, 2); /* '2' indicates to return
* immediately after getting one key. */
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 ((buf1[0] == 'q') || (buf1[0] == '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 (buf1[0] == ' ') {
kdb_printf("\r");
suspend_grep = 1; /* for this recursion */
} else if (buf1[0] == '\n') {
kdb_nextline = linecount - 1;
kdb_printf("\r");
suspend_grep = 1; /* for this recursion */
} else if (buf1[0] == '/' && !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 (buf1[0] && buf1[0] != '\n') {
/* user hit something other than enter */
suspend_grep = 1; /* for this recursion */
if (buf1[0] != '/')
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;
strcpy(kdb_buffer, cphold);
len = strlen(kdb_buffer);
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;
if (KDB_STATE(PRINTF_LOCK) && got_printf_lock) {
got_printf_lock = 0;
spin_unlock_irqrestore(&kdb_printf_lock, flags);
KDB_STATE_CLEAR(PRINTF_LOCK);
atomic_dec(&kdb_event);
} else {
__release(kdb_printf_lock);
}
kdb_trap_printk = saved_trap_printk;
preempt_enable();
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);