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 * 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.
22 */
23
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25
26 /**
27 * @file
28 *
29 * EFI SNP HII protocol
30 *
31 * The HII protocols are some of the less-well designed parts of the
32 * entire EFI specification. This is a significant accomplishment.
33 *
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.
39 *
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.
46 *
47 * Fundamentally, the problem seems to be that Javascript programmers
48 * should not be allowed to design APIs for C code.
49 */
50
51 #include <string.h>
52 #include <strings.h>
53 #include <stdlib.h>
54 #include <stdio.h>
55 #include <wchar.h>
56 #include <errno.h>
57 #include <ipxe/settings.h>
58 #include <ipxe/nvo.h>
59 #include <ipxe/device.h>
60 #include <ipxe/netdevice.h>
61 #include <ipxe/version.h>
62 #include <ipxe/efi/efi.h>
63 #include <ipxe/efi/efi_hii.h>
64 #include <ipxe/efi/efi_snp.h>
65 #include <ipxe/efi/efi_strings.h>
66 #include <ipxe/efi/efi_utils.h>
67 #include <config/branding.h>
68
69 /** EFI platform setup formset GUID */
70 static EFI_GUID efi_hii_platform_setup_formset_guid
71 = EFI_HII_PLATFORM_SETUP_FORMSET_GUID;
72
73 /** EFI IBM UCM compliant formset GUID */
74 static EFI_GUID efi_hii_ibm_ucm_compliant_formset_guid
75 = EFI_HII_IBM_UCM_COMPLIANT_FORMSET_GUID;
76
77 /** EFI HII database protocol */
78 static EFI_HII_DATABASE_PROTOCOL *efihii;
79 EFI_REQUEST_PROTOCOL ( EFI_HII_DATABASE_PROTOCOL, &efihii );
80
81 /**
82 * Identify settings to be exposed via HII
83 *
84 * @v snpdev SNP device
85 * @ret settings Settings, or NULL
86 */
efi_snp_hii_settings(struct efi_snp_device * snpdev)87 static struct settings * efi_snp_hii_settings ( struct efi_snp_device *snpdev ){
88
89 return find_child_settings ( netdev_settings ( snpdev->netdev ),
90 NVO_SETTINGS_NAME );
91 }
92
93 /**
94 * Check whether or not setting is applicable
95 *
96 * @v snpdev SNP device
97 * @v setting Setting
98 * @ret applies Setting applies
99 */
efi_snp_hii_setting_applies(struct efi_snp_device * snpdev,struct setting * setting)100 static int efi_snp_hii_setting_applies ( struct efi_snp_device *snpdev,
101 struct setting *setting ) {
102
103 return nvo_applies ( efi_snp_hii_settings ( snpdev ), setting );
104 }
105
106 /**
107 * Generate a random GUID
108 *
109 * @v guid GUID to fill in
110 */
efi_snp_hii_random_guid(EFI_GUID * guid)111 static void efi_snp_hii_random_guid ( EFI_GUID *guid ) {
112 uint8_t *byte = ( ( uint8_t * ) guid );
113 unsigned int i;
114
115 for ( i = 0 ; i < sizeof ( *guid ) ; i++ )
116 *(byte++) = random();
117 }
118
119 /**
120 * Generate EFI SNP questions
121 *
122 * @v snpdev SNP device
123 * @v ifr IFR builder
124 * @v varstore_id Variable store identifier
125 */
efi_snp_hii_questions(struct efi_snp_device * snpdev,struct efi_ifr_builder * ifr,unsigned int varstore_id)126 static void efi_snp_hii_questions ( struct efi_snp_device *snpdev,
127 struct efi_ifr_builder *ifr,
128 unsigned int varstore_id ) {
129 struct setting *setting;
130 struct setting *previous = NULL;
131 unsigned int name_id;
132 unsigned int prompt_id;
133 unsigned int help_id;
134 unsigned int question_id;
135
136 /* Add all applicable settings */
137 for_each_table_entry ( setting, SETTINGS ) {
138 if ( ! efi_snp_hii_setting_applies ( snpdev, setting ) )
139 continue;
140 if ( previous && ( setting_cmp ( setting, previous ) == 0 ) )
141 continue;
142 previous = setting;
143 name_id = efi_ifr_string ( ifr, "%s", setting->name );
144 prompt_id = efi_ifr_string ( ifr, "%s", setting->description );
145 help_id = efi_ifr_string ( ifr, PRODUCT_SETTING_URI,
146 setting->name );
147 question_id = setting->tag;
148 efi_ifr_string_op ( ifr, prompt_id, help_id,
149 question_id, varstore_id, name_id,
150 0, 0x00, 0xff, 0 );
151 }
152 }
153
154 /**
155 * Build HII package list for SNP device
156 *
157 * @v snpdev SNP device
158 * @ret package Package list, or NULL on error
159 */
160 static EFI_HII_PACKAGE_LIST_HEADER *
efi_snp_hii_package_list(struct efi_snp_device * snpdev)161 efi_snp_hii_package_list ( struct efi_snp_device *snpdev ) {
162 struct net_device *netdev = snpdev->netdev;
163 struct device *dev = netdev->dev;
164 struct efi_ifr_builder ifr;
165 EFI_HII_PACKAGE_LIST_HEADER *package;
166 const char *name;
167 EFI_GUID package_guid;
168 EFI_GUID formset_guid;
169 EFI_GUID varstore_guid;
170 unsigned int title_id;
171 unsigned int varstore_id;
172
173 /* Initialise IFR builder */
174 efi_ifr_init ( &ifr );
175
176 /* Determine product name */
177 name = ( product_name[0] ? product_name : product_short_name );
178
179 /* Generate GUIDs */
180 efi_snp_hii_random_guid ( &package_guid );
181 efi_snp_hii_random_guid ( &formset_guid );
182 efi_snp_hii_random_guid ( &varstore_guid );
183
184 /* Generate title string (used more than once) */
185 title_id = efi_ifr_string ( &ifr, "%s (%s)", name,
186 netdev_addr ( netdev ) );
187
188 /* Generate opcodes */
189 efi_ifr_form_set_op ( &ifr, &formset_guid, title_id,
190 efi_ifr_string ( &ifr, "Configure %s",
191 product_short_name ),
192 &efi_hii_platform_setup_formset_guid,
193 &efi_hii_ibm_ucm_compliant_formset_guid, NULL );
194 efi_ifr_guid_class_op ( &ifr, EFI_NETWORK_DEVICE_CLASS );
195 efi_ifr_guid_subclass_op ( &ifr, 0x03 );
196 varstore_id = efi_ifr_varstore_name_value_op ( &ifr, &varstore_guid );
197 efi_ifr_form_op ( &ifr, title_id );
198 efi_ifr_text_op ( &ifr,
199 efi_ifr_string ( &ifr, "Name" ),
200 efi_ifr_string ( &ifr, "Firmware product name" ),
201 efi_ifr_string ( &ifr, "%s", name ) );
202 efi_ifr_text_op ( &ifr,
203 efi_ifr_string ( &ifr, "Version" ),
204 efi_ifr_string ( &ifr, "Firmware version" ),
205 efi_ifr_string ( &ifr, "%s", product_version ) );
206 efi_ifr_text_op ( &ifr,
207 efi_ifr_string ( &ifr, "Driver" ),
208 efi_ifr_string ( &ifr, "Firmware driver" ),
209 efi_ifr_string ( &ifr, "%s", dev->driver_name ) );
210 efi_ifr_text_op ( &ifr,
211 efi_ifr_string ( &ifr, "Device" ),
212 efi_ifr_string ( &ifr, "Hardware device" ),
213 efi_ifr_string ( &ifr, "%s", dev->name ) );
214 efi_snp_hii_questions ( snpdev, &ifr, varstore_id );
215 efi_ifr_end_op ( &ifr );
216 efi_ifr_end_op ( &ifr );
217
218 /* Build package */
219 package = efi_ifr_package ( &ifr, &package_guid, "en-us",
220 efi_ifr_string ( &ifr, "English" ) );
221 if ( ! package ) {
222 DBGC ( snpdev, "SNPDEV %p could not build IFR package\n",
223 snpdev );
224 efi_ifr_free ( &ifr );
225 return NULL;
226 }
227
228 /* Free temporary storage */
229 efi_ifr_free ( &ifr );
230 return package;
231 }
232
233 /**
234 * Append response to result string
235 *
236 * @v snpdev SNP device
237 * @v key Key
238 * @v value Value
239 * @v results Result string
240 * @ret rc Return status code
241 *
242 * The result string is allocated dynamically using
243 * BootServices::AllocatePool(), and the caller is responsible for
244 * eventually calling BootServices::FreePool().
245 */
efi_snp_hii_append(struct efi_snp_device * snpdev __unused,const char * key,const char * value,wchar_t ** results)246 static int efi_snp_hii_append ( struct efi_snp_device *snpdev __unused,
247 const char *key, const char *value,
248 wchar_t **results ) {
249 EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
250 size_t len;
251 void *new;
252
253 /* Allocate new string */
254 len = ( ( *results ? ( wcslen ( *results ) + 1 /* "&" */ ) : 0 ) +
255 strlen ( key ) + 1 /* "=" */ + strlen ( value ) + 1 /* NUL */ );
256 bs->AllocatePool ( EfiBootServicesData, ( len * sizeof ( wchar_t ) ),
257 &new );
258 if ( ! new )
259 return -ENOMEM;
260
261 /* Populate string */
262 efi_snprintf ( new, len, "%ls%s%s=%s", ( *results ? *results : L"" ),
263 ( *results ? L"&" : L"" ), key, value );
264 bs->FreePool ( *results );
265 *results = new;
266
267 return 0;
268 }
269
270 /**
271 * Fetch HII setting
272 *
273 * @v snpdev SNP device
274 * @v key Key
275 * @v value Value
276 * @v results Result string
277 * @v have_setting Flag indicating detection of a setting
278 * @ret rc Return status code
279 */
efi_snp_hii_fetch(struct efi_snp_device * snpdev,const char * key,const char * value,wchar_t ** results,int * have_setting)280 static int efi_snp_hii_fetch ( struct efi_snp_device *snpdev,
281 const char *key, const char *value,
282 wchar_t **results, int *have_setting ) {
283 struct settings *settings = efi_snp_hii_settings ( snpdev );
284 struct settings *origin;
285 struct setting *setting;
286 struct setting fetched;
287 int len;
288 char *buf;
289 char *encoded;
290 int i;
291 int rc;
292
293 /* Handle ConfigHdr components */
294 if ( ( strcasecmp ( key, "GUID" ) == 0 ) ||
295 ( strcasecmp ( key, "NAME" ) == 0 ) ||
296 ( strcasecmp ( key, "PATH" ) == 0 ) ) {
297 return efi_snp_hii_append ( snpdev, key, value, results );
298 }
299 if ( have_setting )
300 *have_setting = 1;
301
302 /* Do nothing more unless we have a settings block */
303 if ( ! settings ) {
304 rc = -ENOTSUP;
305 goto err_no_settings;
306 }
307
308 /* Identify setting */
309 setting = find_setting ( key );
310 if ( ! setting ) {
311 DBGC ( snpdev, "SNPDEV %p no such setting \"%s\"\n",
312 snpdev, key );
313 rc = -ENODEV;
314 goto err_find_setting;
315 }
316
317 /* Encode value */
318 if ( setting_exists ( settings, setting ) ) {
319
320 /* Calculate formatted length */
321 len = fetchf_setting ( settings, setting, &origin, &fetched,
322 NULL, 0 );
323 if ( len < 0 ) {
324 rc = len;
325 DBGC ( snpdev, "SNPDEV %p could not fetch %s: %s\n",
326 snpdev, setting->name, strerror ( rc ) );
327 goto err_fetchf_len;
328 }
329
330 /* Allocate buffer for formatted value and HII-encoded value */
331 buf = zalloc ( len + 1 /* NUL */ + ( len * 4 ) + 1 /* NUL */ );
332 if ( ! buf ) {
333 rc = -ENOMEM;
334 goto err_alloc;
335 }
336 encoded = ( buf + len + 1 /* NUL */ );
337
338 /* Format value */
339 fetchf_setting ( origin, &fetched, NULL, NULL, buf,
340 ( len + 1 /* NUL */ ) );
341 for ( i = 0 ; i < len ; i++ ) {
342 sprintf ( ( encoded + ( 4 * i ) ), "%04x",
343 *( ( uint8_t * ) buf + i ) );
344 }
345
346 } else {
347
348 /* Non-existent or inapplicable setting */
349 buf = NULL;
350 encoded = "";
351 }
352
353 /* Append results */
354 if ( ( rc = efi_snp_hii_append ( snpdev, key, encoded,
355 results ) ) != 0 ) {
356 goto err_append;
357 }
358
359 /* Success */
360 rc = 0;
361
362 err_append:
363 free ( buf );
364 err_alloc:
365 err_fetchf_len:
366 err_find_setting:
367 err_no_settings:
368 return rc;
369 }
370
371 /**
372 * Fetch HII setting
373 *
374 * @v snpdev SNP device
375 * @v key Key
376 * @v value Value
377 * @v results Result string (unused)
378 * @v have_setting Flag indicating detection of a setting (unused)
379 * @ret rc Return status code
380 */
efi_snp_hii_store(struct efi_snp_device * snpdev,const char * key,const char * value,wchar_t ** results __unused,int * have_setting __unused)381 static int efi_snp_hii_store ( struct efi_snp_device *snpdev,
382 const char *key, const char *value,
383 wchar_t **results __unused,
384 int *have_setting __unused ) {
385 struct settings *settings = efi_snp_hii_settings ( snpdev );
386 struct setting *setting;
387 char *buf;
388 char tmp[5];
389 char *endp;
390 int len;
391 int i;
392 int rc;
393
394 /* Handle ConfigHdr components */
395 if ( ( strcasecmp ( key, "GUID" ) == 0 ) ||
396 ( strcasecmp ( key, "NAME" ) == 0 ) ||
397 ( strcasecmp ( key, "PATH" ) == 0 ) ) {
398 /* Nothing to do */
399 return 0;
400 }
401
402 /* Do nothing more unless we have a settings block */
403 if ( ! settings ) {
404 rc = -ENOTSUP;
405 goto err_no_settings;
406 }
407
408 /* Identify setting */
409 setting = find_setting ( key );
410 if ( ! setting ) {
411 DBGC ( snpdev, "SNPDEV %p no such setting \"%s\"\n",
412 snpdev, key );
413 rc = -ENODEV;
414 goto err_find_setting;
415 }
416
417 /* Allocate buffer */
418 len = ( strlen ( value ) / 4 );
419 buf = zalloc ( len + 1 /* NUL */ );
420 if ( ! buf ) {
421 rc = -ENOMEM;
422 goto err_alloc;
423 }
424
425 /* Decode value */
426 tmp[4] = '\0';
427 for ( i = 0 ; i < len ; i++ ) {
428 memcpy ( tmp, ( value + ( i * 4 ) ), 4 );
429 buf[i] = strtoul ( tmp, &endp, 16 );
430 if ( endp != &tmp[4] ) {
431 DBGC ( snpdev, "SNPDEV %p invalid character %s\n",
432 snpdev, tmp );
433 rc = -EINVAL;
434 goto err_inval;
435 }
436 }
437
438 /* Store value */
439 if ( ( rc = storef_setting ( settings, setting, buf ) ) != 0 ) {
440 DBGC ( snpdev, "SNPDEV %p could not store \"%s\" into %s: %s\n",
441 snpdev, buf, setting->name, strerror ( rc ) );
442 goto err_storef;
443 }
444
445 /* Success */
446 rc = 0;
447
448 err_storef:
449 err_inval:
450 free ( buf );
451 err_alloc:
452 err_find_setting:
453 err_no_settings:
454 return rc;
455 }
456
457 /**
458 * Process portion of HII configuration string
459 *
460 * @v snpdev SNP device
461 * @v string HII configuration string
462 * @v progress Progress through HII configuration string
463 * @v results Results string
464 * @v have_setting Flag indicating detection of a setting (unused)
465 * @v process Function used to process key=value pairs
466 * @ret rc Return status code
467 */
efi_snp_hii_process(struct efi_snp_device * snpdev,wchar_t * string,wchar_t ** progress,wchar_t ** results,int * have_setting,int (* process)(struct efi_snp_device *,const char * key,const char * value,wchar_t ** results,int * have_setting))468 static int efi_snp_hii_process ( struct efi_snp_device *snpdev,
469 wchar_t *string, wchar_t **progress,
470 wchar_t **results, int *have_setting,
471 int ( * process ) ( struct efi_snp_device *,
472 const char *key,
473 const char *value,
474 wchar_t **results,
475 int *have_setting ) ) {
476 wchar_t *wkey = string;
477 wchar_t *wend = string;
478 wchar_t *wvalue = NULL;
479 size_t key_len;
480 size_t value_len;
481 void *temp;
482 char *key;
483 char *value;
484 int rc;
485
486 /* Locate key, value (if any), and end */
487 while ( *wend ) {
488 if ( *wend == L'&' )
489 break;
490 if ( *(wend++) == L'=' )
491 wvalue = wend;
492 }
493
494 /* Allocate memory for key and value */
495 key_len = ( ( wvalue ? ( wvalue - 1 ) : wend ) - wkey );
496 value_len = ( wvalue ? ( wend - wvalue ) : 0 );
497 temp = zalloc ( key_len + 1 /* NUL */ + value_len + 1 /* NUL */ );
498 if ( ! temp )
499 return -ENOMEM;
500 key = temp;
501 value = ( temp + key_len + 1 /* NUL */ );
502
503 /* Copy key and value */
504 while ( key_len-- )
505 key[key_len] = wkey[key_len];
506 while ( value_len-- )
507 value[value_len] = wvalue[value_len];
508
509 /* Process key and value */
510 if ( ( rc = process ( snpdev, key, value, results,
511 have_setting ) ) != 0 ) {
512 goto err;
513 }
514
515 /* Update progress marker */
516 *progress = wend;
517
518 err:
519 /* Free temporary storage */
520 free ( temp );
521
522 return rc;
523 }
524
525 /**
526 * Fetch configuration
527 *
528 * @v hii HII configuration access protocol
529 * @v request Configuration to fetch
530 * @ret progress Progress made through configuration to fetch
531 * @ret results Query results
532 * @ret efirc EFI status code
533 */
534 static EFI_STATUS EFIAPI
efi_snp_hii_extract_config(const EFI_HII_CONFIG_ACCESS_PROTOCOL * hii,EFI_STRING request,EFI_STRING * progress,EFI_STRING * results)535 efi_snp_hii_extract_config ( const EFI_HII_CONFIG_ACCESS_PROTOCOL *hii,
536 EFI_STRING request, EFI_STRING *progress,
537 EFI_STRING *results ) {
538 struct efi_snp_device *snpdev =
539 container_of ( hii, struct efi_snp_device, hii );
540 int have_setting = 0;
541 wchar_t *pos;
542 int rc;
543
544 DBGC ( snpdev, "SNPDEV %p ExtractConfig request \"%ls\"\n",
545 snpdev, request );
546
547 /* Initialise results */
548 *results = NULL;
549
550 /* Work around apparently broken UEFI specification */
551 if ( ! ( request && request[0] ) ) {
552 DBGC ( snpdev, "SNPDEV %p ExtractConfig ignoring malformed "
553 "request\n", snpdev );
554 return EFI_INVALID_PARAMETER;
555 }
556
557 /* Process all request fragments */
558 for ( pos = *progress = request ; *progress && **progress ;
559 pos = *progress + 1 ) {
560 if ( ( rc = efi_snp_hii_process ( snpdev, pos, progress,
561 results, &have_setting,
562 efi_snp_hii_fetch ) ) != 0 ) {
563 return EFIRC ( rc );
564 }
565 }
566
567 /* If we have no explicit request, return all settings */
568 if ( ! have_setting ) {
569 struct setting *setting;
570
571 for_each_table_entry ( setting, SETTINGS ) {
572 if ( ! efi_snp_hii_setting_applies ( snpdev, setting ) )
573 continue;
574 if ( ( rc = efi_snp_hii_fetch ( snpdev, setting->name,
575 NULL, results,
576 NULL ) ) != 0 ) {
577 return EFIRC ( rc );
578 }
579 }
580 }
581
582 DBGC ( snpdev, "SNPDEV %p ExtractConfig results \"%ls\"\n",
583 snpdev, *results );
584 return 0;
585 }
586
587 /**
588 * Store configuration
589 *
590 * @v hii HII configuration access protocol
591 * @v config Configuration to store
592 * @ret progress Progress made through configuration to store
593 * @ret efirc EFI status code
594 */
595 static EFI_STATUS EFIAPI
efi_snp_hii_route_config(const EFI_HII_CONFIG_ACCESS_PROTOCOL * hii,EFI_STRING config,EFI_STRING * progress)596 efi_snp_hii_route_config ( const EFI_HII_CONFIG_ACCESS_PROTOCOL *hii,
597 EFI_STRING config, EFI_STRING *progress ) {
598 struct efi_snp_device *snpdev =
599 container_of ( hii, struct efi_snp_device, hii );
600 wchar_t *pos;
601 int rc;
602
603 DBGC ( snpdev, "SNPDEV %p RouteConfig \"%ls\"\n", snpdev, config );
604
605 /* Process all request fragments */
606 for ( pos = *progress = config ; *progress && **progress ;
607 pos = *progress + 1 ) {
608 if ( ( rc = efi_snp_hii_process ( snpdev, pos, progress,
609 NULL, NULL,
610 efi_snp_hii_store ) ) != 0 ) {
611 return EFIRC ( rc );
612 }
613 }
614
615 return 0;
616 }
617
618 /**
619 * Handle form actions
620 *
621 * @v hii HII configuration access protocol
622 * @v action Form browser action
623 * @v question_id Question ID
624 * @v type Type of value
625 * @v value Value
626 * @ret action_request Action requested by driver
627 * @ret efirc EFI status code
628 */
629 static EFI_STATUS EFIAPI
efi_snp_hii_callback(const EFI_HII_CONFIG_ACCESS_PROTOCOL * hii,EFI_BROWSER_ACTION action __unused,EFI_QUESTION_ID question_id __unused,UINT8 type __unused,EFI_IFR_TYPE_VALUE * value __unused,EFI_BROWSER_ACTION_REQUEST * action_request __unused)630 efi_snp_hii_callback ( const EFI_HII_CONFIG_ACCESS_PROTOCOL *hii,
631 EFI_BROWSER_ACTION action __unused,
632 EFI_QUESTION_ID question_id __unused,
633 UINT8 type __unused, EFI_IFR_TYPE_VALUE *value __unused,
634 EFI_BROWSER_ACTION_REQUEST *action_request __unused ) {
635 struct efi_snp_device *snpdev =
636 container_of ( hii, struct efi_snp_device, hii );
637
638 DBGC ( snpdev, "SNPDEV %p Callback\n", snpdev );
639 return EFI_UNSUPPORTED;
640 }
641
642 /** HII configuration access protocol */
643 static EFI_HII_CONFIG_ACCESS_PROTOCOL efi_snp_device_hii = {
644 .ExtractConfig = efi_snp_hii_extract_config,
645 .RouteConfig = efi_snp_hii_route_config,
646 .Callback = efi_snp_hii_callback,
647 };
648
649 /**
650 * Install HII protocol and packages for SNP device
651 *
652 * @v snpdev SNP device
653 * @ret rc Return status code
654 */
efi_snp_hii_install(struct efi_snp_device * snpdev)655 int efi_snp_hii_install ( struct efi_snp_device *snpdev ) {
656 EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
657 VENDOR_DEVICE_PATH *vendor_path;
658 EFI_DEVICE_PATH_PROTOCOL *path_end;
659 size_t path_prefix_len;
660 int efirc;
661 int rc;
662
663 /* Do nothing if HII database protocol is not supported */
664 if ( ! efihii ) {
665 rc = -ENOTSUP;
666 goto err_no_hii;
667 }
668
669 /* Initialise HII protocol */
670 memcpy ( &snpdev->hii, &efi_snp_device_hii, sizeof ( snpdev->hii ) );
671
672 /* Create HII package list */
673 snpdev->package_list = efi_snp_hii_package_list ( snpdev );
674 if ( ! snpdev->package_list ) {
675 DBGC ( snpdev, "SNPDEV %p could not create HII package list\n",
676 snpdev );
677 rc = -ENOMEM;
678 goto err_build_package_list;
679 }
680
681 /* Allocate the new device path */
682 path_prefix_len = efi_devpath_len ( snpdev->path );
683 snpdev->hii_child_path = zalloc ( path_prefix_len +
684 sizeof ( *vendor_path ) +
685 sizeof ( *path_end ) );
686 if ( ! snpdev->hii_child_path ) {
687 DBGC ( snpdev,
688 "SNPDEV %p could not allocate HII child device path\n",
689 snpdev );
690 rc = -ENOMEM;
691 goto err_alloc_child_path;
692 }
693
694 /* Populate the device path */
695 memcpy ( snpdev->hii_child_path, snpdev->path, path_prefix_len );
696 vendor_path = ( ( ( void * ) snpdev->hii_child_path ) +
697 path_prefix_len );
698 vendor_path->Header.Type = HARDWARE_DEVICE_PATH;
699 vendor_path->Header.SubType = HW_VENDOR_DP;
700 vendor_path->Header.Length[0] = sizeof ( *vendor_path );
701 efi_snp_hii_random_guid ( &vendor_path->Guid );
702 path_end = ( ( void * ) ( vendor_path + 1 ) );
703 path_end->Type = END_DEVICE_PATH_TYPE;
704 path_end->SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE;
705 path_end->Length[0] = sizeof ( *path_end );
706
707 /* Create device path and child handle for HII association */
708 if ( ( efirc = bs->InstallMultipleProtocolInterfaces (
709 &snpdev->hii_child_handle,
710 &efi_device_path_protocol_guid, snpdev->hii_child_path,
711 NULL ) ) != 0 ) {
712 rc = -EEFI ( efirc );
713 DBGC ( snpdev, "SNPDEV %p could not create HII child handle: "
714 "%s\n", snpdev, strerror ( rc ) );
715 goto err_hii_child_handle;
716 }
717
718 /* Add HII packages */
719 if ( ( efirc = efihii->NewPackageList ( efihii, snpdev->package_list,
720 snpdev->hii_child_handle,
721 &snpdev->hii_handle ) ) != 0 ) {
722 rc = -EEFI ( efirc );
723 DBGC ( snpdev, "SNPDEV %p could not add HII packages: %s\n",
724 snpdev, strerror ( rc ) );
725 goto err_new_package_list;
726 }
727
728 /* Install HII protocol */
729 if ( ( efirc = bs->InstallMultipleProtocolInterfaces (
730 &snpdev->hii_child_handle,
731 &efi_hii_config_access_protocol_guid, &snpdev->hii,
732 NULL ) ) != 0 ) {
733 rc = -EEFI ( efirc );
734 DBGC ( snpdev, "SNPDEV %p could not install HII protocol: %s\n",
735 snpdev, strerror ( rc ) );
736 goto err_install_protocol;
737 }
738
739 /* Add as child of handle with SNP instance */
740 if ( ( rc = efi_child_add ( snpdev->handle,
741 snpdev->hii_child_handle ) ) != 0 ) {
742 DBGC ( snpdev,
743 "SNPDEV %p could not adopt HII child handle: %s\n",
744 snpdev, strerror ( rc ) );
745 goto err_efi_child_add;
746 }
747
748 return 0;
749
750 efi_child_del ( snpdev->handle, snpdev->hii_child_handle );
751 err_efi_child_add:
752 bs->UninstallMultipleProtocolInterfaces (
753 snpdev->hii_child_handle,
754 &efi_hii_config_access_protocol_guid, &snpdev->hii,
755 NULL );
756 err_install_protocol:
757 efihii->RemovePackageList ( efihii, snpdev->hii_handle );
758 err_new_package_list:
759 bs->UninstallMultipleProtocolInterfaces (
760 snpdev->hii_child_handle,
761 &efi_device_path_protocol_guid, snpdev->hii_child_path,
762 NULL );
763 err_hii_child_handle:
764 free ( snpdev->hii_child_path );
765 snpdev->hii_child_path = NULL;
766 err_alloc_child_path:
767 free ( snpdev->package_list );
768 snpdev->package_list = NULL;
769 err_build_package_list:
770 err_no_hii:
771 return rc;
772 }
773
774 /**
775 * Uninstall HII protocol and package for SNP device
776 *
777 * @v snpdev SNP device
778 */
efi_snp_hii_uninstall(struct efi_snp_device * snpdev)779 void efi_snp_hii_uninstall ( struct efi_snp_device *snpdev ) {
780 EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
781
782 /* Do nothing if HII database protocol is not supported */
783 if ( ! efihii )
784 return;
785
786 /* Uninstall protocols and remove package list */
787 efi_child_del ( snpdev->handle, snpdev->hii_child_handle );
788 bs->UninstallMultipleProtocolInterfaces (
789 snpdev->hii_child_handle,
790 &efi_hii_config_access_protocol_guid, &snpdev->hii,
791 NULL );
792 efihii->RemovePackageList ( efihii, snpdev->hii_handle );
793 bs->UninstallMultipleProtocolInterfaces (
794 snpdev->hii_child_handle,
795 &efi_device_path_protocol_guid, snpdev->hii_child_path,
796 NULL );
797 free ( snpdev->hii_child_path );
798 snpdev->hii_child_path = NULL;
799 free ( snpdev->package_list );
800 snpdev->package_list = NULL;
801 }
802