blob: 330d7c5830b5b5a8d8e69db0205710bce3b6bd97 [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
Heinrich Schuchardt955a3212025-01-16 20:26:59 +01009#define LOG_CATEGORY LOGC_EFI
10
Leif Lindholm5aab1b82019-01-21 12:12:57 +090011#include <efi_loader.h>
12#include <malloc.h>
13#include <asm/unaligned.h>
14
15const efi_guid_t efi_guid_hii_database_protocol
16 = EFI_HII_DATABASE_PROTOCOL_GUID;
17const efi_guid_t efi_guid_hii_string_protocol = EFI_HII_STRING_PROTOCOL_GUID;
18
19static LIST_HEAD(efi_package_lists);
AKASHI Takahiro976e9312019-01-21 12:12:59 +090020static LIST_HEAD(efi_keyboard_layout_list);
Leif Lindholm5aab1b82019-01-21 12:12:57 +090021
22struct efi_hii_packagelist {
23 struct list_head link;
24 // TODO should there be an associated efi_object?
25 efi_handle_t driver_handle;
26 u32 max_string_id;
27 struct list_head string_tables; /* list of efi_string_table */
AKASHI Takahiro0fa82b92019-01-21 12:12:58 +090028 struct list_head guid_list;
AKASHI Takahiro976e9312019-01-21 12:12:59 +090029 struct list_head keyboard_packages;
Leif Lindholm5aab1b82019-01-21 12:12:57 +090030
31 /* we could also track fonts, images, etc */
32};
33
34static int efi_hii_packagelist_exists(efi_hii_handle_t package_list)
35{
36 struct efi_hii_packagelist *hii;
37 int found = 0;
38
39 list_for_each_entry(hii, &efi_package_lists, link) {
40 if (hii == package_list) {
41 found = 1;
42 break;
43 }
44 }
45
46 return found;
47}
48
49static u32 efi_hii_package_type(struct efi_hii_package_header *header)
50{
51 u32 fields;
52
53 fields = get_unaligned_le32(&header->fields);
54
55 return (fields >> __EFI_HII_PACKAGE_TYPE_SHIFT)
56 & __EFI_HII_PACKAGE_TYPE_MASK;
57}
58
59static u32 efi_hii_package_len(struct efi_hii_package_header *header)
60{
61 u32 fields;
62
63 fields = get_unaligned_le32(&header->fields);
64
65 return (fields >> __EFI_HII_PACKAGE_LEN_SHIFT)
66 & __EFI_HII_PACKAGE_LEN_MASK;
67}
68
69struct efi_string_info {
70 efi_string_t string;
71 /* we could also track font info, etc */
72};
73
74struct efi_string_table {
75 struct list_head link;
76 efi_string_id_t language_name;
77 char *language;
78 u32 nstrings;
79 /*
80 * NOTE:
81 * string id starts at 1 so value is stbl->strings[id-1],
82 * and strings[] is a array of stbl->nstrings elements
83 */
84 struct efi_string_info *strings;
85};
86
AKASHI Takahiro0fa82b92019-01-21 12:12:58 +090087struct efi_guid_data {
88 struct list_head link;
89 struct efi_hii_guid_package package;
90};
91
AKASHI Takahiro976e9312019-01-21 12:12:59 +090092struct efi_keyboard_layout_data {
93 struct list_head link; /* in package */
94 struct list_head link_sys; /* in global list */
95 struct efi_hii_keyboard_layout keyboard_layout;
96};
97
98struct efi_keyboard_package_data {
99 struct list_head link; /* in package_list */
100 struct list_head keyboard_layout_list;
101};
102
Leif Lindholm5aab1b82019-01-21 12:12:57 +0900103static void free_strings_table(struct efi_string_table *stbl)
104{
105 int i;
106
107 for (i = 0; i < stbl->nstrings; i++)
108 free(stbl->strings[i].string);
109 free(stbl->strings);
110 free(stbl->language);
111 free(stbl);
112}
113
114static void remove_strings_package(struct efi_hii_packagelist *hii)
115{
116 while (!list_empty(&hii->string_tables)) {
117 struct efi_string_table *stbl;
118
119 stbl = list_first_entry(&hii->string_tables,
120 struct efi_string_table, link);
121 list_del(&stbl->link);
122 free_strings_table(stbl);
123 }
124}
125
126static efi_status_t
127add_strings_package(struct efi_hii_packagelist *hii,
128 struct efi_hii_strings_package *strings_package)
129{
130 struct efi_hii_string_block *block;
131 void *end;
132 u32 nstrings = 0, idx = 0;
133 struct efi_string_table *stbl = NULL;
134 efi_status_t ret;
135
Heinrich Schuchardt4b6f19f2019-01-23 22:55:36 +0100136 EFI_PRINT("header_size: %08x\n",
137 get_unaligned_le32(&strings_package->header_size));
138 EFI_PRINT("string_info_offset: %08x\n",
139 get_unaligned_le32(&strings_package->string_info_offset));
140 EFI_PRINT("language_name: %u\n",
141 get_unaligned_le16(&strings_package->language_name));
142 EFI_PRINT("language: %s\n", strings_package->language);
Leif Lindholm5aab1b82019-01-21 12:12:57 +0900143
144 /* count # of string entries: */
145 end = ((void *)strings_package)
146 + efi_hii_package_len(&strings_package->header);
147 block = ((void *)strings_package)
148 + get_unaligned_le32(&strings_package->string_info_offset);
149
150 while ((void *)block < end) {
151 switch (block->block_type) {
152 case EFI_HII_SIBT_STRING_UCS2: {
153 struct efi_hii_sibt_string_ucs2_block *ucs2;
154
155 ucs2 = (void *)block;
156 nstrings++;
157 block = efi_hii_sibt_string_ucs2_block_next(ucs2);
158 break;
159 }
160 case EFI_HII_SIBT_END:
161 block = end;
162 break;
163 default:
Heinrich Schuchardt4b6f19f2019-01-23 22:55:36 +0100164 EFI_PRINT("unknown HII string block type: %02x\n",
165 block->block_type);
Leif Lindholm5aab1b82019-01-21 12:12:57 +0900166 return EFI_INVALID_PARAMETER;
167 }
168 }
169
170 stbl = calloc(sizeof(*stbl), 1);
171 if (!stbl) {
172 ret = EFI_OUT_OF_RESOURCES;
173 goto error;
174 }
175 stbl->strings = calloc(sizeof(stbl->strings[0]), nstrings);
176 if (!stbl->strings) {
177 ret = EFI_OUT_OF_RESOURCES;
178 goto error;
179 }
180 stbl->language_name =
181 get_unaligned_le16(&strings_package->language_name);
182 stbl->language = strdup((char *)strings_package->language);
183 if (!stbl->language) {
184 ret = EFI_OUT_OF_RESOURCES;
185 goto error;
186 }
187 stbl->nstrings = nstrings;
188
189 /* and now parse string entries and populate efi_string_table */
190 block = ((void *)strings_package)
191 + get_unaligned_le32(&strings_package->string_info_offset);
192
193 while ((void *)block < end) {
194 switch (block->block_type) {
195 case EFI_HII_SIBT_STRING_UCS2: {
196 struct efi_hii_sibt_string_ucs2_block *ucs2;
197
198 ucs2 = (void *)block;
Heinrich Schuchardt4b6f19f2019-01-23 22:55:36 +0100199 EFI_PRINT("%4u: \"%ls\"\n", idx + 1, ucs2->string_text);
Leif Lindholm5aab1b82019-01-21 12:12:57 +0900200 stbl->strings[idx].string =
201 u16_strdup(ucs2->string_text);
202 if (!stbl->strings[idx].string) {
203 ret = EFI_OUT_OF_RESOURCES;
204 goto error;
205 }
206 idx++;
207 /* FIXME: accessing u16 * here */
208 block = efi_hii_sibt_string_ucs2_block_next(ucs2);
209 break;
210 }
211 case EFI_HII_SIBT_END:
212 goto out;
213 default:
Heinrich Schuchardt4b6f19f2019-01-23 22:55:36 +0100214 EFI_PRINT("unknown HII string block type: %02x\n",
215 block->block_type);
Leif Lindholm5aab1b82019-01-21 12:12:57 +0900216 ret = EFI_INVALID_PARAMETER;
217 goto error;
218 }
219 }
220
221out:
222 list_add(&stbl->link, &hii->string_tables);
223 if (hii->max_string_id < nstrings)
224 hii->max_string_id = nstrings;
225
226 return EFI_SUCCESS;
227
228error:
229 if (stbl) {
230 free(stbl->language);
Heinrich Schuchardtd177dbd2019-03-19 12:30:27 +0100231 while (idx > 0)
232 free(stbl->strings[--idx].string);
Leif Lindholm5aab1b82019-01-21 12:12:57 +0900233 free(stbl->strings);
234 }
235 free(stbl);
236
237 return ret;
238}
239
AKASHI Takahiro0fa82b92019-01-21 12:12:58 +0900240static void remove_guid_package(struct efi_hii_packagelist *hii)
241{
242 struct efi_guid_data *data;
243
244 while (!list_empty(&hii->guid_list)) {
245 data = list_first_entry(&hii->guid_list,
246 struct efi_guid_data, link);
247 list_del(&data->link);
248 free(data);
249 }
250}
251
252static efi_status_t
253add_guid_package(struct efi_hii_packagelist *hii,
254 struct efi_hii_guid_package *package)
255{
256 struct efi_guid_data *data;
257
258 data = calloc(sizeof(*data), 1);
259 if (!data)
260 return EFI_OUT_OF_RESOURCES;
261
262 /* TODO: we don't know any about data field */
263 memcpy(&data->package, package, sizeof(*package));
264 list_add_tail(&data->link, &hii->guid_list);
265
266 return EFI_SUCCESS;
AKASHI Takahiro976e9312019-01-21 12:12:59 +0900267}
268
269static void free_keyboard_layouts(struct efi_keyboard_package_data *package)
270{
271 struct efi_keyboard_layout_data *layout_data;
272
273 while (!list_empty(&package->keyboard_layout_list)) {
274 layout_data = list_first_entry(&package->keyboard_layout_list,
275 struct efi_keyboard_layout_data,
276 link);
277 list_del(&layout_data->link);
278 list_del(&layout_data->link_sys);
279 free(layout_data);
280 }
281}
282
283static void remove_keyboard_package(struct efi_hii_packagelist *hii)
284{
285 struct efi_keyboard_package_data *package;
286
287 while (!list_empty(&hii->keyboard_packages)) {
288 package = list_first_entry(&hii->keyboard_packages,
289 struct efi_keyboard_package_data,
290 link);
291 free_keyboard_layouts(package);
292 list_del(&package->link);
293 free(package);
294 }
295}
296
297static efi_status_t
298add_keyboard_package(struct efi_hii_packagelist *hii,
299 struct efi_hii_keyboard_package *keyboard_package)
300{
301 struct efi_keyboard_package_data *package_data;
302 struct efi_hii_keyboard_layout *layout;
303 struct efi_keyboard_layout_data *layout_data;
304 u16 layout_count, layout_length;
305 int i;
306
307 package_data = malloc(sizeof(*package_data));
308 if (!package_data)
309 return EFI_OUT_OF_RESOURCES;
310 INIT_LIST_HEAD(&package_data->link);
311 INIT_LIST_HEAD(&package_data->keyboard_layout_list);
312
313 layout = &keyboard_package->layout[0];
314 layout_count = get_unaligned_le16(&keyboard_package->layout_count);
315 for (i = 0; i < layout_count; i++) {
316 layout_length = get_unaligned_le16(&layout->layout_length);
317 layout_data = malloc(sizeof(*layout_data) + layout_length);
318 if (!layout_data)
319 goto out;
320
321 memcpy(&layout_data->keyboard_layout, layout, layout_length);
322 list_add_tail(&layout_data->link,
323 &package_data->keyboard_layout_list);
324 list_add_tail(&layout_data->link_sys,
325 &efi_keyboard_layout_list);
326
327 layout += layout_length;
328 }
329
330 list_add_tail(&package_data->link, &hii->keyboard_packages);
331
332 return EFI_SUCCESS;
333
334out:
335 free_keyboard_layouts(package_data);
336 free(package_data);
337
338 return EFI_OUT_OF_RESOURCES;
AKASHI Takahiro0fa82b92019-01-21 12:12:58 +0900339}
340
Leif Lindholm5aab1b82019-01-21 12:12:57 +0900341static struct efi_hii_packagelist *new_packagelist(void)
342{
343 struct efi_hii_packagelist *hii;
344
345 hii = malloc(sizeof(*hii));
Vincent Stehlé2edcddf2025-04-01 13:15:00 +0200346 if (!hii)
347 return NULL;
348
Heinrich Schuchardt87895f62019-02-28 23:07:00 +0100349 list_add_tail(&hii->link, &efi_package_lists);
Leif Lindholm5aab1b82019-01-21 12:12:57 +0900350 hii->max_string_id = 0;
351 INIT_LIST_HEAD(&hii->string_tables);
AKASHI Takahiro0fa82b92019-01-21 12:12:58 +0900352 INIT_LIST_HEAD(&hii->guid_list);
AKASHI Takahiro976e9312019-01-21 12:12:59 +0900353 INIT_LIST_HEAD(&hii->keyboard_packages);
Leif Lindholm5aab1b82019-01-21 12:12:57 +0900354
355 return hii;
356}
357
358static void free_packagelist(struct efi_hii_packagelist *hii)
359{
360 remove_strings_package(hii);
AKASHI Takahiro0fa82b92019-01-21 12:12:58 +0900361 remove_guid_package(hii);
AKASHI Takahiro976e9312019-01-21 12:12:59 +0900362 remove_keyboard_package(hii);
Leif Lindholm5aab1b82019-01-21 12:12:57 +0900363
364 list_del(&hii->link);
365 free(hii);
366}
367
368static efi_status_t
369add_packages(struct efi_hii_packagelist *hii,
370 const struct efi_hii_package_list_header *package_list)
371{
372 struct efi_hii_package_header *package;
373 void *end;
374 efi_status_t ret = EFI_SUCCESS;
375
376 end = ((void *)package_list)
377 + get_unaligned_le32(&package_list->package_length);
378
Heinrich Schuchardt282249d2022-01-16 14:15:31 +0100379 EFI_PRINT("package_list: %pUs (%u)\n", &package_list->package_list_guid,
Heinrich Schuchardt4b6f19f2019-01-23 22:55:36 +0100380 get_unaligned_le32(&package_list->package_length));
Leif Lindholm5aab1b82019-01-21 12:12:57 +0900381
382 package = ((void *)package_list) + sizeof(*package_list);
383 while ((void *)package < end) {
Heinrich Schuchardt4b6f19f2019-01-23 22:55:36 +0100384 EFI_PRINT("package=%p, package type=%x, length=%u\n", package,
385 efi_hii_package_type(package),
386 efi_hii_package_len(package));
Leif Lindholm5aab1b82019-01-21 12:12:57 +0900387
388 switch (efi_hii_package_type(package)) {
389 case EFI_HII_PACKAGE_TYPE_GUID:
AKASHI Takahiro0fa82b92019-01-21 12:12:58 +0900390 ret = add_guid_package(hii,
391 (struct efi_hii_guid_package *)package);
Leif Lindholm5aab1b82019-01-21 12:12:57 +0900392 break;
393 case EFI_HII_PACKAGE_FORMS:
Heinrich Schuchardt196e2122019-02-28 23:56:35 +0100394 EFI_PRINT("Form package not supported\n");
Leif Lindholm5aab1b82019-01-21 12:12:57 +0900395 ret = EFI_INVALID_PARAMETER;
396 break;
397 case EFI_HII_PACKAGE_STRINGS:
398 ret = add_strings_package(hii,
399 (struct efi_hii_strings_package *)package);
400 break;
401 case EFI_HII_PACKAGE_FONTS:
Heinrich Schuchardt196e2122019-02-28 23:56:35 +0100402 EFI_PRINT("Font package not supported\n");
Leif Lindholm5aab1b82019-01-21 12:12:57 +0900403 ret = EFI_INVALID_PARAMETER;
404 break;
405 case EFI_HII_PACKAGE_IMAGES:
Heinrich Schuchardt196e2122019-02-28 23:56:35 +0100406 EFI_PRINT("Image package not supported\n");
Leif Lindholm5aab1b82019-01-21 12:12:57 +0900407 ret = EFI_INVALID_PARAMETER;
408 break;
409 case EFI_HII_PACKAGE_SIMPLE_FONTS:
Heinrich Schuchardt196e2122019-02-28 23:56:35 +0100410 EFI_PRINT("Simple font package not supported\n");
Leif Lindholm5aab1b82019-01-21 12:12:57 +0900411 ret = EFI_INVALID_PARAMETER;
412 break;
413 case EFI_HII_PACKAGE_DEVICE_PATH:
Heinrich Schuchardt196e2122019-02-28 23:56:35 +0100414 EFI_PRINT("Device path package not supported\n");
Leif Lindholm5aab1b82019-01-21 12:12:57 +0900415 ret = EFI_INVALID_PARAMETER;
416 break;
417 case EFI_HII_PACKAGE_KEYBOARD_LAYOUT:
AKASHI Takahiro976e9312019-01-21 12:12:59 +0900418 ret = add_keyboard_package(hii,
419 (struct efi_hii_keyboard_package *)package);
Leif Lindholm5aab1b82019-01-21 12:12:57 +0900420 break;
421 case EFI_HII_PACKAGE_ANIMATIONS:
Heinrich Schuchardt196e2122019-02-28 23:56:35 +0100422 EFI_PRINT("Animation package not supported\n");
Leif Lindholm5aab1b82019-01-21 12:12:57 +0900423 ret = EFI_INVALID_PARAMETER;
424 break;
425 case EFI_HII_PACKAGE_END:
426 goto out;
427 case EFI_HII_PACKAGE_TYPE_SYSTEM_BEGIN:
428 case EFI_HII_PACKAGE_TYPE_SYSTEM_END:
429 default:
430 break;
431 }
432
433 if (ret != EFI_SUCCESS)
434 return ret;
435
436 package = (void *)package + efi_hii_package_len(package);
437 }
438out:
439 // TODO in theory there is some notifications that should be sent..
440 return EFI_SUCCESS;
441}
442
443/*
444 * EFI_HII_DATABASE_PROTOCOL
445 */
446
447static efi_status_t EFIAPI
448new_package_list(const struct efi_hii_database_protocol *this,
449 const struct efi_hii_package_list_header *package_list,
450 const efi_handle_t driver_handle,
451 efi_hii_handle_t *handle)
452{
453 struct efi_hii_packagelist *hii;
454 efi_status_t ret;
455
456 EFI_ENTRY("%p, %p, %p, %p", this, package_list, driver_handle, handle);
457
458 if (!package_list || !handle)
459 return EFI_EXIT(EFI_INVALID_PARAMETER);
460
461 hii = new_packagelist();
462 if (!hii)
463 return EFI_EXIT(EFI_OUT_OF_RESOURCES);
464
465 ret = add_packages(hii, package_list);
466 if (ret != EFI_SUCCESS) {
467 free_packagelist(hii);
468 return EFI_EXIT(ret);
469 }
470
471 hii->driver_handle = driver_handle;
Leif Lindholm5aab1b82019-01-21 12:12:57 +0900472 *handle = hii;
473
474 return EFI_EXIT(EFI_SUCCESS);
475}
476
477static efi_status_t EFIAPI
478remove_package_list(const struct efi_hii_database_protocol *this,
479 efi_hii_handle_t handle)
480{
481 struct efi_hii_packagelist *hii = handle;
482
483 EFI_ENTRY("%p, %p", this, handle);
484
485 if (!handle || !efi_hii_packagelist_exists(handle))
486 return EFI_EXIT(EFI_NOT_FOUND);
487
488 free_packagelist(hii);
489
490 return EFI_EXIT(EFI_SUCCESS);
491}
492
493static efi_status_t EFIAPI
494update_package_list(const struct efi_hii_database_protocol *this,
495 efi_hii_handle_t handle,
496 const struct efi_hii_package_list_header *package_list)
497{
498 struct efi_hii_packagelist *hii = handle;
499 struct efi_hii_package_header *package;
500 void *end;
501 efi_status_t ret = EFI_SUCCESS;
502
503 EFI_ENTRY("%p, %p, %p", this, handle, package_list);
504
505 if (!handle || !efi_hii_packagelist_exists(handle))
506 return EFI_EXIT(EFI_NOT_FOUND);
507
508 if (!package_list)
509 return EFI_EXIT(EFI_INVALID_PARAMETER);
510
Heinrich Schuchardt282249d2022-01-16 14:15:31 +0100511 EFI_PRINT("package_list: %pUs (%u)\n", &package_list->package_list_guid,
Heinrich Schuchardt4b6f19f2019-01-23 22:55:36 +0100512 get_unaligned_le32(&package_list->package_length));
Leif Lindholm5aab1b82019-01-21 12:12:57 +0900513
514 package = ((void *)package_list) + sizeof(*package_list);
515 end = ((void *)package_list)
516 + get_unaligned_le32(&package_list->package_length);
517
518 while ((void *)package < end) {
Heinrich Schuchardt4b6f19f2019-01-23 22:55:36 +0100519 EFI_PRINT("package=%p, package type=%x, length=%u\n", package,
520 efi_hii_package_type(package),
521 efi_hii_package_len(package));
Leif Lindholm5aab1b82019-01-21 12:12:57 +0900522
523 switch (efi_hii_package_type(package)) {
524 case EFI_HII_PACKAGE_TYPE_GUID:
AKASHI Takahiro0fa82b92019-01-21 12:12:58 +0900525 remove_guid_package(hii);
Leif Lindholm5aab1b82019-01-21 12:12:57 +0900526 break;
527 case EFI_HII_PACKAGE_FORMS:
Heinrich Schuchardt196e2122019-02-28 23:56:35 +0100528 EFI_PRINT("Form package not supported\n");
Leif Lindholm5aab1b82019-01-21 12:12:57 +0900529 ret = EFI_INVALID_PARAMETER;
530 break;
531 case EFI_HII_PACKAGE_STRINGS:
532 remove_strings_package(hii);
533 break;
534 case EFI_HII_PACKAGE_FONTS:
Heinrich Schuchardt196e2122019-02-28 23:56:35 +0100535 EFI_PRINT("Font package not supported\n");
Leif Lindholm5aab1b82019-01-21 12:12:57 +0900536 ret = EFI_INVALID_PARAMETER;
537 break;
538 case EFI_HII_PACKAGE_IMAGES:
Heinrich Schuchardt196e2122019-02-28 23:56:35 +0100539 EFI_PRINT("Image package not supported\n");
Leif Lindholm5aab1b82019-01-21 12:12:57 +0900540 ret = EFI_INVALID_PARAMETER;
541 break;
542 case EFI_HII_PACKAGE_SIMPLE_FONTS:
Heinrich Schuchardt196e2122019-02-28 23:56:35 +0100543 EFI_PRINT("Simple font package not supported\n");
Leif Lindholm5aab1b82019-01-21 12:12:57 +0900544 ret = EFI_INVALID_PARAMETER;
545 break;
546 case EFI_HII_PACKAGE_DEVICE_PATH:
Heinrich Schuchardt196e2122019-02-28 23:56:35 +0100547 EFI_PRINT("Device path package not supported\n");
Leif Lindholm5aab1b82019-01-21 12:12:57 +0900548 ret = EFI_INVALID_PARAMETER;
549 break;
550 case EFI_HII_PACKAGE_KEYBOARD_LAYOUT:
AKASHI Takahiro976e9312019-01-21 12:12:59 +0900551 remove_keyboard_package(hii);
Leif Lindholm5aab1b82019-01-21 12:12:57 +0900552 break;
553 case EFI_HII_PACKAGE_ANIMATIONS:
Heinrich Schuchardt196e2122019-02-28 23:56:35 +0100554 EFI_PRINT("Animation package not supported\n");
Leif Lindholm5aab1b82019-01-21 12:12:57 +0900555 ret = EFI_INVALID_PARAMETER;
556 break;
557 case EFI_HII_PACKAGE_END:
558 goto out;
559 case EFI_HII_PACKAGE_TYPE_SYSTEM_BEGIN:
560 case EFI_HII_PACKAGE_TYPE_SYSTEM_END:
561 default:
562 break;
563 }
564
565 /* TODO: already removed some packages */
566 if (ret != EFI_SUCCESS)
567 return EFI_EXIT(ret);
568
569 package = ((void *)package)
570 + efi_hii_package_len(package);
571 }
572out:
573 ret = add_packages(hii, package_list);
574
575 return EFI_EXIT(ret);
576}
577
578static efi_status_t EFIAPI
579list_package_lists(const struct efi_hii_database_protocol *this,
580 u8 package_type,
581 const efi_guid_t *package_guid,
582 efi_uintn_t *handle_buffer_length,
583 efi_hii_handle_t *handle)
584{
585 struct efi_hii_packagelist *hii =
586 (struct efi_hii_packagelist *)handle;
587 int package_cnt, package_max;
Heinrich Schuchardtbd425182019-06-17 20:46:29 +0200588 efi_status_t ret = EFI_NOT_FOUND;
Leif Lindholm5aab1b82019-01-21 12:12:57 +0900589
Heinrich Schuchardt282249d2022-01-16 14:15:31 +0100590 EFI_ENTRY("%p, %u, %pUs, %p, %p", this, package_type, package_guid,
Leif Lindholm5aab1b82019-01-21 12:12:57 +0900591 handle_buffer_length, handle);
592
593 if (!handle_buffer_length ||
Heinrich Schuchardtbd425182019-06-17 20:46:29 +0200594 (*handle_buffer_length && !handle)) {
595 ret = EFI_INVALID_PARAMETER;
596 goto out;
597 }
Leif Lindholm5aab1b82019-01-21 12:12:57 +0900598
599 if ((package_type != EFI_HII_PACKAGE_TYPE_GUID && package_guid) ||
Heinrich Schuchardtbd425182019-06-17 20:46:29 +0200600 (package_type == EFI_HII_PACKAGE_TYPE_GUID && !package_guid)) {
601 ret = EFI_INVALID_PARAMETER;
602 goto out;
603 }
Leif Lindholm5aab1b82019-01-21 12:12:57 +0900604
Heinrich Schuchardt282249d2022-01-16 14:15:31 +0100605 EFI_PRINT("package type=%x, guid=%pUs, length=%zu\n", (int)package_type,
Heinrich Schuchardt4b6f19f2019-01-23 22:55:36 +0100606 package_guid, *handle_buffer_length);
Leif Lindholm5aab1b82019-01-21 12:12:57 +0900607
608 package_cnt = 0;
609 package_max = *handle_buffer_length / sizeof(*handle);
610 list_for_each_entry(hii, &efi_package_lists, link) {
611 switch (package_type) {
612 case EFI_HII_PACKAGE_TYPE_ALL:
613 break;
614 case EFI_HII_PACKAGE_TYPE_GUID:
AKASHI Takahiro0fa82b92019-01-21 12:12:58 +0900615 if (!list_empty(&hii->guid_list))
616 break;
Leif Lindholm5aab1b82019-01-21 12:12:57 +0900617 continue;
Leif Lindholm5aab1b82019-01-21 12:12:57 +0900618 case EFI_HII_PACKAGE_STRINGS:
619 if (!list_empty(&hii->string_tables))
620 break;
621 continue;
Leif Lindholm5aab1b82019-01-21 12:12:57 +0900622 case EFI_HII_PACKAGE_KEYBOARD_LAYOUT:
AKASHI Takahiro976e9312019-01-21 12:12:59 +0900623 if (!list_empty(&hii->keyboard_packages))
624 break;
Leif Lindholm5aab1b82019-01-21 12:12:57 +0900625 continue;
Leif Lindholm5aab1b82019-01-21 12:12:57 +0900626 default:
627 continue;
628 }
629
630 package_cnt++;
Heinrich Schuchardtbd425182019-06-17 20:46:29 +0200631 if (package_cnt <= package_max) {
Leif Lindholm5aab1b82019-01-21 12:12:57 +0900632 *handle++ = hii;
Heinrich Schuchardtbd425182019-06-17 20:46:29 +0200633 ret = EFI_SUCCESS;
634 } else {
Leif Lindholm5aab1b82019-01-21 12:12:57 +0900635 ret = EFI_BUFFER_TOO_SMALL;
Heinrich Schuchardtbd425182019-06-17 20:46:29 +0200636 }
Leif Lindholm5aab1b82019-01-21 12:12:57 +0900637 }
638 *handle_buffer_length = package_cnt * sizeof(*handle);
Heinrich Schuchardtbd425182019-06-17 20:46:29 +0200639out:
Leif Lindholm5aab1b82019-01-21 12:12:57 +0900640 return EFI_EXIT(ret);
641}
642
643static efi_status_t EFIAPI
644export_package_lists(const struct efi_hii_database_protocol *this,
645 efi_hii_handle_t handle,
646 efi_uintn_t *buffer_size,
647 struct efi_hii_package_list_header *buffer)
648{
649 EFI_ENTRY("%p, %p, %p, %p", this, handle, buffer_size, buffer);
650
651 if (!buffer_size || !buffer)
652 return EFI_EXIT(EFI_INVALID_PARAMETER);
653
654 return EFI_EXIT(EFI_NOT_FOUND);
655}
656
657static efi_status_t EFIAPI
658register_package_notify(const struct efi_hii_database_protocol *this,
659 u8 package_type,
660 const efi_guid_t *package_guid,
661 const void *package_notify_fn,
662 efi_uintn_t notify_type,
663 efi_handle_t *notify_handle)
664{
Heinrich Schuchardt282249d2022-01-16 14:15:31 +0100665 EFI_ENTRY("%p, %u, %pUs, %p, %zu, %p", this, package_type,
Leif Lindholm5aab1b82019-01-21 12:12:57 +0900666 package_guid, package_notify_fn, notify_type,
667 notify_handle);
668
669 if (!notify_handle)
670 return EFI_EXIT(EFI_INVALID_PARAMETER);
671
672 if ((package_type != EFI_HII_PACKAGE_TYPE_GUID && package_guid) ||
673 (package_type == EFI_HII_PACKAGE_TYPE_GUID && !package_guid))
674 return EFI_EXIT(EFI_INVALID_PARAMETER);
675
676 return EFI_EXIT(EFI_OUT_OF_RESOURCES);
677}
678
679static efi_status_t EFIAPI
680unregister_package_notify(const struct efi_hii_database_protocol *this,
681 efi_handle_t notification_handle)
682{
683 EFI_ENTRY("%p, %p", this, notification_handle);
684
685 return EFI_EXIT(EFI_NOT_FOUND);
686}
687
688static efi_status_t EFIAPI
689find_keyboard_layouts(const struct efi_hii_database_protocol *this,
690 u16 *key_guid_buffer_length,
691 efi_guid_t *key_guid_buffer)
692{
AKASHI Takahiro976e9312019-01-21 12:12:59 +0900693 struct efi_keyboard_layout_data *layout_data;
694 int package_cnt, package_max;
695 efi_status_t ret = EFI_SUCCESS;
696
Leif Lindholm5aab1b82019-01-21 12:12:57 +0900697 EFI_ENTRY("%p, %p, %p", this, key_guid_buffer_length, key_guid_buffer);
698
AKASHI Takahiro976e9312019-01-21 12:12:59 +0900699 if (!key_guid_buffer_length ||
700 (*key_guid_buffer_length && !key_guid_buffer))
701 return EFI_EXIT(EFI_INVALID_PARAMETER);
702
703 package_cnt = 0;
704 package_max = *key_guid_buffer_length / sizeof(*key_guid_buffer);
705 list_for_each_entry(layout_data, &efi_keyboard_layout_list, link_sys) {
706 package_cnt++;
707 if (package_cnt <= package_max)
708 memcpy(key_guid_buffer++,
709 &layout_data->keyboard_layout.guid,
710 sizeof(*key_guid_buffer));
711 else
712 ret = EFI_BUFFER_TOO_SMALL;
713 }
714 *key_guid_buffer_length = package_cnt * sizeof(*key_guid_buffer);
715
716 return EFI_EXIT(ret);
Leif Lindholm5aab1b82019-01-21 12:12:57 +0900717}
718
719static efi_status_t EFIAPI
720get_keyboard_layout(const struct efi_hii_database_protocol *this,
721 efi_guid_t *key_guid,
722 u16 *keyboard_layout_length,
723 struct efi_hii_keyboard_layout *keyboard_layout)
724{
AKASHI Takahiro976e9312019-01-21 12:12:59 +0900725 struct efi_keyboard_layout_data *layout_data;
726 u16 layout_length;
727
Heinrich Schuchardt282249d2022-01-16 14:15:31 +0100728 EFI_ENTRY("%p, %pUs, %p, %p", this, key_guid, keyboard_layout_length,
Leif Lindholm5aab1b82019-01-21 12:12:57 +0900729 keyboard_layout);
730
AKASHI Takahiro976e9312019-01-21 12:12:59 +0900731 if (!keyboard_layout_length ||
732 (*keyboard_layout_length && !keyboard_layout))
733 return EFI_EXIT(EFI_INVALID_PARAMETER);
734
735 /* TODO: no notion of current keyboard layout */
736 if (!key_guid)
737 return EFI_EXIT(EFI_INVALID_PARAMETER);
738
739 list_for_each_entry(layout_data, &efi_keyboard_layout_list, link_sys) {
740 if (!guidcmp(&layout_data->keyboard_layout.guid, key_guid))
741 goto found;
742 }
743
Leif Lindholm5aab1b82019-01-21 12:12:57 +0900744 return EFI_EXIT(EFI_NOT_FOUND);
AKASHI Takahiro976e9312019-01-21 12:12:59 +0900745
746found:
747 layout_length =
748 get_unaligned_le16(&layout_data->keyboard_layout.layout_length);
749 if (*keyboard_layout_length < layout_length) {
750 *keyboard_layout_length = layout_length;
751 return EFI_EXIT(EFI_BUFFER_TOO_SMALL);
752 }
753
754 memcpy(keyboard_layout, &layout_data->keyboard_layout, layout_length);
755
756 return EFI_EXIT(EFI_SUCCESS);
Leif Lindholm5aab1b82019-01-21 12:12:57 +0900757}
758
759static efi_status_t EFIAPI
760set_keyboard_layout(const struct efi_hii_database_protocol *this,
761 efi_guid_t *key_guid)
762{
Heinrich Schuchardt282249d2022-01-16 14:15:31 +0100763 EFI_ENTRY("%p, %pUs", this, key_guid);
Leif Lindholm5aab1b82019-01-21 12:12:57 +0900764
Vincent Stehlé00a3dfd2023-01-06 10:46:40 +0100765 if (!key_guid)
766 return EFI_EXIT(EFI_INVALID_PARAMETER);
767
Leif Lindholm5aab1b82019-01-21 12:12:57 +0900768 return EFI_EXIT(EFI_NOT_FOUND);
769}
770
771static efi_status_t EFIAPI
772get_package_list_handle(const struct efi_hii_database_protocol *this,
773 efi_hii_handle_t package_list_handle,
774 efi_handle_t *driver_handle)
775{
776 struct efi_hii_packagelist *hii;
777
778 EFI_ENTRY("%p, %p, %p", this, package_list_handle, driver_handle);
779
780 if (!driver_handle)
781 return EFI_EXIT(EFI_INVALID_PARAMETER);
782
783 list_for_each_entry(hii, &efi_package_lists, link) {
784 if (hii == package_list_handle) {
785 *driver_handle = hii->driver_handle;
786 return EFI_EXIT(EFI_SUCCESS);
787 }
788 }
789
Vincent Stehléd6f8e7a2022-12-13 22:39:09 +0100790 return EFI_EXIT(EFI_INVALID_PARAMETER);
Leif Lindholm5aab1b82019-01-21 12:12:57 +0900791}
792
793const struct efi_hii_database_protocol efi_hii_database = {
794 .new_package_list = new_package_list,
795 .remove_package_list = remove_package_list,
796 .update_package_list = update_package_list,
797 .list_package_lists = list_package_lists,
798 .export_package_lists = export_package_lists,
799 .register_package_notify = register_package_notify,
800 .unregister_package_notify = unregister_package_notify,
801 .find_keyboard_layouts = find_keyboard_layouts,
802 .get_keyboard_layout = get_keyboard_layout,
803 .set_keyboard_layout = set_keyboard_layout,
804 .get_package_list_handle = get_package_list_handle
805};
806
807/*
808 * EFI_HII_STRING_PROTOCOL
809 */
810
811static bool language_match(char *language, char *languages)
812{
813 size_t n;
814
815 n = strlen(language);
816 /* match primary language? */
817 if (!strncasecmp(language, languages, n) &&
818 (languages[n] == ';' || languages[n] == '\0'))
819 return true;
820
821 return false;
822}
823
824static efi_status_t EFIAPI
825new_string(const struct efi_hii_string_protocol *this,
826 efi_hii_handle_t package_list,
827 efi_string_id_t *string_id,
828 const u8 *language,
829 const u16 *language_name,
830 const efi_string_t string,
831 const struct efi_font_info *string_font_info)
832{
833 struct efi_hii_packagelist *hii = package_list;
834 struct efi_string_table *stbl;
835
836 EFI_ENTRY("%p, %p, %p, \"%s\", %p, \"%ls\", %p", this, package_list,
837 string_id, language, language_name, string,
838 string_font_info);
839
840 if (!package_list || !efi_hii_packagelist_exists(package_list))
841 return EFI_EXIT(EFI_NOT_FOUND);
842
843 if (!string_id || !language || !string)
844 return EFI_EXIT(EFI_INVALID_PARAMETER);
845
846 list_for_each_entry(stbl, &hii->string_tables, link) {
847 if (language_match((char *)language, stbl->language)) {
848 efi_string_id_t new_id;
849 void *buf;
850 efi_string_t str;
851
852 new_id = ++hii->max_string_id;
853 if (stbl->nstrings < new_id) {
854 buf = realloc(stbl->strings,
855 sizeof(stbl->strings[0])
856 * new_id);
857 if (!buf)
858 return EFI_EXIT(EFI_OUT_OF_RESOURCES);
859
860 memset(&stbl->strings[stbl->nstrings], 0,
861 (new_id - stbl->nstrings)
862 * sizeof(stbl->strings[0]));
863 stbl->strings = buf;
864 stbl->nstrings = new_id;
865 }
866
867 str = u16_strdup(string);
868 if (!str)
869 return EFI_EXIT(EFI_OUT_OF_RESOURCES);
870
871 stbl->strings[new_id - 1].string = str;
872 *string_id = new_id;
873
874 return EFI_EXIT(EFI_SUCCESS);
875 }
876 }
877
878 return EFI_EXIT(EFI_NOT_FOUND);
879}
880
881static efi_status_t EFIAPI
882get_string(const struct efi_hii_string_protocol *this,
883 const u8 *language,
884 efi_hii_handle_t package_list,
885 efi_string_id_t string_id,
886 efi_string_t string,
887 efi_uintn_t *string_size,
888 struct efi_font_info **string_font_info)
889{
890 struct efi_hii_packagelist *hii = package_list;
891 struct efi_string_table *stbl;
892
893 EFI_ENTRY("%p, \"%s\", %p, %u, %p, %p, %p", this, language,
894 package_list, string_id, string, string_size,
895 string_font_info);
896
897 if (!package_list || !efi_hii_packagelist_exists(package_list))
898 return EFI_EXIT(EFI_NOT_FOUND);
899
900 list_for_each_entry(stbl, &hii->string_tables, link) {
901 if (language_match((char *)language, stbl->language)) {
902 efi_string_t str;
903 size_t len;
904
905 if (stbl->nstrings < string_id)
906 return EFI_EXIT(EFI_NOT_FOUND);
907
908 str = stbl->strings[string_id - 1].string;
909 if (str) {
Heinrich Schuchardt28b06b22022-04-02 11:47:00 +0200910 len = u16_strsize(str);
Leif Lindholm5aab1b82019-01-21 12:12:57 +0900911 if (*string_size < len) {
912 *string_size = len;
913
914 return EFI_EXIT(EFI_BUFFER_TOO_SMALL);
915 }
916 memcpy(string, str, len);
917 *string_size = len;
918 } else {
919 return EFI_EXIT(EFI_NOT_FOUND);
920 }
921
922 return EFI_EXIT(EFI_SUCCESS);
923 }
924 }
925
926 return EFI_EXIT(EFI_NOT_FOUND);
927}
928
929static efi_status_t EFIAPI
930set_string(const struct efi_hii_string_protocol *this,
931 efi_hii_handle_t package_list,
932 efi_string_id_t string_id,
933 const u8 *language,
934 const efi_string_t string,
935 const struct efi_font_info *string_font_info)
936{
937 struct efi_hii_packagelist *hii = package_list;
938 struct efi_string_table *stbl;
939
940 EFI_ENTRY("%p, %p, %u, \"%s\", \"%ls\", %p", this, package_list,
941 string_id, language, string, string_font_info);
942
943 if (!package_list || !efi_hii_packagelist_exists(package_list))
944 return EFI_EXIT(EFI_NOT_FOUND);
945
946 if (string_id > hii->max_string_id)
947 return EFI_EXIT(EFI_NOT_FOUND);
948
949 if (!string || !language)
950 return EFI_EXIT(EFI_INVALID_PARAMETER);
951
952 list_for_each_entry(stbl, &hii->string_tables, link) {
953 if (language_match((char *)language, stbl->language)) {
954 efi_string_t str;
955
956 if (hii->max_string_id < string_id)
957 return EFI_EXIT(EFI_NOT_FOUND);
958
959 if (stbl->nstrings < string_id) {
960 void *buf;
961
962 buf = realloc(stbl->strings,
963 string_id
964 * sizeof(stbl->strings[0]));
965 if (!buf)
966 return EFI_EXIT(EFI_OUT_OF_RESOURCES);
967
968 memset(&stbl->strings[string_id - 1], 0,
969 (string_id - stbl->nstrings)
970 * sizeof(stbl->strings[0]));
971 stbl->strings = buf;
972 }
973
974 str = u16_strdup(string);
975 if (!str)
976 return EFI_EXIT(EFI_OUT_OF_RESOURCES);
977
978 free(stbl->strings[string_id - 1].string);
979 stbl->strings[string_id - 1].string = str;
980
981 return EFI_EXIT(EFI_SUCCESS);
982 }
983 }
984
985 return EFI_EXIT(EFI_NOT_FOUND);
986}
987
988static efi_status_t EFIAPI
989get_languages(const struct efi_hii_string_protocol *this,
990 efi_hii_handle_t package_list,
991 u8 *languages,
992 efi_uintn_t *languages_size)
993{
994 struct efi_hii_packagelist *hii = package_list;
995 struct efi_string_table *stbl;
996 size_t len = 0;
997 char *p;
998
999 EFI_ENTRY("%p, %p, %p, %p", this, package_list, languages,
1000 languages_size);
1001
1002 if (!package_list || !efi_hii_packagelist_exists(package_list))
1003 return EFI_EXIT(EFI_NOT_FOUND);
1004
1005 if (!languages_size ||
1006 (*languages_size && !languages))
1007 return EFI_EXIT(EFI_INVALID_PARAMETER);
1008
1009 /* figure out required size: */
1010 list_for_each_entry(stbl, &hii->string_tables, link) {
1011 len += strlen((char *)stbl->language) + 1;
1012 }
1013
1014 if (*languages_size < len) {
1015 *languages_size = len;
1016
1017 return EFI_EXIT(EFI_BUFFER_TOO_SMALL);
1018 }
1019
1020 p = (char *)languages;
1021 list_for_each_entry(stbl, &hii->string_tables, link) {
1022 if (p != (char *)languages)
1023 *p++ = ';';
1024 strcpy(p, stbl->language);
1025 p += strlen((char *)stbl->language);
1026 }
1027 *p = '\0';
1028
Heinrich Schuchardt4b6f19f2019-01-23 22:55:36 +01001029 EFI_PRINT("languages: %s\n", languages);
Leif Lindholm5aab1b82019-01-21 12:12:57 +09001030
1031 return EFI_EXIT(EFI_SUCCESS);
1032}
1033
1034static efi_status_t EFIAPI
1035get_secondary_languages(const struct efi_hii_string_protocol *this,
1036 efi_hii_handle_t package_list,
1037 const u8 *primary_language,
1038 u8 *secondary_languages,
1039 efi_uintn_t *secondary_languages_size)
1040{
1041 struct efi_hii_packagelist *hii = package_list;
1042 struct efi_string_table *stbl;
1043 bool found = false;
1044
1045 EFI_ENTRY("%p, %p, \"%s\", %p, %p", this, package_list,
1046 primary_language, secondary_languages,
1047 secondary_languages_size);
1048
1049 if (!package_list || !efi_hii_packagelist_exists(package_list))
1050 return EFI_EXIT(EFI_NOT_FOUND);
1051
1052 if (!secondary_languages_size ||
1053 (*secondary_languages_size && !secondary_languages))
1054 return EFI_EXIT(EFI_INVALID_PARAMETER);
1055
1056 list_for_each_entry(stbl, &hii->string_tables, link) {
1057 if (language_match((char *)primary_language, stbl->language)) {
1058 found = true;
1059 break;
1060 }
1061 }
1062 if (!found)
1063 return EFI_EXIT(EFI_INVALID_LANGUAGE);
1064
1065 /*
1066 * TODO: What is secondary language?
1067 * *secondary_languages = '\0';
1068 * *secondary_languages_size = 0;
1069 */
1070
1071 return EFI_EXIT(EFI_NOT_FOUND);
1072}
1073
1074const struct efi_hii_string_protocol efi_hii_string = {
1075 .new_string = new_string,
1076 .get_string = get_string,
1077 .set_string = set_string,
1078 .get_languages = get_languages,
1079 .get_secondary_languages = get_secondary_languages
1080};