2
* Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>.
4
* This program is free software; you can redistribute it and/or
5
* modify it under the terms of the GNU General Public License as
6
* published by the Free Software Foundation; either version 2 of the
7
* License, or any later version.
9
* This program is distributed in the hope that it will be useful, but
10
* WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
* General Public License for more details.
14
* You should have received a copy of the GNU General Public License
15
* along with this program; if not, write to the Free Software
16
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
* You can also choose to distribute this program under the terms of
20
* the Unmodified Binary Distribution Licence (as given in the file
21
* COPYING.UBDL), provided that you have satisfied its requirements.
24
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
29
* EFI SNP HII protocol
31
* The HII protocols are some of the less-well designed parts of the
32
* entire EFI specification. This is a significant accomplishment.
34
* The face-slappingly ludicrous query string syntax seems to be
35
* motivated by the desire to allow a caller to query multiple drivers
36
* simultaneously via the single-instance HII_CONFIG_ROUTING_PROTOCOL,
37
* which is supposed to pass relevant subsets of the query string to
38
* the relevant drivers.
40
* Nobody uses the HII_CONFIG_ROUTING_PROTOCOL. Not even the EFI
41
* setup browser uses the HII_CONFIG_ROUTING_PROTOCOL. To the best of
42
* my knowledge, there has only ever been one implementation of the
43
* HII_CONFIG_ROUTING_PROTOCOL (as part of EDK2), and it just doesn't
44
* work. It's so badly broken that I can't even figure out what the
45
* code is _trying_ to do.
47
* Fundamentally, the problem seems to be that Javascript programmers
48
* should not be allowed to design APIs for C code.
57
#include <ipxe/settings.h>
59
#include <ipxe/device.h>
60
#include <ipxe/netdevice.h>
61
#include <ipxe/version.h>
62
#include <ipxe/efi/efi.h>
63
#include <ipxe/efi/efi_hii.h>
64
#include <ipxe/efi/efi_snp.h>
65
#include <ipxe/efi/efi_strings.h>
66
#include <ipxe/efi/efi_utils.h>
67
#include <config/branding.h>
69
/** EFI platform setup formset GUID */
70
static EFI_GUID efi_hii_platform_setup_formset_guid
71
= EFI_HII_PLATFORM_SETUP_FORMSET_GUID;
73
/** EFI IBM UCM compliant formset GUID */
74
static EFI_GUID efi_hii_ibm_ucm_compliant_formset_guid
75
= EFI_HII_IBM_UCM_COMPLIANT_FORMSET_GUID;
77
/** EFI HII database protocol */
78
static EFI_HII_DATABASE_PROTOCOL *efihii;
79
EFI_REQUEST_PROTOCOL ( EFI_HII_DATABASE_PROTOCOL, &efihii );
82
* Identify settings to be exposed via HII
84
* @v snpdev SNP device
85
* @ret settings Settings, or NULL
87
static struct settings * efi_snp_hii_settings ( struct efi_snp_device *snpdev ){
89
return find_child_settings ( netdev_settings ( snpdev->netdev ),
94
* Check whether or not setting is applicable
96
* @v snpdev SNP device
98
* @ret applies Setting applies
100
static int efi_snp_hii_setting_applies ( struct efi_snp_device *snpdev,
101
struct setting *setting ) {
103
return nvo_applies ( efi_snp_hii_settings ( snpdev ), setting );
107
* Generate a random GUID
109
* @v guid GUID to fill in
111
static void efi_snp_hii_random_guid ( EFI_GUID *guid ) {
112
uint8_t *byte = ( ( uint8_t * ) guid );
115
for ( i = 0 ; i < sizeof ( *guid ) ; i++ )
116
*(byte++) = random();
120
* Generate EFI SNP questions
122
* @v snpdev SNP device
124
* @v varstore_id Variable store identifier
126
static void efi_snp_hii_questions ( struct efi_snp_device *snpdev,
127
struct efi_ifr_builder *ifr,
128
unsigned int varstore_id ) {
129
struct setting *setting;
130
struct setting *previous = NULL;
131
unsigned int name_id;
132
unsigned int prompt_id;
133
unsigned int help_id;
134
unsigned int question_id;
136
/* Add all applicable settings */
137
for_each_table_entry ( setting, SETTINGS ) {
138
if ( ! efi_snp_hii_setting_applies ( snpdev, setting ) )
140
if ( previous && ( setting_cmp ( setting, previous ) == 0 ) )
143
name_id = efi_ifr_string ( ifr, "%s", setting->name );
144
prompt_id = efi_ifr_string ( ifr, "%s", setting->description );
145
help_id = efi_ifr_string ( ifr, PRODUCT_SETTING_URI,
147
question_id = setting->tag;
148
efi_ifr_string_op ( ifr, prompt_id, help_id,
149
question_id, varstore_id, name_id,
155
* Build HII package list for SNP device
157
* @v snpdev SNP device
158
* @ret package Package list, or NULL on error
160
static EFI_HII_PACKAGE_LIST_HEADER *
161
efi_snp_hii_package_list ( struct efi_snp_device *snpdev ) {
162
struct net_device *netdev = snpdev->netdev;
163
struct device *dev = netdev->dev;
164
struct efi_ifr_builder ifr;
165
EFI_HII_PACKAGE_LIST_HEADER *package;
167
EFI_GUID package_guid;
168
EFI_GUID formset_guid;
169
EFI_GUID varstore_guid;
170
unsigned int title_id;
171
unsigned int varstore_id;
173
/* Initialise IFR builder */
174
efi_ifr_init ( &ifr );
176
/* Determine product name */
177
name = ( product_name[0] ? product_name : product_short_name );
180
efi_snp_hii_random_guid ( &package_guid );
181
efi_snp_hii_random_guid ( &formset_guid );
182
efi_snp_hii_random_guid ( &varstore_guid );
184
/* Generate title string (used more than once) */
185
title_id = efi_ifr_string ( &ifr, "%s (%s)", name,
186
netdev_addr ( netdev ) );
188
/* Generate opcodes */
189
efi_ifr_form_set_op ( &ifr, &formset_guid, title_id,
190
efi_ifr_string ( &ifr, "Configure %s",
191
product_short_name ),
192
&efi_hii_platform_setup_formset_guid,
193
&efi_hii_ibm_ucm_compliant_formset_guid, NULL );
194
efi_ifr_guid_class_op ( &ifr, EFI_NETWORK_DEVICE_CLASS );
195
efi_ifr_guid_subclass_op ( &ifr, 0x03 );
196
varstore_id = efi_ifr_varstore_name_value_op ( &ifr, &varstore_guid );
197
efi_ifr_form_op ( &ifr, title_id );
198
efi_ifr_text_op ( &ifr,
199
efi_ifr_string ( &ifr, "Name" ),
200
efi_ifr_string ( &ifr, "Firmware product name" ),
201
efi_ifr_string ( &ifr, "%s", name ) );
202
efi_ifr_text_op ( &ifr,
203
efi_ifr_string ( &ifr, "Version" ),
204
efi_ifr_string ( &ifr, "Firmware version" ),
205
efi_ifr_string ( &ifr, "%s", product_version ) );
206
efi_ifr_text_op ( &ifr,
207
efi_ifr_string ( &ifr, "Driver" ),
208
efi_ifr_string ( &ifr, "Firmware driver" ),
209
efi_ifr_string ( &ifr, "%s", dev->driver_name ) );
210
efi_ifr_text_op ( &ifr,
211
efi_ifr_string ( &ifr, "Device" ),
212
efi_ifr_string ( &ifr, "Hardware device" ),
213
efi_ifr_string ( &ifr, "%s", dev->name ) );
214
efi_snp_hii_questions ( snpdev, &ifr, varstore_id );
215
efi_ifr_end_op ( &ifr );
216
efi_ifr_end_op ( &ifr );
219
package = efi_ifr_package ( &ifr, &package_guid, "en-us",
220
efi_ifr_string ( &ifr, "English" ) );
222
DBGC ( snpdev, "SNPDEV %p could not build IFR package\n",
224
efi_ifr_free ( &ifr );
228
/* Free temporary storage */
229
efi_ifr_free ( &ifr );
234
* Append response to result string
236
* @v snpdev SNP device
239
* @v results Result string
240
* @ret rc Return status code
242
* The result string is allocated dynamically using
243
* BootServices::AllocatePool(), and the caller is responsible for
244
* eventually calling BootServices::FreePool().
246
static int efi_snp_hii_append ( struct efi_snp_device *snpdev __unused,
247
const char *key, const char *value,
248
wchar_t **results ) {
249
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
253
/* Allocate new string */
254
len = ( ( *results ? ( wcslen ( *results ) + 1 /* "&" */ ) : 0 ) +
255
strlen ( key ) + 1 /* "=" */ + strlen ( value ) + 1 /* NUL */ );
256
bs->AllocatePool ( EfiBootServicesData, ( len * sizeof ( wchar_t ) ),
261
/* Populate string */
262
efi_snprintf ( new, len, "%ls%s%s=%s", ( *results ? *results : L"" ),
263
( *results ? L"&" : L"" ), key, value );
264
bs->FreePool ( *results );
273
* @v snpdev SNP device
276
* @v results Result string
277
* @v have_setting Flag indicating detection of a setting
278
* @ret rc Return status code
280
static int efi_snp_hii_fetch ( struct efi_snp_device *snpdev,
281
const char *key, const char *value,
282
wchar_t **results, int *have_setting ) {
283
struct settings *settings = efi_snp_hii_settings ( snpdev );
284
struct settings *origin;
285
struct setting *setting;
286
struct setting fetched;
293
/* Handle ConfigHdr components */
294
if ( ( strcasecmp ( key, "GUID" ) == 0 ) ||
295
( strcasecmp ( key, "NAME" ) == 0 ) ||
296
( strcasecmp ( key, "PATH" ) == 0 ) ) {
297
return efi_snp_hii_append ( snpdev, key, value, results );
302
/* Do nothing more unless we have a settings block */
305
goto err_no_settings;
308
/* Identify setting */
309
setting = find_setting ( key );
311
DBGC ( snpdev, "SNPDEV %p no such setting \"%s\"\n",
314
goto err_find_setting;
318
if ( setting_exists ( settings, setting ) ) {
320
/* Calculate formatted length */
321
len = fetchf_setting ( settings, setting, &origin, &fetched,
325
DBGC ( snpdev, "SNPDEV %p could not fetch %s: %s\n",
326
snpdev, setting->name, strerror ( rc ) );
330
/* Allocate buffer for formatted value and HII-encoded value */
331
buf = zalloc ( len + 1 /* NUL */ + ( len * 4 ) + 1 /* NUL */ );
336
encoded = ( buf + len + 1 /* NUL */ );
339
fetchf_setting ( origin, &fetched, NULL, NULL, buf,
340
( len + 1 /* NUL */ ) );
341
for ( i = 0 ; i < len ; i++ ) {
342
sprintf ( ( encoded + ( 4 * i ) ), "%04x",
343
*( ( uint8_t * ) buf + i ) );
348
/* Non-existent or inapplicable setting */
354
if ( ( rc = efi_snp_hii_append ( snpdev, key, encoded,
374
* @v snpdev SNP device
377
* @v results Result string (unused)
378
* @v have_setting Flag indicating detection of a setting (unused)
379
* @ret rc Return status code
381
static int efi_snp_hii_store ( struct efi_snp_device *snpdev,
382
const char *key, const char *value,
383
wchar_t **results __unused,
384
int *have_setting __unused ) {
385
struct settings *settings = efi_snp_hii_settings ( snpdev );
386
struct setting *setting;
394
/* Handle ConfigHdr components */
395
if ( ( strcasecmp ( key, "GUID" ) == 0 ) ||
396
( strcasecmp ( key, "NAME" ) == 0 ) ||
397
( strcasecmp ( key, "PATH" ) == 0 ) ) {
402
/* Do nothing more unless we have a settings block */
405
goto err_no_settings;
408
/* Identify setting */
409
setting = find_setting ( key );
411
DBGC ( snpdev, "SNPDEV %p no such setting \"%s\"\n",
414
goto err_find_setting;
417
/* Allocate buffer */
418
len = ( strlen ( value ) / 4 );
419
buf = zalloc ( len + 1 /* NUL */ );
427
for ( i = 0 ; i < len ; i++ ) {
428
memcpy ( tmp, ( value + ( i * 4 ) ), 4 );
429
buf[i] = strtoul ( tmp, &endp, 16 );
430
if ( endp != &tmp[4] ) {
431
DBGC ( snpdev, "SNPDEV %p invalid character %s\n",
439
if ( ( rc = storef_setting ( settings, setting, buf ) ) != 0 ) {
440
DBGC ( snpdev, "SNPDEV %p could not store \"%s\" into %s: %s\n",
441
snpdev, buf, setting->name, strerror ( rc ) );
458
* Process portion of HII configuration string
460
* @v snpdev SNP device
461
* @v string HII configuration string
462
* @v progress Progress through HII configuration string
463
* @v results Results string
464
* @v have_setting Flag indicating detection of a setting (unused)
465
* @v process Function used to process key=value pairs
466
* @ret rc Return status code
468
static int efi_snp_hii_process ( struct efi_snp_device *snpdev,
469
wchar_t *string, wchar_t **progress,
470
wchar_t **results, int *have_setting,
471
int ( * process ) ( struct efi_snp_device *,
475
int *have_setting ) ) {
476
wchar_t *wkey = string;
477
wchar_t *wend = string;
478
wchar_t *wvalue = NULL;
486
/* Locate key, value (if any), and end */
490
if ( *(wend++) == L'=' )
494
/* Allocate memory for key and value */
495
key_len = ( ( wvalue ? ( wvalue - 1 ) : wend ) - wkey );
496
value_len = ( wvalue ? ( wend - wvalue ) : 0 );
497
temp = zalloc ( key_len + 1 /* NUL */ + value_len + 1 /* NUL */ );
501
value = ( temp + key_len + 1 /* NUL */ );
503
/* Copy key and value */
505
key[key_len] = wkey[key_len];
506
while ( value_len-- )
507
value[value_len] = wvalue[value_len];
509
/* Process key and value */
510
if ( ( rc = process ( snpdev, key, value, results,
511
have_setting ) ) != 0 ) {
515
/* Update progress marker */
519
/* Free temporary storage */
526
* Fetch configuration
528
* @v hii HII configuration access protocol
529
* @v request Configuration to fetch
530
* @ret progress Progress made through configuration to fetch
531
* @ret results Query results
532
* @ret efirc EFI status code
534
static EFI_STATUS EFIAPI
535
efi_snp_hii_extract_config ( const EFI_HII_CONFIG_ACCESS_PROTOCOL *hii,
536
EFI_STRING request, EFI_STRING *progress,
537
EFI_STRING *results ) {
538
struct efi_snp_device *snpdev =
539
container_of ( hii, struct efi_snp_device, hii );
540
int have_setting = 0;
544
DBGC ( snpdev, "SNPDEV %p ExtractConfig request \"%ls\"\n",
547
/* Initialise results */
550
/* Work around apparently broken UEFI specification */
551
if ( ! ( request && request[0] ) ) {
552
DBGC ( snpdev, "SNPDEV %p ExtractConfig ignoring malformed "
553
"request\n", snpdev );
554
return EFI_INVALID_PARAMETER;
557
/* Process all request fragments */
558
for ( pos = *progress = request ; *progress && **progress ;
559
pos = *progress + 1 ) {
560
if ( ( rc = efi_snp_hii_process ( snpdev, pos, progress,
561
results, &have_setting,
562
efi_snp_hii_fetch ) ) != 0 ) {
567
/* If we have no explicit request, return all settings */
568
if ( ! have_setting ) {
569
struct setting *setting;
571
for_each_table_entry ( setting, SETTINGS ) {
572
if ( ! efi_snp_hii_setting_applies ( snpdev, setting ) )
574
if ( ( rc = efi_snp_hii_fetch ( snpdev, setting->name,
582
DBGC ( snpdev, "SNPDEV %p ExtractConfig results \"%ls\"\n",
588
* Store configuration
590
* @v hii HII configuration access protocol
591
* @v config Configuration to store
592
* @ret progress Progress made through configuration to store
593
* @ret efirc EFI status code
595
static EFI_STATUS EFIAPI
596
efi_snp_hii_route_config ( const EFI_HII_CONFIG_ACCESS_PROTOCOL *hii,
597
EFI_STRING config, EFI_STRING *progress ) {
598
struct efi_snp_device *snpdev =
599
container_of ( hii, struct efi_snp_device, hii );
603
DBGC ( snpdev, "SNPDEV %p RouteConfig \"%ls\"\n", snpdev, config );
605
/* Process all request fragments */
606
for ( pos = *progress = config ; *progress && **progress ;
607
pos = *progress + 1 ) {
608
if ( ( rc = efi_snp_hii_process ( snpdev, pos, progress,
610
efi_snp_hii_store ) ) != 0 ) {
619
* Handle form actions
621
* @v hii HII configuration access protocol
622
* @v action Form browser action
623
* @v question_id Question ID
624
* @v type Type of value
626
* @ret action_request Action requested by driver
627
* @ret efirc EFI status code
629
static EFI_STATUS EFIAPI
630
efi_snp_hii_callback ( const EFI_HII_CONFIG_ACCESS_PROTOCOL *hii,
631
EFI_BROWSER_ACTION action __unused,
632
EFI_QUESTION_ID question_id __unused,
633
UINT8 type __unused, EFI_IFR_TYPE_VALUE *value __unused,
634
EFI_BROWSER_ACTION_REQUEST *action_request __unused ) {
635
struct efi_snp_device *snpdev =
636
container_of ( hii, struct efi_snp_device, hii );
638
DBGC ( snpdev, "SNPDEV %p Callback\n", snpdev );
639
return EFI_UNSUPPORTED;
642
/** HII configuration access protocol */
643
static EFI_HII_CONFIG_ACCESS_PROTOCOL efi_snp_device_hii = {
644
.ExtractConfig = efi_snp_hii_extract_config,
645
.RouteConfig = efi_snp_hii_route_config,
646
.Callback = efi_snp_hii_callback,
650
* Install HII protocol and packages for SNP device
652
* @v snpdev SNP device
653
* @ret rc Return status code
655
int efi_snp_hii_install ( struct efi_snp_device *snpdev ) {
656
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
657
VENDOR_DEVICE_PATH *vendor_path;
658
EFI_DEVICE_PATH_PROTOCOL *path_end;
659
size_t path_prefix_len;
663
/* Do nothing if HII database protocol is not supported */
669
/* Initialise HII protocol */
670
memcpy ( &snpdev->hii, &efi_snp_device_hii, sizeof ( snpdev->hii ) );
672
/* Create HII package list */
673
snpdev->package_list = efi_snp_hii_package_list ( snpdev );
674
if ( ! snpdev->package_list ) {
675
DBGC ( snpdev, "SNPDEV %p could not create HII package list\n",
678
goto err_build_package_list;
681
/* Allocate the new device path */
682
path_prefix_len = efi_devpath_len ( snpdev->path );
683
snpdev->hii_child_path = zalloc ( path_prefix_len +
684
sizeof ( *vendor_path ) +
685
sizeof ( *path_end ) );
686
if ( ! snpdev->hii_child_path ) {
688
"SNPDEV %p could not allocate HII child device path\n",
691
goto err_alloc_child_path;
694
/* Populate the device path */
695
memcpy ( snpdev->hii_child_path, snpdev->path, path_prefix_len );
696
vendor_path = ( ( ( void * ) snpdev->hii_child_path ) +
698
vendor_path->Header.Type = HARDWARE_DEVICE_PATH;
699
vendor_path->Header.SubType = HW_VENDOR_DP;
700
vendor_path->Header.Length[0] = sizeof ( *vendor_path );
701
efi_snp_hii_random_guid ( &vendor_path->Guid );
702
path_end = ( ( void * ) ( vendor_path + 1 ) );
703
path_end->Type = END_DEVICE_PATH_TYPE;
704
path_end->SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE;
705
path_end->Length[0] = sizeof ( *path_end );
707
/* Create device path and child handle for HII association */
708
if ( ( efirc = bs->InstallMultipleProtocolInterfaces (
709
&snpdev->hii_child_handle,
710
&efi_device_path_protocol_guid, snpdev->hii_child_path,
712
rc = -EEFI ( efirc );
713
DBGC ( snpdev, "SNPDEV %p could not create HII child handle: "
714
"%s\n", snpdev, strerror ( rc ) );
715
goto err_hii_child_handle;
718
/* Add HII packages */
719
if ( ( efirc = efihii->NewPackageList ( efihii, snpdev->package_list,
720
snpdev->hii_child_handle,
721
&snpdev->hii_handle ) ) != 0 ) {
722
rc = -EEFI ( efirc );
723
DBGC ( snpdev, "SNPDEV %p could not add HII packages: %s\n",
724
snpdev, strerror ( rc ) );
725
goto err_new_package_list;
728
/* Install HII protocol */
729
if ( ( efirc = bs->InstallMultipleProtocolInterfaces (
730
&snpdev->hii_child_handle,
731
&efi_hii_config_access_protocol_guid, &snpdev->hii,
733
rc = -EEFI ( efirc );
734
DBGC ( snpdev, "SNPDEV %p could not install HII protocol: %s\n",
735
snpdev, strerror ( rc ) );
736
goto err_install_protocol;
739
/* Add as child of handle with SNP instance */
740
if ( ( rc = efi_child_add ( snpdev->handle,
741
snpdev->hii_child_handle ) ) != 0 ) {
743
"SNPDEV %p could not adopt HII child handle: %s\n",
744
snpdev, strerror ( rc ) );
745
goto err_efi_child_add;
750
efi_child_del ( snpdev->handle, snpdev->hii_child_handle );
752
bs->UninstallMultipleProtocolInterfaces (
753
snpdev->hii_child_handle,
754
&efi_hii_config_access_protocol_guid, &snpdev->hii,
756
err_install_protocol:
757
efihii->RemovePackageList ( efihii, snpdev->hii_handle );
758
err_new_package_list:
759
bs->UninstallMultipleProtocolInterfaces (
760
snpdev->hii_child_handle,
761
&efi_device_path_protocol_guid, snpdev->hii_child_path,
763
err_hii_child_handle:
764
free ( snpdev->hii_child_path );
765
snpdev->hii_child_path = NULL;
766
err_alloc_child_path:
767
free ( snpdev->package_list );
768
snpdev->package_list = NULL;
769
err_build_package_list:
775
* Uninstall HII protocol and package for SNP device
777
* @v snpdev SNP device
779
void efi_snp_hii_uninstall ( struct efi_snp_device *snpdev ) {
780
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
782
/* Do nothing if HII database protocol is not supported */
786
/* Uninstall protocols and remove package list */
787
efi_child_del ( snpdev->handle, snpdev->hii_child_handle );
788
bs->UninstallMultipleProtocolInterfaces (
789
snpdev->hii_child_handle,
790
&efi_hii_config_access_protocol_guid, &snpdev->hii,
792
efihii->RemovePackageList ( efihii, snpdev->hii_handle );
793
bs->UninstallMultipleProtocolInterfaces (
794
snpdev->hii_child_handle,
795
&efi_device_path_protocol_guid, snpdev->hii_child_path,
797
free ( snpdev->hii_child_path );
798
snpdev->hii_child_path = NULL;
799
free ( snpdev->package_list );
800
snpdev->package_list = NULL;