blob: 3b54ecb11ac13e59f7dc42a60afc401c1efd9a85 [file] [log] [blame]
Leif Lindholmc9bfb222019-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);
AKASHI Takahiro8d3b77e2019-01-21 12:12:59 +090019static LIST_HEAD(efi_keyboard_layout_list);
Leif Lindholmc9bfb222019-01-21 12:12:57 +090020
21struct 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 */
AKASHI Takahiro9ab0bdd2019-01-21 12:12:58 +090027 struct list_head guid_list;
AKASHI Takahiro8d3b77e2019-01-21 12:12:59 +090028 struct list_head keyboard_packages;
Leif Lindholmc9bfb222019-01-21 12:12:57 +090029
30 /* we could also track fonts, images, etc */
31};
32
33static 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
48static 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
58static 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
68struct efi_string_info {
69 efi_string_t string;
70 /* we could also track font info, etc */
71};
72
73struct 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
AKASHI Takahiro9ab0bdd2019-01-21 12:12:58 +090086struct efi_guid_data {
87 struct list_head link;
88 struct efi_hii_guid_package package;
89};
90
AKASHI Takahiro8d3b77e2019-01-21 12:12:59 +090091struct 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
97struct efi_keyboard_package_data {
98 struct list_head link; /* in package_list */
99 struct list_head keyboard_layout_list;
100};
101
Leif Lindholmc9bfb222019-01-21 12:12:57 +0900102static 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
113static 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
125static efi_status_t
126add_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
Heinrich Schuchardt6f8f4212019-01-23 22:55:36 +0100135 EFI_PRINT("header_size: %08x\n",
136 get_unaligned_le32(&strings_package->header_size));
137 EFI_PRINT("string_info_offset: %08x\n",
138 get_unaligned_le32(&strings_package->string_info_offset));
139 EFI_PRINT("language_name: %u\n",
140 get_unaligned_le16(&strings_package->language_name));
141 EFI_PRINT("language: %s\n", strings_package->language);
Leif Lindholmc9bfb222019-01-21 12:12:57 +0900142
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:
Heinrich Schuchardt6f8f4212019-01-23 22:55:36 +0100163 EFI_PRINT("unknown HII string block type: %02x\n",
164 block->block_type);
Leif Lindholmc9bfb222019-01-21 12:12:57 +0900165 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;
Heinrich Schuchardt6f8f4212019-01-23 22:55:36 +0100198 EFI_PRINT("%4u: \"%ls\"\n", idx + 1, ucs2->string_text);
Leif Lindholmc9bfb222019-01-21 12:12:57 +0900199 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:
Heinrich Schuchardt6f8f4212019-01-23 22:55:36 +0100213 EFI_PRINT("unknown HII string block type: %02x\n",
214 block->block_type);
Leif Lindholmc9bfb222019-01-21 12:12:57 +0900215 ret = EFI_INVALID_PARAMETER;
216 goto error;
217 }
218 }
219
220out:
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
227error:
228 if (stbl) {
229 free(stbl->language);
Heinrich Schuchardtbd3b7472019-03-19 12:30:27 +0100230 while (idx > 0)
231 free(stbl->strings[--idx].string);
Leif Lindholmc9bfb222019-01-21 12:12:57 +0900232 free(stbl->strings);
233 }
234 free(stbl);
235
236 return ret;
237}
238
AKASHI Takahiro9ab0bdd2019-01-21 12:12:58 +0900239static void remove_guid_package(struct efi_hii_packagelist *hii)
240{
241 struct efi_guid_data *data;
242
243 while (!list_empty(&hii->guid_list)) {
244 data = list_first_entry(&hii->guid_list,
245 struct efi_guid_data, link);
246 list_del(&data->link);
247 free(data);
248 }
249}
250
251static efi_status_t
252add_guid_package(struct efi_hii_packagelist *hii,
253 struct efi_hii_guid_package *package)
254{
255 struct efi_guid_data *data;
256
257 data = calloc(sizeof(*data), 1);
258 if (!data)
259 return EFI_OUT_OF_RESOURCES;
260
261 /* TODO: we don't know any about data field */
262 memcpy(&data->package, package, sizeof(*package));
263 list_add_tail(&data->link, &hii->guid_list);
264
265 return EFI_SUCCESS;
266}
267
AKASHI Takahiro8d3b77e2019-01-21 12:12:59 +0900268static void free_keyboard_layouts(struct efi_keyboard_package_data *package)
269{
270 struct efi_keyboard_layout_data *layout_data;
271
272 while (!list_empty(&package->keyboard_layout_list)) {
273 layout_data = list_first_entry(&package->keyboard_layout_list,
274 struct efi_keyboard_layout_data,
275 link);
276 list_del(&layout_data->link);
277 list_del(&layout_data->link_sys);
278 free(layout_data);
279 }
280}
281
282static void remove_keyboard_package(struct efi_hii_packagelist *hii)
283{
284 struct efi_keyboard_package_data *package;
285
286 while (!list_empty(&hii->keyboard_packages)) {
287 package = list_first_entry(&hii->keyboard_packages,
288 struct efi_keyboard_package_data,
289 link);
290 free_keyboard_layouts(package);
291 list_del(&package->link);
292 free(package);
293 }
294}
295
296static efi_status_t
297add_keyboard_package(struct efi_hii_packagelist *hii,
298 struct efi_hii_keyboard_package *keyboard_package)
299{
300 struct efi_keyboard_package_data *package_data;
301 struct efi_hii_keyboard_layout *layout;
302 struct efi_keyboard_layout_data *layout_data;
303 u16 layout_count, layout_length;
304 int i;
305
306 package_data = malloc(sizeof(*package_data));
307 if (!package_data)
308 return EFI_OUT_OF_RESOURCES;
309 INIT_LIST_HEAD(&package_data->link);
310 INIT_LIST_HEAD(&package_data->keyboard_layout_list);
311
312 layout = &keyboard_package->layout[0];
313 layout_count = get_unaligned_le16(&keyboard_package->layout_count);
314 for (i = 0; i < layout_count; i++) {
315 layout_length = get_unaligned_le16(&layout->layout_length);
316 layout_data = malloc(sizeof(*layout_data) + layout_length);
317 if (!layout_data)
318 goto out;
319
320 memcpy(&layout_data->keyboard_layout, layout, layout_length);
321 list_add_tail(&layout_data->link,
322 &package_data->keyboard_layout_list);
323 list_add_tail(&layout_data->link_sys,
324 &efi_keyboard_layout_list);
325
326 layout += layout_length;
327 }
328
329 list_add_tail(&package_data->link, &hii->keyboard_packages);
330
331 return EFI_SUCCESS;
332
333out:
334 free_keyboard_layouts(package_data);
335 free(package_data);
336
337 return EFI_OUT_OF_RESOURCES;
338}
339
Leif Lindholmc9bfb222019-01-21 12:12:57 +0900340static struct efi_hii_packagelist *new_packagelist(void)
341{
342 struct efi_hii_packagelist *hii;
343
344 hii = malloc(sizeof(*hii));
Heinrich Schuchardtfdef2982019-02-28 23:07:00 +0100345 list_add_tail(&hii->link, &efi_package_lists);
Leif Lindholmc9bfb222019-01-21 12:12:57 +0900346 hii->max_string_id = 0;
347 INIT_LIST_HEAD(&hii->string_tables);
AKASHI Takahiro9ab0bdd2019-01-21 12:12:58 +0900348 INIT_LIST_HEAD(&hii->guid_list);
AKASHI Takahiro8d3b77e2019-01-21 12:12:59 +0900349 INIT_LIST_HEAD(&hii->keyboard_packages);
Leif Lindholmc9bfb222019-01-21 12:12:57 +0900350
351 return hii;
352}
353
354static void free_packagelist(struct efi_hii_packagelist *hii)
355{
356 remove_strings_package(hii);
AKASHI Takahiro9ab0bdd2019-01-21 12:12:58 +0900357 remove_guid_package(hii);
AKASHI Takahiro8d3b77e2019-01-21 12:12:59 +0900358 remove_keyboard_package(hii);
Leif Lindholmc9bfb222019-01-21 12:12:57 +0900359
360 list_del(&hii->link);
361 free(hii);
362}
363
364static efi_status_t
365add_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
Heinrich Schuchardtce00a742022-01-16 14:15:31 +0100375 EFI_PRINT("package_list: %pUs (%u)\n", &package_list->package_list_guid,
Heinrich Schuchardt6f8f4212019-01-23 22:55:36 +0100376 get_unaligned_le32(&package_list->package_length));
Leif Lindholmc9bfb222019-01-21 12:12:57 +0900377
378 package = ((void *)package_list) + sizeof(*package_list);
379 while ((void *)package < end) {
Heinrich Schuchardt6f8f4212019-01-23 22:55:36 +0100380 EFI_PRINT("package=%p, package type=%x, length=%u\n", package,
381 efi_hii_package_type(package),
382 efi_hii_package_len(package));
Leif Lindholmc9bfb222019-01-21 12:12:57 +0900383
384 switch (efi_hii_package_type(package)) {
385 case EFI_HII_PACKAGE_TYPE_GUID:
AKASHI Takahiro9ab0bdd2019-01-21 12:12:58 +0900386 ret = add_guid_package(hii,
387 (struct efi_hii_guid_package *)package);
Leif Lindholmc9bfb222019-01-21 12:12:57 +0900388 break;
389 case EFI_HII_PACKAGE_FORMS:
Heinrich Schuchardt1a9fce52019-02-28 23:56:35 +0100390 EFI_PRINT("Form package not supported\n");
Leif Lindholmc9bfb222019-01-21 12:12:57 +0900391 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:
Heinrich Schuchardt1a9fce52019-02-28 23:56:35 +0100398 EFI_PRINT("Font package not supported\n");
Leif Lindholmc9bfb222019-01-21 12:12:57 +0900399 ret = EFI_INVALID_PARAMETER;
400 break;
401 case EFI_HII_PACKAGE_IMAGES:
Heinrich Schuchardt1a9fce52019-02-28 23:56:35 +0100402 EFI_PRINT("Image package not supported\n");
Leif Lindholmc9bfb222019-01-21 12:12:57 +0900403 ret = EFI_INVALID_PARAMETER;
404 break;
405 case EFI_HII_PACKAGE_SIMPLE_FONTS:
Heinrich Schuchardt1a9fce52019-02-28 23:56:35 +0100406 EFI_PRINT("Simple font package not supported\n");
Leif Lindholmc9bfb222019-01-21 12:12:57 +0900407 ret = EFI_INVALID_PARAMETER;
408 break;
409 case EFI_HII_PACKAGE_DEVICE_PATH:
Heinrich Schuchardt1a9fce52019-02-28 23:56:35 +0100410 EFI_PRINT("Device path package not supported\n");
Leif Lindholmc9bfb222019-01-21 12:12:57 +0900411 ret = EFI_INVALID_PARAMETER;
412 break;
413 case EFI_HII_PACKAGE_KEYBOARD_LAYOUT:
AKASHI Takahiro8d3b77e2019-01-21 12:12:59 +0900414 ret = add_keyboard_package(hii,
415 (struct efi_hii_keyboard_package *)package);
Leif Lindholmc9bfb222019-01-21 12:12:57 +0900416 break;
417 case EFI_HII_PACKAGE_ANIMATIONS:
Heinrich Schuchardt1a9fce52019-02-28 23:56:35 +0100418 EFI_PRINT("Animation package not supported\n");
Leif Lindholmc9bfb222019-01-21 12:12:57 +0900419 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 }
434out:
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
443static efi_status_t EFIAPI
444new_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;
Leif Lindholmc9bfb222019-01-21 12:12:57 +0900468 *handle = hii;
469
470 return EFI_EXIT(EFI_SUCCESS);
471}
472
473static efi_status_t EFIAPI
474remove_package_list(const struct efi_hii_database_protocol *this,
475 efi_hii_handle_t handle)
476{
477 struct efi_hii_packagelist *hii = handle;
478
479 EFI_ENTRY("%p, %p", this, handle);
480
481 if (!handle || !efi_hii_packagelist_exists(handle))
482 return EFI_EXIT(EFI_NOT_FOUND);
483
484 free_packagelist(hii);
485
486 return EFI_EXIT(EFI_SUCCESS);
487}
488
489static efi_status_t EFIAPI
490update_package_list(const struct efi_hii_database_protocol *this,
491 efi_hii_handle_t handle,
492 const struct efi_hii_package_list_header *package_list)
493{
494 struct efi_hii_packagelist *hii = handle;
495 struct efi_hii_package_header *package;
496 void *end;
497 efi_status_t ret = EFI_SUCCESS;
498
499 EFI_ENTRY("%p, %p, %p", this, handle, package_list);
500
501 if (!handle || !efi_hii_packagelist_exists(handle))
502 return EFI_EXIT(EFI_NOT_FOUND);
503
504 if (!package_list)
505 return EFI_EXIT(EFI_INVALID_PARAMETER);
506
Heinrich Schuchardtce00a742022-01-16 14:15:31 +0100507 EFI_PRINT("package_list: %pUs (%u)\n", &package_list->package_list_guid,
Heinrich Schuchardt6f8f4212019-01-23 22:55:36 +0100508 get_unaligned_le32(&package_list->package_length));
Leif Lindholmc9bfb222019-01-21 12:12:57 +0900509
510 package = ((void *)package_list) + sizeof(*package_list);
511 end = ((void *)package_list)
512 + get_unaligned_le32(&package_list->package_length);
513
514 while ((void *)package < end) {
Heinrich Schuchardt6f8f4212019-01-23 22:55:36 +0100515 EFI_PRINT("package=%p, package type=%x, length=%u\n", package,
516 efi_hii_package_type(package),
517 efi_hii_package_len(package));
Leif Lindholmc9bfb222019-01-21 12:12:57 +0900518
519 switch (efi_hii_package_type(package)) {
520 case EFI_HII_PACKAGE_TYPE_GUID:
AKASHI Takahiro9ab0bdd2019-01-21 12:12:58 +0900521 remove_guid_package(hii);
Leif Lindholmc9bfb222019-01-21 12:12:57 +0900522 break;
523 case EFI_HII_PACKAGE_FORMS:
Heinrich Schuchardt1a9fce52019-02-28 23:56:35 +0100524 EFI_PRINT("Form package not supported\n");
Leif Lindholmc9bfb222019-01-21 12:12:57 +0900525 ret = EFI_INVALID_PARAMETER;
526 break;
527 case EFI_HII_PACKAGE_STRINGS:
528 remove_strings_package(hii);
529 break;
530 case EFI_HII_PACKAGE_FONTS:
Heinrich Schuchardt1a9fce52019-02-28 23:56:35 +0100531 EFI_PRINT("Font package not supported\n");
Leif Lindholmc9bfb222019-01-21 12:12:57 +0900532 ret = EFI_INVALID_PARAMETER;
533 break;
534 case EFI_HII_PACKAGE_IMAGES:
Heinrich Schuchardt1a9fce52019-02-28 23:56:35 +0100535 EFI_PRINT("Image package not supported\n");
Leif Lindholmc9bfb222019-01-21 12:12:57 +0900536 ret = EFI_INVALID_PARAMETER;
537 break;
538 case EFI_HII_PACKAGE_SIMPLE_FONTS:
Heinrich Schuchardt1a9fce52019-02-28 23:56:35 +0100539 EFI_PRINT("Simple font package not supported\n");
Leif Lindholmc9bfb222019-01-21 12:12:57 +0900540 ret = EFI_INVALID_PARAMETER;
541 break;
542 case EFI_HII_PACKAGE_DEVICE_PATH:
Heinrich Schuchardt1a9fce52019-02-28 23:56:35 +0100543 EFI_PRINT("Device path package not supported\n");
Leif Lindholmc9bfb222019-01-21 12:12:57 +0900544 ret = EFI_INVALID_PARAMETER;
545 break;
546 case EFI_HII_PACKAGE_KEYBOARD_LAYOUT:
AKASHI Takahiro8d3b77e2019-01-21 12:12:59 +0900547 remove_keyboard_package(hii);
Leif Lindholmc9bfb222019-01-21 12:12:57 +0900548 break;
549 case EFI_HII_PACKAGE_ANIMATIONS:
Heinrich Schuchardt1a9fce52019-02-28 23:56:35 +0100550 EFI_PRINT("Animation package not supported\n");
Leif Lindholmc9bfb222019-01-21 12:12:57 +0900551 ret = EFI_INVALID_PARAMETER;
552 break;
553 case EFI_HII_PACKAGE_END:
554 goto out;
555 case EFI_HII_PACKAGE_TYPE_SYSTEM_BEGIN:
556 case EFI_HII_PACKAGE_TYPE_SYSTEM_END:
557 default:
558 break;
559 }
560
561 /* TODO: already removed some packages */
562 if (ret != EFI_SUCCESS)
563 return EFI_EXIT(ret);
564
565 package = ((void *)package)
566 + efi_hii_package_len(package);
567 }
568out:
569 ret = add_packages(hii, package_list);
570
571 return EFI_EXIT(ret);
572}
573
574static efi_status_t EFIAPI
575list_package_lists(const struct efi_hii_database_protocol *this,
576 u8 package_type,
577 const efi_guid_t *package_guid,
578 efi_uintn_t *handle_buffer_length,
579 efi_hii_handle_t *handle)
580{
581 struct efi_hii_packagelist *hii =
582 (struct efi_hii_packagelist *)handle;
583 int package_cnt, package_max;
Heinrich Schuchardtc974ab02019-06-17 20:46:29 +0200584 efi_status_t ret = EFI_NOT_FOUND;
Leif Lindholmc9bfb222019-01-21 12:12:57 +0900585
Heinrich Schuchardtce00a742022-01-16 14:15:31 +0100586 EFI_ENTRY("%p, %u, %pUs, %p, %p", this, package_type, package_guid,
Leif Lindholmc9bfb222019-01-21 12:12:57 +0900587 handle_buffer_length, handle);
588
589 if (!handle_buffer_length ||
Heinrich Schuchardtc974ab02019-06-17 20:46:29 +0200590 (*handle_buffer_length && !handle)) {
591 ret = EFI_INVALID_PARAMETER;
592 goto out;
593 }
Leif Lindholmc9bfb222019-01-21 12:12:57 +0900594
595 if ((package_type != EFI_HII_PACKAGE_TYPE_GUID && package_guid) ||
Heinrich Schuchardtc974ab02019-06-17 20:46:29 +0200596 (package_type == EFI_HII_PACKAGE_TYPE_GUID && !package_guid)) {
597 ret = EFI_INVALID_PARAMETER;
598 goto out;
599 }
Leif Lindholmc9bfb222019-01-21 12:12:57 +0900600
Heinrich Schuchardtce00a742022-01-16 14:15:31 +0100601 EFI_PRINT("package type=%x, guid=%pUs, length=%zu\n", (int)package_type,
Heinrich Schuchardt6f8f4212019-01-23 22:55:36 +0100602 package_guid, *handle_buffer_length);
Leif Lindholmc9bfb222019-01-21 12:12:57 +0900603
604 package_cnt = 0;
605 package_max = *handle_buffer_length / sizeof(*handle);
606 list_for_each_entry(hii, &efi_package_lists, link) {
607 switch (package_type) {
608 case EFI_HII_PACKAGE_TYPE_ALL:
609 break;
610 case EFI_HII_PACKAGE_TYPE_GUID:
AKASHI Takahiro9ab0bdd2019-01-21 12:12:58 +0900611 if (!list_empty(&hii->guid_list))
612 break;
Leif Lindholmc9bfb222019-01-21 12:12:57 +0900613 continue;
Leif Lindholmc9bfb222019-01-21 12:12:57 +0900614 case EFI_HII_PACKAGE_STRINGS:
615 if (!list_empty(&hii->string_tables))
616 break;
617 continue;
Leif Lindholmc9bfb222019-01-21 12:12:57 +0900618 case EFI_HII_PACKAGE_KEYBOARD_LAYOUT:
AKASHI Takahiro8d3b77e2019-01-21 12:12:59 +0900619 if (!list_empty(&hii->keyboard_packages))
620 break;
Leif Lindholmc9bfb222019-01-21 12:12:57 +0900621 continue;
Leif Lindholmc9bfb222019-01-21 12:12:57 +0900622 default:
623 continue;
624 }
625
626 package_cnt++;
Heinrich Schuchardtc974ab02019-06-17 20:46:29 +0200627 if (package_cnt <= package_max) {
Leif Lindholmc9bfb222019-01-21 12:12:57 +0900628 *handle++ = hii;
Heinrich Schuchardtc974ab02019-06-17 20:46:29 +0200629 ret = EFI_SUCCESS;
630 } else {
Leif Lindholmc9bfb222019-01-21 12:12:57 +0900631 ret = EFI_BUFFER_TOO_SMALL;
Heinrich Schuchardtc974ab02019-06-17 20:46:29 +0200632 }
Leif Lindholmc9bfb222019-01-21 12:12:57 +0900633 }
634 *handle_buffer_length = package_cnt * sizeof(*handle);
Heinrich Schuchardtc974ab02019-06-17 20:46:29 +0200635out:
Leif Lindholmc9bfb222019-01-21 12:12:57 +0900636 return EFI_EXIT(ret);
637}
638
639static efi_status_t EFIAPI
640export_package_lists(const struct efi_hii_database_protocol *this,
641 efi_hii_handle_t handle,
642 efi_uintn_t *buffer_size,
643 struct efi_hii_package_list_header *buffer)
644{
645 EFI_ENTRY("%p, %p, %p, %p", this, handle, buffer_size, buffer);
646
647 if (!buffer_size || !buffer)
648 return EFI_EXIT(EFI_INVALID_PARAMETER);
649
650 return EFI_EXIT(EFI_NOT_FOUND);
651}
652
653static efi_status_t EFIAPI
654register_package_notify(const struct efi_hii_database_protocol *this,
655 u8 package_type,
656 const efi_guid_t *package_guid,
657 const void *package_notify_fn,
658 efi_uintn_t notify_type,
659 efi_handle_t *notify_handle)
660{
Heinrich Schuchardtce00a742022-01-16 14:15:31 +0100661 EFI_ENTRY("%p, %u, %pUs, %p, %zu, %p", this, package_type,
Leif Lindholmc9bfb222019-01-21 12:12:57 +0900662 package_guid, package_notify_fn, notify_type,
663 notify_handle);
664
665 if (!notify_handle)
666 return EFI_EXIT(EFI_INVALID_PARAMETER);
667
668 if ((package_type != EFI_HII_PACKAGE_TYPE_GUID && package_guid) ||
669 (package_type == EFI_HII_PACKAGE_TYPE_GUID && !package_guid))
670 return EFI_EXIT(EFI_INVALID_PARAMETER);
671
672 return EFI_EXIT(EFI_OUT_OF_RESOURCES);
673}
674
675static efi_status_t EFIAPI
676unregister_package_notify(const struct efi_hii_database_protocol *this,
677 efi_handle_t notification_handle)
678{
679 EFI_ENTRY("%p, %p", this, notification_handle);
680
681 return EFI_EXIT(EFI_NOT_FOUND);
682}
683
684static efi_status_t EFIAPI
685find_keyboard_layouts(const struct efi_hii_database_protocol *this,
686 u16 *key_guid_buffer_length,
687 efi_guid_t *key_guid_buffer)
688{
AKASHI Takahiro8d3b77e2019-01-21 12:12:59 +0900689 struct efi_keyboard_layout_data *layout_data;
690 int package_cnt, package_max;
691 efi_status_t ret = EFI_SUCCESS;
692
Leif Lindholmc9bfb222019-01-21 12:12:57 +0900693 EFI_ENTRY("%p, %p, %p", this, key_guid_buffer_length, key_guid_buffer);
694
AKASHI Takahiro8d3b77e2019-01-21 12:12:59 +0900695 if (!key_guid_buffer_length ||
696 (*key_guid_buffer_length && !key_guid_buffer))
697 return EFI_EXIT(EFI_INVALID_PARAMETER);
698
699 package_cnt = 0;
700 package_max = *key_guid_buffer_length / sizeof(*key_guid_buffer);
701 list_for_each_entry(layout_data, &efi_keyboard_layout_list, link_sys) {
702 package_cnt++;
703 if (package_cnt <= package_max)
704 memcpy(key_guid_buffer++,
705 &layout_data->keyboard_layout.guid,
706 sizeof(*key_guid_buffer));
707 else
708 ret = EFI_BUFFER_TOO_SMALL;
709 }
710 *key_guid_buffer_length = package_cnt * sizeof(*key_guid_buffer);
711
712 return EFI_EXIT(ret);
Leif Lindholmc9bfb222019-01-21 12:12:57 +0900713}
714
715static efi_status_t EFIAPI
716get_keyboard_layout(const struct efi_hii_database_protocol *this,
717 efi_guid_t *key_guid,
718 u16 *keyboard_layout_length,
719 struct efi_hii_keyboard_layout *keyboard_layout)
720{
AKASHI Takahiro8d3b77e2019-01-21 12:12:59 +0900721 struct efi_keyboard_layout_data *layout_data;
722 u16 layout_length;
723
Heinrich Schuchardtce00a742022-01-16 14:15:31 +0100724 EFI_ENTRY("%p, %pUs, %p, %p", this, key_guid, keyboard_layout_length,
Leif Lindholmc9bfb222019-01-21 12:12:57 +0900725 keyboard_layout);
726
AKASHI Takahiro8d3b77e2019-01-21 12:12:59 +0900727 if (!keyboard_layout_length ||
728 (*keyboard_layout_length && !keyboard_layout))
729 return EFI_EXIT(EFI_INVALID_PARAMETER);
730
731 /* TODO: no notion of current keyboard layout */
732 if (!key_guid)
733 return EFI_EXIT(EFI_INVALID_PARAMETER);
734
735 list_for_each_entry(layout_data, &efi_keyboard_layout_list, link_sys) {
736 if (!guidcmp(&layout_data->keyboard_layout.guid, key_guid))
737 goto found;
738 }
739
Leif Lindholmc9bfb222019-01-21 12:12:57 +0900740 return EFI_EXIT(EFI_NOT_FOUND);
AKASHI Takahiro8d3b77e2019-01-21 12:12:59 +0900741
742found:
743 layout_length =
744 get_unaligned_le16(&layout_data->keyboard_layout.layout_length);
745 if (*keyboard_layout_length < layout_length) {
746 *keyboard_layout_length = layout_length;
747 return EFI_EXIT(EFI_BUFFER_TOO_SMALL);
748 }
749
750 memcpy(keyboard_layout, &layout_data->keyboard_layout, layout_length);
751
752 return EFI_EXIT(EFI_SUCCESS);
Leif Lindholmc9bfb222019-01-21 12:12:57 +0900753}
754
755static efi_status_t EFIAPI
756set_keyboard_layout(const struct efi_hii_database_protocol *this,
757 efi_guid_t *key_guid)
758{
Heinrich Schuchardtce00a742022-01-16 14:15:31 +0100759 EFI_ENTRY("%p, %pUs", this, key_guid);
Leif Lindholmc9bfb222019-01-21 12:12:57 +0900760
Vincent Stehlé65b91a32023-01-06 10:46:40 +0100761 if (!key_guid)
762 return EFI_EXIT(EFI_INVALID_PARAMETER);
763
Leif Lindholmc9bfb222019-01-21 12:12:57 +0900764 return EFI_EXIT(EFI_NOT_FOUND);
765}
766
767static efi_status_t EFIAPI
768get_package_list_handle(const struct efi_hii_database_protocol *this,
769 efi_hii_handle_t package_list_handle,
770 efi_handle_t *driver_handle)
771{
772 struct efi_hii_packagelist *hii;
773
774 EFI_ENTRY("%p, %p, %p", this, package_list_handle, driver_handle);
775
776 if (!driver_handle)
777 return EFI_EXIT(EFI_INVALID_PARAMETER);
778
779 list_for_each_entry(hii, &efi_package_lists, link) {
780 if (hii == package_list_handle) {
781 *driver_handle = hii->driver_handle;
782 return EFI_EXIT(EFI_SUCCESS);
783 }
784 }
785
Vincent Stehléc33d3892022-12-13 22:39:09 +0100786 return EFI_EXIT(EFI_INVALID_PARAMETER);
Leif Lindholmc9bfb222019-01-21 12:12:57 +0900787}
788
789const struct efi_hii_database_protocol efi_hii_database = {
790 .new_package_list = new_package_list,
791 .remove_package_list = remove_package_list,
792 .update_package_list = update_package_list,
793 .list_package_lists = list_package_lists,
794 .export_package_lists = export_package_lists,
795 .register_package_notify = register_package_notify,
796 .unregister_package_notify = unregister_package_notify,
797 .find_keyboard_layouts = find_keyboard_layouts,
798 .get_keyboard_layout = get_keyboard_layout,
799 .set_keyboard_layout = set_keyboard_layout,
800 .get_package_list_handle = get_package_list_handle
801};
802
803/*
804 * EFI_HII_STRING_PROTOCOL
805 */
806
807static bool language_match(char *language, char *languages)
808{
809 size_t n;
810
811 n = strlen(language);
812 /* match primary language? */
813 if (!strncasecmp(language, languages, n) &&
814 (languages[n] == ';' || languages[n] == '\0'))
815 return true;
816
817 return false;
818}
819
820static efi_status_t EFIAPI
821new_string(const struct efi_hii_string_protocol *this,
822 efi_hii_handle_t package_list,
823 efi_string_id_t *string_id,
824 const u8 *language,
825 const u16 *language_name,
826 const efi_string_t string,
827 const struct efi_font_info *string_font_info)
828{
829 struct efi_hii_packagelist *hii = package_list;
830 struct efi_string_table *stbl;
831
832 EFI_ENTRY("%p, %p, %p, \"%s\", %p, \"%ls\", %p", this, package_list,
833 string_id, language, language_name, string,
834 string_font_info);
835
836 if (!package_list || !efi_hii_packagelist_exists(package_list))
837 return EFI_EXIT(EFI_NOT_FOUND);
838
839 if (!string_id || !language || !string)
840 return EFI_EXIT(EFI_INVALID_PARAMETER);
841
842 list_for_each_entry(stbl, &hii->string_tables, link) {
843 if (language_match((char *)language, stbl->language)) {
844 efi_string_id_t new_id;
845 void *buf;
846 efi_string_t str;
847
848 new_id = ++hii->max_string_id;
849 if (stbl->nstrings < new_id) {
850 buf = realloc(stbl->strings,
851 sizeof(stbl->strings[0])
852 * new_id);
853 if (!buf)
854 return EFI_EXIT(EFI_OUT_OF_RESOURCES);
855
856 memset(&stbl->strings[stbl->nstrings], 0,
857 (new_id - stbl->nstrings)
858 * sizeof(stbl->strings[0]));
859 stbl->strings = buf;
860 stbl->nstrings = new_id;
861 }
862
863 str = u16_strdup(string);
864 if (!str)
865 return EFI_EXIT(EFI_OUT_OF_RESOURCES);
866
867 stbl->strings[new_id - 1].string = str;
868 *string_id = new_id;
869
870 return EFI_EXIT(EFI_SUCCESS);
871 }
872 }
873
874 return EFI_EXIT(EFI_NOT_FOUND);
875}
876
877static efi_status_t EFIAPI
878get_string(const struct efi_hii_string_protocol *this,
879 const u8 *language,
880 efi_hii_handle_t package_list,
881 efi_string_id_t string_id,
882 efi_string_t string,
883 efi_uintn_t *string_size,
884 struct efi_font_info **string_font_info)
885{
886 struct efi_hii_packagelist *hii = package_list;
887 struct efi_string_table *stbl;
888
889 EFI_ENTRY("%p, \"%s\", %p, %u, %p, %p, %p", this, language,
890 package_list, string_id, string, string_size,
891 string_font_info);
892
893 if (!package_list || !efi_hii_packagelist_exists(package_list))
894 return EFI_EXIT(EFI_NOT_FOUND);
895
896 list_for_each_entry(stbl, &hii->string_tables, link) {
897 if (language_match((char *)language, stbl->language)) {
898 efi_string_t str;
899 size_t len;
900
901 if (stbl->nstrings < string_id)
902 return EFI_EXIT(EFI_NOT_FOUND);
903
904 str = stbl->strings[string_id - 1].string;
905 if (str) {
Heinrich Schuchardtb27d8e42022-04-02 11:47:00 +0200906 len = u16_strsize(str);
Leif Lindholmc9bfb222019-01-21 12:12:57 +0900907 if (*string_size < len) {
908 *string_size = len;
909
910 return EFI_EXIT(EFI_BUFFER_TOO_SMALL);
911 }
912 memcpy(string, str, len);
913 *string_size = len;
914 } else {
915 return EFI_EXIT(EFI_NOT_FOUND);
916 }
917
918 return EFI_EXIT(EFI_SUCCESS);
919 }
920 }
921
922 return EFI_EXIT(EFI_NOT_FOUND);
923}
924
925static efi_status_t EFIAPI
926set_string(const struct efi_hii_string_protocol *this,
927 efi_hii_handle_t package_list,
928 efi_string_id_t string_id,
929 const u8 *language,
930 const efi_string_t string,
931 const struct efi_font_info *string_font_info)
932{
933 struct efi_hii_packagelist *hii = package_list;
934 struct efi_string_table *stbl;
935
936 EFI_ENTRY("%p, %p, %u, \"%s\", \"%ls\", %p", this, package_list,
937 string_id, language, string, string_font_info);
938
939 if (!package_list || !efi_hii_packagelist_exists(package_list))
940 return EFI_EXIT(EFI_NOT_FOUND);
941
942 if (string_id > hii->max_string_id)
943 return EFI_EXIT(EFI_NOT_FOUND);
944
945 if (!string || !language)
946 return EFI_EXIT(EFI_INVALID_PARAMETER);
947
948 list_for_each_entry(stbl, &hii->string_tables, link) {
949 if (language_match((char *)language, stbl->language)) {
950 efi_string_t str;
951
952 if (hii->max_string_id < string_id)
953 return EFI_EXIT(EFI_NOT_FOUND);
954
955 if (stbl->nstrings < string_id) {
956 void *buf;
957
958 buf = realloc(stbl->strings,
959 string_id
960 * sizeof(stbl->strings[0]));
961 if (!buf)
962 return EFI_EXIT(EFI_OUT_OF_RESOURCES);
963
964 memset(&stbl->strings[string_id - 1], 0,
965 (string_id - stbl->nstrings)
966 * sizeof(stbl->strings[0]));
967 stbl->strings = buf;
968 }
969
970 str = u16_strdup(string);
971 if (!str)
972 return EFI_EXIT(EFI_OUT_OF_RESOURCES);
973
974 free(stbl->strings[string_id - 1].string);
975 stbl->strings[string_id - 1].string = str;
976
977 return EFI_EXIT(EFI_SUCCESS);
978 }
979 }
980
981 return EFI_EXIT(EFI_NOT_FOUND);
982}
983
984static efi_status_t EFIAPI
985get_languages(const struct efi_hii_string_protocol *this,
986 efi_hii_handle_t package_list,
987 u8 *languages,
988 efi_uintn_t *languages_size)
989{
990 struct efi_hii_packagelist *hii = package_list;
991 struct efi_string_table *stbl;
992 size_t len = 0;
993 char *p;
994
995 EFI_ENTRY("%p, %p, %p, %p", this, package_list, languages,
996 languages_size);
997
998 if (!package_list || !efi_hii_packagelist_exists(package_list))
999 return EFI_EXIT(EFI_NOT_FOUND);
1000
1001 if (!languages_size ||
1002 (*languages_size && !languages))
1003 return EFI_EXIT(EFI_INVALID_PARAMETER);
1004
1005 /* figure out required size: */
1006 list_for_each_entry(stbl, &hii->string_tables, link) {
1007 len += strlen((char *)stbl->language) + 1;
1008 }
1009
1010 if (*languages_size < len) {
1011 *languages_size = len;
1012
1013 return EFI_EXIT(EFI_BUFFER_TOO_SMALL);
1014 }
1015
1016 p = (char *)languages;
1017 list_for_each_entry(stbl, &hii->string_tables, link) {
1018 if (p != (char *)languages)
1019 *p++ = ';';
1020 strcpy(p, stbl->language);
1021 p += strlen((char *)stbl->language);
1022 }
1023 *p = '\0';
1024
Heinrich Schuchardt6f8f4212019-01-23 22:55:36 +01001025 EFI_PRINT("languages: %s\n", languages);
Leif Lindholmc9bfb222019-01-21 12:12:57 +09001026
1027 return EFI_EXIT(EFI_SUCCESS);
1028}
1029
1030static efi_status_t EFIAPI
1031get_secondary_languages(const struct efi_hii_string_protocol *this,
1032 efi_hii_handle_t package_list,
1033 const u8 *primary_language,
1034 u8 *secondary_languages,
1035 efi_uintn_t *secondary_languages_size)
1036{
1037 struct efi_hii_packagelist *hii = package_list;
1038 struct efi_string_table *stbl;
1039 bool found = false;
1040
1041 EFI_ENTRY("%p, %p, \"%s\", %p, %p", this, package_list,
1042 primary_language, secondary_languages,
1043 secondary_languages_size);
1044
1045 if (!package_list || !efi_hii_packagelist_exists(package_list))
1046 return EFI_EXIT(EFI_NOT_FOUND);
1047
1048 if (!secondary_languages_size ||
1049 (*secondary_languages_size && !secondary_languages))
1050 return EFI_EXIT(EFI_INVALID_PARAMETER);
1051
1052 list_for_each_entry(stbl, &hii->string_tables, link) {
1053 if (language_match((char *)primary_language, stbl->language)) {
1054 found = true;
1055 break;
1056 }
1057 }
1058 if (!found)
1059 return EFI_EXIT(EFI_INVALID_LANGUAGE);
1060
1061 /*
1062 * TODO: What is secondary language?
1063 * *secondary_languages = '\0';
1064 * *secondary_languages_size = 0;
1065 */
1066
1067 return EFI_EXIT(EFI_NOT_FOUND);
1068}
1069
1070const struct efi_hii_string_protocol efi_hii_string = {
1071 .new_string = new_string,
1072 .get_string = get_string,
1073 .set_string = set_string,
1074 .get_languages = get_languages,
1075 .get_secondary_languages = get_secondary_languages
1076};