kmdb_promif.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/types.h>
#include <sys/termios.h>
#include <sys/promif.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <kmdb/kmdb_promif_impl.h>
#include <kmdb/kmdb_kdi.h>
#include <kmdb/kmdb_dpi.h>
#include <mdb/mdb_debug.h>
#include <mdb/mdb_err.h>
#include <mdb/mdb_frame.h>
#include <mdb/mdb_string.h>
#include <mdb/mdb.h>
#define KMDB_PROM_DEF_CONS_MODE "9600,n,1,-,-"
#define KMDB_PROM_READBUF_SIZE 1024
static char kmdb_prom_readbuf[KMDB_PROM_READBUF_SIZE];
static int kmdb_prom_readbuf_head;
static int kmdb_prom_readbuf_tail;
ssize_t
kmdb_prom_polled_read(caddr_t buf, size_t len)
{
uintptr_t arg = (uintptr_t)mdb.m_pio->cons_polledio_argument;
uintptr_t ischar = (uintptr_t)mdb.m_pio->cons_polledio_ischar;
int nread = 0;
int c;
while ((ischar == NULL || kmdb_dpi_call(ischar, 1, &arg)) &&
nread < len) {
c = kmdb_dpi_call((uintptr_t)mdb.m_pio->cons_polledio_getchar,
1, &arg);
*buf++ = (char)c;
nread++;
}
return (nread);
}
ssize_t
kmdb_prom_polled_write(caddr_t buf, size_t len)
{
uintptr_t args[2];
int i;
args[0] = (uintptr_t)mdb.m_pio->cons_polledio_argument;
for (i = 0; i < len; i++) {
args[1] = *buf++;
(void) kmdb_dpi_call(
(uintptr_t)mdb.m_pio->cons_polledio_putchar, 2, args);
}
return (len);
}
static ssize_t
kmdb_prom_reader(caddr_t buf, size_t len)
{
int nread = 0;
int c;
if (mdb.m_pio != NULL && mdb.m_pio->cons_polledio_getchar != NULL)
return (kmdb_prom_polled_read(buf, len));
while (nread < len && (c = prom_mayget()) != -1) {
*buf++ = (char)c;
nread++;
}
return (nread);
}
static ssize_t
kmdb_prom_writer(caddr_t buf, size_t len)
{
if (mdb.m_pio != NULL && mdb.m_pio->cons_polledio_putchar != NULL)
return (kmdb_prom_polled_write(buf, len));
return (kmdb_prom_obp_writer(buf, len));
}
/*
* Due to the nature of kmdb, we don't have signals. This prevents us from
* receiving asynchronous notification when the user would like to abort
* active dcmds. Whereas mdb can simply declare a SIGINT handler, we must
* occasionally poll the input stream, looking for pending ^C characters. To
* give the illusion of asynchronous interrupt delivery, this polling is
* triggered from several commonly-used functions, such as kmdb_prom_write,
* kmdb_prom_read, and the *read and *write target ops. When an interrupt
* check is triggered, we read through pending input, looking for interrupt
* characters. If we find one, we deliver it.
*
* OBP doesn't have an "unget" facility. Any character read for interrupt
* checking is gone forever, unless we save it. Loss of these characters
* would prevent us from supporting typeahead. We like typeahead, so we're
* going to save characters gathered during interrupt checking. As with
* ungetc(3c), however, we can only store a finite number of characters in
* our typeahead buffer. Characters read beyond that will be silently dropped
* after they undergo interrupt processing.
*
* The typeahead facility is implemented as a ring buffer, stored in
* kmdb_prom_readbuf, and has the following interfaces:
*
* kmdb_prom_drain_readbuf - Given a buffer and a length, copy that many bytes
* into the passed buffer. This routine will not attempt to refill the ring
* buffer. The number of bytes actually copied will be returned.
*
* kmdb_prom_fill_readbuf - Attempt to refill the ring buffer from the input
* stream. The `lossy' argument governs the action taken if the available
* input will overflow the available space in the ring buffer. If the lossy
* argument is set, available input will be exhausted. Characters that will
* not fit in the ring buffer will be discarded after interrupt processing.
* If this routine is invoked in lossless mode (i.e. the lossy flag is not
* set), input will be read only until the buffer is full.
*
* kmdb_prom_* routines may call drain and fill directly. Callers outside of
* the kmdb promif subsystem must use kmdb_prom_check_interrupt, which will
* invoke fill in lossy mode.
*/
size_t
kmdb_prom_drain_readbuf(void *buf, size_t len)
{
size_t n, tailread;
/*
* If head > tail, life is easy - we can simply read as much as we need
* in one gulp.
*/
if (kmdb_prom_readbuf_head > kmdb_prom_readbuf_tail) {
n = MIN(kmdb_prom_readbuf_head - kmdb_prom_readbuf_tail, len);
bcopy(kmdb_prom_readbuf + kmdb_prom_readbuf_tail, buf, n);
kmdb_prom_readbuf_tail += n;
return (n);
} else if (kmdb_prom_readbuf_head == kmdb_prom_readbuf_tail) {
return (0);
}
/*
* The consumable slots wrap around zero (there are slots from tail to
* zero, and from zero to head). We have to read them in two parts.
*/
n = MIN(KMDB_PROM_READBUF_SIZE - kmdb_prom_readbuf_tail, len);
bcopy(kmdb_prom_readbuf + kmdb_prom_readbuf_tail, buf, n);
kmdb_prom_readbuf_tail = (kmdb_prom_readbuf_tail + n) %
KMDB_PROM_READBUF_SIZE;
if (n == len) {
/*
* We filled the passed buffer from the first part, so there's
* no need to read the second.
*/
return (n);
} else {
tailread = n;
}
n = MIN(kmdb_prom_readbuf_head, len - tailread);
buf = (void *)((uintptr_t)buf + tailread);
bcopy(kmdb_prom_readbuf, buf, n);
kmdb_prom_readbuf_tail = (kmdb_prom_readbuf_tail + n) %
KMDB_PROM_READBUF_SIZE;
return (tailread + n);
}
static void
check_int(char *buf, size_t len)
{
int i;
for (i = 0; i < len; i++) {
if (buf[i] == ('c' & 037)) {
kmdb_prom_readbuf_tail = kmdb_prom_readbuf_head;
if (mdb.m_intr == 0)
longjmp(mdb.m_frame->f_pcb, MDB_ERR_SIGINT);
else
mdb.m_pend++;
}
}
}
void
kmdb_prom_fill_readbuf(int lossy)
{
int oldhead, left, n;
/*
* Calculate the number of slots left before we wrap around to the
* beginning again.
*/
left = KMDB_PROM_READBUF_SIZE - kmdb_prom_readbuf_head;
if (kmdb_prom_readbuf_tail == 0)
left--;
if (kmdb_prom_readbuf_head == kmdb_prom_readbuf_tail ||
(kmdb_prom_readbuf_head > kmdb_prom_readbuf_tail && left > 0)) {
/*
* head > tail, so we have to read in two parts - the slots
* from head until we wrap back around to zero, and the ones
* from zero to tail. We handle the first part here, and let
* the common code handle the second.
*/
if ((n = kmdb_prom_reader(kmdb_prom_readbuf +
kmdb_prom_readbuf_head, left)) <= 0)
return;
oldhead = kmdb_prom_readbuf_head;
kmdb_prom_readbuf_head = (kmdb_prom_readbuf_head + n) %
KMDB_PROM_READBUF_SIZE;
check_int(kmdb_prom_readbuf + oldhead, n);
if (n != left)
return;
}
left = kmdb_prom_readbuf_tail - kmdb_prom_readbuf_head - 1;
if (left > 0) {
if ((n = kmdb_prom_reader(kmdb_prom_readbuf +
kmdb_prom_readbuf_head, left)) <= 0)
return;
oldhead = kmdb_prom_readbuf_head;
kmdb_prom_readbuf_head = (kmdb_prom_readbuf_head + n) %
KMDB_PROM_READBUF_SIZE;
check_int(kmdb_prom_readbuf + oldhead, n);
if (n != left)
return;
}
if (lossy) {
char c;
while (kmdb_prom_reader(&c, 1) == 1)
check_int(&c, 1);
}
}
void
kmdb_prom_check_interrupt(void)
{
kmdb_prom_fill_readbuf(1);
}
/*
* OBP reads are always non-blocking. If there are characters available,
* we'll return as many as we can. If nothing is available, we'll spin
* until one shows up.
*/
ssize_t
kmdb_prom_read(void *buf, size_t len, struct termios *tio)
{
size_t totread = 0;
size_t thisread;
char *c = (char *)buf;
for (;;) {
kmdb_prom_fill_readbuf(0);
thisread = kmdb_prom_drain_readbuf(c, len);
len -= thisread;
totread += thisread;
c += thisread;
/* wait until something shows up */
if (totread == 0)
continue;
/*
* We're done if we've exhausted available input or if we've
* filled the provided buffer.
*/
if (len == 0 || thisread == 0)
break;
}
if (tio->c_iflag & ICRNL) {
char *cbuf = buf;
int i;
for (i = 0; i < totread; i++) {
if (cbuf[i] == '\r')
cbuf[i] = '\n';
}
}
if (tio->c_lflag & ECHO)
(void) kmdb_prom_write(buf, totread, tio);
return (totread);
}
/*ARGSUSED*/
ssize_t
kmdb_prom_write(const void *bufp, size_t len, struct termios *tio)
{
caddr_t buf = (caddr_t)bufp;
size_t left = len;
char *nl = "\r\n";
char *c;
kmdb_prom_check_interrupt();
if (!(tio->c_oflag & ONLCR))
return (kmdb_prom_writer(buf, left));
/* translate every \n into \r\n */
while ((c = strnchr(buf, '\n', left)) != NULL) {
if (c != buf) {
size_t sz = (size_t)(c - buf);
(void) kmdb_prom_writer(buf, sz);
left -= sz;
}
buf = c + 1;
left--;
(void) kmdb_prom_writer(nl, 2);
}
if (*buf != '\0')
(void) kmdb_prom_writer(buf, left);
return (len);
}
static char *
kmdb_get_ttyio_mode(kmdb_auxv_t *kav, char *devname)
{
char *modepname, *modepval;
modepname = mdb_alloc(strlen(devname) + 5 + 1, UM_SLEEP);
strcpy(modepname, devname);
strcat(modepname, "-mode");
modepval = kmdb_prom_get_options_prop(kav, modepname);
strfree(modepname);
return (modepval);
}
static int
termios_setispeed(struct termios *tip, speed_t s)
{
if (s > (2 * CBAUD + 1))
return (-1);
if ((s << IBSHIFT) > CIBAUD) {
tip->c_cflag |= CIBAUDEXT;
s -= ((CIBAUD >> IBSHIFT) + 1);
} else
tip->c_cflag &= ~CIBAUDEXT;
tip->c_cflag = (tip->c_cflag & ~CIBAUD) | ((s << IBSHIFT) & CIBAUD);
return (0);
}
static int
termios_setospeed(struct termios *tip, speed_t s)
{
if (s > (2 * CBAUD + 1))
return (-1);
if (s > CBAUD) {
tip->c_cflag |= CBAUDEXT;
s -= (CBAUD + 1);
} else
tip->c_cflag &= ~CBAUDEXT;
tip->c_cflag = (tip->c_cflag & ~CBAUD) | (s & CBAUD);
return (0);
}
static int
kmdb_parse_mode(const char *mode, struct termios *tip, int in)
{
static const uint_t baudmap[] = {
0, 50, 75, 110, 134, 150, 200, 300, 600, 1200,
1800, 2400, 4800, 9600, 19200, 38400, 57600,
76800, 115200, 153600, 230400, 307200, 460800
};
static const uint_t bitsmap[] = { CS6, CS6, CS7, CS8 };
char *m = strdup(mode);
char *w;
int rc = -1;
speed_t speed;
int baud, i;
/*
* termios supports different baud rates and flow control types for
* input and output, but it requires character width, parity, and stop
* bits to be equal in input and output. obp allows them to be
* different, but we're going to (silently) assume that nobody will use
* it that way.
*/
/* baud rate - see baudmap above */
if ((w = strtok(m, ",")) == NULL)
goto parse_mode_bail;
baud = strtol(w, NULL, 10);
speed = 0;
for (i = 0; i < sizeof (baudmap) / sizeof (baudmap[0]); i++) {
if (baudmap[i] == baud) {
speed = i;
break;
}
}
if (speed == 0)
goto parse_mode_bail;
if (in == 1)
(void) termios_setispeed(tip, speed);
else
(void) termios_setospeed(tip, speed);
/* character width (bits) - 5, 6, 7, or 8 */
if ((w = strtok(NULL, ",")) == NULL || strlen(w) != 1 || *w < '5' ||
*w > '8')
goto parse_mode_bail;
tip->c_cflag = (tip->c_cflag & ~CSIZE) | bitsmap[*w - '5'];
/* parity - `n' (none), `e' (even), or `o' (odd) */
if ((w = strtok(NULL, ",")) == NULL || strlen(w) != 1 ||
strchr("neo", *w) == NULL)
goto parse_mode_bail;
tip->c_cflag = (tip->c_cflag & ~(PARENB|PARODD));
switch (*w) {
case 'n':
/* nothing */
break;
case 'e':
tip->c_cflag |= PARENB;
break;
case 'o':
tip->c_cflag |= PARENB|PARODD;
break;
}
/*
* stop bits - 1, or 2. obp can, in theory, support 1.5 bits,
* but we can't. how many angels can dance on half of a bit?
*/
if ((w = strtok(NULL, ",")) == NULL || strlen(w) != 1 || *w < '1' ||
*w > '2')
goto parse_mode_bail;
if (*w == '1')
tip->c_cflag &= ~CSTOPB;
else
tip->c_cflag |= CSTOPB;
/* flow control - `-' (none), `h' (h/w), or `s' (s/w - XON/XOFF) */
if ((w = strtok(NULL, ",")) == NULL || strlen(w) != 1 ||
strchr("-hs", *w) == NULL)
goto parse_mode_bail;
tip->c_cflag &= ~(CRTSXOFF|CRTSCTS);
tip->c_iflag &= ~(IXON|IXANY|IXOFF);
switch (*w) {
case 'h':
tip->c_cflag |= (in == 1 ? CRTSXOFF : CRTSCTS);
break;
case 's':
tip->c_iflag |= (in == 1 ? IXOFF : IXON);
break;
}
rc = 0;
parse_mode_bail:
strfree(m);
return (rc);
}
#ifdef __sparc
#define ATTACHED_TERM_TYPE "sun"
#else
#define ATTACHED_TERM_TYPE "sun-color"
#endif
static void
kmdb_prom_term_init(kmdb_auxv_t *kav, kmdb_promif_t *pif)
{
const char ccs[NCCS] = { 0x03, 0x1c, 0x08, 0x15, 0x04, 0x00, 0x00,
0x00, 0x11, 0x13, 0x1a, 0x19, 0x12, 0x0f, 0x17, 0x16 };
char *conin = NULL, *conout = NULL;
if (kmdb_prom_stdout_is_framebuffer(kav))
pif->pif_oterm = ATTACHED_TERM_TYPE;
bzero(&pif->pif_tios, sizeof (struct termios));
/* output device characteristics */
if ((conout = kmdb_prom_get_options_prop(kav, "output-device")) ==
NULL || strcmp(conout, "screen") == 0) {
(void) kmdb_parse_mode(KMDB_PROM_DEF_CONS_MODE,
&pif->pif_tios, 0);
} else if (*conout == '/') {
/*
* We're not going to be able to get characteristics for a
* device that's specified as a path, so don't even try.
* Conveniently, this allows us to avoid chattering on
* Serengetis.
*/
(void) kmdb_parse_mode(KMDB_PROM_DEF_CONS_MODE,
&pif->pif_tios, 0);
} else {
char *mode = kmdb_get_ttyio_mode(kav, conout);
#ifdef __sparc
/*
* Some platforms (Starfire) define a value of `ttya' for
* output-device, but neglect to provide a specific property
* with the characteristics. We'll provide a default value.
*/
if (mode == NULL && strcmp(conout, "ttya") == 0) {
(void) kmdb_parse_mode(KMDB_PROM_DEF_CONS_MODE,
&pif->pif_tios, 0);
} else
#endif
{
if (mode == NULL || kmdb_parse_mode(mode,
&pif->pif_tios, 0) < 0) {
/*
* Either we couldn't retrieve the
* characteristics for this console, or they
* weren't parseable. The console hasn't been
* set up yet, so we can't warn. We'll have to
* silently fall back to the default
* characteristics.
*/
(void) kmdb_parse_mode(KMDB_PROM_DEF_CONS_MODE,
&pif->pif_tios, 0);
}
}
if (mode != NULL)
kmdb_prom_free_options_prop(mode);
}
/* input device characteristics */
if ((conin = kmdb_prom_get_options_prop(kav, "input-device")) == NULL ||
strcmp(conin, "keyboard") == 0) {
(void) kmdb_parse_mode(KMDB_PROM_DEF_CONS_MODE,
&pif->pif_tios, 1);
} else if (*conin == '/') {
/* See similar case in output-device above */
(void) kmdb_parse_mode(KMDB_PROM_DEF_CONS_MODE,
&pif->pif_tios, 1);
} else {
char *mode = kmdb_get_ttyio_mode(kav, conin);
#ifdef __sparc
/*
* Some platforms (Starfire) define a value of `ttya' for
* input-device, but neglect to provide a specific property
* with the characteristics. We'll provide a default value.
*/
if (mode == NULL && strcmp(conin, "ttya") == 0) {
(void) kmdb_parse_mode(KMDB_PROM_DEF_CONS_MODE,
&pif->pif_tios, 1);
} else
#endif
{
if (mode == NULL || kmdb_parse_mode(mode,
&pif->pif_tios, 1) < 0) {
/*
* Either we couldn't retrieve the
* characteristics for this console, or they
* weren't parseable. The console hasn't been
* set up yet, so we can't warn. We'll have to
* silently fall back to the default
* characteristics.
*/
(void) kmdb_parse_mode(KMDB_PROM_DEF_CONS_MODE,
&pif->pif_tios, 1);
}
}
if (mode != NULL)
kmdb_prom_free_options_prop(mode);
}
/* various characteristics of the prom read/write interface */
pif->pif_tios.c_iflag |= ICRNL;
pif->pif_tios.c_lflag |= ECHO;
bcopy(ccs, &pif->pif_tios.c_cc, sizeof (ccs));
if (conin != NULL)
kmdb_prom_free_options_prop(conin);
if (conout != NULL)
kmdb_prom_free_options_prop(conout);
}
char *
kmdb_prom_term_type(void)
{
return (mdb.m_promif->pif_oterm);
}
int
kmdb_prom_term_ctl(int req, void *arg)
{
switch (req) {
case TCGETS: {
struct termios *ti = arg;
bcopy(&mdb.m_promif->pif_tios, ti, sizeof (struct termios));
return (0);
}
case TIOCGWINSZ:
/*
* When kmdb is used over a serial console, we have no idea how
* large the terminal window is. When we're invoked on a local
* console, however, we do, and need to share that information
* with the debugger in order to contradict potentially
* incorrect sizing information retrieved from the terminfo
* database. One specific case where this happens is with the
* Intel console, which is 80x25. The terminfo entry for
* sun-color -- the default terminal type for local Intel
* consoles -- was cloned from sun, which has a height of 34
* rows.
*/
if (mdb.m_promif->pif_oterm != NULL) {
struct winsize *wsz = arg;
wsz->ws_row = KMDB_PIF_WINSIZE_ROWS;
wsz->ws_col = KMDB_PIF_WINSIZE_COLS;
wsz->ws_xpixel = wsz->ws_ypixel = 0;
return (0);
}
return (set_errno(ENOTSUP));
default:
return (set_errno(EINVAL));
}
}
int
kmdb_prom_vtop(uintptr_t virt, physaddr_t *pap)
{
physaddr_t pa;
int rc = kmdb_kdi_vtop(virt, &pa);
#ifdef __sparc
if (rc < 0 && errno == EAGAIN)
rc = kmdb_prom_translate_virt(virt, &pa);
#endif
if (rc == 0 && pap != NULL)
*pap = pa;
return (rc);
}
void
kmdb_prom_debugger_entry(void)
{
/*
* While kmdb_prom_debugger_entry and kmdb_prom_debugger_exit are not
* guaranteed to be called an identical number of times (an intentional
* debugger fault will cause an additional entry call without a matching
* exit call), we must ensure that the polled I/O entry and exit calls
* match.
*/
if (mdb.m_pio == NULL) {
mdb.m_pio = kmdb_kdi_get_polled_io();
if (mdb.m_pio != NULL &&
mdb.m_pio->cons_polledio_enter != NULL) {
(void) kmdb_dpi_call(
(uintptr_t)mdb.m_pio->cons_polledio_enter, 1,
(uintptr_t *)&mdb.m_pio->cons_polledio_argument);
}
}
}
void
kmdb_prom_debugger_exit(void)
{
if (mdb.m_pio != NULL && mdb.m_pio->cons_polledio_exit != NULL) {
(void) kmdb_dpi_call((uintptr_t)mdb.m_pio->cons_polledio_exit,
1, (uintptr_t *)&mdb.m_pio->cons_polledio_argument);
}
mdb.m_pio = NULL;
}
#ifdef DEBUG
/*
* The prom_* files use ASSERT, which is #defined as assfail().
* We need to redirect that to our assert function.
*/
int
kmdb_prom_assfail(const char *assertion, const char *file, int line)
{
(void) mdb_dassert(assertion, file, line);
/*NOTREACHED*/
return (0);
}
#endif
/*
* Begin the initialization of the debugger/PROM interface. Initialization is
* performed in two steps due to interlocking dependencies between promif and
* both the memory allocator and mdb_create. The first phase is performed
* before either of the others have been initialized, and thus must neither
* attempt to allocate memory nor access/write to `mdb'.
*/
void
kmdb_prom_init_begin(char *pgmname, kmdb_auxv_t *kav)
{
prom_init(pgmname, kav->kav_romp);
/* Initialize the interrupt ring buffer */
kmdb_prom_readbuf_head = kmdb_prom_readbuf_tail;
#if defined(__i386) || defined(__amd64)
kmdb_sysp = kav->kav_romp;
#endif
}
/*
* Conclude the initialization of the debugger/PROM interface. Memory
* allocation and the global `mdb' object are now available.
*/
void
kmdb_prom_init_finish(kmdb_auxv_t *kav)
{
mdb.m_promif = mdb_zalloc(sizeof (kmdb_promif_t), UM_SLEEP);
kmdb_prom_term_init(kav, mdb.m_promif);
}