Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / ipxe / src / interface / efi / efi_snp_hii.c
1 /*
2  * Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>.
3  *
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.
8  *
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.
13  *
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
17  * 02110-1301, USA.
18  */
19
20 FILE_LICENCE ( GPL2_OR_LATER );
21
22 /**
23  * @file
24  *
25  * EFI SNP HII protocol
26  *
27  * The HII protocols are some of the less-well designed parts of the
28  * entire EFI specification.  This is a significant accomplishment.
29  *
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.
35  *
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.
42  *
43  * Fundamentally, the problem seems to be that Javascript programmers
44  * should not be allowed to design APIs for C code.
45  */
46
47 #include <string.h>
48 #include <strings.h>
49 #include <stdlib.h>
50 #include <stdio.h>
51 #include <wchar.h>
52 #include <errno.h>
53 #include <ipxe/settings.h>
54 #include <ipxe/nvo.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>
62
63 /** EFI platform setup formset GUID */
64 static EFI_GUID efi_hii_platform_setup_formset_guid
65         = EFI_HII_PLATFORM_SETUP_FORMSET_GUID;
66
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;
70
71 /** EFI HII database protocol */
72 static EFI_HII_DATABASE_PROTOCOL *efihii;
73 EFI_REQUEST_PROTOCOL ( EFI_HII_DATABASE_PROTOCOL, &efihii );
74
75 /**
76  * Identify settings to be exposed via HII
77  *
78  * @v snpdev            SNP device
79  * @ret settings        Settings, or NULL
80  */
81 static struct settings * efi_snp_hii_settings ( struct efi_snp_device *snpdev ){
82
83         return find_child_settings ( netdev_settings ( snpdev->netdev ),
84                                      NVO_SETTINGS_NAME );
85 }
86
87 /**
88  * Check whether or not setting is applicable
89  *
90  * @v snpdev            SNP device
91  * @v setting           Setting
92  * @ret applies         Setting applies
93  */
94 static int efi_snp_hii_setting_applies ( struct efi_snp_device *snpdev,
95                                          struct setting *setting ) {
96
97         return nvo_applies ( efi_snp_hii_settings ( snpdev ), setting );
98 }
99
100 /**
101  * Generate a random GUID
102  *
103  * @v guid              GUID to fill in
104  */
105 static void efi_snp_hii_random_guid ( EFI_GUID *guid ) {
106         uint8_t *byte = ( ( uint8_t * ) guid );
107         unsigned int i;
108
109         for ( i = 0 ; i < sizeof ( *guid ) ; i++ )
110                 *(byte++) = random();
111 }
112
113 /**
114  * Generate EFI SNP questions
115  *
116  * @v snpdev            SNP device
117  * @v ifr               IFR builder
118  * @v varstore_id       Variable store identifier
119  */
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;
129
130         /* Add all applicable settings */
131         for_each_table_entry ( setting, SETTINGS ) {
132                 if ( ! efi_snp_hii_setting_applies ( snpdev, setting ) )
133                         continue;
134                 if ( previous && ( setting_cmp ( setting, previous ) == 0 ) )
135                         continue;
136                 previous = setting;
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",
140                                            setting->name );
141                 question_id = setting->tag;
142                 efi_ifr_string_op ( ifr, prompt_id, help_id,
143                                     question_id, varstore_id, name_id,
144                                     0, 0x00, 0xff, 0 );
145         }
146 }
147
148 /**
149  * Build HII package list for SNP device
150  *
151  * @v snpdev            SNP device
152  * @ret package         Package list, or NULL on error
153  */
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;
160         const char *name;
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;
166
167         /* Initialise IFR builder */
168         efi_ifr_init ( &ifr );
169
170         /* Determine product name */
171         name = ( product_name[0] ? product_name : product_short_name );
172
173         /* Generate GUIDs */
174         efi_snp_hii_random_guid ( &package_guid );
175         efi_snp_hii_random_guid ( &formset_guid );
176         efi_snp_hii_random_guid ( &varstore_guid );
177
178         /* Generate title string (used more than once) */
179         title_id = efi_ifr_string ( &ifr, "%s (%s)", name,
180                                     netdev_addr ( netdev ) );
181
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 );
211
212         /* Build package */
213         package = efi_ifr_package ( &ifr, &package_guid, "en-us",
214                                     efi_ifr_string ( &ifr, "English" ) );
215         if ( ! package ) {
216                 DBGC ( snpdev, "SNPDEV %p could not build IFR package\n",
217                        snpdev );
218                 efi_ifr_free ( &ifr );
219                 return NULL;
220         }
221
222         /* Free temporary storage */
223         efi_ifr_free ( &ifr );
224         return package;
225 }
226
227 /**
228  * Append response to result string
229  *
230  * @v snpdev            SNP device
231  * @v key               Key
232  * @v value             Value
233  * @v results           Result string
234  * @ret rc              Return status code
235  *
236  * The result string is allocated dynamically using
237  * BootServices::AllocatePool(), and the caller is responsible for
238  * eventually calling BootServices::FreePool().
239  */
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;
244         size_t len;
245         void *new;
246
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 ) ),
251                            &new );
252         if ( ! new )
253                 return -ENOMEM;
254
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 );
259         *results = new;
260
261         return 0;
262 }
263
264 /**
265  * Fetch HII setting
266  *
267  * @v snpdev            SNP device
268  * @v key               Key
269  * @v value             Value
270  * @v results           Result string
271  * @v have_setting      Flag indicating detection of a setting
272  * @ret rc              Return status code
273  */
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;
281         int len;
282         char *buf;
283         char *encoded;
284         int i;
285         int rc;
286
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 );
292         }
293         if ( have_setting )
294                 *have_setting = 1;
295
296         /* Do nothing more unless we have a settings block */
297         if ( ! settings ) {
298                 rc = -ENOTSUP;
299                 goto err_no_settings;
300         }
301
302         /* Identify setting */
303         setting = find_setting ( key );
304         if ( ! setting ) {
305                 DBGC ( snpdev, "SNPDEV %p no such setting \"%s\"\n",
306                        snpdev, key );
307                 rc = -ENODEV;
308                 goto err_find_setting;
309         }
310
311         /* Encode value */
312         if ( setting_exists ( settings, setting ) ) {
313
314                 /* Calculate formatted length */
315                 len = fetchf_setting ( settings, setting, &origin, &fetched,
316                                        NULL, 0 );
317                 if ( len < 0 ) {
318                         rc = len;
319                         DBGC ( snpdev, "SNPDEV %p could not fetch %s: %s\n",
320                                snpdev, setting->name, strerror ( rc ) );
321                         goto err_fetchf_len;
322                 }
323
324                 /* Allocate buffer for formatted value and HII-encoded value */
325                 buf = zalloc ( len + 1 /* NUL */ + ( len * 4 ) + 1 /* NUL */ );
326                 if ( ! buf ) {
327                         rc = -ENOMEM;
328                         goto err_alloc;
329                 }
330                 encoded = ( buf + len + 1 /* NUL */ );
331
332                 /* Format value */
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 ) );
338                 }
339
340         } else {
341
342                 /* Non-existent or inapplicable setting */
343                 buf = NULL;
344                 encoded = "";
345         }
346
347         /* Append results */
348         if ( ( rc = efi_snp_hii_append ( snpdev, key, encoded,
349                                          results ) ) != 0 ) {
350                 goto err_append;
351         }
352
353         /* Success */
354         rc = 0;
355
356  err_append:
357         free ( buf );
358  err_alloc:
359  err_fetchf_len:
360  err_find_setting:
361  err_no_settings:
362         return rc;
363 }
364
365 /**
366  * Fetch HII setting
367  *
368  * @v snpdev            SNP device
369  * @v key               Key
370  * @v value             Value
371  * @v results           Result string (unused)
372  * @v have_setting      Flag indicating detection of a setting (unused)
373  * @ret rc              Return status code
374  */
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;
381         char *buf;
382         char tmp[5];
383         char *endp;
384         int len;
385         int i;
386         int rc;
387
388         /* Handle ConfigHdr components */
389         if ( ( strcasecmp ( key, "GUID" ) == 0 ) ||
390              ( strcasecmp ( key, "NAME" ) == 0 ) ||
391              ( strcasecmp ( key, "PATH" ) == 0 ) ) {
392                 /* Nothing to do */
393                 return 0;
394         }
395
396         /* Do nothing more unless we have a settings block */
397         if ( ! settings ) {
398                 rc = -ENOTSUP;
399                 goto err_no_settings;
400         }
401
402         /* Identify setting */
403         setting = find_setting ( key );
404         if ( ! setting ) {
405                 DBGC ( snpdev, "SNPDEV %p no such setting \"%s\"\n",
406                        snpdev, key );
407                 rc = -ENODEV;
408                 goto err_find_setting;
409         }
410
411         /* Allocate buffer */
412         len = ( strlen ( value ) / 4 );
413         buf = zalloc ( len + 1 /* NUL */ );
414         if ( ! buf ) {
415                 rc = -ENOMEM;
416                 goto err_alloc;
417         }
418
419         /* Decode value */
420         tmp[4] = '\0';
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",
426                                snpdev, tmp );
427                         rc = -EINVAL;
428                         goto err_inval;
429                 }
430         }
431
432         /* Store value */
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 ) );
436                 goto err_storef;
437         }
438
439         /* Success */
440         rc = 0;
441
442  err_storef:
443  err_inval:
444         free ( buf );
445  err_alloc:
446  err_find_setting:
447  err_no_settings:
448         return rc;
449 }
450
451 /**
452  * Process portion of HII configuration string
453  *
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
461  */
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 *,
466                                                      const char *key,
467                                                      const char *value,
468                                                      wchar_t **results,
469                                                      int *have_setting ) ) {
470         wchar_t *wkey = string;
471         wchar_t *wend = string;
472         wchar_t *wvalue = NULL;
473         size_t key_len;
474         size_t value_len;
475         void *temp;
476         char *key;
477         char *value;
478         int rc;
479
480         /* Locate key, value (if any), and end */
481         while ( *wend ) {
482                 if ( *wend == L'&' )
483                         break;
484                 if ( *(wend++) == L'=' )
485                         wvalue = wend;
486         }
487
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 */ );
492         if ( ! temp )
493                 return -ENOMEM;
494         key = temp;
495         value = ( temp + key_len + 1 /* NUL */ );
496
497         /* Copy key and value */
498         while ( key_len-- )
499                 key[key_len] = wkey[key_len];
500         while ( value_len-- )
501                 value[value_len] = wvalue[value_len];
502
503         /* Process key and value */
504         if ( ( rc = process ( snpdev, key, value, results,
505                               have_setting ) ) != 0 ) {
506                 goto err;
507         }
508
509         /* Update progress marker */
510         *progress = wend;
511
512  err:
513         /* Free temporary storage */
514         free ( temp );
515
516         return rc;
517 }
518
519 /**
520  * Fetch configuration
521  *
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
527  */
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;
535         wchar_t *pos;
536         int rc;
537
538         DBGC ( snpdev, "SNPDEV %p ExtractConfig request \"%ls\"\n",
539                snpdev, request );
540
541         /* Initialise results */
542         *results = NULL;
543
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 ) {
550                         return EFIRC ( rc );
551                 }
552         }
553
554         /* If we have no explicit request, return all settings */
555         if ( ! have_setting ) {
556                 struct setting *setting;
557
558                 for_each_table_entry ( setting, SETTINGS ) {
559                         if ( ! efi_snp_hii_setting_applies ( snpdev, setting ) )
560                                 continue;
561                         if ( ( rc = efi_snp_hii_fetch ( snpdev, setting->name,
562                                                         NULL, results,
563                                                         NULL ) ) != 0 ) {
564                                 return EFIRC ( rc );
565                         }
566                 }
567         }
568
569         DBGC ( snpdev, "SNPDEV %p ExtractConfig results \"%ls\"\n",
570                snpdev, *results );
571         return 0;
572 }
573
574 /**
575  * Store configuration
576  *
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
581  */
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 );
587         wchar_t *pos;
588         int rc;
589
590         DBGC ( snpdev, "SNPDEV %p RouteConfig \"%ls\"\n", snpdev, config );
591
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,
596                                                   NULL, NULL,
597                                                   efi_snp_hii_store ) ) != 0 ) {
598                         return EFIRC ( rc );
599                 }
600         }
601
602         return 0;
603 }
604
605 /**
606  * Handle form actions
607  *
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
612  * @v value             Value
613  * @ret action_request  Action requested by driver
614  * @ret efirc           EFI status code
615  */
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 );
624
625         DBGC ( snpdev, "SNPDEV %p Callback\n", snpdev );
626         return EFI_UNSUPPORTED;
627 }
628
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,
634 };
635
636 /**
637  * Install HII protocol and packages for SNP device
638  *
639  * @v snpdev            SNP device
640  * @ret rc              Return status code
641  */
642 int efi_snp_hii_install ( struct efi_snp_device *snpdev ) {
643         EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
644         int efirc;
645         int rc;
646
647         /* Do nothing if HII database protocol is not supported */
648         if ( ! efihii ) {
649                 rc = -ENOTSUP;
650                 goto err_no_hii;
651         }
652
653         /* Initialise HII protocol */
654         memcpy ( &snpdev->hii, &efi_snp_device_hii, sizeof ( snpdev->hii ) );
655
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",
660                        snpdev );
661                 rc = -ENOMEM;
662                 goto err_build_package_list;
663         }
664
665         /* Add HII packages */
666         if ( ( efirc = efihii->NewPackageList ( efihii, snpdev->package_list,
667                                                 snpdev->handle,
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;
673         }
674
675         /* Install HII protocol */
676         if ( ( efirc = bs->InstallMultipleProtocolInterfaces (
677                          &snpdev->handle,
678                          &efi_hii_config_access_protocol_guid, &snpdev->hii,
679                          NULL ) ) != 0 ) {
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;
684         }
685
686         return 0;
687
688         bs->UninstallMultipleProtocolInterfaces (
689                         snpdev->handle,
690                         &efi_hii_config_access_protocol_guid, &snpdev->hii,
691                         NULL );
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:
698  err_no_hii:
699         return rc;
700 }
701
702 /**
703  * Uninstall HII protocol and package for SNP device
704  *
705  * @v snpdev            SNP device
706  */
707 void efi_snp_hii_uninstall ( struct efi_snp_device *snpdev ) {
708         EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
709
710         /* Do nothing if HII database protocol is not supported */
711         if ( ! efihii )
712                 return;
713
714         /* Uninstall protocols and remove package list */
715         bs->UninstallMultipleProtocolInterfaces (
716                         snpdev->handle,
717                         &efi_hii_config_access_protocol_guid, &snpdev->hii,
718                         NULL );
719         efihii->RemovePackageList ( efihii, snpdev->hii_handle );
720         free ( snpdev->package_list );
721         snpdev->package_list = NULL;
722 }