/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" #include #include #include #ifdef sun4v #include #endif #include #include #include #include #include #include #include #include #include #include #include #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; static 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); } static 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 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 an interrupt immediately. * * In a read context, we can deliver the interrupt character directly back to * the termio handler rather than raising an interrupt. * * 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. */ static 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] == CTRL('c')) { 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++; } } } /* * Attempt to refill the ring buffer from the input stream. This called from * two contexts: * * Direct read: read the input into our buffer until input is exhausted, or the * buffer is full. * * Interrupt check: called 'asynchronously' from the normal read routines; read * the input into our buffer until it is exhausted, discarding input if the * buffer is full. In this case we look ahead for any interrupt characters, * delivering an interrupt directly if we find one. */ static void kmdb_prom_fill_readbuf(int check_for_int) { 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; if (check_for_int) 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; if (check_for_int) check_int(kmdb_prom_readbuf + oldhead, n); if (n != left) return; } if (check_for_int) { 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_ddi_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_ddi_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_ddi_prop(mode); } /* input device characteristics */ if ((conin = kmdb_prom_get_ddi_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_ddi_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_ddi_prop(conin); if (conout != NULL) kmdb_prom_free_ddi_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) { #ifdef sun4v if (kav->kav_domaining) kmdb_prom_init_promif(pgmname, kav); else prom_init(pgmname, kav->kav_romp); #else prom_init(pgmname, kav->kav_romp); #endif /* Initialize the interrupt ring buffer */ kmdb_prom_readbuf_head = kmdb_prom_readbuf_tail; #if defined(__i386) || defined(__amd64) kmdb_sysp = kav->kav_romp; #endif } #ifdef sun4v void kmdb_prom_init_promif(char *pgmname, kmdb_auxv_t *kav) { ASSERT(kav->kav_domaining); cif_init(pgmname, kav->kav_promif_root, kav->kav_promif_in, kav->kav_promif_out, kav->kav_promif_pin, kav->kav_promif_pout, kav->kav_promif_chosennode, kav->kav_promif_optionsnode); } #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); }