1*3446Smrj /* 2*3446Smrj * CDDL HEADER START 3*3446Smrj * 4*3446Smrj * The contents of this file are subject to the terms of the 5*3446Smrj * Common Development and Distribution License (the "License"). 6*3446Smrj * You may not use this file except in compliance with the License. 7*3446Smrj * 8*3446Smrj * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*3446Smrj * or http://www.opensolaris.org/os/licensing. 10*3446Smrj * See the License for the specific language governing permissions 11*3446Smrj * and limitations under the License. 12*3446Smrj * 13*3446Smrj * When distributing Covered Code, include this CDDL HEADER in each 14*3446Smrj * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*3446Smrj * If applicable, add the following below this CDDL HEADER, with the 16*3446Smrj * fields enclosed by brackets "[]" replaced with your own identifying 17*3446Smrj * information: Portions Copyright [yyyy] [name of copyright owner] 18*3446Smrj * 19*3446Smrj * CDDL HEADER END 20*3446Smrj */ 21*3446Smrj /* 22*3446Smrj * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23*3446Smrj * Use is subject to license terms. 24*3446Smrj */ 25*3446Smrj 26*3446Smrj #pragma ident "%Z%%M% %I% %E% SMI" 27*3446Smrj 28*3446Smrj #include <sys/types.h> 29*3446Smrj #include <sys/systm.h> 30*3446Smrj #include <sys/archsystm.h> 31*3446Smrj #include <sys/boot_console.h> 32*3446Smrj 33*3446Smrj #include "boot_serial.h" 34*3446Smrj #include "boot_vga.h" 35*3446Smrj 36*3446Smrj #if defined(_BOOT) 37*3446Smrj #include "../dboot/dboot_xboot.h" 38*3446Smrj #include <util/string.h> 39*3446Smrj #else 40*3446Smrj #include <sys/bootconf.h> 41*3446Smrj static char *usbser_buf; 42*3446Smrj static char *usbser_cur; 43*3446Smrj #endif 44*3446Smrj 45*3446Smrj static int cons_color = CONS_COLOR; 46*3446Smrj int console = CONS_SCREEN_TEXT; 47*3446Smrj /* or CONS_TTYA, CONS_TTYB */ 48*3446Smrj static int serial_ischar(void); 49*3446Smrj static int serial_getchar(void); 50*3446Smrj static void serial_putchar(int); 51*3446Smrj static void serial_adjust_prop(void); 52*3446Smrj 53*3446Smrj static char *boot_line = NULL; 54*3446Smrj 55*3446Smrj /* Clear the screen and initialize VIDEO, XPOS and YPOS. */ 56*3446Smrj static void 57*3446Smrj clear_screen(void) 58*3446Smrj { 59*3446Smrj /* 60*3446Smrj * XXX should set vga mode so we don't depend on the 61*3446Smrj * state left by the boot loader 62*3446Smrj */ 63*3446Smrj vga_clear(cons_color); 64*3446Smrj vga_setpos(0, 0); 65*3446Smrj } 66*3446Smrj 67*3446Smrj /* Put the character C on the screen. */ 68*3446Smrj static void 69*3446Smrj screen_putchar(int c) 70*3446Smrj { 71*3446Smrj int row, col; 72*3446Smrj 73*3446Smrj vga_getpos(&row, &col); 74*3446Smrj switch (c) { 75*3446Smrj case '\t': 76*3446Smrj col += 8 - (col % 8); 77*3446Smrj if (col == VGA_TEXT_COLS) 78*3446Smrj col = 79; 79*3446Smrj vga_setpos(row, col); 80*3446Smrj break; 81*3446Smrj 82*3446Smrj case '\r': 83*3446Smrj vga_setpos(row, 0); 84*3446Smrj break; 85*3446Smrj 86*3446Smrj case '\b': 87*3446Smrj if (col > 0) 88*3446Smrj vga_setpos(row, col - 1); 89*3446Smrj break; 90*3446Smrj 91*3446Smrj case '\n': 92*3446Smrj if (row < VGA_TEXT_ROWS - 1) 93*3446Smrj vga_setpos(row + 1, col); 94*3446Smrj else 95*3446Smrj vga_scroll(cons_color); 96*3446Smrj break; 97*3446Smrj 98*3446Smrj default: 99*3446Smrj vga_drawc(c, cons_color); 100*3446Smrj if (col < VGA_TEXT_COLS -1) 101*3446Smrj vga_setpos(row, col + 1); 102*3446Smrj else if (row < VGA_TEXT_ROWS - 1) 103*3446Smrj vga_setpos(row + 1, 0); 104*3446Smrj else { 105*3446Smrj vga_setpos(row, 0); 106*3446Smrj vga_scroll(cons_color); 107*3446Smrj } 108*3446Smrj break; 109*3446Smrj } 110*3446Smrj } 111*3446Smrj 112*3446Smrj /* serial port stuff */ 113*3446Smrj static int port; 114*3446Smrj 115*3446Smrj static void 116*3446Smrj serial_init(void) 117*3446Smrj { 118*3446Smrj switch (console) { 119*3446Smrj case CONS_TTYA: 120*3446Smrj port = 0x3f8; 121*3446Smrj break; 122*3446Smrj case CONS_TTYB: 123*3446Smrj port = 0x2f8; 124*3446Smrj break; 125*3446Smrj } 126*3446Smrj 127*3446Smrj outb(port + ISR, 0x20); 128*3446Smrj if (inb(port + ISR) & 0x20) { 129*3446Smrj /* 130*3446Smrj * 82510 chip is present 131*3446Smrj */ 132*3446Smrj outb(port + DAT+7, 0x04); /* clear status */ 133*3446Smrj outb(port + ISR, 0x40); /* set to bank 2 */ 134*3446Smrj outb(port + MCR, 0x08); /* IMD */ 135*3446Smrj outb(port + DAT, 0x21); /* FMD */ 136*3446Smrj outb(port + ISR, 0x00); /* set to bank 0 */ 137*3446Smrj } else { 138*3446Smrj /* 139*3446Smrj * set the UART in FIFO mode if it has FIFO buffers. 140*3446Smrj * use 16550 fifo reset sequence specified in NS 141*3446Smrj * application note. disable fifos until chip is 142*3446Smrj * initialized. 143*3446Smrj */ 144*3446Smrj outb(port + FIFOR, 0x00); /* clear */ 145*3446Smrj outb(port + FIFOR, FIFO_ON); /* enable */ 146*3446Smrj outb(port + FIFOR, FIFO_ON|FIFORXFLSH); /* reset */ 147*3446Smrj outb(port + FIFOR, 148*3446Smrj FIFO_ON|FIFODMA|FIFOTXFLSH|FIFORXFLSH|0x80); 149*3446Smrj if ((inb(port + ISR) & 0xc0) != 0xc0) { 150*3446Smrj /* 151*3446Smrj * no fifo buffers so disable fifos. 152*3446Smrj * this is true for 8250's 153*3446Smrj */ 154*3446Smrj outb(port + FIFOR, 0x00); 155*3446Smrj } 156*3446Smrj } 157*3446Smrj 158*3446Smrj /* disable interrupts */ 159*3446Smrj outb(port + ICR, 0); 160*3446Smrj 161*3446Smrj /* adjust setting based on tty properties */ 162*3446Smrj serial_adjust_prop(); 163*3446Smrj 164*3446Smrj #if defined(_BOOT) 165*3446Smrj /* 166*3446Smrj * Do a full reset to match console behavior. 167*3446Smrj * 0x1B + c - reset everything 168*3446Smrj */ 169*3446Smrj serial_putchar(0x1B); 170*3446Smrj serial_putchar('c'); 171*3446Smrj #endif 172*3446Smrj } 173*3446Smrj 174*3446Smrj 175*3446Smrj #define MATCHES(p, pat) \ 176*3446Smrj (strncmp(p, pat, strlen(pat)) == 0 ? (p += strlen(pat), 1) : 0) 177*3446Smrj 178*3446Smrj #define SKIP(p, c) \ 179*3446Smrj while (*(p) != 0 && *p != (c)) \ 180*3446Smrj ++(p); \ 181*3446Smrj if (*(p) == (c)) \ 182*3446Smrj ++(p); 183*3446Smrj 184*3446Smrj /* 185*3446Smrj * find a tty mode property either from cmdline or from boot properties 186*3446Smrj */ 187*3446Smrj static char * 188*3446Smrj get_mode_value(char *name) 189*3446Smrj { 190*3446Smrj char *p; 191*3446Smrj 192*3446Smrj /* 193*3446Smrj * when specified on boot line it looks like "name" "=".... 194*3446Smrj */ 195*3446Smrj if (boot_line != NULL) { 196*3446Smrj p = strstr(boot_line, name); 197*3446Smrj if (p == NULL) 198*3446Smrj return (NULL); 199*3446Smrj SKIP(p, '='); 200*3446Smrj return (p); 201*3446Smrj } 202*3446Smrj 203*3446Smrj #if defined(_BOOT) 204*3446Smrj return (NULL); 205*3446Smrj #else 206*3446Smrj /* 207*3446Smrj * if we're running in the full kernel we check the bootenv.rc settings 208*3446Smrj */ 209*3446Smrj { 210*3446Smrj static char propval[20]; 211*3446Smrj 212*3446Smrj propval[0] = 0; 213*3446Smrj if (bootops == NULL || BOP_GETPROPLEN(bootops, name) == 0) 214*3446Smrj return (NULL); 215*3446Smrj (void) BOP_GETPROP(bootops, name, propval); 216*3446Smrj return (propval); 217*3446Smrj } 218*3446Smrj #endif 219*3446Smrj } 220*3446Smrj 221*3446Smrj /* 222*3446Smrj * adjust serial port based on properties 223*3446Smrj * These come either from the cmdline or from boot properties. 224*3446Smrj */ 225*3446Smrj static void 226*3446Smrj serial_adjust_prop(void) 227*3446Smrj { 228*3446Smrj char propname[20]; 229*3446Smrj char *propval; 230*3446Smrj char *p; 231*3446Smrj ulong_t baud; 232*3446Smrj uchar_t lcr = 0; 233*3446Smrj uchar_t mcr = DTR | RTS; 234*3446Smrj 235*3446Smrj (void) strcpy(propname, "ttyX-mode"); 236*3446Smrj propname[3] = 'a' + console - CONS_TTYA; 237*3446Smrj propval = get_mode_value(propname); 238*3446Smrj if (propval == NULL) 239*3446Smrj propval = "9600,8,n,1,-"; 240*3446Smrj 241*3446Smrj /* property is of the form: "9600,8,n,1,-" */ 242*3446Smrj p = propval; 243*3446Smrj if (MATCHES(p, "110,")) 244*3446Smrj baud = ASY110; 245*3446Smrj else if (MATCHES(p, "150,")) 246*3446Smrj baud = ASY150; 247*3446Smrj else if (MATCHES(p, "300,")) 248*3446Smrj baud = ASY300; 249*3446Smrj else if (MATCHES(p, "600,")) 250*3446Smrj baud = ASY600; 251*3446Smrj else if (MATCHES(p, "1200,")) 252*3446Smrj baud = ASY1200; 253*3446Smrj else if (MATCHES(p, "2400,")) 254*3446Smrj baud = ASY2400; 255*3446Smrj else if (MATCHES(p, "4800,")) 256*3446Smrj baud = ASY4800; 257*3446Smrj else if (MATCHES(p, "19200,")) 258*3446Smrj baud = ASY19200; 259*3446Smrj else if (MATCHES(p, "38400,")) 260*3446Smrj baud = ASY38400; 261*3446Smrj else if (MATCHES(p, "57600,")) 262*3446Smrj baud = ASY57600; 263*3446Smrj else if (MATCHES(p, "115200,")) 264*3446Smrj baud = ASY115200; 265*3446Smrj else { 266*3446Smrj baud = ASY9600; 267*3446Smrj SKIP(p, ','); 268*3446Smrj } 269*3446Smrj outb(port + LCR, DLAB); 270*3446Smrj outb(port + DAT + DLL, baud & 0xff); 271*3446Smrj outb(port + DAT + DLH, (baud >> 8) & 0xff); 272*3446Smrj 273*3446Smrj switch (*p) { 274*3446Smrj case '5': 275*3446Smrj lcr |= BITS5; 276*3446Smrj ++p; 277*3446Smrj break; 278*3446Smrj case '6': 279*3446Smrj lcr |= BITS6; 280*3446Smrj ++p; 281*3446Smrj break; 282*3446Smrj case '7': 283*3446Smrj lcr |= BITS7; 284*3446Smrj ++p; 285*3446Smrj break; 286*3446Smrj case '8': 287*3446Smrj ++p; 288*3446Smrj default: 289*3446Smrj lcr |= BITS8; 290*3446Smrj break; 291*3446Smrj } 292*3446Smrj 293*3446Smrj SKIP(p, ','); 294*3446Smrj 295*3446Smrj switch (*p) { 296*3446Smrj case 'n': 297*3446Smrj lcr |= PARITY_NONE; 298*3446Smrj ++p; 299*3446Smrj break; 300*3446Smrj case 'o': 301*3446Smrj lcr |= PARITY_ODD; 302*3446Smrj ++p; 303*3446Smrj break; 304*3446Smrj case 'e': 305*3446Smrj ++p; 306*3446Smrj default: 307*3446Smrj lcr |= PARITY_EVEN; 308*3446Smrj break; 309*3446Smrj } 310*3446Smrj 311*3446Smrj 312*3446Smrj SKIP(p, ','); 313*3446Smrj 314*3446Smrj switch (*p) { 315*3446Smrj case '1': 316*3446Smrj /* STOP1 is 0 */ 317*3446Smrj ++p; 318*3446Smrj break; 319*3446Smrj default: 320*3446Smrj lcr |= STOP2; 321*3446Smrj break; 322*3446Smrj } 323*3446Smrj /* set parity bits */ 324*3446Smrj outb(port + LCR, lcr); 325*3446Smrj 326*3446Smrj (void) strcpy(propname, "ttyX-rts-dtr-off"); 327*3446Smrj propname[3] = 'a' + console - CONS_TTYA; 328*3446Smrj propval = get_mode_value(propname); 329*3446Smrj if (propval == NULL) 330*3446Smrj propval = "false"; 331*3446Smrj if (propval[0] != 'f' && propval[0] != 'F') 332*3446Smrj mcr = 0; 333*3446Smrj /* set modem control bits */ 334*3446Smrj outb(port + MCR, mcr | OUT2); 335*3446Smrj } 336*3446Smrj 337*3446Smrj void 338*3446Smrj bcons_init(char *bootstr) 339*3446Smrj { 340*3446Smrj boot_line = bootstr; 341*3446Smrj console = CONS_INVALID; 342*3446Smrj 343*3446Smrj if (strstr(bootstr, "console=ttya") != 0) 344*3446Smrj console = CONS_TTYA; 345*3446Smrj else if (strstr(bootstr, "console=ttyb") != 0) 346*3446Smrj console = CONS_TTYB; 347*3446Smrj else if (strstr(bootstr, "console=text") != 0) 348*3446Smrj console = CONS_SCREEN_TEXT; 349*3446Smrj 350*3446Smrj /* 351*3446Smrj * If no console device specified, default to text. 352*3446Smrj * Remember what was specified for second phase. 353*3446Smrj */ 354*3446Smrj if (console == CONS_INVALID) 355*3446Smrj console = CONS_SCREEN_TEXT; 356*3446Smrj 357*3446Smrj switch (console) { 358*3446Smrj case CONS_TTYA: 359*3446Smrj case CONS_TTYB: 360*3446Smrj serial_init(); 361*3446Smrj break; 362*3446Smrj 363*3446Smrj case CONS_SCREEN_TEXT: 364*3446Smrj default: 365*3446Smrj #if defined(_BOOT) 366*3446Smrj clear_screen(); /* clears the grub screen */ 367*3446Smrj #endif 368*3446Smrj kb_init(); 369*3446Smrj break; 370*3446Smrj } 371*3446Smrj boot_line = NULL; 372*3446Smrj } 373*3446Smrj 374*3446Smrj /* 375*3446Smrj * 2nd part of console initialization. 376*3446Smrj * In the kernel (ie. fakebop), this can be used only to switch to 377*3446Smrj * using a serial port instead of screen based on the contents 378*3446Smrj * of the bootenv.rc file. 379*3446Smrj */ 380*3446Smrj /*ARGSUSED*/ 381*3446Smrj void 382*3446Smrj bcons_init2(char *inputdev, char *outputdev, char *consoledev) 383*3446Smrj { 384*3446Smrj #if !defined(_BOOT) 385*3446Smrj int cons = CONS_INVALID; 386*3446Smrj 387*3446Smrj if (consoledev) { 388*3446Smrj if (strstr(consoledev, "ttya") != 0) 389*3446Smrj cons = CONS_TTYA; 390*3446Smrj else if (strstr(consoledev, "ttyb") != 0) 391*3446Smrj cons = CONS_TTYB; 392*3446Smrj else if (strstr(consoledev, "usb-serial") != 0) 393*3446Smrj cons = CONS_USBSER; 394*3446Smrj } 395*3446Smrj 396*3446Smrj if (cons == CONS_INVALID && inputdev) { 397*3446Smrj if (strstr(inputdev, "ttya") != 0) 398*3446Smrj cons = CONS_TTYA; 399*3446Smrj else if (strstr(inputdev, "ttyb") != 0) 400*3446Smrj cons = CONS_TTYB; 401*3446Smrj else if (strstr(inputdev, "usb-serial") != 0) 402*3446Smrj cons = CONS_USBSER; 403*3446Smrj } 404*3446Smrj 405*3446Smrj if (cons == CONS_INVALID && outputdev) { 406*3446Smrj if (strstr(outputdev, "ttya") != 0) 407*3446Smrj cons = CONS_TTYA; 408*3446Smrj else if (strstr(outputdev, "ttyb") != 0) 409*3446Smrj cons = CONS_TTYB; 410*3446Smrj else if (strstr(outputdev, "usb-serial") != 0) 411*3446Smrj cons = CONS_USBSER; 412*3446Smrj } 413*3446Smrj 414*3446Smrj if (cons == CONS_INVALID) 415*3446Smrj return; 416*3446Smrj if (cons == console) 417*3446Smrj return; 418*3446Smrj 419*3446Smrj console = cons; 420*3446Smrj if (cons == CONS_TTYA || cons == CONS_TTYB) { 421*3446Smrj serial_init(); 422*3446Smrj return; 423*3446Smrj } 424*3446Smrj 425*3446Smrj /* 426*3446Smrj * USB serial -- we just collect data into a buffer 427*3446Smrj */ 428*3446Smrj if (cons == CONS_USBSER) { 429*3446Smrj extern void *usbser_init(size_t); 430*3446Smrj usbser_buf = usbser_cur = usbser_init(MMU_PAGESIZE); 431*3446Smrj } 432*3446Smrj #endif /* _BOOT */ 433*3446Smrj } 434*3446Smrj 435*3446Smrj #if !defined(_BOOT) 436*3446Smrj static void 437*3446Smrj usbser_putchar(int c) 438*3446Smrj { 439*3446Smrj if (usbser_cur - usbser_buf < MMU_PAGESIZE) 440*3446Smrj *usbser_cur++ = c; 441*3446Smrj } 442*3446Smrj #endif /* _BOOT */ 443*3446Smrj 444*3446Smrj static void 445*3446Smrj serial_putchar(int c) 446*3446Smrj { 447*3446Smrj int checks = 10000; 448*3446Smrj 449*3446Smrj while (((inb(port + LSR) & XHRE) == 0) && checks--) 450*3446Smrj ; 451*3446Smrj outb(port + DAT, (char)c); 452*3446Smrj } 453*3446Smrj 454*3446Smrj static int 455*3446Smrj serial_getchar(void) 456*3446Smrj { 457*3446Smrj uchar_t lsr; 458*3446Smrj 459*3446Smrj while (serial_ischar() == 0) 460*3446Smrj ; 461*3446Smrj 462*3446Smrj lsr = inb(port + LSR); 463*3446Smrj if (lsr & (SERIAL_BREAK | SERIAL_FRAME | 464*3446Smrj SERIAL_PARITY | SERIAL_OVERRUN)) { 465*3446Smrj if (lsr & SERIAL_OVERRUN) { 466*3446Smrj return (inb(port + DAT)); 467*3446Smrj } else { 468*3446Smrj /* Toss the garbage */ 469*3446Smrj (void) inb(port + DAT); 470*3446Smrj return (0); 471*3446Smrj } 472*3446Smrj } 473*3446Smrj return (inb(port + DAT)); 474*3446Smrj } 475*3446Smrj 476*3446Smrj static int 477*3446Smrj serial_ischar(void) 478*3446Smrj { 479*3446Smrj return (inb(port + LSR) & RCA); 480*3446Smrj } 481*3446Smrj 482*3446Smrj static void 483*3446Smrj _doputchar(int c) 484*3446Smrj { 485*3446Smrj switch (console) { 486*3446Smrj case CONS_TTYA: 487*3446Smrj case CONS_TTYB: 488*3446Smrj serial_putchar(c); 489*3446Smrj return; 490*3446Smrj case CONS_SCREEN_TEXT: 491*3446Smrj screen_putchar(c); 492*3446Smrj return; 493*3446Smrj #if !defined(_BOOT) 494*3446Smrj case CONS_USBSER: 495*3446Smrj usbser_putchar(c); 496*3446Smrj return; 497*3446Smrj #endif /* _BOOT */ 498*3446Smrj } 499*3446Smrj } 500*3446Smrj 501*3446Smrj void 502*3446Smrj bcons_putchar(int c) 503*3446Smrj { 504*3446Smrj static int bhcharpos = 0; 505*3446Smrj 506*3446Smrj if (c == '\t') { 507*3446Smrj do { 508*3446Smrj _doputchar(' '); 509*3446Smrj } while (++bhcharpos % 8); 510*3446Smrj return; 511*3446Smrj } else if (c == '\n' || c == '\r') { 512*3446Smrj bhcharpos = 0; 513*3446Smrj _doputchar('\r'); 514*3446Smrj _doputchar(c); 515*3446Smrj return; 516*3446Smrj } else if (c == '\b') { 517*3446Smrj if (bhcharpos) 518*3446Smrj bhcharpos--; 519*3446Smrj _doputchar(c); 520*3446Smrj return; 521*3446Smrj } 522*3446Smrj 523*3446Smrj bhcharpos++; 524*3446Smrj _doputchar(c); 525*3446Smrj } 526*3446Smrj 527*3446Smrj /* 528*3446Smrj * kernel character input functions 529*3446Smrj */ 530*3446Smrj int 531*3446Smrj bcons_getchar(void) 532*3446Smrj { 533*3446Smrj switch (console) { 534*3446Smrj case CONS_TTYA: 535*3446Smrj case CONS_TTYB: 536*3446Smrj return (serial_getchar()); 537*3446Smrj default: 538*3446Smrj return (kb_getchar()); 539*3446Smrj } 540*3446Smrj } 541*3446Smrj 542*3446Smrj #if !defined(_BOOT) 543*3446Smrj 544*3446Smrj int 545*3446Smrj bcons_ischar(void) 546*3446Smrj { 547*3446Smrj switch (console) { 548*3446Smrj case CONS_TTYA: 549*3446Smrj case CONS_TTYB: 550*3446Smrj return (serial_ischar()); 551*3446Smrj default: 552*3446Smrj return (kb_ischar()); 553*3446Smrj } 554*3446Smrj } 555*3446Smrj 556*3446Smrj #endif /* _BOOT */ 557