efi: hii: add guid package support
[oweals/u-boot.git] / lib / efi_loader / efi_hii.c
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);
19
20 struct 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 */
26         struct list_head guid_list;
27
28         /* we could also track fonts, images, etc */
29 };
30
31 static 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
46 static 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
56 static 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
66 struct efi_string_info {
67         efi_string_t string;
68         /* we could also track font info, etc */
69 };
70
71 struct 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
84 struct efi_guid_data {
85         struct list_head link;
86         struct efi_hii_guid_package package;
87 };
88
89 static 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
100 static 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
112 static efi_status_t
113 add_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
207 out:
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
214 error:
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
227 static 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
239 static efi_status_t
240 add_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
256 static 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);
263         INIT_LIST_HEAD(&hii->guid_list);
264
265         return hii;
266 }
267
268 static void free_packagelist(struct efi_hii_packagelist *hii)
269 {
270         remove_strings_package(hii);
271         remove_guid_package(hii);
272
273         list_del(&hii->link);
274         free(hii);
275 }
276
277 static efi_status_t
278 add_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:
299                         ret = add_guid_package(hii,
300                                 (struct efi_hii_guid_package *)package);
301                         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         }
347 out:
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
356 static efi_status_t EFIAPI
357 new_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
387 static efi_status_t EFIAPI
388 remove_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
403 static efi_status_t EFIAPI
404 update_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:
435                         remove_guid_package(hii);
436                         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         }
482 out:
483         ret = add_packages(hii, package_list);
484
485         return EFI_EXIT(ret);
486 }
487
488 static efi_status_t EFIAPI
489 list_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:
521                         if (!list_empty(&hii->guid_list))
522                                 break;
523                         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
573 static efi_status_t EFIAPI
574 export_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
587 static efi_status_t EFIAPI
588 register_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
609 static efi_status_t EFIAPI
610 unregister_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
618 static efi_status_t EFIAPI
619 find_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
628 static efi_status_t EFIAPI
629 get_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
640 static efi_status_t EFIAPI
641 set_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
649 static efi_status_t EFIAPI
650 get_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
671 const 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
689 static 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
702 static efi_status_t EFIAPI
703 new_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
759 static efi_status_t EFIAPI
760 get_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
807 static efi_status_t EFIAPI
808 set_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
866 static efi_status_t EFIAPI
867 get_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
912 static efi_status_t EFIAPI
913 get_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
952 const 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 };