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