uboot/lib/efi_loader/efi_hii.c
<<
>>
Prefs
   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
  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);
  19static LIST_HEAD(efi_keyboard_layout_list);
  20
  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 */
  27        struct list_head guid_list;
  28        struct list_head keyboard_packages;
  29
  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
  86struct efi_guid_data {
  87        struct list_head link;
  88        struct efi_hii_guid_package package;
  89};
  90
  91struct 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
 102static 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
 135        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);
 142
 143        /* count # of string entries: */
 144        end = ((void *)strings_package)
 145                        + efi_hii_package_len(&strings_package->header);
 146        block = ((void *)strings_package)
 147                + get_unaligned_le32(&strings_package->string_info_offset);
 148
 149        while ((void *)block < end) {
 150                switch (block->block_type) {
 151                case EFI_HII_SIBT_STRING_UCS2: {
 152                        struct efi_hii_sibt_string_ucs2_block *ucs2;
 153
 154                        ucs2 = (void *)block;
 155                        nstrings++;
 156                        block = efi_hii_sibt_string_ucs2_block_next(ucs2);
 157                        break;
 158                }
 159                case EFI_HII_SIBT_END:
 160                        block = end;
 161                        break;
 162                default:
 163                        EFI_PRINT("unknown HII string block type: %02x\n",
 164                                  block->block_type);
 165                        return EFI_INVALID_PARAMETER;
 166                }
 167        }
 168
 169        stbl = calloc(sizeof(*stbl), 1);
 170        if (!stbl) {
 171                ret = EFI_OUT_OF_RESOURCES;
 172                goto error;
 173        }
 174        stbl->strings = calloc(sizeof(stbl->strings[0]), nstrings);
 175        if (!stbl->strings) {
 176                ret = EFI_OUT_OF_RESOURCES;
 177                goto error;
 178        }
 179        stbl->language_name =
 180                        get_unaligned_le16(&strings_package->language_name);
 181        stbl->language = strdup((char *)strings_package->language);
 182        if (!stbl->language) {
 183                ret = EFI_OUT_OF_RESOURCES;
 184                goto error;
 185        }
 186        stbl->nstrings = nstrings;
 187
 188        /* and now parse string entries and populate efi_string_table */
 189        block = ((void *)strings_package)
 190                + get_unaligned_le32(&strings_package->string_info_offset);
 191
 192        while ((void *)block < end) {
 193                switch (block->block_type) {
 194                case EFI_HII_SIBT_STRING_UCS2: {
 195                        struct efi_hii_sibt_string_ucs2_block *ucs2;
 196
 197                        ucs2 = (void *)block;
 198                        EFI_PRINT("%4u: \"%ls\"\n", idx + 1, ucs2->string_text);
 199                        stbl->strings[idx].string =
 200                                u16_strdup(ucs2->string_text);
 201                        if (!stbl->strings[idx].string) {
 202                                ret = EFI_OUT_OF_RESOURCES;
 203                                goto error;
 204                        }
 205                        idx++;
 206                        /* FIXME: accessing u16 * here */
 207                        block = efi_hii_sibt_string_ucs2_block_next(ucs2);
 208                        break;
 209                }
 210                case EFI_HII_SIBT_END:
 211                        goto out;
 212                default:
 213                        EFI_PRINT("unknown HII string block type: %02x\n",
 214                                  block->block_type);
 215                        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);
 230                while (idx > 0)
 231                        free(stbl->strings[--idx].string);
 232                free(stbl->strings);
 233        }
 234        free(stbl);
 235
 236        return ret;
 237}
 238
 239static 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
 268static 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
 340static struct efi_hii_packagelist *new_packagelist(void)
 341{
 342        struct efi_hii_packagelist *hii;
 343
 344        hii = malloc(sizeof(*hii));
 345        list_add_tail(&hii->link, &efi_package_lists);
 346        hii->max_string_id = 0;
 347        INIT_LIST_HEAD(&hii->string_tables);
 348        INIT_LIST_HEAD(&hii->guid_list);
 349        INIT_LIST_HEAD(&hii->keyboard_packages);
 350
 351        return hii;
 352}
 353
 354static void free_packagelist(struct efi_hii_packagelist *hii)
 355{
 356        remove_strings_package(hii);
 357        remove_guid_package(hii);
 358        remove_keyboard_package(hii);
 359
 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
 375        EFI_PRINT("package_list: %pUl (%u)\n", &package_list->package_list_guid,
 376                  get_unaligned_le32(&package_list->package_length));
 377
 378        package = ((void *)package_list) + sizeof(*package_list);
 379        while ((void *)package < end) {
 380                EFI_PRINT("package=%p, package type=%x, length=%u\n", package,
 381                          efi_hii_package_type(package),
 382                          efi_hii_package_len(package));
 383
 384                switch (efi_hii_package_type(package)) {
 385                case EFI_HII_PACKAGE_TYPE_GUID:
 386                        ret = add_guid_package(hii,
 387                                (struct efi_hii_guid_package *)package);
 388                        break;
 389                case EFI_HII_PACKAGE_FORMS:
 390                        EFI_PRINT("Form package not supported\n");
 391                        ret = EFI_INVALID_PARAMETER;
 392                        break;
 393                case EFI_HII_PACKAGE_STRINGS:
 394                        ret = add_strings_package(hii,
 395                                (struct efi_hii_strings_package *)package);
 396                        break;
 397                case EFI_HII_PACKAGE_FONTS:
 398                        EFI_PRINT("Font package not supported\n");
 399                        ret = EFI_INVALID_PARAMETER;
 400                        break;
 401                case EFI_HII_PACKAGE_IMAGES:
 402                        EFI_PRINT("Image package not supported\n");
 403                        ret = EFI_INVALID_PARAMETER;
 404                        break;
 405                case EFI_HII_PACKAGE_SIMPLE_FONTS:
 406                        EFI_PRINT("Simple font package not supported\n");
 407                        ret = EFI_INVALID_PARAMETER;
 408                        break;
 409                case EFI_HII_PACKAGE_DEVICE_PATH:
 410                        EFI_PRINT("Device path package not supported\n");
 411                        ret = EFI_INVALID_PARAMETER;
 412                        break;
 413                case EFI_HII_PACKAGE_KEYBOARD_LAYOUT:
 414                        ret = add_keyboard_package(hii,
 415                                (struct efi_hii_keyboard_package *)package);
 416                        break;
 417                case EFI_HII_PACKAGE_ANIMATIONS:
 418                        EFI_PRINT("Animation package not supported\n");
 419                        ret = EFI_INVALID_PARAMETER;
 420                        break;
 421                case EFI_HII_PACKAGE_END:
 422                        goto out;
 423                case EFI_HII_PACKAGE_TYPE_SYSTEM_BEGIN:
 424                case EFI_HII_PACKAGE_TYPE_SYSTEM_END:
 425                default:
 426                        break;
 427                }
 428
 429                if (ret != EFI_SUCCESS)
 430                        return ret;
 431
 432                package = (void *)package + efi_hii_package_len(package);
 433        }
 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;
 468        *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
 507        EFI_PRINT("package_list: %pUl (%u)\n", &package_list->package_list_guid,
 508                  get_unaligned_le32(&package_list->package_length));
 509
 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) {
 515                EFI_PRINT("package=%p, package type=%x, length=%u\n", package,
 516                          efi_hii_package_type(package),
 517                          efi_hii_package_len(package));
 518
 519                switch (efi_hii_package_type(package)) {
 520                case EFI_HII_PACKAGE_TYPE_GUID:
 521                        remove_guid_package(hii);
 522                        break;
 523                case EFI_HII_PACKAGE_FORMS:
 524                        EFI_PRINT("Form package not supported\n");
 525                        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:
 531                        EFI_PRINT("Font package not supported\n");
 532                        ret = EFI_INVALID_PARAMETER;
 533                        break;
 534                case EFI_HII_PACKAGE_IMAGES:
 535                        EFI_PRINT("Image package not supported\n");
 536                        ret = EFI_INVALID_PARAMETER;
 537                        break;
 538                case EFI_HII_PACKAGE_SIMPLE_FONTS:
 539                        EFI_PRINT("Simple font package not supported\n");
 540                        ret = EFI_INVALID_PARAMETER;
 541                        break;
 542                case EFI_HII_PACKAGE_DEVICE_PATH:
 543                        EFI_PRINT("Device path package not supported\n");
 544                        ret = EFI_INVALID_PARAMETER;
 545                        break;
 546                case EFI_HII_PACKAGE_KEYBOARD_LAYOUT:
 547                        remove_keyboard_package(hii);
 548                        break;
 549                case EFI_HII_PACKAGE_ANIMATIONS:
 550                        EFI_PRINT("Animation package not supported\n");
 551                        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;
 584        efi_status_t ret = EFI_NOT_FOUND;
 585
 586        EFI_ENTRY("%p, %u, %pUl, %p, %p", this, package_type, package_guid,
 587                  handle_buffer_length, handle);
 588
 589        if (!handle_buffer_length ||
 590            (*handle_buffer_length && !handle)) {
 591                ret = EFI_INVALID_PARAMETER;
 592                goto out;
 593        }
 594
 595        if ((package_type != EFI_HII_PACKAGE_TYPE_GUID && package_guid) ||
 596            (package_type == EFI_HII_PACKAGE_TYPE_GUID && !package_guid)) {
 597                ret = EFI_INVALID_PARAMETER;
 598                goto out;
 599        }
 600
 601        EFI_PRINT("package type=%x, guid=%pUl, length=%zu\n", (int)package_type,
 602                  package_guid, *handle_buffer_length);
 603
 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:
 611                        if (!list_empty(&hii->guid_list))
 612                                break;
 613                        continue;
 614                case EFI_HII_PACKAGE_STRINGS:
 615                        if (!list_empty(&hii->string_tables))
 616                                break;
 617                        continue;
 618                case EFI_HII_PACKAGE_KEYBOARD_LAYOUT:
 619                        if (!list_empty(&hii->keyboard_packages))
 620                                break;
 621                        continue;
 622                default:
 623                        continue;
 624                }
 625
 626                package_cnt++;
 627                if (package_cnt <= package_max) {
 628                        *handle++ = hii;
 629                        ret = EFI_SUCCESS;
 630                } else {
 631                        ret = EFI_BUFFER_TOO_SMALL;
 632                }
 633        }
 634        *handle_buffer_length = package_cnt * sizeof(*handle);
 635out:
 636        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{
 661        EFI_ENTRY("%p, %u, %pUl, %p, %zu, %p", this, package_type,
 662                  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{
 689        struct efi_keyboard_layout_data *layout_data;
 690        int package_cnt, package_max;
 691        efi_status_t ret = EFI_SUCCESS;
 692
 693        EFI_ENTRY("%p, %p, %p", this, key_guid_buffer_length, key_guid_buffer);
 694
 695        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);
 713}
 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{
 721        struct efi_keyboard_layout_data *layout_data;
 722        u16 layout_length;
 723
 724        EFI_ENTRY("%p, %pUl, %p, %p", this, key_guid, keyboard_layout_length,
 725                  keyboard_layout);
 726
 727        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
 740        return EFI_EXIT(EFI_NOT_FOUND);
 741
 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);
 753}
 754
 755static efi_status_t EFIAPI
 756set_keyboard_layout(const struct efi_hii_database_protocol *this,
 757                    efi_guid_t *key_guid)
 758{
 759        EFI_ENTRY("%p, %pUl", this, key_guid);
 760
 761        return EFI_EXIT(EFI_NOT_FOUND);
 762}
 763
 764static efi_status_t EFIAPI
 765get_package_list_handle(const struct efi_hii_database_protocol *this,
 766                        efi_hii_handle_t package_list_handle,
 767                        efi_handle_t *driver_handle)
 768{
 769        struct efi_hii_packagelist *hii;
 770
 771        EFI_ENTRY("%p, %p, %p", this, package_list_handle, driver_handle);
 772
 773        if (!driver_handle)
 774                return EFI_EXIT(EFI_INVALID_PARAMETER);
 775
 776        list_for_each_entry(hii, &efi_package_lists, link) {
 777                if (hii == package_list_handle) {
 778                        *driver_handle = hii->driver_handle;
 779                        return EFI_EXIT(EFI_SUCCESS);
 780                }
 781        }
 782
 783        return EFI_EXIT(EFI_NOT_FOUND);
 784}
 785
 786const struct efi_hii_database_protocol efi_hii_database = {
 787        .new_package_list = new_package_list,
 788        .remove_package_list = remove_package_list,
 789        .update_package_list = update_package_list,
 790        .list_package_lists = list_package_lists,
 791        .export_package_lists = export_package_lists,
 792        .register_package_notify = register_package_notify,
 793        .unregister_package_notify = unregister_package_notify,
 794        .find_keyboard_layouts = find_keyboard_layouts,
 795        .get_keyboard_layout = get_keyboard_layout,
 796        .set_keyboard_layout = set_keyboard_layout,
 797        .get_package_list_handle = get_package_list_handle
 798};
 799
 800/*
 801 * EFI_HII_STRING_PROTOCOL
 802 */
 803
 804static bool language_match(char *language, char *languages)
 805{
 806        size_t n;
 807
 808        n = strlen(language);
 809        /* match primary language? */
 810        if (!strncasecmp(language, languages, n) &&
 811            (languages[n] == ';' || languages[n] == '\0'))
 812                return true;
 813
 814        return false;
 815}
 816
 817static efi_status_t EFIAPI
 818new_string(const struct efi_hii_string_protocol *this,
 819           efi_hii_handle_t package_list,
 820           efi_string_id_t *string_id,
 821           const u8 *language,
 822           const u16 *language_name,
 823           const efi_string_t string,
 824           const struct efi_font_info *string_font_info)
 825{
 826        struct efi_hii_packagelist *hii = package_list;
 827        struct efi_string_table *stbl;
 828
 829        EFI_ENTRY("%p, %p, %p, \"%s\", %p, \"%ls\", %p", this, package_list,
 830                  string_id, language, language_name, string,
 831                  string_font_info);
 832
 833        if (!package_list || !efi_hii_packagelist_exists(package_list))
 834                return EFI_EXIT(EFI_NOT_FOUND);
 835
 836        if (!string_id || !language || !string)
 837                return EFI_EXIT(EFI_INVALID_PARAMETER);
 838
 839        list_for_each_entry(stbl, &hii->string_tables, link) {
 840                if (language_match((char *)language, stbl->language)) {
 841                        efi_string_id_t new_id;
 842                        void *buf;
 843                        efi_string_t str;
 844
 845                        new_id = ++hii->max_string_id;
 846                        if (stbl->nstrings < new_id) {
 847                                buf = realloc(stbl->strings,
 848                                              sizeof(stbl->strings[0])
 849                                                * new_id);
 850                                if (!buf)
 851                                        return EFI_EXIT(EFI_OUT_OF_RESOURCES);
 852
 853                                memset(&stbl->strings[stbl->nstrings], 0,
 854                                       (new_id - stbl->nstrings)
 855                                         * sizeof(stbl->strings[0]));
 856                                stbl->strings = buf;
 857                                stbl->nstrings = new_id;
 858                        }
 859
 860                        str = u16_strdup(string);
 861                        if (!str)
 862                                return EFI_EXIT(EFI_OUT_OF_RESOURCES);
 863
 864                        stbl->strings[new_id - 1].string = str;
 865                        *string_id = new_id;
 866
 867                        return EFI_EXIT(EFI_SUCCESS);
 868                }
 869        }
 870
 871        return EFI_EXIT(EFI_NOT_FOUND);
 872}
 873
 874static efi_status_t EFIAPI
 875get_string(const struct efi_hii_string_protocol *this,
 876           const u8 *language,
 877           efi_hii_handle_t package_list,
 878           efi_string_id_t string_id,
 879           efi_string_t string,
 880           efi_uintn_t *string_size,
 881           struct efi_font_info **string_font_info)
 882{
 883        struct efi_hii_packagelist *hii = package_list;
 884        struct efi_string_table *stbl;
 885
 886        EFI_ENTRY("%p, \"%s\", %p, %u, %p, %p, %p", this, language,
 887                  package_list, string_id, string, string_size,
 888                  string_font_info);
 889
 890        if (!package_list || !efi_hii_packagelist_exists(package_list))
 891                return EFI_EXIT(EFI_NOT_FOUND);
 892
 893        list_for_each_entry(stbl, &hii->string_tables, link) {
 894                if (language_match((char *)language, stbl->language)) {
 895                        efi_string_t str;
 896                        size_t len;
 897
 898                        if (stbl->nstrings < string_id)
 899                                return EFI_EXIT(EFI_NOT_FOUND);
 900
 901                        str = stbl->strings[string_id - 1].string;
 902                        if (str) {
 903                                len = (u16_strlen(str) + 1) * sizeof(u16);
 904                                if (*string_size < len) {
 905                                        *string_size = len;
 906
 907                                        return EFI_EXIT(EFI_BUFFER_TOO_SMALL);
 908                                }
 909                                memcpy(string, str, len);
 910                                *string_size = len;
 911                        } else {
 912                                return EFI_EXIT(EFI_NOT_FOUND);
 913                        }
 914
 915                        return EFI_EXIT(EFI_SUCCESS);
 916                }
 917        }
 918
 919        return EFI_EXIT(EFI_NOT_FOUND);
 920}
 921
 922static efi_status_t EFIAPI
 923set_string(const struct efi_hii_string_protocol *this,
 924           efi_hii_handle_t package_list,
 925           efi_string_id_t string_id,
 926           const u8 *language,
 927           const efi_string_t string,
 928           const struct efi_font_info *string_font_info)
 929{
 930        struct efi_hii_packagelist *hii = package_list;
 931        struct efi_string_table *stbl;
 932
 933        EFI_ENTRY("%p, %p, %u, \"%s\", \"%ls\", %p", this, package_list,
 934                  string_id, language, string, string_font_info);
 935
 936        if (!package_list || !efi_hii_packagelist_exists(package_list))
 937                return EFI_EXIT(EFI_NOT_FOUND);
 938
 939        if (string_id > hii->max_string_id)
 940                return EFI_EXIT(EFI_NOT_FOUND);
 941
 942        if (!string || !language)
 943                return EFI_EXIT(EFI_INVALID_PARAMETER);
 944
 945        list_for_each_entry(stbl, &hii->string_tables, link) {
 946                if (language_match((char *)language, stbl->language)) {
 947                        efi_string_t str;
 948
 949                        if (hii->max_string_id < string_id)
 950                                return EFI_EXIT(EFI_NOT_FOUND);
 951
 952                        if (stbl->nstrings < string_id) {
 953                                void *buf;
 954
 955                                buf = realloc(stbl->strings,
 956                                              string_id
 957                                                * sizeof(stbl->strings[0]));
 958                                if (!buf)
 959                                        return EFI_EXIT(EFI_OUT_OF_RESOURCES);
 960
 961                                memset(&stbl->strings[string_id - 1], 0,
 962                                       (string_id - stbl->nstrings)
 963                                         * sizeof(stbl->strings[0]));
 964                                stbl->strings = buf;
 965                        }
 966
 967                        str = u16_strdup(string);
 968                        if (!str)
 969                                return EFI_EXIT(EFI_OUT_OF_RESOURCES);
 970
 971                        free(stbl->strings[string_id - 1].string);
 972                        stbl->strings[string_id - 1].string = str;
 973
 974                        return EFI_EXIT(EFI_SUCCESS);
 975                }
 976        }
 977
 978        return EFI_EXIT(EFI_NOT_FOUND);
 979}
 980
 981static efi_status_t EFIAPI
 982get_languages(const struct efi_hii_string_protocol *this,
 983              efi_hii_handle_t package_list,
 984              u8 *languages,
 985              efi_uintn_t *languages_size)
 986{
 987        struct efi_hii_packagelist *hii = package_list;
 988        struct efi_string_table *stbl;
 989        size_t len = 0;
 990        char *p;
 991
 992        EFI_ENTRY("%p, %p, %p, %p", this, package_list, languages,
 993                  languages_size);
 994
 995        if (!package_list || !efi_hii_packagelist_exists(package_list))
 996                return EFI_EXIT(EFI_NOT_FOUND);
 997
 998        if (!languages_size ||
 999            (*languages_size && !languages))
1000                return EFI_EXIT(EFI_INVALID_PARAMETER);
1001
1002        /* figure out required size: */
1003        list_for_each_entry(stbl, &hii->string_tables, link) {
1004                len += strlen((char *)stbl->language) + 1;
1005        }
1006
1007        if (*languages_size < len) {
1008                *languages_size = len;
1009
1010                return EFI_EXIT(EFI_BUFFER_TOO_SMALL);
1011        }
1012
1013        p = (char *)languages;
1014        list_for_each_entry(stbl, &hii->string_tables, link) {
1015                if (p != (char *)languages)
1016                        *p++ = ';';
1017                strcpy(p, stbl->language);
1018                p += strlen((char *)stbl->language);
1019        }
1020        *p = '\0';
1021
1022        EFI_PRINT("languages: %s\n", languages);
1023
1024        return EFI_EXIT(EFI_SUCCESS);
1025}
1026
1027static efi_status_t EFIAPI
1028get_secondary_languages(const struct efi_hii_string_protocol *this,
1029                        efi_hii_handle_t package_list,
1030                        const u8 *primary_language,
1031                        u8 *secondary_languages,
1032                        efi_uintn_t *secondary_languages_size)
1033{
1034        struct efi_hii_packagelist *hii = package_list;
1035        struct efi_string_table *stbl;
1036        bool found = false;
1037
1038        EFI_ENTRY("%p, %p, \"%s\", %p, %p", this, package_list,
1039                  primary_language, secondary_languages,
1040                  secondary_languages_size);
1041
1042        if (!package_list || !efi_hii_packagelist_exists(package_list))
1043                return EFI_EXIT(EFI_NOT_FOUND);
1044
1045        if (!secondary_languages_size ||
1046            (*secondary_languages_size && !secondary_languages))
1047                return EFI_EXIT(EFI_INVALID_PARAMETER);
1048
1049        list_for_each_entry(stbl, &hii->string_tables, link) {
1050                if (language_match((char *)primary_language, stbl->language)) {
1051                        found = true;
1052                        break;
1053                }
1054        }
1055        if (!found)
1056                return EFI_EXIT(EFI_INVALID_LANGUAGE);
1057
1058        /*
1059         * TODO: What is secondary language?
1060         * *secondary_languages = '\0';
1061         * *secondary_languages_size = 0;
1062         */
1063
1064        return EFI_EXIT(EFI_NOT_FOUND);
1065}
1066
1067const struct efi_hii_string_protocol efi_hii_string = {
1068        .new_string = new_string,
1069        .get_string = get_string,
1070        .set_string = set_string,
1071        .get_languages = get_languages,
1072        .get_secondary_languages = get_secondary_languages
1073};
1074