You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

efi_snp_hii.c 22KB

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