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 #include <teken.h> 33 34 #include "bootstrap.h" 35 36 static EFI_GUID simple_input_ex_guid = EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID; 37 static SIMPLE_TEXT_OUTPUT_INTERFACE *conout; 38 static SIMPLE_INPUT_INTERFACE *conin; 39 static EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *coninex; 40 41 static tf_bell_t efi_cons_bell; 42 static tf_cursor_t efi_text_cursor; 43 static tf_putchar_t efi_text_putchar; 44 static tf_fill_t efi_text_fill; 45 static tf_copy_t efi_text_copy; 46 static tf_param_t efi_text_param; 47 static tf_respond_t efi_cons_respond; 48 49 static teken_funcs_t tf = { 50 .tf_bell = efi_cons_bell, 51 .tf_cursor = efi_text_cursor, 52 .tf_putchar = efi_text_putchar, 53 .tf_fill = efi_text_fill, 54 .tf_copy = efi_text_copy, 55 .tf_param = efi_text_param, 56 .tf_respond = efi_cons_respond, 57 }; 58 59 teken_t teken; 60 teken_pos_t tp; 61 62 struct text_pixel { 63 teken_char_t c; 64 teken_attr_t a; 65 }; 66 67 static struct text_pixel *buffer; 68 69 #define KEYBUFSZ 10 70 static unsigned keybuf[KEYBUFSZ]; /* keybuf for extended codes */ 71 static int key_pending; 72 73 static const unsigned char teken_color_to_efi_color[16] = { 74 EFI_BLACK, 75 EFI_RED, 76 EFI_GREEN, 77 EFI_BROWN, 78 EFI_BLUE, 79 EFI_MAGENTA, 80 EFI_CYAN, 81 EFI_LIGHTGRAY, 82 EFI_DARKGRAY, 83 EFI_LIGHTRED, 84 EFI_LIGHTGREEN, 85 EFI_YELLOW, 86 EFI_LIGHTBLUE, 87 EFI_LIGHTMAGENTA, 88 EFI_LIGHTCYAN, 89 EFI_WHITE 90 }; 91 92 static void efi_cons_probe(struct console *); 93 static int efi_cons_init(int); 94 void efi_cons_putchar(int); 95 int efi_cons_getchar(void); 96 void efi_cons_efiputchar(int); 97 int efi_cons_poll(void); 98 99 struct console efi_console = { 100 "efi", 101 "EFI console", 102 C_WIDEOUT, 103 efi_cons_probe, 104 efi_cons_init, 105 efi_cons_putchar, 106 efi_cons_getchar, 107 efi_cons_poll 108 }; 109 110 /* 111 * Not implemented. 112 */ 113 static void 114 efi_cons_bell(void *s __unused) 115 { 116 } 117 118 static void 119 efi_text_cursor(void *s __unused, const teken_pos_t *p) 120 { 121 UINTN row, col; 122 123 (void) conout->QueryMode(conout, conout->Mode->Mode, &col, &row); 124 125 if (p->tp_col == col) 126 col = p->tp_col - 1; 127 else 128 col = p->tp_col; 129 130 if (p->tp_row == row) 131 row = p->tp_row - 1; 132 else 133 row = p->tp_row; 134 135 conout->SetCursorPosition(conout, col, row); 136 } 137 138 static void 139 efi_text_printchar(const teken_pos_t *p) 140 { 141 UINTN a, attr; 142 struct text_pixel *px; 143 teken_color_t fg, bg, tmp; 144 145 px = buffer + p->tp_col + p->tp_row * tp.tp_col; 146 a = conout->Mode->Attribute; 147 148 fg = teken_256to16(px->a.ta_fgcolor); 149 bg = teken_256to16(px->a.ta_bgcolor); 150 if (px->a.ta_format & TF_BOLD) 151 fg |= TC_LIGHT; 152 if (px->a.ta_format & TF_BLINK) 153 bg |= TC_LIGHT; 154 155 if (px->a.ta_format & TF_REVERSE) { 156 tmp = fg; 157 fg = bg; 158 bg = tmp; 159 } 160 161 attr = EFI_TEXT_ATTR(teken_color_to_efi_color[fg], 162 teken_color_to_efi_color[bg] & 0x7); 163 164 conout->SetCursorPosition(conout, p->tp_col, p->tp_row); 165 166 /* to prvent autoscroll, skip print of lower right char */ 167 if (p->tp_row == tp.tp_row - 1 && 168 p->tp_col == tp.tp_col - 1) 169 return; 170 171 (void) conout->SetAttribute(conout, attr); 172 efi_cons_efiputchar(px->c); 173 (void) conout->SetAttribute(conout, a); 174 } 175 176 static void 177 efi_text_putchar(void *s __unused, const teken_pos_t *p, teken_char_t c, 178 const teken_attr_t *a) 179 { 180 EFI_STATUS status; 181 int idx; 182 183 idx = p->tp_col + p->tp_row * tp.tp_col; 184 buffer[idx].c = c; 185 buffer[idx].a = *a; 186 efi_text_printchar(p); 187 } 188 189 static void 190 efi_text_fill(void *s, const teken_rect_t *r, teken_char_t c, 191 const teken_attr_t *a) 192 { 193 teken_pos_t p; 194 UINTN row, col; 195 196 (void) conout->QueryMode(conout, conout->Mode->Mode, &col, &row); 197 198 conout->EnableCursor(conout, FALSE); 199 for (p.tp_row = r->tr_begin.tp_row; p.tp_row < r->tr_end.tp_row; 200 p.tp_row++) 201 for (p.tp_col = r->tr_begin.tp_col; 202 p.tp_col < r->tr_end.tp_col; p.tp_col++) 203 efi_text_putchar(s, &p, c, a); 204 conout->EnableCursor(conout, TRUE); 205 } 206 207 static bool 208 efi_same_pixel(struct text_pixel *px1, struct text_pixel *px2) 209 { 210 if (px1->c != px2->c) 211 return (false); 212 213 if (px1->a.ta_format != px2->a.ta_format) 214 return (false); 215 if (px1->a.ta_fgcolor != px2->a.ta_fgcolor) 216 return (false); 217 if (px1->a.ta_bgcolor != px2->a.ta_bgcolor) 218 return (false); 219 220 return (true); 221 } 222 223 static void 224 efi_text_copy(void *ptr __unused, const teken_rect_t *r, const teken_pos_t *p) 225 { 226 int srow, drow; 227 int nrow, ncol, x, y; /* Has to be signed - >= 0 comparison */ 228 teken_pos_t d, s; 229 230 /* 231 * Copying is a little tricky. We must make sure we do it in 232 * correct order, to make sure we don't overwrite our own data. 233 */ 234 235 nrow = r->tr_end.tp_row - r->tr_begin.tp_row; 236 ncol = r->tr_end.tp_col - r->tr_begin.tp_col; 237 238 conout->EnableCursor(conout, FALSE); 239 if (p->tp_row < r->tr_begin.tp_row) { 240 /* Copy from bottom to top. */ 241 for (y = 0; y < nrow; y++) { 242 d.tp_row = p->tp_row + y; 243 s.tp_row = r->tr_begin.tp_row + y; 244 drow = d.tp_row * tp.tp_col; 245 srow = s.tp_row * tp.tp_col; 246 for (x = 0; x < ncol; x++) { 247 d.tp_col = p->tp_col + x; 248 s.tp_col = r->tr_begin.tp_col + x; 249 250 if (!efi_same_pixel( 251 &buffer[d.tp_col + drow], 252 &buffer[s.tp_col + srow])) { 253 buffer[d.tp_col + drow] = 254 buffer[s.tp_col + srow]; 255 efi_text_printchar(&d); 256 } 257 } 258 } 259 } else { 260 /* Copy from top to bottom. */ 261 if (p->tp_col < r->tr_begin.tp_col) { 262 /* Copy from right to left. */ 263 for (y = nrow - 1; y >= 0; y--) { 264 d.tp_row = p->tp_row + y; 265 s.tp_row = r->tr_begin.tp_row + y; 266 drow = d.tp_row * tp.tp_col; 267 srow = s.tp_row * tp.tp_col; 268 for (x = 0; x < ncol; x++) { 269 d.tp_col = p->tp_col + x; 270 s.tp_col = r->tr_begin.tp_col + x; 271 272 if (!efi_same_pixel( 273 &buffer[d.tp_col + drow], 274 &buffer[s.tp_col + srow])) { 275 buffer[d.tp_col + drow] = 276 buffer[s.tp_col + srow]; 277 efi_text_printchar(&d); 278 } 279 } 280 } 281 } else { 282 /* Copy from left to right. */ 283 for (y = nrow - 1; y >= 0; y--) { 284 d.tp_row = p->tp_row + y; 285 s.tp_row = r->tr_begin.tp_row + y; 286 drow = d.tp_row * tp.tp_col; 287 srow = s.tp_row * tp.tp_col; 288 for (x = ncol - 1; x >= 0; x--) { 289 d.tp_col = p->tp_col + x; 290 s.tp_col = r->tr_begin.tp_col + x; 291 292 if (!efi_same_pixel( 293 &buffer[d.tp_col + drow], 294 &buffer[s.tp_col + srow])) { 295 buffer[d.tp_col + drow] = 296 buffer[s.tp_col + srow]; 297 efi_text_printchar(&d); 298 } 299 } 300 } 301 } 302 } 303 conout->EnableCursor(conout, TRUE); 304 } 305 306 static void 307 efi_text_param(void *s __unused, int cmd, unsigned int value) 308 { 309 switch (cmd) { 310 case TP_SETLOCALCURSOR: 311 /* 312 * 0 means normal (usually block), 1 means hidden, and 313 * 2 means blinking (always block) for compatibility with 314 * syscons. We don't support any changes except hiding, 315 * so must map 2 to 0. 316 */ 317 value = (value == 1) ? 0 : 1; 318 /* FALLTHROUGH */ 319 case TP_SHOWCURSOR: 320 if (value == 1) 321 conout->EnableCursor(conout, TRUE); 322 else 323 conout->EnableCursor(conout, FALSE); 324 break; 325 default: 326 /* Not yet implemented */ 327 break; 328 } 329 } 330 331 /* 332 * Not implemented. 333 */ 334 static void 335 efi_cons_respond(void *s __unused, const void *buf __unused, 336 size_t len __unused) 337 { 338 } 339 340 static void 341 efi_cons_probe(struct console *cp) 342 { 343 cp->c_flags |= C_PRESENTIN | C_PRESENTOUT; 344 } 345 346 bool 347 efi_cons_update_mode(void) 348 { 349 UINTN cols, rows; 350 const teken_attr_t *a; 351 EFI_STATUS status; 352 char env[8]; 353 354 status = conout->QueryMode(conout, conout->Mode->Mode, &cols, &rows); 355 if (EFI_ERROR(status)) { 356 cols = 80; 357 rows = 24; 358 } 359 360 if (buffer != NULL) { 361 if (tp.tp_row == rows && tp.tp_col == cols) 362 return (true); 363 free(buffer); 364 } else { 365 teken_init(&teken, &tf, NULL); 366 } 367 368 tp.tp_row = rows; 369 tp.tp_col = cols; 370 buffer = malloc(rows * cols * sizeof(*buffer)); 371 if (buffer == NULL) 372 return (false); 373 374 teken_set_winsize(&teken, &tp); 375 a = teken_get_defattr(&teken); 376 377 for (int row = 0; row < rows; row++) 378 for (int col = 0; col < cols; col++) { 379 buffer[col + row * tp.tp_col].c = ' '; 380 buffer[col + row * tp.tp_col].a = *a; 381 } 382 383 snprintf(env, sizeof (env), "%u", (unsigned)rows); 384 setenv("LINES", env, 1); 385 snprintf(env, sizeof (env), "%u", (unsigned)cols); 386 setenv("COLUMNS", env, 1); 387 388 return (true); 389 } 390 391 static int 392 efi_cons_init(int arg) 393 { 394 EFI_STATUS status; 395 396 if (conin != NULL) 397 return (0); 398 399 conout = ST->ConOut; 400 conin = ST->ConIn; 401 402 conout->EnableCursor(conout, TRUE); 403 status = BS->OpenProtocol(ST->ConsoleInHandle, &simple_input_ex_guid, 404 (void **)&coninex, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); 405 if (status != EFI_SUCCESS) 406 coninex = NULL; 407 408 if (efi_cons_update_mode()) 409 return (0); 410 411 return (1); 412 } 413 414 void 415 efi_cons_putchar(int c) 416 { 417 unsigned char ch = c; 418 419 if (buffer != NULL) 420 teken_input(&teken, &ch, sizeof (ch)); 421 else 422 efi_cons_efiputchar(c); 423 } 424 425 static int 426 keybuf_getchar(void) 427 { 428 int i, c = 0; 429 430 for (i = 0; i < KEYBUFSZ; i++) { 431 if (keybuf[i] != 0) { 432 c = keybuf[i]; 433 keybuf[i] = 0; 434 break; 435 } 436 } 437 438 return (c); 439 } 440 441 static bool 442 keybuf_ischar(void) 443 { 444 int i; 445 446 for (i = 0; i < KEYBUFSZ; i++) { 447 if (keybuf[i] != 0) 448 return (true); 449 } 450 return (false); 451 } 452 453 /* 454 * We are not reading input before keybuf is empty, so we are safe 455 * just to fill keybuf from the beginning. 456 */ 457 static void 458 keybuf_inschar(EFI_INPUT_KEY *key) 459 { 460 461 switch (key->ScanCode) { 462 case SCAN_UP: /* UP */ 463 keybuf[0] = 0x1b; /* esc */ 464 keybuf[1] = '['; 465 keybuf[2] = 'A'; 466 break; 467 case SCAN_DOWN: /* DOWN */ 468 keybuf[0] = 0x1b; /* esc */ 469 keybuf[1] = '['; 470 keybuf[2] = 'B'; 471 break; 472 case SCAN_RIGHT: /* RIGHT */ 473 keybuf[0] = 0x1b; /* esc */ 474 keybuf[1] = '['; 475 keybuf[2] = 'C'; 476 break; 477 case SCAN_LEFT: /* LEFT */ 478 keybuf[0] = 0x1b; /* esc */ 479 keybuf[1] = '['; 480 keybuf[2] = 'D'; 481 break; 482 case SCAN_DELETE: 483 keybuf[0] = CHAR_BACKSPACE; 484 break; 485 case SCAN_ESC: 486 keybuf[0] = 0x1b; /* esc */ 487 break; 488 default: 489 keybuf[0] = key->UnicodeChar; 490 break; 491 } 492 } 493 494 static bool 495 efi_readkey(void) 496 { 497 EFI_STATUS status; 498 EFI_INPUT_KEY key; 499 500 status = conin->ReadKeyStroke(conin, &key); 501 if (status == EFI_SUCCESS) { 502 keybuf_inschar(&key); 503 return (true); 504 } 505 return (false); 506 } 507 508 static bool 509 efi_readkey_ex(void) 510 { 511 EFI_STATUS status; 512 EFI_INPUT_KEY *kp; 513 EFI_KEY_DATA key_data; 514 uint32_t kss; 515 516 status = coninex->ReadKeyStrokeEx(coninex, &key_data); 517 if (status == EFI_SUCCESS) { 518 kss = key_data.KeyState.KeyShiftState; 519 kp = &key_data.Key; 520 if (kss & EFI_SHIFT_STATE_VALID) { 521 522 /* 523 * quick mapping to control chars, replace with 524 * map lookup later. 525 */ 526 if (kss & EFI_RIGHT_CONTROL_PRESSED || 527 kss & EFI_LEFT_CONTROL_PRESSED) { 528 if (kp->UnicodeChar >= 'a' && 529 kp->UnicodeChar <= 'z') { 530 kp->UnicodeChar -= 'a'; 531 kp->UnicodeChar++; 532 } 533 } 534 } 535 536 keybuf_inschar(kp); 537 return (true); 538 } 539 return (false); 540 } 541 542 int 543 efi_cons_getchar(void) 544 { 545 int c; 546 547 if ((c = keybuf_getchar()) != 0) 548 return (c); 549 550 key_pending = 0; 551 552 if (coninex == NULL) { 553 if (efi_readkey()) 554 return (keybuf_getchar()); 555 } else { 556 if (efi_readkey_ex()) 557 return (keybuf_getchar()); 558 } 559 560 return (-1); 561 } 562 563 int 564 efi_cons_poll(void) 565 { 566 EFI_STATUS status; 567 568 if (keybuf_ischar() || key_pending) 569 return (1); 570 571 /* 572 * Some EFI implementation (u-boot for example) do not support 573 * WaitForKey(). 574 * CheckEvent() can clear the signaled state. 575 */ 576 if (coninex != NULL) { 577 if (coninex->WaitForKeyEx == NULL) { 578 key_pending = efi_readkey_ex(); 579 } else { 580 status = BS->CheckEvent(coninex->WaitForKeyEx); 581 key_pending = status == EFI_SUCCESS; 582 } 583 } else { 584 if (conin->WaitForKey == NULL) { 585 key_pending = efi_readkey(); 586 } else { 587 status = BS->CheckEvent(conin->WaitForKey); 588 key_pending = status == EFI_SUCCESS; 589 } 590 } 591 592 return (key_pending); 593 } 594 595 /* Plain direct access to EFI OutputString(). */ 596 void 597 efi_cons_efiputchar(int c) 598 { 599 CHAR16 buf[2]; 600 EFI_STATUS status; 601 602 buf[0] = c; 603 buf[1] = 0; /* terminate string */ 604 605 status = conout->TestString(conout, buf); 606 if (EFI_ERROR(status)) 607 buf[0] = '?'; 608 conout->OutputString(conout, buf); 609 } 610