]>
Commit | Line | Data |
---|---|---|
c9bfb222 LL |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * EFI Human Interface Infrastructure ... database and packages | |
4 | * | |
5 | * Copyright (c) 2017 Leif Lindholm | |
6 | * Copyright (c) 2018 AKASHI Takahiro, Linaro Limited | |
7 | */ | |
8 | ||
9 | #include <common.h> | |
10 | #include <efi_loader.h> | |
11 | #include <malloc.h> | |
12 | #include <asm/unaligned.h> | |
13 | ||
14 | const efi_guid_t efi_guid_hii_database_protocol | |
15 | = EFI_HII_DATABASE_PROTOCOL_GUID; | |
16 | const efi_guid_t efi_guid_hii_string_protocol = EFI_HII_STRING_PROTOCOL_GUID; | |
17 | ||
18 | static LIST_HEAD(efi_package_lists); | |
8d3b77e3 | 19 | static LIST_HEAD(efi_keyboard_layout_list); |
c9bfb222 LL |
20 | |
21 | struct efi_hii_packagelist { | |
22 | struct list_head link; | |
23 | // TODO should there be an associated efi_object? | |
24 | efi_handle_t driver_handle; | |
25 | u32 max_string_id; | |
26 | struct list_head string_tables; /* list of efi_string_table */ | |
9ab0bdd9 | 27 | struct list_head guid_list; |
8d3b77e3 | 28 | struct list_head keyboard_packages; |
c9bfb222 LL |
29 | |
30 | /* we could also track fonts, images, etc */ | |
31 | }; | |
32 | ||
33 | static int efi_hii_packagelist_exists(efi_hii_handle_t package_list) | |
34 | { | |
35 | struct efi_hii_packagelist *hii; | |
36 | int found = 0; | |
37 | ||
38 | list_for_each_entry(hii, &efi_package_lists, link) { | |
39 | if (hii == package_list) { | |
40 | found = 1; | |
41 | break; | |
42 | } | |
43 | } | |
44 | ||
45 | return found; | |
46 | } | |
47 | ||
48 | static u32 efi_hii_package_type(struct efi_hii_package_header *header) | |
49 | { | |
50 | u32 fields; | |
51 | ||
52 | fields = get_unaligned_le32(&header->fields); | |
53 | ||
54 | return (fields >> __EFI_HII_PACKAGE_TYPE_SHIFT) | |
55 | & __EFI_HII_PACKAGE_TYPE_MASK; | |
56 | } | |
57 | ||
58 | static u32 efi_hii_package_len(struct efi_hii_package_header *header) | |
59 | { | |
60 | u32 fields; | |
61 | ||
62 | fields = get_unaligned_le32(&header->fields); | |
63 | ||
64 | return (fields >> __EFI_HII_PACKAGE_LEN_SHIFT) | |
65 | & __EFI_HII_PACKAGE_LEN_MASK; | |
66 | } | |
67 | ||
68 | struct efi_string_info { | |
69 | efi_string_t string; | |
70 | /* we could also track font info, etc */ | |
71 | }; | |
72 | ||
73 | struct efi_string_table { | |
74 | struct list_head link; | |
75 | efi_string_id_t language_name; | |
76 | char *language; | |
77 | u32 nstrings; | |
78 | /* | |
79 | * NOTE: | |
80 | * string id starts at 1 so value is stbl->strings[id-1], | |
81 | * and strings[] is a array of stbl->nstrings elements | |
82 | */ | |
83 | struct efi_string_info *strings; | |
84 | }; | |
85 | ||
9ab0bdd9 AT |
86 | struct efi_guid_data { |
87 | struct list_head link; | |
88 | struct efi_hii_guid_package package; | |
89 | }; | |
90 | ||
8d3b77e3 AT |
91 | struct efi_keyboard_layout_data { |
92 | struct list_head link; /* in package */ | |
93 | struct list_head link_sys; /* in global list */ | |
94 | struct efi_hii_keyboard_layout keyboard_layout; | |
95 | }; | |
96 | ||
97 | struct efi_keyboard_package_data { | |
98 | struct list_head link; /* in package_list */ | |
99 | struct list_head keyboard_layout_list; | |
100 | }; | |
101 | ||
c9bfb222 LL |
102 | static void free_strings_table(struct efi_string_table *stbl) |
103 | { | |
104 | int i; | |
105 | ||
106 | for (i = 0; i < stbl->nstrings; i++) | |
107 | free(stbl->strings[i].string); | |
108 | free(stbl->strings); | |
109 | free(stbl->language); | |
110 | free(stbl); | |
111 | } | |
112 | ||
113 | static void remove_strings_package(struct efi_hii_packagelist *hii) | |
114 | { | |
115 | while (!list_empty(&hii->string_tables)) { | |
116 | struct efi_string_table *stbl; | |
117 | ||
118 | stbl = list_first_entry(&hii->string_tables, | |
119 | struct efi_string_table, link); | |
120 | list_del(&stbl->link); | |
121 | free_strings_table(stbl); | |
122 | } | |
123 | } | |
124 | ||
125 | static efi_status_t | |
126 | add_strings_package(struct efi_hii_packagelist *hii, | |
127 | struct efi_hii_strings_package *strings_package) | |
128 | { | |
129 | struct efi_hii_string_block *block; | |
130 | void *end; | |
131 | u32 nstrings = 0, idx = 0; | |
132 | struct efi_string_table *stbl = NULL; | |
133 | efi_status_t ret; | |
134 | ||
6f8f4217 HS |
135 | EFI_PRINT("header_size: %08x\n", |
136 | get_unaligned_le32(&strings_package->header_size)); | |
137 | EFI_PRINT("string_info_offset: %08x\n", | |
138 | get_unaligned_le32(&strings_package->string_info_offset)); | |
139 | EFI_PRINT("language_name: %u\n", | |
140 | get_unaligned_le16(&strings_package->language_name)); | |
141 | EFI_PRINT("language: %s\n", strings_package->language); | |
c9bfb222 LL |
142 | |
143 | /* count # of string entries: */ | |
144 | end = ((void *)strings_package) | |
145 | + efi_hii_package_len(&strings_package->header); | |
146 | block = ((void *)strings_package) | |
147 | + get_unaligned_le32(&strings_package->string_info_offset); | |
148 | ||
149 | while ((void *)block < end) { | |
150 | switch (block->block_type) { | |
151 | case EFI_HII_SIBT_STRING_UCS2: { | |
152 | struct efi_hii_sibt_string_ucs2_block *ucs2; | |
153 | ||
154 | ucs2 = (void *)block; | |
155 | nstrings++; | |
156 | block = efi_hii_sibt_string_ucs2_block_next(ucs2); | |
157 | break; | |
158 | } | |
159 | case EFI_HII_SIBT_END: | |
160 | block = end; | |
161 | break; | |
162 | default: | |
6f8f4217 HS |
163 | EFI_PRINT("unknown HII string block type: %02x\n", |
164 | block->block_type); | |
c9bfb222 LL |
165 | return EFI_INVALID_PARAMETER; |
166 | } | |
167 | } | |
168 | ||
169 | stbl = calloc(sizeof(*stbl), 1); | |
170 | if (!stbl) { | |
171 | ret = EFI_OUT_OF_RESOURCES; | |
172 | goto error; | |
173 | } | |
174 | stbl->strings = calloc(sizeof(stbl->strings[0]), nstrings); | |
175 | if (!stbl->strings) { | |
176 | ret = EFI_OUT_OF_RESOURCES; | |
177 | goto error; | |
178 | } | |
179 | stbl->language_name = | |
180 | get_unaligned_le16(&strings_package->language_name); | |
181 | stbl->language = strdup((char *)strings_package->language); | |
182 | if (!stbl->language) { | |
183 | ret = EFI_OUT_OF_RESOURCES; | |
184 | goto error; | |
185 | } | |
186 | stbl->nstrings = nstrings; | |
187 | ||
188 | /* and now parse string entries and populate efi_string_table */ | |
189 | block = ((void *)strings_package) | |
190 | + get_unaligned_le32(&strings_package->string_info_offset); | |
191 | ||
192 | while ((void *)block < end) { | |
193 | switch (block->block_type) { | |
194 | case EFI_HII_SIBT_STRING_UCS2: { | |
195 | struct efi_hii_sibt_string_ucs2_block *ucs2; | |
196 | ||
197 | ucs2 = (void *)block; | |
6f8f4217 | 198 | EFI_PRINT("%4u: \"%ls\"\n", idx + 1, ucs2->string_text); |
c9bfb222 LL |
199 | stbl->strings[idx].string = |
200 | u16_strdup(ucs2->string_text); | |
201 | if (!stbl->strings[idx].string) { | |
202 | ret = EFI_OUT_OF_RESOURCES; | |
203 | goto error; | |
204 | } | |
205 | idx++; | |
206 | /* FIXME: accessing u16 * here */ | |
207 | block = efi_hii_sibt_string_ucs2_block_next(ucs2); | |
208 | break; | |
209 | } | |
210 | case EFI_HII_SIBT_END: | |
211 | goto out; | |
212 | default: | |
6f8f4217 HS |
213 | EFI_PRINT("unknown HII string block type: %02x\n", |
214 | block->block_type); | |
c9bfb222 LL |
215 | ret = EFI_INVALID_PARAMETER; |
216 | goto error; | |
217 | } | |
218 | } | |
219 | ||
220 | out: | |
221 | list_add(&stbl->link, &hii->string_tables); | |
222 | if (hii->max_string_id < nstrings) | |
223 | hii->max_string_id = nstrings; | |
224 | ||
225 | return EFI_SUCCESS; | |
226 | ||
227 | error: | |
228 | if (stbl) { | |
229 | free(stbl->language); | |
bd3b7478 HS |
230 | while (idx > 0) |
231 | free(stbl->strings[--idx].string); | |
c9bfb222 LL |
232 | free(stbl->strings); |
233 | } | |
234 | free(stbl); | |
235 | ||
236 | return ret; | |
237 | } | |
238 | ||
9ab0bdd9 AT |
239 | static void remove_guid_package(struct efi_hii_packagelist *hii) |
240 | { | |
241 | struct efi_guid_data *data; | |
242 | ||
243 | while (!list_empty(&hii->guid_list)) { | |
244 | data = list_first_entry(&hii->guid_list, | |
245 | struct efi_guid_data, link); | |
246 | list_del(&data->link); | |
247 | free(data); | |
248 | } | |
249 | } | |
250 | ||
251 | static efi_status_t | |
252 | add_guid_package(struct efi_hii_packagelist *hii, | |
253 | struct efi_hii_guid_package *package) | |
254 | { | |
255 | struct efi_guid_data *data; | |
256 | ||
257 | data = calloc(sizeof(*data), 1); | |
258 | if (!data) | |
259 | return EFI_OUT_OF_RESOURCES; | |
260 | ||
261 | /* TODO: we don't know any about data field */ | |
262 | memcpy(&data->package, package, sizeof(*package)); | |
263 | list_add_tail(&data->link, &hii->guid_list); | |
264 | ||
265 | return EFI_SUCCESS; | |
266 | } | |
267 | ||
8d3b77e3 AT |
268 | static void free_keyboard_layouts(struct efi_keyboard_package_data *package) |
269 | { | |
270 | struct efi_keyboard_layout_data *layout_data; | |
271 | ||
272 | while (!list_empty(&package->keyboard_layout_list)) { | |
273 | layout_data = list_first_entry(&package->keyboard_layout_list, | |
274 | struct efi_keyboard_layout_data, | |
275 | link); | |
276 | list_del(&layout_data->link); | |
277 | list_del(&layout_data->link_sys); | |
278 | free(layout_data); | |
279 | } | |
280 | } | |
281 | ||
282 | static void remove_keyboard_package(struct efi_hii_packagelist *hii) | |
283 | { | |
284 | struct efi_keyboard_package_data *package; | |
285 | ||
286 | while (!list_empty(&hii->keyboard_packages)) { | |
287 | package = list_first_entry(&hii->keyboard_packages, | |
288 | struct efi_keyboard_package_data, | |
289 | link); | |
290 | free_keyboard_layouts(package); | |
291 | list_del(&package->link); | |
292 | free(package); | |
293 | } | |
294 | } | |
295 | ||
296 | static efi_status_t | |
297 | add_keyboard_package(struct efi_hii_packagelist *hii, | |
298 | struct efi_hii_keyboard_package *keyboard_package) | |
299 | { | |
300 | struct efi_keyboard_package_data *package_data; | |
301 | struct efi_hii_keyboard_layout *layout; | |
302 | struct efi_keyboard_layout_data *layout_data; | |
303 | u16 layout_count, layout_length; | |
304 | int i; | |
305 | ||
306 | package_data = malloc(sizeof(*package_data)); | |
307 | if (!package_data) | |
308 | return EFI_OUT_OF_RESOURCES; | |
309 | INIT_LIST_HEAD(&package_data->link); | |
310 | INIT_LIST_HEAD(&package_data->keyboard_layout_list); | |
311 | ||
312 | layout = &keyboard_package->layout[0]; | |
313 | layout_count = get_unaligned_le16(&keyboard_package->layout_count); | |
314 | for (i = 0; i < layout_count; i++) { | |
315 | layout_length = get_unaligned_le16(&layout->layout_length); | |
316 | layout_data = malloc(sizeof(*layout_data) + layout_length); | |
317 | if (!layout_data) | |
318 | goto out; | |
319 | ||
320 | memcpy(&layout_data->keyboard_layout, layout, layout_length); | |
321 | list_add_tail(&layout_data->link, | |
322 | &package_data->keyboard_layout_list); | |
323 | list_add_tail(&layout_data->link_sys, | |
324 | &efi_keyboard_layout_list); | |
325 | ||
326 | layout += layout_length; | |
327 | } | |
328 | ||
329 | list_add_tail(&package_data->link, &hii->keyboard_packages); | |
330 | ||
331 | return EFI_SUCCESS; | |
332 | ||
333 | out: | |
334 | free_keyboard_layouts(package_data); | |
335 | free(package_data); | |
336 | ||
337 | return EFI_OUT_OF_RESOURCES; | |
338 | } | |
339 | ||
c9bfb222 LL |
340 | static struct efi_hii_packagelist *new_packagelist(void) |
341 | { | |
342 | struct efi_hii_packagelist *hii; | |
343 | ||
344 | hii = malloc(sizeof(*hii)); | |
fdef2983 | 345 | list_add_tail(&hii->link, &efi_package_lists); |
c9bfb222 LL |
346 | hii->max_string_id = 0; |
347 | INIT_LIST_HEAD(&hii->string_tables); | |
9ab0bdd9 | 348 | INIT_LIST_HEAD(&hii->guid_list); |
8d3b77e3 | 349 | INIT_LIST_HEAD(&hii->keyboard_packages); |
c9bfb222 LL |
350 | |
351 | return hii; | |
352 | } | |
353 | ||
354 | static void free_packagelist(struct efi_hii_packagelist *hii) | |
355 | { | |
356 | remove_strings_package(hii); | |
9ab0bdd9 | 357 | remove_guid_package(hii); |
8d3b77e3 | 358 | remove_keyboard_package(hii); |
c9bfb222 LL |
359 | |
360 | list_del(&hii->link); | |
361 | free(hii); | |
362 | } | |
363 | ||
364 | static efi_status_t | |
365 | add_packages(struct efi_hii_packagelist *hii, | |
366 | const struct efi_hii_package_list_header *package_list) | |
367 | { | |
368 | struct efi_hii_package_header *package; | |
369 | void *end; | |
370 | efi_status_t ret = EFI_SUCCESS; | |
371 | ||
372 | end = ((void *)package_list) | |
373 | + get_unaligned_le32(&package_list->package_length); | |
374 | ||
6f8f4217 HS |
375 | EFI_PRINT("package_list: %pUl (%u)\n", &package_list->package_list_guid, |
376 | get_unaligned_le32(&package_list->package_length)); | |
c9bfb222 LL |
377 | |
378 | package = ((void *)package_list) + sizeof(*package_list); | |
379 | while ((void *)package < end) { | |
6f8f4217 HS |
380 | EFI_PRINT("package=%p, package type=%x, length=%u\n", package, |
381 | efi_hii_package_type(package), | |
382 | efi_hii_package_len(package)); | |
c9bfb222 LL |
383 | |
384 | switch (efi_hii_package_type(package)) { | |
385 | case EFI_HII_PACKAGE_TYPE_GUID: | |
9ab0bdd9 AT |
386 | ret = add_guid_package(hii, |
387 | (struct efi_hii_guid_package *)package); | |
c9bfb222 LL |
388 | break; |
389 | case EFI_HII_PACKAGE_FORMS: | |
1a9fce50 | 390 | EFI_PRINT("Form package not supported\n"); |
c9bfb222 LL |
391 | ret = EFI_INVALID_PARAMETER; |
392 | break; | |
393 | case EFI_HII_PACKAGE_STRINGS: | |
394 | ret = add_strings_package(hii, | |
395 | (struct efi_hii_strings_package *)package); | |
396 | break; | |
397 | case EFI_HII_PACKAGE_FONTS: | |
1a9fce50 | 398 | EFI_PRINT("Font package not supported\n"); |
c9bfb222 LL |
399 | ret = EFI_INVALID_PARAMETER; |
400 | break; | |
401 | case EFI_HII_PACKAGE_IMAGES: | |
1a9fce50 | 402 | EFI_PRINT("Image package not supported\n"); |
c9bfb222 LL |
403 | ret = EFI_INVALID_PARAMETER; |
404 | break; | |
405 | case EFI_HII_PACKAGE_SIMPLE_FONTS: | |
1a9fce50 | 406 | EFI_PRINT("Simple font package not supported\n"); |
c9bfb222 LL |
407 | ret = EFI_INVALID_PARAMETER; |
408 | break; | |
409 | case EFI_HII_PACKAGE_DEVICE_PATH: | |
1a9fce50 | 410 | EFI_PRINT("Device path package not supported\n"); |
c9bfb222 LL |
411 | ret = EFI_INVALID_PARAMETER; |
412 | break; | |
413 | case EFI_HII_PACKAGE_KEYBOARD_LAYOUT: | |
8d3b77e3 AT |
414 | ret = add_keyboard_package(hii, |
415 | (struct efi_hii_keyboard_package *)package); | |
c9bfb222 LL |
416 | break; |
417 | case EFI_HII_PACKAGE_ANIMATIONS: | |
1a9fce50 | 418 | EFI_PRINT("Animation package not supported\n"); |
c9bfb222 LL |
419 | ret = EFI_INVALID_PARAMETER; |
420 | break; | |
421 | case EFI_HII_PACKAGE_END: | |
422 | goto out; | |
423 | case EFI_HII_PACKAGE_TYPE_SYSTEM_BEGIN: | |
424 | case EFI_HII_PACKAGE_TYPE_SYSTEM_END: | |
425 | default: | |
426 | break; | |
427 | } | |
428 | ||
429 | if (ret != EFI_SUCCESS) | |
430 | return ret; | |
431 | ||
432 | package = (void *)package + efi_hii_package_len(package); | |
433 | } | |
434 | out: | |
435 | // TODO in theory there is some notifications that should be sent.. | |
436 | return EFI_SUCCESS; | |
437 | } | |
438 | ||
439 | /* | |
440 | * EFI_HII_DATABASE_PROTOCOL | |
441 | */ | |
442 | ||
443 | static efi_status_t EFIAPI | |
444 | new_package_list(const struct efi_hii_database_protocol *this, | |
445 | const struct efi_hii_package_list_header *package_list, | |
446 | const efi_handle_t driver_handle, | |
447 | efi_hii_handle_t *handle) | |
448 | { | |
449 | struct efi_hii_packagelist *hii; | |
450 | efi_status_t ret; | |
451 | ||
452 | EFI_ENTRY("%p, %p, %p, %p", this, package_list, driver_handle, handle); | |
453 | ||
454 | if (!package_list || !handle) | |
455 | return EFI_EXIT(EFI_INVALID_PARAMETER); | |
456 | ||
457 | hii = new_packagelist(); | |
458 | if (!hii) | |
459 | return EFI_EXIT(EFI_OUT_OF_RESOURCES); | |
460 | ||
461 | ret = add_packages(hii, package_list); | |
462 | if (ret != EFI_SUCCESS) { | |
463 | free_packagelist(hii); | |
464 | return EFI_EXIT(ret); | |
465 | } | |
466 | ||
467 | hii->driver_handle = driver_handle; | |
c9bfb222 LL |
468 | *handle = hii; |
469 | ||
470 | return EFI_EXIT(EFI_SUCCESS); | |
471 | } | |
472 | ||
473 | static efi_status_t EFIAPI | |
474 | remove_package_list(const struct efi_hii_database_protocol *this, | |
475 | efi_hii_handle_t handle) | |
476 | { | |
477 | struct efi_hii_packagelist *hii = handle; | |
478 | ||
479 | EFI_ENTRY("%p, %p", this, handle); | |
480 | ||
481 | if (!handle || !efi_hii_packagelist_exists(handle)) | |
482 | return EFI_EXIT(EFI_NOT_FOUND); | |
483 | ||
484 | free_packagelist(hii); | |
485 | ||
486 | return EFI_EXIT(EFI_SUCCESS); | |
487 | } | |
488 | ||
489 | static efi_status_t EFIAPI | |
490 | update_package_list(const struct efi_hii_database_protocol *this, | |
491 | efi_hii_handle_t handle, | |
492 | const struct efi_hii_package_list_header *package_list) | |
493 | { | |
494 | struct efi_hii_packagelist *hii = handle; | |
495 | struct efi_hii_package_header *package; | |
496 | void *end; | |
497 | efi_status_t ret = EFI_SUCCESS; | |
498 | ||
499 | EFI_ENTRY("%p, %p, %p", this, handle, package_list); | |
500 | ||
501 | if (!handle || !efi_hii_packagelist_exists(handle)) | |
502 | return EFI_EXIT(EFI_NOT_FOUND); | |
503 | ||
504 | if (!package_list) | |
505 | return EFI_EXIT(EFI_INVALID_PARAMETER); | |
506 | ||
6f8f4217 HS |
507 | EFI_PRINT("package_list: %pUl (%u)\n", &package_list->package_list_guid, |
508 | get_unaligned_le32(&package_list->package_length)); | |
c9bfb222 LL |
509 | |
510 | package = ((void *)package_list) + sizeof(*package_list); | |
511 | end = ((void *)package_list) | |
512 | + get_unaligned_le32(&package_list->package_length); | |
513 | ||
514 | while ((void *)package < end) { | |
6f8f4217 HS |
515 | EFI_PRINT("package=%p, package type=%x, length=%u\n", package, |
516 | efi_hii_package_type(package), | |
517 | efi_hii_package_len(package)); | |
c9bfb222 LL |
518 | |
519 | switch (efi_hii_package_type(package)) { | |
520 | case EFI_HII_PACKAGE_TYPE_GUID: | |
9ab0bdd9 | 521 | remove_guid_package(hii); |
c9bfb222 LL |
522 | break; |
523 | case EFI_HII_PACKAGE_FORMS: | |
1a9fce50 | 524 | EFI_PRINT("Form package not supported\n"); |
c9bfb222 LL |
525 | ret = EFI_INVALID_PARAMETER; |
526 | break; | |
527 | case EFI_HII_PACKAGE_STRINGS: | |
528 | remove_strings_package(hii); | |
529 | break; | |
530 | case EFI_HII_PACKAGE_FONTS: | |
1a9fce50 | 531 | EFI_PRINT("Font package not supported\n"); |
c9bfb222 LL |
532 | ret = EFI_INVALID_PARAMETER; |
533 | break; | |
534 | case EFI_HII_PACKAGE_IMAGES: | |
1a9fce50 | 535 | EFI_PRINT("Image package not supported\n"); |
c9bfb222 LL |
536 | ret = EFI_INVALID_PARAMETER; |
537 | break; | |
538 | case EFI_HII_PACKAGE_SIMPLE_FONTS: | |
1a9fce50 | 539 | EFI_PRINT("Simple font package not supported\n"); |
c9bfb222 LL |
540 | ret = EFI_INVALID_PARAMETER; |
541 | break; | |
542 | case EFI_HII_PACKAGE_DEVICE_PATH: | |
1a9fce50 | 543 | EFI_PRINT("Device path package not supported\n"); |
c9bfb222 LL |
544 | ret = EFI_INVALID_PARAMETER; |
545 | break; | |
546 | case EFI_HII_PACKAGE_KEYBOARD_LAYOUT: | |
8d3b77e3 | 547 | remove_keyboard_package(hii); |
c9bfb222 LL |
548 | break; |
549 | case EFI_HII_PACKAGE_ANIMATIONS: | |
1a9fce50 | 550 | EFI_PRINT("Animation package not supported\n"); |
c9bfb222 LL |
551 | ret = EFI_INVALID_PARAMETER; |
552 | break; | |
553 | case EFI_HII_PACKAGE_END: | |
554 | goto out; | |
555 | case EFI_HII_PACKAGE_TYPE_SYSTEM_BEGIN: | |
556 | case EFI_HII_PACKAGE_TYPE_SYSTEM_END: | |
557 | default: | |
558 | break; | |
559 | } | |
560 | ||
561 | /* TODO: already removed some packages */ | |
562 | if (ret != EFI_SUCCESS) | |
563 | return EFI_EXIT(ret); | |
564 | ||
565 | package = ((void *)package) | |
566 | + efi_hii_package_len(package); | |
567 | } | |
568 | out: | |
569 | ret = add_packages(hii, package_list); | |
570 | ||
571 | return EFI_EXIT(ret); | |
572 | } | |
573 | ||
574 | static efi_status_t EFIAPI | |
575 | list_package_lists(const struct efi_hii_database_protocol *this, | |
576 | u8 package_type, | |
577 | const efi_guid_t *package_guid, | |
578 | efi_uintn_t *handle_buffer_length, | |
579 | efi_hii_handle_t *handle) | |
580 | { | |
581 | struct efi_hii_packagelist *hii = | |
582 | (struct efi_hii_packagelist *)handle; | |
583 | int package_cnt, package_max; | |
c974ab0e | 584 | efi_status_t ret = EFI_NOT_FOUND; |
c9bfb222 LL |
585 | |
586 | EFI_ENTRY("%p, %u, %pUl, %p, %p", this, package_type, package_guid, | |
587 | handle_buffer_length, handle); | |
588 | ||
589 | if (!handle_buffer_length || | |
c974ab0e HS |
590 | (*handle_buffer_length && !handle)) { |
591 | ret = EFI_INVALID_PARAMETER; | |
592 | goto out; | |
593 | } | |
c9bfb222 LL |
594 | |
595 | if ((package_type != EFI_HII_PACKAGE_TYPE_GUID && package_guid) || | |
c974ab0e HS |
596 | (package_type == EFI_HII_PACKAGE_TYPE_GUID && !package_guid)) { |
597 | ret = EFI_INVALID_PARAMETER; | |
598 | goto out; | |
599 | } | |
c9bfb222 | 600 | |
6f8f4217 HS |
601 | EFI_PRINT("package type=%x, guid=%pUl, length=%zu\n", (int)package_type, |
602 | package_guid, *handle_buffer_length); | |
c9bfb222 LL |
603 | |
604 | package_cnt = 0; | |
605 | package_max = *handle_buffer_length / sizeof(*handle); | |
606 | list_for_each_entry(hii, &efi_package_lists, link) { | |
607 | switch (package_type) { | |
608 | case EFI_HII_PACKAGE_TYPE_ALL: | |
609 | break; | |
610 | case EFI_HII_PACKAGE_TYPE_GUID: | |
9ab0bdd9 AT |
611 | if (!list_empty(&hii->guid_list)) |
612 | break; | |
c9bfb222 | 613 | continue; |
c9bfb222 LL |
614 | case EFI_HII_PACKAGE_STRINGS: |
615 | if (!list_empty(&hii->string_tables)) | |
616 | break; | |
617 | continue; | |
c9bfb222 | 618 | case EFI_HII_PACKAGE_KEYBOARD_LAYOUT: |
8d3b77e3 AT |
619 | if (!list_empty(&hii->keyboard_packages)) |
620 | break; | |
c9bfb222 | 621 | continue; |
c9bfb222 LL |
622 | default: |
623 | continue; | |
624 | } | |
625 | ||
626 | package_cnt++; | |
c974ab0e | 627 | if (package_cnt <= package_max) { |
c9bfb222 | 628 | *handle++ = hii; |
c974ab0e HS |
629 | ret = EFI_SUCCESS; |
630 | } else { | |
c9bfb222 | 631 | ret = EFI_BUFFER_TOO_SMALL; |
c974ab0e | 632 | } |
c9bfb222 LL |
633 | } |
634 | *handle_buffer_length = package_cnt * sizeof(*handle); | |
c974ab0e | 635 | out: |
c9bfb222 LL |
636 | return EFI_EXIT(ret); |
637 | } | |
638 | ||
639 | static efi_status_t EFIAPI | |
640 | export_package_lists(const struct efi_hii_database_protocol *this, | |
641 | efi_hii_handle_t handle, | |
642 | efi_uintn_t *buffer_size, | |
643 | struct efi_hii_package_list_header *buffer) | |
644 | { | |
645 | EFI_ENTRY("%p, %p, %p, %p", this, handle, buffer_size, buffer); | |
646 | ||
647 | if (!buffer_size || !buffer) | |
648 | return EFI_EXIT(EFI_INVALID_PARAMETER); | |
649 | ||
650 | return EFI_EXIT(EFI_NOT_FOUND); | |
651 | } | |
652 | ||
653 | static efi_status_t EFIAPI | |
654 | register_package_notify(const struct efi_hii_database_protocol *this, | |
655 | u8 package_type, | |
656 | const efi_guid_t *package_guid, | |
657 | const void *package_notify_fn, | |
658 | efi_uintn_t notify_type, | |
659 | efi_handle_t *notify_handle) | |
660 | { | |
661 | EFI_ENTRY("%p, %u, %pUl, %p, %zu, %p", this, package_type, | |
662 | package_guid, package_notify_fn, notify_type, | |
663 | notify_handle); | |
664 | ||
665 | if (!notify_handle) | |
666 | return EFI_EXIT(EFI_INVALID_PARAMETER); | |
667 | ||
668 | if ((package_type != EFI_HII_PACKAGE_TYPE_GUID && package_guid) || | |
669 | (package_type == EFI_HII_PACKAGE_TYPE_GUID && !package_guid)) | |
670 | return EFI_EXIT(EFI_INVALID_PARAMETER); | |
671 | ||
672 | return EFI_EXIT(EFI_OUT_OF_RESOURCES); | |
673 | } | |
674 | ||
675 | static efi_status_t EFIAPI | |
676 | unregister_package_notify(const struct efi_hii_database_protocol *this, | |
677 | efi_handle_t notification_handle) | |
678 | { | |
679 | EFI_ENTRY("%p, %p", this, notification_handle); | |
680 | ||
681 | return EFI_EXIT(EFI_NOT_FOUND); | |
682 | } | |
683 | ||
684 | static efi_status_t EFIAPI | |
685 | find_keyboard_layouts(const struct efi_hii_database_protocol *this, | |
686 | u16 *key_guid_buffer_length, | |
687 | efi_guid_t *key_guid_buffer) | |
688 | { | |
8d3b77e3 AT |
689 | struct efi_keyboard_layout_data *layout_data; |
690 | int package_cnt, package_max; | |
691 | efi_status_t ret = EFI_SUCCESS; | |
692 | ||
c9bfb222 LL |
693 | EFI_ENTRY("%p, %p, %p", this, key_guid_buffer_length, key_guid_buffer); |
694 | ||
8d3b77e3 AT |
695 | if (!key_guid_buffer_length || |
696 | (*key_guid_buffer_length && !key_guid_buffer)) | |
697 | return EFI_EXIT(EFI_INVALID_PARAMETER); | |
698 | ||
699 | package_cnt = 0; | |
700 | package_max = *key_guid_buffer_length / sizeof(*key_guid_buffer); | |
701 | list_for_each_entry(layout_data, &efi_keyboard_layout_list, link_sys) { | |
702 | package_cnt++; | |
703 | if (package_cnt <= package_max) | |
704 | memcpy(key_guid_buffer++, | |
705 | &layout_data->keyboard_layout.guid, | |
706 | sizeof(*key_guid_buffer)); | |
707 | else | |
708 | ret = EFI_BUFFER_TOO_SMALL; | |
709 | } | |
710 | *key_guid_buffer_length = package_cnt * sizeof(*key_guid_buffer); | |
711 | ||
712 | return EFI_EXIT(ret); | |
c9bfb222 LL |
713 | } |
714 | ||
715 | static efi_status_t EFIAPI | |
716 | get_keyboard_layout(const struct efi_hii_database_protocol *this, | |
717 | efi_guid_t *key_guid, | |
718 | u16 *keyboard_layout_length, | |
719 | struct efi_hii_keyboard_layout *keyboard_layout) | |
720 | { | |
8d3b77e3 AT |
721 | struct efi_keyboard_layout_data *layout_data; |
722 | u16 layout_length; | |
723 | ||
c9bfb222 LL |
724 | EFI_ENTRY("%p, %pUl, %p, %p", this, key_guid, keyboard_layout_length, |
725 | keyboard_layout); | |
726 | ||
8d3b77e3 AT |
727 | if (!keyboard_layout_length || |
728 | (*keyboard_layout_length && !keyboard_layout)) | |
729 | return EFI_EXIT(EFI_INVALID_PARAMETER); | |
730 | ||
731 | /* TODO: no notion of current keyboard layout */ | |
732 | if (!key_guid) | |
733 | return EFI_EXIT(EFI_INVALID_PARAMETER); | |
734 | ||
735 | list_for_each_entry(layout_data, &efi_keyboard_layout_list, link_sys) { | |
736 | if (!guidcmp(&layout_data->keyboard_layout.guid, key_guid)) | |
737 | goto found; | |
738 | } | |
739 | ||
c9bfb222 | 740 | return EFI_EXIT(EFI_NOT_FOUND); |
8d3b77e3 AT |
741 | |
742 | found: | |
743 | layout_length = | |
744 | get_unaligned_le16(&layout_data->keyboard_layout.layout_length); | |
745 | if (*keyboard_layout_length < layout_length) { | |
746 | *keyboard_layout_length = layout_length; | |
747 | return EFI_EXIT(EFI_BUFFER_TOO_SMALL); | |
748 | } | |
749 | ||
750 | memcpy(keyboard_layout, &layout_data->keyboard_layout, layout_length); | |
751 | ||
752 | return EFI_EXIT(EFI_SUCCESS); | |
c9bfb222 LL |
753 | } |
754 | ||
755 | static efi_status_t EFIAPI | |
756 | set_keyboard_layout(const struct efi_hii_database_protocol *this, | |
757 | efi_guid_t *key_guid) | |
758 | { | |
759 | EFI_ENTRY("%p, %pUl", this, key_guid); | |
760 | ||
761 | return EFI_EXIT(EFI_NOT_FOUND); | |
762 | } | |
763 | ||
764 | static efi_status_t EFIAPI | |
765 | get_package_list_handle(const struct efi_hii_database_protocol *this, | |
766 | efi_hii_handle_t package_list_handle, | |
767 | efi_handle_t *driver_handle) | |
768 | { | |
769 | struct efi_hii_packagelist *hii; | |
770 | ||
771 | EFI_ENTRY("%p, %p, %p", this, package_list_handle, driver_handle); | |
772 | ||
773 | if (!driver_handle) | |
774 | return EFI_EXIT(EFI_INVALID_PARAMETER); | |
775 | ||
776 | list_for_each_entry(hii, &efi_package_lists, link) { | |
777 | if (hii == package_list_handle) { | |
778 | *driver_handle = hii->driver_handle; | |
779 | return EFI_EXIT(EFI_SUCCESS); | |
780 | } | |
781 | } | |
782 | ||
783 | return EFI_EXIT(EFI_NOT_FOUND); | |
784 | } | |
785 | ||
786 | const struct efi_hii_database_protocol efi_hii_database = { | |
787 | .new_package_list = new_package_list, | |
788 | .remove_package_list = remove_package_list, | |
789 | .update_package_list = update_package_list, | |
790 | .list_package_lists = list_package_lists, | |
791 | .export_package_lists = export_package_lists, | |
792 | .register_package_notify = register_package_notify, | |
793 | .unregister_package_notify = unregister_package_notify, | |
794 | .find_keyboard_layouts = find_keyboard_layouts, | |
795 | .get_keyboard_layout = get_keyboard_layout, | |
796 | .set_keyboard_layout = set_keyboard_layout, | |
797 | .get_package_list_handle = get_package_list_handle | |
798 | }; | |
799 | ||
800 | /* | |
801 | * EFI_HII_STRING_PROTOCOL | |
802 | */ | |
803 | ||
804 | static bool language_match(char *language, char *languages) | |
805 | { | |
806 | size_t n; | |
807 | ||
808 | n = strlen(language); | |
809 | /* match primary language? */ | |
810 | if (!strncasecmp(language, languages, n) && | |
811 | (languages[n] == ';' || languages[n] == '\0')) | |
812 | return true; | |
813 | ||
814 | return false; | |
815 | } | |
816 | ||
817 | static efi_status_t EFIAPI | |
818 | new_string(const struct efi_hii_string_protocol *this, | |
819 | efi_hii_handle_t package_list, | |
820 | efi_string_id_t *string_id, | |
821 | const u8 *language, | |
822 | const u16 *language_name, | |
823 | const efi_string_t string, | |
824 | const struct efi_font_info *string_font_info) | |
825 | { | |
826 | struct efi_hii_packagelist *hii = package_list; | |
827 | struct efi_string_table *stbl; | |
828 | ||
829 | EFI_ENTRY("%p, %p, %p, \"%s\", %p, \"%ls\", %p", this, package_list, | |
830 | string_id, language, language_name, string, | |
831 | string_font_info); | |
832 | ||
833 | if (!package_list || !efi_hii_packagelist_exists(package_list)) | |
834 | return EFI_EXIT(EFI_NOT_FOUND); | |
835 | ||
836 | if (!string_id || !language || !string) | |
837 | return EFI_EXIT(EFI_INVALID_PARAMETER); | |
838 | ||
839 | list_for_each_entry(stbl, &hii->string_tables, link) { | |
840 | if (language_match((char *)language, stbl->language)) { | |
841 | efi_string_id_t new_id; | |
842 | void *buf; | |
843 | efi_string_t str; | |
844 | ||
845 | new_id = ++hii->max_string_id; | |
846 | if (stbl->nstrings < new_id) { | |
847 | buf = realloc(stbl->strings, | |
848 | sizeof(stbl->strings[0]) | |
849 | * new_id); | |
850 | if (!buf) | |
851 | return EFI_EXIT(EFI_OUT_OF_RESOURCES); | |
852 | ||
853 | memset(&stbl->strings[stbl->nstrings], 0, | |
854 | (new_id - stbl->nstrings) | |
855 | * sizeof(stbl->strings[0])); | |
856 | stbl->strings = buf; | |
857 | stbl->nstrings = new_id; | |
858 | } | |
859 | ||
860 | str = u16_strdup(string); | |
861 | if (!str) | |
862 | return EFI_EXIT(EFI_OUT_OF_RESOURCES); | |
863 | ||
864 | stbl->strings[new_id - 1].string = str; | |
865 | *string_id = new_id; | |
866 | ||
867 | return EFI_EXIT(EFI_SUCCESS); | |
868 | } | |
869 | } | |
870 | ||
871 | return EFI_EXIT(EFI_NOT_FOUND); | |
872 | } | |
873 | ||
874 | static efi_status_t EFIAPI | |
875 | get_string(const struct efi_hii_string_protocol *this, | |
876 | const u8 *language, | |
877 | efi_hii_handle_t package_list, | |
878 | efi_string_id_t string_id, | |
879 | efi_string_t string, | |
880 | efi_uintn_t *string_size, | |
881 | struct efi_font_info **string_font_info) | |
882 | { | |
883 | struct efi_hii_packagelist *hii = package_list; | |
884 | struct efi_string_table *stbl; | |
885 | ||
886 | EFI_ENTRY("%p, \"%s\", %p, %u, %p, %p, %p", this, language, | |
887 | package_list, string_id, string, string_size, | |
888 | string_font_info); | |
889 | ||
890 | if (!package_list || !efi_hii_packagelist_exists(package_list)) | |
891 | return EFI_EXIT(EFI_NOT_FOUND); | |
892 | ||
893 | list_for_each_entry(stbl, &hii->string_tables, link) { | |
894 | if (language_match((char *)language, stbl->language)) { | |
895 | efi_string_t str; | |
896 | size_t len; | |
897 | ||
898 | if (stbl->nstrings < string_id) | |
899 | return EFI_EXIT(EFI_NOT_FOUND); | |
900 | ||
901 | str = stbl->strings[string_id - 1].string; | |
902 | if (str) { | |
903 | len = (u16_strlen(str) + 1) * sizeof(u16); | |
904 | if (*string_size < len) { | |
905 | *string_size = len; | |
906 | ||
907 | return EFI_EXIT(EFI_BUFFER_TOO_SMALL); | |
908 | } | |
909 | memcpy(string, str, len); | |
910 | *string_size = len; | |
911 | } else { | |
912 | return EFI_EXIT(EFI_NOT_FOUND); | |
913 | } | |
914 | ||
915 | return EFI_EXIT(EFI_SUCCESS); | |
916 | } | |
917 | } | |
918 | ||
919 | return EFI_EXIT(EFI_NOT_FOUND); | |
920 | } | |
921 | ||
922 | static efi_status_t EFIAPI | |
923 | set_string(const struct efi_hii_string_protocol *this, | |
924 | efi_hii_handle_t package_list, | |
925 | efi_string_id_t string_id, | |
926 | const u8 *language, | |
927 | const efi_string_t string, | |
928 | const struct efi_font_info *string_font_info) | |
929 | { | |
930 | struct efi_hii_packagelist *hii = package_list; | |
931 | struct efi_string_table *stbl; | |
932 | ||
933 | EFI_ENTRY("%p, %p, %u, \"%s\", \"%ls\", %p", this, package_list, | |
934 | string_id, language, string, string_font_info); | |
935 | ||
936 | if (!package_list || !efi_hii_packagelist_exists(package_list)) | |
937 | return EFI_EXIT(EFI_NOT_FOUND); | |
938 | ||
939 | if (string_id > hii->max_string_id) | |
940 | return EFI_EXIT(EFI_NOT_FOUND); | |
941 | ||
942 | if (!string || !language) | |
943 | return EFI_EXIT(EFI_INVALID_PARAMETER); | |
944 | ||
945 | list_for_each_entry(stbl, &hii->string_tables, link) { | |
946 | if (language_match((char *)language, stbl->language)) { | |
947 | efi_string_t str; | |
948 | ||
949 | if (hii->max_string_id < string_id) | |
950 | return EFI_EXIT(EFI_NOT_FOUND); | |
951 | ||
952 | if (stbl->nstrings < string_id) { | |
953 | void *buf; | |
954 | ||
955 | buf = realloc(stbl->strings, | |
956 | string_id | |
957 | * sizeof(stbl->strings[0])); | |
958 | if (!buf) | |
959 | return EFI_EXIT(EFI_OUT_OF_RESOURCES); | |
960 | ||
961 | memset(&stbl->strings[string_id - 1], 0, | |
962 | (string_id - stbl->nstrings) | |
963 | * sizeof(stbl->strings[0])); | |
964 | stbl->strings = buf; | |
965 | } | |
966 | ||
967 | str = u16_strdup(string); | |
968 | if (!str) | |
969 | return EFI_EXIT(EFI_OUT_OF_RESOURCES); | |
970 | ||
971 | free(stbl->strings[string_id - 1].string); | |
972 | stbl->strings[string_id - 1].string = str; | |
973 | ||
974 | return EFI_EXIT(EFI_SUCCESS); | |
975 | } | |
976 | } | |
977 | ||
978 | return EFI_EXIT(EFI_NOT_FOUND); | |
979 | } | |
980 | ||
981 | static efi_status_t EFIAPI | |
982 | get_languages(const struct efi_hii_string_protocol *this, | |
983 | efi_hii_handle_t package_list, | |
984 | u8 *languages, | |
985 | efi_uintn_t *languages_size) | |
986 | { | |
987 | struct efi_hii_packagelist *hii = package_list; | |
988 | struct efi_string_table *stbl; | |
989 | size_t len = 0; | |
990 | char *p; | |
991 | ||
992 | EFI_ENTRY("%p, %p, %p, %p", this, package_list, languages, | |
993 | languages_size); | |
994 | ||
995 | if (!package_list || !efi_hii_packagelist_exists(package_list)) | |
996 | return EFI_EXIT(EFI_NOT_FOUND); | |
997 | ||
998 | if (!languages_size || | |
999 | (*languages_size && !languages)) | |
1000 | return EFI_EXIT(EFI_INVALID_PARAMETER); | |
1001 | ||
1002 | /* figure out required size: */ | |
1003 | list_for_each_entry(stbl, &hii->string_tables, link) { | |
1004 | len += strlen((char *)stbl->language) + 1; | |
1005 | } | |
1006 | ||
1007 | if (*languages_size < len) { | |
1008 | *languages_size = len; | |
1009 | ||
1010 | return EFI_EXIT(EFI_BUFFER_TOO_SMALL); | |
1011 | } | |
1012 | ||
1013 | p = (char *)languages; | |
1014 | list_for_each_entry(stbl, &hii->string_tables, link) { | |
1015 | if (p != (char *)languages) | |
1016 | *p++ = ';'; | |
1017 | strcpy(p, stbl->language); | |
1018 | p += strlen((char *)stbl->language); | |
1019 | } | |
1020 | *p = '\0'; | |
1021 | ||
6f8f4217 | 1022 | EFI_PRINT("languages: %s\n", languages); |
c9bfb222 LL |
1023 | |
1024 | return EFI_EXIT(EFI_SUCCESS); | |
1025 | } | |
1026 | ||
1027 | static efi_status_t EFIAPI | |
1028 | get_secondary_languages(const struct efi_hii_string_protocol *this, | |
1029 | efi_hii_handle_t package_list, | |
1030 | const u8 *primary_language, | |
1031 | u8 *secondary_languages, | |
1032 | efi_uintn_t *secondary_languages_size) | |
1033 | { | |
1034 | struct efi_hii_packagelist *hii = package_list; | |
1035 | struct efi_string_table *stbl; | |
1036 | bool found = false; | |
1037 | ||
1038 | EFI_ENTRY("%p, %p, \"%s\", %p, %p", this, package_list, | |
1039 | primary_language, secondary_languages, | |
1040 | secondary_languages_size); | |
1041 | ||
1042 | if (!package_list || !efi_hii_packagelist_exists(package_list)) | |
1043 | return EFI_EXIT(EFI_NOT_FOUND); | |
1044 | ||
1045 | if (!secondary_languages_size || | |
1046 | (*secondary_languages_size && !secondary_languages)) | |
1047 | return EFI_EXIT(EFI_INVALID_PARAMETER); | |
1048 | ||
1049 | list_for_each_entry(stbl, &hii->string_tables, link) { | |
1050 | if (language_match((char *)primary_language, stbl->language)) { | |
1051 | found = true; | |
1052 | break; | |
1053 | } | |
1054 | } | |
1055 | if (!found) | |
1056 | return EFI_EXIT(EFI_INVALID_LANGUAGE); | |
1057 | ||
1058 | /* | |
1059 | * TODO: What is secondary language? | |
1060 | * *secondary_languages = '\0'; | |
1061 | * *secondary_languages_size = 0; | |
1062 | */ | |
1063 | ||
1064 | return EFI_EXIT(EFI_NOT_FOUND); | |
1065 | } | |
1066 | ||
1067 | const struct efi_hii_string_protocol efi_hii_string = { | |
1068 | .new_string = new_string, | |
1069 | .get_string = get_string, | |
1070 | .set_string = set_string, | |
1071 | .get_languages = get_languages, | |
1072 | .get_secondary_languages = get_secondary_languages | |
1073 | }; |