[efi] Expose net device non-volatile settings via HII
Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
parent
41ea18a455
commit
a4d1250810
@ -8,138 +8,89 @@
|
|||||||
|
|
||||||
FILE_LICENCE ( GPL2_OR_LATER );
|
FILE_LICENCE ( GPL2_OR_LATER );
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
#include <ipxe/efi/Uefi/UefiInternalFormRepresentation.h>
|
#include <ipxe/efi/Uefi/UefiInternalFormRepresentation.h>
|
||||||
#include <ipxe/efi/Guid/MdeModuleHii.h>
|
#include <ipxe/efi/Guid/MdeModuleHii.h>
|
||||||
|
|
||||||
/**
|
|
||||||
* Define an EFI IFR form set type
|
|
||||||
*
|
|
||||||
* @v num_class_guids Number of class GUIDs
|
|
||||||
* @ret type Form set type
|
|
||||||
*/
|
|
||||||
#define EFI_IFR_FORM_SET_TYPE( num_class_guids ) \
|
|
||||||
struct { \
|
|
||||||
EFI_IFR_FORM_SET FormSet; \
|
|
||||||
EFI_GUID ClassGuid[num_class_guids]; \
|
|
||||||
} __attribute__ (( packed ))
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define an EFI IFR form set
|
|
||||||
*
|
|
||||||
* @v guid GUID
|
|
||||||
* @v title Title string
|
|
||||||
* @v help Help string
|
|
||||||
* @v type Form set type (as returned by EFI_IFR_FORM_SET_TYPE())
|
|
||||||
* @ret ifr Form set
|
|
||||||
*
|
|
||||||
* This definition opens a new scope, which must be closed by an
|
|
||||||
* EFI_IFR_END().
|
|
||||||
*/
|
|
||||||
#define EFI_IFR_FORM_SET( guid, title, help, type, ... ) { \
|
|
||||||
.FormSet = { \
|
|
||||||
.Header = { \
|
|
||||||
.OpCode = EFI_IFR_FORM_SET_OP, \
|
|
||||||
.Length = sizeof ( type ), \
|
|
||||||
.Scope = 1, \
|
|
||||||
}, \
|
|
||||||
.Guid = guid, \
|
|
||||||
.FormSetTitle = title, \
|
|
||||||
.Help = help, \
|
|
||||||
.Flags = ( sizeof ( ( ( type * ) NULL )->ClassGuid ) / \
|
|
||||||
sizeof ( ( ( type * ) NULL )->ClassGuid[0] ) ), \
|
|
||||||
}, \
|
|
||||||
.ClassGuid = { \
|
|
||||||
__VA_ARGS__ \
|
|
||||||
}, \
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define an EFI IFR GUID class
|
|
||||||
*
|
|
||||||
* @v class Class
|
|
||||||
* @ret ifr GUID class
|
|
||||||
*/
|
|
||||||
#define EFI_IFR_GUID_CLASS( class ) { \
|
|
||||||
.Header = { \
|
|
||||||
.OpCode = EFI_IFR_GUID_OP, \
|
|
||||||
.Length = sizeof ( EFI_IFR_GUID_CLASS ), \
|
|
||||||
}, \
|
|
||||||
.Guid = EFI_IFR_TIANO_GUID, \
|
|
||||||
.ExtendOpCode = EFI_IFR_EXTEND_OP_CLASS, \
|
|
||||||
.Class = class, \
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define an EFI IFR GUID subclass
|
|
||||||
*
|
|
||||||
* @v subclass Subclass
|
|
||||||
* @ret ifr GUID subclass
|
|
||||||
*/
|
|
||||||
#define EFI_IFR_GUID_SUBCLASS( subclass ) { \
|
|
||||||
.Header = { \
|
|
||||||
.OpCode = EFI_IFR_GUID_OP, \
|
|
||||||
.Length = sizeof ( EFI_IFR_GUID_SUBCLASS ), \
|
|
||||||
}, \
|
|
||||||
.Guid = EFI_IFR_TIANO_GUID, \
|
|
||||||
.ExtendOpCode = EFI_IFR_EXTEND_OP_SUBCLASS, \
|
|
||||||
.SubClass = subclass, \
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define an EFI IFR form
|
|
||||||
*
|
|
||||||
* @v formid Form ID
|
|
||||||
* @v title Title string
|
|
||||||
* @ret ifr Form
|
|
||||||
*
|
|
||||||
* This definition opens a new scope, which must be closed by an
|
|
||||||
* EFI_IFR_END().
|
|
||||||
*/
|
|
||||||
#define EFI_IFR_FORM( formid, title ) { \
|
|
||||||
.Header = { \
|
|
||||||
.OpCode = EFI_IFR_FORM_OP, \
|
|
||||||
.Length = sizeof ( EFI_IFR_FORM ), \
|
|
||||||
.Scope = 1, \
|
|
||||||
}, \
|
|
||||||
.FormId = formid, \
|
|
||||||
.FormTitle = title, \
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define an EFI IFR text widget
|
|
||||||
*
|
|
||||||
* @v prompt Prompt string
|
|
||||||
* @v help Help string
|
|
||||||
* @v text Text string
|
|
||||||
* @ret ifr Text widget
|
|
||||||
*/
|
|
||||||
#define EFI_IFR_TEXT( prompt, help, text ) { \
|
|
||||||
.Header = { \
|
|
||||||
.OpCode = EFI_IFR_TEXT_OP, \
|
|
||||||
.Length = sizeof ( EFI_IFR_TEXT ), \
|
|
||||||
}, \
|
|
||||||
.Statement = { \
|
|
||||||
.Prompt = prompt, \
|
|
||||||
.Help = help, \
|
|
||||||
}, \
|
|
||||||
.TextTwo = text, \
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define an EFI IFR end marker
|
|
||||||
*
|
|
||||||
* @ret ifr End marker
|
|
||||||
*/
|
|
||||||
#define EFI_IFR_END() { \
|
|
||||||
.Header = { \
|
|
||||||
.OpCode = EFI_IFR_END_OP, \
|
|
||||||
.Length = sizeof ( EFI_IFR_END ), \
|
|
||||||
}, \
|
|
||||||
}
|
|
||||||
|
|
||||||
/** GUID indicating formset compliance for IBM Unified Configuration Manager */
|
/** GUID indicating formset compliance for IBM Unified Configuration Manager */
|
||||||
#define EFI_HII_IBM_UCM_COMPLIANT_FORMSET_GUID \
|
#define EFI_HII_IBM_UCM_COMPLIANT_FORMSET_GUID \
|
||||||
{ 0x5c8e9746, 0xa5f7, 0x4593, \
|
{ 0x5c8e9746, 0xa5f7, 0x4593, \
|
||||||
{ 0xaf, 0x1f, 0x66, 0xa8, 0x2a, 0xa1, 0x9c, 0xb1 } }
|
{ 0xaf, 0x1f, 0x66, 0xa8, 0x2a, 0xa1, 0x9c, 0xb1 } }
|
||||||
|
|
||||||
|
/** An EFI IFR builder */
|
||||||
|
struct efi_ifr_builder {
|
||||||
|
/** IFR opcodes */
|
||||||
|
EFI_IFR_OP_HEADER *ops;
|
||||||
|
/** Length of IFR opcodes */
|
||||||
|
size_t ops_len;
|
||||||
|
/** Strings */
|
||||||
|
EFI_HII_STRING_BLOCK *strings;
|
||||||
|
/** Length of strings */
|
||||||
|
size_t strings_len;
|
||||||
|
/** Current string identifier */
|
||||||
|
unsigned int string_id;
|
||||||
|
/** Current variable store identifier */
|
||||||
|
unsigned int varstore_id;
|
||||||
|
/** Current form identifier */
|
||||||
|
unsigned int form_id;
|
||||||
|
/** An allocation has failed */
|
||||||
|
int failed;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialise IFR builder
|
||||||
|
*
|
||||||
|
* @v ifr IFR builder
|
||||||
|
*
|
||||||
|
* The caller must eventually call efi_ifr_free() to free the dynamic
|
||||||
|
* storage associated with the IFR builder.
|
||||||
|
*/
|
||||||
|
static inline void efi_ifr_init ( struct efi_ifr_builder *ifr ) {
|
||||||
|
memset ( ifr, 0, sizeof ( *ifr ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
extern unsigned int efi_ifr_string ( struct efi_ifr_builder *ifr,
|
||||||
|
const char *fmt, ... );
|
||||||
|
extern void efi_ifr_end_op ( struct efi_ifr_builder *ifr );
|
||||||
|
extern void efi_ifr_false_op ( struct efi_ifr_builder *ifr );
|
||||||
|
extern unsigned int efi_ifr_form_op ( struct efi_ifr_builder *ifr,
|
||||||
|
unsigned int title_id );
|
||||||
|
extern void efi_ifr_form_set_op ( struct efi_ifr_builder *ifr,
|
||||||
|
const EFI_GUID *guid,
|
||||||
|
unsigned int title_id, unsigned int help_id,
|
||||||
|
... );
|
||||||
|
void efi_ifr_get_op ( struct efi_ifr_builder *ifr, unsigned int varstore_id,
|
||||||
|
unsigned int varstore_info, unsigned int varstore_type );
|
||||||
|
extern void efi_ifr_guid_class_op ( struct efi_ifr_builder *ifr,
|
||||||
|
unsigned int class );
|
||||||
|
extern void efi_ifr_guid_subclass_op ( struct efi_ifr_builder *ifr,
|
||||||
|
unsigned int subclass );
|
||||||
|
extern 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 );
|
||||||
|
extern 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 );
|
||||||
|
extern void efi_ifr_suppress_if_op ( struct efi_ifr_builder *ifr );
|
||||||
|
extern void efi_ifr_text_op ( struct efi_ifr_builder *ifr,
|
||||||
|
unsigned int prompt_id, unsigned int help_id,
|
||||||
|
unsigned int text_id );
|
||||||
|
extern void efi_ifr_true_op ( struct efi_ifr_builder *ifr );
|
||||||
|
extern unsigned int
|
||||||
|
efi_ifr_varstore_name_value_op ( struct efi_ifr_builder *ifr,
|
||||||
|
const EFI_GUID *guid );
|
||||||
|
extern void efi_ifr_free ( struct efi_ifr_builder *ifr );
|
||||||
|
extern EFI_HII_PACKAGE_LIST_HEADER *
|
||||||
|
efi_ifr_package ( struct efi_ifr_builder *ifr, const EFI_GUID *guid,
|
||||||
|
const char *language, unsigned int language_id );
|
||||||
|
|
||||||
#endif /* _IPXE_EFI_HII_H */
|
#endif /* _IPXE_EFI_HII_H */
|
||||||
|
577
src/interface/efi/efi_hii.c
Normal file
577
src/interface/efi/efi_hii.c
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -19,9 +19,39 @@
|
|||||||
|
|
||||||
FILE_LICENCE ( GPL2_OR_LATER );
|
FILE_LICENCE ( GPL2_OR_LATER );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
*
|
||||||
|
* EFI SNP HII protocol
|
||||||
|
*
|
||||||
|
* The HII protocols are some of the less-well designed parts of the
|
||||||
|
* entire EFI specification. This is a significant accomplishment.
|
||||||
|
*
|
||||||
|
* The face-slappingly ludicrous query string syntax seems to be
|
||||||
|
* motivated by the desire to allow a caller to query multiple drivers
|
||||||
|
* simultaneously via the single-instance HII_CONFIG_ROUTING_PROTOCOL,
|
||||||
|
* which is supposed to pass relevant subsets of the query string to
|
||||||
|
* the relevant drivers.
|
||||||
|
*
|
||||||
|
* Nobody uses the HII_CONFIG_ROUTING_PROTOCOL. Not even the EFI
|
||||||
|
* setup browser uses the HII_CONFIG_ROUTING_PROTOCOL. To the best of
|
||||||
|
* my knowledge, there has only ever been one implementation of the
|
||||||
|
* HII_CONFIG_ROUTING_PROTOCOL (as part of EDK2), and it just doesn't
|
||||||
|
* work. It's so badly broken that I can't even figure out what the
|
||||||
|
* code is _trying_ to do.
|
||||||
|
*
|
||||||
|
* Fundamentally, the problem seems to be that Javascript programmers
|
||||||
|
* should not be allowed to design APIs for C code.
|
||||||
|
*/
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <strings.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <wchar.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <ipxe/settings.h>
|
||||||
|
#include <ipxe/nvo.h>
|
||||||
#include <ipxe/device.h>
|
#include <ipxe/device.h>
|
||||||
#include <ipxe/netdevice.h>
|
#include <ipxe/netdevice.h>
|
||||||
#include <ipxe/efi/efi.h>
|
#include <ipxe/efi/efi.h>
|
||||||
@ -34,250 +64,452 @@ FILE_LICENCE ( GPL2_OR_LATER );
|
|||||||
static EFI_GUID efi_hii_config_access_protocol_guid
|
static EFI_GUID efi_hii_config_access_protocol_guid
|
||||||
= EFI_HII_CONFIG_ACCESS_PROTOCOL_GUID;
|
= EFI_HII_CONFIG_ACCESS_PROTOCOL_GUID;
|
||||||
|
|
||||||
|
/** EFI platform setup formset GUID */
|
||||||
|
static EFI_GUID efi_hii_platform_setup_formset_guid
|
||||||
|
= EFI_HII_PLATFORM_SETUP_FORMSET_GUID;
|
||||||
|
|
||||||
|
/** EFI IBM UCM compliant formset GUID */
|
||||||
|
static EFI_GUID efi_hii_ibm_ucm_compliant_formset_guid
|
||||||
|
= EFI_HII_IBM_UCM_COMPLIANT_FORMSET_GUID;
|
||||||
|
|
||||||
/** EFI HII database protocol */
|
/** EFI HII database protocol */
|
||||||
static EFI_HII_DATABASE_PROTOCOL *efihii;
|
static EFI_HII_DATABASE_PROTOCOL *efihii;
|
||||||
EFI_REQUIRE_PROTOCOL ( EFI_HII_DATABASE_PROTOCOL, &efihii );
|
EFI_REQUIRE_PROTOCOL ( EFI_HII_DATABASE_PROTOCOL, &efihii );
|
||||||
|
|
||||||
/** Local base GUID used for our EFI SNP formset */
|
/**
|
||||||
#define EFI_SNP_FORMSET_GUID_BASE \
|
* Identify settings to be exposed via HII
|
||||||
{ 0xc4f84019, 0x6dfd, 0x4a27, \
|
*
|
||||||
{ 0x9b, 0x94, 0xb7, 0x2e, 0x1f, 0xbc, 0xad, 0xca } }
|
* @v snpdev SNP device
|
||||||
|
* @ret settings Settings, or NULL
|
||||||
|
*/
|
||||||
|
static struct settings * efi_snp_hii_settings ( struct efi_snp_device *snpdev ){
|
||||||
|
|
||||||
/** Form identifiers used for our EFI SNP HII */
|
return find_child_settings ( netdev_settings ( snpdev->netdev ),
|
||||||
enum efi_snp_hii_form_id {
|
NVO_SETTINGS_NAME );
|
||||||
EFI_SNP_FORM = 0x0001, /**< The only form */
|
}
|
||||||
};
|
|
||||||
|
|
||||||
/** String identifiers used for our EFI SNP HII */
|
|
||||||
enum efi_snp_hii_string_id {
|
|
||||||
/* Language name */
|
|
||||||
EFI_SNP_LANGUAGE_NAME = 0x0001,
|
|
||||||
/* Formset */
|
|
||||||
EFI_SNP_FORMSET_TITLE, EFI_SNP_FORMSET_HELP,
|
|
||||||
/* Product name */
|
|
||||||
EFI_SNP_PRODUCT_PROMPT, EFI_SNP_PRODUCT_HELP, EFI_SNP_PRODUCT_TEXT,
|
|
||||||
/* Version */
|
|
||||||
EFI_SNP_VERSION_PROMPT, EFI_SNP_VERSION_HELP, EFI_SNP_VERSION_TEXT,
|
|
||||||
/* Driver */
|
|
||||||
EFI_SNP_DRIVER_PROMPT, EFI_SNP_DRIVER_HELP, EFI_SNP_DRIVER_TEXT,
|
|
||||||
/* Device */
|
|
||||||
EFI_SNP_DEVICE_PROMPT, EFI_SNP_DEVICE_HELP, EFI_SNP_DEVICE_TEXT,
|
|
||||||
/* End of list */
|
|
||||||
EFI_SNP_MAX_STRING_ID
|
|
||||||
};
|
|
||||||
|
|
||||||
/** EFI SNP formset */
|
|
||||||
struct efi_snp_formset {
|
|
||||||
EFI_HII_PACKAGE_HEADER Header;
|
|
||||||
EFI_IFR_FORM_SET_TYPE(2) FormSet;
|
|
||||||
EFI_IFR_GUID_CLASS Class;
|
|
||||||
EFI_IFR_GUID_SUBCLASS SubClass;
|
|
||||||
EFI_IFR_FORM Form;
|
|
||||||
EFI_IFR_TEXT ProductText;
|
|
||||||
EFI_IFR_TEXT VersionText;
|
|
||||||
EFI_IFR_TEXT DriverText;
|
|
||||||
EFI_IFR_TEXT DeviceText;
|
|
||||||
EFI_IFR_END EndForm;
|
|
||||||
EFI_IFR_END EndFormSet;
|
|
||||||
} __attribute__ (( packed )) efi_snp_formset = {
|
|
||||||
.Header = {
|
|
||||||
.Length = sizeof ( efi_snp_formset ),
|
|
||||||
.Type = EFI_HII_PACKAGE_FORMS,
|
|
||||||
},
|
|
||||||
.FormSet = EFI_IFR_FORM_SET ( EFI_SNP_FORMSET_GUID_BASE,
|
|
||||||
EFI_SNP_FORMSET_TITLE,
|
|
||||||
EFI_SNP_FORMSET_HELP,
|
|
||||||
typeof ( efi_snp_formset.FormSet ),
|
|
||||||
EFI_HII_PLATFORM_SETUP_FORMSET_GUID,
|
|
||||||
EFI_HII_IBM_UCM_COMPLIANT_FORMSET_GUID ),
|
|
||||||
.Class = EFI_IFR_GUID_CLASS ( EFI_NETWORK_DEVICE_CLASS ),
|
|
||||||
.SubClass = EFI_IFR_GUID_SUBCLASS ( 0x03 ),
|
|
||||||
.Form = EFI_IFR_FORM ( EFI_SNP_FORM, EFI_SNP_FORMSET_TITLE ),
|
|
||||||
.ProductText = EFI_IFR_TEXT ( EFI_SNP_PRODUCT_PROMPT,
|
|
||||||
EFI_SNP_PRODUCT_HELP,
|
|
||||||
EFI_SNP_PRODUCT_TEXT ),
|
|
||||||
.VersionText = EFI_IFR_TEXT ( EFI_SNP_VERSION_PROMPT,
|
|
||||||
EFI_SNP_VERSION_HELP,
|
|
||||||
EFI_SNP_VERSION_TEXT ),
|
|
||||||
.DriverText = EFI_IFR_TEXT ( EFI_SNP_DRIVER_PROMPT,
|
|
||||||
EFI_SNP_DRIVER_HELP,
|
|
||||||
EFI_SNP_DRIVER_TEXT ),
|
|
||||||
.DeviceText = EFI_IFR_TEXT ( EFI_SNP_DEVICE_PROMPT,
|
|
||||||
EFI_SNP_DEVICE_HELP,
|
|
||||||
EFI_SNP_DEVICE_TEXT ),
|
|
||||||
.EndForm = EFI_IFR_END(),
|
|
||||||
.EndFormSet = EFI_IFR_END(),
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate EFI SNP string
|
* Check whether or not setting is applicable
|
||||||
*
|
*
|
||||||
* @v wbuf Buffer
|
|
||||||
* @v swlen Size of buffer (in wide characters)
|
|
||||||
* @v snpdev SNP device
|
* @v snpdev SNP device
|
||||||
* @ret wlen Length of string (in wide characters)
|
* @v setting Setting
|
||||||
|
* @ret applies Setting applies
|
||||||
*/
|
*/
|
||||||
static int efi_snp_string ( wchar_t *wbuf, ssize_t swlen,
|
static int efi_snp_hii_setting_applies ( struct efi_snp_device *snpdev,
|
||||||
enum efi_snp_hii_string_id id,
|
struct setting *setting ) {
|
||||||
struct efi_snp_device *snpdev ) {
|
|
||||||
struct net_device *netdev = snpdev->netdev;
|
|
||||||
struct device *dev = netdev->dev;
|
|
||||||
|
|
||||||
switch ( id ) {
|
return nvo_applies ( efi_snp_hii_settings ( snpdev ), setting );
|
||||||
case EFI_SNP_LANGUAGE_NAME:
|
}
|
||||||
return efi_ssnprintf ( wbuf, swlen, "English" );
|
|
||||||
case EFI_SNP_FORMSET_TITLE:
|
/**
|
||||||
return efi_ssnprintf ( wbuf, swlen, "%s (%s)",
|
* Generate a random GUID
|
||||||
( PRODUCT_NAME[0] ?
|
*
|
||||||
PRODUCT_NAME : PRODUCT_SHORT_NAME ),
|
* @v guid GUID to fill in
|
||||||
netdev_addr ( netdev ) );
|
*/
|
||||||
case EFI_SNP_FORMSET_HELP:
|
static void efi_snp_hii_random_guid ( EFI_GUID *guid ) {
|
||||||
return efi_ssnprintf ( wbuf, swlen,
|
uint8_t *byte = ( ( uint8_t * ) guid );
|
||||||
"Configure " PRODUCT_SHORT_NAME );
|
unsigned int i;
|
||||||
case EFI_SNP_PRODUCT_PROMPT:
|
|
||||||
return efi_ssnprintf ( wbuf, swlen, "Name" );
|
for ( i = 0 ; i < sizeof ( *guid ) ; i++ )
|
||||||
case EFI_SNP_PRODUCT_HELP:
|
*(byte++) = random();
|
||||||
return efi_ssnprintf ( wbuf, swlen, "Firmware product name" );
|
}
|
||||||
case EFI_SNP_PRODUCT_TEXT:
|
|
||||||
return efi_ssnprintf ( wbuf, swlen, "%s",
|
/**
|
||||||
( PRODUCT_NAME[0] ?
|
* Generate EFI SNP questions
|
||||||
PRODUCT_NAME : PRODUCT_SHORT_NAME ) );
|
*
|
||||||
case EFI_SNP_VERSION_PROMPT:
|
* @v snpdev SNP device
|
||||||
return efi_ssnprintf ( wbuf, swlen, "Version" );
|
* @v ifr IFR builder
|
||||||
case EFI_SNP_VERSION_HELP:
|
* @v varstore_id Variable store identifier
|
||||||
return efi_ssnprintf ( wbuf, swlen, "Firmware version" );
|
*/
|
||||||
case EFI_SNP_VERSION_TEXT:
|
static void efi_snp_hii_questions ( struct efi_snp_device *snpdev,
|
||||||
return efi_ssnprintf ( wbuf, swlen, VERSION );
|
struct efi_ifr_builder *ifr,
|
||||||
case EFI_SNP_DRIVER_PROMPT:
|
unsigned int varstore_id ) {
|
||||||
return efi_ssnprintf ( wbuf, swlen, "Driver" );
|
struct setting *setting;
|
||||||
case EFI_SNP_DRIVER_HELP:
|
unsigned int name_id;
|
||||||
return efi_ssnprintf ( wbuf, swlen, "Firmware driver" );
|
unsigned int prompt_id;
|
||||||
case EFI_SNP_DRIVER_TEXT:
|
unsigned int help_id;
|
||||||
return efi_ssnprintf ( wbuf, swlen, "%s", dev->driver_name );
|
unsigned int question_id;
|
||||||
case EFI_SNP_DEVICE_PROMPT:
|
|
||||||
return efi_ssnprintf ( wbuf, swlen, "Device" );
|
/* Add all applicable settings */
|
||||||
case EFI_SNP_DEVICE_HELP:
|
for_each_table_entry ( setting, SETTINGS ) {
|
||||||
return efi_ssnprintf ( wbuf, swlen, "Hardware device" );
|
if ( ! efi_snp_hii_setting_applies ( snpdev, setting ) )
|
||||||
case EFI_SNP_DEVICE_TEXT:
|
continue;
|
||||||
return efi_ssnprintf ( wbuf, swlen, "%s", dev->name );
|
name_id = efi_ifr_string ( ifr, "%s", setting->name );
|
||||||
default:
|
prompt_id = efi_ifr_string ( ifr, "%s", setting->description );
|
||||||
assert ( 0 );
|
help_id = efi_ifr_string ( ifr, "http://ipxe.org/cfg/%s",
|
||||||
return 0;
|
setting->name );
|
||||||
|
question_id = setting->tag;
|
||||||
|
efi_ifr_string_op ( ifr, prompt_id, help_id,
|
||||||
|
question_id, varstore_id, name_id,
|
||||||
|
0, 0x00, 0xff, 0 );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate EFI SNP string package
|
* Build HII package list for SNP device
|
||||||
*
|
|
||||||
* @v strings String package header buffer
|
|
||||||
* @v max_len Buffer length
|
|
||||||
* @v snpdev SNP device
|
|
||||||
* @ret len Length of string package
|
|
||||||
*/
|
|
||||||
static int efi_snp_strings ( EFI_HII_STRING_PACKAGE_HDR *strings,
|
|
||||||
size_t max_len, struct efi_snp_device *snpdev ) {
|
|
||||||
static const char language[] = "en-us";
|
|
||||||
void *buf = strings;
|
|
||||||
ssize_t remaining = max_len;
|
|
||||||
size_t hdrsize;
|
|
||||||
EFI_HII_SIBT_STRING_UCS2_BLOCK *string;
|
|
||||||
ssize_t wremaining;
|
|
||||||
size_t string_wlen;
|
|
||||||
unsigned int id;
|
|
||||||
EFI_HII_STRING_BLOCK *end;
|
|
||||||
size_t len;
|
|
||||||
|
|
||||||
/* Calculate header size */
|
|
||||||
hdrsize = ( offsetof ( typeof ( *strings ), Language ) +
|
|
||||||
sizeof ( language ) );
|
|
||||||
buf += hdrsize;
|
|
||||||
remaining -= hdrsize;
|
|
||||||
|
|
||||||
/* Fill in strings */
|
|
||||||
for ( id = 1 ; id < EFI_SNP_MAX_STRING_ID ; id++ ) {
|
|
||||||
string = buf;
|
|
||||||
if ( remaining >= ( ( ssize_t ) sizeof ( string->Header ) ) )
|
|
||||||
string->Header.BlockType = EFI_HII_SIBT_STRING_UCS2;
|
|
||||||
buf += offsetof ( typeof ( *string ), StringText );
|
|
||||||
remaining -= offsetof ( typeof ( *string ), StringText );
|
|
||||||
wremaining = ( remaining /
|
|
||||||
( ( ssize_t ) sizeof ( string->StringText[0] )));
|
|
||||||
assert ( ! ( ( remaining <= 0 ) && ( wremaining > 0 ) ) );
|
|
||||||
string_wlen = efi_snp_string ( string->StringText, wremaining,
|
|
||||||
id, snpdev );
|
|
||||||
buf += ( ( string_wlen + 1 /* wNUL */ ) *
|
|
||||||
sizeof ( string->StringText[0] ) );
|
|
||||||
remaining -= ( ( string_wlen + 1 /* wNUL */ ) *
|
|
||||||
sizeof ( string->StringText[0] ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Fill in end marker */
|
|
||||||
end = buf;
|
|
||||||
if ( remaining >= ( ( ssize_t ) sizeof ( *end ) ) )
|
|
||||||
end->BlockType = EFI_HII_SIBT_END;
|
|
||||||
buf += sizeof ( *end );
|
|
||||||
remaining -= sizeof ( *end );
|
|
||||||
|
|
||||||
/* Calculate overall length */
|
|
||||||
len = ( max_len - remaining );
|
|
||||||
|
|
||||||
/* Fill in string package header */
|
|
||||||
if ( strings ) {
|
|
||||||
memset ( strings, 0, sizeof ( *strings ) );
|
|
||||||
strings->Header.Length = len;
|
|
||||||
strings->Header.Type = EFI_HII_PACKAGE_STRINGS;
|
|
||||||
strings->HdrSize = hdrsize;
|
|
||||||
strings->StringInfoOffset = hdrsize;
|
|
||||||
strings->LanguageName = EFI_SNP_LANGUAGE_NAME;
|
|
||||||
memcpy ( strings->Language, language, sizeof ( language ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate EFI SNP package list
|
|
||||||
*
|
*
|
||||||
* @v snpdev SNP device
|
* @v snpdev SNP device
|
||||||
* @ret package_list Package list, or NULL on error
|
* @ret package Package list, or NULL on error
|
||||||
*
|
|
||||||
* The package list is allocated using malloc(), and must eventually
|
|
||||||
* be freed by the caller.
|
|
||||||
*/
|
*/
|
||||||
static EFI_HII_PACKAGE_LIST_HEADER *
|
static EFI_HII_PACKAGE_LIST_HEADER *
|
||||||
efi_snp_package_list ( struct efi_snp_device *snpdev ) {
|
efi_snp_hii_package_list ( struct efi_snp_device *snpdev ) {
|
||||||
size_t strings_len = efi_snp_strings ( NULL, 0, snpdev );
|
struct net_device *netdev = snpdev->netdev;
|
||||||
struct {
|
struct device *dev = netdev->dev;
|
||||||
EFI_HII_PACKAGE_LIST_HEADER header;
|
struct efi_ifr_builder ifr;
|
||||||
struct efi_snp_formset formset;
|
EFI_HII_PACKAGE_LIST_HEADER *package;
|
||||||
union {
|
const char *product_name;
|
||||||
EFI_HII_STRING_PACKAGE_HDR strings;
|
EFI_GUID package_guid;
|
||||||
uint8_t pad[strings_len];
|
EFI_GUID formset_guid;
|
||||||
} __attribute__ (( packed )) strings;
|
EFI_GUID varstore_guid;
|
||||||
EFI_HII_PACKAGE_HEADER end;
|
unsigned int title_id;
|
||||||
} __attribute__ (( packed )) *package_list;
|
unsigned int varstore_id;
|
||||||
|
|
||||||
/* Allocate package list */
|
/* Initialise IFR builder */
|
||||||
package_list = zalloc ( sizeof ( *package_list ) );
|
efi_ifr_init ( &ifr );
|
||||||
if ( ! package_list )
|
|
||||||
|
/* Determine product name */
|
||||||
|
product_name = ( PRODUCT_NAME[0] ? PRODUCT_NAME : PRODUCT_SHORT_NAME );
|
||||||
|
|
||||||
|
/* Generate GUIDs */
|
||||||
|
efi_snp_hii_random_guid ( &package_guid );
|
||||||
|
efi_snp_hii_random_guid ( &formset_guid );
|
||||||
|
efi_snp_hii_random_guid ( &varstore_guid );
|
||||||
|
|
||||||
|
/* Generate title string (used more than once) */
|
||||||
|
title_id = efi_ifr_string ( &ifr, "%s (%s)", product_name,
|
||||||
|
netdev_addr ( netdev ) );
|
||||||
|
|
||||||
|
/* Generate opcodes */
|
||||||
|
efi_ifr_form_set_op ( &ifr, &formset_guid, title_id,
|
||||||
|
efi_ifr_string ( &ifr,
|
||||||
|
"Configure " PRODUCT_SHORT_NAME),
|
||||||
|
&efi_hii_platform_setup_formset_guid,
|
||||||
|
&efi_hii_ibm_ucm_compliant_formset_guid, NULL );
|
||||||
|
efi_ifr_guid_class_op ( &ifr, EFI_NETWORK_DEVICE_CLASS );
|
||||||
|
efi_ifr_guid_subclass_op ( &ifr, 0x03 );
|
||||||
|
varstore_id = efi_ifr_varstore_name_value_op ( &ifr, &varstore_guid );
|
||||||
|
efi_ifr_form_op ( &ifr, title_id );
|
||||||
|
efi_ifr_text_op ( &ifr,
|
||||||
|
efi_ifr_string ( &ifr, "Name" ),
|
||||||
|
efi_ifr_string ( &ifr, "Firmware product name" ),
|
||||||
|
efi_ifr_string ( &ifr, "%s", product_name ) );
|
||||||
|
efi_ifr_text_op ( &ifr,
|
||||||
|
efi_ifr_string ( &ifr, "Version" ),
|
||||||
|
efi_ifr_string ( &ifr, "Firmware version" ),
|
||||||
|
efi_ifr_string ( &ifr, VERSION ) );
|
||||||
|
efi_ifr_text_op ( &ifr,
|
||||||
|
efi_ifr_string ( &ifr, "Driver" ),
|
||||||
|
efi_ifr_string ( &ifr, "Firmware driver" ),
|
||||||
|
efi_ifr_string ( &ifr, "%s", dev->driver_name ) );
|
||||||
|
efi_ifr_text_op ( &ifr,
|
||||||
|
efi_ifr_string ( &ifr, "Device" ),
|
||||||
|
efi_ifr_string ( &ifr, "Hardware device" ),
|
||||||
|
efi_ifr_string ( &ifr, "%s", dev->name ) );
|
||||||
|
efi_snp_hii_questions ( snpdev, &ifr, varstore_id );
|
||||||
|
efi_ifr_end_op ( &ifr );
|
||||||
|
efi_ifr_end_op ( &ifr );
|
||||||
|
|
||||||
|
/* Build package */
|
||||||
|
package = efi_ifr_package ( &ifr, &package_guid, "en-us",
|
||||||
|
efi_ifr_string ( &ifr, "English" ) );
|
||||||
|
if ( ! package ) {
|
||||||
|
DBGC ( snpdev, "SNPDEV %p could not build IFR package\n",
|
||||||
|
snpdev );
|
||||||
|
efi_ifr_free ( &ifr );
|
||||||
return NULL;
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/* Create a unique GUID for this package list and formset */
|
/* Free temporary storage */
|
||||||
efi_snp_formset.FormSet.FormSet.Guid.Data1++;
|
efi_ifr_free ( &ifr );
|
||||||
|
return package;
|
||||||
|
}
|
||||||
|
|
||||||
/* Populate package list */
|
/**
|
||||||
memcpy ( &package_list->header.PackageListGuid,
|
* Append response to result string
|
||||||
&efi_snp_formset.FormSet.FormSet.Guid,
|
*
|
||||||
sizeof ( package_list->header.PackageListGuid ) );
|
* @v snpdev SNP device
|
||||||
package_list->header.PackageLength = sizeof ( *package_list );
|
* @v key Key
|
||||||
memcpy ( &package_list->formset, &efi_snp_formset,
|
* @v value Value
|
||||||
sizeof ( package_list->formset ) );
|
* @v results Result string
|
||||||
efi_snp_strings ( &package_list->strings.strings,
|
* @ret rc Return status code
|
||||||
sizeof ( package_list->strings ), snpdev );
|
*
|
||||||
package_list->end.Length = sizeof ( package_list->end );
|
* The result string is allocated dynamically using
|
||||||
package_list->end.Type = EFI_HII_PACKAGE_END;
|
* BootServices::AllocatePool(), and the caller is responsible for
|
||||||
|
* eventually calling BootServices::FreePool().
|
||||||
|
*/
|
||||||
|
static int efi_snp_hii_append ( struct efi_snp_device *snpdev __unused,
|
||||||
|
const char *key, const char *value,
|
||||||
|
wchar_t **results ) {
|
||||||
|
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
|
||||||
|
size_t len;
|
||||||
|
void *new;
|
||||||
|
|
||||||
return &package_list->header;
|
/* Allocate new string */
|
||||||
|
len = ( ( *results ? ( wcslen ( *results ) + 1 /* "&" */ ) : 0 ) +
|
||||||
|
strlen ( key ) + 1 /* "=" */ + strlen ( value ) + 1 /* NUL */ );
|
||||||
|
bs->AllocatePool ( EfiBootServicesData, ( len * sizeof ( wchar_t ) ),
|
||||||
|
&new );
|
||||||
|
if ( ! new )
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
/* Populate string */
|
||||||
|
efi_snprintf ( new, len, "%ls%s%s=%s", ( *results ? *results : L"" ),
|
||||||
|
( *results ? L"&" : L"" ), key, value );
|
||||||
|
bs->FreePool ( *results );
|
||||||
|
*results = new;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch HII setting
|
||||||
|
*
|
||||||
|
* @v snpdev SNP device
|
||||||
|
* @v key Key
|
||||||
|
* @v value Value
|
||||||
|
* @v results Result string
|
||||||
|
* @v have_setting Flag indicating detection of a setting
|
||||||
|
* @ret rc Return status code
|
||||||
|
*/
|
||||||
|
static int efi_snp_hii_fetch ( struct efi_snp_device *snpdev,
|
||||||
|
const char *key, const char *value,
|
||||||
|
wchar_t **results, int *have_setting ) {
|
||||||
|
struct settings *settings = efi_snp_hii_settings ( snpdev );
|
||||||
|
struct setting *setting;
|
||||||
|
int len;
|
||||||
|
char *buf;
|
||||||
|
char *encoded;
|
||||||
|
int i;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
/* Handle ConfigHdr components */
|
||||||
|
if ( ( strcasecmp ( key, "GUID" ) == 0 ) ||
|
||||||
|
( strcasecmp ( key, "NAME" ) == 0 ) ||
|
||||||
|
( strcasecmp ( key, "PATH" ) == 0 ) ) {
|
||||||
|
return efi_snp_hii_append ( snpdev, key, value, results );
|
||||||
|
}
|
||||||
|
if ( have_setting )
|
||||||
|
*have_setting = 1;
|
||||||
|
|
||||||
|
/* Do nothing more unless we have a settings block */
|
||||||
|
if ( ! settings ) {
|
||||||
|
rc = -ENOTSUP;
|
||||||
|
goto err_no_settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Identify setting */
|
||||||
|
setting = find_setting ( key );
|
||||||
|
if ( ! setting ) {
|
||||||
|
DBGC ( snpdev, "SNPDEV %p no such setting \"%s\"\n",
|
||||||
|
snpdev, key );
|
||||||
|
rc = -ENODEV;
|
||||||
|
goto err_find_setting;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Encode value */
|
||||||
|
if ( setting_exists ( settings, setting ) ) {
|
||||||
|
|
||||||
|
/* Calculate formatted length */
|
||||||
|
len = fetchf_setting ( settings, setting, NULL, 0 );
|
||||||
|
if ( len < 0 ) {
|
||||||
|
rc = len;
|
||||||
|
DBGC ( snpdev, "SNPDEV %p could not fetch %s: %s\n",
|
||||||
|
snpdev, setting->name, strerror ( rc ) );
|
||||||
|
goto err_fetchf_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Allocate buffer for formatted value and HII-encoded value */
|
||||||
|
buf = zalloc ( len + 1 /* NUL */ + ( len * 4 ) + 1 /* NUL */ );
|
||||||
|
if ( ! buf ) {
|
||||||
|
rc = -ENOMEM;
|
||||||
|
goto err_alloc;
|
||||||
|
}
|
||||||
|
encoded = ( buf + len + 1 /* NUL */ );
|
||||||
|
|
||||||
|
/* Format value */
|
||||||
|
fetchf_setting ( settings, setting, buf, ( len + 1 /* NUL */ ));
|
||||||
|
for ( i = 0 ; i < len ; i++ ) {
|
||||||
|
sprintf ( ( encoded + ( 4 * i ) ), "%04x",
|
||||||
|
*( ( uint8_t * ) buf + i ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
/* Non-existent or inapplicable setting */
|
||||||
|
buf = NULL;
|
||||||
|
encoded = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Append results */
|
||||||
|
if ( ( rc = efi_snp_hii_append ( snpdev, key, encoded,
|
||||||
|
results ) ) != 0 ) {
|
||||||
|
goto err_append;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Success */
|
||||||
|
rc = 0;
|
||||||
|
|
||||||
|
err_append:
|
||||||
|
free ( buf );
|
||||||
|
err_alloc:
|
||||||
|
err_fetchf_len:
|
||||||
|
err_find_setting:
|
||||||
|
err_no_settings:
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch HII setting
|
||||||
|
*
|
||||||
|
* @v snpdev SNP device
|
||||||
|
* @v key Key
|
||||||
|
* @v value Value
|
||||||
|
* @v results Result string (unused)
|
||||||
|
* @v have_setting Flag indicating detection of a setting (unused)
|
||||||
|
* @ret rc Return status code
|
||||||
|
*/
|
||||||
|
static int efi_snp_hii_store ( struct efi_snp_device *snpdev,
|
||||||
|
const char *key, const char *value,
|
||||||
|
wchar_t **results __unused,
|
||||||
|
int *have_setting __unused ) {
|
||||||
|
struct settings *settings = efi_snp_hii_settings ( snpdev );
|
||||||
|
struct setting *setting;
|
||||||
|
char *buf;
|
||||||
|
char tmp[5];
|
||||||
|
char *endp;
|
||||||
|
int len;
|
||||||
|
int i;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
/* Handle ConfigHdr components */
|
||||||
|
if ( ( strcasecmp ( key, "GUID" ) == 0 ) ||
|
||||||
|
( strcasecmp ( key, "NAME" ) == 0 ) ||
|
||||||
|
( strcasecmp ( key, "PATH" ) == 0 ) ) {
|
||||||
|
/* Nothing to do */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Do nothing more unless we have a settings block */
|
||||||
|
if ( ! settings ) {
|
||||||
|
rc = -ENOTSUP;
|
||||||
|
goto err_no_settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Identify setting */
|
||||||
|
setting = find_setting ( key );
|
||||||
|
if ( ! setting ) {
|
||||||
|
DBGC ( snpdev, "SNPDEV %p no such setting \"%s\"\n",
|
||||||
|
snpdev, key );
|
||||||
|
rc = -ENODEV;
|
||||||
|
goto err_find_setting;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Allocate buffer */
|
||||||
|
len = ( strlen ( value ) / 4 );
|
||||||
|
buf = zalloc ( len + 1 /* NUL */ );
|
||||||
|
if ( ! buf ) {
|
||||||
|
rc = -ENOMEM;
|
||||||
|
goto err_alloc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Decode value */
|
||||||
|
tmp[4] = '\0';
|
||||||
|
for ( i = 0 ; i < len ; i++ ) {
|
||||||
|
memcpy ( tmp, ( value + ( i * 4 ) ), 4 );
|
||||||
|
buf[i] = strtoul ( tmp, &endp, 16 );
|
||||||
|
if ( endp != &tmp[4] ) {
|
||||||
|
DBGC ( snpdev, "SNPDEV %p invalid character %s\n",
|
||||||
|
snpdev, tmp );
|
||||||
|
rc = -EINVAL;
|
||||||
|
goto err_inval;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Store value */
|
||||||
|
if ( ( rc = storef_setting ( settings, setting, buf ) ) != 0 ) {
|
||||||
|
DBGC ( snpdev, "SNPDEV %p could not store \"%s\" into %s: %s\n",
|
||||||
|
snpdev, buf, setting->name, strerror ( rc ) );
|
||||||
|
goto err_storef;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Success */
|
||||||
|
rc = 0;
|
||||||
|
|
||||||
|
err_storef:
|
||||||
|
err_inval:
|
||||||
|
free ( buf );
|
||||||
|
err_alloc:
|
||||||
|
err_find_setting:
|
||||||
|
err_no_settings:
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process portion of HII configuration string
|
||||||
|
*
|
||||||
|
* @v snpdev SNP device
|
||||||
|
* @v string HII configuration string
|
||||||
|
* @v progress Progress through HII configuration string
|
||||||
|
* @v results Results string
|
||||||
|
* @v have_setting Flag indicating detection of a setting (unused)
|
||||||
|
* @v process Function used to process key=value pairs
|
||||||
|
* @ret rc Return status code
|
||||||
|
*/
|
||||||
|
static int efi_snp_hii_process ( struct efi_snp_device *snpdev,
|
||||||
|
wchar_t *string, wchar_t **progress,
|
||||||
|
wchar_t **results, int *have_setting,
|
||||||
|
int ( * process ) ( struct efi_snp_device *,
|
||||||
|
const char *key,
|
||||||
|
const char *value,
|
||||||
|
wchar_t **results,
|
||||||
|
int *have_setting ) ) {
|
||||||
|
wchar_t *wkey = string;
|
||||||
|
wchar_t *wend = string;
|
||||||
|
wchar_t *wvalue = NULL;
|
||||||
|
size_t key_len;
|
||||||
|
size_t value_len;
|
||||||
|
void *temp;
|
||||||
|
char *key;
|
||||||
|
char *value;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
/* Locate key, value (if any), and end */
|
||||||
|
while ( *wend ) {
|
||||||
|
if ( *wend == L'&' )
|
||||||
|
break;
|
||||||
|
if ( *(wend++) == L'=' )
|
||||||
|
wvalue = wend;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Allocate memory for key and value */
|
||||||
|
key_len = ( ( wvalue ? ( wvalue - 1 ) : wend ) - wkey );
|
||||||
|
value_len = ( wvalue ? ( wend - wvalue ) : 0 );
|
||||||
|
temp = zalloc ( key_len + 1 /* NUL */ + value_len + 1 /* NUL */ );
|
||||||
|
if ( ! temp )
|
||||||
|
return -ENOMEM;
|
||||||
|
key = temp;
|
||||||
|
value = ( temp + key_len + 1 /* NUL */ );
|
||||||
|
|
||||||
|
/* Copy key and value */
|
||||||
|
while ( key_len-- )
|
||||||
|
key[key_len] = wkey[key_len];
|
||||||
|
while ( value_len-- )
|
||||||
|
value[value_len] = wvalue[value_len];
|
||||||
|
|
||||||
|
/* Process key and value */
|
||||||
|
if ( ( rc = process ( snpdev, key, value, results,
|
||||||
|
have_setting ) ) != 0 ) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update progress marker */
|
||||||
|
*progress = wend;
|
||||||
|
|
||||||
|
err:
|
||||||
|
/* Free temporary storage */
|
||||||
|
free ( temp );
|
||||||
|
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -292,14 +524,47 @@ efi_snp_package_list ( struct efi_snp_device *snpdev ) {
|
|||||||
static EFI_STATUS EFIAPI
|
static EFI_STATUS EFIAPI
|
||||||
efi_snp_hii_extract_config ( const EFI_HII_CONFIG_ACCESS_PROTOCOL *hii,
|
efi_snp_hii_extract_config ( const EFI_HII_CONFIG_ACCESS_PROTOCOL *hii,
|
||||||
EFI_STRING request, EFI_STRING *progress,
|
EFI_STRING request, EFI_STRING *progress,
|
||||||
EFI_STRING *results __unused ) {
|
EFI_STRING *results ) {
|
||||||
struct efi_snp_device *snpdev =
|
struct efi_snp_device *snpdev =
|
||||||
container_of ( hii, struct efi_snp_device, hii );
|
container_of ( hii, struct efi_snp_device, hii );
|
||||||
|
int have_setting = 0;
|
||||||
|
wchar_t *pos;
|
||||||
|
int rc;
|
||||||
|
|
||||||
DBGC ( snpdev, "SNPDEV %p ExtractConfig \"%ls\"\n", snpdev, request );
|
DBGC ( snpdev, "SNPDEV %p ExtractConfig request \"%ls\"\n",
|
||||||
|
snpdev, request );
|
||||||
|
|
||||||
*progress = request;
|
/* Initialise results */
|
||||||
return EFI_INVALID_PARAMETER;
|
*results = NULL;
|
||||||
|
|
||||||
|
/* Process all request fragments */
|
||||||
|
for ( pos = *progress = request ; *progress && **progress ;
|
||||||
|
pos = *progress + 1 ) {
|
||||||
|
if ( ( rc = efi_snp_hii_process ( snpdev, pos, progress,
|
||||||
|
results, &have_setting,
|
||||||
|
efi_snp_hii_fetch ) ) != 0 ) {
|
||||||
|
return RC_TO_EFIRC ( rc );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we have no explicit request, return all settings */
|
||||||
|
if ( ! have_setting ) {
|
||||||
|
struct setting *setting;
|
||||||
|
|
||||||
|
for_each_table_entry ( setting, SETTINGS ) {
|
||||||
|
if ( ! efi_snp_hii_setting_applies ( snpdev, setting ) )
|
||||||
|
continue;
|
||||||
|
if ( ( rc = efi_snp_hii_fetch ( snpdev, setting->name,
|
||||||
|
NULL, results,
|
||||||
|
NULL ) ) != 0 ) {
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DBGC ( snpdev, "SNPDEV %p ExtractConfig results \"%ls\"\n",
|
||||||
|
snpdev, *results );
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -315,11 +580,22 @@ efi_snp_hii_route_config ( const EFI_HII_CONFIG_ACCESS_PROTOCOL *hii,
|
|||||||
EFI_STRING config, EFI_STRING *progress ) {
|
EFI_STRING config, EFI_STRING *progress ) {
|
||||||
struct efi_snp_device *snpdev =
|
struct efi_snp_device *snpdev =
|
||||||
container_of ( hii, struct efi_snp_device, hii );
|
container_of ( hii, struct efi_snp_device, hii );
|
||||||
|
wchar_t *pos;
|
||||||
|
int rc;
|
||||||
|
|
||||||
DBGC ( snpdev, "SNPDEV %p RouteConfig \"%ls\"\n", snpdev, config );
|
DBGC ( snpdev, "SNPDEV %p RouteConfig \"%ls\"\n", snpdev, config );
|
||||||
|
|
||||||
*progress = config;
|
/* Process all request fragments */
|
||||||
return EFI_INVALID_PARAMETER;
|
for ( pos = *progress = config ; *progress && **progress ;
|
||||||
|
pos = *progress + 1 ) {
|
||||||
|
if ( ( rc = efi_snp_hii_process ( snpdev, pos, progress,
|
||||||
|
NULL, NULL,
|
||||||
|
efi_snp_hii_store ) ) != 0 ) {
|
||||||
|
return RC_TO_EFIRC ( rc );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -368,7 +644,7 @@ int efi_snp_hii_install ( struct efi_snp_device *snpdev ) {
|
|||||||
memcpy ( &snpdev->hii, &efi_snp_device_hii, sizeof ( snpdev->hii ) );
|
memcpy ( &snpdev->hii, &efi_snp_device_hii, sizeof ( snpdev->hii ) );
|
||||||
|
|
||||||
/* Create HII package list */
|
/* Create HII package list */
|
||||||
snpdev->package_list = efi_snp_package_list ( snpdev );
|
snpdev->package_list = efi_snp_hii_package_list ( snpdev );
|
||||||
if ( ! snpdev->package_list ) {
|
if ( ! snpdev->package_list ) {
|
||||||
DBGC ( snpdev, "SNPDEV %p could not create HII package list\n",
|
DBGC ( snpdev, "SNPDEV %p could not create HII package list\n",
|
||||||
snpdev );
|
snpdev );
|
||||||
|
Reference in New Issue
Block a user