console.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471
  1. /*
  2. * GRUB -- GRand Unified Bootloader
  3. * Copyright (C) 2006,2007,2008 Free Software Foundation, Inc.
  4. *
  5. * GRUB is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * GRUB is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #include <grub/term.h>
  19. #include <grub/misc.h>
  20. #include <grub/types.h>
  21. #include <grub/err.h>
  22. #include <grub/efi/efi.h>
  23. #include <grub/efi/api.h>
  24. #include <grub/efi/console.h>
  25. typedef enum {
  26. GRUB_TEXT_MODE_UNDEFINED = -1,
  27. GRUB_TEXT_MODE_UNAVAILABLE = 0,
  28. GRUB_TEXT_MODE_AVAILABLE
  29. }
  30. grub_text_mode;
  31. static grub_text_mode text_mode = GRUB_TEXT_MODE_UNDEFINED;
  32. static grub_term_color_state text_colorstate = GRUB_TERM_COLOR_UNDEFINED;
  33. static grub_uint32_t
  34. map_char (grub_uint32_t c)
  35. {
  36. /* Map some unicode characters to the EFI character. */
  37. switch (c)
  38. {
  39. case GRUB_UNICODE_LEFTARROW:
  40. c = GRUB_UNICODE_BLACK_LEFT_TRIANGLE;
  41. break;
  42. case GRUB_UNICODE_UPARROW:
  43. c = GRUB_UNICODE_BLACK_UP_TRIANGLE;
  44. break;
  45. case GRUB_UNICODE_RIGHTARROW:
  46. c = GRUB_UNICODE_BLACK_RIGHT_TRIANGLE;
  47. break;
  48. case GRUB_UNICODE_DOWNARROW:
  49. c = GRUB_UNICODE_BLACK_DOWN_TRIANGLE;
  50. break;
  51. case GRUB_UNICODE_HLINE:
  52. c = GRUB_UNICODE_LIGHT_HLINE;
  53. break;
  54. case GRUB_UNICODE_VLINE:
  55. c = GRUB_UNICODE_LIGHT_VLINE;
  56. break;
  57. case GRUB_UNICODE_CORNER_UL:
  58. c = GRUB_UNICODE_LIGHT_CORNER_UL;
  59. break;
  60. case GRUB_UNICODE_CORNER_UR:
  61. c = GRUB_UNICODE_LIGHT_CORNER_UR;
  62. break;
  63. case GRUB_UNICODE_CORNER_LL:
  64. c = GRUB_UNICODE_LIGHT_CORNER_LL;
  65. break;
  66. case GRUB_UNICODE_CORNER_LR:
  67. c = GRUB_UNICODE_LIGHT_CORNER_LR;
  68. break;
  69. }
  70. return c;
  71. }
  72. static void
  73. grub_console_setcolorstate (struct grub_term_output *term
  74. __attribute__ ((unused)),
  75. grub_term_color_state state)
  76. {
  77. grub_efi_simple_text_output_interface_t *o;
  78. if (grub_efi_is_finished)
  79. return;
  80. o = grub_efi_system_table->con_out;
  81. switch (state) {
  82. case GRUB_TERM_COLOR_STANDARD:
  83. efi_call_2 (o->set_attributes, o, GRUB_TERM_DEFAULT_STANDARD_COLOR
  84. & 0x7f);
  85. break;
  86. case GRUB_TERM_COLOR_NORMAL:
  87. efi_call_2 (o->set_attributes, o, grub_term_normal_color & 0x7f);
  88. break;
  89. case GRUB_TERM_COLOR_HIGHLIGHT:
  90. efi_call_2 (o->set_attributes, o, grub_term_highlight_color & 0x7f);
  91. break;
  92. default:
  93. break;
  94. }
  95. }
  96. static void
  97. grub_console_setcursor (struct grub_term_output *term __attribute__ ((unused)),
  98. int on)
  99. {
  100. grub_efi_simple_text_output_interface_t *o;
  101. if (grub_efi_is_finished)
  102. return;
  103. o = grub_efi_system_table->con_out;
  104. efi_call_2 (o->enable_cursor, o, on);
  105. }
  106. static grub_err_t
  107. grub_prepare_for_text_output (struct grub_term_output *term)
  108. {
  109. if (grub_efi_is_finished)
  110. return GRUB_ERR_BAD_DEVICE;
  111. if (text_mode != GRUB_TEXT_MODE_UNDEFINED)
  112. return text_mode ? GRUB_ERR_NONE : GRUB_ERR_BAD_DEVICE;
  113. if (! grub_efi_set_text_mode (1))
  114. {
  115. /* This really should never happen */
  116. grub_error (GRUB_ERR_BAD_DEVICE, "cannot set text mode");
  117. text_mode = GRUB_TEXT_MODE_UNAVAILABLE;
  118. return GRUB_ERR_BAD_DEVICE;
  119. }
  120. grub_console_setcursor (term, 1);
  121. if (text_colorstate != GRUB_TERM_COLOR_UNDEFINED)
  122. grub_console_setcolorstate (term, text_colorstate);
  123. text_mode = GRUB_TEXT_MODE_AVAILABLE;
  124. return GRUB_ERR_NONE;
  125. }
  126. static void
  127. grub_console_putchar (struct grub_term_output *term,
  128. const struct grub_unicode_glyph *c)
  129. {
  130. grub_efi_char16_t str[2 + 30];
  131. grub_efi_simple_text_output_interface_t *o;
  132. unsigned i, j;
  133. if (grub_prepare_for_text_output (term) != GRUB_ERR_NONE)
  134. return;
  135. o = grub_efi_system_table->con_out;
  136. /* For now, do not try to use a surrogate pair. */
  137. if (c->base > 0xffff)
  138. str[0] = '?';
  139. else
  140. str[0] = (grub_efi_char16_t) map_char (c->base & 0xffff);
  141. j = 1;
  142. for (i = 0; i < c->ncomb && j + 1 < ARRAY_SIZE (str); i++)
  143. if (c->base < 0xffff)
  144. str[j++] = grub_unicode_get_comb (c)[i].code;
  145. str[j] = 0;
  146. /* Should this test be cached? */
  147. if ((c->base > 0x7f || c->ncomb)
  148. && efi_call_2 (o->test_string, o, str) != GRUB_EFI_SUCCESS)
  149. return;
  150. efi_call_2 (o->output_string, o, str);
  151. }
  152. const unsigned efi_codes[] =
  153. {
  154. 0, GRUB_TERM_KEY_UP, GRUB_TERM_KEY_DOWN, GRUB_TERM_KEY_RIGHT,
  155. GRUB_TERM_KEY_LEFT, GRUB_TERM_KEY_HOME, GRUB_TERM_KEY_END, GRUB_TERM_KEY_INSERT,
  156. GRUB_TERM_KEY_DC, GRUB_TERM_KEY_PPAGE, GRUB_TERM_KEY_NPAGE, GRUB_TERM_KEY_F1,
  157. GRUB_TERM_KEY_F2, GRUB_TERM_KEY_F3, GRUB_TERM_KEY_F4, GRUB_TERM_KEY_F5,
  158. GRUB_TERM_KEY_F6, GRUB_TERM_KEY_F7, GRUB_TERM_KEY_F8, GRUB_TERM_KEY_F9,
  159. GRUB_TERM_KEY_F10, GRUB_TERM_KEY_F11, GRUB_TERM_KEY_F12, GRUB_TERM_ESC
  160. };
  161. static int
  162. grub_efi_translate_key (grub_efi_input_key_t key)
  163. {
  164. if (key.scan_code == 0)
  165. {
  166. /* Some firmware implementations use VT100-style codes against the spec.
  167. This is especially likely if driven by serial.
  168. */
  169. if (key.unicode_char < 0x20 && key.unicode_char != 0
  170. && key.unicode_char != '\t' && key.unicode_char != '\b'
  171. && key.unicode_char != '\n' && key.unicode_char != '\r')
  172. return GRUB_TERM_CTRL | (key.unicode_char - 1 + 'a');
  173. else
  174. return key.unicode_char;
  175. }
  176. /* Some devices send enter with scan_code 0x0d (F3) and unicode_char 0x0d. */
  177. else if (key.scan_code == '\r' && key.unicode_char == '\r')
  178. return key.unicode_char;
  179. else if (key.scan_code < ARRAY_SIZE (efi_codes))
  180. return efi_codes[key.scan_code];
  181. if ((key.unicode_char >= 0x20 && key.unicode_char <= 0x7f)
  182. || key.unicode_char == '\t' || key.unicode_char == '\b'
  183. || key.unicode_char == '\n' || key.unicode_char == '\r')
  184. return key.unicode_char;
  185. return GRUB_TERM_NO_KEY;
  186. }
  187. static int
  188. grub_console_getkey_con (struct grub_term_input *term __attribute__ ((unused)))
  189. {
  190. grub_efi_simple_input_interface_t *i;
  191. grub_efi_input_key_t key;
  192. grub_efi_status_t status;
  193. i = grub_efi_system_table->con_in;
  194. status = efi_call_2 (i->read_key_stroke, i, &key);
  195. if (status != GRUB_EFI_SUCCESS)
  196. return GRUB_TERM_NO_KEY;
  197. return grub_efi_translate_key(key);
  198. }
  199. /*
  200. * When more then just modifiers are pressed, our getkeystatus() consumes a
  201. * press from the queue, this function buffers the press for the regular
  202. * getkey() so that it does not get lost.
  203. */
  204. static grub_err_t
  205. grub_console_read_key_stroke (
  206. grub_efi_simple_text_input_ex_interface_t *text_input,
  207. grub_efi_key_data_t *key_data_ret, int *key_ret,
  208. int consume)
  209. {
  210. static grub_efi_key_data_t key_data;
  211. grub_efi_status_t status;
  212. int key;
  213. if (!text_input)
  214. return GRUB_ERR_EOF;
  215. key = grub_efi_translate_key (key_data.key);
  216. if (key == GRUB_TERM_NO_KEY) {
  217. status = efi_call_2 (text_input->read_key_stroke, text_input, &key_data);
  218. if (status != GRUB_EFI_SUCCESS)
  219. return GRUB_ERR_EOF;
  220. key = grub_efi_translate_key (key_data.key);
  221. }
  222. *key_data_ret = key_data;
  223. *key_ret = key;
  224. if (consume) {
  225. key_data.key.scan_code = 0;
  226. key_data.key.unicode_char = 0;
  227. }
  228. return GRUB_ERR_NONE;
  229. }
  230. static int
  231. grub_console_getkey_ex (struct grub_term_input *term)
  232. {
  233. grub_efi_key_data_t key_data;
  234. grub_efi_uint32_t kss;
  235. grub_err_t err;
  236. int key = -1;
  237. err = grub_console_read_key_stroke (term->data, &key_data, &key, 1);
  238. if (err != GRUB_ERR_NONE || key == GRUB_TERM_NO_KEY)
  239. return GRUB_TERM_NO_KEY;
  240. kss = key_data.key_state.key_shift_state;
  241. if (kss & GRUB_EFI_SHIFT_STATE_VALID)
  242. {
  243. if ((kss & GRUB_EFI_LEFT_SHIFT_PRESSED
  244. || kss & GRUB_EFI_RIGHT_SHIFT_PRESSED)
  245. && (key & GRUB_TERM_EXTENDED))
  246. key |= GRUB_TERM_SHIFT;
  247. if (kss & GRUB_EFI_LEFT_ALT_PRESSED || kss & GRUB_EFI_RIGHT_ALT_PRESSED)
  248. key |= GRUB_TERM_ALT;
  249. if (kss & GRUB_EFI_LEFT_CONTROL_PRESSED
  250. || kss & GRUB_EFI_RIGHT_CONTROL_PRESSED)
  251. key |= GRUB_TERM_CTRL;
  252. }
  253. return key;
  254. }
  255. static int
  256. grub_console_getkeystatus (struct grub_term_input *term)
  257. {
  258. grub_efi_key_data_t key_data;
  259. grub_efi_uint32_t kss;
  260. int key, mods = 0;
  261. if (grub_efi_is_finished)
  262. return 0;
  263. if (grub_console_read_key_stroke (term->data, &key_data, &key, 0))
  264. return 0;
  265. kss = key_data.key_state.key_shift_state;
  266. if (kss & GRUB_EFI_SHIFT_STATE_VALID)
  267. {
  268. if (kss & GRUB_EFI_LEFT_SHIFT_PRESSED)
  269. mods |= GRUB_TERM_STATUS_LSHIFT;
  270. if (kss & GRUB_EFI_RIGHT_SHIFT_PRESSED)
  271. mods |= GRUB_TERM_STATUS_RSHIFT;
  272. if (kss & GRUB_EFI_LEFT_ALT_PRESSED)
  273. mods |= GRUB_TERM_STATUS_LALT;
  274. if (kss & GRUB_EFI_RIGHT_ALT_PRESSED)
  275. mods |= GRUB_TERM_STATUS_RALT;
  276. if (kss & GRUB_EFI_LEFT_CONTROL_PRESSED)
  277. mods |= GRUB_TERM_STATUS_LCTRL;
  278. if (kss & GRUB_EFI_RIGHT_CONTROL_PRESSED)
  279. mods |= GRUB_TERM_STATUS_RCTRL;
  280. }
  281. return mods;
  282. }
  283. static grub_err_t
  284. grub_efi_console_input_init (struct grub_term_input *term)
  285. {
  286. grub_efi_guid_t text_input_ex_guid =
  287. GRUB_EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID;
  288. if (grub_efi_is_finished)
  289. return 0;
  290. grub_efi_simple_text_input_ex_interface_t *text_input = term->data;
  291. if (text_input)
  292. return 0;
  293. text_input = grub_efi_open_protocol(grub_efi_system_table->console_in_handler,
  294. &text_input_ex_guid,
  295. GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
  296. term->data = (void *)text_input;
  297. return 0;
  298. }
  299. static int
  300. grub_console_getkey (struct grub_term_input *term)
  301. {
  302. if (grub_efi_is_finished)
  303. return 0;
  304. if (term->data)
  305. return grub_console_getkey_ex(term);
  306. else
  307. return grub_console_getkey_con(term);
  308. }
  309. static struct grub_term_coordinate
  310. grub_console_getwh (struct grub_term_output *term)
  311. {
  312. grub_efi_simple_text_output_interface_t *o;
  313. grub_efi_uintn_t columns, rows;
  314. o = grub_efi_system_table->con_out;
  315. if (grub_prepare_for_text_output (term) != GRUB_ERR_NONE ||
  316. efi_call_4 (o->query_mode, o, o->mode->mode,
  317. &columns, &rows) != GRUB_EFI_SUCCESS)
  318. {
  319. /* Why does this fail? */
  320. columns = 80;
  321. rows = 25;
  322. }
  323. return (struct grub_term_coordinate) { columns, rows };
  324. }
  325. static struct grub_term_coordinate
  326. grub_console_getxy (struct grub_term_output *term __attribute__ ((unused)))
  327. {
  328. grub_efi_simple_text_output_interface_t *o;
  329. if (grub_efi_is_finished || text_mode != GRUB_TEXT_MODE_AVAILABLE)
  330. return (struct grub_term_coordinate) { 0, 0 };
  331. o = grub_efi_system_table->con_out;
  332. return (struct grub_term_coordinate) { o->mode->cursor_column, o->mode->cursor_row };
  333. }
  334. static void
  335. grub_console_gotoxy (struct grub_term_output *term,
  336. struct grub_term_coordinate pos)
  337. {
  338. grub_efi_simple_text_output_interface_t *o;
  339. if (grub_prepare_for_text_output (term) != GRUB_ERR_NONE)
  340. return;
  341. o = grub_efi_system_table->con_out;
  342. efi_call_3 (o->set_cursor_position, o, pos.x, pos.y);
  343. }
  344. static void
  345. grub_console_cls (struct grub_term_output *term __attribute__ ((unused)))
  346. {
  347. grub_efi_simple_text_output_interface_t *o;
  348. grub_efi_int32_t orig_attr;
  349. if (grub_efi_is_finished || text_mode != GRUB_TEXT_MODE_AVAILABLE)
  350. return;
  351. o = grub_efi_system_table->con_out;
  352. orig_attr = o->mode->attribute;
  353. efi_call_2 (o->set_attributes, o, GRUB_EFI_BACKGROUND_BLACK);
  354. efi_call_1 (o->clear_screen, o);
  355. efi_call_2 (o->set_attributes, o, orig_attr);
  356. }
  357. static grub_err_t
  358. grub_efi_console_output_fini (struct grub_term_output *term)
  359. {
  360. if (text_mode != GRUB_TEXT_MODE_AVAILABLE)
  361. return 0;
  362. grub_console_setcursor (term, 0);
  363. grub_efi_set_text_mode (0);
  364. text_mode = GRUB_TEXT_MODE_UNDEFINED;
  365. return 0;
  366. }
  367. static struct grub_term_input grub_console_term_input =
  368. {
  369. .name = "console",
  370. .getkey = grub_console_getkey,
  371. .getkeystatus = grub_console_getkeystatus,
  372. .init = grub_efi_console_input_init,
  373. };
  374. static struct grub_term_output grub_console_term_output =
  375. {
  376. .name = "console",
  377. .fini = grub_efi_console_output_fini,
  378. .putchar = grub_console_putchar,
  379. .getwh = grub_console_getwh,
  380. .getxy = grub_console_getxy,
  381. .gotoxy = grub_console_gotoxy,
  382. .cls = grub_console_cls,
  383. .setcolorstate = grub_console_setcolorstate,
  384. .setcursor = grub_console_setcursor,
  385. .flags = GRUB_TERM_CODE_TYPE_VISUAL_GLYPHS,
  386. .progress_update_divisor = GRUB_PROGRESS_FAST
  387. };
  388. void
  389. grub_console_init (void)
  390. {
  391. grub_term_register_output ("console", &grub_console_term_output);
  392. grub_term_register_input ("console", &grub_console_term_input);
  393. }
  394. void
  395. grub_console_fini (void)
  396. {
  397. grub_term_unregister_input (&grub_console_term_input);
  398. grub_term_unregister_output (&grub_console_term_output);
  399. }