console.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
* 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"
#define MINLINES 10
#define MAXLINES 48
#define LOSCREENLINES 34
#define HISCREENLINES 48
#define MINCOLS 10
#define MAXCOLS 120
#define LOSCREENCOLS 80
#define HISCREENCOLS 120
/*
* The current set of polled I/O routines (if any)
*/
struct cons_polledio *cons_polledio;
/*
* Console I/O Routines
*
* In the event that kernel messages are generated with cmn_err(9F) or printf()
* early in boot, after a panic, in resource-constrained situations, or sent
* characters directly to the frame buffer using the underlying prom_*()
* routines. These in turn may attempt to use PROM services directly, or may
* use a kernel console emulator if one is available. Unfortunately, if PROM
* services are being used by the kernel on a multi-CPU system, these routines
* might be called while another CPU is simultaneously accessing a frame buffer
* memory mapping (perhaps through the X server). This situation may not be
* supported by the frame buffer hardware.
*
* To handle this situation, we implement a two-phase locking scheme which we
* use to protect accesses to the underlying prom_*() rendering routines. The
* common-code functions console_hold() and console_rele() are used to gain
* exclusive access to the console from within the kernel. We use a standard
* r/w lock in writer-mode only to implement the kernel lock. We use an r/w
* lock instead of a mutex here because character rendering is slow and hold
* times will be relatively long, and there is no point in adaptively spinning.
* These routines may be called recursively, in which case subsequent calls
* just increment the console_depth hold count. Once exclusive access is
* gained, we grab the frame buffer device node and block further mappings to
* it by holding the specfs node lock and the device node's lock. We then
* observe if any mappings are present by examining the specfs node's s_mapcnt
* (non-clone mmaps) and the devinfo node's devi_ref count (clone opens).
*
* Then, around each character rendering call, the routines console_enter()
* and console_exit() are used to inform the platform code that we are
* accessing the character rendering routines. These platform routines can
* then examine the "busy" flag returned by console_enter() and briefly stop
* the other CPUs so that they cannot access the frame buffer hardware while
* we are busy rendering characters. This mess can all be removed when the
* impossible dream of a unified kernel console emulator is someday realized.
*/
static krwlock_t console_lock;
static uint_t console_depth;
static int console_busy;
extern void pm_cfb_check_and_powerup(void);
extern void pm_cfb_rele(void);
static int
console_hold(void)
{
return (console_busy); /* assume exclusive access in panic */
if (console_depth++ != 0)
return (console_busy); /* lock is being entered recursively */
#ifdef _CONSOLE_OUTPUT_VIA_FIRMWARE
}
}
#endif
return (console_busy);
}
static void
console_rele(void)
{
return; /* do not modify lock states if we are panicking */
ASSERT(console_depth != 0);
if (--console_depth != 0)
return; /* lock is being dropped recursively */
#ifdef _CONSOLE_OUTPUT_VIA_FIRMWARE
}
#endif
pm_cfb_rele();
console_busy = 0;
}
static void
{
uint_t i;
*sp = 0;
for (i = 0; i < len; i++) {
break;
}
}
}
/*
* Gets the number of rows and columns (in char's) and the
* width and height (in pixels) of the console.
*/
void
{
int rel_needed = 0;
/*
* If we have loaded the console IO stuff, then ask for the screen
* size properties from the layered terminal emulator. Else ask for
* them from the root node, which will eventually fall through to the
* options node and get them from the prom.
*/
dip = ddi_root_node();
dev = DDI_DEV_T_ANY;
} else {
rel_needed = 1;
}
/*
* If we have not initialized a console yet and don't have a root
* node (ie. we have not initialized the DDI yet) return our default
* size for the screen.
*/
*r = LOSCREENLINES;
*c = LOSCREENCOLS;
*x = *y = 0;
return;
}
if (*c < MINCOLS)
*c = LOSCREENCOLS;
else if (*c > MAXCOLS)
*c = HISCREENCOLS;
if (*r < MINLINES)
*r = LOSCREENLINES;
else if (*r > MAXLINES)
*r = HISCREENLINES;
if (rel_needed)
}
typedef struct console_msg {
char cm_text[1];
static void
{
busy = console_hold();
console_rele();
}
}
void
{
cm, TQ_NOSLEEP) != 0)
return;
}
busy = console_hold();
console_rele();
}
/*PRINTFLIKE1*/
void
console_printf(const char *fmt, ...)
{
}
/*
* Write the specified number of bytes from a string to the console device.
*/
void
console_puts(const char *s, size_t n)
{
busy = console_hold();
prom_writestr(s, n);
console_rele();
}
void
console_putc(int c)
{
int busy = console_hold();
if (c == '\n')
prom_putchar('\r');
prom_putchar(c);
console_rele();
}
/*
* Read a string from the console device. We only permit synchronous
* conversation between the kernel and a console user early in boot prior to
* the initialization of rconsvp.
*/
void
{
char *p = s;
char *q = s + len - 1;
int c;
(void) console_hold();
for (;;) {
switch (c = (prom_getchar() & 0x7f)) {
case 0x7f: /* DEL */
if (p == s)
break;
console_putc(c);
c = '\b';
/*FALLTHRU*/
case '\b':
if (p == s)
break;
console_putc('\b');
console_putc(' ');
/*FALLTHRU*/
case '#': /* historical backspace alias */
console_putc(c);
if (p > s)
p--;
break;
case CTRL('u'):
console_putc(c);
console_putc('\n');
p = s;
break;
case '\r':
case '\n':
console_putc('\n');
goto done;
default:
if (p < q) {
console_putc(c);
*p++ = c;
} else
console_putc('\a');
}
}
done:
console_rele();
*p = '\0';
}
/*
* Read a character from the console device. Synchronous conversation between
* the kernel and a console user is only permitted early in boot prior to the
* initialization of rconsvp.
*/
int
console_getc(void)
{
int c;
c = prom_getchar();
if (c == '\r')
c = '\n';
console_putc(c);
return (c);
}