1ae115bc7Smrj /* 2ae115bc7Smrj * CDDL HEADER START 3ae115bc7Smrj * 4ae115bc7Smrj * The contents of this file are subject to the terms of the 5ae115bc7Smrj * Common Development and Distribution License (the "License"). 6ae115bc7Smrj * You may not use this file except in compliance with the License. 7ae115bc7Smrj * 8ae115bc7Smrj * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9ae115bc7Smrj * or http://www.opensolaris.org/os/licensing. 10ae115bc7Smrj * See the License for the specific language governing permissions 11ae115bc7Smrj * and limitations under the License. 12ae115bc7Smrj * 13ae115bc7Smrj * When distributing Covered Code, include this CDDL HEADER in each 14ae115bc7Smrj * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15ae115bc7Smrj * If applicable, add the following below this CDDL HEADER, with the 16ae115bc7Smrj * fields enclosed by brackets "[]" replaced with your own identifying 17ae115bc7Smrj * information: Portions Copyright [yyyy] [name of copyright owner] 18ae115bc7Smrj * 19ae115bc7Smrj * CDDL HEADER END 20ae115bc7Smrj */ 21ae115bc7Smrj /* 220d928757SGary Mills * Copyright (c) 2012 Gary Mills 232e6e9b6bSJohn Levon * Copyright 2020 Joyent, Inc. 240d928757SGary Mills * 2548633f18SJan Setje-Eilers * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 26ae115bc7Smrj * Use is subject to license terms. 27ae115bc7Smrj */ 28ae115bc7Smrj 292e6e9b6bSJohn Levon /* 302e6e9b6bSJohn Levon * Boot console support. Most of the file is shared between dboot, and the 312e6e9b6bSJohn Levon * early kernel / fakebop. 322e6e9b6bSJohn Levon */ 332e6e9b6bSJohn Levon 34ae115bc7Smrj #include <sys/types.h> 35ae115bc7Smrj #include <sys/systm.h> 36ae115bc7Smrj #include <sys/archsystm.h> 378e6d016fSToomas Soome #include <sys/framebuffer.h> 38ae115bc7Smrj #include <sys/boot_console.h> 39843e1988Sjohnlev #include <sys/panic.h> 40adb91f47Srscott #include <sys/ctype.h> 418e6d016fSToomas Soome #include <sys/ascii.h> 428e6d016fSToomas Soome #include <sys/vgareg.h> 43843e1988Sjohnlev #if defined(__xpv) 44843e1988Sjohnlev #include <sys/hypervisor.h> 45843e1988Sjohnlev #endif /* __xpv */ 46ae115bc7Smrj 478e6d016fSToomas Soome #include "boot_console_impl.h" 48ae115bc7Smrj #include "boot_serial.h" 49ae115bc7Smrj 50ae115bc7Smrj #if defined(_BOOT) 51843e1988Sjohnlev #include <dboot/dboot_asm.h> 52adb91f47Srscott #include <dboot/dboot_xboot.h> 53843e1988Sjohnlev #else /* _BOOT */ 54ae115bc7Smrj #include <sys/bootconf.h> 55843e1988Sjohnlev #if defined(__xpv) 56843e1988Sjohnlev #include <sys/evtchn_impl.h> 57843e1988Sjohnlev #endif /* __xpv */ 5848633f18SJan Setje-Eilers static char *defcons_buf; 5948633f18SJan Setje-Eilers static char *defcons_cur; 60843e1988Sjohnlev #endif /* _BOOT */ 61843e1988Sjohnlev 62843e1988Sjohnlev #if defined(__xpv) 63843e1988Sjohnlev extern void bcons_init_xen(char *); 64843e1988Sjohnlev extern void bcons_putchar_xen(int); 65843e1988Sjohnlev extern int bcons_getchar_xen(void); 66843e1988Sjohnlev extern int bcons_ischar_xen(void); 67843e1988Sjohnlev #endif /* __xpv */ 68ae115bc7Smrj 698e6d016fSToomas Soome fb_info_t fb_info; 7029a77b73SToomas Soome static bcons_dev_t bcons_dev; /* Device callbacks */ 710d928757SGary Mills static int console = CONS_SCREEN_TEXT; 7260cbda0dSToomas Soome static int diag = CONS_INVALID; 730d928757SGary Mills static int tty_num = 0; 740d928757SGary Mills static int tty_addr[] = {0x3f8, 0x2f8, 0x3e8, 0x2e8}; 75f289ce6eSToomas Soome static char *boot_line; 76f289ce6eSToomas Soome static struct boot_env { 77f289ce6eSToomas Soome char *be_env; /* ends with double ascii nul */ 78f289ce6eSToomas Soome size_t be_size; /* size of the environment, including nul */ 79f289ce6eSToomas Soome } boot_env; 80f289ce6eSToomas Soome 8129a77b73SToomas Soome /* 8229a77b73SToomas Soome * Simple console terminal emulator for early boot. 8329a77b73SToomas Soome * We need this to support kmdb, all other console output is supposed 8429a77b73SToomas Soome * to be simple text output. 8529a77b73SToomas Soome */ 8629a77b73SToomas Soome typedef enum btem_state_type { 8729a77b73SToomas Soome A_STATE_START, 8829a77b73SToomas Soome A_STATE_ESC, 8929a77b73SToomas Soome A_STATE_CSI, 9029a77b73SToomas Soome A_STATE_CSI_QMARK, 9129a77b73SToomas Soome A_STATE_CSI_EQUAL 9229a77b73SToomas Soome } btem_state_type_t; 9329a77b73SToomas Soome 9429a77b73SToomas Soome #define BTEM_MAXPARAMS 5 9529a77b73SToomas Soome typedef struct btem_state { 9629a77b73SToomas Soome btem_state_type_t btem_state; 9729a77b73SToomas Soome boolean_t btem_gotparam; 9829a77b73SToomas Soome int btem_curparam; 9929a77b73SToomas Soome int btem_paramval; 10029a77b73SToomas Soome int btem_params[BTEM_MAXPARAMS]; 10129a77b73SToomas Soome } btem_state_t; 10229a77b73SToomas Soome 10329a77b73SToomas Soome static btem_state_t boot_tem; 10429a77b73SToomas Soome 105f289ce6eSToomas Soome static int serial_ischar(void); 106f289ce6eSToomas Soome static int serial_getchar(void); 107f289ce6eSToomas Soome static void serial_putchar(int); 108f289ce6eSToomas Soome static void serial_adjust_prop(void); 109f289ce6eSToomas Soome 1102e6e9b6bSJohn Levon static void defcons_putchar(int); 1112e6e9b6bSJohn Levon 112f289ce6eSToomas Soome #if !defined(_BOOT) 1132e6e9b6bSJohn Levon static boolean_t bootprop_set_tty_mode; 114f289ce6eSToomas Soome #endif 115f289ce6eSToomas Soome 116843e1988Sjohnlev #if defined(__xpv) 117843e1988Sjohnlev static int console_hypervisor_redirect = B_FALSE; 1180d928757SGary Mills static int console_hypervisor_device = CONS_INVALID; 1190d928757SGary Mills static int console_hypervisor_tty_num = 0; 1200d928757SGary Mills 1210d928757SGary Mills /* Obtain the hypervisor console type */ 1220d928757SGary Mills int 1230d928757SGary Mills console_hypervisor_dev_type(int *tnum) 1240d928757SGary Mills { 1250d928757SGary Mills if (tnum != NULL) 1260d928757SGary Mills *tnum = console_hypervisor_tty_num; 1270d928757SGary Mills return (console_hypervisor_device); 1280d928757SGary Mills } 129843e1988Sjohnlev #endif /* __xpv */ 130843e1988Sjohnlev 131ae115bc7Smrj static int port; 132ae115bc7Smrj 133ae115bc7Smrj static void 134ae115bc7Smrj serial_init(void) 135ae115bc7Smrj { 1360d928757SGary Mills port = tty_addr[tty_num]; 137ae115bc7Smrj 138ae115bc7Smrj outb(port + ISR, 0x20); 139ae115bc7Smrj if (inb(port + ISR) & 0x20) { 140ae115bc7Smrj /* 141ae115bc7Smrj * 82510 chip is present 142ae115bc7Smrj */ 143ae115bc7Smrj outb(port + DAT+7, 0x04); /* clear status */ 144ae115bc7Smrj outb(port + ISR, 0x40); /* set to bank 2 */ 145ae115bc7Smrj outb(port + MCR, 0x08); /* IMD */ 146ae115bc7Smrj outb(port + DAT, 0x21); /* FMD */ 147ae115bc7Smrj outb(port + ISR, 0x00); /* set to bank 0 */ 148ae115bc7Smrj } else { 149ae115bc7Smrj /* 150ae115bc7Smrj * set the UART in FIFO mode if it has FIFO buffers. 151ae115bc7Smrj * use 16550 fifo reset sequence specified in NS 152ae115bc7Smrj * application note. disable fifos until chip is 153ae115bc7Smrj * initialized. 154ae115bc7Smrj */ 155ae115bc7Smrj outb(port + FIFOR, 0x00); /* clear */ 156ae115bc7Smrj outb(port + FIFOR, FIFO_ON); /* enable */ 157ae115bc7Smrj outb(port + FIFOR, FIFO_ON|FIFORXFLSH); /* reset */ 158ae115bc7Smrj outb(port + FIFOR, 159ae115bc7Smrj FIFO_ON|FIFODMA|FIFOTXFLSH|FIFORXFLSH|0x80); 160ae115bc7Smrj if ((inb(port + ISR) & 0xc0) != 0xc0) { 161ae115bc7Smrj /* 162ae115bc7Smrj * no fifo buffers so disable fifos. 163ae115bc7Smrj * this is true for 8250's 164ae115bc7Smrj */ 165ae115bc7Smrj outb(port + FIFOR, 0x00); 166ae115bc7Smrj } 167ae115bc7Smrj } 168ae115bc7Smrj 169ae115bc7Smrj /* disable interrupts */ 170ae115bc7Smrj outb(port + ICR, 0); 171ae115bc7Smrj 172843e1988Sjohnlev #if !defined(_BOOT) 173843e1988Sjohnlev if (IN_XPV_PANIC()) 174843e1988Sjohnlev return; 175843e1988Sjohnlev #endif 176843e1988Sjohnlev 177ae115bc7Smrj /* adjust setting based on tty properties */ 178ae115bc7Smrj serial_adjust_prop(); 179ae115bc7Smrj } 180ae115bc7Smrj 181adb91f47Srscott /* Advance str pointer past white space */ 182adb91f47Srscott #define EAT_WHITE_SPACE(str) { \ 183adb91f47Srscott while ((*str != '\0') && ISSPACE(*str)) \ 184adb91f47Srscott str++; \ 185adb91f47Srscott } 186adb91f47Srscott 187adb91f47Srscott /* 188adb91f47Srscott * boot_line is set when we call here. Search it for the argument name, 189adb91f47Srscott * and if found, return a pointer to it. 190adb91f47Srscott */ 191adb91f47Srscott static char * 192adb91f47Srscott find_boot_line_prop(const char *name) 193adb91f47Srscott { 194adb91f47Srscott char *ptr; 195c6d6228cSEnrico Perla - Sun Microsystems char *ret = NULL; 196adb91f47Srscott char end_char; 197adb91f47Srscott size_t len; 198adb91f47Srscott 199adb91f47Srscott if (boot_line == NULL) 200adb91f47Srscott return (NULL); 201adb91f47Srscott 202adb91f47Srscott len = strlen(name); 203adb91f47Srscott 204adb91f47Srscott /* 205adb91f47Srscott * We have two nested loops here: the outer loop discards all options 206adb91f47Srscott * except -B, and the inner loop parses the -B options looking for 207adb91f47Srscott * the one we're interested in. 208adb91f47Srscott */ 209adb91f47Srscott for (ptr = boot_line; *ptr != '\0'; ptr++) { 210adb91f47Srscott EAT_WHITE_SPACE(ptr); 211adb91f47Srscott 212adb91f47Srscott if (*ptr == '-') { 213adb91f47Srscott ptr++; 214adb91f47Srscott while ((*ptr != '\0') && (*ptr != 'B') && 215adb91f47Srscott !ISSPACE(*ptr)) 216adb91f47Srscott ptr++; 217adb91f47Srscott if (*ptr == '\0') 218c6d6228cSEnrico Perla - Sun Microsystems goto out; 219adb91f47Srscott else if (*ptr != 'B') 220adb91f47Srscott continue; 221adb91f47Srscott } else { 222adb91f47Srscott while ((*ptr != '\0') && !ISSPACE(*ptr)) 223adb91f47Srscott ptr++; 224adb91f47Srscott if (*ptr == '\0') 225c6d6228cSEnrico Perla - Sun Microsystems goto out; 226adb91f47Srscott continue; 227adb91f47Srscott } 228adb91f47Srscott 229adb91f47Srscott do { 230adb91f47Srscott ptr++; 231adb91f47Srscott EAT_WHITE_SPACE(ptr); 232adb91f47Srscott 233adb91f47Srscott if ((strncmp(ptr, name, len) == 0) && 234adb91f47Srscott (ptr[len] == '=')) { 235adb91f47Srscott ptr += len + 1; 236c6d6228cSEnrico Perla - Sun Microsystems if ((*ptr == '\'') || (*ptr == '"')) { 237c6d6228cSEnrico Perla - Sun Microsystems ret = ptr + 1; 238c6d6228cSEnrico Perla - Sun Microsystems end_char = *ptr; 239c6d6228cSEnrico Perla - Sun Microsystems ptr++; 240c6d6228cSEnrico Perla - Sun Microsystems } else { 241c6d6228cSEnrico Perla - Sun Microsystems ret = ptr; 242c6d6228cSEnrico Perla - Sun Microsystems end_char = ','; 243c6d6228cSEnrico Perla - Sun Microsystems } 244c6d6228cSEnrico Perla - Sun Microsystems goto consume_property; 245adb91f47Srscott } 246adb91f47Srscott 247adb91f47Srscott /* 248adb91f47Srscott * We have a property, and it's not the one we're 249adb91f47Srscott * interested in. Skip the property name. A name 250adb91f47Srscott * can end with '=', a comma, or white space. 251adb91f47Srscott */ 252adb91f47Srscott while ((*ptr != '\0') && (*ptr != '=') && 253adb91f47Srscott (*ptr != ',') && (!ISSPACE(*ptr))) 254adb91f47Srscott ptr++; 255adb91f47Srscott 256adb91f47Srscott /* 257adb91f47Srscott * We only want to go through the rest of the inner 258adb91f47Srscott * loop if we have a comma. If we have a property 259adb91f47Srscott * name without a value, either continue or break. 260adb91f47Srscott */ 261adb91f47Srscott if (*ptr == '\0') 262c6d6228cSEnrico Perla - Sun Microsystems goto out; 263adb91f47Srscott else if (*ptr == ',') 264adb91f47Srscott continue; 265adb91f47Srscott else if (ISSPACE(*ptr)) 266adb91f47Srscott break; 267adb91f47Srscott ptr++; 268adb91f47Srscott 269adb91f47Srscott /* 270adb91f47Srscott * Is the property quoted? 271adb91f47Srscott */ 272adb91f47Srscott if ((*ptr == '\'') || (*ptr == '"')) { 273adb91f47Srscott end_char = *ptr; 274c6d6228cSEnrico Perla - Sun Microsystems ptr++; 275adb91f47Srscott } else { 276adb91f47Srscott /* 277adb91f47Srscott * Not quoted, so the string ends at a comma 278adb91f47Srscott * or at white space. Deal with white space 279adb91f47Srscott * later. 280adb91f47Srscott */ 281adb91f47Srscott end_char = ','; 282adb91f47Srscott } 283adb91f47Srscott 284adb91f47Srscott /* 285adb91f47Srscott * Now, we can ignore any characters until we find 286adb91f47Srscott * end_char. 287adb91f47Srscott */ 288c6d6228cSEnrico Perla - Sun Microsystems consume_property: 289adb91f47Srscott for (; (*ptr != '\0') && (*ptr != end_char); ptr++) { 290adb91f47Srscott if ((end_char == ',') && ISSPACE(*ptr)) 291adb91f47Srscott break; 292adb91f47Srscott } 293c6d6228cSEnrico Perla - Sun Microsystems if (*ptr && (*ptr != ',') && !ISSPACE(*ptr)) 294adb91f47Srscott ptr++; 295adb91f47Srscott } while (*ptr == ','); 296adb91f47Srscott } 297c6d6228cSEnrico Perla - Sun Microsystems out: 298c6d6228cSEnrico Perla - Sun Microsystems return (ret); 299adb91f47Srscott } 300adb91f47Srscott 301f289ce6eSToomas Soome /* 302f289ce6eSToomas Soome * Find prop from boot env module. The data in module is list of C strings 303f289ce6eSToomas Soome * name=value, the list is terminated by double nul. 304f289ce6eSToomas Soome */ 305f289ce6eSToomas Soome static const char * 306f289ce6eSToomas Soome find_boot_env_prop(const char *name) 307f289ce6eSToomas Soome { 308f289ce6eSToomas Soome char *ptr; 309f289ce6eSToomas Soome size_t len; 310f289ce6eSToomas Soome uintptr_t size; 311f289ce6eSToomas Soome 312f289ce6eSToomas Soome if (boot_env.be_env == NULL) 313f289ce6eSToomas Soome return (NULL); 314f289ce6eSToomas Soome 315f289ce6eSToomas Soome ptr = boot_env.be_env; 316f289ce6eSToomas Soome len = strlen(name); 317f289ce6eSToomas Soome 318f289ce6eSToomas Soome /* 319f289ce6eSToomas Soome * Make sure we have at least len + 2 bytes in the environment. 320f289ce6eSToomas Soome * We are looking for name=value\0 constructs, and the environment 321f289ce6eSToomas Soome * itself is terminated by '\0'. 322f289ce6eSToomas Soome */ 323f289ce6eSToomas Soome if (boot_env.be_size < len + 2) 324f289ce6eSToomas Soome return (NULL); 325f289ce6eSToomas Soome 326f289ce6eSToomas Soome do { 327f289ce6eSToomas Soome if ((strncmp(ptr, name, len) == 0) && (ptr[len] == '=')) { 328f289ce6eSToomas Soome ptr += len + 1; 329f289ce6eSToomas Soome return (ptr); 330f289ce6eSToomas Soome } 331f289ce6eSToomas Soome /* find the first '\0' */ 332f289ce6eSToomas Soome while (*ptr != '\0') { 333f289ce6eSToomas Soome ptr++; 334f289ce6eSToomas Soome size = (uintptr_t)ptr - (uintptr_t)boot_env.be_env; 335f289ce6eSToomas Soome if (size > boot_env.be_size) 336f289ce6eSToomas Soome return (NULL); 337f289ce6eSToomas Soome } 338f289ce6eSToomas Soome ptr++; 339f289ce6eSToomas Soome 340f289ce6eSToomas Soome /* If the remainder is shorter than name + 2, get out. */ 341f289ce6eSToomas Soome size = (uintptr_t)ptr - (uintptr_t)boot_env.be_env; 342f289ce6eSToomas Soome if (boot_env.be_size - size < len + 2) 343f289ce6eSToomas Soome return (NULL); 344f289ce6eSToomas Soome } while (*ptr != '\0'); 345f289ce6eSToomas Soome return (NULL); 346f289ce6eSToomas Soome } 347f289ce6eSToomas Soome 348f289ce6eSToomas Soome /* 349f289ce6eSToomas Soome * Get prop value from either command line or boot environment. 350f289ce6eSToomas Soome * We always check kernel command line first, as this will keep the 351f289ce6eSToomas Soome * functionality and will allow user to override the values in environment. 352f289ce6eSToomas Soome */ 353f289ce6eSToomas Soome const char * 354f289ce6eSToomas Soome find_boot_prop(const char *name) 355f289ce6eSToomas Soome { 356f289ce6eSToomas Soome const char *value = find_boot_line_prop(name); 357f289ce6eSToomas Soome 358f289ce6eSToomas Soome if (value == NULL) 359f289ce6eSToomas Soome value = find_boot_env_prop(name); 360f289ce6eSToomas Soome return (value); 361f289ce6eSToomas Soome } 362ae115bc7Smrj 363ae115bc7Smrj #define MATCHES(p, pat) \ 364ae115bc7Smrj (strncmp(p, pat, strlen(pat)) == 0 ? (p += strlen(pat), 1) : 0) 365ae115bc7Smrj 366ae115bc7Smrj #define SKIP(p, c) \ 367ae115bc7Smrj while (*(p) != 0 && *p != (c)) \ 368ae115bc7Smrj ++(p); \ 369ae115bc7Smrj if (*(p) == (c)) \ 370ae115bc7Smrj ++(p); 371ae115bc7Smrj 372ae115bc7Smrj /* 373ae115bc7Smrj * find a tty mode property either from cmdline or from boot properties 374ae115bc7Smrj */ 375f289ce6eSToomas Soome static const char * 376ae115bc7Smrj get_mode_value(char *name) 377ae115bc7Smrj { 378ae115bc7Smrj /* 379ae115bc7Smrj * when specified on boot line it looks like "name" "=".... 380ae115bc7Smrj */ 381ae115bc7Smrj if (boot_line != NULL) { 382f289ce6eSToomas Soome return (find_boot_prop(name)); 383ae115bc7Smrj } 384ae115bc7Smrj 385ae115bc7Smrj #if defined(_BOOT) 386ae115bc7Smrj return (NULL); 387ae115bc7Smrj #else 388ae115bc7Smrj /* 389ae115bc7Smrj * if we're running in the full kernel we check the bootenv.rc settings 390ae115bc7Smrj */ 391ae115bc7Smrj { 392ae115bc7Smrj static char propval[20]; 393ae115bc7Smrj 394ae115bc7Smrj propval[0] = 0; 395adb91f47Srscott if (do_bsys_getproplen(NULL, name) <= 0) 396ae115bc7Smrj return (NULL); 397adb91f47Srscott (void) do_bsys_getprop(NULL, name, propval); 398ae115bc7Smrj return (propval); 399ae115bc7Smrj } 400ae115bc7Smrj #endif 401ae115bc7Smrj } 402ae115bc7Smrj 403ae115bc7Smrj /* 404ae115bc7Smrj * adjust serial port based on properties 405ae115bc7Smrj * These come either from the cmdline or from boot properties. 406ae115bc7Smrj */ 407ae115bc7Smrj static void 408ae115bc7Smrj serial_adjust_prop(void) 409ae115bc7Smrj { 410ae115bc7Smrj char propname[20]; 411f289ce6eSToomas Soome const char *propval; 412f289ce6eSToomas Soome const char *p; 413ae115bc7Smrj ulong_t baud; 414ae115bc7Smrj uchar_t lcr = 0; 415ae115bc7Smrj uchar_t mcr = DTR | RTS; 416ae115bc7Smrj 417ae115bc7Smrj (void) strcpy(propname, "ttyX-mode"); 4180d928757SGary Mills propname[3] = 'a' + tty_num; 419ae115bc7Smrj propval = get_mode_value(propname); 420843e1988Sjohnlev #if !defined(_BOOT) 4212e6e9b6bSJohn Levon if (propval != NULL) 4222e6e9b6bSJohn Levon bootprop_set_tty_mode = B_TRUE; 423843e1988Sjohnlev #endif 4242e6e9b6bSJohn Levon if (propval == NULL) 4252e6e9b6bSJohn Levon propval = "9600,8,n,1,-"; 426ae115bc7Smrj 427ae115bc7Smrj /* property is of the form: "9600,8,n,1,-" */ 428ae115bc7Smrj p = propval; 429ae115bc7Smrj if (MATCHES(p, "110,")) 430ae115bc7Smrj baud = ASY110; 431ae115bc7Smrj else if (MATCHES(p, "150,")) 432ae115bc7Smrj baud = ASY150; 433ae115bc7Smrj else if (MATCHES(p, "300,")) 434ae115bc7Smrj baud = ASY300; 435ae115bc7Smrj else if (MATCHES(p, "600,")) 436ae115bc7Smrj baud = ASY600; 437ae115bc7Smrj else if (MATCHES(p, "1200,")) 438ae115bc7Smrj baud = ASY1200; 439ae115bc7Smrj else if (MATCHES(p, "2400,")) 440ae115bc7Smrj baud = ASY2400; 441ae115bc7Smrj else if (MATCHES(p, "4800,")) 442ae115bc7Smrj baud = ASY4800; 443ae115bc7Smrj else if (MATCHES(p, "19200,")) 444ae115bc7Smrj baud = ASY19200; 445ae115bc7Smrj else if (MATCHES(p, "38400,")) 446ae115bc7Smrj baud = ASY38400; 447ae115bc7Smrj else if (MATCHES(p, "57600,")) 448ae115bc7Smrj baud = ASY57600; 449ae115bc7Smrj else if (MATCHES(p, "115200,")) 450ae115bc7Smrj baud = ASY115200; 451ae115bc7Smrj else { 452ae115bc7Smrj baud = ASY9600; 453ae115bc7Smrj SKIP(p, ','); 454ae115bc7Smrj } 455ae115bc7Smrj outb(port + LCR, DLAB); 456ae115bc7Smrj outb(port + DAT + DLL, baud & 0xff); 457ae115bc7Smrj outb(port + DAT + DLH, (baud >> 8) & 0xff); 458ae115bc7Smrj 459ae115bc7Smrj switch (*p) { 460ae115bc7Smrj case '5': 461ae115bc7Smrj lcr |= BITS5; 462ae115bc7Smrj ++p; 463ae115bc7Smrj break; 464ae115bc7Smrj case '6': 465ae115bc7Smrj lcr |= BITS6; 466ae115bc7Smrj ++p; 467ae115bc7Smrj break; 468ae115bc7Smrj case '7': 469ae115bc7Smrj lcr |= BITS7; 470ae115bc7Smrj ++p; 471ae115bc7Smrj break; 472ae115bc7Smrj case '8': 473ae115bc7Smrj ++p; 474df23f1c1SToomas Soome /* FALLTHROUGH */ 475ae115bc7Smrj default: 476ae115bc7Smrj lcr |= BITS8; 477ae115bc7Smrj break; 478ae115bc7Smrj } 479ae115bc7Smrj 480ae115bc7Smrj SKIP(p, ','); 481ae115bc7Smrj 482ae115bc7Smrj switch (*p) { 483ae115bc7Smrj case 'n': 484ae115bc7Smrj lcr |= PARITY_NONE; 485ae115bc7Smrj ++p; 486ae115bc7Smrj break; 487ae115bc7Smrj case 'o': 488ae115bc7Smrj lcr |= PARITY_ODD; 489ae115bc7Smrj ++p; 490ae115bc7Smrj break; 491ae115bc7Smrj case 'e': 492ae115bc7Smrj ++p; 493df23f1c1SToomas Soome /* FALLTHROUGH */ 494ae115bc7Smrj default: 495ae115bc7Smrj lcr |= PARITY_EVEN; 496ae115bc7Smrj break; 497ae115bc7Smrj } 498ae115bc7Smrj 499ae115bc7Smrj 500ae115bc7Smrj SKIP(p, ','); 501ae115bc7Smrj 502ae115bc7Smrj switch (*p) { 503ae115bc7Smrj case '1': 504ae115bc7Smrj /* STOP1 is 0 */ 505ae115bc7Smrj ++p; 506ae115bc7Smrj break; 507ae115bc7Smrj default: 508ae115bc7Smrj lcr |= STOP2; 509ae115bc7Smrj break; 510ae115bc7Smrj } 511ae115bc7Smrj /* set parity bits */ 512ae115bc7Smrj outb(port + LCR, lcr); 513ae115bc7Smrj 514ae115bc7Smrj (void) strcpy(propname, "ttyX-rts-dtr-off"); 5150d928757SGary Mills propname[3] = 'a' + tty_num; 516ae115bc7Smrj propval = get_mode_value(propname); 517ae115bc7Smrj if (propval == NULL) 518ae115bc7Smrj propval = "false"; 519ae115bc7Smrj if (propval[0] != 'f' && propval[0] != 'F') 520ae115bc7Smrj mcr = 0; 521ae115bc7Smrj /* set modem control bits */ 522ae115bc7Smrj outb(port + MCR, mcr | OUT2); 523ae115bc7Smrj } 524ae115bc7Smrj 5250d928757SGary Mills /* Obtain the console type */ 5260d928757SGary Mills int 5270d928757SGary Mills boot_console_type(int *tnum) 5280d928757SGary Mills { 5290d928757SGary Mills if (tnum != NULL) 5300d928757SGary Mills *tnum = tty_num; 5310d928757SGary Mills return (console); 5320d928757SGary Mills } 5330d928757SGary Mills 534adb91f47Srscott /* 535adb91f47Srscott * A structure to map console names to values. 536adb91f47Srscott */ 537adb91f47Srscott typedef struct { 538adb91f47Srscott char *name; 539adb91f47Srscott int value; 540adb91f47Srscott } console_value_t; 541adb91f47Srscott 542adb91f47Srscott console_value_t console_devices[] = { 5430d928757SGary Mills { "ttya", CONS_TTY }, /* 0 */ 5440d928757SGary Mills { "ttyb", CONS_TTY }, /* 1 */ 5450d928757SGary Mills { "ttyc", CONS_TTY }, /* 2 */ 5460d928757SGary Mills { "ttyd", CONS_TTY }, /* 3 */ 547adb91f47Srscott { "text", CONS_SCREEN_TEXT }, 54867ce1dadSJan Setje-Eilers { "graphics", CONS_SCREEN_GRAPHICS }, 549843e1988Sjohnlev #if defined(__xpv) 550843e1988Sjohnlev { "hypervisor", CONS_HYPERVISOR }, 551843e1988Sjohnlev #endif 552adb91f47Srscott #if !defined(_BOOT) 553adb91f47Srscott { "usb-serial", CONS_USBSER }, 554adb91f47Srscott #endif 5550d928757SGary Mills { NULL, CONS_INVALID } 556adb91f47Srscott }; 557adb91f47Srscott 558f289ce6eSToomas Soome static void 559f289ce6eSToomas Soome bcons_init_env(struct xboot_info *xbi) 560f289ce6eSToomas Soome { 561f289ce6eSToomas Soome uint32_t i; 562f289ce6eSToomas Soome struct boot_modules *modules; 563f289ce6eSToomas Soome 564f289ce6eSToomas Soome modules = (struct boot_modules *)(uintptr_t)xbi->bi_modules; 565f289ce6eSToomas Soome for (i = 0; i < xbi->bi_module_cnt; i++) { 566f289ce6eSToomas Soome if (modules[i].bm_type == BMT_ENV) 567f289ce6eSToomas Soome break; 568f289ce6eSToomas Soome } 569f289ce6eSToomas Soome if (i == xbi->bi_module_cnt) 570f289ce6eSToomas Soome return; 571f289ce6eSToomas Soome 572f289ce6eSToomas Soome boot_env.be_env = (char *)(uintptr_t)modules[i].bm_addr; 573f289ce6eSToomas Soome boot_env.be_size = modules[i].bm_size; 574f289ce6eSToomas Soome } 575f289ce6eSToomas Soome 5768e6d016fSToomas Soome int 5778e6d016fSToomas Soome boot_fb(struct xboot_info *xbi, int console) 5788e6d016fSToomas Soome { 57929a77b73SToomas Soome if (xbi_fb_init(xbi, &bcons_dev) == B_FALSE) 5808e6d016fSToomas Soome return (console); 5818e6d016fSToomas Soome 5828e6d016fSToomas Soome /* FB address is not set, fall back to serial terminal. */ 58329a77b73SToomas Soome if (fb_info.paddr == 0) 5848e6d016fSToomas Soome return (CONS_TTY); 5858e6d016fSToomas Soome 58629a77b73SToomas Soome fb_info.terminal.x = VGA_TEXT_COLS; 58729a77b73SToomas Soome fb_info.terminal.y = VGA_TEXT_ROWS; 5888e6d016fSToomas Soome boot_fb_init(CONS_FRAMEBUFFER); 5898e6d016fSToomas Soome 5908e6d016fSToomas Soome if (console == CONS_SCREEN_TEXT) 5918e6d016fSToomas Soome return (CONS_FRAMEBUFFER); 5928e6d016fSToomas Soome return (console); 5938e6d016fSToomas Soome } 5948e6d016fSToomas Soome 5958e6d016fSToomas Soome /* 5968e6d016fSToomas Soome * TODO. 5978e6d016fSToomas Soome * quick and dirty local atoi. Perhaps should build with strtol, but 5988e6d016fSToomas Soome * dboot & early boot mix does overcomplicate things much. 5998e6d016fSToomas Soome * Stolen from libc anyhow. 6008e6d016fSToomas Soome */ 6018e6d016fSToomas Soome static int 6028e6d016fSToomas Soome atoi(const char *p) 6038e6d016fSToomas Soome { 6048e6d016fSToomas Soome int n, c, neg = 0; 6058e6d016fSToomas Soome unsigned char *up = (unsigned char *)p; 6068e6d016fSToomas Soome 6078e6d016fSToomas Soome if (!isdigit(c = *up)) { 6088e6d016fSToomas Soome while (isspace(c)) 6098e6d016fSToomas Soome c = *++up; 6108e6d016fSToomas Soome switch (c) { 6118e6d016fSToomas Soome case '-': 6128e6d016fSToomas Soome neg++; 6138e6d016fSToomas Soome /* FALLTHROUGH */ 6148e6d016fSToomas Soome case '+': 6158e6d016fSToomas Soome c = *++up; 6168e6d016fSToomas Soome } 6178e6d016fSToomas Soome if (!isdigit(c)) 6188e6d016fSToomas Soome return (0); 6198e6d016fSToomas Soome } 6208e6d016fSToomas Soome for (n = '0' - c; isdigit(c = *++up); ) { 6218e6d016fSToomas Soome n *= 10; /* two steps to avoid unnecessary overflow */ 6228e6d016fSToomas Soome n += '0' - c; /* accum neg to avoid surprises at MAX */ 6238e6d016fSToomas Soome } 6248e6d016fSToomas Soome return (neg ? n : -n); 6258e6d016fSToomas Soome } 6268e6d016fSToomas Soome 6278e6d016fSToomas Soome static void 6288e6d016fSToomas Soome bcons_init_fb(void) 6298e6d016fSToomas Soome { 6308e6d016fSToomas Soome const char *propval; 6318e6d016fSToomas Soome int intval; 6328e6d016fSToomas Soome 6338e6d016fSToomas Soome /* initialize with explicit default values */ 6348e6d016fSToomas Soome fb_info.fg_color = CONS_COLOR; 6358e6d016fSToomas Soome fb_info.bg_color = 0; 6368e6d016fSToomas Soome fb_info.inverse = B_FALSE; 6378e6d016fSToomas Soome fb_info.inverse_screen = B_FALSE; 6388e6d016fSToomas Soome 639a4e6b9b6SToomas Soome /* color values are 0 - 255 */ 6408e6d016fSToomas Soome propval = find_boot_prop("tem.fg_color"); 6418e6d016fSToomas Soome if (propval != NULL) { 6428e6d016fSToomas Soome intval = atoi(propval); 643a4e6b9b6SToomas Soome if (intval >= 0 && intval <= 255) 6448e6d016fSToomas Soome fb_info.fg_color = intval; 6458e6d016fSToomas Soome } 6468e6d016fSToomas Soome 647a4e6b9b6SToomas Soome /* color values are 0 - 255 */ 6488e6d016fSToomas Soome propval = find_boot_prop("tem.bg_color"); 6498e6d016fSToomas Soome if (propval != NULL && ISDIGIT(*propval)) { 6508e6d016fSToomas Soome intval = atoi(propval); 651a4e6b9b6SToomas Soome if (intval >= 0 && intval <= 255) 6528e6d016fSToomas Soome fb_info.bg_color = intval; 6538e6d016fSToomas Soome } 6548e6d016fSToomas Soome 6558e6d016fSToomas Soome /* get inverses. allow 0, 1, true, false */ 6568e6d016fSToomas Soome propval = find_boot_prop("tem.inverse"); 6578e6d016fSToomas Soome if (propval != NULL) { 6588e6d016fSToomas Soome if (*propval == '1' || MATCHES(propval, "true")) 6598e6d016fSToomas Soome fb_info.inverse = B_TRUE; 6608e6d016fSToomas Soome } 6618e6d016fSToomas Soome 6628e6d016fSToomas Soome propval = find_boot_prop("tem.inverse-screen"); 6638e6d016fSToomas Soome if (propval != NULL) { 6648e6d016fSToomas Soome if (*propval == '1' || MATCHES(propval, "true")) 6658e6d016fSToomas Soome fb_info.inverse_screen = B_TRUE; 6668e6d016fSToomas Soome } 6678e6d016fSToomas Soome 668cbc8e155SToomas Soome #if defined(_BOOT) 6698e6d016fSToomas Soome /* 6708e6d016fSToomas Soome * Load cursor position from bootloader only in dboot, 6718e6d016fSToomas Soome * dboot will pass cursor position to kernel via xboot info. 6728e6d016fSToomas Soome */ 6738e6d016fSToomas Soome propval = find_boot_prop("tem.cursor.row"); 6748e6d016fSToomas Soome if (propval != NULL) { 6758e6d016fSToomas Soome intval = atoi(propval); 6768e6d016fSToomas Soome if (intval >= 0 && intval <= 0xFFFF) 6778e6d016fSToomas Soome fb_info.cursor.pos.y = intval; 6788e6d016fSToomas Soome } 6798e6d016fSToomas Soome 6808e6d016fSToomas Soome propval = find_boot_prop("tem.cursor.col"); 6818e6d016fSToomas Soome if (propval != NULL) { 6828e6d016fSToomas Soome intval = atoi(propval); 6838e6d016fSToomas Soome if (intval >= 0 && intval <= 0xFFFF) 6848e6d016fSToomas Soome fb_info.cursor.pos.x = intval; 6858e6d016fSToomas Soome } 6868e6d016fSToomas Soome #endif 6878e6d016fSToomas Soome } 6888e6d016fSToomas Soome 68960cbda0dSToomas Soome /* 6902e6e9b6bSJohn Levon * Go through the known console device names trying to match the string we were 6912e6e9b6bSJohn Levon * given. The string on the command line must end with a comma or white space. 69260cbda0dSToomas Soome * 6932e6e9b6bSJohn Levon * For convenience, we provide the caller with an integer index for the CONS_TTY 6942e6e9b6bSJohn Levon * case. 69560cbda0dSToomas Soome */ 69660cbda0dSToomas Soome static int 6972e6e9b6bSJohn Levon lookup_console_device(const char *cons_str, int *indexp) 69860cbda0dSToomas Soome { 69960cbda0dSToomas Soome int n, cons; 70060cbda0dSToomas Soome size_t len, cons_len; 70160cbda0dSToomas Soome console_value_t *consolep; 70260cbda0dSToomas Soome 70360cbda0dSToomas Soome cons = CONS_INVALID; 70460cbda0dSToomas Soome if (cons_str != NULL) { 70560cbda0dSToomas Soome 70660cbda0dSToomas Soome cons_len = strlen(cons_str); 70760cbda0dSToomas Soome for (n = 0; console_devices[n].name != NULL; n++) { 70860cbda0dSToomas Soome consolep = &console_devices[n]; 70960cbda0dSToomas Soome len = strlen(consolep->name); 71060cbda0dSToomas Soome if ((len <= cons_len) && ((cons_str[len] == '\0') || 71160cbda0dSToomas Soome (cons_str[len] == ',') || (cons_str[len] == '\'') || 71260cbda0dSToomas Soome (cons_str[len] == '"') || ISSPACE(cons_str[len])) && 71360cbda0dSToomas Soome (strncmp(cons_str, consolep->name, len) == 0)) { 71460cbda0dSToomas Soome cons = consolep->value; 71560cbda0dSToomas Soome if (cons == CONS_TTY) 7162e6e9b6bSJohn Levon *indexp = n; 71760cbda0dSToomas Soome break; 71860cbda0dSToomas Soome } 71960cbda0dSToomas Soome } 72060cbda0dSToomas Soome } 72160cbda0dSToomas Soome return (cons); 72260cbda0dSToomas Soome } 72360cbda0dSToomas Soome 724ae115bc7Smrj void 725f289ce6eSToomas Soome bcons_init(struct xboot_info *xbi) 726ae115bc7Smrj { 727f289ce6eSToomas Soome const char *cons_str; 7289db7147eSSherry Moore #if !defined(_BOOT) 7299db7147eSSherry Moore static char console_text[] = "text"; 7309db7147eSSherry Moore extern int post_fastreboot; 7319db7147eSSherry Moore #endif 732adb91f47Srscott 733805e8fd0SToomas Soome if (xbi == NULL) { 734805e8fd0SToomas Soome /* This is very early dboot console, set up ttya. */ 735805e8fd0SToomas Soome console = CONS_TTY; 736805e8fd0SToomas Soome serial_init(); 737805e8fd0SToomas Soome return; 738805e8fd0SToomas Soome } 739805e8fd0SToomas Soome 740f289ce6eSToomas Soome /* Set up data to fetch properties from commad line and boot env. */ 741f289ce6eSToomas Soome boot_line = (char *)(uintptr_t)xbi->bi_cmdline; 742f289ce6eSToomas Soome bcons_init_env(xbi); 743ae115bc7Smrj console = CONS_INVALID; 744ae115bc7Smrj 7458e6d016fSToomas Soome /* set up initial fb_info */ 7468e6d016fSToomas Soome bcons_init_fb(); 7478e6d016fSToomas Soome 748843e1988Sjohnlev #if defined(__xpv) 749f289ce6eSToomas Soome bcons_init_xen(boot_line); 750843e1988Sjohnlev #endif /* __xpv */ 751843e1988Sjohnlev 75260cbda0dSToomas Soome /* 75360cbda0dSToomas Soome * First check for diag-device. 75460cbda0dSToomas Soome */ 75560cbda0dSToomas Soome cons_str = find_boot_prop("diag-device"); 75660cbda0dSToomas Soome if (cons_str != NULL) 7572e6e9b6bSJohn Levon diag = lookup_console_device(cons_str, &tty_num); 75860cbda0dSToomas Soome 759f289ce6eSToomas Soome cons_str = find_boot_prop("console"); 760adb91f47Srscott if (cons_str == NULL) 761f289ce6eSToomas Soome cons_str = find_boot_prop("output-device"); 762adb91f47Srscott 7639db7147eSSherry Moore #if !defined(_BOOT) 7649db7147eSSherry Moore if (post_fastreboot && strcmp(cons_str, "graphics") == 0) 7659db7147eSSherry Moore cons_str = console_text; 7669db7147eSSherry Moore #endif 7679db7147eSSherry Moore 76860cbda0dSToomas Soome if (cons_str != NULL) 7692e6e9b6bSJohn Levon console = lookup_console_device(cons_str, &tty_num); 770ae115bc7Smrj 771843e1988Sjohnlev #if defined(__xpv) 772843e1988Sjohnlev /* 773843e1988Sjohnlev * domU's always use the hypervisor regardless of what 774843e1988Sjohnlev * the console variable may be set to. 775843e1988Sjohnlev */ 776843e1988Sjohnlev if (!DOMAIN_IS_INITDOMAIN(xen_info)) { 777843e1988Sjohnlev console = CONS_HYPERVISOR; 778843e1988Sjohnlev console_hypervisor_redirect = B_TRUE; 779843e1988Sjohnlev } 780843e1988Sjohnlev #endif /* __xpv */ 781843e1988Sjohnlev 782ae115bc7Smrj if (console == CONS_INVALID) 783ae115bc7Smrj console = CONS_SCREEN_TEXT; 784843e1988Sjohnlev 785843e1988Sjohnlev #if defined(__xpv) 786843e1988Sjohnlev if (DOMAIN_IS_INITDOMAIN(xen_info)) { 787843e1988Sjohnlev switch (HYPERVISOR_console_io(CONSOLEIO_get_device, 0, NULL)) { 788843e1988Sjohnlev case XEN_CONSOLE_COM1: 789843e1988Sjohnlev case XEN_CONSOLE_COM2: 7900d928757SGary Mills console_hypervisor_device = CONS_TTY; 7910d928757SGary Mills console_hypervisor_tty_num = tty_num; 792843e1988Sjohnlev break; 793843e1988Sjohnlev case XEN_CONSOLE_VGA: 794843e1988Sjohnlev /* 795843e1988Sjohnlev * Currently xen doesn't really support 796843e1988Sjohnlev * keyboard/display console devices. 797843e1988Sjohnlev * What this setting means is that 798843e1988Sjohnlev * "vga=keep" has been enabled, which is 799843e1988Sjohnlev * more of a xen debugging tool that a 800843e1988Sjohnlev * true console mode. Hence, we're going 801843e1988Sjohnlev * to ignore this xen "console" setting. 802843e1988Sjohnlev */ 803843e1988Sjohnlev /*FALLTHROUGH*/ 804843e1988Sjohnlev default: 805843e1988Sjohnlev console_hypervisor_device = CONS_INVALID; 806843e1988Sjohnlev } 807843e1988Sjohnlev } 808843e1988Sjohnlev 809843e1988Sjohnlev /* 810843e1988Sjohnlev * if the hypervisor is using the currently selected serial 811843e1988Sjohnlev * port then default to using the hypervisor as the console 812843e1988Sjohnlev * device. 813843e1988Sjohnlev */ 814843e1988Sjohnlev if (console == console_hypervisor_device) { 815843e1988Sjohnlev console = CONS_HYPERVISOR; 816843e1988Sjohnlev console_hypervisor_redirect = B_TRUE; 817843e1988Sjohnlev } 818843e1988Sjohnlev #endif /* __xpv */ 819ae115bc7Smrj 8208e6d016fSToomas Soome /* make sure the FB is set up if present */ 8218e6d016fSToomas Soome console = boot_fb(xbi, console); 822ae115bc7Smrj switch (console) { 8230d928757SGary Mills case CONS_TTY: 824ae115bc7Smrj serial_init(); 825ae115bc7Smrj break; 826ae115bc7Smrj 827843e1988Sjohnlev case CONS_HYPERVISOR: 828843e1988Sjohnlev break; 829843e1988Sjohnlev 830adb91f47Srscott #if !defined(_BOOT) 831adb91f47Srscott case CONS_USBSER: 832adb91f47Srscott /* 833adb91f47Srscott * We can't do anything with the usb serial 834adb91f47Srscott * until we have memory management. 835adb91f47Srscott */ 836adb91f47Srscott break; 837adb91f47Srscott #endif 83867ce1dadSJan Setje-Eilers case CONS_SCREEN_GRAPHICS: 83967ce1dadSJan Setje-Eilers kb_init(); 84067ce1dadSJan Setje-Eilers break; 841ae115bc7Smrj case CONS_SCREEN_TEXT: 84229a77b73SToomas Soome boot_vga_init(&bcons_dev); 8438e6d016fSToomas Soome /* Fall through */ 844ae115bc7Smrj default: 845ae115bc7Smrj kb_init(); 846ae115bc7Smrj break; 847ae115bc7Smrj } 84860cbda0dSToomas Soome 84960cbda0dSToomas Soome /* 85060cbda0dSToomas Soome * Initialize diag device unless already done. 85160cbda0dSToomas Soome */ 85260cbda0dSToomas Soome switch (diag) { 85360cbda0dSToomas Soome case CONS_TTY: 85460cbda0dSToomas Soome if (console != CONS_TTY) 85560cbda0dSToomas Soome serial_init(); 85660cbda0dSToomas Soome break; 85760cbda0dSToomas Soome case CONS_SCREEN_GRAPHICS: 85860cbda0dSToomas Soome case CONS_SCREEN_TEXT: 85960cbda0dSToomas Soome if (console != CONS_SCREEN_GRAPHICS && 86060cbda0dSToomas Soome console != CONS_SCREEN_TEXT) 86160cbda0dSToomas Soome kb_init(); 86260cbda0dSToomas Soome break; 86360cbda0dSToomas Soome default: 86460cbda0dSToomas Soome break; 86560cbda0dSToomas Soome } 866ae115bc7Smrj } 867ae115bc7Smrj 868ae115bc7Smrj static void 869ae115bc7Smrj serial_putchar(int c) 870ae115bc7Smrj { 871ae115bc7Smrj int checks = 10000; 872ae115bc7Smrj 873ae115bc7Smrj while (((inb(port + LSR) & XHRE) == 0) && checks--) 874ae115bc7Smrj ; 875ae115bc7Smrj outb(port + DAT, (char)c); 876ae115bc7Smrj } 877ae115bc7Smrj 878ae115bc7Smrj static int 879ae115bc7Smrj serial_getchar(void) 880ae115bc7Smrj { 881ae115bc7Smrj uchar_t lsr; 882ae115bc7Smrj 883ae115bc7Smrj while (serial_ischar() == 0) 884ae115bc7Smrj ; 885ae115bc7Smrj 886ae115bc7Smrj lsr = inb(port + LSR); 887ae115bc7Smrj if (lsr & (SERIAL_BREAK | SERIAL_FRAME | 888ae115bc7Smrj SERIAL_PARITY | SERIAL_OVERRUN)) { 889ae115bc7Smrj if (lsr & SERIAL_OVERRUN) { 890ae115bc7Smrj return (inb(port + DAT)); 891ae115bc7Smrj } else { 892ae115bc7Smrj /* Toss the garbage */ 893ae115bc7Smrj (void) inb(port + DAT); 894ae115bc7Smrj return (0); 895ae115bc7Smrj } 896ae115bc7Smrj } 897ae115bc7Smrj return (inb(port + DAT)); 898ae115bc7Smrj } 899ae115bc7Smrj 900ae115bc7Smrj static int 901ae115bc7Smrj serial_ischar(void) 902ae115bc7Smrj { 903ae115bc7Smrj return (inb(port + LSR) & RCA); 904ae115bc7Smrj } 905ae115bc7Smrj 90629a77b73SToomas Soome static void 90729a77b73SToomas Soome btem_control(btem_state_t *btem, int c) 90829a77b73SToomas Soome { 90929a77b73SToomas Soome int y, rows, cols; 91029a77b73SToomas Soome 91129a77b73SToomas Soome rows = fb_info.cursor.pos.y; 91229a77b73SToomas Soome cols = fb_info.cursor.pos.x; 91329a77b73SToomas Soome 91429a77b73SToomas Soome btem->btem_state = A_STATE_START; 91529a77b73SToomas Soome switch (c) { 91629a77b73SToomas Soome case A_BS: 91729a77b73SToomas Soome bcons_dev.bd_setpos(rows, cols - 1); 91829a77b73SToomas Soome break; 91929a77b73SToomas Soome 92029a77b73SToomas Soome case A_HT: 92129a77b73SToomas Soome cols += 8 - (cols % 8); 92229a77b73SToomas Soome if (cols >= fb_info.terminal.x) 92329a77b73SToomas Soome cols = fb_info.terminal.x - 1; 92429a77b73SToomas Soome bcons_dev.bd_setpos(rows, cols); 92529a77b73SToomas Soome break; 92629a77b73SToomas Soome 92729a77b73SToomas Soome case A_CR: 92829a77b73SToomas Soome bcons_dev.bd_setpos(rows, 0); 92929a77b73SToomas Soome break; 93029a77b73SToomas Soome 93129a77b73SToomas Soome case A_FF: 93229a77b73SToomas Soome for (y = 0; y < fb_info.terminal.y; y++) { 93329a77b73SToomas Soome bcons_dev.bd_setpos(y, 0); 93429a77b73SToomas Soome bcons_dev.bd_eraseline(); 93529a77b73SToomas Soome } 93629a77b73SToomas Soome bcons_dev.bd_setpos(0, 0); 93729a77b73SToomas Soome break; 93829a77b73SToomas Soome 93929a77b73SToomas Soome case A_ESC: 94029a77b73SToomas Soome btem->btem_state = A_STATE_ESC; 94129a77b73SToomas Soome break; 94229a77b73SToomas Soome 94329a77b73SToomas Soome default: 94429a77b73SToomas Soome bcons_dev.bd_putchar(c); 94529a77b73SToomas Soome break; 94629a77b73SToomas Soome } 94729a77b73SToomas Soome } 94829a77b73SToomas Soome 94929a77b73SToomas Soome /* 95029a77b73SToomas Soome * if parameters [0..count - 1] are not set, set them to the value 95129a77b73SToomas Soome * of newparam. 95229a77b73SToomas Soome */ 95329a77b73SToomas Soome static void 95429a77b73SToomas Soome btem_setparam(btem_state_t *btem, int count, int newparam) 95529a77b73SToomas Soome { 95629a77b73SToomas Soome int i; 95729a77b73SToomas Soome 95829a77b73SToomas Soome for (i = 0; i < count; i++) { 95929a77b73SToomas Soome if (btem->btem_params[i] == -1) 96029a77b73SToomas Soome btem->btem_params[i] = newparam; 96129a77b73SToomas Soome } 96229a77b73SToomas Soome } 96329a77b73SToomas Soome 96429a77b73SToomas Soome static void 96529a77b73SToomas Soome btem_chkparam(btem_state_t *btem, int c) 96629a77b73SToomas Soome { 96729a77b73SToomas Soome int rows, cols; 96829a77b73SToomas Soome 96929a77b73SToomas Soome rows = fb_info.cursor.pos.y; 97029a77b73SToomas Soome cols = fb_info.cursor.pos.x; 97129a77b73SToomas Soome switch (c) { 97229a77b73SToomas Soome case '@': /* insert char */ 97329a77b73SToomas Soome btem_setparam(btem, 1, 1); 97429a77b73SToomas Soome bcons_dev.bd_shift(btem->btem_params[0]); 97529a77b73SToomas Soome break; 97629a77b73SToomas Soome 97729a77b73SToomas Soome case 'A': /* cursor up */ 97829a77b73SToomas Soome btem_setparam(btem, 1, 1); 97929a77b73SToomas Soome bcons_dev.bd_setpos(rows - btem->btem_params[0], cols); 98029a77b73SToomas Soome break; 98129a77b73SToomas Soome 98229a77b73SToomas Soome case 'B': /* cursor down */ 98329a77b73SToomas Soome btem_setparam(btem, 1, 1); 98429a77b73SToomas Soome bcons_dev.bd_setpos(rows + btem->btem_params[0], cols); 98529a77b73SToomas Soome break; 98629a77b73SToomas Soome 98729a77b73SToomas Soome case 'C': /* cursor right */ 98829a77b73SToomas Soome btem_setparam(btem, 1, 1); 98929a77b73SToomas Soome bcons_dev.bd_setpos(rows, cols + btem->btem_params[0]); 99029a77b73SToomas Soome break; 99129a77b73SToomas Soome 99229a77b73SToomas Soome case 'D': /* cursor left */ 99329a77b73SToomas Soome btem_setparam(btem, 1, 1); 99429a77b73SToomas Soome bcons_dev.bd_setpos(rows, cols - btem->btem_params[0]); 99529a77b73SToomas Soome break; 99629a77b73SToomas Soome 99729a77b73SToomas Soome case 'K': 99829a77b73SToomas Soome bcons_dev.bd_eraseline(); 99929a77b73SToomas Soome break; 100029a77b73SToomas Soome default: 100129a77b73SToomas Soome /* bcons_dev.bd_putchar(c); */ 100229a77b73SToomas Soome break; 100329a77b73SToomas Soome } 100429a77b73SToomas Soome btem->btem_state = A_STATE_START; 100529a77b73SToomas Soome } 100629a77b73SToomas Soome 100729a77b73SToomas Soome static void 100829a77b73SToomas Soome btem_getparams(btem_state_t *btem, int c) 100929a77b73SToomas Soome { 101029a77b73SToomas Soome if (isdigit(c)) { 101129a77b73SToomas Soome btem->btem_paramval = btem->btem_paramval * 10 + c - '0'; 101229a77b73SToomas Soome btem->btem_gotparam = B_TRUE; 101329a77b73SToomas Soome return; 101429a77b73SToomas Soome } 101529a77b73SToomas Soome 101629a77b73SToomas Soome if (btem->btem_curparam < BTEM_MAXPARAMS) { 101729a77b73SToomas Soome if (btem->btem_gotparam == B_TRUE) { 101829a77b73SToomas Soome btem->btem_params[btem->btem_curparam] = 101929a77b73SToomas Soome btem->btem_paramval; 102029a77b73SToomas Soome } 102129a77b73SToomas Soome btem->btem_curparam++; 102229a77b73SToomas Soome } 102329a77b73SToomas Soome 102429a77b73SToomas Soome if (c == ';') { 102529a77b73SToomas Soome /* Restart parameter search */ 102629a77b73SToomas Soome btem->btem_gotparam = B_FALSE; 102729a77b73SToomas Soome btem->btem_paramval = 0; 102829a77b73SToomas Soome } else { 102929a77b73SToomas Soome btem_chkparam(btem, c); 103029a77b73SToomas Soome } 103129a77b73SToomas Soome } 103229a77b73SToomas Soome 103329a77b73SToomas Soome /* Simple boot terminal parser. */ 103429a77b73SToomas Soome static void 103529a77b73SToomas Soome btem_parse(btem_state_t *btem, int c) 103629a77b73SToomas Soome { 103729a77b73SToomas Soome int i; 103829a77b73SToomas Soome 103929a77b73SToomas Soome /* Normal state? */ 104029a77b73SToomas Soome if (btem->btem_state == A_STATE_START) { 104129a77b73SToomas Soome if (c == A_CSI || c < ' ') 104229a77b73SToomas Soome btem_control(btem, c); 104329a77b73SToomas Soome else 104429a77b73SToomas Soome bcons_dev.bd_putchar(c); 104529a77b73SToomas Soome return; 104629a77b73SToomas Soome } 104729a77b73SToomas Soome 104829a77b73SToomas Soome /* In <ESC> sequence */ 104929a77b73SToomas Soome if (btem->btem_state != A_STATE_ESC) { 105029a77b73SToomas Soome btem_getparams(btem, c); 105129a77b73SToomas Soome return; 105229a77b73SToomas Soome } 105329a77b73SToomas Soome 105429a77b73SToomas Soome /* Previous char was <ESC> */ 105529a77b73SToomas Soome switch (c) { 105629a77b73SToomas Soome case '[': 105729a77b73SToomas Soome btem->btem_curparam = 0; 105829a77b73SToomas Soome btem->btem_paramval = 0; 105929a77b73SToomas Soome btem->btem_gotparam = B_FALSE; 106029a77b73SToomas Soome /* clear the parameters */ 106129a77b73SToomas Soome for (i = 0; i < BTEM_MAXPARAMS; i++) 106229a77b73SToomas Soome btem->btem_params[i] = -1; 106329a77b73SToomas Soome btem->btem_state = A_STATE_CSI; 106429a77b73SToomas Soome return; 106529a77b73SToomas Soome 106629a77b73SToomas Soome case 'Q': /* <ESC>Q */ 106729a77b73SToomas Soome case 'C': /* <ESC>C */ 106829a77b73SToomas Soome btem->btem_state = A_STATE_START; 106929a77b73SToomas Soome return; 107029a77b73SToomas Soome 107129a77b73SToomas Soome default: 107229a77b73SToomas Soome btem->btem_state = A_STATE_START; 107329a77b73SToomas Soome break; 107429a77b73SToomas Soome } 107529a77b73SToomas Soome 107629a77b73SToomas Soome if (c < ' ') 107729a77b73SToomas Soome btem_control(btem, c); 107829a77b73SToomas Soome else 107929a77b73SToomas Soome bcons_dev.bd_putchar(c); 108029a77b73SToomas Soome } 108129a77b73SToomas Soome 1082ae115bc7Smrj static void 108360cbda0dSToomas Soome _doputchar(int device, int c) 1084ae115bc7Smrj { 108560cbda0dSToomas Soome switch (device) { 10860d928757SGary Mills case CONS_TTY: 1087ae115bc7Smrj serial_putchar(c); 1088ae115bc7Smrj return; 1089ae115bc7Smrj case CONS_SCREEN_TEXT: 10908e6d016fSToomas Soome case CONS_FRAMEBUFFER: 109129a77b73SToomas Soome bcons_dev.bd_cursor(B_FALSE); 109229a77b73SToomas Soome btem_parse(&boot_tem, c); 109329a77b73SToomas Soome bcons_dev.bd_cursor(B_TRUE); 10948e6d016fSToomas Soome return; 109567ce1dadSJan Setje-Eilers case CONS_SCREEN_GRAPHICS: 1096ae115bc7Smrj #if !defined(_BOOT) 1097ae115bc7Smrj case CONS_USBSER: 109848633f18SJan Setje-Eilers defcons_putchar(c); 1099ae115bc7Smrj #endif /* _BOOT */ 110060cbda0dSToomas Soome default: 110148633f18SJan Setje-Eilers return; 1102ae115bc7Smrj } 1103ae115bc7Smrj } 1104ae115bc7Smrj 1105ae115bc7Smrj void 1106ae115bc7Smrj bcons_putchar(int c) 1107ae115bc7Smrj { 1108843e1988Sjohnlev #if defined(__xpv) 1109843e1988Sjohnlev if (!DOMAIN_IS_INITDOMAIN(xen_info) || 1110843e1988Sjohnlev console == CONS_HYPERVISOR) { 1111843e1988Sjohnlev bcons_putchar_xen(c); 1112843e1988Sjohnlev return; 1113843e1988Sjohnlev } 1114843e1988Sjohnlev #endif /* __xpv */ 1115843e1988Sjohnlev 111629a77b73SToomas Soome if (c == '\n') { 111729a77b73SToomas Soome _doputchar(console, '\r'); 111860cbda0dSToomas Soome if (diag != console) 111929a77b73SToomas Soome _doputchar(diag, '\r'); 1120ae115bc7Smrj } 112160cbda0dSToomas Soome _doputchar(console, c); 112260cbda0dSToomas Soome if (diag != console) 112360cbda0dSToomas Soome _doputchar(diag, c); 1124ae115bc7Smrj } 1125ae115bc7Smrj 1126ae115bc7Smrj /* 1127ae115bc7Smrj * kernel character input functions 1128ae115bc7Smrj */ 1129ae115bc7Smrj int 1130ae115bc7Smrj bcons_getchar(void) 1131ae115bc7Smrj { 1132843e1988Sjohnlev #if defined(__xpv) 1133843e1988Sjohnlev if (!DOMAIN_IS_INITDOMAIN(xen_info) || 1134843e1988Sjohnlev console == CONS_HYPERVISOR) 1135843e1988Sjohnlev return (bcons_getchar_xen()); 1136843e1988Sjohnlev #endif /* __xpv */ 1137843e1988Sjohnlev 113860cbda0dSToomas Soome for (;;) { 113960cbda0dSToomas Soome if (console == CONS_TTY || diag == CONS_TTY) { 114060cbda0dSToomas Soome if (serial_ischar()) 114160cbda0dSToomas Soome return (serial_getchar()); 114260cbda0dSToomas Soome } 114360cbda0dSToomas Soome if (console != CONS_INVALID || diag != CONS_INVALID) { 114460cbda0dSToomas Soome if (kb_ischar()) 114560cbda0dSToomas Soome return (kb_getchar()); 114660cbda0dSToomas Soome } 1147ae115bc7Smrj } 1148ae115bc7Smrj } 1149ae115bc7Smrj 11502e6e9b6bSJohn Levon /* 11512e6e9b6bSJohn Levon * Nothing below is used by dboot. 11522e6e9b6bSJohn Levon */ 1153ae115bc7Smrj #if !defined(_BOOT) 1154ae115bc7Smrj 1155ae115bc7Smrj int 1156ae115bc7Smrj bcons_ischar(void) 1157ae115bc7Smrj { 115860cbda0dSToomas Soome int c = 0; 1159843e1988Sjohnlev 1160843e1988Sjohnlev #if defined(__xpv) 1161843e1988Sjohnlev if (!DOMAIN_IS_INITDOMAIN(xen_info) || 1162843e1988Sjohnlev console == CONS_HYPERVISOR) 1163843e1988Sjohnlev return (bcons_ischar_xen()); 1164843e1988Sjohnlev #endif /* __xpv */ 1165843e1988Sjohnlev 1166ae115bc7Smrj switch (console) { 11670d928757SGary Mills case CONS_TTY: 116860cbda0dSToomas Soome c = serial_ischar(); 116960cbda0dSToomas Soome break; 117060cbda0dSToomas Soome 117160cbda0dSToomas Soome case CONS_INVALID: 117260cbda0dSToomas Soome break; 117360cbda0dSToomas Soome 117460cbda0dSToomas Soome default: 117560cbda0dSToomas Soome c = kb_ischar(); 117660cbda0dSToomas Soome } 117760cbda0dSToomas Soome if (c != 0) 117860cbda0dSToomas Soome return (c); 117960cbda0dSToomas Soome 118060cbda0dSToomas Soome switch (diag) { 118160cbda0dSToomas Soome case CONS_TTY: 118260cbda0dSToomas Soome c = serial_ischar(); 118360cbda0dSToomas Soome break; 118460cbda0dSToomas Soome 118560cbda0dSToomas Soome case CONS_INVALID: 118660cbda0dSToomas Soome break; 118760cbda0dSToomas Soome 1188ae115bc7Smrj default: 118960cbda0dSToomas Soome c = kb_ischar(); 1190ae115bc7Smrj } 119160cbda0dSToomas Soome 119260cbda0dSToomas Soome return (c); 1193ae115bc7Smrj } 1194ae115bc7Smrj 11952e6e9b6bSJohn Levon /* 11962e6e9b6bSJohn Levon * 2nd part of console initialization: we've now processed bootenv.rc; update 11972e6e9b6bSJohn Levon * console settings as appropriate. This only really processes serial console 11982e6e9b6bSJohn Levon * modifications. 11992e6e9b6bSJohn Levon */ 12002e6e9b6bSJohn Levon void 12012e6e9b6bSJohn Levon bcons_post_bootenvrc(char *inputdev, char *outputdev, char *consoledev) 12022e6e9b6bSJohn Levon { 12032e6e9b6bSJohn Levon int cons = CONS_INVALID; 12042e6e9b6bSJohn Levon int ttyn; 12052e6e9b6bSJohn Levon char *devnames[] = { consoledev, outputdev, inputdev, NULL }; 12062e6e9b6bSJohn Levon int i; 12072e6e9b6bSJohn Levon extern int post_fastreboot; 12082e6e9b6bSJohn Levon 1209*584b574aSToomas Soome ttyn = 0; 12102e6e9b6bSJohn Levon if (post_fastreboot && console == CONS_SCREEN_GRAPHICS) 12112e6e9b6bSJohn Levon console = CONS_SCREEN_TEXT; 12122e6e9b6bSJohn Levon 12132e6e9b6bSJohn Levon /* 12142e6e9b6bSJohn Levon * USB serial and GRAPHICS console: we just collect data into a buffer. 12152e6e9b6bSJohn Levon */ 12162e6e9b6bSJohn Levon if (console == CONS_USBSER || console == CONS_SCREEN_GRAPHICS) { 12172e6e9b6bSJohn Levon extern void *defcons_init(size_t); 12182e6e9b6bSJohn Levon defcons_buf = defcons_cur = defcons_init(MMU_PAGESIZE); 12192e6e9b6bSJohn Levon return; 12202e6e9b6bSJohn Levon } 12212e6e9b6bSJohn Levon 12222e6e9b6bSJohn Levon for (i = 0; devnames[i] != NULL; i++) { 12232e6e9b6bSJohn Levon cons = lookup_console_device(devnames[i], &ttyn); 12242e6e9b6bSJohn Levon if (cons != CONS_INVALID) 12252e6e9b6bSJohn Levon break; 12262e6e9b6bSJohn Levon } 12272e6e9b6bSJohn Levon 12282e6e9b6bSJohn Levon if (cons == CONS_INVALID) { 12292e6e9b6bSJohn Levon /* 12302e6e9b6bSJohn Levon * No console change, but let's see if bootenv.rc had a mode 12312e6e9b6bSJohn Levon * setting we should apply. 12322e6e9b6bSJohn Levon */ 12332e6e9b6bSJohn Levon if (console == CONS_TTY && !bootprop_set_tty_mode) 12342e6e9b6bSJohn Levon serial_init(); 12352e6e9b6bSJohn Levon return; 12362e6e9b6bSJohn Levon } 12372e6e9b6bSJohn Levon 12382e6e9b6bSJohn Levon #if defined(__xpv) 12392e6e9b6bSJohn Levon /* 12402e6e9b6bSJohn Levon * if the hypervisor is using the currently selected console device then 12412e6e9b6bSJohn Levon * default to using the hypervisor as the console device. 12422e6e9b6bSJohn Levon */ 12432e6e9b6bSJohn Levon if (cons == console_hypervisor_device) { 12442e6e9b6bSJohn Levon cons = CONS_HYPERVISOR; 12452e6e9b6bSJohn Levon console_hypervisor_redirect = B_TRUE; 12462e6e9b6bSJohn Levon } 12472e6e9b6bSJohn Levon #endif /* __xpv */ 12482e6e9b6bSJohn Levon 12492e6e9b6bSJohn Levon console = cons; 12502e6e9b6bSJohn Levon 12512e6e9b6bSJohn Levon if (console == CONS_TTY) { 12522e6e9b6bSJohn Levon tty_num = ttyn; 12532e6e9b6bSJohn Levon serial_init(); 12542e6e9b6bSJohn Levon } 12552e6e9b6bSJohn Levon } 12562e6e9b6bSJohn Levon 12572e6e9b6bSJohn Levon #if defined(__xpv) 12582e6e9b6bSJohn Levon boolean_t 12592e6e9b6bSJohn Levon bcons_hypervisor_redirect(void) 12602e6e9b6bSJohn Levon { 12612e6e9b6bSJohn Levon return (console_hypervisor_redirect); 12622e6e9b6bSJohn Levon } 12632e6e9b6bSJohn Levon 12642e6e9b6bSJohn Levon void 12652e6e9b6bSJohn Levon bcons_device_change(int new_console) 12662e6e9b6bSJohn Levon { 12672e6e9b6bSJohn Levon if (new_console < CONS_MIN || new_console > CONS_MAX) 12682e6e9b6bSJohn Levon return; 12692e6e9b6bSJohn Levon 12702e6e9b6bSJohn Levon /* 12712e6e9b6bSJohn Levon * If we are asked to switch the console to the hypervisor, that 12722e6e9b6bSJohn Levon * really means to switch the console to whichever device the 12732e6e9b6bSJohn Levon * hypervisor is/was using. 12742e6e9b6bSJohn Levon */ 12752e6e9b6bSJohn Levon if (new_console == CONS_HYPERVISOR) 12762e6e9b6bSJohn Levon new_console = console_hypervisor_device; 12772e6e9b6bSJohn Levon 12782e6e9b6bSJohn Levon console = new_console; 12792e6e9b6bSJohn Levon 12802e6e9b6bSJohn Levon if (new_console == CONS_TTY) { 12812e6e9b6bSJohn Levon tty_num = console_hypervisor_tty_num; 12822e6e9b6bSJohn Levon serial_init(); 12832e6e9b6bSJohn Levon } 12842e6e9b6bSJohn Levon } 12852e6e9b6bSJohn Levon #endif /* __xpv */ 12862e6e9b6bSJohn Levon 12872e6e9b6bSJohn Levon static void 12882e6e9b6bSJohn Levon defcons_putchar(int c) 12892e6e9b6bSJohn Levon { 12902e6e9b6bSJohn Levon if (defcons_buf != NULL && 12912e6e9b6bSJohn Levon defcons_cur + 1 - defcons_buf < MMU_PAGESIZE) { 12922e6e9b6bSJohn Levon *defcons_cur++ = c; 12932e6e9b6bSJohn Levon *defcons_cur = 0; 12942e6e9b6bSJohn Levon } 12952e6e9b6bSJohn Levon } 12962e6e9b6bSJohn Levon 1297ae115bc7Smrj #endif /* _BOOT */ 1298