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