uboot/lib/efi_loader/efi_device_path.c
<<
>>
Prefs
   1/*
   2 * EFI device path from u-boot device-model mapping
   3 *
   4 * (C) Copyright 2017 Rob Clark
   5 *
   6 * SPDX-License-Identifier:     GPL-2.0+
   7 */
   8
   9#include <common.h>
  10#include <blk.h>
  11#include <dm.h>
  12#include <usb.h>
  13#include <mmc.h>
  14#include <efi_loader.h>
  15#include <inttypes.h>
  16#include <part.h>
  17
  18/* template END node: */
  19static const struct efi_device_path END = {
  20        .type     = DEVICE_PATH_TYPE_END,
  21        .sub_type = DEVICE_PATH_SUB_TYPE_END,
  22        .length   = sizeof(END),
  23};
  24
  25#define U_BOOT_GUID \
  26        EFI_GUID(0xe61d73b9, 0xa384, 0x4acc, \
  27                 0xae, 0xab, 0x82, 0xe8, 0x28, 0xf3, 0x62, 0x8b)
  28
  29/* template ROOT node: */
  30static const struct efi_device_path_vendor ROOT = {
  31        .dp = {
  32                .type     = DEVICE_PATH_TYPE_HARDWARE_DEVICE,
  33                .sub_type = DEVICE_PATH_SUB_TYPE_VENDOR,
  34                .length   = sizeof(ROOT),
  35        },
  36        .guid = U_BOOT_GUID,
  37};
  38
  39#if defined(CONFIG_DM_MMC) && defined(CONFIG_MMC)
  40/*
  41 * Determine if an MMC device is an SD card.
  42 *
  43 * @desc        block device descriptor
  44 * @return      true if the device is an SD card
  45 */
  46static bool is_sd(struct blk_desc *desc)
  47{
  48        struct mmc *mmc = find_mmc_device(desc->devnum);
  49
  50        if (!mmc)
  51                return false;
  52
  53        return IS_SD(mmc) != 0U;
  54}
  55#endif
  56
  57static void *dp_alloc(size_t sz)
  58{
  59        void *buf;
  60
  61        if (efi_allocate_pool(EFI_ALLOCATE_ANY_PAGES, sz, &buf) != EFI_SUCCESS)
  62                return NULL;
  63
  64        return buf;
  65}
  66
  67/*
  68 * Iterate to next block in device-path, terminating (returning NULL)
  69 * at /End* node.
  70 */
  71struct efi_device_path *efi_dp_next(const struct efi_device_path *dp)
  72{
  73        if (dp == NULL)
  74                return NULL;
  75        if (dp->type == DEVICE_PATH_TYPE_END)
  76                return NULL;
  77        dp = ((void *)dp) + dp->length;
  78        if (dp->type == DEVICE_PATH_TYPE_END)
  79                return NULL;
  80        return (struct efi_device_path *)dp;
  81}
  82
  83/*
  84 * Compare two device-paths, stopping when the shorter of the two hits
  85 * an End* node.  This is useful to, for example, compare a device-path
  86 * representing a device with one representing a file on the device, or
  87 * a device with a parent device.
  88 */
  89int efi_dp_match(const struct efi_device_path *a,
  90                 const struct efi_device_path *b)
  91{
  92        while (1) {
  93                int ret;
  94
  95                ret = memcmp(&a->length, &b->length, sizeof(a->length));
  96                if (ret)
  97                        return ret;
  98
  99                ret = memcmp(a, b, a->length);
 100                if (ret)
 101                        return ret;
 102
 103                a = efi_dp_next(a);
 104                b = efi_dp_next(b);
 105
 106                if (!a || !b)
 107                        return 0;
 108        }
 109}
 110
 111
 112/*
 113 * See UEFI spec (section 3.1.2, about short-form device-paths..
 114 * tl;dr: we can have a device-path that starts with a USB WWID
 115 * or USB Class node, and a few other cases which don't encode
 116 * the full device path with bus hierarchy:
 117 *
 118 *   - MESSAGING:USB_WWID
 119 *   - MESSAGING:USB_CLASS
 120 *   - MEDIA:FILE_PATH
 121 *   - MEDIA:HARD_DRIVE
 122 *   - MESSAGING:URI
 123 */
 124static struct efi_device_path *shorten_path(struct efi_device_path *dp)
 125{
 126        while (dp) {
 127                /*
 128                 * TODO: Add MESSAGING:USB_WWID and MESSAGING:URI..
 129                 * in practice fallback.efi just uses MEDIA:HARD_DRIVE
 130                 * so not sure when we would see these other cases.
 131                 */
 132                if (EFI_DP_TYPE(dp, MESSAGING_DEVICE, MSG_USB_CLASS) ||
 133                    EFI_DP_TYPE(dp, MEDIA_DEVICE, HARD_DRIVE_PATH) ||
 134                    EFI_DP_TYPE(dp, MEDIA_DEVICE, FILE_PATH))
 135                        return dp;
 136
 137                dp = efi_dp_next(dp);
 138        }
 139
 140        return dp;
 141}
 142
 143static struct efi_object *find_obj(struct efi_device_path *dp, bool short_path,
 144                                   struct efi_device_path **rem)
 145{
 146        struct efi_object *efiobj;
 147        unsigned int dp_size = efi_dp_size(dp);
 148
 149        list_for_each_entry(efiobj, &efi_obj_list, link) {
 150                struct efi_handler *handler;
 151                struct efi_device_path *obj_dp;
 152                efi_status_t ret;
 153
 154                ret = efi_search_protocol(efiobj->handle,
 155                                          &efi_guid_device_path, &handler);
 156                if (ret != EFI_SUCCESS)
 157                        continue;
 158                obj_dp = handler->protocol_interface;
 159
 160                do {
 161                        if (efi_dp_match(dp, obj_dp) == 0) {
 162                                if (rem) {
 163                                        /*
 164                                         * Allow partial matches, but inform
 165                                         * the caller.
 166                                         */
 167                                        *rem = ((void *)dp) +
 168                                                efi_dp_size(obj_dp);
 169                                        return efiobj;
 170                                } else {
 171                                        /* Only return on exact matches */
 172                                        if (efi_dp_size(obj_dp) == dp_size)
 173                                                return efiobj;
 174                                }
 175                        }
 176
 177                        obj_dp = shorten_path(efi_dp_next(obj_dp));
 178                } while (short_path && obj_dp);
 179        }
 180
 181        return NULL;
 182}
 183
 184
 185/*
 186 * Find an efiobj from device-path, if 'rem' is not NULL, returns the
 187 * remaining part of the device path after the matched object.
 188 */
 189struct efi_object *efi_dp_find_obj(struct efi_device_path *dp,
 190                                   struct efi_device_path **rem)
 191{
 192        struct efi_object *efiobj;
 193
 194        /* Search for an exact match first */
 195        efiobj = find_obj(dp, false, NULL);
 196
 197        /* Then for a fuzzy match */
 198        if (!efiobj)
 199                efiobj = find_obj(dp, false, rem);
 200
 201        /* And now for a fuzzy short match */
 202        if (!efiobj)
 203                efiobj = find_obj(dp, true, rem);
 204
 205        return efiobj;
 206}
 207
 208/* return size not including End node: */
 209unsigned efi_dp_size(const struct efi_device_path *dp)
 210{
 211        unsigned sz = 0;
 212
 213        while (dp) {
 214                sz += dp->length;
 215                dp = efi_dp_next(dp);
 216        }
 217
 218        return sz;
 219}
 220
 221struct efi_device_path *efi_dp_dup(const struct efi_device_path *dp)
 222{
 223        struct efi_device_path *ndp;
 224        unsigned sz = efi_dp_size(dp) + sizeof(END);
 225
 226        if (!dp)
 227                return NULL;
 228
 229        ndp = dp_alloc(sz);
 230        memcpy(ndp, dp, sz);
 231
 232        return ndp;
 233}
 234
 235struct efi_device_path *efi_dp_append(const struct efi_device_path *dp1,
 236                                      const struct efi_device_path *dp2)
 237{
 238        struct efi_device_path *ret;
 239
 240        if (!dp1) {
 241                ret = efi_dp_dup(dp2);
 242        } else if (!dp2) {
 243                ret = efi_dp_dup(dp1);
 244        } else {
 245                /* both dp1 and dp2 are non-null */
 246                unsigned sz1 = efi_dp_size(dp1);
 247                unsigned sz2 = efi_dp_size(dp2);
 248                void *p = dp_alloc(sz1 + sz2 + sizeof(END));
 249                memcpy(p, dp1, sz1);
 250                memcpy(p + sz1, dp2, sz2);
 251                memcpy(p + sz1 + sz2, &END, sizeof(END));
 252                ret = p;
 253        }
 254
 255        return ret;
 256}
 257
 258struct efi_device_path *efi_dp_append_node(const struct efi_device_path *dp,
 259                                           const struct efi_device_path *node)
 260{
 261        struct efi_device_path *ret;
 262
 263        if (!node && !dp) {
 264                ret = efi_dp_dup(&END);
 265        } else if (!node) {
 266                ret = efi_dp_dup(dp);
 267        } else if (!dp) {
 268                unsigned sz = node->length;
 269                void *p = dp_alloc(sz + sizeof(END));
 270                memcpy(p, node, sz);
 271                memcpy(p + sz, &END, sizeof(END));
 272                ret = p;
 273        } else {
 274                /* both dp and node are non-null */
 275                unsigned sz = efi_dp_size(dp);
 276                void *p = dp_alloc(sz + node->length + sizeof(END));
 277                memcpy(p, dp, sz);
 278                memcpy(p + sz, node, node->length);
 279                memcpy(p + sz + node->length, &END, sizeof(END));
 280                ret = p;
 281        }
 282
 283        return ret;
 284}
 285
 286#ifdef CONFIG_DM
 287/* size of device-path not including END node for device and all parents
 288 * up to the root device.
 289 */
 290static unsigned dp_size(struct udevice *dev)
 291{
 292        if (!dev || !dev->driver)
 293                return sizeof(ROOT);
 294
 295        switch (dev->driver->id) {
 296        case UCLASS_ROOT:
 297        case UCLASS_SIMPLE_BUS:
 298                /* stop traversing parents at this point: */
 299                return sizeof(ROOT);
 300        case UCLASS_MMC:
 301                return dp_size(dev->parent) +
 302                        sizeof(struct efi_device_path_sd_mmc_path);
 303        case UCLASS_MASS_STORAGE:
 304        case UCLASS_USB_HUB:
 305                return dp_size(dev->parent) +
 306                        sizeof(struct efi_device_path_usb_class);
 307        default:
 308                /* just skip over unknown classes: */
 309                return dp_size(dev->parent);
 310        }
 311}
 312
 313static void *dp_fill(void *buf, struct udevice *dev)
 314{
 315        if (!dev || !dev->driver)
 316                return buf;
 317
 318        switch (dev->driver->id) {
 319        case UCLASS_ROOT:
 320        case UCLASS_SIMPLE_BUS: {
 321                /* stop traversing parents at this point: */
 322                struct efi_device_path_vendor *vdp = buf;
 323                *vdp = ROOT;
 324                return &vdp[1];
 325        }
 326#if defined(CONFIG_DM_MMC) && defined(CONFIG_MMC)
 327        case UCLASS_MMC: {
 328                struct efi_device_path_sd_mmc_path *sddp =
 329                        dp_fill(buf, dev->parent);
 330                struct mmc *mmc = mmc_get_mmc_dev(dev);
 331                struct blk_desc *desc = mmc_get_blk_desc(mmc);
 332
 333                sddp->dp.type     = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
 334                sddp->dp.sub_type = is_sd(desc) ?
 335                        DEVICE_PATH_SUB_TYPE_MSG_SD :
 336                        DEVICE_PATH_SUB_TYPE_MSG_MMC;
 337                sddp->dp.length   = sizeof(*sddp);
 338                sddp->slot_number = dev->seq;
 339
 340                return &sddp[1];
 341        }
 342#endif
 343        case UCLASS_MASS_STORAGE:
 344        case UCLASS_USB_HUB: {
 345                struct efi_device_path_usb_class *udp =
 346                        dp_fill(buf, dev->parent);
 347                struct usb_device *udev = dev_get_parent_priv(dev);
 348                struct usb_device_descriptor *desc = &udev->descriptor;
 349
 350                udp->dp.type     = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
 351                udp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_USB_CLASS;
 352                udp->dp.length   = sizeof(*udp);
 353                udp->vendor_id   = desc->idVendor;
 354                udp->product_id  = desc->idProduct;
 355                udp->device_class    = desc->bDeviceClass;
 356                udp->device_subclass = desc->bDeviceSubClass;
 357                udp->device_protocol = desc->bDeviceProtocol;
 358
 359                return &udp[1];
 360        }
 361        default:
 362                debug("unhandled device class: %s (%u)\n",
 363                      dev->name, dev->driver->id);
 364                return dp_fill(buf, dev->parent);
 365        }
 366}
 367
 368/* Construct a device-path from a device: */
 369struct efi_device_path *efi_dp_from_dev(struct udevice *dev)
 370{
 371        void *buf, *start;
 372
 373        start = buf = dp_alloc(dp_size(dev) + sizeof(END));
 374        buf = dp_fill(buf, dev);
 375        *((struct efi_device_path *)buf) = END;
 376
 377        return start;
 378}
 379#endif
 380
 381static unsigned dp_part_size(struct blk_desc *desc, int part)
 382{
 383        unsigned dpsize;
 384
 385#ifdef CONFIG_BLK
 386        dpsize = dp_size(desc->bdev->parent);
 387#else
 388        dpsize = sizeof(ROOT) + sizeof(struct efi_device_path_usb);
 389#endif
 390
 391        if (part == 0) /* the actual disk, not a partition */
 392                return dpsize;
 393
 394        if (desc->part_type == PART_TYPE_ISO)
 395                dpsize += sizeof(struct efi_device_path_cdrom_path);
 396        else
 397                dpsize += sizeof(struct efi_device_path_hard_drive_path);
 398
 399        return dpsize;
 400}
 401
 402/*
 403 * Create a device path for a block device or one of its partitions.
 404 *
 405 * @buf         buffer to which the device path is wirtten
 406 * @desc        block device descriptor
 407 * @part        partition number, 0 identifies a block device
 408 */
 409static void *dp_part_fill(void *buf, struct blk_desc *desc, int part)
 410{
 411        disk_partition_t info;
 412
 413#ifdef CONFIG_BLK
 414        buf = dp_fill(buf, desc->bdev->parent);
 415#else
 416        /*
 417         * We *could* make a more accurate path, by looking at if_type
 418         * and handling all the different cases like we do for non-
 419         * legacy (ie CONFIG_BLK=y) case.  But most important thing
 420         * is just to have a unique device-path for if_type+devnum.
 421         * So map things to a fictitious USB device.
 422         */
 423        struct efi_device_path_usb *udp;
 424
 425        memcpy(buf, &ROOT, sizeof(ROOT));
 426        buf += sizeof(ROOT);
 427
 428        udp = buf;
 429        udp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
 430        udp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_USB;
 431        udp->dp.length = sizeof(*udp);
 432        udp->parent_port_number = desc->if_type;
 433        udp->usb_interface = desc->devnum;
 434        buf = &udp[1];
 435#endif
 436
 437        if (part == 0) /* the actual disk, not a partition */
 438                return buf;
 439
 440        part_get_info(desc, part, &info);
 441
 442        if (desc->part_type == PART_TYPE_ISO) {
 443                struct efi_device_path_cdrom_path *cddp = buf;
 444
 445                cddp->boot_entry = part;
 446                cddp->dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE;
 447                cddp->dp.sub_type = DEVICE_PATH_SUB_TYPE_CDROM_PATH;
 448                cddp->dp.length = sizeof(*cddp);
 449                cddp->partition_start = info.start;
 450                cddp->partition_end = info.size;
 451
 452                buf = &cddp[1];
 453        } else {
 454                struct efi_device_path_hard_drive_path *hddp = buf;
 455
 456                hddp->dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE;
 457                hddp->dp.sub_type = DEVICE_PATH_SUB_TYPE_HARD_DRIVE_PATH;
 458                hddp->dp.length = sizeof(*hddp);
 459                hddp->partition_number = part;
 460                hddp->partition_start = info.start;
 461                hddp->partition_end = info.size;
 462                if (desc->part_type == PART_TYPE_EFI)
 463                        hddp->partmap_type = 2;
 464                else
 465                        hddp->partmap_type = 1;
 466
 467                switch (desc->sig_type) {
 468                case SIG_TYPE_NONE:
 469                default:
 470                        hddp->signature_type = 0;
 471                        memset(hddp->partition_signature, 0,
 472                               sizeof(hddp->partition_signature));
 473                        break;
 474                case SIG_TYPE_MBR:
 475                        hddp->signature_type = 1;
 476                        memset(hddp->partition_signature, 0,
 477                               sizeof(hddp->partition_signature));
 478                        memcpy(hddp->partition_signature, &desc->mbr_sig,
 479                               sizeof(desc->mbr_sig));
 480                        break;
 481                case SIG_TYPE_GUID:
 482                        hddp->signature_type = 2;
 483                        memcpy(hddp->partition_signature, &desc->guid_sig,
 484                               sizeof(hddp->partition_signature));
 485                        break;
 486                }
 487
 488                buf = &hddp[1];
 489        }
 490
 491        return buf;
 492}
 493
 494
 495/* Construct a device-path from a partition on a blk device: */
 496struct efi_device_path *efi_dp_from_part(struct blk_desc *desc, int part)
 497{
 498        void *buf, *start;
 499
 500        start = buf = dp_alloc(dp_part_size(desc, part) + sizeof(END));
 501
 502        buf = dp_part_fill(buf, desc, part);
 503
 504        *((struct efi_device_path *)buf) = END;
 505
 506        return start;
 507}
 508
 509/* convert path to an UEFI style path (ie. DOS style backslashes and utf16) */
 510static void path_to_uefi(u16 *uefi, const char *path)
 511{
 512        while (*path) {
 513                char c = *(path++);
 514                if (c == '/')
 515                        c = '\\';
 516                *(uefi++) = c;
 517        }
 518        *uefi = '\0';
 519}
 520
 521/*
 522 * If desc is NULL, this creates a path with only the file component,
 523 * otherwise it creates a full path with both device and file components
 524 */
 525struct efi_device_path *efi_dp_from_file(struct blk_desc *desc, int part,
 526                const char *path)
 527{
 528        struct efi_device_path_file_path *fp;
 529        void *buf, *start;
 530        unsigned dpsize = 0, fpsize;
 531
 532        if (desc)
 533                dpsize = dp_part_size(desc, part);
 534
 535        fpsize = sizeof(struct efi_device_path) + 2 * (strlen(path) + 1);
 536        dpsize += fpsize;
 537
 538        start = buf = dp_alloc(dpsize + sizeof(END));
 539
 540        if (desc)
 541                buf = dp_part_fill(buf, desc, part);
 542
 543        /* add file-path: */
 544        fp = buf;
 545        fp->dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE;
 546        fp->dp.sub_type = DEVICE_PATH_SUB_TYPE_FILE_PATH;
 547        fp->dp.length = fpsize;
 548        path_to_uefi(fp->str, path);
 549        buf += fpsize;
 550
 551        *((struct efi_device_path *)buf) = END;
 552
 553        return start;
 554}
 555
 556#ifdef CONFIG_NET
 557struct efi_device_path *efi_dp_from_eth(void)
 558{
 559        struct efi_device_path_mac_addr *ndp;
 560        void *buf, *start;
 561        unsigned dpsize = 0;
 562
 563        assert(eth_get_dev());
 564
 565#ifdef CONFIG_DM_ETH
 566        dpsize += dp_size(eth_get_dev());
 567#else
 568        dpsize += sizeof(ROOT);
 569#endif
 570        dpsize += sizeof(*ndp);
 571
 572        start = buf = dp_alloc(dpsize + sizeof(END));
 573
 574#ifdef CONFIG_DM_ETH
 575        buf = dp_fill(buf, eth_get_dev());
 576#else
 577        memcpy(buf, &ROOT, sizeof(ROOT));
 578        buf += sizeof(ROOT);
 579#endif
 580
 581        ndp = buf;
 582        ndp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
 583        ndp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_MAC_ADDR;
 584        ndp->dp.length = sizeof(*ndp);
 585        memcpy(ndp->mac.addr, eth_get_ethaddr(), ARP_HLEN);
 586        buf = &ndp[1];
 587
 588        *((struct efi_device_path *)buf) = END;
 589
 590        return start;
 591}
 592#endif
 593
 594/* Construct a device-path for memory-mapped image */
 595struct efi_device_path *efi_dp_from_mem(uint32_t memory_type,
 596                                        uint64_t start_address,
 597                                        uint64_t end_address)
 598{
 599        struct efi_device_path_memory *mdp;
 600        void *buf, *start;
 601
 602        start = buf = dp_alloc(sizeof(*mdp) + sizeof(END));
 603
 604        mdp = buf;
 605        mdp->dp.type = DEVICE_PATH_TYPE_HARDWARE_DEVICE;
 606        mdp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MEMORY;
 607        mdp->dp.length = sizeof(*mdp);
 608        mdp->memory_type = memory_type;
 609        mdp->start_address = start_address;
 610        mdp->end_address = end_address;
 611        buf = &mdp[1];
 612
 613        *((struct efi_device_path *)buf) = END;
 614
 615        return start;
 616}
 617
 618/*
 619 * Helper to split a full device path (containing both device and file
 620 * parts) into it's constituent parts.
 621 */
 622void efi_dp_split_file_path(struct efi_device_path *full_path,
 623                            struct efi_device_path **device_path,
 624                            struct efi_device_path **file_path)
 625{
 626        struct efi_device_path *p, *dp, *fp;
 627
 628        dp = efi_dp_dup(full_path);
 629        p = dp;
 630        while (!EFI_DP_TYPE(p, MEDIA_DEVICE, FILE_PATH))
 631                p = efi_dp_next(p);
 632        fp = efi_dp_dup(p);
 633
 634        p->type = DEVICE_PATH_TYPE_END;
 635        p->sub_type = DEVICE_PATH_SUB_TYPE_END;
 636        p->length = sizeof(*p);
 637
 638        *device_path = dp;
 639        *file_path = fp;
 640}
 641