diff options
Diffstat (limited to 'roms/ipxe/src/interface/efi/efi_hii.c')
-rw-r--r-- | roms/ipxe/src/interface/efi/efi_hii.c | 577 |
1 files changed, 577 insertions, 0 deletions
diff --git a/roms/ipxe/src/interface/efi/efi_hii.c b/roms/ipxe/src/interface/efi/efi_hii.c new file mode 100644 index 00000000..834060b5 --- /dev/null +++ b/roms/ipxe/src/interface/efi/efi_hii.c @@ -0,0 +1,577 @@ +/* + * Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stdlib.h> +#include <stddef.h> +#include <stdarg.h> +#include <string.h> +#include <ipxe/efi/efi.h> +#include <ipxe/efi/efi_strings.h> +#include <ipxe/efi/efi_hii.h> + +/** Tiano GUID */ +static const EFI_GUID tiano_guid = EFI_IFR_TIANO_GUID; + +/** + * Add string to IFR builder + * + * @v ifr IFR builder + * @v fmt Format string + * @v ... Arguments + * @ret string_id String identifier, or zero on failure + */ +unsigned int efi_ifr_string ( struct efi_ifr_builder *ifr, const char *fmt, + ... ) { + EFI_HII_STRING_BLOCK *new_strings; + EFI_HII_SIBT_STRING_UCS2_BLOCK *ucs2; + size_t new_strings_len; + va_list args; + size_t len; + unsigned int string_id; + + /* Do nothing if a previous allocation has failed */ + if ( ifr->failed ) + return 0; + + /* Calculate string length */ + va_start ( args, fmt ); + len = ( efi_vsnprintf ( NULL, 0, fmt, args ) + 1 /* wNUL */ ); + va_end ( args ); + + /* Reallocate strings */ + new_strings_len = ( ifr->strings_len + + offsetof ( typeof ( *ucs2 ), StringText ) + + ( len * sizeof ( ucs2->StringText[0] ) ) ); + new_strings = realloc ( ifr->strings, new_strings_len ); + if ( ! new_strings ) { + ifr->failed = 1; + return 0; + } + ucs2 = ( ( ( void * ) new_strings ) + ifr->strings_len ); + ifr->strings = new_strings; + ifr->strings_len = new_strings_len; + + /* Fill in string */ + ucs2->Header.BlockType = EFI_HII_SIBT_STRING_UCS2; + va_start ( args, fmt ); + efi_vsnprintf ( ucs2->StringText, len, fmt, args ); + va_end ( args ); + + /* Allocate string ID */ + string_id = ++(ifr->string_id); + + DBGC ( ifr, "IFR %p string %#04x is \"%ls\"\n", + ifr, string_id, ucs2->StringText ); + return string_id; +} + +/** + * Add IFR opcode to IFR builder + * + * @v ifr IFR builder + * @v opcode Opcode + * @v len Opcode length + * @ret op Opcode, or NULL + */ +static void * efi_ifr_op ( struct efi_ifr_builder *ifr, unsigned int opcode, + size_t len ) { + EFI_IFR_OP_HEADER *new_ops; + EFI_IFR_OP_HEADER *op; + size_t new_ops_len; + + /* Do nothing if a previous allocation has failed */ + if ( ifr->failed ) + return NULL; + + /* Reallocate opcodes */ + new_ops_len = ( ifr->ops_len + len ); + new_ops = realloc ( ifr->ops, new_ops_len ); + if ( ! new_ops ) { + ifr->failed = 1; + return NULL; + } + op = ( ( ( void * ) new_ops ) + ifr->ops_len ); + ifr->ops = new_ops; + ifr->ops_len = new_ops_len; + + /* Fill in opcode header */ + op->OpCode = opcode; + op->Length = len; + + return op; +} + +/** + * Add end opcode to IFR builder + * + * @v ifr IFR builder + */ +void efi_ifr_end_op ( struct efi_ifr_builder *ifr ) { + size_t dispaddr = ifr->ops_len; + EFI_IFR_END *end; + + /* Add opcode */ + end = efi_ifr_op ( ifr, EFI_IFR_END_OP, sizeof ( *end ) ); + + DBGC ( ifr, "IFR %p end\n", ifr ); + DBGC2_HDA ( ifr, dispaddr, end, sizeof ( *end ) ); +} + +/** + * Add false opcode to IFR builder + * + * @v ifr IFR builder + */ +void efi_ifr_false_op ( struct efi_ifr_builder *ifr ) { + size_t dispaddr = ifr->ops_len; + EFI_IFR_FALSE *false; + + /* Add opcode */ + false = efi_ifr_op ( ifr, EFI_IFR_FALSE_OP, sizeof ( *false ) ); + + DBGC ( ifr, "IFR %p false\n", ifr ); + DBGC2_HDA ( ifr, dispaddr, false, sizeof ( *false ) ); +} + +/** + * Add form opcode to IFR builder + * + * @v ifr IFR builder + * @v title_id Title string identifier + * @ret form_id Form identifier + */ +unsigned int efi_ifr_form_op ( struct efi_ifr_builder *ifr, + unsigned int title_id ) { + size_t dispaddr = ifr->ops_len; + EFI_IFR_FORM *form; + + /* Add opcode */ + form = efi_ifr_op ( ifr, EFI_IFR_FORM_OP, sizeof ( *form ) ); + if ( ! form ) + return 0; + form->Header.Scope = 1; + form->FormId = ++(ifr->form_id); + form->FormTitle = title_id; + + DBGC ( ifr, "IFR %p name/value store %#04x title %#04x\n", + ifr, form->FormId, title_id ); + DBGC2_HDA ( ifr, dispaddr, form, sizeof ( *form ) ); + return form->FormId; +} + +/** + * Add formset opcode to IFR builder + * + * @v ifr IFR builder + * @v guid GUID + * @v title_id Title string identifier + * @v help_id Help string identifier + * @v ... Class GUIDs (terminated by NULL) + */ +void efi_ifr_form_set_op ( struct efi_ifr_builder *ifr, const EFI_GUID *guid, + unsigned int title_id, unsigned int help_id, ... ) { + size_t dispaddr = ifr->ops_len; + EFI_IFR_FORM_SET *formset; + EFI_GUID *class_guid; + unsigned int num_class_guids = 0; + size_t len; + va_list args; + + /* Count number of class GUIDs */ + va_start ( args, help_id ); + while ( va_arg ( args, const EFI_GUID * ) != NULL ) + num_class_guids++; + va_end ( args ); + + /* Add opcode */ + len = ( sizeof ( *formset ) + + ( num_class_guids * sizeof ( *class_guid ) ) ); + formset = efi_ifr_op ( ifr, EFI_IFR_FORM_SET_OP, len ); + if ( ! formset ) + return; + formset->Header.Scope = 1; + memcpy ( &formset->Guid, guid, sizeof ( formset->Guid ) ); + formset->FormSetTitle = title_id; + formset->Help = help_id; + formset->Flags = num_class_guids; + + /* Add class GUIDs */ + class_guid = ( ( ( void * ) formset ) + sizeof ( *formset ) ); + va_start ( args, help_id ); + while ( num_class_guids-- ) { + memcpy ( class_guid++, va_arg ( args, const EFI_GUID * ), + sizeof ( *class_guid ) ); + } + va_end ( args ); + + DBGC ( ifr, "IFR %p formset title %#04x help %#04x\n", + ifr, title_id, help_id ); + DBGC2_HDA ( ifr, dispaddr, formset, len ); +} + +/** + * Add get opcode to IFR builder + * + * @v ifr IFR builder + * @v varstore_id Variable store identifier + * @v varstore_info Variable string identifier or offset + * @v varstore_type Variable type + */ +void efi_ifr_get_op ( struct efi_ifr_builder *ifr, unsigned int varstore_id, + unsigned int varstore_info, unsigned int varstore_type ) { + size_t dispaddr = ifr->ops_len; + EFI_IFR_GET *get; + + /* Add opcode */ + get = efi_ifr_op ( ifr, EFI_IFR_GET_OP, sizeof ( *get ) ); + get->VarStoreId = varstore_id; + get->VarStoreInfo.VarName = varstore_info; + get->VarStoreType = varstore_type; + + DBGC ( ifr, "IFR %p get varstore %#04x:%#04x type %#02x\n", + ifr, varstore_id, varstore_info, varstore_type ); + DBGC2_HDA ( ifr, dispaddr, get, sizeof ( *get ) ); +} + +/** + * Add GUID class opcode to IFR builder + * + * @v ifr IFR builder + * @v class Class + */ +void efi_ifr_guid_class_op ( struct efi_ifr_builder *ifr, unsigned int class ) { + size_t dispaddr = ifr->ops_len; + EFI_IFR_GUID_CLASS *guid_class; + + /* Add opcode */ + guid_class = efi_ifr_op ( ifr, EFI_IFR_GUID_OP, + sizeof ( *guid_class ) ); + if ( ! guid_class ) + return; + memcpy ( &guid_class->Guid, &tiano_guid, sizeof ( guid_class->Guid ) ); + guid_class->ExtendOpCode = EFI_IFR_EXTEND_OP_CLASS; + guid_class->Class = class; + + DBGC ( ifr, "IFR %p GUID class %#02x\n", ifr, class ); + DBGC2_HDA ( ifr, dispaddr, guid_class, sizeof ( *guid_class ) ); +} + +/** + * Add GUID subclass opcode to IFR builder + * + * @v ifr IFR builder + * @v subclass Subclass + */ +void efi_ifr_guid_subclass_op ( struct efi_ifr_builder *ifr, + unsigned int subclass ) { + size_t dispaddr = ifr->ops_len; + EFI_IFR_GUID_SUBCLASS *guid_subclass; + + /* Add opcode */ + guid_subclass = efi_ifr_op ( ifr, EFI_IFR_GUID_OP, + sizeof ( *guid_subclass ) ); + if ( ! guid_subclass ) + return; + memcpy ( &guid_subclass->Guid, &tiano_guid, + sizeof ( guid_subclass->Guid ) ); + guid_subclass->ExtendOpCode = EFI_IFR_EXTEND_OP_SUBCLASS; + guid_subclass->SubClass = subclass; + + DBGC ( ifr, "IFR %p GUID subclass %#02x\n", ifr, subclass ); + DBGC2_HDA ( ifr, dispaddr, guid_subclass, sizeof ( *guid_subclass ) ); +} + +/** + * Add numeric opcode to IFR builder + * + * @v ifr IFR builder + * @v prompt_id Prompt string identifier + * @v help_id Help string identifier + * @v question_id Question identifier + * @v varstore_id Variable store identifier + * @v varstore_info Variable string identifier or offset + * @v vflags Variable flags + * @v min_value Minimum value + * @v max_value Maximum value + * @v step Step + * @v flags Flags + */ +void efi_ifr_numeric_op ( struct efi_ifr_builder *ifr, unsigned int prompt_id, + unsigned int help_id, unsigned int question_id, + unsigned int varstore_id, unsigned int varstore_info, + unsigned int vflags, unsigned long min_value, + unsigned long max_value, unsigned int step, + unsigned int flags ) { + size_t dispaddr = ifr->ops_len; + EFI_IFR_NUMERIC *numeric; + unsigned int size; + + /* Add opcode */ + numeric = efi_ifr_op ( ifr, EFI_IFR_NUMERIC_OP, sizeof ( *numeric ) ); + if ( ! numeric ) + return; + numeric->Question.Header.Prompt = prompt_id; + numeric->Question.Header.Help = help_id; + numeric->Question.QuestionId = question_id; + numeric->Question.VarStoreId = varstore_id; + numeric->Question.VarStoreInfo.VarName = varstore_info; + numeric->Question.Flags = vflags; + size = ( flags & EFI_IFR_NUMERIC_SIZE ); + switch ( size ) { + case EFI_IFR_NUMERIC_SIZE_1 : + numeric->data.u8.MinValue = min_value; + numeric->data.u8.MaxValue = max_value; + numeric->data.u8.Step = step; + break; + case EFI_IFR_NUMERIC_SIZE_2 : + numeric->data.u16.MinValue = min_value; + numeric->data.u16.MaxValue = max_value; + numeric->data.u16.Step = step; + break; + case EFI_IFR_NUMERIC_SIZE_4 : + numeric->data.u32.MinValue = min_value; + numeric->data.u32.MaxValue = max_value; + numeric->data.u32.Step = step; + break; + case EFI_IFR_NUMERIC_SIZE_8 : + numeric->data.u64.MinValue = min_value; + numeric->data.u64.MaxValue = max_value; + numeric->data.u64.Step = step; + break; + } + + DBGC ( ifr, "IFR %p numeric prompt %#04x help %#04x question %#04x " + "varstore %#04x:%#04x\n", ifr, prompt_id, help_id, question_id, + varstore_id, varstore_info ); + DBGC2_HDA ( ifr, dispaddr, numeric, sizeof ( *numeric ) ); +} + +/** + * Add string opcode to IFR builder + * + * @v ifr IFR builder + * @v prompt_id Prompt string identifier + * @v help_id Help string identifier + * @v question_id Question identifier + * @v varstore_id Variable store identifier + * @v varstore_info Variable string identifier or offset + * @v vflags Variable flags + * @v min_size Minimum size + * @v max_size Maximum size + * @v flags Flags + */ +void efi_ifr_string_op ( struct efi_ifr_builder *ifr, unsigned int prompt_id, + unsigned int help_id, unsigned int question_id, + unsigned int varstore_id, unsigned int varstore_info, + unsigned int vflags, unsigned int min_size, + unsigned int max_size, unsigned int flags ) { + size_t dispaddr = ifr->ops_len; + EFI_IFR_STRING *string; + + /* Add opcode */ + string = efi_ifr_op ( ifr, EFI_IFR_STRING_OP, sizeof ( *string ) ); + if ( ! string ) + return; + string->Question.Header.Prompt = prompt_id; + string->Question.Header.Help = help_id; + string->Question.QuestionId = question_id; + string->Question.VarStoreId = varstore_id; + string->Question.VarStoreInfo.VarName = varstore_info; + string->Question.Flags = vflags; + string->MinSize = min_size; + string->MaxSize = max_size; + string->Flags = flags; + + DBGC ( ifr, "IFR %p string prompt %#04x help %#04x question %#04x " + "varstore %#04x:%#04x\n", ifr, prompt_id, help_id, question_id, + varstore_id, varstore_info ); + DBGC2_HDA ( ifr, dispaddr, string, sizeof ( *string ) ); +} + +/** + * Add suppress-if opcode to IFR builder + * + * @v ifr IFR builder + */ +void efi_ifr_suppress_if_op ( struct efi_ifr_builder *ifr ) { + size_t dispaddr = ifr->ops_len; + EFI_IFR_SUPPRESS_IF *suppress_if; + + /* Add opcode */ + suppress_if = efi_ifr_op ( ifr, EFI_IFR_SUPPRESS_IF_OP, + sizeof ( *suppress_if ) ); + suppress_if->Header.Scope = 1; + + DBGC ( ifr, "IFR %p suppress-if\n", ifr ); + DBGC2_HDA ( ifr, dispaddr, suppress_if, sizeof ( *suppress_if ) ); +} + +/** + * Add text opcode to IFR builder + * + * @v ifr IFR builder + * @v prompt_id Prompt string identifier + * @v help_id Help string identifier + * @v text_id Text string identifier + */ +void efi_ifr_text_op ( struct efi_ifr_builder *ifr, unsigned int prompt_id, + unsigned int help_id, unsigned int text_id ) { + size_t dispaddr = ifr->ops_len; + EFI_IFR_TEXT *text; + + /* Add opcode */ + text = efi_ifr_op ( ifr, EFI_IFR_TEXT_OP, sizeof ( *text ) ); + if ( ! text ) + return; + text->Statement.Prompt = prompt_id; + text->Statement.Help = help_id; + text->TextTwo = text_id; + + DBGC ( ifr, "IFR %p text prompt %#04x help %#04x text %#04x\n", + ifr, prompt_id, help_id, text_id ); + DBGC2_HDA ( ifr, dispaddr, text, sizeof ( *text ) ); +} + +/** + * Add true opcode to IFR builder + * + * @v ifr IFR builder + */ +void efi_ifr_true_op ( struct efi_ifr_builder *ifr ) { + size_t dispaddr = ifr->ops_len; + EFI_IFR_TRUE *true; + + /* Add opcode */ + true = efi_ifr_op ( ifr, EFI_IFR_TRUE_OP, sizeof ( *true ) ); + + DBGC ( ifr, "IFR %p true\n", ifr ); + DBGC2_HDA ( ifr, dispaddr, true, sizeof ( *true ) ); +} + +/** + * Add name/value store opcode to IFR builder + * + * @v ifr IFR builder + * @v guid GUID + * @ret varstore_id Variable store identifier, or 0 on failure + */ +unsigned int efi_ifr_varstore_name_value_op ( struct efi_ifr_builder *ifr, + const EFI_GUID *guid ) { + size_t dispaddr = ifr->ops_len; + EFI_IFR_VARSTORE_NAME_VALUE *varstore; + + /* Add opcode */ + varstore = efi_ifr_op ( ifr, EFI_IFR_VARSTORE_NAME_VALUE_OP, + sizeof ( *varstore ) ); + if ( ! varstore ) + return 0; + varstore->VarStoreId = ++(ifr->varstore_id); + memcpy ( &varstore->Guid, guid, sizeof ( varstore->Guid ) ); + + DBGC ( ifr, "IFR %p name/value store %#04x\n", + ifr, varstore->VarStoreId ); + DBGC2_HDA ( ifr, dispaddr, varstore, sizeof ( *varstore ) ); + return varstore->VarStoreId; +} + +/** + * Free memory used by IFR builder + * + * @v ifr IFR builder + */ +void efi_ifr_free ( struct efi_ifr_builder *ifr ) { + + free ( ifr->ops ); + free ( ifr->strings ); + memset ( ifr, 0, sizeof ( *ifr ) ); +} + +/** + * Construct package list from IFR builder + * + * @v ifr IFR builder + * @v guid Package GUID + * @v language Language + * @v language_id Language string ID + * @ret package Package list, or NULL + * + * The package list is allocated using malloc(), and must eventually + * be freed by the caller. (The caller must also call efi_ifr_free() + * to free the temporary storage used during construction.) + */ +EFI_HII_PACKAGE_LIST_HEADER * efi_ifr_package ( struct efi_ifr_builder *ifr, + const EFI_GUID *guid, + const char *language, + unsigned int language_id ) { + struct { + EFI_HII_PACKAGE_LIST_HEADER header; + struct { + EFI_HII_PACKAGE_HEADER header; + uint8_t data[ifr->ops_len]; + } __attribute__ (( packed )) ops; + struct { + union { + EFI_HII_STRING_PACKAGE_HDR header; + uint8_t pad[offsetof(EFI_HII_STRING_PACKAGE_HDR, + Language) + + strlen ( language ) + 1 /* NUL */ ]; + } __attribute__ (( packed )) header; + uint8_t data[ifr->strings_len]; + EFI_HII_STRING_BLOCK end; + } __attribute__ (( packed )) strings; + EFI_HII_PACKAGE_HEADER end; + } __attribute__ (( packed )) *package; + + /* Fail if any previous allocation failed */ + if ( ifr->failed ) + return NULL; + + /* Allocate package list */ + package = zalloc ( sizeof ( *package ) ); + if ( ! package ) + return NULL; + + /* Populate package list */ + package->header.PackageLength = sizeof ( *package ); + memcpy ( &package->header.PackageListGuid, guid, + sizeof ( package->header.PackageListGuid ) ); + package->ops.header.Length = sizeof ( package->ops ); + package->ops.header.Type = EFI_HII_PACKAGE_FORMS; + memcpy ( package->ops.data, ifr->ops, sizeof ( package->ops.data ) ); + package->strings.header.header.Header.Length = + sizeof ( package->strings ); + package->strings.header.header.Header.Type = + EFI_HII_PACKAGE_STRINGS; + package->strings.header.header.HdrSize = + sizeof ( package->strings.header ); + package->strings.header.header.StringInfoOffset = + sizeof ( package->strings.header ); + package->strings.header.header.LanguageName = language_id; + strcpy ( package->strings.header.header.Language, language ); + memcpy ( package->strings.data, ifr->strings, + sizeof ( package->strings.data ) ); + package->strings.end.BlockType = EFI_HII_SIBT_END; + package->end.Type = EFI_HII_PACKAGE_END; + package->end.Length = sizeof ( package->end ); + + return &package->header; +} + |