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