1*c1311ad4SAlexander Graf /* 2*c1311ad4SAlexander Graf * EFI application console interface 3*c1311ad4SAlexander Graf * 4*c1311ad4SAlexander Graf * Copyright (c) 2016 Alexander Graf 5*c1311ad4SAlexander Graf * 6*c1311ad4SAlexander Graf * SPDX-License-Identifier: GPL-2.0+ 7*c1311ad4SAlexander Graf */ 8*c1311ad4SAlexander Graf 9*c1311ad4SAlexander Graf #include <common.h> 10*c1311ad4SAlexander Graf #include <efi_loader.h> 11*c1311ad4SAlexander Graf 12*c1311ad4SAlexander Graf /* If we can't determine the console size, default to 80x24 */ 13*c1311ad4SAlexander Graf static int console_columns = 80; 14*c1311ad4SAlexander Graf static int console_rows = 24; 15*c1311ad4SAlexander Graf static bool console_size_queried; 16*c1311ad4SAlexander Graf 17*c1311ad4SAlexander Graf const efi_guid_t efi_guid_console_control = CONSOLE_CONTROL_GUID; 18*c1311ad4SAlexander Graf 19*c1311ad4SAlexander Graf #define cESC '\x1b' 20*c1311ad4SAlexander Graf #define ESC "\x1b" 21*c1311ad4SAlexander Graf 22*c1311ad4SAlexander Graf static efi_status_t EFIAPI efi_cin_get_mode( 23*c1311ad4SAlexander Graf struct efi_console_control_protocol *this, 24*c1311ad4SAlexander Graf int *mode, char *uga_exists, char *std_in_locked) 25*c1311ad4SAlexander Graf { 26*c1311ad4SAlexander Graf EFI_ENTRY("%p, %p, %p, %p", this, mode, uga_exists, std_in_locked); 27*c1311ad4SAlexander Graf 28*c1311ad4SAlexander Graf if (mode) 29*c1311ad4SAlexander Graf *mode = EFI_CONSOLE_MODE_TEXT; 30*c1311ad4SAlexander Graf if (uga_exists) 31*c1311ad4SAlexander Graf *uga_exists = 0; 32*c1311ad4SAlexander Graf if (std_in_locked) 33*c1311ad4SAlexander Graf *std_in_locked = 0; 34*c1311ad4SAlexander Graf 35*c1311ad4SAlexander Graf return EFI_EXIT(EFI_SUCCESS); 36*c1311ad4SAlexander Graf } 37*c1311ad4SAlexander Graf 38*c1311ad4SAlexander Graf static efi_status_t EFIAPI efi_cin_set_mode( 39*c1311ad4SAlexander Graf struct efi_console_control_protocol *this, int mode) 40*c1311ad4SAlexander Graf { 41*c1311ad4SAlexander Graf EFI_ENTRY("%p, %d", this, mode); 42*c1311ad4SAlexander Graf return EFI_EXIT(EFI_UNSUPPORTED); 43*c1311ad4SAlexander Graf } 44*c1311ad4SAlexander Graf 45*c1311ad4SAlexander Graf static efi_status_t EFIAPI efi_cin_lock_std_in( 46*c1311ad4SAlexander Graf struct efi_console_control_protocol *this, 47*c1311ad4SAlexander Graf uint16_t *password) 48*c1311ad4SAlexander Graf { 49*c1311ad4SAlexander Graf EFI_ENTRY("%p, %p", this, password); 50*c1311ad4SAlexander Graf return EFI_EXIT(EFI_UNSUPPORTED); 51*c1311ad4SAlexander Graf } 52*c1311ad4SAlexander Graf 53*c1311ad4SAlexander Graf const struct efi_console_control_protocol efi_console_control = { 54*c1311ad4SAlexander Graf .get_mode = efi_cin_get_mode, 55*c1311ad4SAlexander Graf .set_mode = efi_cin_set_mode, 56*c1311ad4SAlexander Graf .lock_std_in = efi_cin_lock_std_in, 57*c1311ad4SAlexander Graf }; 58*c1311ad4SAlexander Graf 59*c1311ad4SAlexander Graf static struct simple_text_output_mode efi_con_mode = { 60*c1311ad4SAlexander Graf .max_mode = 0, 61*c1311ad4SAlexander Graf .mode = 0, 62*c1311ad4SAlexander Graf .attribute = 0, 63*c1311ad4SAlexander Graf .cursor_column = 0, 64*c1311ad4SAlexander Graf .cursor_row = 0, 65*c1311ad4SAlexander Graf .cursor_visible = 1, 66*c1311ad4SAlexander Graf }; 67*c1311ad4SAlexander Graf 68*c1311ad4SAlexander Graf static int term_read_reply(int *n, int maxnum, char end_char) 69*c1311ad4SAlexander Graf { 70*c1311ad4SAlexander Graf char c; 71*c1311ad4SAlexander Graf int i = 0; 72*c1311ad4SAlexander Graf 73*c1311ad4SAlexander Graf c = getc(); 74*c1311ad4SAlexander Graf if (c != cESC) 75*c1311ad4SAlexander Graf return -1; 76*c1311ad4SAlexander Graf c = getc(); 77*c1311ad4SAlexander Graf if (c != '[') 78*c1311ad4SAlexander Graf return -1; 79*c1311ad4SAlexander Graf 80*c1311ad4SAlexander Graf n[0] = 0; 81*c1311ad4SAlexander Graf while (1) { 82*c1311ad4SAlexander Graf c = getc(); 83*c1311ad4SAlexander Graf if (c == ';') { 84*c1311ad4SAlexander Graf i++; 85*c1311ad4SAlexander Graf if (i >= maxnum) 86*c1311ad4SAlexander Graf return -1; 87*c1311ad4SAlexander Graf n[i] = 0; 88*c1311ad4SAlexander Graf continue; 89*c1311ad4SAlexander Graf } else if (c == end_char) { 90*c1311ad4SAlexander Graf break; 91*c1311ad4SAlexander Graf } else if (c > '9' || c < '0') { 92*c1311ad4SAlexander Graf return -1; 93*c1311ad4SAlexander Graf } 94*c1311ad4SAlexander Graf 95*c1311ad4SAlexander Graf /* Read one more decimal position */ 96*c1311ad4SAlexander Graf n[i] *= 10; 97*c1311ad4SAlexander Graf n[i] += c - '0'; 98*c1311ad4SAlexander Graf } 99*c1311ad4SAlexander Graf 100*c1311ad4SAlexander Graf return 0; 101*c1311ad4SAlexander Graf } 102*c1311ad4SAlexander Graf 103*c1311ad4SAlexander Graf static efi_status_t EFIAPI efi_cout_reset( 104*c1311ad4SAlexander Graf struct efi_simple_text_output_protocol *this, 105*c1311ad4SAlexander Graf char extended_verification) 106*c1311ad4SAlexander Graf { 107*c1311ad4SAlexander Graf EFI_ENTRY("%p, %d", this, extended_verification); 108*c1311ad4SAlexander Graf return EFI_EXIT(EFI_UNSUPPORTED); 109*c1311ad4SAlexander Graf } 110*c1311ad4SAlexander Graf 111*c1311ad4SAlexander Graf static void print_unicode_in_utf8(u16 c) 112*c1311ad4SAlexander Graf { 113*c1311ad4SAlexander Graf char utf8[4] = { 0 }; 114*c1311ad4SAlexander Graf char *b = utf8; 115*c1311ad4SAlexander Graf 116*c1311ad4SAlexander Graf if (c < 0x80) { 117*c1311ad4SAlexander Graf *(b++) = c; 118*c1311ad4SAlexander Graf } else if (c < 0x800) { 119*c1311ad4SAlexander Graf *(b++) = 192 + c / 64; 120*c1311ad4SAlexander Graf *(b++) = 128 + c % 64; 121*c1311ad4SAlexander Graf } else { 122*c1311ad4SAlexander Graf *(b++) = 224 + c / 4096; 123*c1311ad4SAlexander Graf *(b++) = 128 + c / 64 % 64; 124*c1311ad4SAlexander Graf *(b++) = 128 + c % 64; 125*c1311ad4SAlexander Graf } 126*c1311ad4SAlexander Graf 127*c1311ad4SAlexander Graf puts(utf8); 128*c1311ad4SAlexander Graf } 129*c1311ad4SAlexander Graf 130*c1311ad4SAlexander Graf static efi_status_t EFIAPI efi_cout_output_string( 131*c1311ad4SAlexander Graf struct efi_simple_text_output_protocol *this, 132*c1311ad4SAlexander Graf const unsigned short *string) 133*c1311ad4SAlexander Graf { 134*c1311ad4SAlexander Graf u16 ch; 135*c1311ad4SAlexander Graf 136*c1311ad4SAlexander Graf EFI_ENTRY("%p, %p", this, string); 137*c1311ad4SAlexander Graf for (;(ch = *string); string++) { 138*c1311ad4SAlexander Graf print_unicode_in_utf8(ch); 139*c1311ad4SAlexander Graf efi_con_mode.cursor_column++; 140*c1311ad4SAlexander Graf if (ch == '\n') { 141*c1311ad4SAlexander Graf efi_con_mode.cursor_column = 1; 142*c1311ad4SAlexander Graf efi_con_mode.cursor_row++; 143*c1311ad4SAlexander Graf } else if (efi_con_mode.cursor_column > console_columns) { 144*c1311ad4SAlexander Graf efi_con_mode.cursor_column = 1; 145*c1311ad4SAlexander Graf efi_con_mode.cursor_row++; 146*c1311ad4SAlexander Graf } 147*c1311ad4SAlexander Graf if (efi_con_mode.cursor_row > console_rows) { 148*c1311ad4SAlexander Graf efi_con_mode.cursor_row = console_rows; 149*c1311ad4SAlexander Graf } 150*c1311ad4SAlexander Graf } 151*c1311ad4SAlexander Graf 152*c1311ad4SAlexander Graf return EFI_EXIT(EFI_SUCCESS); 153*c1311ad4SAlexander Graf } 154*c1311ad4SAlexander Graf 155*c1311ad4SAlexander Graf static efi_status_t EFIAPI efi_cout_test_string( 156*c1311ad4SAlexander Graf struct efi_simple_text_output_protocol *this, 157*c1311ad4SAlexander Graf const unsigned short *string) 158*c1311ad4SAlexander Graf { 159*c1311ad4SAlexander Graf EFI_ENTRY("%p, %p", this, string); 160*c1311ad4SAlexander Graf return EFI_EXIT(EFI_SUCCESS); 161*c1311ad4SAlexander Graf } 162*c1311ad4SAlexander Graf 163*c1311ad4SAlexander Graf static efi_status_t EFIAPI efi_cout_query_mode( 164*c1311ad4SAlexander Graf struct efi_simple_text_output_protocol *this, 165*c1311ad4SAlexander Graf unsigned long mode_number, unsigned long *columns, 166*c1311ad4SAlexander Graf unsigned long *rows) 167*c1311ad4SAlexander Graf { 168*c1311ad4SAlexander Graf EFI_ENTRY("%p, %ld, %p, %p", this, mode_number, columns, rows); 169*c1311ad4SAlexander Graf 170*c1311ad4SAlexander Graf if (!console_size_queried) { 171*c1311ad4SAlexander Graf /* Ask the terminal about its size */ 172*c1311ad4SAlexander Graf int n[3]; 173*c1311ad4SAlexander Graf u64 timeout; 174*c1311ad4SAlexander Graf 175*c1311ad4SAlexander Graf console_size_queried = true; 176*c1311ad4SAlexander Graf 177*c1311ad4SAlexander Graf /* Empty input buffer */ 178*c1311ad4SAlexander Graf while (tstc()) 179*c1311ad4SAlexander Graf getc(); 180*c1311ad4SAlexander Graf 181*c1311ad4SAlexander Graf printf(ESC"[18t"); 182*c1311ad4SAlexander Graf 183*c1311ad4SAlexander Graf /* Check if we have a terminal that understands */ 184*c1311ad4SAlexander Graf timeout = timer_get_us() + 1000000; 185*c1311ad4SAlexander Graf while (!tstc()) 186*c1311ad4SAlexander Graf if (timer_get_us() > timeout) 187*c1311ad4SAlexander Graf goto out; 188*c1311ad4SAlexander Graf 189*c1311ad4SAlexander Graf /* Read {depth,rows,cols} */ 190*c1311ad4SAlexander Graf if (term_read_reply(n, 3, 't')) { 191*c1311ad4SAlexander Graf goto out; 192*c1311ad4SAlexander Graf } 193*c1311ad4SAlexander Graf 194*c1311ad4SAlexander Graf console_columns = n[2]; 195*c1311ad4SAlexander Graf console_rows = n[1]; 196*c1311ad4SAlexander Graf } 197*c1311ad4SAlexander Graf 198*c1311ad4SAlexander Graf out: 199*c1311ad4SAlexander Graf if (columns) 200*c1311ad4SAlexander Graf *columns = console_columns; 201*c1311ad4SAlexander Graf if (rows) 202*c1311ad4SAlexander Graf *rows = console_rows; 203*c1311ad4SAlexander Graf 204*c1311ad4SAlexander Graf return EFI_EXIT(EFI_SUCCESS); 205*c1311ad4SAlexander Graf } 206*c1311ad4SAlexander Graf 207*c1311ad4SAlexander Graf static efi_status_t EFIAPI efi_cout_set_mode( 208*c1311ad4SAlexander Graf struct efi_simple_text_output_protocol *this, 209*c1311ad4SAlexander Graf unsigned long mode_number) 210*c1311ad4SAlexander Graf { 211*c1311ad4SAlexander Graf EFI_ENTRY("%p, %ld", this, mode_number); 212*c1311ad4SAlexander Graf 213*c1311ad4SAlexander Graf /* We only support text output for now */ 214*c1311ad4SAlexander Graf if (mode_number == EFI_CONSOLE_MODE_TEXT) 215*c1311ad4SAlexander Graf return EFI_EXIT(EFI_SUCCESS); 216*c1311ad4SAlexander Graf 217*c1311ad4SAlexander Graf return EFI_EXIT(EFI_UNSUPPORTED); 218*c1311ad4SAlexander Graf } 219*c1311ad4SAlexander Graf 220*c1311ad4SAlexander Graf static efi_status_t EFIAPI efi_cout_set_attribute( 221*c1311ad4SAlexander Graf struct efi_simple_text_output_protocol *this, 222*c1311ad4SAlexander Graf unsigned long attribute) 223*c1311ad4SAlexander Graf { 224*c1311ad4SAlexander Graf EFI_ENTRY("%p, %lx", this, attribute); 225*c1311ad4SAlexander Graf 226*c1311ad4SAlexander Graf /* Just ignore attributes (colors) for now */ 227*c1311ad4SAlexander Graf return EFI_EXIT(EFI_UNSUPPORTED); 228*c1311ad4SAlexander Graf } 229*c1311ad4SAlexander Graf 230*c1311ad4SAlexander Graf static efi_status_t EFIAPI efi_cout_clear_screen( 231*c1311ad4SAlexander Graf struct efi_simple_text_output_protocol *this) 232*c1311ad4SAlexander Graf { 233*c1311ad4SAlexander Graf EFI_ENTRY("%p", this); 234*c1311ad4SAlexander Graf 235*c1311ad4SAlexander Graf printf(ESC"[2J"); 236*c1311ad4SAlexander Graf 237*c1311ad4SAlexander Graf return EFI_EXIT(EFI_SUCCESS); 238*c1311ad4SAlexander Graf } 239*c1311ad4SAlexander Graf 240*c1311ad4SAlexander Graf static efi_status_t EFIAPI efi_cout_set_cursor_position( 241*c1311ad4SAlexander Graf struct efi_simple_text_output_protocol *this, 242*c1311ad4SAlexander Graf unsigned long column, unsigned long row) 243*c1311ad4SAlexander Graf { 244*c1311ad4SAlexander Graf EFI_ENTRY("%p, %ld, %ld", this, column, row); 245*c1311ad4SAlexander Graf 246*c1311ad4SAlexander Graf printf(ESC"[%d;%df", (int)row, (int)column); 247*c1311ad4SAlexander Graf efi_con_mode.cursor_column = column; 248*c1311ad4SAlexander Graf efi_con_mode.cursor_row = row; 249*c1311ad4SAlexander Graf 250*c1311ad4SAlexander Graf return EFI_EXIT(EFI_SUCCESS); 251*c1311ad4SAlexander Graf } 252*c1311ad4SAlexander Graf 253*c1311ad4SAlexander Graf static efi_status_t EFIAPI efi_cout_enable_cursor( 254*c1311ad4SAlexander Graf struct efi_simple_text_output_protocol *this, 255*c1311ad4SAlexander Graf bool enable) 256*c1311ad4SAlexander Graf { 257*c1311ad4SAlexander Graf EFI_ENTRY("%p, %d", this, enable); 258*c1311ad4SAlexander Graf 259*c1311ad4SAlexander Graf printf(ESC"[?25%c", enable ? 'h' : 'l'); 260*c1311ad4SAlexander Graf 261*c1311ad4SAlexander Graf return EFI_EXIT(EFI_SUCCESS); 262*c1311ad4SAlexander Graf } 263*c1311ad4SAlexander Graf 264*c1311ad4SAlexander Graf const struct efi_simple_text_output_protocol efi_con_out = { 265*c1311ad4SAlexander Graf .reset = efi_cout_reset, 266*c1311ad4SAlexander Graf .output_string = efi_cout_output_string, 267*c1311ad4SAlexander Graf .test_string = efi_cout_test_string, 268*c1311ad4SAlexander Graf .query_mode = efi_cout_query_mode, 269*c1311ad4SAlexander Graf .set_mode = efi_cout_set_mode, 270*c1311ad4SAlexander Graf .set_attribute = efi_cout_set_attribute, 271*c1311ad4SAlexander Graf .clear_screen = efi_cout_clear_screen, 272*c1311ad4SAlexander Graf .set_cursor_position = efi_cout_set_cursor_position, 273*c1311ad4SAlexander Graf .enable_cursor = efi_cout_enable_cursor, 274*c1311ad4SAlexander Graf .mode = (void*)&efi_con_mode, 275*c1311ad4SAlexander Graf }; 276*c1311ad4SAlexander Graf 277*c1311ad4SAlexander Graf static efi_status_t EFIAPI efi_cin_reset( 278*c1311ad4SAlexander Graf struct efi_simple_input_interface *this, 279*c1311ad4SAlexander Graf bool extended_verification) 280*c1311ad4SAlexander Graf { 281*c1311ad4SAlexander Graf EFI_ENTRY("%p, %d", this, extended_verification); 282*c1311ad4SAlexander Graf return EFI_EXIT(EFI_UNSUPPORTED); 283*c1311ad4SAlexander Graf } 284*c1311ad4SAlexander Graf 285*c1311ad4SAlexander Graf static efi_status_t EFIAPI efi_cin_read_key_stroke( 286*c1311ad4SAlexander Graf struct efi_simple_input_interface *this, 287*c1311ad4SAlexander Graf struct efi_input_key *key) 288*c1311ad4SAlexander Graf { 289*c1311ad4SAlexander Graf struct efi_input_key pressed_key = { 290*c1311ad4SAlexander Graf .scan_code = 0, 291*c1311ad4SAlexander Graf .unicode_char = 0, 292*c1311ad4SAlexander Graf }; 293*c1311ad4SAlexander Graf char ch; 294*c1311ad4SAlexander Graf 295*c1311ad4SAlexander Graf EFI_ENTRY("%p, %p", this, key); 296*c1311ad4SAlexander Graf 297*c1311ad4SAlexander Graf /* We don't do interrupts, so check for timers cooperatively */ 298*c1311ad4SAlexander Graf efi_timer_check(); 299*c1311ad4SAlexander Graf 300*c1311ad4SAlexander Graf if (!tstc()) { 301*c1311ad4SAlexander Graf /* No key pressed */ 302*c1311ad4SAlexander Graf return EFI_EXIT(EFI_NOT_READY); 303*c1311ad4SAlexander Graf } 304*c1311ad4SAlexander Graf 305*c1311ad4SAlexander Graf ch = getc(); 306*c1311ad4SAlexander Graf if (ch == cESC) { 307*c1311ad4SAlexander Graf /* Escape Sequence */ 308*c1311ad4SAlexander Graf ch = getc(); 309*c1311ad4SAlexander Graf switch (ch) { 310*c1311ad4SAlexander Graf case cESC: /* ESC */ 311*c1311ad4SAlexander Graf pressed_key.scan_code = 23; 312*c1311ad4SAlexander Graf break; 313*c1311ad4SAlexander Graf case 'O': /* F1 - F4 */ 314*c1311ad4SAlexander Graf pressed_key.scan_code = getc() - 'P' + 11; 315*c1311ad4SAlexander Graf break; 316*c1311ad4SAlexander Graf case 'a'...'z': 317*c1311ad4SAlexander Graf ch = ch - 'a'; 318*c1311ad4SAlexander Graf break; 319*c1311ad4SAlexander Graf case '[': 320*c1311ad4SAlexander Graf ch = getc(); 321*c1311ad4SAlexander Graf switch (ch) { 322*c1311ad4SAlexander Graf case 'A'...'D': /* up, down right, left */ 323*c1311ad4SAlexander Graf pressed_key.scan_code = ch - 'A' + 1; 324*c1311ad4SAlexander Graf break; 325*c1311ad4SAlexander Graf case 'F': /* End */ 326*c1311ad4SAlexander Graf pressed_key.scan_code = 6; 327*c1311ad4SAlexander Graf break; 328*c1311ad4SAlexander Graf case 'H': /* Home */ 329*c1311ad4SAlexander Graf pressed_key.scan_code = 5; 330*c1311ad4SAlexander Graf break; 331*c1311ad4SAlexander Graf case '1': /* F5 - F8 */ 332*c1311ad4SAlexander Graf pressed_key.scan_code = getc() - '0' + 11; 333*c1311ad4SAlexander Graf getc(); 334*c1311ad4SAlexander Graf break; 335*c1311ad4SAlexander Graf case '2': /* F9 - F12 */ 336*c1311ad4SAlexander Graf pressed_key.scan_code = getc() - '0' + 19; 337*c1311ad4SAlexander Graf getc(); 338*c1311ad4SAlexander Graf break; 339*c1311ad4SAlexander Graf case '3': /* DEL */ 340*c1311ad4SAlexander Graf pressed_key.scan_code = 8; 341*c1311ad4SAlexander Graf getc(); 342*c1311ad4SAlexander Graf break; 343*c1311ad4SAlexander Graf } 344*c1311ad4SAlexander Graf break; 345*c1311ad4SAlexander Graf } 346*c1311ad4SAlexander Graf } else if (ch == 0x7f) { 347*c1311ad4SAlexander Graf /* Backspace */ 348*c1311ad4SAlexander Graf ch = 0x08; 349*c1311ad4SAlexander Graf } 350*c1311ad4SAlexander Graf pressed_key.unicode_char = ch; 351*c1311ad4SAlexander Graf *key = pressed_key; 352*c1311ad4SAlexander Graf 353*c1311ad4SAlexander Graf return EFI_EXIT(EFI_SUCCESS); 354*c1311ad4SAlexander Graf } 355*c1311ad4SAlexander Graf 356*c1311ad4SAlexander Graf const struct efi_simple_input_interface efi_con_in = { 357*c1311ad4SAlexander Graf .reset = efi_cin_reset, 358*c1311ad4SAlexander Graf .read_key_stroke = efi_cin_read_key_stroke, 359*c1311ad4SAlexander Graf .wait_for_key = NULL, 360*c1311ad4SAlexander Graf }; 361