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
20 FILE_LICENCE ( GPL2_OR_LATER );
25 * EFI SNP HII protocol
27 * The HII protocols are some of the less-well designed parts of the
28 * entire EFI specification. This is a significant accomplishment.
30 * The face-slappingly ludicrous query string syntax seems to be
31 * motivated by the desire to allow a caller to query multiple drivers
32 * simultaneously via the single-instance HII_CONFIG_ROUTING_PROTOCOL,
33 * which is supposed to pass relevant subsets of the query string to
34 * the relevant drivers.
36 * Nobody uses the HII_CONFIG_ROUTING_PROTOCOL. Not even the EFI
37 * setup browser uses the HII_CONFIG_ROUTING_PROTOCOL. To the best of
38 * my knowledge, there has only ever been one implementation of the
39 * HII_CONFIG_ROUTING_PROTOCOL (as part of EDK2), and it just doesn't
40 * work. It's so badly broken that I can't even figure out what the
41 * code is _trying_ to do.
43 * Fundamentally, the problem seems to be that Javascript programmers
44 * should not be allowed to design APIs for C code.
53 #include <ipxe/settings.h>
55 #include <ipxe/device.h>
56 #include <ipxe/netdevice.h>
57 #include <ipxe/version.h>
58 #include <ipxe/efi/efi.h>
59 #include <ipxe/efi/efi_hii.h>
60 #include <ipxe/efi/efi_snp.h>
61 #include <ipxe/efi/efi_strings.h>
63 /** EFI platform setup formset GUID */
64 static EFI_GUID efi_hii_platform_setup_formset_guid
65 = EFI_HII_PLATFORM_SETUP_FORMSET_GUID;
67 /** EFI IBM UCM compliant formset GUID */
68 static EFI_GUID efi_hii_ibm_ucm_compliant_formset_guid
69 = EFI_HII_IBM_UCM_COMPLIANT_FORMSET_GUID;
71 /** EFI HII database protocol */
72 static EFI_HII_DATABASE_PROTOCOL *efihii;
73 EFI_REQUEST_PROTOCOL ( EFI_HII_DATABASE_PROTOCOL, &efihii );
76 * Identify settings to be exposed via HII
78 * @v snpdev SNP device
79 * @ret settings Settings, or NULL
81 static struct settings * efi_snp_hii_settings ( struct efi_snp_device *snpdev ){
83 return find_child_settings ( netdev_settings ( snpdev->netdev ),
88 * Check whether or not setting is applicable
90 * @v snpdev SNP device
92 * @ret applies Setting applies
94 static int efi_snp_hii_setting_applies ( struct efi_snp_device *snpdev,
95 struct setting *setting ) {
97 return nvo_applies ( efi_snp_hii_settings ( snpdev ), setting );
101 * Generate a random GUID
103 * @v guid GUID to fill in
105 static void efi_snp_hii_random_guid ( EFI_GUID *guid ) {
106 uint8_t *byte = ( ( uint8_t * ) guid );
109 for ( i = 0 ; i < sizeof ( *guid ) ; i++ )
110 *(byte++) = random();
114 * Generate EFI SNP questions
116 * @v snpdev SNP device
118 * @v varstore_id Variable store identifier
120 static void efi_snp_hii_questions ( struct efi_snp_device *snpdev,
121 struct efi_ifr_builder *ifr,
122 unsigned int varstore_id ) {
123 struct setting *setting;
124 struct setting *previous = NULL;
125 unsigned int name_id;
126 unsigned int prompt_id;
127 unsigned int help_id;
128 unsigned int question_id;
130 /* Add all applicable settings */
131 for_each_table_entry ( setting, SETTINGS ) {
132 if ( ! efi_snp_hii_setting_applies ( snpdev, setting ) )
134 if ( previous && ( setting_cmp ( setting, previous ) == 0 ) )
137 name_id = efi_ifr_string ( ifr, "%s", setting->name );
138 prompt_id = efi_ifr_string ( ifr, "%s", setting->description );
139 help_id = efi_ifr_string ( ifr, "http://ipxe.org/cfg/%s",
141 question_id = setting->tag;
142 efi_ifr_string_op ( ifr, prompt_id, help_id,
143 question_id, varstore_id, name_id,
149 * Build HII package list for SNP device
151 * @v snpdev SNP device
152 * @ret package Package list, or NULL on error
154 static EFI_HII_PACKAGE_LIST_HEADER *
155 efi_snp_hii_package_list ( struct efi_snp_device *snpdev ) {
156 struct net_device *netdev = snpdev->netdev;
157 struct device *dev = netdev->dev;
158 struct efi_ifr_builder ifr;
159 EFI_HII_PACKAGE_LIST_HEADER *package;
161 EFI_GUID package_guid;
162 EFI_GUID formset_guid;
163 EFI_GUID varstore_guid;
164 unsigned int title_id;
165 unsigned int varstore_id;
167 /* Initialise IFR builder */
168 efi_ifr_init ( &ifr );
170 /* Determine product name */
171 name = ( product_name[0] ? product_name : product_short_name );
174 efi_snp_hii_random_guid ( &package_guid );
175 efi_snp_hii_random_guid ( &formset_guid );
176 efi_snp_hii_random_guid ( &varstore_guid );
178 /* Generate title string (used more than once) */
179 title_id = efi_ifr_string ( &ifr, "%s (%s)", name,
180 netdev_addr ( netdev ) );
182 /* Generate opcodes */
183 efi_ifr_form_set_op ( &ifr, &formset_guid, title_id,
184 efi_ifr_string ( &ifr, "Configure %s",
185 product_short_name ),
186 &efi_hii_platform_setup_formset_guid,
187 &efi_hii_ibm_ucm_compliant_formset_guid, NULL );
188 efi_ifr_guid_class_op ( &ifr, EFI_NETWORK_DEVICE_CLASS );
189 efi_ifr_guid_subclass_op ( &ifr, 0x03 );
190 varstore_id = efi_ifr_varstore_name_value_op ( &ifr, &varstore_guid );
191 efi_ifr_form_op ( &ifr, title_id );
192 efi_ifr_text_op ( &ifr,
193 efi_ifr_string ( &ifr, "Name" ),
194 efi_ifr_string ( &ifr, "Firmware product name" ),
195 efi_ifr_string ( &ifr, "%s", name ) );
196 efi_ifr_text_op ( &ifr,
197 efi_ifr_string ( &ifr, "Version" ),
198 efi_ifr_string ( &ifr, "Firmware version" ),
199 efi_ifr_string ( &ifr, "%s", product_version ) );
200 efi_ifr_text_op ( &ifr,
201 efi_ifr_string ( &ifr, "Driver" ),
202 efi_ifr_string ( &ifr, "Firmware driver" ),
203 efi_ifr_string ( &ifr, "%s", dev->driver_name ) );
204 efi_ifr_text_op ( &ifr,
205 efi_ifr_string ( &ifr, "Device" ),
206 efi_ifr_string ( &ifr, "Hardware device" ),
207 efi_ifr_string ( &ifr, "%s", dev->name ) );
208 efi_snp_hii_questions ( snpdev, &ifr, varstore_id );
209 efi_ifr_end_op ( &ifr );
210 efi_ifr_end_op ( &ifr );
213 package = efi_ifr_package ( &ifr, &package_guid, "en-us",
214 efi_ifr_string ( &ifr, "English" ) );
216 DBGC ( snpdev, "SNPDEV %p could not build IFR package\n",
218 efi_ifr_free ( &ifr );
222 /* Free temporary storage */
223 efi_ifr_free ( &ifr );
228 * Append response to result string
230 * @v snpdev SNP device
233 * @v results Result string
234 * @ret rc Return status code
236 * The result string is allocated dynamically using
237 * BootServices::AllocatePool(), and the caller is responsible for
238 * eventually calling BootServices::FreePool().
240 static int efi_snp_hii_append ( struct efi_snp_device *snpdev __unused,
241 const char *key, const char *value,
242 wchar_t **results ) {
243 EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
247 /* Allocate new string */
248 len = ( ( *results ? ( wcslen ( *results ) + 1 /* "&" */ ) : 0 ) +
249 strlen ( key ) + 1 /* "=" */ + strlen ( value ) + 1 /* NUL */ );
250 bs->AllocatePool ( EfiBootServicesData, ( len * sizeof ( wchar_t ) ),
255 /* Populate string */
256 efi_snprintf ( new, len, "%ls%s%s=%s", ( *results ? *results : L"" ),
257 ( *results ? L"&" : L"" ), key, value );
258 bs->FreePool ( *results );
267 * @v snpdev SNP device
270 * @v results Result string
271 * @v have_setting Flag indicating detection of a setting
272 * @ret rc Return status code
274 static int efi_snp_hii_fetch ( struct efi_snp_device *snpdev,
275 const char *key, const char *value,
276 wchar_t **results, int *have_setting ) {
277 struct settings *settings = efi_snp_hii_settings ( snpdev );
278 struct settings *origin;
279 struct setting *setting;
280 struct setting fetched;
287 /* Handle ConfigHdr components */
288 if ( ( strcasecmp ( key, "GUID" ) == 0 ) ||
289 ( strcasecmp ( key, "NAME" ) == 0 ) ||
290 ( strcasecmp ( key, "PATH" ) == 0 ) ) {
291 return efi_snp_hii_append ( snpdev, key, value, results );
296 /* Do nothing more unless we have a settings block */
299 goto err_no_settings;
302 /* Identify setting */
303 setting = find_setting ( key );
305 DBGC ( snpdev, "SNPDEV %p no such setting \"%s\"\n",
308 goto err_find_setting;
312 if ( setting_exists ( settings, setting ) ) {
314 /* Calculate formatted length */
315 len = fetchf_setting ( settings, setting, &origin, &fetched,
319 DBGC ( snpdev, "SNPDEV %p could not fetch %s: %s\n",
320 snpdev, setting->name, strerror ( rc ) );
324 /* Allocate buffer for formatted value and HII-encoded value */
325 buf = zalloc ( len + 1 /* NUL */ + ( len * 4 ) + 1 /* NUL */ );
330 encoded = ( buf + len + 1 /* NUL */ );
333 fetchf_setting ( origin, &fetched, NULL, NULL, buf,
334 ( len + 1 /* NUL */ ) );
335 for ( i = 0 ; i < len ; i++ ) {
336 sprintf ( ( encoded + ( 4 * i ) ), "%04x",
337 *( ( uint8_t * ) buf + i ) );
342 /* Non-existent or inapplicable setting */
348 if ( ( rc = efi_snp_hii_append ( snpdev, key, encoded,
368 * @v snpdev SNP device
371 * @v results Result string (unused)
372 * @v have_setting Flag indicating detection of a setting (unused)
373 * @ret rc Return status code
375 static int efi_snp_hii_store ( struct efi_snp_device *snpdev,
376 const char *key, const char *value,
377 wchar_t **results __unused,
378 int *have_setting __unused ) {
379 struct settings *settings = efi_snp_hii_settings ( snpdev );
380 struct setting *setting;
388 /* Handle ConfigHdr components */
389 if ( ( strcasecmp ( key, "GUID" ) == 0 ) ||
390 ( strcasecmp ( key, "NAME" ) == 0 ) ||
391 ( strcasecmp ( key, "PATH" ) == 0 ) ) {
396 /* Do nothing more unless we have a settings block */
399 goto err_no_settings;
402 /* Identify setting */
403 setting = find_setting ( key );
405 DBGC ( snpdev, "SNPDEV %p no such setting \"%s\"\n",
408 goto err_find_setting;
411 /* Allocate buffer */
412 len = ( strlen ( value ) / 4 );
413 buf = zalloc ( len + 1 /* NUL */ );
421 for ( i = 0 ; i < len ; i++ ) {
422 memcpy ( tmp, ( value + ( i * 4 ) ), 4 );
423 buf[i] = strtoul ( tmp, &endp, 16 );
424 if ( endp != &tmp[4] ) {
425 DBGC ( snpdev, "SNPDEV %p invalid character %s\n",
433 if ( ( rc = storef_setting ( settings, setting, buf ) ) != 0 ) {
434 DBGC ( snpdev, "SNPDEV %p could not store \"%s\" into %s: %s\n",
435 snpdev, buf, setting->name, strerror ( rc ) );
452 * Process portion of HII configuration string
454 * @v snpdev SNP device
455 * @v string HII configuration string
456 * @v progress Progress through HII configuration string
457 * @v results Results string
458 * @v have_setting Flag indicating detection of a setting (unused)
459 * @v process Function used to process key=value pairs
460 * @ret rc Return status code
462 static int efi_snp_hii_process ( struct efi_snp_device *snpdev,
463 wchar_t *string, wchar_t **progress,
464 wchar_t **results, int *have_setting,
465 int ( * process ) ( struct efi_snp_device *,
469 int *have_setting ) ) {
470 wchar_t *wkey = string;
471 wchar_t *wend = string;
472 wchar_t *wvalue = NULL;
480 /* Locate key, value (if any), and end */
484 if ( *(wend++) == L'=' )
488 /* Allocate memory for key and value */
489 key_len = ( ( wvalue ? ( wvalue - 1 ) : wend ) - wkey );
490 value_len = ( wvalue ? ( wend - wvalue ) : 0 );
491 temp = zalloc ( key_len + 1 /* NUL */ + value_len + 1 /* NUL */ );
495 value = ( temp + key_len + 1 /* NUL */ );
497 /* Copy key and value */
499 key[key_len] = wkey[key_len];
500 while ( value_len-- )
501 value[value_len] = wvalue[value_len];
503 /* Process key and value */
504 if ( ( rc = process ( snpdev, key, value, results,
505 have_setting ) ) != 0 ) {
509 /* Update progress marker */
513 /* Free temporary storage */
520 * Fetch configuration
522 * @v hii HII configuration access protocol
523 * @v request Configuration to fetch
524 * @ret progress Progress made through configuration to fetch
525 * @ret results Query results
526 * @ret efirc EFI status code
528 static EFI_STATUS EFIAPI
529 efi_snp_hii_extract_config ( const EFI_HII_CONFIG_ACCESS_PROTOCOL *hii,
530 EFI_STRING request, EFI_STRING *progress,
531 EFI_STRING *results ) {
532 struct efi_snp_device *snpdev =
533 container_of ( hii, struct efi_snp_device, hii );
534 int have_setting = 0;
538 DBGC ( snpdev, "SNPDEV %p ExtractConfig request \"%ls\"\n",
541 /* Initialise results */
544 /* Process all request fragments */
545 for ( pos = *progress = request ; *progress && **progress ;
546 pos = *progress + 1 ) {
547 if ( ( rc = efi_snp_hii_process ( snpdev, pos, progress,
548 results, &have_setting,
549 efi_snp_hii_fetch ) ) != 0 ) {
554 /* If we have no explicit request, return all settings */
555 if ( ! have_setting ) {
556 struct setting *setting;
558 for_each_table_entry ( setting, SETTINGS ) {
559 if ( ! efi_snp_hii_setting_applies ( snpdev, setting ) )
561 if ( ( rc = efi_snp_hii_fetch ( snpdev, setting->name,
569 DBGC ( snpdev, "SNPDEV %p ExtractConfig results \"%ls\"\n",
575 * Store configuration
577 * @v hii HII configuration access protocol
578 * @v config Configuration to store
579 * @ret progress Progress made through configuration to store
580 * @ret efirc EFI status code
582 static EFI_STATUS EFIAPI
583 efi_snp_hii_route_config ( const EFI_HII_CONFIG_ACCESS_PROTOCOL *hii,
584 EFI_STRING config, EFI_STRING *progress ) {
585 struct efi_snp_device *snpdev =
586 container_of ( hii, struct efi_snp_device, hii );
590 DBGC ( snpdev, "SNPDEV %p RouteConfig \"%ls\"\n", snpdev, config );
592 /* Process all request fragments */
593 for ( pos = *progress = config ; *progress && **progress ;
594 pos = *progress + 1 ) {
595 if ( ( rc = efi_snp_hii_process ( snpdev, pos, progress,
597 efi_snp_hii_store ) ) != 0 ) {
606 * Handle form actions
608 * @v hii HII configuration access protocol
609 * @v action Form browser action
610 * @v question_id Question ID
611 * @v type Type of value
613 * @ret action_request Action requested by driver
614 * @ret efirc EFI status code
616 static EFI_STATUS EFIAPI
617 efi_snp_hii_callback ( const EFI_HII_CONFIG_ACCESS_PROTOCOL *hii,
618 EFI_BROWSER_ACTION action __unused,
619 EFI_QUESTION_ID question_id __unused,
620 UINT8 type __unused, EFI_IFR_TYPE_VALUE *value __unused,
621 EFI_BROWSER_ACTION_REQUEST *action_request __unused ) {
622 struct efi_snp_device *snpdev =
623 container_of ( hii, struct efi_snp_device, hii );
625 DBGC ( snpdev, "SNPDEV %p Callback\n", snpdev );
626 return EFI_UNSUPPORTED;
629 /** HII configuration access protocol */
630 static EFI_HII_CONFIG_ACCESS_PROTOCOL efi_snp_device_hii = {
631 .ExtractConfig = efi_snp_hii_extract_config,
632 .RouteConfig = efi_snp_hii_route_config,
633 .Callback = efi_snp_hii_callback,
637 * Install HII protocol and packages for SNP device
639 * @v snpdev SNP device
640 * @ret rc Return status code
642 int efi_snp_hii_install ( struct efi_snp_device *snpdev ) {
643 EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
647 /* Do nothing if HII database protocol is not supported */
653 /* Initialise HII protocol */
654 memcpy ( &snpdev->hii, &efi_snp_device_hii, sizeof ( snpdev->hii ) );
656 /* Create HII package list */
657 snpdev->package_list = efi_snp_hii_package_list ( snpdev );
658 if ( ! snpdev->package_list ) {
659 DBGC ( snpdev, "SNPDEV %p could not create HII package list\n",
662 goto err_build_package_list;
665 /* Add HII packages */
666 if ( ( efirc = efihii->NewPackageList ( efihii, snpdev->package_list,
668 &snpdev->hii_handle ) ) != 0 ) {
669 rc = -EEFI ( efirc );
670 DBGC ( snpdev, "SNPDEV %p could not add HII packages: %s\n",
671 snpdev, strerror ( rc ) );
672 goto err_new_package_list;
675 /* Install HII protocol */
676 if ( ( efirc = bs->InstallMultipleProtocolInterfaces (
678 &efi_hii_config_access_protocol_guid, &snpdev->hii,
680 rc = -EEFI ( efirc );
681 DBGC ( snpdev, "SNPDEV %p could not install HII protocol: %s\n",
682 snpdev, strerror ( rc ) );
683 goto err_install_protocol;
688 bs->UninstallMultipleProtocolInterfaces (
690 &efi_hii_config_access_protocol_guid, &snpdev->hii,
692 err_install_protocol:
693 efihii->RemovePackageList ( efihii, snpdev->hii_handle );
694 err_new_package_list:
695 free ( snpdev->package_list );
696 snpdev->package_list = NULL;
697 err_build_package_list:
703 * Uninstall HII protocol and package for SNP device
705 * @v snpdev SNP device
707 void efi_snp_hii_uninstall ( struct efi_snp_device *snpdev ) {
708 EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
710 /* Do nothing if HII database protocol is not supported */
714 /* Uninstall protocols and remove package list */
715 bs->UninstallMultipleProtocolInterfaces (
717 &efi_hii_config_access_protocol_guid, &snpdev->hii,
719 efihii->RemovePackageList ( efihii, snpdev->hii_handle );
720 free ( snpdev->package_list );
721 snpdev->package_list = NULL;