diff options
-rw-r--r-- | src/arch/x86/interface/pcbios/vesafb.c | 233 | ||||
-rw-r--r-- | src/config/config_efi.c | 8 | ||||
-rw-r--r-- | src/config/general.h | 1 | ||||
-rw-r--r-- | src/core/fbcon.c | 72 | ||||
-rw-r--r-- | src/core/menu.c | 13 | ||||
-rw-r--r-- | src/core/settings.c | 47 | ||||
-rw-r--r-- | src/hci/commands/console_cmd.c | 2 | ||||
-rw-r--r-- | src/hci/commands/efimap_cmd.c | 77 | ||||
-rw-r--r-- | src/hci/commands/login_cmd.c | 13 | ||||
-rw-r--r-- | src/hci/commands/menu_cmd.c | 13 | ||||
-rw-r--r-- | src/hci/tui/login_ui.c | 22 | ||||
-rw-r--r-- | src/hci/tui/menu_ui.c | 13 | ||||
-rw-r--r-- | src/include/ipxe/console.h | 2 | ||||
-rw-r--r-- | src/include/ipxe/login_ui.h | 2 | ||||
-rw-r--r-- | src/include/ipxe/menu.h | 4 | ||||
-rw-r--r-- | src/include/ipxe/settings.h | 1 | ||||
-rw-r--r-- | src/include/usr/efiboot.h | 15 | ||||
-rw-r--r-- | src/interface/efi/efi_block.c | 2 | ||||
-rw-r--r-- | src/interface/efi/efi_boot.c | 475 | ||||
-rw-r--r-- | src/interface/efi/efi_fbcon.c | 157 | ||||
-rw-r--r-- | src/interface/efi/efi_local.c | 89 |
21 files changed, 1103 insertions, 158 deletions
diff --git a/src/arch/x86/interface/pcbios/vesafb.c b/src/arch/x86/interface/pcbios/vesafb.c index 86edbda4..3ca15271 100644 --- a/src/arch/x86/interface/pcbios/vesafb.c +++ b/src/arch/x86/interface/pcbios/vesafb.c @@ -151,8 +151,140 @@ static void vesafb_glyph ( unsigned int character, uint8_t *glyph ) { /* ASCII character: use corresponding glyph */ index = character; } else { - /* Non-ASCII character: use "unknown" glyph */ - index = VESAFB_UNKNOWN; + switch ( character ) { + /* above ASCII */ + case 0xc7: index = 0x80; break; + case 0xfc: index = 0x81; break; + case 0xe9: index = 0x82; break; + case 0xe2: index = 0x83; break; + case 0xe4: index = 0x84; break; + case 0xe0: index = 0x85; break; + case 0xe5: index = 0x86; break; + case 0xe7: index = 0x87; break; + case 0xea: index = 0x88; break; + case 0xeb: index = 0x89; break; + case 0xe8: index = 0x8a; break; + case 0xef: index = 0x8b; break; + case 0xee: index = 0x8c; break; + case 0xec: index = 0x8d; break; + case 0xc4: index = 0x8e; break; + case 0xc5: index = 0x8f; break; + case 0xc9: index = 0x90; break; + case 0xe6: index = 0x91; break; + case 0xc6: index = 0x92; break; + case 0xf4: index = 0x93; break; + case 0xf6: index = 0x94; break; + case 0xf2: index = 0x95; break; + case 0xfb: index = 0x96; break; + case 0xf9: index = 0x97; break; + case 0xff: index = 0x98; break; + case 0xd6: index = 0x99; break; + case 0xdc: index = 0x9a; break; + case 0xa2: index = 0x9b; break; + case 0xa3: index = 0x9c; break; + case 0xa5: index = 0x9d; break; + case 0x20a7: index = 0x9e; break; + case 0x192: index = 0x9f; break; + case 0xe1: index = 0xa0; break; + case 0xed: index = 0xa1; break; + case 0xf3: index = 0xa2; break; + case 0xfa: index = 0xa3; break; + case 0xf1: index = 0xa4; break; + case 0xd1: index = 0xa5; break; + case 0xaa: index = 0xa6; break; + case 0xba: index = 0xa7; break; + case 0xbf: index = 0xa8; break; + case 0x2310: index = 0xa9; break; + case 0xac: index = 0xaa; break; + case 0xbd: index = 0xab; break; + case 0xbc: index = 0xac; break; + case 0xa1: index = 0xad; break; + case 0xab: index = 0xae; break; + case 0xbb: index = 0xaf; break; + case 0x2591: index = 0xb0; break; + case 0x2592: index = 0xb1; break; + case 0x2593: index = 0xb2; break; + case 0x2502: index = 0xb3; break; + case 0x2524: index = 0xb4; break; + case 0x2561: index = 0xb5; break; + case 0x2562: index = 0xb6; break; + case 0x2556: index = 0xb7; break; + case 0x2555: index = 0xb8; break; + case 0x2563: index = 0xb9; break; + case 0x2551: index = 0xba; break; + case 0x2557: index = 0xbb; break; + case 0x255d: index = 0xbc; break; + case 0x255c: index = 0xbd; break; + case 0x255b: index = 0xbe; break; + case 0x2510: index = 0xbf; break; + case 0x2514: index = 0xc0; break; + case 0x2534: index = 0xc1; break; + case 0x252c: index = 0xc2; break; + case 0x251c: index = 0xc3; break; + case 0x2500: index = 0xc4; break; + case 0x253c: index = 0xc5; break; + case 0x255e: index = 0xc6; break; + case 0x255f: index = 0xc7; break; + case 0x255a: index = 0xc8; break; + case 0x2554: index = 0xc9; break; + case 0x2569: index = 0xca; break; + case 0x2566: index = 0xcb; break; + case 0x2560: index = 0xcc; break; + case 0x2550: index = 0xcd; break; + case 0x256c: index = 0xce; break; + case 0x2567: index = 0xcf; break; + case 0x2568: index = 0xd0; break; + case 0x2564: index = 0xd1; break; + case 0x2565: index = 0xd2; break; + case 0x2559: index = 0xd3; break; + case 0x2558: index = 0xd4; break; + case 0x2552: index = 0xd5; break; + case 0x2553: index = 0xd6; break; + case 0x256b: index = 0xd7; break; + case 0x256a: index = 0xd8; break; + case 0x2518: index = 0xd9; break; + case 0x250c: index = 0xda; break; + case 0x2588: index = 0xdb; break; + case 0x2584: index = 0xdc; break; + case 0x258c: index = 0xdd; break; + case 0x2590: index = 0xde; break; + case 0x2580: index = 0xdf; break; + case 0x3b1: index = 0xe0; break; + case 0xdf: index = 0xe1; break; + case 0x393: index = 0xe2; break; + case 0x3c0: index = 0xe3; break; + case 0x3a3: index = 0xe4; break; + case 0x3c3: index = 0xe5; break; + case 0xb5: index = 0xe6; break; + case 0x3c4: index = 0xe7; break; + case 0x3a6: index = 0xe8; break; + case 0x398: index = 0xe9; break; + case 0x3a9: index = 0xea; break; + case 0x3b4: index = 0xeb; break; + case 0x221e: index = 0xec; break; + case 0x3c6: index = 0xed; break; + case 0x3b5: index = 0xee; break; + case 0x2229: index = 0xef; break; + case 0x2261: index = 0xf0; break; + case 0xb1: index = 0xf1; break; + case 0x2265: index = 0xf2; break; + case 0x2264: index = 0xf3; break; + case 0x2320: index = 0xf4; break; + case 0x2321: index = 0xf5; break; + case 0xf7: index = 0xf6; break; + case 0x2248: index = 0xf7; break; + case 0xb0: index = 0xf8; break; + case 0x2219: index = 0xf9; break; + case 0xb7: index = 0xfa; break; + case 0x221a: index = 0xfb; break; + case 0x207f: index = 0xfc; break; + case 0xb2: index = 0xfd; break; + case 0x25a0: index = 0xfe; break; + case 0xa0: index = 0xff; break; + default: + /* Non-CP437 character: use "unknown" glyph */ + index = VESAFB_UNKNOWN; + } } /* Copy glyph from BIOS font table */ @@ -443,34 +575,36 @@ static void vesafb_restore ( void ) { */ static int vesafb_init ( struct console_configuration *config ) { uint32_t discard_b; - uint16_t *mode_numbers; + uint16_t *mode_numbers = NULL; int mode_number; int rc; - /* Record current VGA mode */ - __asm__ __volatile__ ( REAL_CODE ( "int $0x10" ) - : "=a" ( vesafb.saved_mode ), "=b" ( discard_b ) - : "a" ( VBE_GET_VGA_MODE ) ); - DBGC ( &vbe_buf, "VESAFB saved VGA mode %#02x\n", vesafb.saved_mode ); - - /* Get VESA mode list */ - if ( ( rc = vesafb_mode_list ( &mode_numbers ) ) != 0 ) - goto err_mode_list; - - /* Select mode */ - if ( ( mode_number = vesafb_select_mode ( mode_numbers, config->width, - config->height, - config->depth ) ) < 0 ) { - rc = mode_number; - goto err_select_mode; - } + if ( ! config->lazy_update ) { + /* Record current VGA mode */ + __asm__ __volatile__ ( REAL_CODE ( "int $0x10" ) + : "=a" ( vesafb.saved_mode ), "=b" ( discard_b ) + : "a" ( VBE_GET_VGA_MODE ) ); + DBGC ( &vbe_buf, "VESAFB saved VGA mode %#02x\n", vesafb.saved_mode ); + + /* Get VESA mode list */ + if ( ( rc = vesafb_mode_list ( &mode_numbers ) ) != 0 ) + goto err_mode_list; + + /* Select mode */ + if ( ( mode_number = vesafb_select_mode ( mode_numbers, config->width, + config->height, + config->depth ) ) < 0 ) { + rc = mode_number; + goto err_select_mode; + } - /* Set mode */ - if ( ( rc = vesafb_set_mode ( mode_number ) ) != 0 ) - goto err_set_mode; + /* Set mode */ + if ( ( rc = vesafb_set_mode ( mode_number ) ) != 0 ) + goto err_set_mode; - /* Get font data */ - vesafb_font(); + /* Get font data */ + vesafb_font(); + } /* Initialise frame buffer console */ if ( ( rc = fbcon_init ( &vesafb.fbcon, phys_to_user ( vesafb.start ), @@ -523,17 +657,38 @@ static void vesafb_putchar ( int character ) { static int vesafb_configure ( struct console_configuration *config ) { int rc; - /* Reset console, if applicable */ - if ( ! vesafb_console.disabled ) { - vesafb_fini(); - bios_console.disabled &= ~CONSOLE_DISABLED_OUTPUT; - ansicol_reset_magic(); + if ( config && config->lazy_update ) { + if ( vesafb_console.disabled ) { + /* --update mode with disabled console -> nothing to do */ + return 0; + } + /* No width/height given, use current so we can update the border */ + if ( ( config->width == 0 ) || ( config->height == 0 ) ) { + if ( ( vesafb.pixel.width == 0 ) || ( vesafb.pixel.height == 0 ) ) { + return -EINVAL; + } + config->width = vesafb.pixel.width; + config->height = vesafb.pixel.height; + } else { + /* Otherwise make sure the new dimensions match the old ones */ + if ( ( vesafb.pixel.width != config->width ) || + ( vesafb.pixel.height != config->height ) ) { + return -EINVAL; + } + } + } else { + /* Reset console, if applicable */ + if ( ! vesafb_console.disabled ) { + vesafb_fini(); + bios_console.disabled &= ~CONSOLE_DISABLED_OUTPUT; + ansicol_reset_magic(); + } + vesafb_console.disabled = CONSOLE_DISABLED; } - vesafb_console.disabled = CONSOLE_DISABLED; /* Do nothing more unless we have a usable configuration */ if ( ( config == NULL ) || - ( config->width == 0 ) || ( config->height == 0 ) ) { + ( config->width == 0 ) || ( config->height == 0 ) ) { return 0; } @@ -541,13 +696,15 @@ static int vesafb_configure ( struct console_configuration *config ) { if ( ( rc = vesafb_init ( config ) ) != 0 ) return rc; - /* Mark console as enabled */ - vesafb_console.disabled = 0; - bios_console.disabled |= CONSOLE_DISABLED_OUTPUT; + if ( ! config->lazy_update ) { + /* Mark console as enabled */ + vesafb_console.disabled = 0; + bios_console.disabled |= CONSOLE_DISABLED_OUTPUT; - /* Set magic colour to transparent if we have a background picture */ - if ( config->pixbuf ) - ansicol_set_magic_transparent(); + /* Set magic colour to transparent if we have a background picture */ + if ( config->pixbuf ) + ansicol_set_magic_transparent(); + } return 0; } diff --git a/src/config/config_efi.c b/src/config/config_efi.c index 92678d12..29bd14cb 100644 --- a/src/config/config_efi.c +++ b/src/config/config_efi.c @@ -49,3 +49,11 @@ REQUIRE_OBJECT ( efi_fbcon ); #ifdef DOWNLOAD_PROTO_FILE REQUIRE_OBJECT ( efi_local ); #endif + +/* + * Drag in EFI-specific commands + * + */ +#ifdef EFIMAP_CMD +REQUIRE_OBJECT ( efimap_cmd ); +#endif diff --git a/src/config/general.h b/src/config/general.h index c9cdb3dd..bcf7e69c 100644 --- a/src/config/general.h +++ b/src/config/general.h @@ -167,6 +167,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); //#define CERT_CMD /* Certificate management commands */ //#define IMAGE_MEM_CMD /* Read memory command */ #define IMAGE_ARCHIVE_CMD /* Archive image management commands */ +#define EFIMAP_CMD /* EFI Map command */ #define SHIM_CMD /* EFI shim command (or dummy command) */ /* diff --git a/src/core/fbcon.c b/src/core/fbcon.c index ff3132ac..056b164c 100644 --- a/src/core/fbcon.c +++ b/src/core/fbcon.c @@ -525,6 +525,9 @@ static int fbcon_picture_init ( struct fbcon *fbcon, /* Allocate buffer */ len = ( pixel->height * pixel->stride ); + if ( picture->start ) { + ufree( picture->start ); + } picture->start = umalloc ( len ); if ( ! picture->start ) { DBGC ( fbcon, "FBCON %p could not allocate %zd bytes for " @@ -600,21 +603,23 @@ int fbcon_init ( struct fbcon *fbcon, userptr_t start, unsigned int bottom; int rc; - /* Initialise data structure */ - memset ( fbcon, 0, sizeof ( *fbcon ) ); - fbcon->start = start; - fbcon->pixel = pixel; - assert ( pixel->len <= sizeof ( uint32_t ) ); - fbcon->map = map; - fbcon->font = font; - fbcon->ctx.handlers = fbcon_ansiesc_handlers; - fbcon->show_cursor = 1; - - /* Derive overall length */ - fbcon->len = ( pixel->height * pixel->stride ); - DBGC ( fbcon, "FBCON %p at [%08lx,%08lx)\n", fbcon, - user_to_phys ( fbcon->start, 0 ), - user_to_phys ( fbcon->start, fbcon->len ) ); + if ( ! config->lazy_update ) { + /* Initialise data structure */ + memset ( fbcon, 0, sizeof ( *fbcon ) ); + fbcon->start = start; + fbcon->pixel = pixel; + assert ( pixel->len <= sizeof ( uint32_t ) ); + fbcon->map = map; + fbcon->font = font; + fbcon->ctx.handlers = fbcon_ansiesc_handlers; + fbcon->show_cursor = 1; + + /* Derive overall length */ + fbcon->len = ( pixel->height * pixel->stride ); + DBGC ( fbcon, "FBCON %p at [%08lx,%08lx)\n", fbcon, + user_to_phys ( fbcon->start, 0 ), + user_to_phys ( fbcon->start, fbcon->len ) ); + } /* Calculate margin. If the actual screen size is larger than * the requested screen size, then update the margins so that @@ -669,27 +674,32 @@ int fbcon_init ( struct fbcon *fbcon, userptr_t start, fbcon->margin.top, ( fbcon->pixel->height - fbcon->margin.bottom ) ); - /* Set default colours */ - fbcon_set_default_foreground ( fbcon ); - fbcon_set_default_background ( fbcon ); + if ( ! config->lazy_update ) { + /* Set default colours */ + fbcon_set_default_foreground ( fbcon ); + fbcon_set_default_background ( fbcon ); + + /* Allocate and initialise stored character array */ + fbcon->text.start = umalloc ( fbcon->character.width * + fbcon->character.height * + sizeof ( struct fbcon_text_cell ) ); + if ( ! fbcon->text.start ) { + rc = -ENOMEM; + goto err_text; + } + fbcon_clear ( fbcon, 0 ); - /* Allocate and initialise stored character array */ - fbcon->text.start = umalloc ( fbcon->character.width * - fbcon->character.height * - sizeof ( struct fbcon_text_cell ) ); - if ( ! fbcon->text.start ) { - rc = -ENOMEM; - goto err_text; + /* Set framebuffer to all black (including margins) */ + memset_user ( fbcon->start, 0, 0, fbcon->len ); + } else { + fbcon_clear ( fbcon, 0 ); } - fbcon_clear ( fbcon, 0 ); - - /* Set framebuffer to all black (including margins) */ - memset_user ( fbcon->start, 0, 0, fbcon->len ); /* Generate pixel buffer from background image, if applicable */ if ( config->pixbuf && - ( ( rc = fbcon_picture_init ( fbcon, config->pixbuf ) ) != 0 ) ) - goto err_picture; + ( ( rc = fbcon_picture_init ( fbcon, config->pixbuf ) ) != 0 ) && + ( ! config->lazy_update ) ) + goto err_picture; /* Keep going w/o background in lazy_update mode */ /* Draw background picture (including margins), if applicable */ if ( fbcon->picture.start ) { diff --git a/src/core/menu.c b/src/core/menu.c index ab5b0c7f..abad5999 100644 --- a/src/core/menu.c +++ b/src/core/menu.c @@ -80,6 +80,7 @@ struct menu * create_menu ( const char *name, const char *title ) { strcpy ( title_copy, title ); menu->title = title_copy; INIT_LIST_HEAD ( &menu->items ); + INIT_LIST_HEAD ( &menu->hidden_items ); /* Add to list of menus */ list_add_tail ( &menu->list, &menus ); @@ -102,7 +103,7 @@ struct menu * create_menu ( const char *name, const char *title ) { */ struct menu_item * add_menu_item ( struct menu *menu, const char *label, const char *text, int shortcut, - int is_default ) { + int is_default, int is_hidden ) { size_t label_len; size_t text_len; size_t len; @@ -135,7 +136,11 @@ struct menu_item * add_menu_item ( struct menu *menu, const char *label, item->is_default = is_default; /* Add to list of items */ - list_add_tail ( &item->list, &menu->items ); + if ( is_hidden ) { + list_add_tail ( &item->list, &menu->hidden_items ); + } else { + list_add_tail ( &item->list, &menu->items ); + } return item; } @@ -157,6 +162,10 @@ void destroy_menu ( struct menu *menu ) { list_del ( &item->list ); free ( item ); } + list_for_each_entry_safe ( item, tmp, &menu->hidden_items, list ) { + list_del ( &item->list ); + free ( item ); + } /* Free menu */ free ( menu ); diff --git a/src/core/settings.c b/src/core/settings.c index 4593876f..bece1afe 100644 --- a/src/core/settings.c +++ b/src/core/settings.c @@ -45,6 +45,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/init.h> #include <ipxe/version.h> #include <ipxe/settings.h> +#include <ipxe/md5.h> /** @file * @@ -2133,6 +2134,46 @@ static int format_hex_raw_setting ( const struct setting_type *type __unused, return hex_encode ( 0, raw, raw_len, buf, len ); } +/** + * Parsing md5 setting doesn't make any sense + * + * @v type Setting type + * @v value Formatted setting value + * @v buf Buffer to contain raw value + * @v len Length of buffer + * @v size Integer size, in bytes + * @ret len Length of raw value, or negative error + */ +static int parse_md5_setting ( const struct setting_type *type __unused, + const char *value __unused, void *buf __unused, + size_t len __unused ) { + return -ENOTSUP; +} + +/** + * Format setting value as md5 hash (hex representation) + * + * @v type Setting type + * @v raw Raw setting value + * @v raw_len Length of raw setting value + * @v buf Buffer to contain formatted value + * @v len Length of buffer + * @ret len Length of formatted value, or negative error + */ +static int format_md5_setting ( const struct setting_type *type __unused, + const void *raw, size_t raw_len, + char *buf, size_t len ) { + struct md5_context ctx; + uint8_t digest[MD5_DIGEST_SIZE]; + + if ( len < MD5_DIGEST_SIZE * 2 ) + return MD5_DIGEST_SIZE * 2; + digest_init ( &md5_algorithm, &ctx ); + digest_update ( &md5_algorithm, &ctx, raw, raw_len ); + digest_final ( &md5_algorithm, &ctx, digest ); + return hex_encode ( 0, digest, sizeof(digest), buf, len ); +} + /** A hex-string setting (colon-delimited) */ const struct setting_type setting_type_hex __setting_type = { .name = "hex", @@ -2154,6 +2195,12 @@ const struct setting_type setting_type_hexraw __setting_type = { .format = format_hex_raw_setting, }; +const struct setting_type setting_type_md5 __setting_type = { + .name = "md5", + .parse = parse_md5_setting, + .format = format_md5_setting, +}; + /** * Parse Base64-encoded setting value * diff --git a/src/hci/commands/console_cmd.c b/src/hci/commands/console_cmd.c index ba472b9f..bd564726 100644 --- a/src/hci/commands/console_cmd.c +++ b/src/hci/commands/console_cmd.c @@ -70,6 +70,8 @@ static struct option_descriptor console_opts[] = { struct console_options, picture, parse_string ), OPTION_DESC ( "keep", 'k', no_argument, struct console_options, keep, parse_flag ), + OPTION_DESC ( "update", 'u', no_argument, + struct console_options, config.lazy_update, parse_flag ), }; /** "console" command descriptor */ diff --git a/src/hci/commands/efimap_cmd.c b/src/hci/commands/efimap_cmd.c new file mode 100644 index 00000000..c183cbc3 --- /dev/null +++ b/src/hci/commands/efimap_cmd.c @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2019 Oracle. All rights reserved. + * + * This program 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 2 of the + * License, or any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <stdio.h> +#include <errno.h> +#include <getopt.h> +#include <string.h> +#include <ipxe/command.h> +#include <ipxe/parseopt.h> +#include <usr/efiboot.h> + +/** @file + * + * EFIMAP command + * + */ + +/** "efimap" options */ +struct efimap_options {}; + +/** "efimap" option list */ +static struct option_descriptor efimap_opts[] = {}; + +/** "efimap" command descriptor */ +static struct command_descriptor efimap_cmd = + COMMAND_DESC ( struct efimap_options, efimap_opts, 0, 0, NULL); + +/** + * The "efimap" command + * + * @v argc Argument count + * @v argv Argument list + * @ret rc Return status code + */ +static int efimap_exec ( int argc, char **argv ) { + struct efimap_options opts; + int rc; + + /* Parse options */ + if ( ( rc = parse_options ( argc, argv, &efimap_cmd, &opts ) ) != 0 ) + return rc; + + efi_boot_display_map (); + + return 0; +} + +/** EFIMAP command */ +struct command efimap_commands[] __command = { + { + .name = "efimap", + .exec = efimap_exec, + }, +}; diff --git a/src/hci/commands/login_cmd.c b/src/hci/commands/login_cmd.c index c9e19643..6ed76c6f 100644 --- a/src/hci/commands/login_cmd.c +++ b/src/hci/commands/login_cmd.c @@ -23,6 +23,7 @@ #include <string.h> #include <stdio.h> +#include <getopt.h> #include <ipxe/command.h> #include <ipxe/parseopt.h> #include <ipxe/login_ui.h> @@ -36,10 +37,16 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ /** "login" options */ -struct login_options {}; +struct login_options { + /** No Username */ + int nouser; +}; /** "login" option list */ -static struct option_descriptor login_opts[] = {}; +static struct option_descriptor login_opts[] = { + OPTION_DESC ( "nouser", 'n', no_argument, + struct login_options, nouser, parse_flag ), +}; /** "login" command descriptor */ static struct command_descriptor login_cmd = @@ -61,7 +68,7 @@ static int login_exec ( int argc, char **argv ) { return rc; /* Show login UI */ - if ( ( rc = login_ui() ) != 0 ) { + if ( ( rc = login_ui( opts.nouser ) ) != 0 ) { printf ( "Could not set credentials: %s\n", strerror ( rc ) ); return rc; diff --git a/src/hci/commands/menu_cmd.c b/src/hci/commands/menu_cmd.c index 76bce869..c8692f1a 100644 --- a/src/hci/commands/menu_cmd.c +++ b/src/hci/commands/menu_cmd.c @@ -118,6 +118,8 @@ struct item_options { int is_default; /** Use as a separator */ int is_gap; + /** Don't show; hotkey only */ + int is_hidden; }; /** "item" option list */ @@ -130,6 +132,8 @@ static struct option_descriptor item_opts[] = { struct item_options, is_default, parse_flag ), OPTION_DESC ( "gap", 'g', no_argument, struct item_options, is_gap, parse_flag ), + OPTION_DESC ( "hidden", 'i', no_argument, + struct item_options, is_hidden, parse_flag ), }; /** "item" command descriptor */ @@ -160,6 +164,12 @@ static int item_exec ( int argc, char **argv ) { if ( ! opts.is_gap ) label = argv[optind++]; /* May be NULL */ + /* Hidden entry without label and shortcut is pointless */ + if ( opts.is_hidden && ( ! opts.key || ! label || ! *label ) ) { + rc = -EINVAL; + goto err_invalid_argument; + } + /* Parse text, if present */ if ( optind < argc ) { text = concat_args ( &argv[optind] ); @@ -175,7 +185,7 @@ static int item_exec ( int argc, char **argv ) { /* Add menu item */ item = add_menu_item ( menu, label, ( text ? text : "" ), - opts.key, opts.is_default ); + opts.key, opts.is_default, opts.is_hidden ); if ( ! item ) { rc = -ENOMEM; goto err_add_menu_item; @@ -188,6 +198,7 @@ static int item_exec ( int argc, char **argv ) { err_parse_menu: free ( text ); err_parse_text: + err_invalid_argument: err_parse_options: return rc; } diff --git a/src/hci/tui/login_ui.c b/src/hci/tui/login_ui.c index 3c55325d..56fc2fa9 100644 --- a/src/hci/tui/login_ui.c +++ b/src/hci/tui/login_ui.c @@ -48,12 +48,12 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define EDITBOX_COL ( ( COLS / 2U ) - 10U ) #define EDITBOX_WIDTH 20U -int login_ui ( void ) { +int login_ui ( int nouser ) { char username[64]; char password[64]; struct edit_box username_box; struct edit_box password_box; - struct edit_box *current_box = &username_box; + struct edit_box *current_box = nouser ? &password_box : &username_box; int key; int rc = -EINPROGRESS; @@ -76,11 +76,15 @@ int login_ui ( void ) { color_set ( CPAIR_NORMAL, NULL ); erase(); attron ( A_BOLD ); - mvprintw ( USERNAME_LABEL_ROW, LABEL_COL, "Username:" ); + if ( ! nouser ) { + mvprintw ( USERNAME_LABEL_ROW, LABEL_COL, "Username:" ); + } mvprintw ( PASSWORD_LABEL_ROW, LABEL_COL, "Password:" ); attroff ( A_BOLD ); color_set ( CPAIR_EDIT, NULL ); - draw_editbox ( &username_box ); + if ( ! nouser ) { + draw_editbox ( &username_box ); + } draw_editbox ( &password_box ); /* Main loop */ @@ -94,11 +98,15 @@ int login_ui ( void ) { current_box = &password_box; break; case KEY_UP: - current_box = &username_box; + if ( ! nouser ) { + current_box = &username_box; + } break; case TAB: - current_box = ( ( current_box == &username_box ) ? - &password_box : &username_box ); + if ( ! nouser ) { + current_box = ( ( current_box == &username_box ) ? + &password_box : &username_box ); + } break; case KEY_ENTER: if ( current_box == &username_box ) { diff --git a/src/hci/tui/menu_ui.c b/src/hci/tui/menu_ui.c index f9dd9d10..cb4edbbc 100644 --- a/src/hci/tui/menu_ui.c +++ b/src/hci/tui/menu_ui.c @@ -217,6 +217,8 @@ static int menu_loop ( struct menu_ui *ui, struct menu_item **selected ) { break; default: i = 0; + + /* Check for shortcut. Visible items */ list_for_each_entry ( item, &ui->menu->items, list ) { if ( ! ( item->shortcut && @@ -231,6 +233,16 @@ static int menu_loop ( struct menu_ui *ui, struct menu_item **selected ) { move = +1; } } + + /* Hidden items */ + list_for_each_entry ( item, &ui->menu->hidden_items, + list ) { + if ( item->shortcut && + ( item->shortcut == key ) ) { + *selected = item; + goto hidden_entry_selected; + } + } break; } } @@ -259,6 +271,7 @@ static int menu_loop ( struct menu_ui *ui, struct menu_item **selected ) { } while ( ( rc == 0 ) && ! chosen ); + hidden_entry_selected: return rc; } diff --git a/src/include/ipxe/console.h b/src/include/ipxe/console.h index 1b764aac..ca8093a7 100644 --- a/src/include/ipxe/console.h +++ b/src/include/ipxe/console.h @@ -38,6 +38,8 @@ struct console_configuration { unsigned int bottom; /** Background picture, if any */ struct pixel_buffer *pixbuf; + /** Update mode: Don't re-init vesa mode, just update margins and bg image */ + int lazy_update; }; /** diff --git a/src/include/ipxe/login_ui.h b/src/include/ipxe/login_ui.h index 313e0734..07b1dd49 100644 --- a/src/include/ipxe/login_ui.h +++ b/src/include/ipxe/login_ui.h @@ -9,6 +9,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); -extern int login_ui ( void ); +extern int login_ui ( int nouser ); #endif /* _IPXE_LOGIN_UI_H */ diff --git a/src/include/ipxe/menu.h b/src/include/ipxe/menu.h index 3cc99be4..33965cfd 100644 --- a/src/include/ipxe/menu.h +++ b/src/include/ipxe/menu.h @@ -21,6 +21,8 @@ struct menu { const char *title; /** Menu items */ struct list_head items; + /** Hidden menu items, accessible via hotkey only */ + struct list_head hidden_items; }; /** A menu item */ @@ -40,7 +42,7 @@ struct menu_item { extern struct menu * create_menu ( const char *name, const char *title ); extern struct menu_item * add_menu_item ( struct menu *menu, const char *label, const char *text, int shortcut, - int is_default ); + int is_default, int is_hidden ); extern void destroy_menu ( struct menu *menu ); extern struct menu * find_menu ( const char *name ); extern int show_menu ( struct menu *menu, unsigned long timeout, diff --git a/src/include/ipxe/settings.h b/src/include/ipxe/settings.h index e042b975..9759a600 100644 --- a/src/include/ipxe/settings.h +++ b/src/include/ipxe/settings.h @@ -424,6 +424,7 @@ extern const struct setting_type setting_type_uint32 __setting_type; extern const struct setting_type setting_type_hex __setting_type; extern const struct setting_type setting_type_hexhyp __setting_type; extern const struct setting_type setting_type_hexraw __setting_type; +extern const struct setting_type setting_type_md5 __setting_type; extern const struct setting_type setting_type_base64 __setting_type; extern const struct setting_type setting_type_uuid __setting_type; extern const struct setting_type setting_type_guid __setting_type; diff --git a/src/include/usr/efiboot.h b/src/include/usr/efiboot.h new file mode 100644 index 00000000..bd3065f0 --- /dev/null +++ b/src/include/usr/efiboot.h @@ -0,0 +1,15 @@ +#ifndef _USR_EFIBOOT_H +#define _USR_EFIBOOT_H + +/** @file + * + * EFI boot support + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +extern void efi_boot_display_map ( void ); +extern int efi_boot_local ( unsigned int drive, const char *filename ); + +#endif /* _USR_EFIBOOT_H */ diff --git a/src/interface/efi/efi_block.c b/src/interface/efi/efi_block.c index 2f0187a0..a9c3d656 100644 --- a/src/interface/efi/efi_block.c +++ b/src/interface/efi/efi_block.c @@ -60,6 +60,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/efi/efi_path.h> #include <ipxe/efi/efi_null.h> #include <ipxe/efi/efi_block.h> +#include <usr/efiboot.h> /** ACPI table protocol protocol */ static EFI_ACPI_TABLE_PROTOCOL *acpi; @@ -990,6 +991,7 @@ static int efi_block_boot ( unsigned int drive, EFI_STATUS efirc; int rc; + /* Release SNP devices */ efi_snp_release(); diff --git a/src/interface/efi/efi_boot.c b/src/interface/efi/efi_boot.c new file mode 100644 index 00000000..19c31886 --- /dev/null +++ b/src/interface/efi/efi_boot.c @@ -0,0 +1,475 @@ +/* + * Copyright (C) 2019 Oracle. All rights reserved. + * + * This program 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 2 of the + * License, or any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** + * @file + * + * EFI boot local protocols + * + */ + +#include <stddef.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <ipxe/refcnt.h> +#include <ipxe/list.h> +#include <ipxe/uri.h> +#include <ipxe/interface.h> +#include <ipxe/blockdev.h> +#include <ipxe/xfer.h> +#include <ipxe/open.h> +#include <ipxe/retry.h> +#include <ipxe/timer.h> +#include <ipxe/process.h> +#include <ipxe/sanboot.h> +#include <ipxe/iso9660.h> +#include <ipxe/acpi.h> +#include <ipxe/efi/efi.h> +#include <ipxe/efi/efi_driver.h> +#include <ipxe/efi/efi_strings.h> +#include <ipxe/efi/efi_snp.h> +#include <ipxe/efi/efi_path.h> +#include <ipxe/efi/efi_block.h> +#include <ipxe/efi/Guid/FileInfo.h> +#include <ipxe/efi/Guid/FileSystemInfo.h> +#include <ipxe/efi/Protocol/BlockIo.h> +#include <ipxe/efi/Protocol/SimpleFileSystem.h> +#include <ipxe/efi/Protocol/AcpiTable.h> + +static wchar_t efi_default_boot_filename[] = EFI_REMOVABLE_MEDIA_FILE_NAME; + +static EFI_DEVICE_PATH_PROTOCOL **DevicePathList; +static UINTN DevicePathListNum; +static EFI_HANDLE *SimpleFSHandleList; +static BOOLEAN efi_boot_map_initialized = FALSE; + +static EFI_HANDLE * efi_boot_get_handlelist ( EFI_GUID *ProtocolGuid ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_HANDLE *HandleList; + UINTN Size; + EFI_STATUS efirc; + EFI_LOCATE_SEARCH_TYPE SearchType; + + /* NULL ProtocolGuid gets all handles in system */ + if ( ProtocolGuid ) + SearchType = ByProtocol; + else + SearchType = AllHandles; + + Size = 0; + HandleList = NULL; + + /* First call gets the handle list size and returns BUFFER_TOO_SMALL. */ + efirc = bs->LocateHandle ( SearchType, (EFI_GUID*) ProtocolGuid, + NULL, &Size, HandleList ); + if ( efirc == EFI_BUFFER_TOO_SMALL ) { + /* Alloc an extra handle for NULL list terminator */ + if ( ( efirc = bs->AllocatePool ( EfiBootServicesData, + ( Size + + sizeof ( EFI_HANDLE ) ), + (void **) &HandleList ) ) + != 0 ) { + return ( NULL ) ; + } + + efirc = bs->LocateHandle ( SearchType, (EFI_GUID*) ProtocolGuid, + NULL, &Size, HandleList ); + if ( HandleList ) + HandleList[Size / sizeof ( EFI_HANDLE )] = NULL; + } + + if ( EFI_ERROR ( efirc ) ) { + if ( HandleList ) + bs->FreePool ( HandleList ); + return ( NULL ) ; + } + + return ( HandleList ); +} + +static EFI_DEVICE_PATH_PROTOCOL * efi_boot_get_devpath ( EFI_HANDLE Handle ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + EFI_STATUS efirc; + + efirc = bs->HandleProtocol ( Handle, &efi_device_path_protocol_guid, + (VOID *) &DevicePath ); + + if ( EFI_ERROR ( efirc ) ) + return NULL; + else + return DevicePath; +} + +static void efi_boot_connect_pcibridges ( void ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_HANDLE *HandleList; + UINTN Count; + + HandleList = efi_boot_get_handlelist ( &efi_pci_root_bridge_io_protocol_guid ); + if ( HandleList == NULL ) { + DBG ( "EFIBOOT efi_boot_connect_pcibridges: no handles!\n" ); + return; + } + + for ( Count = 0 ; HandleList[Count] != NULL ; Count++ ) { + + DBG ( "EFIBOOT efi_boot_connect_pcibridges: connecting " + "handle %s\n", efi_handle_name ( HandleList[Count] ) ); + + (void) bs->ConnectController ( HandleList[Count], NULL, + NULL, 1 ); + + DBG ( "EFIBOOT: handle %s supports protocols:\n", + efi_handle_name ( HandleList[Count] ) ); + DBG_EFI_PROTOCOLS_IF ( LOG, HandleList[Count] ); + } + + bs->FreePool ( HandleList ); + + return; +} + +static int efi_vol_label( EFI_HANDLE handle, char *label_buf, + size_t label_buf_size ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_FILE_SYSTEM_INFO *info; + EFI_FILE_PROTOCOL *root; + EFI_STATUS efirc; + UINTN size; + int rc; + union { + void *interface; + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *fs; + } u; + + /* Open file system protocol */ + if ( ( efirc = bs->OpenProtocol ( handle, + &efi_simple_file_system_protocol_guid, + &u.interface, efi_image_handle, + handle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){ + rc = -1; + DBG ( "Could not open filesystem on %s\n", + efi_handle_name ( handle ) ); + goto err_filesystem; + } + + /* Open root directory */ + if ( ( efirc = u.fs->OpenVolume ( u.fs, &root ) ) != 0 ) { + rc = -1; + DBG ( "Could not open volume on %s\n", + efi_handle_name ( handle ) ); + goto err_volume; + } + + /* Get length of file system information */ + size = 0; + root->GetInfo ( root, &efi_file_system_info_id, &size, NULL ); + + /* Allocate file system information */ + info = malloc ( size ); + if ( ! info ) { + rc = -1; + goto err_alloc_info; + } + + /* Get file system information */ + if ( ( efirc = root->GetInfo ( root, &efi_file_system_info_id, &size, + info ) ) != 0 ) { + rc = -1; + DBG ( "could not get file system info on %s\n", + efi_handle_name ( handle ) ); + goto err_get_info; + } + DBG ( "Found %s with label \"%ls\"\n", + efi_handle_name ( handle ), info->VolumeLabel ); + + snprintf ( label_buf, label_buf_size, "%ls", info->VolumeLabel ); + + /* Success */ + rc = 0; + + err_get_info: + free ( info ); + err_alloc_info: + root->Close ( root ); + err_volume: + bs->CloseProtocol ( handle, &efi_simple_file_system_protocol_guid, + efi_image_handle, handle ); + err_filesystem: + return rc; +} + +static int efi_boot_create_map ( void ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + UINTN Count; + UINTN i,j; + INTN NextIndex; + EFI_STATUS efirc; + EFI_HANDLE *TmpSimpleFSHandleList; + EFI_DEVICE_PATH_PROTOCOL **TmpDevicePathList; + VOID *buffer; + const char *path2; + char path1buf[256]; // 256 is the max buf + // size used internally + // by efi_devpath_text() + + DevicePathList = NULL; + DevicePathListNum = 0; + SimpleFSHandleList = NULL; + + efi_boot_connect_pcibridges (); + + TmpSimpleFSHandleList = efi_boot_get_handlelist ( &efi_simple_file_system_protocol_guid ); + if ( TmpSimpleFSHandleList == NULL ) { + /* valid - no filesystems found */ + efi_boot_map_initialized = TRUE; + return 0; + } + + /* Count number of handles */ + for ( Count = 0 ; TmpSimpleFSHandleList[Count] != NULL ; Count++ ); + + /* Allocate our temporary/local device path list */ + if ( ( efirc = bs->AllocatePool ( EfiBootServicesData, + ( Count * + sizeof ( EFI_DEVICE_PATH_PROTOCOL* ) + ), + &buffer ) ) != 0 ) { + DBG ( "EFIBOOT efi_boot_create_map: AllocatePool failed!\n" ); + bs->FreePool ( TmpSimpleFSHandleList ); + efi_boot_map_initialized = TRUE; + return -1; + } + TmpDevicePathList = (EFI_DEVICE_PATH_PROTOCOL **) buffer; + + /* Populate the devpath list */ + for ( i = 0 ; i < Count; i++ ) { + TmpDevicePathList[i] = efi_boot_get_devpath ( TmpSimpleFSHandleList[i] ); + } + + /* Allocate our global device path list */ + if ( ( efirc = bs->AllocatePool ( EfiBootServicesData, + ( Count * + sizeof ( EFI_DEVICE_PATH_PROTOCOL* ) + ), + &buffer ) ) != 0 ) { + DBG ( "EFIBOOT efi_boot_create_map: AllocatePool failed!\n" ); + bs->FreePool ( TmpSimpleFSHandleList ); + bs->FreePool ( TmpDevicePathList ); + efi_boot_map_initialized = TRUE; + + return -1; + } + DevicePathList = (EFI_DEVICE_PATH_PROTOCOL **) buffer; + + /* Allocate our global SimpleFSHandle list */ + if ( ( efirc = bs->AllocatePool ( EfiBootServicesData, + ( Count * + sizeof ( EFI_HANDLE ) + ), + &buffer ) ) != 0 ) { + DBG ( "EFIBOOT efi_boot_create_map: AllocatePool failed!\n" ); + bs->FreePool ( DevicePathList ); + DevicePathList = NULL; + bs->FreePool ( TmpSimpleFSHandleList ); + bs->FreePool ( TmpDevicePathList ); + efi_boot_map_initialized = TRUE; + + return -1; + } + SimpleFSHandleList = (EFI_HANDLE *) buffer; + + /* + * Populate the global SimpleFSHandle and DevicePath list. + * For consistency, order the list. + * Since each device path begins with PciRoot()/Pci() nodes, + * this will essentially give PCI BDF ordering. + * Put NULL devicepaths at end of the list (should not happen). + */ + for ( i = 0; i < Count; i++ ) { + NextIndex = -1; + path1buf[0]='\0'; + for ( j = 0; j < Count; j++ ) { + if ( TmpDevicePathList[j] == NULL ) + continue; + path2 = efi_devpath_text ( TmpDevicePathList[j] ); + if ( !path2 ) + continue; + + DBG ( "EFIBOOT %d: next=%d, comparing %s to %s\n", + (int) i, (int) NextIndex, path2, path1buf ); + if ( NextIndex == -1 || strncmp ( path2, path1buf, + 256 ) < 0 ) { + NextIndex = j; + strncpy ( path1buf, path2, 256 ); + } + } + if ( NextIndex != -1 ) { + DevicePathList[i] = TmpDevicePathList[NextIndex]; + SimpleFSHandleList[i] = TmpSimpleFSHandleList[NextIndex]; + TmpDevicePathList[NextIndex] = NULL; + } else { + DevicePathList[i] = NULL; + } + } + + DevicePathListNum = Count; + + bs->FreePool ( TmpSimpleFSHandleList ); + bs->FreePool ( TmpDevicePathList ); + + efi_boot_map_initialized = TRUE; + + return 0; +} + +static int efi_boot_local_fs ( EFI_DEVICE_PATH_PROTOCOL *dp, + const char *filename ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_DEVICE_PATH_PROTOCOL *boot_path; + FILEPATH_DEVICE_PATH *filepath; + EFI_DEVICE_PATH_PROTOCOL *end; + size_t prefix_len; + size_t filepath_len; + size_t boot_path_len; + EFI_HANDLE image = NULL; + EFI_STATUS efirc; + int rc; + + if ( dp == NULL ) + return -1; + + DBG ( "EFIBOOT efi_boot_local_fs: device path %s\n", + efi_devpath_text ( dp ) ); + + /* Construct device path for boot image */ + end = efi_path_end ( dp ); + prefix_len = ( ( (void *) end ) - ( (void *) dp ) ); + filepath_len = ( SIZE_OF_FILEPATH_DEVICE_PATH + + ( filename ? ( ( strlen ( filename ) + 1 ) * + sizeof ( filepath->PathName[0] ) ): + sizeof ( efi_default_boot_filename ) ) ); + + boot_path_len = ( prefix_len + filepath_len + sizeof ( *end ) ); + boot_path = zalloc ( boot_path_len ); + if ( !boot_path ) { + rc = -1; + goto err_alloc_path; + } + + memcpy ( boot_path, dp, prefix_len ); + filepath = ( ( (void *) boot_path ) + prefix_len ); + filepath->Header.Type = MEDIA_DEVICE_PATH; + filepath->Header.SubType = MEDIA_FILEPATH_DP; + filepath->Header.Length[0] = ( filepath_len & 0xff ); + filepath->Header.Length[1] = ( filepath_len >> 8 ); + + if ( filename ) { + efi_sprintf ( filepath->PathName, "%s", filename ); + } else { + memcpy ( filepath->PathName, efi_default_boot_filename, + sizeof ( efi_default_boot_filename ) ); + } + + end = ( ( (void *) filepath ) + filepath_len ); + end->Type = END_DEVICE_PATH_TYPE; + end->SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE; + end->Length[0] = sizeof ( *end ); + + /* Release SNP devices */ + efi_snp_release (); + + DBG ( "EFIBOOT attempt to load %s\n", efi_devpath_text ( boot_path ) ); + + if ( ( efirc = bs->LoadImage ( FALSE, efi_image_handle, boot_path, + NULL, 0, &image ) ) != 0 ) { + rc = -1; + DBG ( "EFIBOOT failed to load image\n" ); + goto err_load_image; + } + + DBG ( "EFIBOOT successfully loaded image\n" ); + DBG ( "EFIBOOT trying to start %s\n", + efi_devpath_text ( boot_path ) ); + + efirc = bs->StartImage ( image, NULL, NULL ); + if ( EFI_ERROR ( efirc ) ) + rc = -1; + else + rc = 0; + + DBG ( "EFIBOOT boot image returned: %d\n", rc ); + + bs->UnloadImage ( image ); + +err_load_image: + efi_snp_claim (); + free ( boot_path ); +err_alloc_path: + + return rc; +} + +void efi_boot_display_map ( void ) { + char vol_label[256]; + UINTN i; + int rc; + + if ( !efi_boot_map_initialized ) + efi_boot_create_map (); + + printf ( "Drive#\t[Volume Label] Path\n" ); + printf ( "------\t-------------------\n" ); + for ( i = 0 ; i < DevicePathListNum ; i++ ) { + if ( DevicePathList[i] != NULL ) { + rc = efi_vol_label ( SimpleFSHandleList[i], + vol_label, 256 ); + if ( rc || *vol_label == '\0' ) + strcpy(vol_label, "NO VOLUME LABEL"); + printf ( "%d \t[%s] %s\n", (int) i, vol_label, + efi_devpath_text ( DevicePathList[i] ) ); + } + } +} + +int efi_boot_local ( unsigned int drive, const char *filename ) { + + if ( !efi_boot_map_initialized ) + efi_boot_create_map (); + + if ( DevicePathListNum == 0 || drive > ( DevicePathListNum-1 ) || + DevicePathList[drive] == NULL ) { + printf ( "ERROR: Invalid drive number %#02x\n", drive ); + return -1; + } + + efi_boot_local_fs ( DevicePathList[drive], filename ); + + return 0; +} diff --git a/src/interface/efi/efi_fbcon.c b/src/interface/efi/efi_fbcon.c index d388e031..e3c4d001 100644 --- a/src/interface/efi/efi_fbcon.c +++ b/src/interface/efi/efi_fbcon.c @@ -519,68 +519,70 @@ static int efifb_init ( struct console_configuration *config ) { EFI_STATUS efirc; int rc; - /* Locate graphics output protocol */ - if ( ( efirc = bs->LocateProtocol ( &efi_graphics_output_protocol_guid, - NULL, &interface ) ) != 0 ) { - rc = -EEFI ( efirc ); - DBGC ( &efifb, "EFIFB could not locate graphics output " - "protocol: %s\n", strerror ( rc ) ); - goto err_locate_gop; - } - efifb.gop = interface; + if ( ! config->lazy_update ) { + /* Locate graphics output protocol */ + if ( ( efirc = bs->LocateProtocol ( &efi_graphics_output_protocol_guid, + NULL, &interface ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( &efifb, "EFIFB could not locate graphics output " + "protocol: %s\n", strerror ( rc ) ); + goto err_locate_gop; + } + efifb.gop = interface; - /* Locate HII font protocol */ - if ( ( efirc = bs->LocateProtocol ( &efi_hii_font_protocol_guid, - NULL, &interface ) ) != 0 ) { - rc = -EEFI ( efirc ); - DBGC ( &efifb, "EFIFB could not locate HII font protocol: %s\n", - strerror ( rc ) ); - goto err_locate_hiifont; - } - efifb.hiifont = interface; + /* Locate HII font protocol */ + if ( ( efirc = bs->LocateProtocol ( &efi_hii_font_protocol_guid, + NULL, &interface ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( &efifb, "EFIFB could not locate HII font protocol: %s\n", + strerror ( rc ) ); + goto err_locate_hiifont; + } + efifb.hiifont = interface; - /* Locate glyphs */ - if ( ( rc = efifb_glyphs() ) != 0 ) - goto err_glyphs; + /* Locate glyphs */ + if ( ( rc = efifb_glyphs() ) != 0 ) + goto err_glyphs; - /* Save original mode */ - efifb.saved_mode = efifb.gop->Mode->Mode; + /* Save original mode */ + efifb.saved_mode = efifb.gop->Mode->Mode; - /* Select mode */ - if ( ( mode = efifb_select_mode ( config->width, config->height, - config->depth ) ) < 0 ) { - rc = mode; - goto err_select_mode; - } + /* Select mode */ + if ( ( mode = efifb_select_mode ( config->width, config->height, + config->depth ) ) < 0 ) { + rc = mode; + goto err_select_mode; + } - /* Set mode */ - if ( ( efirc = efifb.gop->SetMode ( efifb.gop, mode ) ) != 0 ) { - rc = -EEFI ( efirc ); - DBGC ( &efifb, "EFIFB could not set mode %d: %s\n", - mode, strerror ( rc ) ); - goto err_set_mode; - } - info = efifb.gop->Mode->Info; - - /* Populate colour map */ - bpp = efifb_colour_map ( info, &efifb.map ); - if ( bpp < 0 ) { - rc = bpp; - DBGC ( &efifb, "EFIFB could not build colour map for " - "mode %d: %s\n", mode, strerror ( rc ) ); - goto err_map; - } + /* Set mode */ + if ( ( efirc = efifb.gop->SetMode ( efifb.gop, mode ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( &efifb, "EFIFB could not set mode %d: %s\n", + mode, strerror ( rc ) ); + goto err_set_mode; + } + info = efifb.gop->Mode->Info; - /* Populate pixel geometry */ - efifb.pixel.width = info->HorizontalResolution; - efifb.pixel.height = info->VerticalResolution; - efifb.pixel.len = ( ( bpp + 7 ) / 8 ); - efifb.pixel.stride = ( efifb.pixel.len * info->PixelsPerScanLine ); + /* Populate colour map */ + bpp = efifb_colour_map ( info, &efifb.map ); + if ( bpp < 0 ) { + rc = bpp; + DBGC ( &efifb, "EFIFB could not build colour map for " + "mode %d: %s\n", mode, strerror ( rc ) ); + goto err_map; + } - /* Populate frame buffer address */ - efifb.start = efifb.gop->Mode->FrameBufferBase; - DBGC ( &efifb, "EFIFB using mode %d (%dx%d %dbpp at %#08lx)\n", - mode, efifb.pixel.width, efifb.pixel.height, bpp, efifb.start ); + /* Populate pixel geometry */ + efifb.pixel.width = info->HorizontalResolution; + efifb.pixel.height = info->VerticalResolution; + efifb.pixel.len = ( ( bpp + 7 ) / 8 ); + efifb.pixel.stride = ( efifb.pixel.len * info->PixelsPerScanLine ); + + /* Populate frame buffer address */ + efifb.start = efifb.gop->Mode->FrameBufferBase; + DBGC ( &efifb, "EFIFB using mode %d (%dx%d %dbpp at %#08lx)\n", + mode, efifb.pixel.width, efifb.pixel.height, bpp, efifb.start ); +} /* Initialise frame buffer console */ if ( ( rc = fbcon_init ( &efifb.fbcon, phys_to_user ( efifb.start ), @@ -638,13 +640,32 @@ static void efifb_putchar ( int character ) { static int efifb_configure ( struct console_configuration *config ) { int rc; - /* Reset console, if applicable */ - if ( ! efifb_console.disabled ) { - efifb_fini(); - efi_console.disabled &= ~CONSOLE_DISABLED_OUTPUT; - ansicol_reset_magic(); + if ( config && config->lazy_update ) { + if ( efifb_console.disabled ) + return 0; + /* No width/height given, use current so we can update the border */ + if ( ( config->width == 0 ) || ( config->height == 0 ) ) { + if ( ( efifb.pixel.width == 0 ) || ( efifb.pixel.height == 0 ) ) { + return -EINVAL; + } + config->width = efifb.pixel.width; + config->height = efifb.pixel.height; + } else { + /* Otherwise make sure the new dimensions match the old ones */ + if ( ( efifb.pixel.width != config->width ) || + ( efifb.pixel.height != config->height ) ) { + return -EINVAL; + } + } + } else { + /* Reset console, if applicable */ + if ( ! efifb_console.disabled ) { + efifb_fini(); + efi_console.disabled &= ~CONSOLE_DISABLED_OUTPUT; + ansicol_reset_magic(); + } + efifb_console.disabled = CONSOLE_DISABLED; } - efifb_console.disabled = CONSOLE_DISABLED; /* Do nothing more unless we have a usable configuration */ if ( ( config == NULL ) || @@ -656,13 +677,15 @@ static int efifb_configure ( struct console_configuration *config ) { if ( ( rc = efifb_init ( config ) ) != 0 ) return rc; - /* Mark console as enabled */ - efifb_console.disabled = 0; - efi_console.disabled |= CONSOLE_DISABLED_OUTPUT; + if ( ! config->lazy_update ) { + /* Mark console as enabled */ + efifb_console.disabled = 0; + efi_console.disabled |= CONSOLE_DISABLED_OUTPUT; - /* Set magic colour to transparent if we have a background picture */ - if ( config->pixbuf ) - ansicol_set_magic_transparent(); + /* Set magic colour to transparent if we have a background picture */ + if ( config->pixbuf ) + ansicol_set_magic_transparent(); + } return 0; } diff --git a/src/interface/efi/efi_local.c b/src/interface/efi/efi_local.c index f54f5dae..d3ac3d54 100644 --- a/src/interface/efi/efi_local.c +++ b/src/interface/efi/efi_local.c @@ -295,6 +295,62 @@ static int efi_local_open_root ( struct efi_local *local, EFI_HANDLE device, } /** + * Open root filesystem of specified volume by index + * + * @v local Local file + * @v index Volume index + * @ret rc Return status code + */ +static int efi_local_open_volume_index ( struct efi_local *local, + UINTN index ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_GUID *protocol = &efi_simple_file_system_protocol_guid; + EFI_FILE_PROTOCOL *root; + EFI_HANDLE *handles; + EFI_HANDLE device; + UINTN num_handles; + EFI_STATUS efirc; + int rc; + + /* Locate all filesystem handles */ + if ( ( efirc = bs->LocateHandleBuffer ( ByProtocol, protocol, + NULL, &num_handles, + &handles ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( local, "LOCAL %p could not enumerate handles: " + "%s\n", local, strerror ( rc ) ); + return rc; + } + + /* Find matching handle */ + if ( index >= num_handles ) { + rc = -ENOENT; + } else { + /* Get this device handle */ + device = handles[index]; + + /* Open root directory */ + if ( ( rc = efi_local_open_root ( local, device, &root ) ) == 0 && root ) { + local->root = root; + rc = 0; + } else { + rc = -EACCES; + } + } + + /* Free handles, if applicable */ + bs->FreePool ( handles ); + + /* Fail if we found no matching handle */ + if ( ! local->root ) { + DBGC ( local, "LOCAL %p found no matching device with index %u\n", + local, (unsigned int)index ); + } + + return rc; +} + +/** * Open root filesystem of specified volume * * @v local Local file @@ -534,7 +590,7 @@ static int efi_local_open ( struct interface *xfer, struct uri *uri ) { struct efi_local *local; const char *volume; const char *path; - int rc; + int rc, vol; /* Parse URI */ volume = ( ( uri->host && uri->host[0] ) ? uri->host : NULL ); @@ -551,13 +607,32 @@ static int efi_local_open ( struct interface *xfer, struct uri *uri ) { process_init_stopped ( &local->process, &efi_local_process_desc, &local->refcnt ); - /* Open specified volume */ - if ( ( rc = efi_local_open_volume ( local, volume ) ) != 0 ) - goto err_open_root; + if ( volume && strcmp ( volume, "*" ) == 0 ) { + /* Open on any volume */ + vol = 0; + while ( ( rc = efi_local_open_volume_index ( local, vol++ ) ) != -ENOENT ) { + if ( rc != 0 ) + continue; + /* Open specified path */ + if ( ( rc = efi_local_open_path ( local, path ) ) != 0 ) { + local->root->Close ( local->root ); + local->root = NULL; + continue; + } + /* Success */ + break; + } + if ( rc != 0 ) + goto err_open_root; + } else { + /* Open specified volume */ + if ( ( rc = efi_local_open_volume ( local, volume ) ) != 0 ) + goto err_open_root; - /* Open specified path */ - if ( ( rc = efi_local_open_path ( local, path ) ) != 0 ) - goto err_open_file; + /* Open specified path */ + if ( ( rc = efi_local_open_path ( local, path ) ) != 0 ) + goto err_open_file; + } /* Get length of file */ if ( ( rc = efi_local_len ( local ) ) != 0 ) |