|
@@ -19,9 +19,39 @@
|
19
|
19
|
|
20
|
20
|
FILE_LICENCE ( GPL2_OR_LATER );
|
21
|
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
|
+
|
22
|
47
|
#include <string.h>
|
|
48
|
+#include <strings.h>
|
23
|
49
|
#include <stdlib.h>
|
|
50
|
+#include <stdio.h>
|
|
51
|
+#include <wchar.h>
|
24
|
52
|
#include <errno.h>
|
|
53
|
+#include <ipxe/settings.h>
|
|
54
|
+#include <ipxe/nvo.h>
|
25
|
55
|
#include <ipxe/device.h>
|
26
|
56
|
#include <ipxe/netdevice.h>
|
27
|
57
|
#include <ipxe/efi/efi.h>
|
|
@@ -34,250 +64,452 @@ FILE_LICENCE ( GPL2_OR_LATER );
|
34
|
64
|
static EFI_GUID efi_hii_config_access_protocol_guid
|
35
|
65
|
= EFI_HII_CONFIG_ACCESS_PROTOCOL_GUID;
|
36
|
66
|
|
|
67
|
+/** EFI platform setup formset GUID */
|
|
68
|
+static EFI_GUID efi_hii_platform_setup_formset_guid
|
|
69
|
+ = EFI_HII_PLATFORM_SETUP_FORMSET_GUID;
|
|
70
|
+
|
|
71
|
+/** EFI IBM UCM compliant formset GUID */
|
|
72
|
+static EFI_GUID efi_hii_ibm_ucm_compliant_formset_guid
|
|
73
|
+ = EFI_HII_IBM_UCM_COMPLIANT_FORMSET_GUID;
|
|
74
|
+
|
37
|
75
|
/** EFI HII database protocol */
|
38
|
76
|
static EFI_HII_DATABASE_PROTOCOL *efihii;
|
39
|
77
|
EFI_REQUIRE_PROTOCOL ( EFI_HII_DATABASE_PROTOCOL, &efihii );
|
40
|
78
|
|
41
|
|
-/** Local base GUID used for our EFI SNP formset */
|
42
|
|
-#define EFI_SNP_FORMSET_GUID_BASE \
|
43
|
|
- { 0xc4f84019, 0x6dfd, 0x4a27, \
|
44
|
|
- { 0x9b, 0x94, 0xb7, 0x2e, 0x1f, 0xbc, 0xad, 0xca } }
|
|
79
|
+/**
|
|
80
|
+ * Identify settings to be exposed via HII
|
|
81
|
+ *
|
|
82
|
+ * @v snpdev SNP device
|
|
83
|
+ * @ret settings Settings, or NULL
|
|
84
|
+ */
|
|
85
|
+static struct settings * efi_snp_hii_settings ( struct efi_snp_device *snpdev ){
|
|
86
|
+
|
|
87
|
+ return find_child_settings ( netdev_settings ( snpdev->netdev ),
|
|
88
|
+ NVO_SETTINGS_NAME );
|
|
89
|
+}
|
45
|
90
|
|
46
|
|
-/** Form identifiers used for our EFI SNP HII */
|
47
|
|
-enum efi_snp_hii_form_id {
|
48
|
|
- EFI_SNP_FORM = 0x0001, /**< The only form */
|
49
|
|
-};
|
|
91
|
+/**
|
|
92
|
+ * Check whether or not setting is applicable
|
|
93
|
+ *
|
|
94
|
+ * @v snpdev SNP device
|
|
95
|
+ * @v setting Setting
|
|
96
|
+ * @ret applies Setting applies
|
|
97
|
+ */
|
|
98
|
+static int efi_snp_hii_setting_applies ( struct efi_snp_device *snpdev,
|
|
99
|
+ struct setting *setting ) {
|
50
|
100
|
|
51
|
|
-/** String identifiers used for our EFI SNP HII */
|
52
|
|
-enum efi_snp_hii_string_id {
|
53
|
|
- /* Language name */
|
54
|
|
- EFI_SNP_LANGUAGE_NAME = 0x0001,
|
55
|
|
- /* Formset */
|
56
|
|
- EFI_SNP_FORMSET_TITLE, EFI_SNP_FORMSET_HELP,
|
57
|
|
- /* Product name */
|
58
|
|
- EFI_SNP_PRODUCT_PROMPT, EFI_SNP_PRODUCT_HELP, EFI_SNP_PRODUCT_TEXT,
|
59
|
|
- /* Version */
|
60
|
|
- EFI_SNP_VERSION_PROMPT, EFI_SNP_VERSION_HELP, EFI_SNP_VERSION_TEXT,
|
61
|
|
- /* Driver */
|
62
|
|
- EFI_SNP_DRIVER_PROMPT, EFI_SNP_DRIVER_HELP, EFI_SNP_DRIVER_TEXT,
|
63
|
|
- /* Device */
|
64
|
|
- EFI_SNP_DEVICE_PROMPT, EFI_SNP_DEVICE_HELP, EFI_SNP_DEVICE_TEXT,
|
65
|
|
- /* End of list */
|
66
|
|
- EFI_SNP_MAX_STRING_ID
|
67
|
|
-};
|
|
101
|
+ return nvo_applies ( efi_snp_hii_settings ( snpdev ), setting );
|
|
102
|
+}
|
68
|
103
|
|
69
|
|
-/** EFI SNP formset */
|
70
|
|
-struct efi_snp_formset {
|
71
|
|
- EFI_HII_PACKAGE_HEADER Header;
|
72
|
|
- EFI_IFR_FORM_SET_TYPE(2) FormSet;
|
73
|
|
- EFI_IFR_GUID_CLASS Class;
|
74
|
|
- EFI_IFR_GUID_SUBCLASS SubClass;
|
75
|
|
- EFI_IFR_FORM Form;
|
76
|
|
- EFI_IFR_TEXT ProductText;
|
77
|
|
- EFI_IFR_TEXT VersionText;
|
78
|
|
- EFI_IFR_TEXT DriverText;
|
79
|
|
- EFI_IFR_TEXT DeviceText;
|
80
|
|
- EFI_IFR_END EndForm;
|
81
|
|
- EFI_IFR_END EndFormSet;
|
82
|
|
-} __attribute__ (( packed )) efi_snp_formset = {
|
83
|
|
- .Header = {
|
84
|
|
- .Length = sizeof ( efi_snp_formset ),
|
85
|
|
- .Type = EFI_HII_PACKAGE_FORMS,
|
86
|
|
- },
|
87
|
|
- .FormSet = EFI_IFR_FORM_SET ( EFI_SNP_FORMSET_GUID_BASE,
|
88
|
|
- EFI_SNP_FORMSET_TITLE,
|
89
|
|
- EFI_SNP_FORMSET_HELP,
|
90
|
|
- typeof ( efi_snp_formset.FormSet ),
|
91
|
|
- EFI_HII_PLATFORM_SETUP_FORMSET_GUID,
|
92
|
|
- EFI_HII_IBM_UCM_COMPLIANT_FORMSET_GUID ),
|
93
|
|
- .Class = EFI_IFR_GUID_CLASS ( EFI_NETWORK_DEVICE_CLASS ),
|
94
|
|
- .SubClass = EFI_IFR_GUID_SUBCLASS ( 0x03 ),
|
95
|
|
- .Form = EFI_IFR_FORM ( EFI_SNP_FORM, EFI_SNP_FORMSET_TITLE ),
|
96
|
|
- .ProductText = EFI_IFR_TEXT ( EFI_SNP_PRODUCT_PROMPT,
|
97
|
|
- EFI_SNP_PRODUCT_HELP,
|
98
|
|
- EFI_SNP_PRODUCT_TEXT ),
|
99
|
|
- .VersionText = EFI_IFR_TEXT ( EFI_SNP_VERSION_PROMPT,
|
100
|
|
- EFI_SNP_VERSION_HELP,
|
101
|
|
- EFI_SNP_VERSION_TEXT ),
|
102
|
|
- .DriverText = EFI_IFR_TEXT ( EFI_SNP_DRIVER_PROMPT,
|
103
|
|
- EFI_SNP_DRIVER_HELP,
|
104
|
|
- EFI_SNP_DRIVER_TEXT ),
|
105
|
|
- .DeviceText = EFI_IFR_TEXT ( EFI_SNP_DEVICE_PROMPT,
|
106
|
|
- EFI_SNP_DEVICE_HELP,
|
107
|
|
- EFI_SNP_DEVICE_TEXT ),
|
108
|
|
- .EndForm = EFI_IFR_END(),
|
109
|
|
- .EndFormSet = EFI_IFR_END(),
|
110
|
|
-};
|
|
104
|
+/**
|
|
105
|
+ * Generate a random GUID
|
|
106
|
+ *
|
|
107
|
+ * @v guid GUID to fill in
|
|
108
|
+ */
|
|
109
|
+static void efi_snp_hii_random_guid ( EFI_GUID *guid ) {
|
|
110
|
+ uint8_t *byte = ( ( uint8_t * ) guid );
|
|
111
|
+ unsigned int i;
|
|
112
|
+
|
|
113
|
+ for ( i = 0 ; i < sizeof ( *guid ) ; i++ )
|
|
114
|
+ *(byte++) = random();
|
|
115
|
+}
|
|
116
|
+
|
|
117
|
+/**
|
|
118
|
+ * Generate EFI SNP questions
|
|
119
|
+ *
|
|
120
|
+ * @v snpdev SNP device
|
|
121
|
+ * @v ifr IFR builder
|
|
122
|
+ * @v varstore_id Variable store identifier
|
|
123
|
+ */
|
|
124
|
+static void efi_snp_hii_questions ( struct efi_snp_device *snpdev,
|
|
125
|
+ struct efi_ifr_builder *ifr,
|
|
126
|
+ unsigned int varstore_id ) {
|
|
127
|
+ struct setting *setting;
|
|
128
|
+ unsigned int name_id;
|
|
129
|
+ unsigned int prompt_id;
|
|
130
|
+ unsigned int help_id;
|
|
131
|
+ unsigned int question_id;
|
|
132
|
+
|
|
133
|
+ /* Add all applicable settings */
|
|
134
|
+ for_each_table_entry ( setting, SETTINGS ) {
|
|
135
|
+ if ( ! efi_snp_hii_setting_applies ( snpdev, setting ) )
|
|
136
|
+ continue;
|
|
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
|
+}
|
111
|
147
|
|
112
|
148
|
/**
|
113
|
|
- * Generate EFI SNP string
|
|
149
|
+ * Build HII package list for SNP device
|
114
|
150
|
*
|
115
|
|
- * @v wbuf Buffer
|
116
|
|
- * @v swlen Size of buffer (in wide characters)
|
117
|
151
|
* @v snpdev SNP device
|
118
|
|
- * @ret wlen Length of string (in wide characters)
|
|
152
|
+ * @ret package Package list, or NULL on error
|
119
|
153
|
*/
|
120
|
|
-static int efi_snp_string ( wchar_t *wbuf, ssize_t swlen,
|
121
|
|
- enum efi_snp_hii_string_id id,
|
122
|
|
- struct efi_snp_device *snpdev ) {
|
|
154
|
+static EFI_HII_PACKAGE_LIST_HEADER *
|
|
155
|
+efi_snp_hii_package_list ( struct efi_snp_device *snpdev ) {
|
123
|
156
|
struct net_device *netdev = snpdev->netdev;
|
124
|
157
|
struct device *dev = netdev->dev;
|
125
|
|
-
|
126
|
|
- switch ( id ) {
|
127
|
|
- case EFI_SNP_LANGUAGE_NAME:
|
128
|
|
- return efi_ssnprintf ( wbuf, swlen, "English" );
|
129
|
|
- case EFI_SNP_FORMSET_TITLE:
|
130
|
|
- return efi_ssnprintf ( wbuf, swlen, "%s (%s)",
|
131
|
|
- ( PRODUCT_NAME[0] ?
|
132
|
|
- PRODUCT_NAME : PRODUCT_SHORT_NAME ),
|
133
|
|
- netdev_addr ( netdev ) );
|
134
|
|
- case EFI_SNP_FORMSET_HELP:
|
135
|
|
- return efi_ssnprintf ( wbuf, swlen,
|
136
|
|
- "Configure " PRODUCT_SHORT_NAME );
|
137
|
|
- case EFI_SNP_PRODUCT_PROMPT:
|
138
|
|
- return efi_ssnprintf ( wbuf, swlen, "Name" );
|
139
|
|
- case EFI_SNP_PRODUCT_HELP:
|
140
|
|
- return efi_ssnprintf ( wbuf, swlen, "Firmware product name" );
|
141
|
|
- case EFI_SNP_PRODUCT_TEXT:
|
142
|
|
- return efi_ssnprintf ( wbuf, swlen, "%s",
|
143
|
|
- ( PRODUCT_NAME[0] ?
|
144
|
|
- PRODUCT_NAME : PRODUCT_SHORT_NAME ) );
|
145
|
|
- case EFI_SNP_VERSION_PROMPT:
|
146
|
|
- return efi_ssnprintf ( wbuf, swlen, "Version" );
|
147
|
|
- case EFI_SNP_VERSION_HELP:
|
148
|
|
- return efi_ssnprintf ( wbuf, swlen, "Firmware version" );
|
149
|
|
- case EFI_SNP_VERSION_TEXT:
|
150
|
|
- return efi_ssnprintf ( wbuf, swlen, VERSION );
|
151
|
|
- case EFI_SNP_DRIVER_PROMPT:
|
152
|
|
- return efi_ssnprintf ( wbuf, swlen, "Driver" );
|
153
|
|
- case EFI_SNP_DRIVER_HELP:
|
154
|
|
- return efi_ssnprintf ( wbuf, swlen, "Firmware driver" );
|
155
|
|
- case EFI_SNP_DRIVER_TEXT:
|
156
|
|
- return efi_ssnprintf ( wbuf, swlen, "%s", dev->driver_name );
|
157
|
|
- case EFI_SNP_DEVICE_PROMPT:
|
158
|
|
- return efi_ssnprintf ( wbuf, swlen, "Device" );
|
159
|
|
- case EFI_SNP_DEVICE_HELP:
|
160
|
|
- return efi_ssnprintf ( wbuf, swlen, "Hardware device" );
|
161
|
|
- case EFI_SNP_DEVICE_TEXT:
|
162
|
|
- return efi_ssnprintf ( wbuf, swlen, "%s", dev->name );
|
163
|
|
- default:
|
164
|
|
- assert ( 0 );
|
165
|
|
- return 0;
|
|
158
|
+ struct efi_ifr_builder ifr;
|
|
159
|
+ EFI_HII_PACKAGE_LIST_HEADER *package;
|
|
160
|
+ const char *product_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
|
+ product_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)", product_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,
|
|
185
|
+ "Configure " 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", product_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, 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;
|
166
|
220
|
}
|
|
221
|
+
|
|
222
|
+ /* Free temporary storage */
|
|
223
|
+ efi_ifr_free ( &ifr );
|
|
224
|
+ return package;
|
167
|
225
|
}
|
168
|
226
|
|
169
|
227
|
/**
|
170
|
|
- * Generate EFI SNP string package
|
|
228
|
+ * Append response to result string
|
171
|
229
|
*
|
172
|
|
- * @v strings String package header buffer
|
173
|
|
- * @v max_len Buffer length
|
174
|
230
|
* @v snpdev SNP device
|
175
|
|
- * @ret len Length of string package
|
|
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().
|
176
|
239
|
*/
|
177
|
|
-static int efi_snp_strings ( EFI_HII_STRING_PACKAGE_HDR *strings,
|
178
|
|
- size_t max_len, struct efi_snp_device *snpdev ) {
|
179
|
|
- static const char language[] = "en-us";
|
180
|
|
- void *buf = strings;
|
181
|
|
- ssize_t remaining = max_len;
|
182
|
|
- size_t hdrsize;
|
183
|
|
- EFI_HII_SIBT_STRING_UCS2_BLOCK *string;
|
184
|
|
- ssize_t wremaining;
|
185
|
|
- size_t string_wlen;
|
186
|
|
- unsigned int id;
|
187
|
|
- EFI_HII_STRING_BLOCK *end;
|
|
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;
|
188
|
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 setting *setting;
|
|
279
|
+ int len;
|
|
280
|
+ char *buf;
|
|
281
|
+ char *encoded;
|
|
282
|
+ int i;
|
|
283
|
+ int rc;
|
|
284
|
+
|
|
285
|
+ /* Handle ConfigHdr components */
|
|
286
|
+ if ( ( strcasecmp ( key, "GUID" ) == 0 ) ||
|
|
287
|
+ ( strcasecmp ( key, "NAME" ) == 0 ) ||
|
|
288
|
+ ( strcasecmp ( key, "PATH" ) == 0 ) ) {
|
|
289
|
+ return efi_snp_hii_append ( snpdev, key, value, results );
|
|
290
|
+ }
|
|
291
|
+ if ( have_setting )
|
|
292
|
+ *have_setting = 1;
|
|
293
|
+
|
|
294
|
+ /* Do nothing more unless we have a settings block */
|
|
295
|
+ if ( ! settings ) {
|
|
296
|
+ rc = -ENOTSUP;
|
|
297
|
+ goto err_no_settings;
|
|
298
|
+ }
|
|
299
|
+
|
|
300
|
+ /* Identify setting */
|
|
301
|
+ setting = find_setting ( key );
|
|
302
|
+ if ( ! setting ) {
|
|
303
|
+ DBGC ( snpdev, "SNPDEV %p no such setting \"%s\"\n",
|
|
304
|
+ snpdev, key );
|
|
305
|
+ rc = -ENODEV;
|
|
306
|
+ goto err_find_setting;
|
|
307
|
+ }
|
189
|
308
|
|
190
|
|
- /* Calculate header size */
|
191
|
|
- hdrsize = ( offsetof ( typeof ( *strings ), Language ) +
|
192
|
|
- sizeof ( language ) );
|
193
|
|
- buf += hdrsize;
|
194
|
|
- remaining -= hdrsize;
|
195
|
|
-
|
196
|
|
- /* Fill in strings */
|
197
|
|
- for ( id = 1 ; id < EFI_SNP_MAX_STRING_ID ; id++ ) {
|
198
|
|
- string = buf;
|
199
|
|
- if ( remaining >= ( ( ssize_t ) sizeof ( string->Header ) ) )
|
200
|
|
- string->Header.BlockType = EFI_HII_SIBT_STRING_UCS2;
|
201
|
|
- buf += offsetof ( typeof ( *string ), StringText );
|
202
|
|
- remaining -= offsetof ( typeof ( *string ), StringText );
|
203
|
|
- wremaining = ( remaining /
|
204
|
|
- ( ( ssize_t ) sizeof ( string->StringText[0] )));
|
205
|
|
- assert ( ! ( ( remaining <= 0 ) && ( wremaining > 0 ) ) );
|
206
|
|
- string_wlen = efi_snp_string ( string->StringText, wremaining,
|
207
|
|
- id, snpdev );
|
208
|
|
- buf += ( ( string_wlen + 1 /* wNUL */ ) *
|
209
|
|
- sizeof ( string->StringText[0] ) );
|
210
|
|
- remaining -= ( ( string_wlen + 1 /* wNUL */ ) *
|
211
|
|
- sizeof ( string->StringText[0] ) );
|
|
309
|
+ /* Encode value */
|
|
310
|
+ if ( setting_exists ( settings, setting ) ) {
|
|
311
|
+
|
|
312
|
+ /* Calculate formatted length */
|
|
313
|
+ len = fetchf_setting ( settings, setting, NULL, 0 );
|
|
314
|
+ if ( len < 0 ) {
|
|
315
|
+ rc = len;
|
|
316
|
+ DBGC ( snpdev, "SNPDEV %p could not fetch %s: %s\n",
|
|
317
|
+ snpdev, setting->name, strerror ( rc ) );
|
|
318
|
+ goto err_fetchf_len;
|
|
319
|
+ }
|
|
320
|
+
|
|
321
|
+ /* Allocate buffer for formatted value and HII-encoded value */
|
|
322
|
+ buf = zalloc ( len + 1 /* NUL */ + ( len * 4 ) + 1 /* NUL */ );
|
|
323
|
+ if ( ! buf ) {
|
|
324
|
+ rc = -ENOMEM;
|
|
325
|
+ goto err_alloc;
|
|
326
|
+ }
|
|
327
|
+ encoded = ( buf + len + 1 /* NUL */ );
|
|
328
|
+
|
|
329
|
+ /* Format value */
|
|
330
|
+ fetchf_setting ( settings, setting, buf, ( len + 1 /* NUL */ ));
|
|
331
|
+ for ( i = 0 ; i < len ; i++ ) {
|
|
332
|
+ sprintf ( ( encoded + ( 4 * i ) ), "%04x",
|
|
333
|
+ *( ( uint8_t * ) buf + i ) );
|
|
334
|
+ }
|
|
335
|
+
|
|
336
|
+ } else {
|
|
337
|
+
|
|
338
|
+ /* Non-existent or inapplicable setting */
|
|
339
|
+ buf = NULL;
|
|
340
|
+ encoded = "";
|
212
|
341
|
}
|
213
|
342
|
|
214
|
|
- /* Fill in end marker */
|
215
|
|
- end = buf;
|
216
|
|
- if ( remaining >= ( ( ssize_t ) sizeof ( *end ) ) )
|
217
|
|
- end->BlockType = EFI_HII_SIBT_END;
|
218
|
|
- buf += sizeof ( *end );
|
219
|
|
- remaining -= sizeof ( *end );
|
220
|
|
-
|
221
|
|
- /* Calculate overall length */
|
222
|
|
- len = ( max_len - remaining );
|
223
|
|
-
|
224
|
|
- /* Fill in string package header */
|
225
|
|
- if ( strings ) {
|
226
|
|
- memset ( strings, 0, sizeof ( *strings ) );
|
227
|
|
- strings->Header.Length = len;
|
228
|
|
- strings->Header.Type = EFI_HII_PACKAGE_STRINGS;
|
229
|
|
- strings->HdrSize = hdrsize;
|
230
|
|
- strings->StringInfoOffset = hdrsize;
|
231
|
|
- strings->LanguageName = EFI_SNP_LANGUAGE_NAME;
|
232
|
|
- memcpy ( strings->Language, language, sizeof ( language ) );
|
|
343
|
+ /* Append results */
|
|
344
|
+ if ( ( rc = efi_snp_hii_append ( snpdev, key, encoded,
|
|
345
|
+ results ) ) != 0 ) {
|
|
346
|
+ goto err_append;
|
233
|
347
|
}
|
234
|
348
|
|
235
|
|
- return len;
|
|
349
|
+ /* Success */
|
|
350
|
+ rc = 0;
|
|
351
|
+
|
|
352
|
+ err_append:
|
|
353
|
+ free ( buf );
|
|
354
|
+ err_alloc:
|
|
355
|
+ err_fetchf_len:
|
|
356
|
+ err_find_setting:
|
|
357
|
+ err_no_settings:
|
|
358
|
+ return rc;
|
236
|
359
|
}
|
237
|
360
|
|
238
|
361
|
/**
|
239
|
|
- * Generate EFI SNP package list
|
|
362
|
+ * Fetch HII setting
|
240
|
363
|
*
|
241
|
364
|
* @v snpdev SNP device
|
242
|
|
- * @ret package_list Package list, or NULL on error
|
|
365
|
+ * @v key Key
|
|
366
|
+ * @v value Value
|
|
367
|
+ * @v results Result string (unused)
|
|
368
|
+ * @v have_setting Flag indicating detection of a setting (unused)
|
|
369
|
+ * @ret rc Return status code
|
|
370
|
+ */
|
|
371
|
+static int efi_snp_hii_store ( struct efi_snp_device *snpdev,
|
|
372
|
+ const char *key, const char *value,
|
|
373
|
+ wchar_t **results __unused,
|
|
374
|
+ int *have_setting __unused ) {
|
|
375
|
+ struct settings *settings = efi_snp_hii_settings ( snpdev );
|
|
376
|
+ struct setting *setting;
|
|
377
|
+ char *buf;
|
|
378
|
+ char tmp[5];
|
|
379
|
+ char *endp;
|
|
380
|
+ int len;
|
|
381
|
+ int i;
|
|
382
|
+ int rc;
|
|
383
|
+
|
|
384
|
+ /* Handle ConfigHdr components */
|
|
385
|
+ if ( ( strcasecmp ( key, "GUID" ) == 0 ) ||
|
|
386
|
+ ( strcasecmp ( key, "NAME" ) == 0 ) ||
|
|
387
|
+ ( strcasecmp ( key, "PATH" ) == 0 ) ) {
|
|
388
|
+ /* Nothing to do */
|
|
389
|
+ return 0;
|
|
390
|
+ }
|
|
391
|
+
|
|
392
|
+ /* Do nothing more unless we have a settings block */
|
|
393
|
+ if ( ! settings ) {
|
|
394
|
+ rc = -ENOTSUP;
|
|
395
|
+ goto err_no_settings;
|
|
396
|
+ }
|
|
397
|
+
|
|
398
|
+ /* Identify setting */
|
|
399
|
+ setting = find_setting ( key );
|
|
400
|
+ if ( ! setting ) {
|
|
401
|
+ DBGC ( snpdev, "SNPDEV %p no such setting \"%s\"\n",
|
|
402
|
+ snpdev, key );
|
|
403
|
+ rc = -ENODEV;
|
|
404
|
+ goto err_find_setting;
|
|
405
|
+ }
|
|
406
|
+
|
|
407
|
+ /* Allocate buffer */
|
|
408
|
+ len = ( strlen ( value ) / 4 );
|
|
409
|
+ buf = zalloc ( len + 1 /* NUL */ );
|
|
410
|
+ if ( ! buf ) {
|
|
411
|
+ rc = -ENOMEM;
|
|
412
|
+ goto err_alloc;
|
|
413
|
+ }
|
|
414
|
+
|
|
415
|
+ /* Decode value */
|
|
416
|
+ tmp[4] = '\0';
|
|
417
|
+ for ( i = 0 ; i < len ; i++ ) {
|
|
418
|
+ memcpy ( tmp, ( value + ( i * 4 ) ), 4 );
|
|
419
|
+ buf[i] = strtoul ( tmp, &endp, 16 );
|
|
420
|
+ if ( endp != &tmp[4] ) {
|
|
421
|
+ DBGC ( snpdev, "SNPDEV %p invalid character %s\n",
|
|
422
|
+ snpdev, tmp );
|
|
423
|
+ rc = -EINVAL;
|
|
424
|
+ goto err_inval;
|
|
425
|
+ }
|
|
426
|
+ }
|
|
427
|
+
|
|
428
|
+ /* Store value */
|
|
429
|
+ if ( ( rc = storef_setting ( settings, setting, buf ) ) != 0 ) {
|
|
430
|
+ DBGC ( snpdev, "SNPDEV %p could not store \"%s\" into %s: %s\n",
|
|
431
|
+ snpdev, buf, setting->name, strerror ( rc ) );
|
|
432
|
+ goto err_storef;
|
|
433
|
+ }
|
|
434
|
+
|
|
435
|
+ /* Success */
|
|
436
|
+ rc = 0;
|
|
437
|
+
|
|
438
|
+ err_storef:
|
|
439
|
+ err_inval:
|
|
440
|
+ free ( buf );
|
|
441
|
+ err_alloc:
|
|
442
|
+ err_find_setting:
|
|
443
|
+ err_no_settings:
|
|
444
|
+ return rc;
|
|
445
|
+}
|
|
446
|
+
|
|
447
|
+/**
|
|
448
|
+ * Process portion of HII configuration string
|
243
|
449
|
*
|
244
|
|
- * The package list is allocated using malloc(), and must eventually
|
245
|
|
- * be freed by the caller.
|
|
450
|
+ * @v snpdev SNP device
|
|
451
|
+ * @v string HII configuration string
|
|
452
|
+ * @v progress Progress through HII configuration string
|
|
453
|
+ * @v results Results string
|
|
454
|
+ * @v have_setting Flag indicating detection of a setting (unused)
|
|
455
|
+ * @v process Function used to process key=value pairs
|
|
456
|
+ * @ret rc Return status code
|
246
|
457
|
*/
|
247
|
|
-static EFI_HII_PACKAGE_LIST_HEADER *
|
248
|
|
-efi_snp_package_list ( struct efi_snp_device *snpdev ) {
|
249
|
|
- size_t strings_len = efi_snp_strings ( NULL, 0, snpdev );
|
250
|
|
- struct {
|
251
|
|
- EFI_HII_PACKAGE_LIST_HEADER header;
|
252
|
|
- struct efi_snp_formset formset;
|
253
|
|
- union {
|
254
|
|
- EFI_HII_STRING_PACKAGE_HDR strings;
|
255
|
|
- uint8_t pad[strings_len];
|
256
|
|
- } __attribute__ (( packed )) strings;
|
257
|
|
- EFI_HII_PACKAGE_HEADER end;
|
258
|
|
- } __attribute__ (( packed )) *package_list;
|
259
|
|
-
|
260
|
|
- /* Allocate package list */
|
261
|
|
- package_list = zalloc ( sizeof ( *package_list ) );
|
262
|
|
- if ( ! package_list )
|
263
|
|
- return NULL;
|
|
458
|
+static int efi_snp_hii_process ( struct efi_snp_device *snpdev,
|
|
459
|
+ wchar_t *string, wchar_t **progress,
|
|
460
|
+ wchar_t **results, int *have_setting,
|
|
461
|
+ int ( * process ) ( struct efi_snp_device *,
|
|
462
|
+ const char *key,
|
|
463
|
+ const char *value,
|
|
464
|
+ wchar_t **results,
|
|
465
|
+ int *have_setting ) ) {
|
|
466
|
+ wchar_t *wkey = string;
|
|
467
|
+ wchar_t *wend = string;
|
|
468
|
+ wchar_t *wvalue = NULL;
|
|
469
|
+ size_t key_len;
|
|
470
|
+ size_t value_len;
|
|
471
|
+ void *temp;
|
|
472
|
+ char *key;
|
|
473
|
+ char *value;
|
|
474
|
+ int rc;
|
|
475
|
+
|
|
476
|
+ /* Locate key, value (if any), and end */
|
|
477
|
+ while ( *wend ) {
|
|
478
|
+ if ( *wend == L'&' )
|
|
479
|
+ break;
|
|
480
|
+ if ( *(wend++) == L'=' )
|
|
481
|
+ wvalue = wend;
|
|
482
|
+ }
|
|
483
|
+
|
|
484
|
+ /* Allocate memory for key and value */
|
|
485
|
+ key_len = ( ( wvalue ? ( wvalue - 1 ) : wend ) - wkey );
|
|
486
|
+ value_len = ( wvalue ? ( wend - wvalue ) : 0 );
|
|
487
|
+ temp = zalloc ( key_len + 1 /* NUL */ + value_len + 1 /* NUL */ );
|
|
488
|
+ if ( ! temp )
|
|
489
|
+ return -ENOMEM;
|
|
490
|
+ key = temp;
|
|
491
|
+ value = ( temp + key_len + 1 /* NUL */ );
|
|
492
|
+
|
|
493
|
+ /* Copy key and value */
|
|
494
|
+ while ( key_len-- )
|
|
495
|
+ key[key_len] = wkey[key_len];
|
|
496
|
+ while ( value_len-- )
|
|
497
|
+ value[value_len] = wvalue[value_len];
|
|
498
|
+
|
|
499
|
+ /* Process key and value */
|
|
500
|
+ if ( ( rc = process ( snpdev, key, value, results,
|
|
501
|
+ have_setting ) ) != 0 ) {
|
|
502
|
+ goto err;
|
|
503
|
+ }
|
264
|
504
|
|
265
|
|
- /* Create a unique GUID for this package list and formset */
|
266
|
|
- efi_snp_formset.FormSet.FormSet.Guid.Data1++;
|
267
|
|
-
|
268
|
|
- /* Populate package list */
|
269
|
|
- memcpy ( &package_list->header.PackageListGuid,
|
270
|
|
- &efi_snp_formset.FormSet.FormSet.Guid,
|
271
|
|
- sizeof ( package_list->header.PackageListGuid ) );
|
272
|
|
- package_list->header.PackageLength = sizeof ( *package_list );
|
273
|
|
- memcpy ( &package_list->formset, &efi_snp_formset,
|
274
|
|
- sizeof ( package_list->formset ) );
|
275
|
|
- efi_snp_strings ( &package_list->strings.strings,
|
276
|
|
- sizeof ( package_list->strings ), snpdev );
|
277
|
|
- package_list->end.Length = sizeof ( package_list->end );
|
278
|
|
- package_list->end.Type = EFI_HII_PACKAGE_END;
|
279
|
|
-
|
280
|
|
- return &package_list->header;
|
|
505
|
+ /* Update progress marker */
|
|
506
|
+ *progress = wend;
|
|
507
|
+
|
|
508
|
+ err:
|
|
509
|
+ /* Free temporary storage */
|
|
510
|
+ free ( temp );
|
|
511
|
+
|
|
512
|
+ return rc;
|
281
|
513
|
}
|
282
|
514
|
|
283
|
515
|
/**
|
|
@@ -292,14 +524,47 @@ efi_snp_package_list ( struct efi_snp_device *snpdev ) {
|
292
|
524
|
static EFI_STATUS EFIAPI
|
293
|
525
|
efi_snp_hii_extract_config ( const EFI_HII_CONFIG_ACCESS_PROTOCOL *hii,
|
294
|
526
|
EFI_STRING request, EFI_STRING *progress,
|
295
|
|
- EFI_STRING *results __unused ) {
|
|
527
|
+ EFI_STRING *results ) {
|
296
|
528
|
struct efi_snp_device *snpdev =
|
297
|
529
|
container_of ( hii, struct efi_snp_device, hii );
|
|
530
|
+ int have_setting = 0;
|
|
531
|
+ wchar_t *pos;
|
|
532
|
+ int rc;
|
|
533
|
+
|
|
534
|
+ DBGC ( snpdev, "SNPDEV %p ExtractConfig request \"%ls\"\n",
|
|
535
|
+ snpdev, request );
|
|
536
|
+
|
|
537
|
+ /* Initialise results */
|
|
538
|
+ *results = NULL;
|
|
539
|
+
|
|
540
|
+ /* Process all request fragments */
|
|
541
|
+ for ( pos = *progress = request ; *progress && **progress ;
|
|
542
|
+ pos = *progress + 1 ) {
|
|
543
|
+ if ( ( rc = efi_snp_hii_process ( snpdev, pos, progress,
|
|
544
|
+ results, &have_setting,
|
|
545
|
+ efi_snp_hii_fetch ) ) != 0 ) {
|
|
546
|
+ return RC_TO_EFIRC ( rc );
|
|
547
|
+ }
|
|
548
|
+ }
|
298
|
549
|
|
299
|
|
- DBGC ( snpdev, "SNPDEV %p ExtractConfig \"%ls\"\n", snpdev, request );
|
|
550
|
+ /* If we have no explicit request, return all settings */
|
|
551
|
+ if ( ! have_setting ) {
|
|
552
|
+ struct setting *setting;
|
|
553
|
+
|
|
554
|
+ for_each_table_entry ( setting, SETTINGS ) {
|
|
555
|
+ if ( ! efi_snp_hii_setting_applies ( snpdev, setting ) )
|
|
556
|
+ continue;
|
|
557
|
+ if ( ( rc = efi_snp_hii_fetch ( snpdev, setting->name,
|
|
558
|
+ NULL, results,
|
|
559
|
+ NULL ) ) != 0 ) {
|
|
560
|
+ return rc;
|
|
561
|
+ }
|
|
562
|
+ }
|
|
563
|
+ }
|
300
|
564
|
|
301
|
|
- *progress = request;
|
302
|
|
- return EFI_INVALID_PARAMETER;
|
|
565
|
+ DBGC ( snpdev, "SNPDEV %p ExtractConfig results \"%ls\"\n",
|
|
566
|
+ snpdev, *results );
|
|
567
|
+ return 0;
|
303
|
568
|
}
|
304
|
569
|
|
305
|
570
|
/**
|
|
@@ -315,11 +580,22 @@ efi_snp_hii_route_config ( const EFI_HII_CONFIG_ACCESS_PROTOCOL *hii,
|
315
|
580
|
EFI_STRING config, EFI_STRING *progress ) {
|
316
|
581
|
struct efi_snp_device *snpdev =
|
317
|
582
|
container_of ( hii, struct efi_snp_device, hii );
|
|
583
|
+ wchar_t *pos;
|
|
584
|
+ int rc;
|
318
|
585
|
|
319
|
586
|
DBGC ( snpdev, "SNPDEV %p RouteConfig \"%ls\"\n", snpdev, config );
|
320
|
587
|
|
321
|
|
- *progress = config;
|
322
|
|
- return EFI_INVALID_PARAMETER;
|
|
588
|
+ /* Process all request fragments */
|
|
589
|
+ for ( pos = *progress = config ; *progress && **progress ;
|
|
590
|
+ pos = *progress + 1 ) {
|
|
591
|
+ if ( ( rc = efi_snp_hii_process ( snpdev, pos, progress,
|
|
592
|
+ NULL, NULL,
|
|
593
|
+ efi_snp_hii_store ) ) != 0 ) {
|
|
594
|
+ return RC_TO_EFIRC ( rc );
|
|
595
|
+ }
|
|
596
|
+ }
|
|
597
|
+
|
|
598
|
+ return 0;
|
323
|
599
|
}
|
324
|
600
|
|
325
|
601
|
/**
|
|
@@ -368,7 +644,7 @@ int efi_snp_hii_install ( struct efi_snp_device *snpdev ) {
|
368
|
644
|
memcpy ( &snpdev->hii, &efi_snp_device_hii, sizeof ( snpdev->hii ) );
|
369
|
645
|
|
370
|
646
|
/* Create HII package list */
|
371
|
|
- snpdev->package_list = efi_snp_package_list ( snpdev );
|
|
647
|
+ snpdev->package_list = efi_snp_hii_package_list ( snpdev );
|
372
|
648
|
if ( ! snpdev->package_list ) {
|
373
|
649
|
DBGC ( snpdev, "SNPDEV %p could not create HII package list\n",
|
374
|
650
|
snpdev );
|