Переглянути джерело

[efi] Expose net device non-volatile settings via HII

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 11 роки тому
джерело
коміт
a4d1250810
3 змінених файлів з 1143 додано та 339 видалено
  1. 74
    123
      src/include/ipxe/efi/efi_hii.h
  2. 577
    0
      src/interface/efi/efi_hii.c
  3. 492
    216
      src/interface/efi/efi_snp_hii.c

+ 74
- 123
src/include/ipxe/efi/efi_hii.h Переглянути файл

@@ -8,138 +8,89 @@
8 8
 
9 9
 FILE_LICENCE ( GPL2_OR_LATER );
10 10
 
11
+#include <string.h>
11 12
 #include <ipxe/efi/Uefi/UefiInternalFormRepresentation.h>
12 13
 #include <ipxe/efi/Guid/MdeModuleHii.h>
13 14
 
14
-/**
15
- * Define an EFI IFR form set type
16
- *
17
- * @v num_class_guids	Number of class GUIDs
18
- * @ret type		Form set type
19
- */
20
-#define EFI_IFR_FORM_SET_TYPE( num_class_guids )			   \
21
-	struct {							   \
22
-		EFI_IFR_FORM_SET FormSet;				   \
23
-		EFI_GUID ClassGuid[num_class_guids];			   \
24
-	} __attribute__ (( packed ))
25
-
26
-/**
27
- * Define an EFI IFR form set
28
- *
29
- * @v guid		GUID
30
- * @v title		Title string
31
- * @v help		Help string
32
- * @v type		Form set type (as returned by EFI_IFR_FORM_SET_TYPE())
33
- * @ret ifr		Form set
34
- *
35
- * This definition opens a new scope, which must be closed by an
36
- * EFI_IFR_END().
37
- */
38
-#define EFI_IFR_FORM_SET( guid, title, help, type, ... ) {		   \
39
-	.FormSet = {							   \
40
-		.Header = {						   \
41
-			.OpCode = EFI_IFR_FORM_SET_OP,			   \
42
-			.Length = sizeof ( type ),			   \
43
-			.Scope = 1,					   \
44
-		},							   \
45
-		.Guid = guid,						   \
46
-		.FormSetTitle = title,					   \
47
-		.Help = help,						   \
48
-		.Flags = ( sizeof ( ( ( type * ) NULL )->ClassGuid ) /	   \
49
-			   sizeof ( ( ( type * ) NULL )->ClassGuid[0] ) ), \
50
-	},								   \
51
-	.ClassGuid = {							   \
52
-		__VA_ARGS__						   \
53
-	},								   \
54
-	}
55
-
56
-/**
57
- * Define an EFI IFR GUID class
58
- *
59
- * @v class		Class
60
- * @ret ifr		GUID class
61
- */
62
-#define EFI_IFR_GUID_CLASS( class ) {					   \
63
-	.Header = {							   \
64
-		.OpCode = EFI_IFR_GUID_OP,				   \
65
-		.Length = sizeof ( EFI_IFR_GUID_CLASS ),		   \
66
-	},								   \
67
-	.Guid = EFI_IFR_TIANO_GUID,					   \
68
-	.ExtendOpCode = EFI_IFR_EXTEND_OP_CLASS,			   \
69
-	.Class = class,							   \
70
-	}
71
-
72
-/**
73
- * Define an EFI IFR GUID subclass
74
- *
75
- * @v subclass		Subclass
76
- * @ret ifr		GUID subclass
77
- */
78
-#define EFI_IFR_GUID_SUBCLASS( subclass ) {				   \
79
-	.Header = {							   \
80
-		.OpCode = EFI_IFR_GUID_OP,				   \
81
-		.Length = sizeof ( EFI_IFR_GUID_SUBCLASS ),		   \
82
-	},								   \
83
-	.Guid = EFI_IFR_TIANO_GUID,					   \
84
-	.ExtendOpCode = EFI_IFR_EXTEND_OP_SUBCLASS,			   \
85
-	.SubClass = subclass,						   \
86
-	}
15
+/** GUID indicating formset compliance for IBM Unified Configuration Manager */
16
+#define EFI_HII_IBM_UCM_COMPLIANT_FORMSET_GUID				   \
17
+	{ 0x5c8e9746, 0xa5f7, 0x4593,					   \
18
+	  { 0xaf, 0x1f, 0x66, 0xa8, 0x2a, 0xa1, 0x9c, 0xb1 } }
87 19
 
