1 /* 2 * EFI application console interface 3 * 4 * Copyright (c) 2016 Alexander Graf 5 * 6 * SPDX-License-Identifier: GPL-2.0+ 7 */ 8 9 #include <common.h> 10 #include <charset.h> 11 #include <dm/device.h> 12 #include <efi_loader.h> 13 #include <stdio_dev.h> 14 #include <video_console.h> 15 16 static bool console_size_queried; 17 18 #define EFI_COUT_MODE_2 2 19 #define EFI_MAX_COUT_MODE 3 20 21 struct cout_mode { 22 unsigned long columns; 23 unsigned long rows; 24 int present; 25 }; 26 27 static struct cout_mode efi_cout_modes[] = { 28 /* EFI Mode 0 is 80x25 and always present */ 29 { 30 .columns = 80, 31 .rows = 25, 32 .present = 1, 33 }, 34 /* EFI Mode 1 is always 80x50 */ 35 { 36 .columns = 80, 37 .rows = 50, 38 .present = 0, 39 }, 40 /* Value are unknown until we query the console */ 41 { 42 .columns = 0, 43 .rows = 0, 44 .present = 0, 45 }, 46 }; 47 48 const efi_guid_t efi_guid_console_control = CONSOLE_CONTROL_GUID; 49 const efi_guid_t efi_guid_text_output_protocol = 50 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_GUID; 51 const efi_guid_t efi_guid_text_input_protocol = 52 EFI_SIMPLE_TEXT_INPUT_PROTOCOL_GUID; 53 54 #define cESC '\x1b' 55 #define ESC "\x1b" 56 57 static efi_status_t EFIAPI efi_cin_get_mode( 58 struct efi_console_control_protocol *this, 59 int *mode, char *uga_exists, char *std_in_locked) 60 { 61 EFI_ENTRY("%p, %p, %p, %p", this, mode, uga_exists, std_in_locked); 62 63 if (mode) 64 *mode = EFI_CONSOLE_MODE_TEXT; 65 if (uga_exists) 66 *uga_exists = 0; 67 if (std_in_locked) 68 *std_in_locked = 0; 69 70 return EFI_EXIT(EFI_SUCCESS); 71 } 72 73 static efi_status_t EFIAPI efi_cin_set_mode( 74 struct efi_console_control_protocol *this, int mode) 75 { 76 EFI_ENTRY("%p, %d", this, mode); 77 return EFI_EXIT(EFI_UNSUPPORTED); 78 } 79 80 static efi_status_t EFIAPI efi_cin_lock_std_in( 81 struct efi_console_control_protocol *this, 82 uint16_t *password) 83 { 84 EFI_ENTRY("%p, %p", this, password); 85 return EFI_EXIT(EFI_UNSUPPORTED); 86 } 87 88 struct efi_console_control_protocol efi_console_control = { 89 .get_mode = efi_cin_get_mode, 90 .set_mode = efi_cin_set_mode, 91 .lock_std_in = efi_cin_lock_std_in, 92 }; 93 94 /* Default to mode 0 */ 95 static struct simple_text_output_mode efi_con_mode = { 96 .max_mode = 1, 97 .mode = 0, 98 .attribute = 0, 99 .cursor_column = 0, 100 .cursor_row = 0, 101 .cursor_visible = 1, 102 }; 103 104 static int term_read_reply(int *n, int maxnum, char end_char) 105 { 106 char c; 107 int i = 0; 108 109 c = getc(); 110 if (c != cESC) 111 return -1; 112 c = getc(); 113 if (c != '[') 114 return -1; 115 116 n[0] = 0; 117 while (1) { 118 c = getc(); 119 if (c == ';') { 120 i++; 121 if (i >= maxnum) 122 return -1; 123 n[i] = 0; 124 continue; 125 } else if (c == end_char) { 126 break; 127 } else if (c > '9' || c < '0') { 128 return -1; 129 } 130 131 /* Read one more decimal position */ 132 n[i] *= 10; 133 n[i] += c - '0'; 134 } 135 136 return 0; 137 } 138 139 static efi_status_t EFIAPI efi_cout_reset( 140 struct efi_simple_text_output_protocol *this, 141 char extended_verification) 142 { 143 EFI_ENTRY("%p, %d", this, extended_verification); 144 return EFI_EXIT(EFI_UNSUPPORTED); 145 } 146 147 static efi_status_t EFIAPI efi_cout_output_string( 148 struct efi_simple_text_output_protocol *this, 149 const efi_string_t string) 150 { 151 struct simple_text_output_mode *con = &efi_con_mode; 152 struct cout_mode *mode = &efi_cout_modes[con->mode]; 153 154 EFI_ENTRY("%p, %p", this, string); 155 156 unsigned int n16 = utf16_strlen(string); 157 char buf[MAX_UTF8_PER_UTF16 * n16 + 1]; 158 char *p; 159 160 *utf16_to_utf8((u8 *)buf, string, n16) = '\0'; 161 162 fputs(stdout, buf); 163 164 for (p = buf; *p; p++) { 165 switch (*p) { 166 case '\r': /* carriage-return */ 167 con->cursor_column = 0; 168 break; 169 case '\n': /* newline */ 170 con->cursor_column = 0; 171 con->cursor_row++; 172 break; 173 case '\t': /* tab, assume 8 char align */ 174 break; 175 case '\b': /* backspace */ 176 con->cursor_column = max(0, con->cursor_column - 1); 177 break; 178 default: 179 con->cursor_column++; 180 break; 181 } 182 if (con->cursor_column >= mode->columns) { 183 con->cursor_column = 0; 184 con->cursor_row++; 185 } 186 con->cursor_row = min(con->cursor_row, (s32)mode->rows - 1); 187 } 188 189 return EFI_EXIT(EFI_SUCCESS); 190 } 191 192 static efi_status_t EFIAPI efi_cout_test_string( 193 struct efi_simple_text_output_protocol *this, 194 const efi_string_t string) 195 { 196 EFI_ENTRY("%p, %p", this, string); 197 return EFI_EXIT(EFI_SUCCESS); 198 } 199 200 static bool cout_mode_matches(struct cout_mode *mode, int rows, int cols) 201 { 202 if (!mode->present) 203 return false; 204 205 return (mode->rows == rows) && (mode->columns == cols); 206 } 207 208 static int query_console_serial(int *rows, int *cols) 209 { 210 /* Ask the terminal about its size */ 211 int n[3]; 212 u64 timeout; 213 214 /* Empty input buffer */ 215 while (tstc()) 216 getc(); 217 218 printf(ESC"[18t"); 219 220 /* Check if we have a terminal that understands */ 221 timeout = timer_get_us() + 1000000; 222 while (!tstc()) 223 if (timer_get_us() > timeout) 224 return -1; 225 226 /* Read {depth,rows,cols} */ 227 if (term_read_reply(n, 3, 't')) 228 return -1; 229 230 *cols = n[2]; 231 *rows = n[1]; 232 233 return 0; 234 } 235 236 static efi_status_t EFIAPI efi_cout_query_mode( 237 struct efi_simple_text_output_protocol *this, 238 unsigned long mode_number, unsigned long *columns, 239 unsigned long *rows) 240 { 241 EFI_ENTRY("%p, %ld, %p, %p", this, mode_number, columns, rows); 242 243 if (!console_size_queried) { 244 const char *stdout_name = env_get("stdout"); 245 int rows, cols; 246 247 console_size_queried = true; 248 249 if (stdout_name && !strcmp(stdout_name, "vidconsole") && 250 IS_ENABLED(CONFIG_DM_VIDEO)) { 251 struct stdio_dev *stdout_dev = 252 stdio_get_by_name("vidconsole"); 253 struct udevice *dev = stdout_dev->priv; 254 struct vidconsole_priv *priv = 255 dev_get_uclass_priv(dev); 256 rows = priv->rows; 257 cols = priv->cols; 258 } else if (query_console_serial(&rows, &cols)) { 259 goto out; 260 } 261 262 /* Test if we can have Mode 1 */ 263 if (cols >= 80 && rows >= 50) { 264 efi_cout_modes[1].present = 1; 265 efi_con_mode.max_mode = 2; 266 } 267 268 /* 269 * Install our mode as mode 2 if it is different 270 * than mode 0 or 1 and set it as the currently selected mode 271 */ 272 if (!cout_mode_matches(&efi_cout_modes[0], rows, cols) && 273 !cout_mode_matches(&efi_cout_modes[1], rows, cols)) { 274 efi_cout_modes[EFI_COUT_MODE_2].columns = cols; 275 efi_cout_modes[EFI_COUT_MODE_2].rows = rows; 276 efi_cout_modes[EFI_COUT_MODE_2].present = 1; 277 efi_con_mode.max_mode = EFI_MAX_COUT_MODE; 278 efi_con_mode.mode = EFI_COUT_MODE_2; 279 } 280 } 281 282 if (mode_number >= efi_con_mode.max_mode) 283 return EFI_EXIT(EFI_UNSUPPORTED); 284 285 if (efi_cout_modes[mode_number].present != 1) 286 return EFI_EXIT(EFI_UNSUPPORTED); 287 288 out: 289 if (columns) 290 *columns = efi_cout_modes[mode_number].columns; 291 if (rows) 292 *rows = efi_cout_modes[mode_number].rows; 293 294 return EFI_EXIT(EFI_SUCCESS); 295 } 296 297 static efi_status_t EFIAPI efi_cout_set_mode( 298 struct efi_simple_text_output_protocol *this, 299 unsigned long mode_number) 300 { 301 EFI_ENTRY("%p, %ld", this, mode_number); 302 303 304 if (mode_number > efi_con_mode.max_mode) 305 return EFI_EXIT(EFI_UNSUPPORTED); 306 307 efi_con_mode.mode = mode_number; 308 efi_con_mode.cursor_column = 0; 309 efi_con_mode.cursor_row = 0; 310 311 return EFI_EXIT(EFI_SUCCESS); 312 } 313 314 static const struct { 315 unsigned int fg; 316 unsigned int bg; 317 } color[] = { 318 { 30, 40 }, /* 0: black */ 319 { 34, 44 }, /* 1: blue */ 320 { 32, 42 }, /* 2: green */ 321 { 36, 46 }, /* 3: cyan */ 322 { 31, 41 }, /* 4: red */ 323 { 35, 45 }, /* 5: magenta */ 324 { 33, 43 }, /* 6: brown, map to yellow as edk2 does*/ 325 { 37, 47 }, /* 7: light grey, map to white */ 326 }; 327 328 /* See EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.SetAttribute(). */ 329 static efi_status_t EFIAPI efi_cout_set_attribute( 330 struct efi_simple_text_output_protocol *this, 331 unsigned long attribute) 332 { 333 unsigned int bold = EFI_ATTR_BOLD(attribute); 334 unsigned int fg = EFI_ATTR_FG(attribute); 335 unsigned int bg = EFI_ATTR_BG(attribute); 336 337 EFI_ENTRY("%p, %lx", this, attribute); 338 339 if (attribute) 340 printf(ESC"[%u;%u;%um", bold, color[fg].fg, color[bg].bg); 341 else 342 printf(ESC"[0;37;40m"); 343 344 return EFI_EXIT(EFI_SUCCESS); 345 } 346 347 static efi_status_t EFIAPI efi_cout_clear_screen( 348 struct efi_simple_text_output_protocol *this) 349 { 350 EFI_ENTRY("%p", this); 351 352 printf(ESC"[2J"); 353 354 return EFI_EXIT(EFI_SUCCESS); 355 } 356 357 static efi_status_t EFIAPI efi_cout_set_cursor_position( 358 struct efi_simple_text_output_protocol *this, 359 unsigned long column, unsigned long row) 360 { 361 EFI_ENTRY("%p, %ld, %ld", this, column, row); 362 363 printf(ESC"[%d;%df", (int)row, (int)column); 364 efi_con_mode.cursor_column = column; 365 efi_con_mode.cursor_row = row; 366 367 return EFI_EXIT(EFI_SUCCESS); 368 } 369 370 static efi_status_t EFIAPI efi_cout_enable_cursor( 371 struct efi_simple_text_output_protocol *this, 372 bool enable) 373 { 374 EFI_ENTRY("%p, %d", this, enable); 375 376 printf(ESC"[?25%c", enable ? 'h' : 'l'); 377 378 return EFI_EXIT(EFI_SUCCESS); 379 } 380 381 struct efi_simple_text_output_protocol efi_con_out = { 382 .reset = efi_cout_reset, 383 .output_string = efi_cout_output_string, 384 .test_string = efi_cout_test_string, 385 .query_mode = efi_cout_query_mode, 386 .set_mode = efi_cout_set_mode, 387 .set_attribute = efi_cout_set_attribute, 388 .clear_screen = efi_cout_clear_screen, 389 .set_cursor_position = efi_cout_set_cursor_position, 390 .enable_cursor = efi_cout_enable_cursor, 391 .mode = (void*)&efi_con_mode, 392 }; 393 394 static efi_status_t EFIAPI efi_cin_reset( 395 struct efi_simple_input_interface *this, 396 bool extended_verification) 397 { 398 EFI_ENTRY("%p, %d", this, extended_verification); 399 return EFI_EXIT(EFI_UNSUPPORTED); 400 } 401 402 static efi_status_t EFIAPI efi_cin_read_key_stroke( 403 struct efi_simple_input_interface *this, 404 struct efi_input_key *key) 405 { 406 struct efi_input_key pressed_key = { 407 .scan_code = 0, 408 .unicode_char = 0, 409 }; 410 char ch; 411 412 EFI_ENTRY("%p, %p", this, key); 413 414 /* We don't do interrupts, so check for timers cooperatively */ 415 efi_timer_check(); 416 417 if (!tstc()) { 418 /* No key pressed */ 419 return EFI_EXIT(EFI_NOT_READY); 420 } 421 422 ch = getc(); 423 if (ch == cESC) { 424 /* Escape Sequence */ 425 ch = getc(); 426 switch (ch) { 427 case cESC: /* ESC */ 428 pressed_key.scan_code = 23; 429 break; 430 case 'O': /* F1 - F4 */ 431 pressed_key.scan_code = getc() - 'P' + 11; 432 break; 433 case 'a'...'z': 434 ch = ch - 'a'; 435 break; 436 case '[': 437 ch = getc(); 438 switch (ch) { 439 case 'A'...'D': /* up, down right, left */ 440 pressed_key.scan_code = ch - 'A' + 1; 441 break; 442 case 'F': /* End */ 443 pressed_key.scan_code = 6; 444 break; 445 case 'H': /* Home */ 446 pressed_key.scan_code = 5; 447 break; 448 case '1': /* F5 - F8 */ 449 pressed_key.scan_code = getc() - '0' + 11; 450 getc(); 451 break; 452 case '2': /* F9 - F12 */ 453 pressed_key.scan_code = getc() - '0' + 19; 454 getc(); 455 break; 456 case '3': /* DEL */ 457 pressed_key.scan_code = 8; 458 getc(); 459 break; 460 } 461 break; 462 } 463 } else if (ch == 0x7f) { 464 /* Backspace */ 465 ch = 0x08; 466 } 467 pressed_key.unicode_char = ch; 468 *key = pressed_key; 469 470 return EFI_EXIT(EFI_SUCCESS); 471 } 472 473 struct efi_simple_input_interface efi_con_in = { 474 .reset = efi_cin_reset, 475 .read_key_stroke = efi_cin_read_key_stroke, 476 .wait_for_key = NULL, 477 }; 478 479 static struct efi_event *console_timer_event; 480 481 static void EFIAPI efi_key_notify(struct efi_event *event, void *context) 482 { 483 } 484 485 /* 486 * Notification function of the console timer event. 487 * 488 * event: console timer event 489 * context: not used 490 */ 491 static void EFIAPI efi_console_timer_notify(struct efi_event *event, 492 void *context) 493 { 494 EFI_ENTRY("%p, %p", event, context); 495 496 /* Check if input is available */ 497 if (tstc()) { 498 /* Queue the wait for key event */ 499 efi_con_in.wait_for_key->is_signaled = true; 500 efi_signal_event(efi_con_in.wait_for_key, true); 501 } 502 EFI_EXIT(EFI_SUCCESS); 503 } 504 505 /* This gets called from do_bootefi_exec(). */ 506 int efi_console_register(void) 507 { 508 efi_status_t r; 509 struct efi_object *efi_console_control_obj; 510 struct efi_object *efi_console_output_obj; 511 struct efi_object *efi_console_input_obj; 512 513 /* Create handles */ 514 r = efi_create_handle((efi_handle_t *)&efi_console_control_obj); 515 if (r != EFI_SUCCESS) 516 goto out_of_memory; 517 r = efi_add_protocol(efi_console_control_obj->handle, 518 &efi_guid_console_control, &efi_console_control); 519 if (r != EFI_SUCCESS) 520 goto out_of_memory; 521 r = efi_create_handle((efi_handle_t *)&efi_console_output_obj); 522 if (r != EFI_SUCCESS) 523 goto out_of_memory; 524 r = efi_add_protocol(efi_console_output_obj->handle, 525 &efi_guid_text_output_protocol, &efi_con_out); 526 if (r != EFI_SUCCESS) 527 goto out_of_memory; 528 r = efi_create_handle((efi_handle_t *)&efi_console_input_obj); 529 if (r != EFI_SUCCESS) 530 goto out_of_memory; 531 r = efi_add_protocol(efi_console_input_obj->handle, 532 &efi_guid_text_input_protocol, &efi_con_in); 533 if (r != EFI_SUCCESS) 534 goto out_of_memory; 535 536 /* Create console events */ 537 r = efi_create_event(EVT_NOTIFY_WAIT, TPL_CALLBACK, 538 efi_key_notify, NULL, &efi_con_in.wait_for_key); 539 if (r != EFI_SUCCESS) { 540 printf("ERROR: Failed to register WaitForKey event\n"); 541 return r; 542 } 543 r = efi_create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK, 544 efi_console_timer_notify, NULL, 545 &console_timer_event); 546 if (r != EFI_SUCCESS) { 547 printf("ERROR: Failed to register console event\n"); 548 return r; 549 } 550 /* 5000 ns cycle is sufficient for 2 MBaud */ 551 r = efi_set_timer(console_timer_event, EFI_TIMER_PERIODIC, 50); 552 if (r != EFI_SUCCESS) 553 printf("ERROR: Failed to set console timer\n"); 554 return r; 555 out_of_memory: 556 printf("ERROR: Out of meemory\n"); 557 return r; 558 } 559