efi_console.c 7.6 KB

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