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 <config/branding.h>
68 /** EFI platform setup formset GUID */
69 static EFI_GUID efi_hii_platform_setup_formset_guid
70 = EFI_HII_PLATFORM_SETUP_FORMSET_GUID;
72 /** EFI IBM UCM compliant formset GUID */
73 static EFI_GUID efi_hii_ibm_ucm_compliant_formset_guid
74 = EFI_HII_IBM_UCM_COMPLIANT_FORMSET_GUID;
76 /** EFI HII database protocol */
77 static EFI_HII_DATABASE_PROTOCOL *efihii;
78 EFI_REQUEST_PROTOCOL ( EFI_HII_DATABASE_PROTOCOL, &efihii );
81 * Identify settings to be exposed via HII
83 * @v snpdev SNP device
84 * @ret settings Settings, or NULL
86 static struct settings * efi_snp_hii_settings ( struct efi_snp_device *snpdev ){
88 return find_child_settings ( netdev_settings ( snpdev->netdev ),
93 * Check whether or not setting is applicable
95 * @v snpdev SNP device
97 * @ret applies Setting applies
99 static int efi_snp_hii_setting_applies ( struct efi_snp_device *snpdev,
100 struct setting *setting ) {
102 return nvo_applies ( efi_snp_hii_settings ( snpdev ), setting );
106 * Generate a random GUID
108 * @v guid GUID to fill in
110 static void efi_snp_hii_random_guid ( EFI_GUID *guid ) {
111 uint8_t *byte = ( ( uint8_t * ) guid );
114 for ( i = 0 ; i < sizeof ( *guid ) ; i++ )
115 *(byte++) = random();
119 * Generate EFI SNP questions
121 * @v snpdev SNP device
123 * @v varstore_id Variable store identifier
125 static void efi_snp_hii_questions ( struct efi_snp_device *snpdev,
126 struct efi_ifr_builder *ifr,
127 unsigned int varstore_id ) {
128 struct setting *setting;
129 struct setting *previous = NULL;
130 unsigned int name_id;
131 unsigned int prompt_id;
132 unsigned int help_id;
133 unsigned int question_id;
135 /* Add all applicable settings */
136 for_each_table_entry ( setting, SETTINGS ) {
137 if ( ! efi_snp_hii_setting_applies ( snpdev, setting ) )
139 if ( previous && ( setting_cmp ( setting, previous ) == 0 ) )
142 name_id = efi_ifr_string ( ifr, "%s", setting->name );
143 prompt_id = efi_ifr_string ( ifr, "%s", setting->description );
144 help_id = efi_ifr_string ( ifr, PRODUCT_SETTING_URI,
146 question_id = setting->tag;
147 efi_ifr_string_op ( ifr, prompt_id, help_id,
148 question_id, varstore_id, name_id,
154 * Build HII package list for SNP device
156 * @v snpdev SNP device
157 * @ret package Package list, or NULL on error
159 static EFI_HII_PACKAGE_LIST_HEADER *
160 efi_snp_hii_package_list ( struct efi_snp_device *snpdev ) {
161 struct net_device *netdev = snpdev->netdev;
162 struct device *dev = netdev->dev;
163 struct efi_ifr_builder ifr;
164 EFI_HII_PACKAGE_LIST_HEADER *package;
166 EFI_GUID package_guid;
167 EFI_GUID formset_guid;
168 EFI_GUID varstore_guid;
169 unsigned int title_id;
170 unsigned int varstore_id;
172 /* Initialise IFR builder */
173 efi_ifr_init ( &ifr );
175 /* Determine product name */
176 name = ( product_name[0] ? product_name : product_short_name );
179 efi_snp_hii_random_guid ( &package_guid );
180 efi_snp_hii_random_guid ( &formset_guid );
181 efi_snp_hii_random_guid ( &varstore_guid );
183 /* Generate title string (used more than once) */
184 title_id = efi_ifr_string ( &ifr, "%s (%s)", name,
185 netdev_addr ( netdev ) );
187 /* Generate opcodes */
188 efi_ifr_form_set_op ( &ifr, &formset_guid, title_id,
189 efi_ifr_string ( &ifr, "Configure %s",
190 product_short_name ),
191 &efi_hii_platform_setup_formset_guid,
192 &efi_hii_ibm_ucm_compliant_formset_guid, NULL );
193 efi_ifr_guid_class_op ( &ifr, EFI_NETWORK_DEVICE_CLASS );
194 efi_ifr_guid_subclass_op ( &ifr, 0x03 );
195 varstore_id = efi_ifr_varstore_name_value_op ( &ifr, &varstore_guid );
196 efi_ifr_form_op ( &ifr, title_id );
197 efi_ifr_text_op ( &ifr,
198 efi_ifr_string ( &ifr, "Name" ),
199 efi_ifr_string ( &ifr, "Firmware product name" ),
200 efi_ifr_string ( &ifr, "%s", name ) );
201 efi_ifr_text_op ( &ifr,
202 efi_ifr_string ( &ifr, "Version" ),
203 efi_ifr_string ( &ifr, "Firmware version" ),
204 efi_ifr_string ( &ifr, "%s", product_version ) );
205 efi_ifr_text_op ( &ifr,
206 efi_ifr_string ( &ifr, "Driver" ),
207 efi_ifr_string ( &ifr, "Firmware driver" ),
208 efi_ifr_string ( &ifr, "%s", dev->driver_name ) );
209 efi_ifr_text_op ( &ifr,
210 efi_ifr_string ( &ifr, "Device" ),
211 efi_ifr_string ( &ifr, "Hardware device" ),
212 efi_ifr_string ( &ifr, "%s", dev->name ) );
213 efi_snp_hii_questions ( snpdev, &ifr, varstore_id );
214 efi_ifr_end_op ( &ifr );
215 efi_ifr_end_op ( &ifr );
218 package = efi_ifr_package ( &ifr, &package_guid, "en-us",
219 efi_ifr_string ( &ifr, "English" ) );
221 DBGC ( snpdev, "SNPDEV %p could not build IFR package\n",
223 efi_ifr_free ( &ifr );
227 /* Free temporary storage */
228 efi_ifr_free ( &ifr );
233 * Append response to result string
235 * @v snpdev SNP device
238 * @v results Result string
239 * @ret rc Return status code
241 * The result string is allocated dynamically using
242 * BootServices::AllocatePool(), and the caller is responsible for
243 * eventually calling BootServices::FreePool().
245 static int efi_snp_hii_append ( struct efi_snp_device *snpdev __unused,
246 const char *key, const char *value,
247 wchar_t **results ) {
248 EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
252 /* Allocate new string */
253 len = ( ( *results ? ( wcslen ( *results ) + 1 /* "&" */ ) : 0 ) +
254 strlen ( key ) + 1 /* "=" */ + strlen ( value ) + 1 /* NUL */ );
255 bs->AllocatePool ( EfiBootServicesData, ( len * sizeof ( wchar_t ) ),
260 /* Populate string */
261 efi_snprintf ( new, len, "%ls%s%s=%s", ( *results ? *results : L"" ),
262 ( *results ? L"&" : L"" ), key, value );
263 bs->FreePool ( *results );
272 * @v snpdev SNP device
275 * @v results Result string
276 * @v have_setting Flag indicating detection of a setting
277 * @ret rc Return status code
279 static int efi_snp_hii_fetch ( struct efi_snp_device *snpdev,
280 const char *key, const char *value,
281 wchar_t **results, int *have_setting ) {
282 struct settings *settings = efi_snp_hii_settings ( snpdev );
283 struct settings *origin;
284 struct setting *setting;
285 struct setting fetched;
292 /* Handle ConfigHdr components */
293 if ( ( strcasecmp ( key, "GUID" ) == 0 ) ||
294 ( strcasecmp ( key, "NAME" ) == 0 ) ||
295 ( strcasecmp ( key, "PATH" ) == 0 ) ) {
296 return efi_snp_hii_append ( snpdev, key, value, results );
301 /* Do nothing more unless we have a settings block */
304 goto err_no_settings;
307 /* Identify setting */
308 setting = find_setting ( key );
310 DBGC ( snpdev, "SNPDEV %p no such setting \"%s\"\n",
313 goto err_find_setting;
317 if ( setting_exists ( settings, setting ) ) {
319 /* Calculate formatted length */
320 len = fetchf_setting ( settings, setting, &origin, &fetched,
324 DBGC ( snpdev, "SNPDEV %p could not fetch %s: %s\n",
325 snpdev, setting->name, strerror ( rc ) );
329 /* Allocate buffer for formatted value and HII-encoded value */
330 buf = zalloc ( len + 1 /* NUL */ + ( len * 4 ) + 1 /* NUL */ );
335 encoded = ( buf + len + 1 /* NUL */ );
338 fetchf_setting ( origin, &fetched, NULL, NULL, buf,
339 ( len + 1 /* NUL */ ) );
340 for ( i = 0 ; i < len ; i++ ) {
341 sprintf ( ( encoded + ( 4 * i ) ), "%04x",
342 *( ( uint8_t * ) buf + i ) );
347 /* Non-existent or inapplicable setting */
353 if ( ( rc = efi_snp_hii_append ( snpdev, key, encoded,
373 * @v snpdev SNP device
376 * @v results Result string (unused)
377 * @v have_setting Flag indicating detection of a setting (unused)
378 * @ret rc Return status code
380 static int efi_snp_hii_store ( struct efi_snp_device *snpdev,
381 const char *key, const char *value,
382 wchar_t **results __unused,
383 int *have_setting __unused ) {
384 struct settings *settings = efi_snp_hii_settings ( snpdev );
385 struct setting *setting;
393 /* Handle ConfigHdr components */
394 if ( ( strcasecmp ( key, "GUID" ) == 0 ) ||
395 ( strcasecmp ( key, "NAME" ) == 0 ) ||
396 ( strcasecmp ( key, "PATH" ) == 0 ) ) {
401 /* Do nothing more unless we have a settings block */
404 goto err_no_settings;
407 /* Identify setting */
408 setting = find_setting ( key );
410 DBGC ( snpdev, "SNPDEV %p no such setting \"%s\"\n",
413 goto err_find_setting;
416 /* Allocate buffer */
417 len = ( strlen ( value ) / 4 );
418 buf = zalloc ( len + 1 /* NUL */ );
426 for ( i = 0 ; i < len ; i++ ) {
427 memcpy ( tmp, ( value + ( i * 4 ) ), 4 );
428 buf[i] = strtoul ( tmp, &endp, 16 );
429 if ( endp != &tmp[4] ) {
430 DBGC ( snpdev, "SNPDEV %p invalid character %s\n",
438 if ( ( rc = storef_setting ( settings, setting, buf ) ) != 0 ) {
439 DBGC ( snpdev, "SNPDEV %p could not store \"%s\" into %s: %s\n",
440 snpdev, buf, setting->name, strerror ( rc ) );
457 * Process portion of HII configuration string
459 * @v snpdev SNP device
460 * @v string HII configuration string
461 * @v progress Progress through HII configuration string
462 * @v results Results string
463 * @v have_setting Flag indicating detection of a setting (unused)
464 * @v process Function used to process key=value pairs
465 * @ret rc Return status code
467 static int efi_snp_hii_process ( struct efi_snp_device *snpdev,
468 wchar_t *string, wchar_t **progress,
469 wchar_t **results, int *have_setting,
470 int ( * process ) ( struct efi_snp_device *,
474 int *have_setting ) ) {
475 wchar_t *wkey = string;
476 wchar_t *wend = string;
477 wchar_t *wvalue = NULL;
485 /* Locate key, value (if any), and end */
489 if ( *(wend++) == L'=' )
493 /* Allocate memory for key and value */
494 key_len = ( ( wvalue ? ( wvalue - 1 ) : wend ) - wkey );
495 value_len = ( wvalue ? ( wend - wvalue ) : 0 );
496 temp = zalloc ( key_len + 1 /* NUL */ + value_len + 1 /* NUL */ );
500 value = ( temp + key_len + 1 /* NUL */ );
502 /* Copy key and value */
504 key[key_len] = wkey[key_len];
505 while ( value_len-- )
506 value[value_len] = wvalue[value_len];
508 /* Process key and value */
509 if ( ( rc = process ( snpdev, key, value, results,
510 have_setting ) ) != 0 ) {
514 /* Update progress marker */
518 /* Free temporary storage */
525 * Fetch configuration
527 * @v hii HII configuration access protocol
528 * @v request Configuration to fetch
529 * @ret progress Progress made through configuration to fetch
530 * @ret results Query results
531 * @ret efirc EFI status code
533 static EFI_STATUS EFIAPI
534 efi_snp_hii_extract_config ( const EFI_HII_CONFIG_ACCESS_PROTOCOL *hii,
535 EFI_STRING request, EFI_STRING *progress,
536 EFI_STRING *results ) {
537 struct efi_snp_device *snpdev =
538 container_of ( hii, struct efi_snp_device, hii );
539 int have_setting = 0;
543 DBGC ( snpdev, "SNPDEV %p ExtractConfig request \"%ls\"\n",
546 /* Initialise results */
549 /* Process all request fragments */
550 for ( pos = *progress = request ; *progress && **progress ;
551 pos = *progress + 1 ) {
552 if ( ( rc = efi_snp_hii_process ( snpdev, pos, progress,
553 results, &have_setting,
554 efi_snp_hii_fetch ) ) != 0 ) {
559 /* If we have no explicit request, return all settings */
560 if ( ! have_setting ) {
561 struct setting *setting;
563 for_each_table_entry ( setting, SETTINGS ) {
564 if ( ! efi_snp_hii_setting_applies ( snpdev, setting ) )
566 if ( ( rc = efi_snp_hii_fetch ( snpdev, setting->name,
574 DBGC ( snpdev, "SNPDEV %p ExtractConfig results \"%ls\"\n",
580 * Store configuration
582 * @v hii HII configuration access protocol
583 * @v config Configuration to store
584 * @ret progress Progress made through configuration to store
585 * @ret efirc EFI status code
587 static EFI_STATUS EFIAPI
588 efi_snp_hii_route_config ( const EFI_HII_CONFIG_ACCESS_PROTOCOL *hii,
589 EFI_STRING config, EFI_STRING *progress ) {
590 struct efi_snp_device *snpdev =
591 container_of ( hii, struct efi_snp_device, hii );
595 DBGC ( snpdev, "SNPDEV %p RouteConfig \"%ls\"\n", snpdev, config );
597 /* Process all request fragments */
598 for ( pos = *progress = config ; *progress && **progress ;
599 pos = *progress + 1 ) {
600 if ( ( rc = efi_snp_hii_process ( snpdev, pos, progress,
602 efi_snp_hii_store ) ) != 0 ) {
611 * Handle form actions
613 * @v hii HII configuration access protocol
614 * @v action Form browser action
615 * @v question_id Question ID
616 * @v type Type of value
618 * @ret action_request Action requested by driver
619 * @ret efirc EFI status code
621 static EFI_STATUS EFIAPI
622 efi_snp_hii_callback ( const EFI_HII_CONFIG_ACCESS_PROTOCOL *hii,
623 EFI_BROWSER_ACTION action __unused,
624 EFI_QUESTION_ID question_id __unused,
625 UINT8 type __unused, EFI_IFR_TYPE_VALUE *value __unused,
626 EFI_BROWSER_ACTION_REQUEST *action_request __unused ) {
627 struct efi_snp_device *snpdev =
628 container_of ( hii, struct efi_snp_device, hii );
630 DBGC ( snpdev, "SNPDEV %p Callback\n", snpdev );
631 return EFI_UNSUPPORTED;
634 /** HII configuration access protocol */
635 static EFI_HII_CONFIG_ACCESS_PROTOCOL efi_snp_device_hii = {
636 .ExtractConfig = efi_snp_hii_extract_config,
637 .RouteConfig = efi_snp_hii_route_config,
638 .Callback = efi_snp_hii_callback,
642 * Install HII protocol and packages for SNP device
644 * @v snpdev SNP device
645 * @ret rc Return status code
647 int efi_snp_hii_install ( struct efi_snp_device *snpdev ) {
648 EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
652 /* Do nothing if HII database protocol is not supported */
658 /* Initialise HII protocol */
659 memcpy ( &snpdev->hii, &efi_snp_device_hii, sizeof ( snpdev->hii ) );
661 /* Create HII package list */
662 snpdev->package_list = efi_snp_hii_package_list ( snpdev );
663 if ( ! snpdev->package_list ) {
664 DBGC ( snpdev, "SNPDEV %p could not create HII package list\n",
667 goto err_build_package_list;
670 /* Add HII packages */
671 if ( ( efirc = efihii->NewPackageList ( efihii, snpdev->package_list,
673 &snpdev->hii_handle ) ) != 0 ) {
674 rc = -EEFI ( efirc );
675 DBGC ( snpdev, "SNPDEV %p could not add HII packages: %s\n",
676 snpdev, strerror ( rc ) );
677 goto err_new_package_list;
680 /* Install HII protocol */
681 if ( ( efirc = bs->InstallMultipleProtocolInterfaces (
683 &efi_hii_config_access_protocol_guid, &snpdev->hii,
685 rc = -EEFI ( efirc );
686 DBGC ( snpdev, "SNPDEV %p could not install HII protocol: %s\n",
687 snpdev, strerror ( rc ) );
688 goto err_install_protocol;
693 bs->UninstallMultipleProtocolInterfaces (
695 &efi_hii_config_access_protocol_guid, &snpdev->hii,
697 err_install_protocol:
698 efihii->RemovePackageList ( efihii, snpdev->hii_handle );
699 err_new_package_list:
700 free ( snpdev->package_list );
701 snpdev->package_list = NULL;
702 err_build_package_list:
708 * Uninstall HII protocol and package for SNP device
710 * @v snpdev SNP device
712 void efi_snp_hii_uninstall ( struct efi_snp_device *snpdev ) {
713 EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
715 /* Do nothing if HII database protocol is not supported */
719 /* Uninstall protocols and remove package list */
720 bs->UninstallMultipleProtocolInterfaces (
722 &efi_hii_config_access_protocol_guid, &snpdev->hii,
724 efihii->RemovePackageList ( efihii, snpdev->hii_handle );
725 free ( snpdev->package_list );
726 snpdev->package_list = NULL;