mirror of https://github.com/ipxe/ipxe.git
728 lines
19 KiB
C
728 lines
19 KiB
C
/*
|
|
* 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.
|
|
*
|
|
* 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 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 <strings.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <wchar.h>
|
|
#include <errno.h>
|
|
#include <ipxe/settings.h>
|
|
#include <ipxe/nvo.h>
|
|
#include <ipxe/device.h>
|
|
#include <ipxe/netdevice.h>
|
|
#include <ipxe/version.h>
|
|
#include <ipxe/efi/efi.h>
|
|
#include <ipxe/efi/efi_hii.h>
|
|
#include <ipxe/efi/efi_snp.h>
|
|
#include <ipxe/efi/efi_strings.h>
|
|
#include <config/branding.h>
|
|
|
|
/** 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 */
|
|
static EFI_HII_DATABASE_PROTOCOL *efihii;
|
|
EFI_REQUEST_PROTOCOL ( EFI_HII_DATABASE_PROTOCOL, &efihii );
|
|
|
|
/**
|
|
* Identify settings to be exposed via HII
|
|
*
|
|
* @v snpdev SNP device
|
|
* @ret settings Settings, or NULL
|
|
*/
|
|
static struct settings * efi_snp_hii_settings ( struct efi_snp_device *snpdev ){
|
|
|
|
return find_child_settings ( netdev_settings ( snpdev->netdev ),
|
|
NVO_SETTINGS_NAME );
|
|
}
|
|
|
|
/**
|
|
* Check whether or not setting is applicable
|
|
*
|
|
* @v snpdev SNP device
|
|
* @v setting Setting
|
|
* @ret applies Setting applies
|
|
*/
|
|
static int efi_snp_hii_setting_applies ( struct efi_snp_device *snpdev,
|
|
struct setting *setting ) {
|
|
|
|
return nvo_applies ( efi_snp_hii_settings ( snpdev ), setting );
|
|
}
|
|
|
|
/**
|
|
* Generate a random GUID
|
|
*
|
|
* @v guid GUID to fill in
|
|
*/
|
|
static void efi_snp_hii_random_guid ( EFI_GUID *guid ) {
|
|
uint8_t *byte = ( ( uint8_t * ) guid );
|
|
unsigned int i;
|
|
|
|
for ( i = 0 ; i < sizeof ( *guid ) ; i++ )
|
|
*(byte++) = random();
|
|
}
|
|
|
|
/**
|
|
* Generate EFI SNP questions
|
|
*
|
|
* @v snpdev SNP device
|
|
* @v ifr IFR builder
|
|
* @v varstore_id Variable store identifier
|
|
*/
|
|
static void efi_snp_hii_questions ( struct efi_snp_device *snpdev,
|
|
struct efi_ifr_builder *ifr,
|
|
unsigned int varstore_id ) {
|
|
struct setting *setting;
|
|
struct setting *previous = NULL;
|
|
unsigned int name_id;
|
|
unsigned int prompt_id;
|
|
unsigned int help_id;
|
|
unsigned int question_id;
|
|
|
|
/* Add all applicable settings */
|
|
for_each_table_entry ( setting, SETTINGS ) {
|
|
if ( ! efi_snp_hii_setting_applies ( snpdev, setting ) )
|
|
continue;
|
|
if ( previous && ( setting_cmp ( setting, previous ) == 0 ) )
|
|
continue;
|
|
previous = setting;
|
|
name_id = efi_ifr_string ( ifr, "%s", setting->name );
|
|
prompt_id = efi_ifr_string ( ifr, "%s", setting->description );
|
|
help_id = efi_ifr_string ( ifr, PRODUCT_SETTING_URI,
|
|
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 );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Build HII package list for SNP device
|
|
*
|
|
* @v snpdev SNP device
|
|
* @ret package Package list, or NULL on error
|
|
*/
|
|
static EFI_HII_PACKAGE_LIST_HEADER *
|
|
efi_snp_hii_package_list ( struct efi_snp_device *snpdev ) {
|
|
struct net_device *netdev = snpdev->netdev;
|
|
struct device *dev = netdev->dev;
|
|
struct efi_ifr_builder ifr;
|
|
EFI_HII_PACKAGE_LIST_HEADER *package;
|
|
const char *name;
|
|
EFI_GUID package_guid;
|
|
EFI_GUID formset_guid;
|
|
EFI_GUID varstore_guid;
|
|
unsigned int title_id;
|
|
unsigned int varstore_id;
|
|
|
|
/* Initialise IFR builder */
|
|
efi_ifr_init ( &ifr );
|
|
|
|
/* Determine product name */
|
|
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)", name,
|
|
netdev_addr ( netdev ) );
|
|
|
|
/* Generate opcodes */
|
|
efi_ifr_form_set_op ( &ifr, &formset_guid, title_id,
|
|
efi_ifr_string ( &ifr, "Configure %s",
|
|
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", name ) );
|
|
efi_ifr_text_op ( &ifr,
|
|
efi_ifr_string ( &ifr, "Version" ),
|
|
efi_ifr_string ( &ifr, "Firmware version" ),
|
|
efi_ifr_string ( &ifr, "%s", product_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;
|
|
}
|
|
|
|
/* Free temporary storage */
|
|
efi_ifr_free ( &ifr );
|
|
return package;
|
|
}
|
|
|
|
/**
|
|
* Append response to result string
|
|
*
|
|
* @v snpdev SNP device
|
|
* @v key Key
|
|
* @v value Value
|
|
* @v results Result string
|
|
* @ret rc Return status code
|
|
*
|
|
* The result string is allocated dynamically using
|
|
* 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;
|
|
|
|
/* 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 settings *origin;
|
|
struct setting *setting;
|
|
struct setting fetched;
|
|
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, &origin, &fetched,
|
|
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 ( origin, &fetched, NULL, NULL, 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;
|
|
}
|
|
|
|
/**
|
|
* Fetch configuration
|
|
*
|
|
* @v hii HII configuration access protocol
|
|
* @v request Configuration to fetch
|
|
* @ret progress Progress made through configuration to fetch
|
|
* @ret results Query results
|
|
* @ret efirc EFI status code
|
|
*/
|
|
static EFI_STATUS EFIAPI
|
|
efi_snp_hii_extract_config ( const EFI_HII_CONFIG_ACCESS_PROTOCOL *hii,
|
|
EFI_STRING request, EFI_STRING *progress,
|
|
EFI_STRING *results ) {
|
|
struct efi_snp_device *snpdev =
|
|
container_of ( hii, struct efi_snp_device, hii );
|
|
int have_setting = 0;
|
|
wchar_t *pos;
|
|
int rc;
|
|
|
|
DBGC ( snpdev, "SNPDEV %p ExtractConfig request \"%ls\"\n",
|
|
snpdev, request );
|
|
|
|
/* Initialise results */
|
|
*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 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 EFIRC ( rc );
|
|
}
|
|
}
|
|
}
|
|
|
|
DBGC ( snpdev, "SNPDEV %p ExtractConfig results \"%ls\"\n",
|
|
snpdev, *results );
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Store configuration
|
|
*
|
|
* @v hii HII configuration access protocol
|
|
* @v config Configuration to store
|
|
* @ret progress Progress made through configuration to store
|
|
* @ret efirc EFI status code
|
|
*/
|
|
static EFI_STATUS EFIAPI
|
|
efi_snp_hii_route_config ( const EFI_HII_CONFIG_ACCESS_PROTOCOL *hii,
|
|
EFI_STRING config, EFI_STRING *progress ) {
|
|
struct efi_snp_device *snpdev =
|
|
container_of ( hii, struct efi_snp_device, hii );
|
|
wchar_t *pos;
|
|
int rc;
|
|
|
|
DBGC ( snpdev, "SNPDEV %p RouteConfig \"%ls\"\n", snpdev, config );
|
|
|
|
/* Process all request fragments */
|
|
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 EFIRC ( rc );
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Handle form actions
|
|
*
|
|
* @v hii HII configuration access protocol
|
|
* @v action Form browser action
|
|
* @v question_id Question ID
|
|
* @v type Type of value
|
|
* @v value Value
|
|
* @ret action_request Action requested by driver
|
|
* @ret efirc EFI status code
|
|
*/
|
|
static EFI_STATUS EFIAPI
|
|
efi_snp_hii_callback ( const EFI_HII_CONFIG_ACCESS_PROTOCOL *hii,
|
|
EFI_BROWSER_ACTION action __unused,
|
|
EFI_QUESTION_ID question_id __unused,
|
|
UINT8 type __unused, EFI_IFR_TYPE_VALUE *value __unused,
|
|
EFI_BROWSER_ACTION_REQUEST *action_request __unused ) {
|
|
struct efi_snp_device *snpdev =
|
|
container_of ( hii, struct efi_snp_device, hii );
|
|
|
|
DBGC ( snpdev, "SNPDEV %p Callback\n", snpdev );
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
/** HII configuration access protocol */
|
|
static EFI_HII_CONFIG_ACCESS_PROTOCOL efi_snp_device_hii = {
|
|
.ExtractConfig = efi_snp_hii_extract_config,
|
|
.RouteConfig = efi_snp_hii_route_config,
|
|
.Callback = efi_snp_hii_callback,
|
|
};
|
|
|
|
/**
|
|
* Install HII protocol and packages for SNP device
|
|
*
|
|
* @v snpdev SNP device
|
|
* @ret rc Return status code
|
|
*/
|
|
int efi_snp_hii_install ( struct efi_snp_device *snpdev ) {
|
|
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
|
|
int efirc;
|
|
int rc;
|
|
|
|
/* Do nothing if HII database protocol is not supported */
|
|
if ( ! efihii ) {
|
|
rc = -ENOTSUP;
|
|
goto err_no_hii;
|
|
}
|
|
|
|
/* Initialise HII protocol */
|
|
memcpy ( &snpdev->hii, &efi_snp_device_hii, sizeof ( snpdev->hii ) );
|
|
|
|
/* Create HII package list */
|
|
snpdev->package_list = efi_snp_hii_package_list ( snpdev );
|
|
if ( ! snpdev->package_list ) {
|
|
DBGC ( snpdev, "SNPDEV %p could not create HII package list\n",
|
|
snpdev );
|
|
rc = -ENOMEM;
|
|
goto err_build_package_list;
|
|
}
|
|
|
|
/* Add HII packages */
|
|
if ( ( efirc = efihii->NewPackageList ( efihii, snpdev->package_list,
|
|
snpdev->handle,
|
|
&snpdev->hii_handle ) ) != 0 ) {
|
|
rc = -EEFI ( efirc );
|
|
DBGC ( snpdev, "SNPDEV %p could not add HII packages: %s\n",
|
|
snpdev, strerror ( rc ) );
|
|
goto err_new_package_list;
|
|
}
|
|
|
|
/* Install HII protocol */
|
|
if ( ( efirc = bs->InstallMultipleProtocolInterfaces (
|
|
&snpdev->handle,
|
|
&efi_hii_config_access_protocol_guid, &snpdev->hii,
|
|
NULL ) ) != 0 ) {
|
|
rc = -EEFI ( efirc );
|
|
DBGC ( snpdev, "SNPDEV %p could not install HII protocol: %s\n",
|
|
snpdev, strerror ( rc ) );
|
|
goto err_install_protocol;
|
|
}
|
|
|
|
return 0;
|
|
|
|
bs->UninstallMultipleProtocolInterfaces (
|
|
snpdev->handle,
|
|
&efi_hii_config_access_protocol_guid, &snpdev->hii,
|
|
NULL );
|
|
err_install_protocol:
|
|
efihii->RemovePackageList ( efihii, snpdev->hii_handle );
|
|
err_new_package_list:
|
|
free ( snpdev->package_list );
|
|
snpdev->package_list = NULL;
|
|
err_build_package_list:
|
|
err_no_hii:
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Uninstall HII protocol and package for SNP device
|
|
*
|
|
* @v snpdev SNP device
|
|
*/
|
|
void efi_snp_hii_uninstall ( struct efi_snp_device *snpdev ) {
|
|
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
|
|
|
|
/* Do nothing if HII database protocol is not supported */
|
|
if ( ! efihii )
|
|
return;
|
|
|
|
/* Uninstall protocols and remove package list */
|
|
bs->UninstallMultipleProtocolInterfaces (
|
|
snpdev->handle,
|
|
&efi_hii_config_access_protocol_guid, &snpdev->hii,
|
|
NULL );
|
|
efihii->RemovePackageList ( efihii, snpdev->hii_handle );
|
|
free ( snpdev->package_list );
|
|
snpdev->package_list = NULL;
|
|
}
|