88
-/**
89
- * Define an EFI IFR form
90
- *
91
- * @v formid		Form ID
92
- * @v title		Title string
93
- * @ret ifr		Form
94
- *
95
- * This definition opens a new scope, which must be closed by an
96
- * EFI_IFR_END().
97
- */
98
-#define EFI_IFR_FORM( formid, title ) {					   \
99
-	.Header = {							   \
100
-		.OpCode = EFI_IFR_FORM_OP,				   \
101
-		.Length = sizeof ( EFI_IFR_FORM ),			   \
102
-		.Scope = 1,						   \
103
-	},								   \
104
-	.FormId = formid,						   \
105
-	.FormTitle = title,						   \
106
-	}
20
+/** An EFI IFR builder */
21
+struct efi_ifr_builder {
22
+	/** IFR opcodes */
23
+	EFI_IFR_OP_HEADER *ops;
24
+	/** Length of IFR opcodes */
25
+	size_t ops_len;
26
+	/** Strings */
27
+	EFI_HII_STRING_BLOCK *strings;
28
+	/** Length of strings */
29
+	size_t strings_len;
30
+	/** Current string identifier */
31
+	unsigned int string_id;
32
+	/** Current variable store identifier */
33
+	unsigned int varstore_id;
34
+	/** Current form identifier */
35
+	unsigned int form_id;
36
+	/** An allocation has failed */
37
+	int failed;
38
+};
107 39
 
108 40
 /**
109
- * Define an EFI IFR text widget
41
+ * Initialise IFR builder
110 42
  *
111
- * @v prompt		Prompt string
112
- * @v help		Help string
113
- * @v text		Text string
114
- * @ret ifr		Text widget
115
- */
116
-#define EFI_IFR_TEXT( prompt, help, text ) {				   \
117
-	.Header = {							   \
118
-		.OpCode = EFI_IFR_TEXT_OP,				   \
119
-		.Length = sizeof ( EFI_IFR_TEXT ),			   \
120
-	},								   \
121
-	.Statement = {							   \
122
-		.Prompt = prompt,					   \
123
-		.Help = help,						   \
124
-	},								   \
125
-	.TextTwo = text,						   \
126
-	}
127
-
128
-/**
129
- * Define an EFI IFR end marker
43
+ * @v ifr		IFR builder
130 44
  *
131
- * @ret ifr		End marker
45
+ * The caller must eventually call efi_ifr_free() to free the dynamic
46
+ * storage associated with the IFR builder.
132 47
  */
133
-#define EFI_IFR_END() {							   \
134
-	.Header = {							   \
135
-		.OpCode = EFI_IFR_END_OP,				   \
136
-		.Length = sizeof ( EFI_IFR_END ),			   \
137
-	},								   \
138
-	}
48
+static inline void efi_ifr_init ( struct efi_ifr_builder *ifr ) {
49
+	memset ( ifr, 0, sizeof ( *ifr ) );
50
+}
139 51
 
140
-/** GUID indicating formset compliance for IBM Unified Configuration Manager */
141
-#define EFI_HII_IBM_UCM_COMPLIANT_FORMSET_GUID				   \
142
-	{ 0x5c8e9746, 0xa5f7, 0x4593,					   \
143
-	  { 0xaf, 0x1f, 0x66, 0xa8, 0x2a, 0xa1, 0x9c, 0xb1 } }
52
+extern unsigned int efi_ifr_string ( struct efi_ifr_builder *ifr,
53
+				     const char *fmt, ... );
54
+extern void efi_ifr_end_op ( struct efi_ifr_builder *ifr );
55
+extern void efi_ifr_false_op ( struct efi_ifr_builder *ifr );
56
+extern unsigned int efi_ifr_form_op ( struct efi_ifr_builder *ifr,
57
+				      unsigned int title_id );
58
+extern void efi_ifr_form_set_op ( struct efi_ifr_builder *ifr,
59
+				  const EFI_GUID *guid,
60
+				  unsigned int title_id, unsigned int help_id,
61
+				  ... );
62
+void efi_ifr_get_op ( struct efi_ifr_builder *ifr, unsigned int varstore_id,
63
+		      unsigned int varstore_info, unsigned int varstore_type );
64
+extern void efi_ifr_guid_class_op ( struct efi_ifr_builder *ifr,
65
+				    unsigned int class );
66
+extern void efi_ifr_guid_subclass_op ( struct efi_ifr_builder *ifr,
67
+				       unsigned int subclass );
68
+extern void efi_ifr_numeric_op ( struct efi_ifr_builder *ifr,
69
+				 unsigned int prompt_id,
70
+				 unsigned int help_id, unsigned int question_id,
71
+				 unsigned int varstore_id,
72
+				 unsigned int varstore_info,
73
+				 unsigned int vflags, unsigned long min_value,
74
+				 unsigned long max_value, unsigned int step,
75
+				 unsigned int flags );
76
+extern void efi_ifr_string_op ( struct efi_ifr_builder *ifr,
77
+				unsigned int prompt_id, unsigned int help_id,
78
+				unsigned int question_id,
79
+				unsigned int varstore_id,
80
+				unsigned int varstore_info, unsigned int vflags,
81
+				unsigned int min_size, unsigned int max_size,
82
+				unsigned int flags );
83
+extern void efi_ifr_suppress_if_op ( struct efi_ifr_builder *ifr );
84
+extern void efi_ifr_text_op ( struct efi_ifr_builder *ifr,
85
+			      unsigned int prompt_id, unsigned int help_id,
86
+			      unsigned int text_id );
87
+extern void efi_ifr_true_op ( struct efi_ifr_builder *ifr );
88
+extern unsigned int
89
+efi_ifr_varstore_name_value_op ( struct efi_ifr_builder *ifr,
90
+				 const EFI_GUID *guid );
91
+extern void efi_ifr_free ( struct efi_ifr_builder *ifr );
92
+extern EFI_HII_PACKAGE_LIST_HEADER *
93
+efi_ifr_package ( struct efi_ifr_builder *ifr, const EFI_GUID *guid,
94
+		  const char *language, unsigned int language_id );
144 95
 
