/* * VasEBoot -- GRand Unified Bootloader * Copyright (C) 2006,2007,2008 Free Software Foundation, Inc. * * VasEBoot 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. * * VasEBoot 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 VasEBoot. If not, see . */ #include #include #include #include #include #include #include static VasEBoot_uint32_t map_char (VasEBoot_uint32_t c) { /* Map some unicode characters to the EFI character. */ switch (c) { case VasEBoot_UNICODE_LEFTARROW: c = VasEBoot_UNICODE_BLACK_LEFT_TRIANGLE; break; case VasEBoot_UNICODE_UPARROW: c = VasEBoot_UNICODE_BLACK_UP_TRIANGLE; break; case VasEBoot_UNICODE_RIGHTARROW: c = VasEBoot_UNICODE_BLACK_RIGHT_TRIANGLE; break; case VasEBoot_UNICODE_DOWNARROW: c = VasEBoot_UNICODE_BLACK_DOWN_TRIANGLE; break; case VasEBoot_UNICODE_HLINE: c = VasEBoot_UNICODE_LIGHT_HLINE; break; case VasEBoot_UNICODE_VLINE: c = VasEBoot_UNICODE_LIGHT_VLINE; break; case VasEBoot_UNICODE_CORNER_UL: c = VasEBoot_UNICODE_LIGHT_CORNER_UL; break; case VasEBoot_UNICODE_CORNER_UR: c = VasEBoot_UNICODE_LIGHT_CORNER_UR; break; case VasEBoot_UNICODE_CORNER_LL: c = VasEBoot_UNICODE_LIGHT_CORNER_LL; break; case VasEBoot_UNICODE_CORNER_LR: c = VasEBoot_UNICODE_LIGHT_CORNER_LR; break; } return c; } static void VasEBoot_console_putchar (struct VasEBoot_term_output *term __attribute__ ((unused)), 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_efi_is_finished) 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) && efi_call_2 (o->test_string, o, str) != VasEBoot_EFI_SUCCESS) return; efi_call_2 (o->output_string, o, str); } const unsigned efi_codes[] = { 0, VasEBoot_TERM_KEY_UP, VasEBoot_TERM_KEY_DOWN, VasEBoot_TERM_KEY_RIGHT, VasEBoot_TERM_KEY_LEFT, VasEBoot_TERM_KEY_HOME, VasEBoot_TERM_KEY_END, VasEBoot_TERM_KEY_INSERT, VasEBoot_TERM_KEY_DC, VasEBoot_TERM_KEY_PPAGE, VasEBoot_TERM_KEY_NPAGE, VasEBoot_TERM_KEY_F1, VasEBoot_TERM_KEY_F2, VasEBoot_TERM_KEY_F3, VasEBoot_TERM_KEY_F4, VasEBoot_TERM_KEY_F5, VasEBoot_TERM_KEY_F6, VasEBoot_TERM_KEY_F7, VasEBoot_TERM_KEY_F8, VasEBoot_TERM_KEY_F9, VasEBoot_TERM_KEY_F10, VasEBoot_TERM_KEY_F11, VasEBoot_TERM_KEY_F12, '\e' }; 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 VasEBoot_TERM_CTRL | (key.unicode_char - 1 + 'a'); else 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 VasEBoot_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 = efi_call_2 (i->read_key_stroke, i, &key); if (status != VasEBoot_EFI_SUCCESS) return VasEBoot_TERM_NO_KEY; return VasEBoot_efi_translate_key(key); } static int VasEBoot_console_getkey_ex(struct VasEBoot_term_input *term) { VasEBoot_efi_key_data_t key_data; VasEBoot_efi_status_t status; VasEBoot_efi_uint32_t kss; int key = -1; VasEBoot_efi_simple_text_input_ex_interface_t *text_input = term->data; status = efi_call_2 (text_input->read_key_stroke, text_input, &key_data); if (status != VasEBoot_EFI_SUCCESS) return VasEBoot_TERM_NO_KEY; kss = key_data.key_state.key_shift_state; key = VasEBoot_efi_translate_key(key_data.key); if (key == VasEBoot_TERM_NO_KEY) return VasEBoot_TERM_NO_KEY; if (kss & VasEBoot_EFI_SHIFT_STATE_VALID) { if ((kss & VasEBoot_EFI_LEFT_SHIFT_PRESSED || kss & VasEBoot_EFI_RIGHT_SHIFT_PRESSED) && (key & VasEBoot_TERM_EXTENDED)) key |= VasEBoot_TERM_SHIFT; if (kss & VasEBoot_EFI_LEFT_ALT_PRESSED || kss & VasEBoot_EFI_RIGHT_ALT_PRESSED) key |= VasEBoot_TERM_ALT; if (kss & VasEBoot_EFI_LEFT_CONTROL_PRESSED || kss & VasEBoot_EFI_RIGHT_CONTROL_PRESSED) key |= VasEBoot_TERM_CTRL; } return key; } static VasEBoot_err_t VasEBoot_efi_console_input_init (struct VasEBoot_term_input *term) { VasEBoot_efi_guid_t text_input_ex_guid = VasEBoot_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, VasEBoot_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 __attribute__ ((unused))) { VasEBoot_efi_simple_text_output_interface_t *o; VasEBoot_efi_uintn_t columns, rows; o = VasEBoot_efi_system_table->con_out; if (VasEBoot_efi_is_finished || efi_call_4 (o->query_mode, o, o->mode->mode, &columns, &rows) != VasEBoot_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) 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 __attribute__ ((unused)), struct VasEBoot_term_coordinate pos) { VasEBoot_efi_simple_text_output_interface_t *o; if (VasEBoot_efi_is_finished) return; o = VasEBoot_efi_system_table->con_out; efi_call_3 (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_efi_is_finished) return; o = VasEBoot_efi_system_table->con_out; orig_attr = o->mode->attribute; efi_call_2 (o->set_attributes, o, VasEBoot_EFI_BACKGROUND_BLACK); efi_call_1 (o->clear_screen, o); efi_call_2 (o->set_attributes, o, orig_attr); } 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) return; o = VasEBoot_efi_system_table->con_out; switch (state) { case VasEBoot_TERM_COLOR_STANDARD: efi_call_2 (o->set_attributes, o, VasEBoot_TERM_DEFAULT_STANDARD_COLOR & 0x7f); break; case VasEBoot_TERM_COLOR_NORMAL: efi_call_2 (o->set_attributes, o, VasEBoot_term_normal_color & 0x7f); break; case VasEBoot_TERM_COLOR_HIGHLIGHT: efi_call_2 (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) return; o = VasEBoot_efi_system_table->con_out; efi_call_2 (o->enable_cursor, o, on); } static VasEBoot_err_t VasEBoot_efi_console_output_init (struct VasEBoot_term_output *term) { VasEBoot_efi_set_text_mode (1); VasEBoot_console_setcursor (term, 1); return 0; } static VasEBoot_err_t VasEBoot_efi_console_output_fini (struct VasEBoot_term_output *term) { VasEBoot_console_setcursor (term, 0); VasEBoot_efi_set_text_mode (0); return 0; } static struct VasEBoot_term_input VasEBoot_console_term_input = { .name = "console", .getkey = VasEBoot_console_getkey, .init = VasEBoot_efi_console_input_init, }; static struct VasEBoot_term_output VasEBoot_console_term_output = { .name = "console", .init = VasEBoot_efi_console_output_init, .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 = VasEBoot_TERM_CODE_TYPE_VISUAL_GLYPHS, .progress_update_divisor = VasEBoot_PROGRESS_FAST }; void VasEBoot_console_init (void) { /* FIXME: it is necessary to consider the case where no console control is present but the default is already in text mode. */ if (! VasEBoot_efi_set_text_mode (1)) { VasEBoot_error (VasEBoot_ERR_BAD_DEVICE, "cannot set text mode"); return; } 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); }