]>
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 | ||
135 | debug("header_size: %08x\n", | |
136 | get_unaligned_le32(&strings_package->header_size)); | |
137 | debug("string_info_offset: %08x\n", | |
138 | get_unaligned_le32(&strings_package->string_info_offset)); | |
139 | debug("language_name: %u\n", | |
140 | get_unaligned_le16(&strings_package->language_name)); | |
141 | debug("language: %s\n", strings_package->language); | |
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: | |
163 | debug("unknown HII string block type: %02x\n", | |
164 | block->block_type); | |
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; | |
198 | debug("%4u: \"%ls\"\n", idx + 1, ucs2->string_text); | |
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: | |
213 | debug("unknown HII string block type: %02x\n", | |
214 | block->block_type); | |
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); | |
230 | if (idx > 0) | |
231 | while (--idx >= 0) | |
232 | free(stbl->strings[idx].string); | |
233 | free(stbl->strings); | |
234 | } | |
235 | free(stbl); | |
236 | ||
237 | return ret; | |
238 | } | |
239 | ||
9ab0bdd9 AT |
240 | static void remove_guid_package(struct efi_hii_packagelist *hii) |
241 | { | |
242 | struct efi_guid_data *data; | |
243 | ||
244 | while (!list_empty(&hii->guid_list)) { | |
245 | data = list_first_entry(&hii->guid_list, | |
246 | struct efi_guid_data, link); | |
247 | list_del(&data->link); | |
248 | free(data); | |
249 | } | |
250 | } | |
251 | ||
252 | static efi_status_t | |
253 | add_guid_package(struct efi_hii_packagelist *hii, | |
254 | struct efi_hii_guid_package *package) | |
255 | { | |
256 | struct efi_guid_data *data; | |
257 | ||
258 | data = calloc(sizeof(*data), 1); | |
259 | if (!data) | |
260 | return EFI_OUT_OF_RESOURCES; | |
261 | ||
262 | /* TODO: we don't know any about data field */ | |
263 | memcpy(&data->package, package, sizeof(*package)); | |
264 | list_add_tail(&data->link, &hii->guid_list); | |
265 | ||
266 | return EFI_SUCCESS; | |
267 | } | |
268 | ||
8d3b77e3 AT |
269 | static void free_keyboard_layouts(struct efi_keyboard_package_data *package) |
270 | { | |
271 | struct efi_keyboard_layout_data *layout_data; | |
272 | ||
273 | while (!list_empty(&package->keyboard_layout_list)) { | |
274 | layout_data = list_first_entry(&package->keyboard_layout_list, | |
275 | struct efi_keyboard_layout_data, | |
276 | link); | |
277 | list_del(&layout_data->link); | |
278 | list_del(&layout_data->link_sys); | |
279 | free(layout_data); | |
280 | } | |
281 | } | |
282 | ||
283 | static void remove_keyboard_package(struct efi_hii_packagelist *hii) | |
284 | { | |
285 | struct efi_keyboard_package_data *package; | |
286 | ||
287 | while (!list_empty(&hii->keyboard_packages)) { | |
288 | package = list_first_entry(&hii->keyboard_packages, | |
289 | struct efi_keyboard_package_data, | |
290 | link); | |
291 | free_keyboard_layouts(package); | |
292 | list_del(&package->link); | |
293 | free(package); | |
294 | } | |
295 | } | |
296 | ||
297 | static efi_status_t | |
298 | add_keyboard_package(struct efi_hii_packagelist *hii, | |
299 | struct efi_hii_keyboard_package *keyboard_package) | |
300 | { | |
301 | struct efi_keyboard_package_data *package_data; | |
302 | struct efi_hii_keyboard_layout *layout; | |
303 | struct efi_keyboard_layout_data *layout_data; | |
304 | u16 layout_count, layout_length; | |
305 | int i; | |
306 | ||
307 | package_data = malloc(sizeof(*package_data)); | |
308 | if (!package_data) | |
309 | return EFI_OUT_OF_RESOURCES; | |
310 | INIT_LIST_HEAD(&package_data->link); | |
311 | INIT_LIST_HEAD(&package_data->keyboard_layout_list); | |
312 | ||
313 | layout = &keyboard_package->layout[0]; | |
314 | layout_count = get_unaligned_le16(&keyboard_package->layout_count); | |
315 | for (i = 0; i < layout_count; i++) { | |
316 | layout_length = get_unaligned_le16(&layout->layout_length); | |
317 | layout_data = malloc(sizeof(*layout_data) + layout_length); | |
318 | if (!layout_data) | |
319 | goto out; | |
320 | ||
321 | memcpy(&layout_data->keyboard_layout, layout, layout_length); | |
322 | list_add_tail(&layout_data->link, | |
323 | &package_data->keyboard_layout_list); | |
324 | list_add_tail(&layout_data->link_sys, | |
325 | &efi_keyboard_layout_list); | |
326 | ||
327 | layout += layout_length; | |
328 | } | |
329 | ||
330 | list_add_tail(&package_data->link, &hii->keyboard_packages); | |
331 | ||
332 | return EFI_SUCCESS; | |
333 | ||
334 | out: | |
335 | free_keyboard_layouts(package_data); | |
336 | free(package_data); | |
337 | ||
338 | return EFI_OUT_OF_RESOURCES; | |
339 | } | |
340 | ||
c9bfb222 LL |
341 | static struct efi_hii_packagelist *new_packagelist(void) |
342 | { | |
343 | struct efi_hii_packagelist *hii; | |
344 | ||
345 | hii = malloc(sizeof(*hii)); | |
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 | ||
375 | debug("package_list: %pUl (%u)\n", &package_list->package_list_guid, | |
376 | get_unaligned_le32(&package_list->package_length)); | |
377 | ||
378 | package = ((void *)package_list) + sizeof(*package_list); | |
379 | while ((void *)package < end) { | |
380 | debug("package=%p, package type=%x, length=%u\n", package, | |
381 | efi_hii_package_type(package), | |
382 | efi_hii_package_len(package)); | |
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: | |
390 | printf("\tForm package not supported\n"); | |
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: | |
398 | printf("\tFont package not supported\n"); | |
399 | ret = EFI_INVALID_PARAMETER; | |
400 | break; | |
401 | case EFI_HII_PACKAGE_IMAGES: | |
402 | printf("\tImage package not supported\n"); | |
403 | ret = EFI_INVALID_PARAMETER; | |
404 | break; | |
405 | case EFI_HII_PACKAGE_SIMPLE_FONTS: | |
406 | printf("\tSimple font package not supported\n"); | |
407 | ret = EFI_INVALID_PARAMETER; | |
408 | break; | |
409 | case EFI_HII_PACKAGE_DEVICE_PATH: | |
410 | printf("\tDevice path package not supported\n"); | |
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: | |
418 | printf("\tAnimation package not supported\n"); | |
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; | |
468 | list_add_tail(&hii->link, &efi_package_lists); | |
469 | *handle = hii; | |
470 | ||
471 | return EFI_EXIT(EFI_SUCCESS); | |
472 | } | |
473 | ||
474 | static efi_status_t EFIAPI | |
475 | remove_package_list(const struct efi_hii_database_protocol *this, | |
476 | efi_hii_handle_t handle) | |
477 | { | |
478 | struct efi_hii_packagelist *hii = handle; | |
479 | ||
480 | EFI_ENTRY("%p, %p", this, handle); | |
481 | ||
482 | if (!handle || !efi_hii_packagelist_exists(handle)) | |
483 | return EFI_EXIT(EFI_NOT_FOUND); | |
484 | ||
485 | free_packagelist(hii); | |
486 | ||
487 | return EFI_EXIT(EFI_SUCCESS); | |
488 | } | |
489 | ||
490 | static efi_status_t EFIAPI | |
491 | update_package_list(const struct efi_hii_database_protocol *this, | |
492 | efi_hii_handle_t handle, | |
493 | const struct efi_hii_package_list_header *package_list) | |
494 | { | |
495 | struct efi_hii_packagelist *hii = handle; | |
496 | struct efi_hii_package_header *package; | |
497 | void *end; | |
498 | efi_status_t ret = EFI_SUCCESS; | |
499 | ||
500 | EFI_ENTRY("%p, %p, %p", this, handle, package_list); | |
501 | ||
502 | if (!handle || !efi_hii_packagelist_exists(handle)) | |
503 | return EFI_EXIT(EFI_NOT_FOUND); | |
504 | ||
505 | if (!package_list) | |
506 | return EFI_EXIT(EFI_INVALID_PARAMETER); | |
507 | ||
508 | debug("package_list: %pUl (%u)\n", &package_list->package_list_guid, | |
509 | get_unaligned_le32(&package_list->package_length)); | |
510 | ||
511 | package = ((void *)package_list) + sizeof(*package_list); | |
512 | end = ((void *)package_list) | |
513 | + get_unaligned_le32(&package_list->package_length); | |
514 | ||
515 | while ((void *)package < end) { | |
516 | debug("package=%p, package type=%x, length=%u\n", package, | |
517 | efi_hii_package_type(package), | |
518 | efi_hii_package_len(package)); | |
519 | ||
520 | switch (efi_hii_package_type(package)) { | |
521 | case EFI_HII_PACKAGE_TYPE_GUID: | |
9ab0bdd9 | 522 | remove_guid_package(hii); |
c9bfb222 LL |
523 | break; |
524 | case EFI_HII_PACKAGE_FORMS: | |
525 | printf("\tForm package not supported\n"); | |
526 | ret = EFI_INVALID_PARAMETER; | |
527 | break; | |
528 | case EFI_HII_PACKAGE_STRINGS: | |
529 | remove_strings_package(hii); | |
530 | break; | |
531 | case EFI_HII_PACKAGE_FONTS: | |
532 | printf("\tFont package not supported\n"); | |
533 | ret = EFI_INVALID_PARAMETER; | |
534 | break; | |
535 | case EFI_HII_PACKAGE_IMAGES: | |
536 | printf("\tImage package not supported\n"); | |
537 | ret = EFI_INVALID_PARAMETER; | |
538 | break; | |
539 | case EFI_HII_PACKAGE_SIMPLE_FONTS: | |
540 | printf("\tSimple font package not supported\n"); | |
541 | ret = EFI_INVALID_PARAMETER; | |
542 | break; | |
543 | case EFI_HII_PACKAGE_DEVICE_PATH: | |
544 | printf("\tDevice path package not supported\n"); | |
545 | ret = EFI_INVALID_PARAMETER; | |
546 | break; | |
547 | case EFI_HII_PACKAGE_KEYBOARD_LAYOUT: | |
8d3b77e3 | 548 | remove_keyboard_package(hii); |
c9bfb222 LL |
549 | break; |
550 | case EFI_HII_PACKAGE_ANIMATIONS: | |
551 | printf("\tAnimation package not supported\n"); | |
552 | ret = EFI_INVALID_PARAMETER; | |
553 | break; | |
554 | case EFI_HII_PACKAGE_END: | |
555 | goto out; | |
556 | case EFI_HII_PACKAGE_TYPE_SYSTEM_BEGIN: | |
557 | case EFI_HII_PACKAGE_TYPE_SYSTEM_END: | |
558 | default: | |
559 | break; | |
560 | } | |
561 | ||
562 | /* TODO: already removed some packages */ | |
563 | if (ret != EFI_SUCCESS) | |
564 | return EFI_EXIT(ret); | |
565 | ||
566 | package = ((void *)package) | |
567 | + efi_hii_package_len(package); | |
568 | } | |
569 | out: | |
570 | ret = add_packages(hii, package_list); | |
571 | ||
572 | return EFI_EXIT(ret); | |
573 | } | |
574 | ||
575 | static efi_status_t EFIAPI | |
576 | list_package_lists(const struct efi_hii_database_protocol *this, | |
577 | u8 package_type, | |
578 | const efi_guid_t *package_guid, | |
579 | efi_uintn_t *handle_buffer_length, | |
580 | efi_hii_handle_t *handle) | |
581 | { | |
582 | struct efi_hii_packagelist *hii = | |
583 | (struct efi_hii_packagelist *)handle; | |
584 | int package_cnt, package_max; | |
585 | efi_status_t ret = EFI_SUCCESS; | |
586 | ||
587 | EFI_ENTRY("%p, %u, %pUl, %p, %p", this, package_type, package_guid, | |
588 | handle_buffer_length, handle); | |
589 | ||
590 | if (!handle_buffer_length || | |
591 | (*handle_buffer_length && !handle)) | |
592 | return EFI_EXIT(EFI_INVALID_PARAMETER); | |
593 | ||
594 | if ((package_type != EFI_HII_PACKAGE_TYPE_GUID && package_guid) || | |
595 | (package_type == EFI_HII_PACKAGE_TYPE_GUID && !package_guid)) | |
596 | return EFI_EXIT(EFI_INVALID_PARAMETER); | |
597 | ||
598 | debug("package type=%x, guid=%pUl, length=%lu\n", (int)package_type, | |
599 | package_guid, *handle_buffer_length); | |
600 | ||
601 | package_cnt = 0; | |
602 | package_max = *handle_buffer_length / sizeof(*handle); | |
603 | list_for_each_entry(hii, &efi_package_lists, link) { | |
604 | switch (package_type) { | |
605 | case EFI_HII_PACKAGE_TYPE_ALL: | |
606 | break; | |
607 | case EFI_HII_PACKAGE_TYPE_GUID: | |
9ab0bdd9 AT |
608 | if (!list_empty(&hii->guid_list)) |
609 | break; | |
c9bfb222 LL |
610 | continue; |
611 | case EFI_HII_PACKAGE_FORMS: | |
612 | printf("\tForm package not supported\n"); | |
613 | ret = EFI_INVALID_PARAMETER; | |
614 | continue; | |
615 | case EFI_HII_PACKAGE_STRINGS: | |
616 | if (!list_empty(&hii->string_tables)) | |
617 | break; | |
618 | continue; | |
619 | case EFI_HII_PACKAGE_FONTS: | |
620 | printf("\tFont package not supported\n"); | |
621 | ret = EFI_INVALID_PARAMETER; | |
622 | continue; | |
623 | case EFI_HII_PACKAGE_IMAGES: | |
624 | printf("\tImage package not supported\n"); | |
625 | ret = EFI_INVALID_PARAMETER; | |
626 | continue; | |
627 | case EFI_HII_PACKAGE_SIMPLE_FONTS: | |
628 | printf("\tSimple font package not supported\n"); | |
629 | ret = EFI_INVALID_PARAMETER; | |
630 | continue; | |
631 | case EFI_HII_PACKAGE_DEVICE_PATH: | |
632 | printf("\tDevice path package not supported\n"); | |
633 | ret = EFI_INVALID_PARAMETER; | |
634 | continue; | |
635 | case EFI_HII_PACKAGE_KEYBOARD_LAYOUT: | |
8d3b77e3 AT |
636 | if (!list_empty(&hii->keyboard_packages)) |
637 | break; | |
c9bfb222 LL |
638 | continue; |
639 | case EFI_HII_PACKAGE_ANIMATIONS: | |
640 | printf("\tAnimation package not supported\n"); | |
641 | ret = EFI_INVALID_PARAMETER; | |
642 | continue; | |
643 | case EFI_HII_PACKAGE_END: | |
644 | case EFI_HII_PACKAGE_TYPE_SYSTEM_BEGIN: | |
645 | case EFI_HII_PACKAGE_TYPE_SYSTEM_END: | |
646 | default: | |
647 | continue; | |
648 | } | |
649 | ||
650 | package_cnt++; | |
651 | if (package_cnt <= package_max) | |
652 | *handle++ = hii; | |
653 | else | |
654 | ret = EFI_BUFFER_TOO_SMALL; | |
655 | } | |
656 | *handle_buffer_length = package_cnt * sizeof(*handle); | |
657 | ||
658 | return EFI_EXIT(ret); | |
659 | } | |
660 | ||
661 | static efi_status_t EFIAPI | |
662 | export_package_lists(const struct efi_hii_database_protocol *this, | |
663 | efi_hii_handle_t handle, | |
664 | efi_uintn_t *buffer_size, | |
665 | struct efi_hii_package_list_header *buffer) | |
666 | { | |
667 | EFI_ENTRY("%p, %p, %p, %p", this, handle, buffer_size, buffer); | |
668 | ||
669 | if (!buffer_size || !buffer) | |
670 | return EFI_EXIT(EFI_INVALID_PARAMETER); | |
671 | ||
672 | return EFI_EXIT(EFI_NOT_FOUND); | |
673 | } | |
674 | ||
675 | static efi_status_t EFIAPI | |
676 | register_package_notify(const struct efi_hii_database_protocol *this, | |
677 | u8 package_type, | |
678 | const efi_guid_t *package_guid, | |
679 | const void *package_notify_fn, | |
680 | efi_uintn_t notify_type, | |
681 | efi_handle_t *notify_handle) | |
682 | { | |
683 | EFI_ENTRY("%p, %u, %pUl, %p, %zu, %p", this, package_type, | |
684 | package_guid, package_notify_fn, notify_type, | |
685 | notify_handle); | |
686 | ||
687 | if (!notify_handle) | |
688 | return EFI_EXIT(EFI_INVALID_PARAMETER); | |
689 | ||
690 | if ((package_type != EFI_HII_PACKAGE_TYPE_GUID && package_guid) || | |
691 | (package_type == EFI_HII_PACKAGE_TYPE_GUID && !package_guid)) | |
692 | return EFI_EXIT(EFI_INVALID_PARAMETER); | |
693 | ||
694 | return EFI_EXIT(EFI_OUT_OF_RESOURCES); | |
695 | } | |
696 | ||
697 | static efi_status_t EFIAPI | |
698 | unregister_package_notify(const struct efi_hii_database_protocol *this, | |
699 | efi_handle_t notification_handle) | |
700 | { | |
701 | EFI_ENTRY("%p, %p", this, notification_handle); | |
702 | ||
703 | return EFI_EXIT(EFI_NOT_FOUND); | |
704 | } | |
705 | ||
706 | static efi_status_t EFIAPI | |
707 | find_keyboard_layouts(const struct efi_hii_database_protocol *this, | |
708 | u16 *key_guid_buffer_length, | |
709 | efi_guid_t *key_guid_buffer) | |
710 | { | |
8d3b77e3 AT |
711 | struct efi_keyboard_layout_data *layout_data; |
712 | int package_cnt, package_max; | |
713 | efi_status_t ret = EFI_SUCCESS; | |
714 | ||
c9bfb222 LL |
715 | EFI_ENTRY("%p, %p, %p", this, key_guid_buffer_length, key_guid_buffer); |
716 | ||
8d3b77e3 AT |
717 | if (!key_guid_buffer_length || |
718 | (*key_guid_buffer_length && !key_guid_buffer)) | |
719 | return EFI_EXIT(EFI_INVALID_PARAMETER); | |
720 | ||
721 | package_cnt = 0; | |
722 | package_max = *key_guid_buffer_length / sizeof(*key_guid_buffer); | |
723 | list_for_each_entry(layout_data, &efi_keyboard_layout_list, link_sys) { | |
724 | package_cnt++; | |
725 | if (package_cnt <= package_max) | |
726 | memcpy(key_guid_buffer++, | |
727 | &layout_data->keyboard_layout.guid, | |
728 | sizeof(*key_guid_buffer)); | |
729 | else | |
730 | ret = EFI_BUFFER_TOO_SMALL; | |
731 | } | |
732 | *key_guid_buffer_length = package_cnt * sizeof(*key_guid_buffer); | |
733 | ||
734 | return EFI_EXIT(ret); | |
c9bfb222 LL |
735 | } |
736 | ||
737 | static efi_status_t EFIAPI | |
738 | get_keyboard_layout(const struct efi_hii_database_protocol *this, | |
739 | efi_guid_t *key_guid, | |
740 | u16 *keyboard_layout_length, | |
741 | struct efi_hii_keyboard_layout *keyboard_layout) | |
742 | { | |
8d3b77e3 AT |
743 | struct efi_keyboard_layout_data *layout_data; |
744 | u16 layout_length; | |
745 | ||
c9bfb222 LL |
746 | EFI_ENTRY("%p, %pUl, %p, %p", this, key_guid, keyboard_layout_length, |
747 | keyboard_layout); | |
748 | ||
8d3b77e3 AT |
749 | if (!keyboard_layout_length || |
750 | (*keyboard_layout_length && !keyboard_layout)) | |
751 | return EFI_EXIT(EFI_INVALID_PARAMETER); | |
752 | ||
753 | /* TODO: no notion of current keyboard layout */ | |
754 | if (!key_guid) | |
755 | return EFI_EXIT(EFI_INVALID_PARAMETER); | |
756 | ||
757 | list_for_each_entry(layout_data, &efi_keyboard_layout_list, link_sys) { | |
758 | if (!guidcmp(&layout_data->keyboard_layout.guid, key_guid)) | |
759 | goto found; | |
760 | } | |
761 | ||
c9bfb222 | 762 | return EFI_EXIT(EFI_NOT_FOUND); |
8d3b77e3 AT |
763 | |
764 | found: | |
765 | layout_length = | |
766 | get_unaligned_le16(&layout_data->keyboard_layout.layout_length); | |
767 | if (*keyboard_layout_length < layout_length) { | |
768 | *keyboard_layout_length = layout_length; | |
769 | return EFI_EXIT(EFI_BUFFER_TOO_SMALL); | |
770 | } | |
771 | ||
772 | memcpy(keyboard_layout, &layout_data->keyboard_layout, layout_length); | |
773 | ||
774 | return EFI_EXIT(EFI_SUCCESS); | |
c9bfb222 LL |
775 | } |
776 | ||
777 | static efi_status_t EFIAPI | |
778 | set_keyboard_layout(const struct efi_hii_database_protocol *this, | |
779 | efi_guid_t *key_guid) | |
780 | { | |
781 | EFI_ENTRY("%p, %pUl", this, key_guid); | |
782 | ||
783 | return EFI_EXIT(EFI_NOT_FOUND); | |
784 | } | |
785 | ||
786 | static efi_status_t EFIAPI | |
787 | get_package_list_handle(const struct efi_hii_database_protocol *this, | |
788 | efi_hii_handle_t package_list_handle, | |
789 | efi_handle_t *driver_handle) | |
790 | { | |
791 | struct efi_hii_packagelist *hii; | |
792 | ||
793 | EFI_ENTRY("%p, %p, %p", this, package_list_handle, driver_handle); | |
794 | ||
795 | if (!driver_handle) | |
796 | return EFI_EXIT(EFI_INVALID_PARAMETER); | |
797 | ||
798 | list_for_each_entry(hii, &efi_package_lists, link) { | |
799 | if (hii == package_list_handle) { | |
800 | *driver_handle = hii->driver_handle; | |
801 | return EFI_EXIT(EFI_SUCCESS); | |
802 | } | |
803 | } | |
804 | ||
805 | return EFI_EXIT(EFI_NOT_FOUND); | |
806 | } | |
807 | ||
808 | const struct efi_hii_database_protocol efi_hii_database = { | |
809 | .new_package_list = new_package_list, | |
810 | .remove_package_list = remove_package_list, | |
811 | .update_package_list = update_package_list, | |
812 | .list_package_lists = list_package_lists, | |
813 | .export_package_lists = export_package_lists, | |
814 | .register_package_notify = register_package_notify, | |
815 | .unregister_package_notify = unregister_package_notify, | |
816 | .find_keyboard_layouts = find_keyboard_layouts, | |
817 | .get_keyboard_layout = get_keyboard_layout, | |
818 | .set_keyboard_layout = set_keyboard_layout, | |
819 | .get_package_list_handle = get_package_list_handle | |
820 | }; | |
821 | ||
822 | /* | |
823 | * EFI_HII_STRING_PROTOCOL | |
824 | */ | |
825 | ||
826 | static bool language_match(char *language, char *languages) | |
827 | { | |
828 | size_t n; | |
829 | ||
830 | n = strlen(language); | |
831 | /* match primary language? */ | |
832 | if (!strncasecmp(language, languages, n) && | |
833 | (languages[n] == ';' || languages[n] == '\0')) | |
834 | return true; | |
835 | ||
836 | return false; | |
837 | } | |
838 | ||
839 | static efi_status_t EFIAPI | |
840 | new_string(const struct efi_hii_string_protocol *this, | |
841 | efi_hii_handle_t package_list, | |
842 | efi_string_id_t *string_id, | |
843 | const u8 *language, | |
844 | const u16 *language_name, | |
845 | const efi_string_t string, | |
846 | const struct efi_font_info *string_font_info) | |
847 | { | |
848 | struct efi_hii_packagelist *hii = package_list; | |
849 | struct efi_string_table *stbl; | |
850 | ||
851 | EFI_ENTRY("%p, %p, %p, \"%s\", %p, \"%ls\", %p", this, package_list, | |
852 | string_id, language, language_name, string, | |
853 | string_font_info); | |
854 | ||
855 | if (!package_list || !efi_hii_packagelist_exists(package_list)) | |
856 | return EFI_EXIT(EFI_NOT_FOUND); | |
857 | ||
858 | if (!string_id || !language || !string) | |
859 | return EFI_EXIT(EFI_INVALID_PARAMETER); | |
860 | ||
861 | list_for_each_entry(stbl, &hii->string_tables, link) { | |
862 | if (language_match((char *)language, stbl->language)) { | |
863 | efi_string_id_t new_id; | |
864 | void *buf; | |
865 | efi_string_t str; | |
866 | ||
867 | new_id = ++hii->max_string_id; | |
868 | if (stbl->nstrings < new_id) { | |
869 | buf = realloc(stbl->strings, | |
870 | sizeof(stbl->strings[0]) | |
871 | * new_id); | |
872 | if (!buf) | |
873 | return EFI_EXIT(EFI_OUT_OF_RESOURCES); | |
874 | ||
875 | memset(&stbl->strings[stbl->nstrings], 0, | |
876 | (new_id - stbl->nstrings) | |
877 | * sizeof(stbl->strings[0])); | |
878 | stbl->strings = buf; | |
879 | stbl->nstrings = new_id; | |
880 | } | |
881 | ||
882 | str = u16_strdup(string); | |
883 | if (!str) | |
884 | return EFI_EXIT(EFI_OUT_OF_RESOURCES); | |
885 | ||
886 | stbl->strings[new_id - 1].string = str; | |
887 | *string_id = new_id; | |
888 | ||
889 | return EFI_EXIT(EFI_SUCCESS); | |
890 | } | |
891 | } | |
892 | ||
893 | return EFI_EXIT(EFI_NOT_FOUND); | |
894 | } | |
895 | ||
896 | static efi_status_t EFIAPI | |
897 | get_string(const struct efi_hii_string_protocol *this, | |
898 | const u8 *language, | |
899 | efi_hii_handle_t package_list, | |
900 | efi_string_id_t string_id, | |
901 | efi_string_t string, | |
902 | efi_uintn_t *string_size, | |
903 | struct efi_font_info **string_font_info) | |
904 | { | |
905 | struct efi_hii_packagelist *hii = package_list; | |
906 | struct efi_string_table *stbl; | |
907 | ||
908 | EFI_ENTRY("%p, \"%s\", %p, %u, %p, %p, %p", this, language, | |
909 | package_list, string_id, string, string_size, | |
910 | string_font_info); | |
911 | ||
912 | if (!package_list || !efi_hii_packagelist_exists(package_list)) | |
913 | return EFI_EXIT(EFI_NOT_FOUND); | |
914 | ||
915 | list_for_each_entry(stbl, &hii->string_tables, link) { | |
916 | if (language_match((char *)language, stbl->language)) { | |
917 | efi_string_t str; | |
918 | size_t len; | |
919 | ||
920 | if (stbl->nstrings < string_id) | |
921 | return EFI_EXIT(EFI_NOT_FOUND); | |
922 | ||
923 | str = stbl->strings[string_id - 1].string; | |
924 | if (str) { | |
925 | len = (u16_strlen(str) + 1) * sizeof(u16); | |
926 | if (*string_size < len) { | |
927 | *string_size = len; | |
928 | ||
929 | return EFI_EXIT(EFI_BUFFER_TOO_SMALL); | |
930 | } | |
931 | memcpy(string, str, len); | |
932 | *string_size = len; | |
933 | } else { | |
934 | return EFI_EXIT(EFI_NOT_FOUND); | |
935 | } | |
936 | ||
937 | return EFI_EXIT(EFI_SUCCESS); | |
938 | } | |
939 | } | |
940 | ||
941 | return EFI_EXIT(EFI_NOT_FOUND); | |
942 | } | |
943 | ||
944 | static efi_status_t EFIAPI | |
945 | set_string(const struct efi_hii_string_protocol *this, | |
946 | efi_hii_handle_t package_list, | |
947 | efi_string_id_t string_id, | |
948 | const u8 *language, | |
949 | const efi_string_t string, | |
950 | const struct efi_font_info *string_font_info) | |
951 | { | |
952 | struct efi_hii_packagelist *hii = package_list; | |
953 | struct efi_string_table *stbl; | |
954 | ||
955 | EFI_ENTRY("%p, %p, %u, \"%s\", \"%ls\", %p", this, package_list, | |
956 | string_id, language, string, string_font_info); | |
957 | ||
958 | if (!package_list || !efi_hii_packagelist_exists(package_list)) | |
959 | return EFI_EXIT(EFI_NOT_FOUND); | |
960 | ||
961 | if (string_id > hii->max_string_id) | |
962 | return EFI_EXIT(EFI_NOT_FOUND); | |
963 | ||
964 | if (!string || !language) | |
965 | return EFI_EXIT(EFI_INVALID_PARAMETER); | |
966 | ||
967 | list_for_each_entry(stbl, &hii->string_tables, link) { | |
968 | if (language_match((char *)language, stbl->language)) { | |
969 | efi_string_t str; | |
970 | ||
971 | if (hii->max_string_id < string_id) | |
972 | return EFI_EXIT(EFI_NOT_FOUND); | |
973 | ||
974 | if (stbl->nstrings < string_id) { | |
975 | void *buf; | |
976 | ||
977 | buf = realloc(stbl->strings, | |
978 | string_id | |
979 | * sizeof(stbl->strings[0])); | |
980 | if (!buf) | |
981 | return EFI_EXIT(EFI_OUT_OF_RESOURCES); | |
982 | ||
983 | memset(&stbl->strings[string_id - 1], 0, | |
984 | (string_id - stbl->nstrings) | |
985 | * sizeof(stbl->strings[0])); | |
986 | stbl->strings = buf; | |
987 | } | |
988 | ||
989 | str = u16_strdup(string); | |
990 | if (!str) | |
991 | return EFI_EXIT(EFI_OUT_OF_RESOURCES); | |
992 | ||
993 | free(stbl->strings[string_id - 1].string); | |
994 | stbl->strings[string_id - 1].string = str; | |
995 | ||
996 | return EFI_EXIT(EFI_SUCCESS); | |
997 | } | |
998 | } | |
999 | ||
1000 | return EFI_EXIT(EFI_NOT_FOUND); | |
1001 | } | |
1002 | ||
1003 | static efi_status_t EFIAPI | |
1004 | get_languages(const struct efi_hii_string_protocol *this, | |
1005 | efi_hii_handle_t package_list, | |
1006 | u8 *languages, | |
1007 | efi_uintn_t *languages_size) | |
1008 | { | |
1009 | struct efi_hii_packagelist *hii = package_list; | |
1010 | struct efi_string_table *stbl; | |
1011 | size_t len = 0; | |
1012 | char *p; | |
1013 | ||
1014 | EFI_ENTRY("%p, %p, %p, %p", this, package_list, languages, | |
1015 | languages_size); | |
1016 | ||
1017 | if (!package_list || !efi_hii_packagelist_exists(package_list)) | |
1018 | return EFI_EXIT(EFI_NOT_FOUND); | |
1019 | ||
1020 | if (!languages_size || | |
1021 | (*languages_size && !languages)) | |
1022 | return EFI_EXIT(EFI_INVALID_PARAMETER); | |
1023 | ||
1024 | /* figure out required size: */ | |
1025 | list_for_each_entry(stbl, &hii->string_tables, link) { | |
1026 | len += strlen((char *)stbl->language) + 1; | |
1027 | } | |
1028 | ||
1029 | if (*languages_size < len) { | |
1030 | *languages_size = len; | |
1031 | ||
1032 | return EFI_EXIT(EFI_BUFFER_TOO_SMALL); | |
1033 | } | |
1034 | ||
1035 | p = (char *)languages; | |
1036 | list_for_each_entry(stbl, &hii->string_tables, link) { | |
1037 | if (p != (char *)languages) | |
1038 | *p++ = ';'; | |
1039 | strcpy(p, stbl->language); | |
1040 | p += strlen((char *)stbl->language); | |
1041 | } | |
1042 | *p = '\0'; | |
1043 | ||
1044 | debug("languages: %s\n", languages); | |
1045 | ||
1046 | return EFI_EXIT(EFI_SUCCESS); | |
1047 | } | |
1048 | ||
1049 | static efi_status_t EFIAPI | |
1050 | get_secondary_languages(const struct efi_hii_string_protocol *this, | |
1051 | efi_hii_handle_t package_list, | |
1052 | const u8 *primary_language, | |
1053 | u8 *secondary_languages, | |
1054 | efi_uintn_t *secondary_languages_size) | |
1055 | { | |
1056 | struct efi_hii_packagelist *hii = package_list; | |
1057 | struct efi_string_table *stbl; | |
1058 | bool found = false; | |
1059 | ||
1060 | EFI_ENTRY("%p, %p, \"%s\", %p, %p", this, package_list, | |
1061 | primary_language, secondary_languages, | |
1062 | secondary_languages_size); | |
1063 | ||
1064 | if (!package_list || !efi_hii_packagelist_exists(package_list)) | |
1065 | return EFI_EXIT(EFI_NOT_FOUND); | |
1066 | ||
1067 | if (!secondary_languages_size || | |
1068 | (*secondary_languages_size && !secondary_languages)) | |
1069 | return EFI_EXIT(EFI_INVALID_PARAMETER); | |
1070 | ||
1071 | list_for_each_entry(stbl, &hii->string_tables, link) { | |
1072 | if (language_match((char *)primary_language, stbl->language)) { | |
1073 | found = true; | |
1074 | break; | |
1075 | } | |
1076 | } | |
1077 | if (!found) | |
1078 | return EFI_EXIT(EFI_INVALID_LANGUAGE); | |
1079 | ||
1080 | /* | |
1081 | * TODO: What is secondary language? | |
1082 | * *secondary_languages = '\0'; | |
1083 | * *secondary_languages_size = 0; | |
1084 | */ | |
1085 | ||
1086 | return EFI_EXIT(EFI_NOT_FOUND); | |
1087 | } | |
1088 | ||
1089 | const struct efi_hii_string_protocol efi_hii_string = { | |
1090 | .new_string = new_string, | |
1091 | .get_string = get_string, | |
1092 | .set_string = set_string, | |
1093 | .get_languages = get_languages, | |
1094 | .get_secondary_languages = get_secondary_languages | |
1095 | }; |