145 96
 #endif /* _IPXE_EFI_HII_H */

+ 577
- 0
src/interface/efi/efi_hii.c Переглянути файл

@@ -0,0 +1,577 @@
1
+/*
2
+ * Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>.
3
+ *
4
+ * This program is free software; you can redistribute it and/or
5
+ * modify it under the terms of the GNU General Public License as
6
+ * published by the Free Software Foundation; either version 2 of the
7
+ * License, or any later version.
8
+ *
9
+ * This program is distributed in the hope that it will be useful, but
10
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
+ * General Public License for more details.
13
+ *
14
+ * You should have received a copy of the GNU General Public License
15
+ * along with this program; if not, write to the Free Software
16
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17
+ * 02110-1301, USA.
18
+ */
19
+
20
+FILE_LICENCE ( GPL2_OR_LATER );
21
+
22
+#include <stdlib.h>
23
+#include <stddef.h>
24
+#include <stdarg.h>
25
+#include <string.h>
26
+#include <ipxe/efi/efi.h>
27
+#include <ipxe/efi/efi_strings.h>
28
+#include <ipxe/efi/efi_hii.h>
29
+
30
+/** Tiano GUID */
31
+static const EFI_GUID tiano_guid = EFI_IFR_TIANO_GUID;
32
+
33
+/**
34
+ * Add string to IFR builder
35
+ *
36
+ * @v ifr		IFR builder
37
+ * @v fmt		Format string
38
+ * @v ...		Arguments
39
+ * @ret string_id	String identifier, or zero on failure
40
+ */
41
+unsigned int efi_ifr_string ( struct efi_ifr_builder *ifr, const char *fmt,
42
+			      ... ) {
43
+	EFI_HII_STRING_BLOCK *new_strings;
44
+	EFI_HII_SIBT_STRING_UCS2_BLOCK *ucs2;
45
+	size_t new_strings_len;
46
+	va_list args;
47
+	size_t len;
48
+	unsigned int string_id;
49
+
50
+	/* Do nothing if a previous allocation has failed */
51
+	if ( ifr->failed )
52
+		return 0;
53
+
54
+	/* Calculate string length */
55
+	va_start ( args, fmt );
56
+	len = ( efi_vsnprintf ( NULL, 0, fmt, args ) + 1 /* wNUL */ );
57
+	va_end ( args );
58
+
59
+	/* Reallocate strings */
60
+	new_strings_len = ( ifr->strings_len +
61
+			    offsetof ( typeof ( *ucs2 ), StringText ) +
62
+			    ( len * sizeof ( ucs2->StringText[0] ) ) );
63
+	new_strings = realloc ( ifr->strings, new_strings_len );
64
+	if ( ! new_strings ) {
65
+		ifr->failed = 1;
66
+		return 0;
67
+	}
68
+	ucs2 = ( ( ( void * ) new_strings ) + ifr->strings_len );
69
+	ifr->strings = new_strings;
70
+	ifr->strings_len = new_strings_len;
71
+
72
+	/* Fill in string */
73
+	ucs2->Header.BlockType = EFI_HII_SIBT_STRING_UCS2;
74
+	va_start ( args, fmt );
75
+	efi_vsnprintf ( ucs2->StringText, len, fmt, args );
76
+	va_end ( args );
77
+
78
+	/* Allocate string ID */
79
+	string_id = ++(ifr->string_id);
80
+
81
+	DBGC ( ifr, "IFR %p string %#04x is \"%ls\"\n",
82
+	       ifr, string_id, ucs2->StringText );
83
+	return string_id;
84
+}
85
+
86
+/**
87
+ * Add IFR opcode to IFR builder
88
+ *
89
+ * @v ifr		IFR builder
90
+ * @v opcode		Opcode
91
+ * @v len		Opcode length
92
+ * @ret op		Opcode, or NULL
93
+ */
94
+static void * efi_ifr_op ( struct efi_ifr_builder *ifr, unsigned int opcode,
95
+			   size_t len ) {
96
+	EFI_IFR_OP_HEADER *new_ops;
97
+	EFI_IFR_OP_HEADER *op;
98
+	size_t new_ops_len;
99
+
100
+	/* Do nothing if a previous allocation has failed */
101
+	if ( ifr->failed )
102
+		return NULL;
103
+
104
+	/* Reallocate opcodes */
105
+	new_ops_len = ( ifr->ops_len + len );
106
+	new_ops = realloc ( ifr->ops, new_ops_len );
107
+	if ( ! new_ops ) {
108
+		ifr->failed = 1;
109
+		return NULL;
110
+	}
111
+	op = ( ( ( void * ) new_ops ) + ifr->ops_len );
112
+	ifr->ops = new_ops;
113
+	ifr->ops_len = new_ops_len;
114
+
115
+	/* Fill in opcode header */
116
+	op->OpCode = opcode;
117
+	op->Length = len;
118
+
119
+	return op;
120
+}
121
+
122
+/**
123
+ * Add end opcode to IFR builder
124
+ *
125
+ * @v ifr		IFR builder
126
+ */
127
+void efi_ifr_end_op ( struct efi_ifr_builder *ifr ) {
128
+	size_t dispaddr = ifr->ops_len;
129
+	EFI_IFR_END *end;
130
+
131
+	/* Add opcode */
132
+	end = efi_ifr_op ( ifr, EFI_IFR_END_OP, sizeof ( *end ) );
133
+
134
+	DBGC ( ifr, "IFR %p end\n", ifr );
135
+	DBGC2_HDA ( ifr, dispaddr, end, sizeof ( *end ) );
136
+}
137
+
138
+/**
139
+ * Add false opcode to IFR builder
140
+ *
141
+ * @v ifr		IFR builder
142
+ */
143
+void efi_ifr_false_op ( struct efi_ifr_builder *ifr ) {
144
+	size_t dispaddr = ifr->ops_len;
145
+	EFI_IFR_FALSE *false;
146
+
147
+	/* Add opcode */
148
+	false = efi_ifr_op ( ifr, EFI_IFR_FALSE_OP, sizeof ( *false ) );
149
+
150
+	DBGC ( ifr, "IFR %p false\n", ifr );
151
+	DBGC2_HDA ( ifr, dispaddr, false, sizeof ( *false ) );
152
+}
153
+
154
+/**
155
+ * Add form opcode to IFR builder
156
+ *
157
+ * @v ifr		IFR builder
158
+ * @v title_id		Title string identifier
159
+ * @ret form_id		Form identifier
160
+ */
161
+unsigned int efi_ifr_form_op ( struct efi_ifr_builder *ifr,
162
+			       unsigned int title_id ) {
163
+	size_t dispaddr = ifr->ops_len;
164
+	EFI_IFR_FORM *form;
165
+
166
+	/* Add opcode */
167
+	form = efi_ifr_op ( ifr, EFI_IFR_FORM_OP, sizeof ( *form ) );
168
+	if ( ! form )
169
+		return 0;
170
+	form->Header.Scope = 1;
171
+	form->FormId = ++(ifr->form_id);
172
+	form->FormTitle = title_id;
173
+
174
+	DBGC ( ifr, "IFR %p name/value store %#04x title %#04x\n",
175
+	       ifr, form->FormId, title_id );
176
+	DBGC2_HDA ( ifr, dispaddr, form, sizeof ( *form ) );
177
+	return form->FormId;
178
+}
179
+
180
+/**
181
+ * Add formset opcode to IFR builder
182
+ *
183
+ * @v ifr		IFR builder
184
+ * @v guid		GUID
185
+ * @v title_id		Title string identifier
186
+ * @v help_id		Help string identifier
187
+ * @v ...		Class GUIDs (terminated by NULL)
188
+ */
189
+void efi_ifr_form_set_op ( struct efi_ifr_builder *ifr, const EFI_GUID *guid,
190
+			   unsigned int title_id, unsigned int help_id, ... ) {
191
+	size_t dispaddr = ifr->ops_len;
192
+	EFI_IFR_FORM_SET *formset;
193
+	EFI_GUID *class_guid;
194
+	unsigned int num_class_guids = 0;
195
+	size_t len;
196
+	va_list args;
197
+
198
+	/* Count number of class GUIDs */
199
+	va_start ( args, help_id );
200
+	while ( va_arg ( args, const EFI_GUID * ) != NULL )
201
+		num_class_guids++;
202
+	va_end ( args );
203
+
204
+	/* Add opcode */
205
+	len = ( sizeof ( *formset ) +
206
+		( num_class_guids * sizeof ( *class_guid ) ) );
207
+	formset = efi_ifr_op ( ifr, EFI_IFR_FORM_SET_OP, len );
208
+	if ( ! formset )
209
+		return;
210
+	formset->Header.Scope = 1;
211
+	memcpy ( &formset->Guid, guid, sizeof ( formset->Guid ) );
212
+	formset->FormSetTitle = title_id;
213
+	formset->Help = help_id;
214
+	formset->Flags = num_class_guids;
215
+
216
+	/* Add class GUIDs */
217
+	class_guid = ( ( ( void * ) formset ) + sizeof ( *formset ) );
218
+	va_start ( args, help_id );
219
+	while ( num_class_guids-- ) {
220
+		memcpy ( class_guid++, va_arg ( args, const EFI_GUID * ),
221
+			 sizeof ( *class_guid ) );
222
+	}
223
+	va_end ( args );
224
+
225
+	DBGC ( ifr, "IFR %p formset title %#04x help %#04x\n",
226
+	       ifr, title_id, help_id );
227
+	DBGC2_HDA ( ifr, dispaddr, formset, len );
228
+}
229
+
230
+/**
231
+ * Add get opcode to IFR builder
232
+ *
233
+ * @v ifr		IFR builder
234
+ * @v varstore_id	Variable store identifier
235
+ * @v varstore_info	Variable string identifier or offset
236
+ * @v varstore_type	Variable type
237
+ */
238
+void efi_ifr_get_op ( struct efi_ifr_builder *ifr, unsigned int varstore_id,
239
+		      unsigned int varstore_info, unsigned int varstore_type ) {
240
+	size_t dispaddr = ifr->ops_len;
241
+	EFI_IFR_GET *get;
242
+
243
+	/* Add opcode */
244
+	get = efi_ifr_op ( ifr, EFI_IFR_GET_OP, sizeof ( *get ) );
245
+	get->VarStoreId = varstore_id;
246
+	get->VarStoreInfo.VarName = varstore_info;
247
+	get->VarStoreType = varstore_type;
248
+
249
+	DBGC ( ifr, "IFR %p get varstore %#04x:%#04x type %#02x\n",
250
+	       ifr, varstore_id, varstore_info, varstore_type );
251
+	DBGC2_HDA ( ifr, dispaddr, get, sizeof ( *get ) );
252
+}
253
+
254
+/**
255
+ * Add GUID class opcode to IFR builder
256
+ *
257
+ * @v ifr		IFR builder
258
+ * @v class		Class
259
+ */
260
+void efi_ifr_guid_class_op ( struct efi_ifr_builder *ifr, unsigned int class ) {
261
+	size_t dispaddr = ifr->ops_len;
262
+	EFI_IFR_GUID_CLASS *guid_class;
263
+
264
+	/* Add opcode */
265
+	guid_class = efi_ifr_op ( ifr, EFI_IFR_GUID_OP,
266
+				  sizeof ( *guid_class ) );
267
+	if ( ! guid_class )
268
+		return;
269
+	memcpy ( &guid_class->Guid, &tiano_guid, sizeof ( guid_class->Guid ) );
270
+	guid_class->ExtendOpCode = EFI_IFR_EXTEND_OP_CLASS;
271
+	guid_class->Class = class;
272
+
273
+	DBGC ( ifr, "IFR %p GUID class %#02x\n", ifr, class );
274
+	DBGC2_HDA ( ifr, dispaddr, guid_class, sizeof ( *guid_class ) );
275
+}
276
+
277
+/**
278
+ * Add GUID subclass opcode to IFR builder
279
+ *
280
+ * @v ifr		IFR builder
281
+ * @v subclass		Subclass
282
+ */
283
+void efi_ifr_guid_subclass_op ( struct efi_ifr_builder *ifr,
284
+				unsigned int subclass ) {
285
+	size_t dispaddr = ifr->ops_len;
286
+	EFI_IFR_GUID_SUBCLASS *guid_subclass;
287
+
288
+	/* Add opcode */
289
+	guid_subclass = efi_ifr_op ( ifr, EFI_IFR_GUID_OP,
290
+				     sizeof ( *guid_subclass ) );
291
+	if ( ! guid_subclass )
292
+		return;
293
+	memcpy ( &guid_subclass->Guid, &tiano_guid,
294
+		 sizeof ( guid_subclass->Guid ) );
295
+	guid_subclass->ExtendOpCode = EFI_IFR_EXTEND_OP_SUBCLASS;
296
+	guid_subclass->SubClass = subclass;
297
+
298
+	DBGC ( ifr, "IFR %p GUID subclass %#02x\n", ifr, subclass );
299
+	DBGC2_HDA ( ifr, dispaddr, guid_subclass, sizeof ( *guid_subclass ) );
300
+}
301
+
302
+/**
303
+ * Add numeric opcode to IFR builder
304
+ *
305
+ * @v ifr		IFR builder
306
+ * @v prompt_id		Prompt string identifier
307
+ * @v help_id		Help string identifier
308
+ * @v question_id	Question identifier
309
+ * @v varstore_id	Variable store identifier
310
+ * @v varstore_info	Variable string identifier or offset
311
+ * @v vflags		Variable flags
312
+ * @v min_value		Minimum value
313
+ * @v max_value		Maximum value
314
+ * @v step		Step
315
+ * @v flags		Flags
316
+ */
317
+void efi_ifr_numeric_op ( struct efi_ifr_builder *ifr, unsigned int prompt_id,
318
+			  unsigned int help_id, unsigned int question_id,
319
+			  unsigned int varstore_id, unsigned int varstore_info,
320
+			  unsigned int vflags, unsigned long min_value,
321
+			  unsigned long max_value, unsigned int step,
322
+			  unsigned int flags ) {
323
+	size_t dispaddr = ifr->ops_len;
324
+	EFI_IFR_NUMERIC *numeric;
325
+	unsigned int size;
326
+
327
+	/* Add opcode */
328
+	numeric = efi_ifr_op ( ifr, EFI_IFR_NUMERIC_OP, sizeof ( *numeric ) );
329
+	if ( ! numeric )
330
+		return;
331
+	numeric->Question.Header.Prompt = prompt_id;
332
+	numeric->Question.Header.Help = help_id;
333
+	numeric->Question.QuestionId = question_id;
334
+	numeric->Question.VarStoreId = varstore_id;
335
+	numeric->Question.VarStoreInfo.VarName = varstore_info;
336
+	numeric->Question.Flags = vflags;
337
+	size = ( flags & EFI_IFR_NUMERIC_SIZE );
338
+	switch ( size ) {
339
+	case EFI_IFR_NUMERIC_SIZE_1 :
340
+		numeric->data.u8.MinValue = min_value;
341
+		numeric->data.u8.MaxValue = max_value;
342
+		numeric->data.u8.Step = step;
343
+		break;
344
+	case EFI_IFR_NUMERIC_SIZE_2 :
345
+		numeric->data.u16.MinValue = min_value;
346
+		numeric->data.u16.MaxValue = max_value;
347
+		numeric->data.u16.Step = step;
348
+		break;
349
+	case EFI_IFR_NUMERIC_SIZE_4 :
350
+		numeric->data.u32.MinValue = min_value;
351
+		numeric->data.u32.MaxValue = max_value;
352
+		numeric->data.u32.Step = step;
353
+		break;
354
+	case EFI_IFR_NUMERIC_SIZE_8 :
355
+		numeric->data.u64.MinValue = min_value;
356
+		numeric->data.u64.MaxValue = max_value;
357
+		numeric->data.u64.Step = step;
358
+		break;
359
+	}
360
+
361
+	DBGC ( ifr, "IFR %p numeric prompt %#04x help %#04x question %#04x "
362
+	       "varstore %#04x:%#04x\n", ifr, prompt_id, help_id, question_id,
363
+	       varstore_id, varstore_info );
364
+	DBGC2_HDA ( ifr, dispaddr, numeric, sizeof ( *numeric ) );
365
+}
366
+
367
+/**
368
+ * Add string opcode to IFR builder
369
+ *
370
+ * @v ifr		IFR builder
371
+ * @v prompt_id		Prompt string identifier
372
+ * @v help_id		Help string identifier
373
+ * @v question_id	Question identifier
374
+ * @v varstore_id	Variable store identifier
375
+ * @v varstore_info	Variable string identifier or offset
376
+ * @v vflags		Variable flags
377
+ * @v min_size		Minimum size
378
+ * @v max_size		Maximum size
379
+ * @v flags		Flags
380
+ */
381
+void efi_ifr_string_op ( struct efi_ifr_builder *ifr, unsigned int prompt_id,
382
+			 unsigned int help_id, unsigned int question_id,
383
+			 unsigned int varstore_id, unsigned int varstore_info,
384
+			 unsigned int vflags, unsigned int min_size,
385
+			 unsigned int max_size, unsigned int flags ) {
386
+	size_t dispaddr = ifr->ops_len;
387
+	EFI_IFR_STRING *string;
388
+
389
+	/* Add opcode */
390
+	string = efi_ifr_op ( ifr, EFI_IFR_STRING_OP, sizeof ( *string ) );
391
+	if ( ! string )
392
+		return;
393
+	string->Question.Header.Prompt = prompt_id;
394
+	string->Question.Header.Help = help_id;
395
+	string->Question.QuestionId = question_id;
396
+	string->Question.VarStoreId = varstore_id;
397
+	string->Question.VarStoreInfo.VarName = varstore_info;
398
+	string->Question.Flags = vflags;
399
+	string->MinSize = min_size;
400
+	string->MaxSize = max_size;
401
+	string->Flags = flags;
402
+
403
+	DBGC ( ifr, "IFR %p string prompt %#04x help %#04x question %#04x "
404
+	       "varstore %#04x:%#04x\n", ifr, prompt_id, help_id, question_id,
405
+	       varstore_id, varstore_info );
406
+	DBGC2_HDA ( ifr, dispaddr, string, sizeof ( *string ) );
407
+}
408
+
409
+/**
410
+ * Add suppress-if opcode to IFR builder
411
+ *
412
+ * @v ifr		IFR builder
413
+ */
414
+void efi_ifr_suppress_if_op ( struct efi_ifr_builder *ifr ) {
415
+	size_t dispaddr = ifr->ops_len;
416
+	EFI_IFR_SUPPRESS_IF *suppress_if;
417
+
418
+	/* Add opcode */
419
+	suppress_if = efi_ifr_op ( ifr, EFI_IFR_SUPPRESS_IF_OP,
420
+				   sizeof ( *suppress_if ) );
421
+	suppress_if->Header.Scope = 1;
422
+
423
+	DBGC ( ifr, "IFR %p suppress-if\n", ifr );
424
+	DBGC2_HDA ( ifr, dispaddr, suppress_if, sizeof ( *suppress_if ) );
425
+}
426
+
427
+/**
428
+ * Add text opcode to IFR builder
429
+ *
430
+ * @v ifr		IFR builder
431
+ * @v prompt_id		Prompt string identifier
432
+ * @v help_id		Help string identifier
433
+ * @v text_id		Text string identifier
434
+ */
435
+void efi_ifr_text_op ( struct efi_ifr_builder *ifr, unsigned int prompt_id,
436
+		       unsigned int help_id, unsigned int text_id ) {
437
+	size_t dispaddr = ifr->ops_len;
438
+	EFI_IFR_TEXT *text;
439
+
440
+	/* Add opcode */
441
+	text = efi_ifr_op ( ifr, EFI_IFR_TEXT_OP, sizeof ( *text ) );
442
+	if ( ! text )
443
+		return;
444
+	text->Statement.Prompt = prompt_id;
445
+	text->Statement.Help = help_id;
446
+	text->TextTwo = text_id;
447
+
448
+	DBGC ( ifr, "IFR %p text prompt %#04x help %#04x text %#04x\n",
449
+	       ifr, prompt_id, help_id, text_id );
450
+	DBGC2_HDA ( ifr, dispaddr, text, sizeof ( *text ) );
451
+}
452
+
453
+/**
454
+ * Add true opcode to IFR builder
455
+ *
456
+ * @v ifr		IFR builder
457
+ */
458
+void efi_ifr_true_op ( struct efi_ifr_builder *ifr ) {
459
+	size_t dispaddr = ifr->ops_len;
460
+	EFI_IFR_TRUE *true;
461
+
462
+	/* Add opcode */
463
+	true = efi_ifr_op ( ifr, EFI_IFR_TRUE_OP, sizeof ( *true ) );
464
+
465
+	DBGC ( ifr, "IFR %p true\n", ifr );
466
+	DBGC2_HDA ( ifr, dispaddr, true, sizeof ( *true ) );
467
+}
468
+
469
+/**
470
+ * Add name/value store opcode to IFR builder
471
+ *
472
+ * @v ifr		IFR builder
473
+ * @v guid		GUID
474
+ * @ret varstore_id	Variable store identifier, or 0 on failure
475
+ */
476
+unsigned int efi_ifr_varstore_name_value_op ( struct efi_ifr_builder *ifr,
477
+					      const EFI_GUID *guid ) {
478
+	size_t dispaddr = ifr->ops_len;
479
+	EFI_IFR_VARSTORE_NAME_VALUE *varstore;
480
+
481
+	/* Add opcode */
482
+	varstore = efi_ifr_op ( ifr, EFI_IFR_VARSTORE_NAME_VALUE_OP,
483
+				sizeof ( *varstore ) );
484
+	if ( ! varstore )
485
+		return 0;
486
+	varstore->VarStoreId = ++(ifr->varstore_id);
487
+	memcpy ( &varstore->Guid, guid, sizeof ( varstore->Guid ) );
488
+
489
+	DBGC ( ifr, "IFR %p name/value store %#04x\n",
490
+	       ifr, varstore->VarStoreId );
491
+	DBGC2_HDA ( ifr, dispaddr, varstore, sizeof ( *varstore ) );
492
+	return varstore->VarStoreId;
493
+}
494
+
495
+/**
496
+ * Free memory used by IFR builder
497
+ *
498
+ * @v ifr		IFR builder
499
+ */
500
+void efi_ifr_free ( struct efi_ifr_builder *ifr ) {
501
+
502
+	free ( ifr->ops );
503
+	free ( ifr->strings );
504
+	memset ( ifr, 0, sizeof ( *ifr ) );
505
+}
506
+
507
+/**
508
+ * Construct package list from IFR builder
509
+ *
510
+ * @v ifr		IFR builder
511
+ * @v guid		Package GUID
512
+ * @v language		Language
513
+ * @v language_id	Language string ID
514
+ * @ret package		Package list, or NULL
515
+ *
516
+ * The package list is allocated using malloc(), and must eventually
517
+ * be freed by the caller.  (The caller must also call efi_ifr_free()
518
+ * to free the temporary storage used during construction.)
519
+ */
520
+EFI_HII_PACKAGE_LIST_HEADER * efi_ifr_package ( struct efi_ifr_builder *ifr,
521
+						const EFI_GUID *guid,
522
+						const char *language,
523
+						unsigned int language_id ) {
524
+	struct {
525
+		EFI_HII_PACKAGE_LIST_HEADER header;
526
+		struct {
527
+			EFI_HII_PACKAGE_HEADER header;
528
+			uint8_t data[ifr->ops_len];
529
+		} __attribute__ (( packed )) ops;
530
+		struct {
531
+			union {
532
+				EFI_HII_STRING_PACKAGE_HDR header;
533
+				uint8_t pad[offsetof(EFI_HII_STRING_PACKAGE_HDR,
534
+						     Language) +
535
+					    strlen ( language ) + 1 /* NUL */ ];
536
+			} __attribute__ (( packed )) header;
537
+			uint8_t data[ifr->strings_len];
538
+			EFI_HII_STRING_BLOCK end;
539
+		} __attribute__ (( packed )) strings;
540
+		EFI_HII_PACKAGE_HEADER end;
541
+	} __attribute__ (( packed )) *package;
542
+
543
+	/* Fail if any previous allocation failed */
544
+	if ( ifr->failed )
545
+		return NULL;
546
+
547
+	/* Allocate package list */
548
+	package = zalloc ( sizeof ( *package ) );
549
+	if ( ! package )
550
+		return NULL;
551
+
552
+	/* Populate package list */
553
+	package->header.PackageLength = sizeof ( *package );
554
+	memcpy ( &package->header.PackageListGuid, guid,
555
+		 sizeof ( package->header.PackageListGuid ) );
556
+	package->ops.header.Length = sizeof ( package->ops );
557
+	package->ops.header.Type = EFI_HII_PACKAGE_FORMS;
558
+	memcpy ( package->ops.data, ifr->ops, sizeof ( package->ops.data ) );
559
+	package->strings.header.header.Header.Length =
560
+		sizeof ( package->strings );
561
+	package->strings.header.header.Header.Type =
562
+		EFI_HII_PACKAGE_STRINGS;
563
+	package->strings.header.header.HdrSize =
564
+		sizeof ( package->strings.header );
565
+	package->strings.header.header.StringInfoOffset =
566
+		sizeof ( package->strings.header );
567
+	package->strings.header.header.LanguageName = language_id;
568
+	strcpy ( package->strings.header.header.Language, language );
569
+	memcpy ( package->strings.data, ifr->strings,
570
+		 sizeof ( package->strings.data ) );
571
+	package->strings.end.BlockType = EFI_HII_SIBT_END;
572
+	package->end.Type = EFI_HII_PACKAGE_END;
573
+	package->end.Length = sizeof ( package->end );
574
+
575
+	return &package->header;
576
+}
577
+

+ 492
- 216
src/interface/efi/efi_snp_hii.c Переглянути файл

@@ -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 );

Завантаження…
Відмінити
Зберегти