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