1 /*- 2 * Copyright (c) 2000 Doug Rabson 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 __FBSDID("$FreeBSD$"); 29 30 #include <efi.h> 31 #include <efilib.h> 32 33 #include "bootstrap.h" 34 35 static SIMPLE_TEXT_OUTPUT_INTERFACE *conout; 36 static SIMPLE_INPUT_INTERFACE *conin; 37 38 #ifdef TERM_EMU 39 #define DEFAULT_FGCOLOR EFI_LIGHTGRAY 40 #define DEFAULT_BGCOLOR EFI_BLACK 41 42 #define MAXARGS 8 43 #define KEYBUFSZ 10 44 static unsigned keybuf[KEYBUFSZ]; /* keybuf for extended codes */ 45 static int args[MAXARGS], argc; 46 static int fg_c, bg_c; 47 static UINTN curx, cury; 48 static int esc; 49 50 void get_pos(UINTN *x, UINTN *y); 51 void curs_move(UINTN *_x, UINTN *_y, UINTN x, UINTN y); 52 static void CL(int); 53 void HO(void); 54 void end_term(void); 55 #endif 56 57 static void efi_cons_probe(struct console *); 58 static int efi_cons_init(struct console *, int); 59 void efi_cons_putchar(struct console *, int); 60 int efi_cons_getchar(struct console *); 61 void efi_cons_efiputchar(int); 62 int efi_cons_poll(struct console *); 63 64 struct console efi_console = { 65 "text", 66 "EFI console", 67 C_WIDEOUT, 68 efi_cons_probe, 69 efi_cons_init, 70 efi_cons_putchar, 71 efi_cons_getchar, 72 efi_cons_poll, 73 0 74 }; 75 76 #ifdef TERM_EMU 77 78 /* Get cursor position. */ 79 void 80 get_pos(UINTN *x, UINTN *y) 81 { 82 *x = conout->Mode->CursorColumn; 83 *y = conout->Mode->CursorRow; 84 } 85 86 /* Move cursor to x rows and y cols (0-based). */ 87 void 88 curs_move(UINTN *_x, UINTN *_y, UINTN x, UINTN y) 89 { 90 conout->SetCursorPosition(conout, x, y); 91 if (_x != NULL) 92 *_x = conout->Mode->CursorColumn; 93 if (_y != NULL) 94 *_y = conout->Mode->CursorRow; 95 } 96 97 /* Clear internal state of the terminal emulation code. */ 98 void 99 end_term(void) 100 { 101 esc = 0; 102 argc = -1; 103 } 104 105 #endif 106 107 static void 108 efi_cons_probe(struct console *cp) 109 { 110 conout = ST->ConOut; 111 conin = ST->ConIn; 112 cp->c_flags |= C_PRESENTIN | C_PRESENTOUT; 113 } 114 115 static int 116 efi_cons_init(struct console *cp __attribute((unused)), 117 int arg __attribute((unused))) 118 { 119 conout->SetAttribute(conout, EFI_TEXT_ATTR(DEFAULT_FGCOLOR, 120 DEFAULT_BGCOLOR)); 121 #ifdef TERM_EMU 122 end_term(); 123 get_pos(&curx, &cury); 124 curs_move(&curx, &cury, curx, cury); 125 fg_c = DEFAULT_FGCOLOR; 126 bg_c = DEFAULT_BGCOLOR; 127 memset(keybuf, 0, KEYBUFSZ); 128 #endif 129 conout->EnableCursor(conout, TRUE); 130 return 0; 131 } 132 133 static void 134 efi_cons_rawputchar(int c) 135 { 136 int i; 137 UINTN x, y; 138 conout->QueryMode(conout, conout->Mode->Mode, &x, &y); 139 static int ignorenl = 0; 140 141 if (c == '\t') 142 /* XXX lame tab expansion */ 143 for (i = 0; i < 8; i++) 144 efi_cons_rawputchar(' '); 145 else { 146 #ifndef TERM_EMU 147 if (c == '\n') 148 efi_cons_efiputchar('\r'); 149 else 150 efi_cons_efiputchar(c); 151 #else 152 switch (c) { 153 case '\r': 154 curx = 0; 155 break; 156 case '\n': 157 if (ignorenl) 158 ignorenl = 0; 159 else 160 cury++; 161 if ((efi_console.c_flags & C_MODERAW) == 0) 162 curx = 0; 163 if (cury >= y) { 164 efi_cons_efiputchar('\n'); 165 cury--; 166 } 167 break; 168 case '\b': 169 if (curx > 0) 170 curx--; 171 break; 172 default: 173 if (curx > x) { 174 curx = 0; 175 cury++; 176 curs_move(&curx, &cury, curx, cury); 177 } 178 if ((efi_console.c_flags & C_MODERAW) == 0) { 179 if (cury > y-1) { 180 curx = 0; 181 efi_cons_efiputchar('\n'); 182 cury--; 183 curs_move(&curx, &cury, curx, cury); 184 } 185 } 186 efi_cons_efiputchar(c); 187 curx++; 188 if ((efi_console.c_flags & C_MODERAW) == 0) { 189 if (curx == x) { 190 curx = 0; 191 ignorenl = 1; 192 } 193 } else if (curx == x) { 194 curx = 0; 195 if (cury == y) 196 efi_cons_efiputchar('\n'); 197 else 198 cury++; 199 } 200 } 201 curs_move(&curx, &cury, curx, cury); 202 #endif 203 } 204 } 205 206 /* Gracefully exit ESC-sequence processing in case of misunderstanding. */ 207 static void 208 bail_out(int c) 209 { 210 char buf[16], *ch; 211 int i; 212 213 if (esc) { 214 efi_cons_rawputchar('\033'); 215 if (esc != '\033') 216 efi_cons_rawputchar(esc); 217 for (i = 0; i <= argc; ++i) { 218 sprintf(buf, "%d", args[i]); 219 ch = buf; 220 while (*ch) 221 efi_cons_rawputchar(*ch++); 222 } 223 } 224 efi_cons_rawputchar(c); 225 end_term(); 226 } 227 228 /* Clear display from current position to end of screen. */ 229 static void 230 CD(void) { 231 UINTN i, x, y; 232 233 get_pos(&curx, &cury); 234 if (curx == 0 && cury == 0) { 235 conout->ClearScreen(conout); 236 end_term(); 237 return; 238 } 239 240 conout->QueryMode(conout, conout->Mode->Mode, &x, &y); 241 CL(0); /* clear current line from cursor to end */ 242 for (i = cury + 1; i < y-1; i++) { 243 curs_move(NULL, NULL, 0, i); 244 CL(0); 245 } 246 curs_move(NULL, NULL, curx, cury); 247 end_term(); 248 } 249 250 /* 251 * Absolute cursor move to args[0] rows and args[1] columns 252 * (the coordinates are 1-based). 253 */ 254 static void 255 CM(void) 256 { 257 if (args[0] > 0) 258 args[0]--; 259 if (args[1] > 0) 260 args[1]--; 261 curs_move(&curx, &cury, args[1], args[0]); 262 end_term(); 263 } 264 265 /* Home cursor (left top corner), also called from mode command. */ 266 void 267 HO(void) 268 { 269 argc = 1; 270 args[0] = args[1] = 1; 271 CM(); 272 } 273 274 /* Clear line from current position to end of line */ 275 static void 276 CL(int direction) 277 { 278 int i, len; 279 UINTN x, y; 280 CHAR16 *line; 281 282 conout->QueryMode(conout, conout->Mode->Mode, &x, &y); 283 switch (direction) { 284 case 0: /* from cursor to end */ 285 len = x - curx + 1; 286 break; 287 case 1: /* from beginning to cursor */ 288 len = curx; 289 break; 290 case 2: /* entire line */ 291 default: 292 len = x; 293 break; 294 } 295 296 if (cury == y - 1) 297 len--; 298 299 line = malloc(len * sizeof (CHAR16)); 300 if (line == NULL) { 301 printf("out of memory\n"); 302 return; 303 } 304 for (i = 0; i < len; i++) 305 line[i] = ' '; 306 line[len-1] = 0; 307 308 if (direction != 0) 309 curs_move(NULL, NULL, 0, cury); 310 311 conout->OutputString(conout, line); 312 /* restore cursor position */ 313 curs_move(NULL, NULL, curx, cury); 314 free(line); 315 end_term(); 316 } 317 318 static void 319 get_arg(int c) 320 { 321 if (argc < 0) 322 argc = 0; 323 args[argc] *= 10; 324 args[argc] += c - '0'; 325 } 326 327 /* Emulate basic capabilities of sun-color terminal */ 328 static void 329 efi_term_emu(int c) 330 { 331 static int ansi_col[] = { 332 0, 4, 2, 6, 1, 5, 3, 7 333 }; 334 int t, i; 335 336 switch (esc) { 337 case 0: 338 switch (c) { 339 case '\033': 340 esc = c; 341 break; 342 default: 343 efi_cons_rawputchar(c); 344 break; 345 } 346 break; 347 case '\033': 348 switch (c) { 349 case '[': 350 esc = c; 351 args[0] = 0; 352 argc = -1; 353 break; 354 default: 355 bail_out(c); 356 break; 357 } 358 break; 359 case '[': 360 switch (c) { 361 case ';': 362 if (argc < 0) 363 argc = 0; 364 else if (argc + 1 >= MAXARGS) 365 bail_out(c); 366 else 367 args[++argc] = 0; 368 break; 369 case 'A': /* UP = \E[%dA */ 370 if (argc == 0) { 371 UINTN x, y; 372 get_pos(&x, &y); 373 args[1] = x + 1; 374 args[0] = y - args[0] + 1; 375 CM(); 376 } else 377 bail_out(c); 378 break; 379 case 'B': /* DO = \E[%dB */ 380 if (argc == 0) { 381 UINTN x, y; 382 get_pos(&x, &y); 383 args[1] = x + 1; 384 args[0] = y + args[0] + 1; 385 CM(); 386 } else 387 bail_out(c); 388 break; 389 case 'C': /* RI = \E[%dC */ 390 if (argc == 0) { 391 UINTN x, y; 392 get_pos(&x, &y); 393 args[1] = args[0] + 1; 394 args[0] = y + 1; 395 CM(); 396 } else 397 bail_out(c); 398 break; 399 case 'H': /* ho = \E[H */ 400 if (argc < 0) 401 HO(); 402 else if (argc == 1) 403 CM(); 404 else 405 bail_out(c); 406 break; 407 case 'J': /* cd = \E[J */ 408 if (argc < 0) 409 CD(); 410 else 411 bail_out(c); 412 break; 413 case 'K': 414 if (argc < 0) 415 CL(0); 416 else if (argc == 0) 417 switch (args[0]) { 418 case 0: 419 case 1: 420 case 2: 421 CL(args[0]); 422 break; 423 default: 424 bail_out(c); 425 } 426 else 427 bail_out(c); 428 break; 429 case 'm': 430 if (argc < 0) { 431 fg_c = DEFAULT_FGCOLOR; 432 bg_c = DEFAULT_BGCOLOR; 433 } 434 for (i = 0; i <= argc; ++i) { 435 switch (args[i]) { 436 case 0: /* back to normal */ 437 fg_c = DEFAULT_FGCOLOR; 438 bg_c = DEFAULT_BGCOLOR; 439 break; 440 case 1: /* bold */ 441 fg_c |= 0x8; 442 break; 443 case 4: /* underline */ 444 case 5: /* blink */ 445 bg_c |= 0x8; 446 break; 447 case 7: /* reverse */ 448 t = fg_c; 449 fg_c = bg_c; 450 bg_c = t; 451 break; 452 case 30: case 31: case 32: case 33: 453 case 34: case 35: case 36: case 37: 454 fg_c = ansi_col[args[i] - 30]; 455 break; 456 case 39: /* normal */ 457 fg_c = DEFAULT_FGCOLOR; 458 break; 459 case 40: case 41: case 42: case 43: 460 case 44: case 45: case 46: case 47: 461 bg_c = ansi_col[args[i] - 40]; 462 break; 463 case 49: /* normal */ 464 bg_c = DEFAULT_BGCOLOR; 465 break; 466 } 467 } 468 conout->SetAttribute(conout, EFI_TEXT_ATTR(fg_c, bg_c)); 469 end_term(); 470 break; 471 default: 472 if (isdigit(c)) 473 get_arg(c); 474 else 475 bail_out(c); 476 break; 477 } 478 break; 479 default: 480 bail_out(c); 481 break; 482 } 483 } 484 485 void 486 efi_cons_putchar(struct console *cp __attribute((unused)), int c) 487 { 488 #ifdef TERM_EMU 489 efi_term_emu(c); 490 #else 491 efi_cons_rawputchar(c); 492 #endif 493 } 494 495 int 496 efi_cons_getchar(struct console *cp __attribute((unused))) 497 { 498 EFI_INPUT_KEY key; 499 EFI_STATUS status; 500 UINTN junk; 501 int i, c; 502 503 for (i = 0; i < KEYBUFSZ; i++) { 504 if (keybuf[i] != 0) { 505 c = keybuf[i]; 506 keybuf[i] = 0; 507 return (c); 508 } 509 } 510 511 /* Try to read a key stroke. We wait for one if none is pending. */ 512 status = conin->ReadKeyStroke(conin, &key); 513 if (status == EFI_NOT_READY) { 514 BS->WaitForEvent(1, &conin->WaitForKey, &junk); 515 status = conin->ReadKeyStroke(conin, &key); 516 } 517 518 switch (key.ScanCode) { 519 case 0x1: /* UP */ 520 keybuf[0] = '['; 521 keybuf[1] = 'A'; 522 return (0x1b); /* esc */ 523 case 0x2: /* DOWN */ 524 keybuf[0] = '['; 525 keybuf[1] = 'B'; 526 return (0x1b); /* esc */ 527 case 0x3: /* RIGHT */ 528 keybuf[0] = '['; 529 keybuf[1] = 'C'; 530 return (0x1b); /* esc */ 531 case 0x4: /* LEFT */ 532 keybuf[0] = '['; 533 keybuf[1] = 'D'; 534 return (0x1b); /* esc */ 535 case 0x17: /* ESC */ 536 return (0x1b); /* esc */ 537 } 538 539 /* this can return */ 540 return (key.UnicodeChar); 541 } 542 543 int 544 efi_cons_poll(struct console *cp __attribute((unused))) 545 { 546 int i; 547 548 for (i = 0; i < KEYBUFSZ; i++) { 549 if (keybuf[i] != 0) 550 return (1); 551 } 552 553 /* This can clear the signaled state. */ 554 return (BS->CheckEvent(conin->WaitForKey) == EFI_SUCCESS); 555 } 556 557 /* Plain direct access to EFI OutputString(). */ 558 void 559 efi_cons_efiputchar(int c) 560 { 561 CHAR16 buf[2]; 562 563 /* 564 * translate box chars to unicode 565 */ 566 switch (c) { 567 /* single frame */ 568 case 0xb3: buf[0] = BOXDRAW_VERTICAL; break; 569 case 0xbf: buf[0] = BOXDRAW_DOWN_LEFT; break; 570 case 0xc0: buf[0] = BOXDRAW_UP_RIGHT; break; 571 case 0xc4: buf[0] = BOXDRAW_HORIZONTAL; break; 572 case 0xda: buf[0] = BOXDRAW_DOWN_RIGHT; break; 573 case 0xd9: buf[0] = BOXDRAW_UP_LEFT; break; 574 575 /* double frame */ 576 case 0xba: buf[0] = BOXDRAW_DOUBLE_VERTICAL; break; 577 case 0xbb: buf[0] = BOXDRAW_DOUBLE_DOWN_LEFT; break; 578 case 0xbc: buf[0] = BOXDRAW_DOUBLE_UP_LEFT; break; 579 case 0xc8: buf[0] = BOXDRAW_DOUBLE_UP_RIGHT; break; 580 case 0xc9: buf[0] = BOXDRAW_DOUBLE_DOWN_RIGHT; break; 581 case 0xcd: buf[0] = BOXDRAW_DOUBLE_HORIZONTAL; break; 582 583 default: 584 buf[0] = c; 585 } 586 buf[1] = 0; /* terminate string */ 587 588 conout->OutputString(conout, buf); 589 } 590