iPXE
efi_snp_hii.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>.
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License as
00006  * published by the Free Software Foundation; either version 2 of the
00007  * License, or any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00017  * 02110-1301, USA.
00018  *
00019  * You can also choose to distribute this program under the terms of
00020  * the Unmodified Binary Distribution Licence (as given in the file
00021  * COPYING.UBDL), provided that you have satisfied its requirements.
00022  */
00023 
00024 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
00025 
00026 /**
00027  * @file
00028  *
00029  * EFI SNP HII protocol
00030  *
00031  * The HII protocols are some of the less-well designed parts of the
00032  * entire EFI specification.  This is a significant accomplishment.
00033  *
00034  * The face-slappingly ludicrous query string syntax seems to be
00035  * motivated by the desire to allow a caller to query multiple drivers
00036  * simultaneously via the single-instance HII_CONFIG_ROUTING_PROTOCOL,
00037  * which is supposed to pass relevant subsets of the query string to
00038  * the relevant drivers.
00039  *
00040  * Nobody uses the HII_CONFIG_ROUTING_PROTOCOL.  Not even the EFI
00041  * setup browser uses the HII_CONFIG_ROUTING_PROTOCOL.  To the best of
00042  * my knowledge, there has only ever been one implementation of the
00043  * HII_CONFIG_ROUTING_PROTOCOL (as part of EDK2), and it just doesn't
00044  * work.  It's so badly broken that I can't even figure out what the
00045  * code is _trying_ to do.
00046  *
00047  * Fundamentally, the problem seems to be that Javascript programmers
00048  * should not be allowed to design APIs for C code.
00049  */
00050 
00051 #include <string.h>
00052 #include <strings.h>
00053 #include <stdlib.h>
00054 #include <stdio.h>
00055 #include <wchar.h>
00056 #include <errno.h>
00057 #include <ipxe/settings.h>
00058 #include <ipxe/nvo.h>
00059 #include <ipxe/device.h>
00060 #include <ipxe/netdevice.h>
00061 #include <ipxe/version.h>
00062 #include <ipxe/efi/efi.h>
00063 #include <ipxe/efi/efi_hii.h>
00064 #include <ipxe/efi/efi_snp.h>
00065 #include <ipxe/efi/efi_strings.h>
00066 #include <ipxe/efi/efi_utils.h>
00067 #include <config/branding.h>
00068 
00069 /** EFI platform setup formset GUID */
00070 static EFI_GUID efi_hii_platform_setup_formset_guid
00071         = EFI_HII_PLATFORM_SETUP_FORMSET_GUID;
00072 
00073 /** EFI IBM UCM compliant formset GUID */
00074 static EFI_GUID efi_hii_ibm_ucm_compliant_formset_guid
00075         = EFI_HII_IBM_UCM_COMPLIANT_FORMSET_GUID;
00076 
00077 /** EFI HII database protocol */
00078 static EFI_HII_DATABASE_PROTOCOL *efihii;
00079 EFI_REQUEST_PROTOCOL ( EFI_HII_DATABASE_PROTOCOL, &efihii );
00080 
00081 /**
00082  * Identify settings to be exposed via HII
00083  *
00084  * @v snpdev            SNP device
00085  * @ret settings        Settings, or NULL
00086  */
00087 static struct settings * efi_snp_hii_settings ( struct efi_snp_device *snpdev ){
00088 
00089         return find_child_settings ( netdev_settings ( snpdev->netdev ),
00090                                      NVO_SETTINGS_NAME );
00091 }
00092 
00093 /**
00094  * Check whether or not setting is applicable
00095  *
00096  * @v snpdev            SNP device
00097  * @v setting           Setting
00098  * @ret applies         Setting applies
00099  */
00100 static int efi_snp_hii_setting_applies ( struct efi_snp_device *snpdev,
00101                                          struct setting *setting ) {
00102 
00103         return nvo_applies ( efi_snp_hii_settings ( snpdev ), setting );
00104 }
00105 
00106 /**
00107  * Generate a random GUID
00108  *
00109  * @v guid              GUID to fill in
00110  */
00111 static void efi_snp_hii_random_guid ( EFI_GUID *guid ) {
00112         uint8_t *byte = ( ( uint8_t * ) guid );
00113         unsigned int i;
00114 
00115         for ( i = 0 ; i < sizeof ( *guid ) ; i++ )
00116                 *(byte++) = random();
00117 }
00118 
00119 /**
00120  * Generate EFI SNP questions
00121  *
00122  * @v snpdev            SNP device
00123  * @v ifr               IFR builder
00124  * @v varstore_id       Variable store identifier
00125  */
00126 static void efi_snp_hii_questions ( struct efi_snp_device *snpdev,
00127                                     struct efi_ifr_builder *ifr,
00128                                     unsigned int varstore_id ) {
00129         struct setting *setting;
00130         struct setting *previous = NULL;
00131         unsigned int name_id;
00132         unsigned int prompt_id;
00133         unsigned int help_id;
00134         unsigned int question_id;
00135 
00136         /* Add all applicable settings */
00137         for_each_table_entry ( setting, SETTINGS ) {
00138                 if ( ! efi_snp_hii_setting_applies ( snpdev, setting ) )
00139                         continue;
00140                 if ( previous && ( setting_cmp ( setting, previous ) == 0 ) )
00141                         continue;
00142                 previous = setting;
00143                 name_id = efi_ifr_string ( ifr, "%s", setting->name );
00144                 prompt_id = efi_ifr_string ( ifr, "%s", setting->description );
00145                 help_id = efi_ifr_string ( ifr, PRODUCT_SETTING_URI,
00146                                            setting->name );
00147                 question_id = setting->tag;
00148                 efi_ifr_string_op ( ifr, prompt_id, help_id,
00149                                     question_id, varstore_id, name_id,
00150                                     0, 0x00, 0xff, 0 );
00151         }
00152 }
00153 
00154 /**
00155  * Build HII package list for SNP device
00156  *
00157  * @v snpdev            SNP device
00158  * @ret package         Package list, or NULL on error
00159  */
00160 static EFI_HII_PACKAGE_LIST_HEADER *
00161 efi_snp_hii_package_list ( struct efi_snp_device *snpdev ) {
00162         struct net_device *netdev = snpdev->netdev;
00163         struct device *dev = netdev->dev;
00164         struct efi_ifr_builder ifr;
00165         EFI_HII_PACKAGE_LIST_HEADER *package;
00166         const char *name;
00167         EFI_GUID package_guid;
00168         EFI_GUID formset_guid;
00169         EFI_GUID varstore_guid;
00170         unsigned int title_id;
00171         unsigned int varstore_id;
00172 
00173         /* Initialise IFR builder */
00174         efi_ifr_init ( &ifr );
00175 
00176         /* Determine product name */
00177         name = ( product_name[0] ? product_name : product_short_name );
00178 
00179         /* Generate GUIDs */
00180         efi_snp_hii_random_guid ( &package_guid );
00181         efi_snp_hii_random_guid ( &formset_guid );
00182         efi_snp_hii_random_guid ( &varstore_guid );
00183 
00184         /* Generate title string (used more than once) */
00185         title_id = efi_ifr_string ( &ifr, "%s (%s)", name,
00186                                     netdev_addr ( netdev ) );
00187 
00188         /* Generate opcodes */
00189         efi_ifr_form_set_op ( &ifr, &formset_guid, title_id,
00190                               efi_ifr_string ( &ifr, "Configure %s",
00191                                                product_short_name ),
00192                               &efi_hii_platform_setup_formset_guid,
00193                               &efi_hii_ibm_ucm_compliant_formset_guid, NULL );
00194         efi_ifr_guid_class_op ( &ifr, EFI_NETWORK_DEVICE_CLASS );
00195         efi_ifr_guid_subclass_op ( &ifr, 0x03 );
00196         varstore_id = efi_ifr_varstore_name_value_op ( &ifr, &varstore_guid );
00197         efi_ifr_form_op ( &ifr, title_id );
00198         efi_ifr_text_op ( &ifr,
00199                           efi_ifr_string ( &ifr, "Name" ),
00200                           efi_ifr_string ( &ifr, "Firmware product name" ),
00201                           efi_ifr_string ( &ifr, "%s", name ) );
00202         efi_ifr_text_op ( &ifr,
00203                           efi_ifr_string ( &ifr, "Version" ),
00204                           efi_ifr_string ( &ifr, "Firmware version" ),
00205                           efi_ifr_string ( &ifr, "%s", product_version ) );
00206         efi_ifr_text_op ( &ifr,
00207                           efi_ifr_string ( &ifr, "Driver" ),
00208                           efi_ifr_string ( &ifr, "Firmware driver" ),
00209                           efi_ifr_string ( &ifr, "%s", dev->driver_name ) );
00210         efi_ifr_text_op ( &ifr,
00211                           efi_ifr_string ( &ifr, "Device" ),
00212                           efi_ifr_string ( &ifr, "Hardware device" ),
00213                           efi_ifr_string ( &ifr, "%s", dev->name ) );
00214         efi_snp_hii_questions ( snpdev, &ifr, varstore_id );
00215         efi_ifr_end_op ( &ifr );
00216         efi_ifr_end_op ( &ifr );
00217 
00218         /* Build package */
00219         package = efi_ifr_package ( &ifr, &package_guid, "en-us",
00220                                     efi_ifr_string ( &ifr, "English" ) );
00221         if ( ! package ) {
00222                 DBGC ( snpdev, "SNPDEV %p could not build IFR package\n",
00223                        snpdev );
00224                 efi_ifr_free ( &ifr );
00225                 return NULL;
00226         }
00227 
00228         /* Free temporary storage */
00229         efi_ifr_free ( &ifr );
00230         return package;
00231 }
00232 
00233 /**
00234  * Append response to result string
00235  *
00236  * @v snpdev            SNP device
00237  * @v key               Key
00238  * @v value             Value
00239  * @v results           Result string
00240  * @ret rc              Return status code
00241  *
00242  * The result string is allocated dynamically using
00243  * BootServices::AllocatePool(), and the caller is responsible for
00244  * eventually calling BootServices::FreePool().
00245  */
00246 static int efi_snp_hii_append ( struct efi_snp_device *snpdev __unused,
00247                                 const char *key, const char *value,
00248                                 wchar_t **results ) {
00249         EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
00250         size_t len;
00251         void *new;
00252 
00253         /* Allocate new string */
00254         len = ( ( *results ? ( wcslen ( *results ) + 1 /* "&" */ ) : 0 ) +
00255                 strlen ( key ) + 1 /* "=" */ + strlen ( value ) + 1 /* NUL */ );
00256         bs->AllocatePool ( EfiBootServicesData, ( len * sizeof ( wchar_t ) ),
00257                            &new );
00258         if ( ! new )
00259                 return -ENOMEM;
00260 
00261         /* Populate string */
00262         efi_snprintf ( new, len, "%ls%s%s=%s", ( *results ? *results : L"" ),
00263                        ( *results ? L"&" : L"" ), key, value );
00264         bs->FreePool ( *results );
00265         *results = new;
00266 
00267         return 0;
00268 }
00269 
00270 /**
00271  * Fetch HII setting
00272  *
00273  * @v snpdev            SNP device
00274  * @v key               Key
00275  * @v value             Value
00276  * @v results           Result string
00277  * @v have_setting      Flag indicating detection of a setting
00278  * @ret rc              Return status code
00279  */
00280 static int efi_snp_hii_fetch ( struct efi_snp_device *snpdev,
00281                                const char *key, const char *value,
00282                                wchar_t **results, int *have_setting ) {
00283         struct settings *settings = efi_snp_hii_settings ( snpdev );
00284         struct settings *origin;
00285         struct setting *setting;
00286         struct setting fetched;
00287         int len;
00288         char *buf;
00289         char *encoded;
00290         int i;
00291         int rc;
00292 
00293         /* Handle ConfigHdr components */
00294         if ( ( strcasecmp ( key, "GUID" ) == 0 ) ||
00295              ( strcasecmp ( key, "NAME" ) == 0 ) ||
00296              ( strcasecmp ( key, "PATH" ) == 0 ) ) {
00297                 return efi_snp_hii_append ( snpdev, key, value, results );
00298         }
00299         if ( have_setting )
00300                 *have_setting = 1;
00301 
00302         /* Do nothing more unless we have a settings block */
00303         if ( ! settings ) {
00304                 rc = -ENOTSUP;
00305                 goto err_no_settings;
00306         }
00307 
00308         /* Identify setting */
00309         setting = find_setting ( key );
00310         if ( ! setting ) {
00311                 DBGC ( snpdev, "SNPDEV %p no such setting \"%s\"\n",
00312                        snpdev, key );
00313                 rc = -ENODEV;
00314                 goto err_find_setting;
00315         }
00316 
00317         /* Encode value */
00318         if ( setting_exists ( settings, setting ) ) {
00319 
00320                 /* Calculate formatted length */
00321                 len = fetchf_setting ( settings, setting, &origin, &fetched,
00322                                        NULL, 0 );
00323                 if ( len < 0 ) {
00324                         rc = len;
00325                         DBGC ( snpdev, "SNPDEV %p could not fetch %s: %s\n",
00326                                snpdev, setting->name, strerror ( rc ) );
00327                         goto err_fetchf_len;
00328                 }
00329 
00330                 /* Allocate buffer for formatted value and HII-encoded value */
00331                 buf = zalloc ( len + 1 /* NUL */ + ( len * 4 ) + 1 /* NUL */ );
00332                 if ( ! buf ) {
00333                         rc = -ENOMEM;
00334                         goto err_alloc;
00335                 }
00336                 encoded = ( buf + len + 1 /* NUL */ );
00337 
00338                 /* Format value */
00339                 fetchf_setting ( origin, &fetched, NULL, NULL, buf,
00340                                  ( len + 1 /* NUL */ ) );
00341                 for ( i = 0 ; i < len ; i++ ) {
00342                         sprintf ( ( encoded + ( 4 * i ) ), "%04x",
00343                                   *( ( uint8_t * ) buf + i ) );
00344                 }
00345 
00346         } else {
00347 
00348                 /* Non-existent or inapplicable setting */
00349                 buf = NULL;
00350                 encoded = "";
00351         }
00352 
00353         /* Append results */
00354         if ( ( rc = efi_snp_hii_append ( snpdev, key, encoded,
00355                                          results ) ) != 0 ) {
00356                 goto err_append;
00357         }
00358 
00359         /* Success */
00360         rc = 0;
00361 
00362  err_append:
00363         free ( buf );
00364  err_alloc:
00365  err_fetchf_len:
00366  err_find_setting:
00367  err_no_settings:
00368         return rc;
00369 }
00370 
00371 /**
00372  * Fetch HII setting
00373  *
00374  * @v snpdev            SNP device
00375  * @v key               Key
00376  * @v value             Value
00377  * @v results           Result string (unused)
00378  * @v have_setting      Flag indicating detection of a setting (unused)
00379  * @ret rc              Return status code
00380  */
00381 static int efi_snp_hii_store ( struct efi_snp_device *snpdev,
00382                                const char *key, const char *value,
00383                                wchar_t **results __unused,
00384                                int *have_setting __unused ) {
00385         struct settings *settings = efi_snp_hii_settings ( snpdev );
00386         struct setting *setting;
00387         char *buf;
00388         char tmp[5];
00389         char *endp;
00390         int len;
00391         int i;
00392         int rc;
00393 
00394         /* Handle ConfigHdr components */
00395         if ( ( strcasecmp ( key, "GUID" ) == 0 ) ||
00396              ( strcasecmp ( key, "NAME" ) == 0 ) ||
00397              ( strcasecmp ( key, "PATH" ) == 0 ) ) {
00398                 /* Nothing to do */
00399                 return 0;
00400         }
00401 
00402         /* Do nothing more unless we have a settings block */
00403         if ( ! settings ) {
00404                 rc = -ENOTSUP;
00405                 goto err_no_settings;
00406         }
00407 
00408         /* Identify setting */
00409         setting = find_setting ( key );
00410         if ( ! setting ) {
00411                 DBGC ( snpdev, "SNPDEV %p no such setting \"%s\"\n",
00412                        snpdev, key );
00413                 rc = -ENODEV;
00414                 goto err_find_setting;
00415         }
00416 
00417         /* Allocate buffer */
00418         len = ( strlen ( value ) / 4 );
00419         buf = zalloc ( len + 1 /* NUL */ );
00420         if ( ! buf ) {
00421                 rc = -ENOMEM;
00422                 goto err_alloc;
00423         }
00424 
00425         /* Decode value */
00426         tmp[4] = '\0';
00427         for ( i = 0 ; i < len ; i++ ) {
00428                 memcpy ( tmp, ( value + ( i * 4 ) ), 4 );
00429                 buf[i] = strtoul ( tmp, &endp, 16 );
00430                 if ( endp != &tmp[4] ) {
00431                         DBGC ( snpdev, "SNPDEV %p invalid character %s\n",
00432                                snpdev, tmp );
00433                         rc = -EINVAL;
00434                         goto err_inval;
00435                 }
00436         }
00437 
00438         /* Store value */
00439         if ( ( rc = storef_setting ( settings, setting, buf ) ) != 0 ) {
00440                 DBGC ( snpdev, "SNPDEV %p could not store \"%s\" into %s: %s\n",
00441                        snpdev, buf, setting->name, strerror ( rc ) );
00442                 goto err_storef;
00443         }
00444 
00445         /* Success */
00446         rc = 0;
00447 
00448  err_storef:
00449  err_inval:
00450         free ( buf );
00451  err_alloc:
00452  err_find_setting:
00453  err_no_settings:
00454         return rc;
00455 }
00456 
00457 /**
00458  * Process portion of HII configuration string
00459  *
00460  * @v snpdev            SNP device
00461  * @v string            HII configuration string
00462  * @v progress          Progress through HII configuration string
00463  * @v results           Results string
00464  * @v have_setting      Flag indicating detection of a setting (unused)
00465  * @v process           Function used to process key=value pairs
00466  * @ret rc              Return status code
00467  */
00468 static int efi_snp_hii_process ( struct efi_snp_device *snpdev,
00469                                  wchar_t *string, wchar_t **progress,
00470                                  wchar_t **results, int *have_setting,
00471                                  int ( * process ) ( struct efi_snp_device *,
00472                                                      const char *key,
00473                                                      const char *value,
00474                                                      wchar_t **results,
00475                                                      int *have_setting ) ) {
00476         wchar_t *wkey = string;
00477         wchar_t *wend = string;
00478         wchar_t *wvalue = NULL;
00479         size_t key_len;
00480         size_t value_len;
00481         void *temp;
00482         char *key;
00483         char *value;
00484         int rc;
00485 
00486         /* Locate key, value (if any), and end */
00487         while ( *wend ) {
00488                 if ( *wend == L'&' )
00489                         break;
00490                 if ( *(wend++) == L'=' )
00491                         wvalue = wend;
00492         }
00493 
00494         /* Allocate memory for key and value */
00495         key_len = ( ( wvalue ? ( wvalue - 1 ) : wend ) - wkey );
00496         value_len = ( wvalue ? ( wend - wvalue ) : 0 );
00497         temp = zalloc ( key_len + 1 /* NUL */ + value_len + 1 /* NUL */ );
00498         if ( ! temp )
00499                 return -ENOMEM;
00500         key = temp;
00501         value = ( temp + key_len + 1 /* NUL */ );
00502 
00503         /* Copy key and value */
00504         while ( key_len-- )
00505                 key[key_len] = wkey[key_len];
00506         while ( value_len-- )
00507                 value[value_len] = wvalue[value_len];
00508 
00509         /* Process key and value */
00510         if ( ( rc = process ( snpdev, key, value, results,
00511                               have_setting ) ) != 0 ) {
00512                 goto err;
00513         }
00514 
00515         /* Update progress marker */
00516         *progress = wend;
00517 
00518  err:
00519         /* Free temporary storage */
00520         free ( temp );
00521 
00522         return rc;
00523 }
00524 
00525 /**
00526  * Fetch configuration
00527  *
00528  * @v hii               HII configuration access protocol
00529  * @v request           Configuration to fetch
00530  * @ret progress        Progress made through configuration to fetch
00531  * @ret results         Query results
00532  * @ret efirc           EFI status code
00533  */
00534 static EFI_STATUS EFIAPI
00535 efi_snp_hii_extract_config ( const EFI_HII_CONFIG_ACCESS_PROTOCOL *hii,
00536                              EFI_STRING request, EFI_STRING *progress,
00537                              EFI_STRING *results ) {
00538         struct efi_snp_device *snpdev =
00539                 container_of ( hii, struct efi_snp_device, hii );
00540         int have_setting = 0;
00541         wchar_t *pos;
00542         int rc;
00543 
00544         DBGC ( snpdev, "SNPDEV %p ExtractConfig request \"%ls\"\n",
00545                snpdev, request );
00546 
00547         /* Initialise results */
00548         *results = NULL;
00549 
00550         /* Work around apparently broken UEFI specification */
00551         if ( ! ( request && request[0] ) ) {
00552                 DBGC ( snpdev, "SNPDEV %p ExtractConfig ignoring malformed "
00553                        "request\n", snpdev );
00554                 return EFI_INVALID_PARAMETER;
00555         }
00556 
00557         /* Process all request fragments */
00558         for ( pos = *progress = request ; *progress && **progress ;
00559               pos = *progress + 1 ) {
00560                 if ( ( rc = efi_snp_hii_process ( snpdev, pos, progress,
00561                                                   results, &have_setting,
00562                                                   efi_snp_hii_fetch ) ) != 0 ) {
00563                         return EFIRC ( rc );
00564                 }
00565         }
00566 
00567         /* If we have no explicit request, return all settings */
00568         if ( ! have_setting ) {
00569                 struct setting *setting;
00570 
00571                 for_each_table_entry ( setting, SETTINGS ) {
00572                         if ( ! efi_snp_hii_setting_applies ( snpdev, setting ) )
00573                                 continue;
00574                         if ( ( rc = efi_snp_hii_fetch ( snpdev, setting->name,
00575                                                         NULL, results,
00576                                                         NULL ) ) != 0 ) {
00577                                 return EFIRC ( rc );
00578                         }
00579                 }
00580         }
00581 
00582         DBGC ( snpdev, "SNPDEV %p ExtractConfig results \"%ls\"\n",
00583                snpdev, *results );
00584         return 0;
00585 }
00586 
00587 /**
00588  * Store configuration
00589  *
00590  * @v hii               HII configuration access protocol
00591  * @v config            Configuration to store
00592  * @ret progress        Progress made through configuration to store
00593  * @ret efirc           EFI status code
00594  */
00595 static EFI_STATUS EFIAPI
00596 efi_snp_hii_route_config ( const EFI_HII_CONFIG_ACCESS_PROTOCOL *hii,
00597                            EFI_STRING config, EFI_STRING *progress ) {
00598         struct efi_snp_device *snpdev =
00599                 container_of ( hii, struct efi_snp_device, hii );
00600         wchar_t *pos;
00601         int rc;
00602 
00603         DBGC ( snpdev, "SNPDEV %p RouteConfig \"%ls\"\n", snpdev, config );
00604 
00605         /* Process all request fragments */
00606         for ( pos = *progress = config ; *progress && **progress ;
00607               pos = *progress + 1 ) {
00608                 if ( ( rc = efi_snp_hii_process ( snpdev, pos, progress,
00609                                                   NULL, NULL,
00610                                                   efi_snp_hii_store ) ) != 0 ) {
00611                         return EFIRC ( rc );
00612                 }
00613         }
00614 
00615         return 0;
00616 }
00617 
00618 /**
00619  * Handle form actions
00620  *
00621  * @v hii               HII configuration access protocol
00622  * @v action            Form browser action
00623  * @v question_id       Question ID
00624  * @v type              Type of value
00625  * @v value             Value
00626  * @ret action_request  Action requested by driver
00627  * @ret efirc           EFI status code
00628  */
00629 static EFI_STATUS EFIAPI
00630 efi_snp_hii_callback ( const EFI_HII_CONFIG_ACCESS_PROTOCOL *hii,
00631                        EFI_BROWSER_ACTION action __unused,
00632                        EFI_QUESTION_ID question_id __unused,
00633                        UINT8 type __unused, EFI_IFR_TYPE_VALUE *value __unused,
00634                        EFI_BROWSER_ACTION_REQUEST *action_request __unused ) {
00635         struct efi_snp_device *snpdev =
00636                 container_of ( hii, struct efi_snp_device, hii );
00637 
00638         DBGC ( snpdev, "SNPDEV %p Callback\n", snpdev );
00639         return EFI_UNSUPPORTED;
00640 }
00641 
00642 /** HII configuration access protocol */
00643 static EFI_HII_CONFIG_ACCESS_PROTOCOL efi_snp_device_hii = {
00644         .ExtractConfig  = efi_snp_hii_extract_config,
00645         .RouteConfig    = efi_snp_hii_route_config,
00646         .Callback       = efi_snp_hii_callback,
00647 };
00648 
00649 /**
00650  * Install HII protocol and packages for SNP device
00651  *
00652  * @v snpdev            SNP device
00653  * @ret rc              Return status code
00654  */
00655 int efi_snp_hii_install ( struct efi_snp_device *snpdev ) {
00656         EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
00657         VENDOR_DEVICE_PATH *vendor_path;
00658         EFI_DEVICE_PATH_PROTOCOL *path_end;
00659         size_t path_prefix_len;
00660         int efirc;
00661         int rc;
00662 
00663         /* Do nothing if HII database protocol is not supported */
00664         if ( ! efihii ) {
00665                 rc = -ENOTSUP;
00666                 goto err_no_hii;
00667         }
00668 
00669         /* Initialise HII protocol */
00670         memcpy ( &snpdev->hii, &efi_snp_device_hii, sizeof ( snpdev->hii ) );
00671 
00672         /* Create HII package list */
00673         snpdev->package_list = efi_snp_hii_package_list ( snpdev );
00674         if ( ! snpdev->package_list ) {
00675                 DBGC ( snpdev, "SNPDEV %p could not create HII package list\n",
00676                        snpdev );
00677                 rc = -ENOMEM;
00678                 goto err_build_package_list;
00679         }
00680 
00681         /* Allocate the new device path */
00682         path_prefix_len = efi_devpath_len ( snpdev->path );
00683         snpdev->hii_child_path = zalloc ( path_prefix_len +
00684                                           sizeof ( *vendor_path ) +
00685                                           sizeof ( *path_end ) );
00686         if ( ! snpdev->hii_child_path ) {
00687                 DBGC ( snpdev,
00688                        "SNPDEV %p could not allocate HII child device path\n",
00689                        snpdev );
00690                 rc = -ENOMEM;
00691                 goto err_alloc_child_path;
00692         }
00693 
00694         /* Populate the device path */
00695         memcpy ( snpdev->hii_child_path, snpdev->path, path_prefix_len );
00696         vendor_path = ( ( ( void * ) snpdev->hii_child_path ) +
00697                         path_prefix_len );
00698         vendor_path->Header.Type = HARDWARE_DEVICE_PATH;
00699         vendor_path->Header.SubType = HW_VENDOR_DP;
00700         vendor_path->Header.Length[0] = sizeof ( *vendor_path );
00701         efi_snp_hii_random_guid ( &vendor_path->Guid );
00702         path_end = ( ( void * ) ( vendor_path + 1 ) );
00703         path_end->Type = END_DEVICE_PATH_TYPE;
00704         path_end->SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE;
00705         path_end->Length[0] = sizeof ( *path_end );
00706 
00707         /* Create device path and child handle for HII association */
00708         if ( ( efirc = bs->InstallMultipleProtocolInterfaces (
00709                         &snpdev->hii_child_handle,
00710                         &efi_device_path_protocol_guid, snpdev->hii_child_path,
00711                         NULL ) ) != 0 ) {
00712                 rc = -EEFI ( efirc );
00713                 DBGC ( snpdev, "SNPDEV %p could not create HII child handle: "
00714                        "%s\n", snpdev, strerror ( rc ) );
00715                 goto err_hii_child_handle;
00716         }
00717 
00718         /* Add HII packages */
00719         if ( ( efirc = efihii->NewPackageList ( efihii, snpdev->package_list,
00720                                                 snpdev->hii_child_handle,
00721                                                 &snpdev->hii_handle ) ) != 0 ) {
00722                 rc = -EEFI ( efirc );
00723                 DBGC ( snpdev, "SNPDEV %p could not add HII packages: %s\n",
00724                        snpdev, strerror ( rc ) );
00725                 goto err_new_package_list;
00726         }
00727 
00728         /* Install HII protocol */
00729         if ( ( efirc = bs->InstallMultipleProtocolInterfaces (
00730                          &snpdev->hii_child_handle,
00731                          &efi_hii_config_access_protocol_guid, &snpdev->hii,
00732                          NULL ) ) != 0 ) {
00733                 rc = -EEFI ( efirc );
00734                 DBGC ( snpdev, "SNPDEV %p could not install HII protocol: %s\n",
00735                        snpdev, strerror ( rc ) );
00736                 goto err_install_protocol;
00737         }
00738 
00739         /* Add as child of handle with SNP instance */
00740         if ( ( rc = efi_child_add ( snpdev->handle,
00741                                     snpdev->hii_child_handle ) ) != 0 ) {
00742                 DBGC ( snpdev,
00743                        "SNPDEV %p could not adopt HII child handle: %s\n",
00744                        snpdev, strerror ( rc ) );
00745                 goto err_efi_child_add;
00746         }
00747 
00748         return 0;
00749 
00750         efi_child_del ( snpdev->handle, snpdev->hii_child_handle );
00751  err_efi_child_add:
00752         bs->UninstallMultipleProtocolInterfaces (
00753                         snpdev->hii_child_handle,
00754                         &efi_hii_config_access_protocol_guid, &snpdev->hii,
00755                         NULL );
00756  err_install_protocol:
00757         efihii->RemovePackageList ( efihii, snpdev->hii_handle );
00758  err_new_package_list:
00759         bs->UninstallMultipleProtocolInterfaces (
00760                         snpdev->hii_child_handle,
00761                         &efi_device_path_protocol_guid, snpdev->hii_child_path,
00762                         NULL );
00763  err_hii_child_handle:
00764         free ( snpdev->hii_child_path );
00765         snpdev->hii_child_path = NULL;
00766  err_alloc_child_path:
00767         free ( snpdev->package_list );
00768         snpdev->package_list = NULL;
00769  err_build_package_list:
00770  err_no_hii:
00771         return rc;
00772 }
00773 
00774 /**
00775  * Uninstall HII protocol and package for SNP device
00776  *
00777  * @v snpdev            SNP device
00778  */
00779 void efi_snp_hii_uninstall ( struct efi_snp_device *snpdev ) {
00780         EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
00781 
00782         /* Do nothing if HII database protocol is not supported */
00783         if ( ! efihii )
00784                 return;
00785 
00786         /* Uninstall protocols and remove package list */
00787         efi_child_del ( snpdev->handle, snpdev->hii_child_handle );
00788         bs->UninstallMultipleProtocolInterfaces (
00789                         snpdev->hii_child_handle,
00790                         &efi_hii_config_access_protocol_guid, &snpdev->hii,
00791                         NULL );
00792         efihii->RemovePackageList ( efihii, snpdev->hii_handle );
00793         bs->UninstallMultipleProtocolInterfaces (
00794                         snpdev->hii_child_handle,
00795                         &efi_device_path_protocol_guid, snpdev->hii_child_path,
00796                         NULL );
00797         free ( snpdev->hii_child_path );
00798         snpdev->hii_child_path = NULL;
00799         free ( snpdev->package_list );
00800         snpdev->package_list = NULL;
00801 }