/* * VAS_EBOOT -- GRand Unified Bootloader * Copyright (C) 2006,2007,2008 Free Software Foundation, Inc. * * VAS_EBOOT is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * VAS_EBOOT is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with VAS_EBOOT. If not, see . */ #include #include #include #include #include #include #include typedef enum { VAS_EBOOT_TEXT_MODE_UNDEFINED = -1, VAS_EBOOT_TEXT_MODE_UNAVAILABLE = 0, VAS_EBOOT_TEXT_MODE_AVAILABLE } VasEBoot_text_mode; typedef enum { VAS_EBOOT_CURSOR_MODE_UNDEFINED = -1, VAS_EBOOT_CURSOR_MODE_OFF = 0, VAS_EBOOT_CURSUR_MODE_ON } VasEBoot_cursor_mode; static VasEBoot_text_mode text_mode = VAS_EBOOT_TEXT_MODE_UNDEFINED; static VasEBoot_cursor_mode cursor_mode = VAS_EBOOT_CURSOR_MODE_UNDEFINED; static VasEBoot_term_color_state text_colorstate = VAS_EBOOT_TERM_COLOR_UNDEFINED; static VasEBoot_uint32_t map_char (VasEBoot_uint32_t c) { /* Map some unicode characters to the EFI character. */ switch (c) { case VAS_EBOOT_UNICODE_LEFTARROW: c = VAS_EBOOT_UNICODE_BLACK_LEFT_TRIANGLE; break; case VAS_EBOOT_UNICODE_UPARROW: c = VAS_EBOOT_UNICODE_BLACK_UP_TRIANGLE; break; case VAS_EBOOT_UNICODE_RIGHTARROW: c = VAS_EBOOT_UNICODE_BLACK_RIGHT_TRIANGLE; break; case VAS_EBOOT_UNICODE_DOWNARROW: c = VAS_EBOOT_UNICODE_BLACK_DOWN_TRIANGLE; break; case VAS_EBOOT_UNICODE_HLINE: c = VAS_EBOOT_UNICODE_LIGHT_HLINE; break; case VAS_EBOOT_UNICODE_VLINE: c = VAS_EBOOT_UNICODE_LIGHT_VLINE; break; case VAS_EBOOT_UNICODE_CORNER_UL: c = VAS_EBOOT_UNICODE_LIGHT_CORNER_UL; break; case VAS_EBOOT_UNICODE_CORNER_UR: c = VAS_EBOOT_UNICODE_LIGHT_CORNER_UR; break; case VAS_EBOOT_UNICODE_CORNER_LL: c = VAS_EBOOT_UNICODE_LIGHT_CORNER_LL; break; case VAS_EBOOT_UNICODE_CORNER_LR: c = VAS_EBOOT_UNICODE_LIGHT_CORNER_LR; break; } return c; } static void VasEBoot_console_setcolorstate (struct VasEBoot_term_output *term __attribute__ ((unused)), VasEBoot_term_color_state state) { VasEBoot_efi_simple_text_output_interface_t *o; if (VasEBoot_efi_is_finished || text_mode != VAS_EBOOT_TEXT_MODE_AVAILABLE) { /* * Cache colorstate changes before the first text-output, this avoids * "color_normal" environment writes causing a switch to textmode. */ text_colorstate = state; return; } if (VasEBoot_efi_is_finished) return; o = VasEBoot_efi_system_table->con_out; switch (state) { case VAS_EBOOT_TERM_COLOR_STANDARD: o->set_attributes (o, VAS_EBOOT_TERM_DEFAULT_STANDARD_COLOR & 0x7f); break; case VAS_EBOOT_TERM_COLOR_NORMAL: o->set_attributes (o, VasEBoot_term_normal_color & 0x7f); break; case VAS_EBOOT_TERM_COLOR_HIGHLIGHT: o->set_attributes (o, VasEBoot_term_highlight_color & 0x7f); break; default: break; } } static void VasEBoot_console_setcursor (struct VasEBoot_term_output *term __attribute__ ((unused)), int on) { VasEBoot_efi_simple_text_output_interface_t *o; if (VasEBoot_efi_is_finished || text_mode != VAS_EBOOT_TEXT_MODE_AVAILABLE) { /* Cache cursor changes before the first text-output */ cursor_mode = on; return; } o = VasEBoot_efi_system_table->con_out; o->enable_cursor (o, on); } static VasEBoot_err_t VasEBoot_prepare_for_text_output (struct VasEBoot_term_output *term) { if (VasEBoot_efi_is_finished) return VAS_EBOOT_ERR_BAD_DEVICE; if (text_mode != VAS_EBOOT_TEXT_MODE_UNDEFINED) return text_mode ? VAS_EBOOT_ERR_NONE : VAS_EBOOT_ERR_BAD_DEVICE; if (! VasEBoot_efi_set_text_mode (1)) { /* This really should never happen */ VasEBoot_error (VAS_EBOOT_ERR_BAD_DEVICE, "cannot set text mode"); text_mode = VAS_EBOOT_TEXT_MODE_UNAVAILABLE; return VAS_EBOOT_ERR_BAD_DEVICE; } if (cursor_mode != VAS_EBOOT_CURSOR_MODE_UNDEFINED) VasEBoot_console_setcursor (term, cursor_mode); if (text_colorstate != VAS_EBOOT_TERM_COLOR_UNDEFINED) VasEBoot_console_setcolorstate (term, text_colorstate); text_mode = VAS_EBOOT_TEXT_MODE_AVAILABLE; return VAS_EBOOT_ERR_NONE; } static void VasEBoot_console_putchar (struct VasEBoot_term_output *term, const struct VasEBoot_unicode_glyph *c) { VasEBoot_efi_char16_t str[2 + 30]; VasEBoot_efi_simple_text_output_interface_t *o; unsigned i, j; if (VasEBoot_prepare_for_text_output (term) != VAS_EBOOT_ERR_NONE) return; o = VasEBoot_efi_system_table->con_out; /* For now, do not try to use a surrogate pair. */ if (c->base > 0xffff) str[0] = '?'; else str[0] = (VasEBoot_efi_char16_t) map_char (c->base & 0xffff); j = 1; for (i = 0; i < c->ncomb && j + 1 < ARRAY_SIZE (str); i++) if (c->base < 0xffff) str[j++] = VasEBoot_unicode_get_comb (c)[i].code; str[j] = 0; /* Should this test be cached? */ if ((c->base > 0x7f || c->ncomb) && o->test_string (o, str) != VAS_EBOOT_EFI_SUCCESS) return; o->output_string (o, str); } const unsigned efi_codes[] = { 0, VAS_EBOOT_TERM_KEY_UP, VAS_EBOOT_TERM_KEY_DOWN, VAS_EBOOT_TERM_KEY_RIGHT, VAS_EBOOT_TERM_KEY_LEFT, VAS_EBOOT_TERM_KEY_HOME, VAS_EBOOT_TERM_KEY_END, VAS_EBOOT_TERM_KEY_INSERT, VAS_EBOOT_TERM_KEY_DC, VAS_EBOOT_TERM_KEY_PPAGE, VAS_EBOOT_TERM_KEY_NPAGE, VAS_EBOOT_TERM_KEY_F1, VAS_EBOOT_TERM_KEY_F2, VAS_EBOOT_TERM_KEY_F3, VAS_EBOOT_TERM_KEY_F4, VAS_EBOOT_TERM_KEY_F5, VAS_EBOOT_TERM_KEY_F6, VAS_EBOOT_TERM_KEY_F7, VAS_EBOOT_TERM_KEY_F8, VAS_EBOOT_TERM_KEY_F9, VAS_EBOOT_TERM_KEY_F10, VAS_EBOOT_TERM_KEY_F11, VAS_EBOOT_TERM_KEY_F12, VAS_EBOOT_TERM_ESC }; static int VasEBoot_efi_translate_key (VasEBoot_efi_input_key_t key) { if (key.scan_code == 0) { /* Some firmware implementations use VT100-style codes against the spec. This is especially likely if driven by serial. */ if (key.unicode_char < 0x20 && key.unicode_char != 0 && key.unicode_char != '\t' && key.unicode_char != '\b' && key.unicode_char != '\n' && key.unicode_char != '\r') return VAS_EBOOT_TERM_CTRL | (key.unicode_char - 1 + 'a'); else return key.unicode_char; } /* Some devices use power key (key.scan_code 0x0102, suspend) as Enter. */ else if (key.scan_code == 0x0102) return '\r'; /* Some devices send enter with scan_code 0x0d (F3) and unicode_char 0x0d. */ else if (key.scan_code == '\r' && key.unicode_char == '\r') return key.unicode_char; else if (key.scan_code < ARRAY_SIZE (efi_codes)) return efi_codes[key.scan_code]; if ((key.unicode_char >= 0x20 && key.unicode_char <= 0x7f) || key.unicode_char == '\t' || key.unicode_char == '\b' || key.unicode_char == '\n' || key.unicode_char == '\r') return key.unicode_char; return VAS_EBOOT_TERM_NO_KEY; } static int VasEBoot_console_getkey_con (struct VasEBoot_term_input *term __attribute__ ((unused))) { VasEBoot_efi_simple_input_interface_t *i; VasEBoot_efi_input_key_t key; VasEBoot_efi_status_t status; i = VasEBoot_efi_system_table->con_in; status = i->read_key_stroke (i, &key); if (status != VAS_EBOOT_EFI_SUCCESS) return VAS_EBOOT_TERM_NO_KEY; return VasEBoot_efi_translate_key(key); } /* * When more then just modifiers are pressed, our getkeystatus() consumes a * press from the queue, this function buffers the press for the regular * getkey() so that it does not get lost. */ static VasEBoot_err_t VasEBoot_console_read_key_stroke ( VasEBoot_efi_simple_text_input_ex_interface_t *text_input, VasEBoot_efi_key_data_t *key_data_ret, int *key_ret, int consume) { static VasEBoot_efi_key_data_t key_data; VasEBoot_efi_status_t status; int key; if (!text_input) return VAS_EBOOT_ERR_EOF; key = VasEBoot_efi_translate_key (key_data.key); if (key == VAS_EBOOT_TERM_NO_KEY) { status = text_input->read_key_stroke (text_input, &key_data); if (status != VAS_EBOOT_EFI_SUCCESS) return VAS_EBOOT_ERR_EOF; key = VasEBoot_efi_translate_key (key_data.key); } *key_data_ret = key_data; *key_ret = key; if (consume) { key_data.key.scan_code = 0; key_data.key.unicode_char = 0; } return VAS_EBOOT_ERR_NONE; } static int VasEBoot_console_getkey_ex (struct VasEBoot_term_input *term) { VasEBoot_efi_key_data_t key_data; VasEBoot_efi_uint32_t kss; VasEBoot_err_t err; int key = -1; err = VasEBoot_console_read_key_stroke (term->data, &key_data, &key, 1); if (err != VAS_EBOOT_ERR_NONE || key == VAS_EBOOT_TERM_NO_KEY) return VAS_EBOOT_TERM_NO_KEY; kss = key_data.key_state.key_shift_state; if (kss & VAS_EBOOT_EFI_SHIFT_STATE_VALID) { if ((kss & VAS_EBOOT_EFI_LEFT_SHIFT_PRESSED || kss & VAS_EBOOT_EFI_RIGHT_SHIFT_PRESSED) && (key & VAS_EBOOT_TERM_EXTENDED)) key |= VAS_EBOOT_TERM_SHIFT; if (kss & VAS_EBOOT_EFI_LEFT_ALT_PRESSED || kss & VAS_EBOOT_EFI_RIGHT_ALT_PRESSED) key |= VAS_EBOOT_TERM_ALT; if (kss & VAS_EBOOT_EFI_LEFT_CONTROL_PRESSED || kss & VAS_EBOOT_EFI_RIGHT_CONTROL_PRESSED) key |= VAS_EBOOT_TERM_CTRL; } return key; } static int VasEBoot_console_getkeystatus (struct VasEBoot_term_input *term) { VasEBoot_efi_key_data_t key_data; VasEBoot_efi_uint32_t kss; int key, mods = 0; if (VasEBoot_efi_is_finished) return 0; if (VasEBoot_console_read_key_stroke (term->data, &key_data, &key, 0)) return 0; kss = key_data.key_state.key_shift_state; if (kss & VAS_EBOOT_EFI_SHIFT_STATE_VALID) { if (kss & VAS_EBOOT_EFI_LEFT_SHIFT_PRESSED) mods |= VAS_EBOOT_TERM_STATUS_LSHIFT; if (kss & VAS_EBOOT_EFI_RIGHT_SHIFT_PRESSED) mods |= VAS_EBOOT_TERM_STATUS_RSHIFT; if (kss & VAS_EBOOT_EFI_LEFT_ALT_PRESSED) mods |= VAS_EBOOT_TERM_STATUS_LALT; if (kss & VAS_EBOOT_EFI_RIGHT_ALT_PRESSED) mods |= VAS_EBOOT_TERM_STATUS_RALT; if (kss & VAS_EBOOT_EFI_LEFT_CONTROL_PRESSED) mods |= VAS_EBOOT_TERM_STATUS_LCTRL; if (kss & VAS_EBOOT_EFI_RIGHT_CONTROL_PRESSED) mods |= VAS_EBOOT_TERM_STATUS_RCTRL; } return mods; } static VasEBoot_err_t VasEBoot_efi_console_input_init (struct VasEBoot_term_input *term) { static VasEBoot_guid_t text_input_ex_guid = VAS_EBOOT_EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID; if (VasEBoot_efi_is_finished) return 0; VasEBoot_efi_simple_text_input_ex_interface_t *text_input = term->data; if (text_input) return 0; text_input = VasEBoot_efi_open_protocol(VasEBoot_efi_system_table->console_in_handler, &text_input_ex_guid, VAS_EBOOT_EFI_OPEN_PROTOCOL_GET_PROTOCOL); term->data = (void *)text_input; return 0; } static int VasEBoot_console_getkey (struct VasEBoot_term_input *term) { if (VasEBoot_efi_is_finished) return 0; if (term->data) return VasEBoot_console_getkey_ex(term); else return VasEBoot_console_getkey_con(term); } static struct VasEBoot_term_coordinate VasEBoot_console_getwh (struct VasEBoot_term_output *term) { VasEBoot_efi_simple_text_output_interface_t *o; VasEBoot_efi_uintn_t columns, rows; o = VasEBoot_efi_system_table->con_out; if (VasEBoot_prepare_for_text_output (term) != VAS_EBOOT_ERR_NONE || o->query_mode (o, o->mode->mode, &columns, &rows) != VAS_EBOOT_EFI_SUCCESS) { /* Why does this fail? */ columns = 80; rows = 25; } return (struct VasEBoot_term_coordinate) { columns, rows }; } static struct VasEBoot_term_coordinate VasEBoot_console_getxy (struct VasEBoot_term_output *term __attribute__ ((unused))) { VasEBoot_efi_simple_text_output_interface_t *o; if (VasEBoot_efi_is_finished || text_mode != VAS_EBOOT_TEXT_MODE_AVAILABLE) return (struct VasEBoot_term_coordinate) { 0, 0 }; o = VasEBoot_efi_system_table->con_out; return (struct VasEBoot_term_coordinate) { o->mode->cursor_column, o->mode->cursor_row }; } static void VasEBoot_console_gotoxy (struct VasEBoot_term_output *term, struct VasEBoot_term_coordinate pos) { VasEBoot_efi_simple_text_output_interface_t *o; if (VasEBoot_prepare_for_text_output (term) != VAS_EBOOT_ERR_NONE) return; o = VasEBoot_efi_system_table->con_out; o->set_cursor_position (o, pos.x, pos.y); } static void VasEBoot_console_cls (struct VasEBoot_term_output *term __attribute__ ((unused))) { VasEBoot_efi_simple_text_output_interface_t *o; VasEBoot_efi_int32_t orig_attr; if (VasEBoot_prepare_for_text_output (term) != VAS_EBOOT_ERR_NONE) return; o = VasEBoot_efi_system_table->con_out; orig_attr = o->mode->attribute; o->set_attributes (o, VAS_EBOOT_EFI_BACKGROUND_BLACK); o->clear_screen (o); o->set_attributes (o, orig_attr); } static VasEBoot_err_t VasEBoot_efi_console_output_fini (struct VasEBoot_term_output *term) { if (text_mode != VAS_EBOOT_TEXT_MODE_AVAILABLE) return 0; VasEBoot_console_setcursor (term, 0); VasEBoot_efi_set_text_mode (0); text_mode = VAS_EBOOT_TEXT_MODE_UNDEFINED; return 0; } static struct VasEBoot_term_input VasEBoot_console_term_input = { .name = "console", .getkey = VasEBoot_console_getkey, .getkeystatus = VasEBoot_console_getkeystatus, .init = VasEBoot_efi_console_input_init, }; static struct VasEBoot_term_output VasEBoot_console_term_output = { .name = "console", .fini = VasEBoot_efi_console_output_fini, .putchar = VasEBoot_console_putchar, .getwh = VasEBoot_console_getwh, .getxy = VasEBoot_console_getxy, .gotoxy = VasEBoot_console_gotoxy, .cls = VasEBoot_console_cls, .setcolorstate = VasEBoot_console_setcolorstate, .setcursor = VasEBoot_console_setcursor, .flags = VAS_EBOOT_TERM_CODE_TYPE_VISUAL_GLYPHS, .progress_update_divisor = VAS_EBOOT_PROGRESS_FAST }; void VasEBoot_console_init (void) { VasEBoot_term_register_output ("console", &VasEBoot_console_term_output); VasEBoot_term_register_input ("console", &VasEBoot_console_term_input); } void VasEBoot_console_fini (void) { VasEBoot_term_unregister_input (&VasEBoot_console_term_input); VasEBoot_term_unregister_output (&VasEBoot_console_term_output); }