]>
Commit | Line | Data |
---|---|---|
b66c60dd RC |
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: */ | |
19 | static 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: */ | |
30 | static 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 | ||
66b051d5 HS |
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 | */ | |
46 | static 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 | ||
b66c60dd RC |
57 | static 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 | */ | |
71 | struct 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 | */ | |
ff401d3f HS |
89 | int efi_dp_match(const struct efi_device_path *a, |
90 | const struct efi_device_path *b) | |
b66c60dd RC |
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 | */ | |
124 | static 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 | ||
143 | static 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; | |
905cb9e1 | 147 | unsigned int dp_size = efi_dp_size(dp); |
b66c60dd RC |
148 | |
149 | list_for_each_entry(efiobj, &efi_obj_list, link) { | |
72292aba HS |
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) { | |
905cb9e1 AG |
163 | /* |
164 | * Allow partial matches, but inform | |
165 | * the caller. | |
166 | */ | |
72292aba HS |
167 | *rem = ((void *)dp) + |
168 | efi_dp_size(obj_dp); | |
905cb9e1 AG |
169 | return efiobj; |
170 | } else { | |
171 | /* Only return on exact matches */ | |
172 | if (efi_dp_size(obj_dp) == dp_size) | |
173 | return efiobj; | |
b66c60dd | 174 | } |
72292aba | 175 | } |
b66c60dd | 176 | |
72292aba HS |
177 | obj_dp = shorten_path(efi_dp_next(obj_dp)); |
178 | } while (short_path && obj_dp); | |
b66c60dd RC |
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 | */ | |
189 | struct efi_object *efi_dp_find_obj(struct efi_device_path *dp, | |
190 | struct efi_device_path **rem) | |
191 | { | |
192 | struct efi_object *efiobj; | |
193 | ||
905cb9e1 AG |
194 | /* Search for an exact match first */ |
195 | efiobj = find_obj(dp, false, NULL); | |
b66c60dd | 196 | |
905cb9e1 AG |
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 */ | |
b66c60dd RC |
202 | if (!efiobj) |
203 | efiobj = find_obj(dp, true, rem); | |
204 | ||
205 | return efiobj; | |
206 | } | |
207 | ||
208 | /* return size not including End node: */ | |
209 | unsigned 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 | ||
221 | struct 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 | ||
235 | struct 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 | ||
258 | struct 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 | */ | |
290 | static 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); | |
af3106a1 HS |
300 | #ifdef CONFIG_BLK |
301 | case UCLASS_BLK: | |
302 | switch (dev->parent->uclass->uc_drv->id) { | |
303 | #ifdef CONFIG_IDE | |
304 | case UCLASS_IDE: | |
305 | return dp_size(dev->parent) + | |
306 | sizeof(struct efi_device_path_atapi); | |
307 | #endif | |
308 | #if defined(CONFIG_SCSI) && defined(CONFIG_DM_SCSI) | |
309 | case UCLASS_SCSI: | |
310 | return dp_size(dev->parent) + | |
311 | sizeof(struct efi_device_path_scsi); | |
312 | #endif | |
313 | default: | |
314 | return dp_size(dev->parent); | |
315 | } | |
316 | #endif | |
b66c60dd RC |
317 | case UCLASS_MMC: |
318 | return dp_size(dev->parent) + | |
319 | sizeof(struct efi_device_path_sd_mmc_path); | |
320 | case UCLASS_MASS_STORAGE: | |
321 | case UCLASS_USB_HUB: | |
322 | return dp_size(dev->parent) + | |
323 | sizeof(struct efi_device_path_usb_class); | |
324 | default: | |
325 | /* just skip over unknown classes: */ | |
326 | return dp_size(dev->parent); | |
327 | } | |
328 | } | |
329 | ||
af3106a1 HS |
330 | /* |
331 | * Recursively build a device path. | |
332 | * | |
333 | * @buf pointer to the end of the device path | |
334 | * @dev device | |
335 | * @return pointer to the end of the device path | |
336 | */ | |
b66c60dd RC |
337 | static void *dp_fill(void *buf, struct udevice *dev) |
338 | { | |
339 | if (!dev || !dev->driver) | |
340 | return buf; | |
341 | ||
342 | switch (dev->driver->id) { | |
343 | case UCLASS_ROOT: | |
344 | case UCLASS_SIMPLE_BUS: { | |
345 | /* stop traversing parents at this point: */ | |
346 | struct efi_device_path_vendor *vdp = buf; | |
347 | *vdp = ROOT; | |
348 | return &vdp[1]; | |
349 | } | |
af3106a1 HS |
350 | #ifdef CONFIG_BLK |
351 | case UCLASS_BLK: | |
352 | switch (dev->parent->uclass->uc_drv->id) { | |
353 | #ifdef CONFIG_IDE | |
354 | case UCLASS_IDE: { | |
355 | struct efi_device_path_atapi *dp = | |
356 | dp_fill(buf, dev->parent); | |
357 | struct blk_desc *desc = dev_get_uclass_platdata(dev); | |
358 | ||
359 | dp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; | |
360 | dp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_ATAPI; | |
361 | dp->dp.length = sizeof(*dp); | |
362 | dp->logical_unit_number = desc->devnum; | |
363 | dp->primary_secondary = IDE_BUS(desc->devnum); | |
364 | dp->slave_master = desc->devnum % | |
365 | (CONFIG_SYS_IDE_MAXDEVICE / | |
366 | CONFIG_SYS_IDE_MAXBUS); | |
367 | return &dp[1]; | |
368 | } | |
369 | #endif | |
370 | #if defined(CONFIG_SCSI) && defined(CONFIG_DM_SCSI) | |
371 | case UCLASS_SCSI: { | |
372 | struct efi_device_path_scsi *dp = | |
373 | dp_fill(buf, dev->parent); | |
374 | struct blk_desc *desc = dev_get_uclass_platdata(dev); | |
375 | ||
376 | dp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; | |
377 | dp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_SCSI; | |
378 | dp->dp.length = sizeof(*dp); | |
379 | dp->logical_unit_number = desc->lun; | |
380 | dp->target_id = desc->target; | |
381 | return &dp[1]; | |
382 | } | |
383 | #endif | |
384 | default: | |
385 | printf("unhandled parent class: %s (%u)\n", | |
386 | dev->name, dev->driver->id); | |
387 | return dp_fill(buf, dev->parent); | |
388 | } | |
389 | #endif | |
b66c60dd RC |
390 | #if defined(CONFIG_DM_MMC) && defined(CONFIG_MMC) |
391 | case UCLASS_MMC: { | |
392 | struct efi_device_path_sd_mmc_path *sddp = | |
393 | dp_fill(buf, dev->parent); | |
394 | struct mmc *mmc = mmc_get_mmc_dev(dev); | |
395 | struct blk_desc *desc = mmc_get_blk_desc(mmc); | |
396 | ||
397 | sddp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; | |
66b051d5 HS |
398 | sddp->dp.sub_type = is_sd(desc) ? |
399 | DEVICE_PATH_SUB_TYPE_MSG_SD : | |
400 | DEVICE_PATH_SUB_TYPE_MSG_MMC; | |
b66c60dd RC |
401 | sddp->dp.length = sizeof(*sddp); |
402 | sddp->slot_number = dev->seq; | |
403 | ||
404 | return &sddp[1]; | |
405 | } | |
406 | #endif | |
407 | case UCLASS_MASS_STORAGE: | |
408 | case UCLASS_USB_HUB: { | |
409 | struct efi_device_path_usb_class *udp = | |
410 | dp_fill(buf, dev->parent); | |
411 | struct usb_device *udev = dev_get_parent_priv(dev); | |
412 | struct usb_device_descriptor *desc = &udev->descriptor; | |
413 | ||
414 | udp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; | |
415 | udp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_USB_CLASS; | |
416 | udp->dp.length = sizeof(*udp); | |
417 | udp->vendor_id = desc->idVendor; | |
418 | udp->product_id = desc->idProduct; | |
419 | udp->device_class = desc->bDeviceClass; | |
420 | udp->device_subclass = desc->bDeviceSubClass; | |
421 | udp->device_protocol = desc->bDeviceProtocol; | |
422 | ||
423 | return &udp[1]; | |
424 | } | |
425 | default: | |
426 | debug("unhandled device class: %s (%u)\n", | |
427 | dev->name, dev->driver->id); | |
428 | return dp_fill(buf, dev->parent); | |
429 | } | |
430 | } | |
431 | ||
432 | /* Construct a device-path from a device: */ | |
433 | struct efi_device_path *efi_dp_from_dev(struct udevice *dev) | |
434 | { | |
435 | void *buf, *start; | |
436 | ||
437 | start = buf = dp_alloc(dp_size(dev) + sizeof(END)); | |
438 | buf = dp_fill(buf, dev); | |
439 | *((struct efi_device_path *)buf) = END; | |
440 | ||
441 | return start; | |
442 | } | |
443 | #endif | |
444 | ||
445 | static unsigned dp_part_size(struct blk_desc *desc, int part) | |
446 | { | |
447 | unsigned dpsize; | |
448 | ||
449 | #ifdef CONFIG_BLK | |
2bc61b83 HS |
450 | { |
451 | struct udevice *dev; | |
452 | int ret = blk_find_device(desc->if_type, desc->devnum, &dev); | |
453 | ||
454 | if (ret) | |
455 | dev = desc->bdev->parent; | |
456 | dpsize = dp_size(dev); | |
457 | } | |
b66c60dd RC |
458 | #else |
459 | dpsize = sizeof(ROOT) + sizeof(struct efi_device_path_usb); | |
460 | #endif | |
461 | ||
462 | if (part == 0) /* the actual disk, not a partition */ | |
463 | return dpsize; | |
464 | ||
465 | if (desc->part_type == PART_TYPE_ISO) | |
466 | dpsize += sizeof(struct efi_device_path_cdrom_path); | |
467 | else | |
468 | dpsize += sizeof(struct efi_device_path_hard_drive_path); | |
469 | ||
470 | return dpsize; | |
471 | } | |
472 | ||
bde6bfe4 HS |
473 | /* |
474 | * Create a device path for a block device or one of its partitions. | |
475 | * | |
476 | * @buf buffer to which the device path is wirtten | |
477 | * @desc block device descriptor | |
478 | * @part partition number, 0 identifies a block device | |
479 | */ | |
b66c60dd RC |
480 | static void *dp_part_fill(void *buf, struct blk_desc *desc, int part) |
481 | { | |
482 | disk_partition_t info; | |
483 | ||
484 | #ifdef CONFIG_BLK | |
2bc61b83 HS |
485 | { |
486 | struct udevice *dev; | |
487 | int ret = blk_find_device(desc->if_type, desc->devnum, &dev); | |
488 | ||
489 | if (ret) | |
490 | dev = desc->bdev->parent; | |
491 | buf = dp_fill(buf, dev); | |
492 | } | |
b66c60dd RC |
493 | #else |
494 | /* | |
495 | * We *could* make a more accurate path, by looking at if_type | |
496 | * and handling all the different cases like we do for non- | |
497 | * legacy (ie CONFIG_BLK=y) case. But most important thing | |
498 | * is just to have a unique device-path for if_type+devnum. | |
bde6bfe4 | 499 | * So map things to a fictitious USB device. |
b66c60dd RC |
500 | */ |
501 | struct efi_device_path_usb *udp; | |
502 | ||
503 | memcpy(buf, &ROOT, sizeof(ROOT)); | |
504 | buf += sizeof(ROOT); | |
505 | ||
506 | udp = buf; | |
507 | udp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; | |
508 | udp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_USB; | |
509 | udp->dp.length = sizeof(*udp); | |
510 | udp->parent_port_number = desc->if_type; | |
511 | udp->usb_interface = desc->devnum; | |
512 | buf = &udp[1]; | |
513 | #endif | |
514 | ||
515 | if (part == 0) /* the actual disk, not a partition */ | |
516 | return buf; | |
517 | ||
518 | part_get_info(desc, part, &info); | |
519 | ||
520 | if (desc->part_type == PART_TYPE_ISO) { | |
521 | struct efi_device_path_cdrom_path *cddp = buf; | |
522 | ||
7b982f00 | 523 | cddp->boot_entry = part; |
b66c60dd RC |
524 | cddp->dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE; |
525 | cddp->dp.sub_type = DEVICE_PATH_SUB_TYPE_CDROM_PATH; | |
526 | cddp->dp.length = sizeof(*cddp); | |
527 | cddp->partition_start = info.start; | |
528 | cddp->partition_end = info.size; | |
529 | ||
530 | buf = &cddp[1]; | |
531 | } else { | |
532 | struct efi_device_path_hard_drive_path *hddp = buf; | |
533 | ||
534 | hddp->dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE; | |
535 | hddp->dp.sub_type = DEVICE_PATH_SUB_TYPE_HARD_DRIVE_PATH; | |
536 | hddp->dp.length = sizeof(*hddp); | |
7b982f00 | 537 | hddp->partition_number = part; |
b66c60dd RC |
538 | hddp->partition_start = info.start; |
539 | hddp->partition_end = info.size; | |
540 | if (desc->part_type == PART_TYPE_EFI) | |
541 | hddp->partmap_type = 2; | |
542 | else | |
543 | hddp->partmap_type = 1; | |
b6e9e097 JG |
544 | |
545 | switch (desc->sig_type) { | |
546 | case SIG_TYPE_NONE: | |
547 | default: | |
548 | hddp->signature_type = 0; | |
549 | memset(hddp->partition_signature, 0, | |
550 | sizeof(hddp->partition_signature)); | |
551 | break; | |
552 | case SIG_TYPE_MBR: | |
553 | hddp->signature_type = 1; | |
554 | memset(hddp->partition_signature, 0, | |
555 | sizeof(hddp->partition_signature)); | |
556 | memcpy(hddp->partition_signature, &desc->mbr_sig, | |
557 | sizeof(desc->mbr_sig)); | |
558 | break; | |
559 | case SIG_TYPE_GUID: | |
560 | hddp->signature_type = 2; | |
b66c60dd RC |
561 | memcpy(hddp->partition_signature, &desc->guid_sig, |
562 | sizeof(hddp->partition_signature)); | |
b6e9e097 JG |
563 | break; |
564 | } | |
b66c60dd RC |
565 | |
566 | buf = &hddp[1]; | |
567 | } | |
568 | ||
569 | return buf; | |
570 | } | |
571 | ||
572 | ||
573 | /* Construct a device-path from a partition on a blk device: */ | |
574 | struct efi_device_path *efi_dp_from_part(struct blk_desc *desc, int part) | |
575 | { | |
576 | void *buf, *start; | |
577 | ||
578 | start = buf = dp_alloc(dp_part_size(desc, part) + sizeof(END)); | |
579 | ||
580 | buf = dp_part_fill(buf, desc, part); | |
581 | ||
582 | *((struct efi_device_path *)buf) = END; | |
583 | ||
584 | return start; | |
585 | } | |
586 | ||
587 | /* convert path to an UEFI style path (ie. DOS style backslashes and utf16) */ | |
588 | static void path_to_uefi(u16 *uefi, const char *path) | |
589 | { | |
590 | while (*path) { | |
591 | char c = *(path++); | |
592 | if (c == '/') | |
593 | c = '\\'; | |
594 | *(uefi++) = c; | |
595 | } | |
596 | *uefi = '\0'; | |
597 | } | |
598 | ||
599 | /* | |
600 | * If desc is NULL, this creates a path with only the file component, | |
601 | * otherwise it creates a full path with both device and file components | |
602 | */ | |
603 | struct efi_device_path *efi_dp_from_file(struct blk_desc *desc, int part, | |
604 | const char *path) | |
605 | { | |
606 | struct efi_device_path_file_path *fp; | |
607 | void *buf, *start; | |
608 | unsigned dpsize = 0, fpsize; | |
609 | ||
610 | if (desc) | |
611 | dpsize = dp_part_size(desc, part); | |
612 | ||
613 | fpsize = sizeof(struct efi_device_path) + 2 * (strlen(path) + 1); | |
614 | dpsize += fpsize; | |
615 | ||
616 | start = buf = dp_alloc(dpsize + sizeof(END)); | |
617 | ||
618 | if (desc) | |
619 | buf = dp_part_fill(buf, desc, part); | |
620 | ||
621 | /* add file-path: */ | |
622 | fp = buf; | |
623 | fp->dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE; | |
624 | fp->dp.sub_type = DEVICE_PATH_SUB_TYPE_FILE_PATH; | |
625 | fp->dp.length = fpsize; | |
626 | path_to_uefi(fp->str, path); | |
627 | buf += fpsize; | |
628 | ||
629 | *((struct efi_device_path *)buf) = END; | |
630 | ||
631 | return start; | |
632 | } | |
633 | ||
634 | #ifdef CONFIG_NET | |
635 | struct efi_device_path *efi_dp_from_eth(void) | |
636 | { | |
637 | struct efi_device_path_mac_addr *ndp; | |
638 | void *buf, *start; | |
639 | unsigned dpsize = 0; | |
640 | ||
641 | assert(eth_get_dev()); | |
642 | ||
643 | #ifdef CONFIG_DM_ETH | |
644 | dpsize += dp_size(eth_get_dev()); | |
645 | #else | |
646 | dpsize += sizeof(ROOT); | |
647 | #endif | |
648 | dpsize += sizeof(*ndp); | |
649 | ||
650 | start = buf = dp_alloc(dpsize + sizeof(END)); | |
651 | ||
652 | #ifdef CONFIG_DM_ETH | |
653 | buf = dp_fill(buf, eth_get_dev()); | |
654 | #else | |
655 | memcpy(buf, &ROOT, sizeof(ROOT)); | |
656 | buf += sizeof(ROOT); | |
657 | #endif | |
658 | ||
659 | ndp = buf; | |
660 | ndp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; | |
661 | ndp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_MAC_ADDR; | |
662 | ndp->dp.length = sizeof(*ndp); | |
663 | memcpy(ndp->mac.addr, eth_get_ethaddr(), ARP_HLEN); | |
664 | buf = &ndp[1]; | |
665 | ||
666 | *((struct efi_device_path *)buf) = END; | |
667 | ||
668 | return start; | |
669 | } | |
670 | #endif | |
671 | ||
bf19273e RC |
672 | /* Construct a device-path for memory-mapped image */ |
673 | struct efi_device_path *efi_dp_from_mem(uint32_t memory_type, | |
674 | uint64_t start_address, | |
675 | uint64_t end_address) | |
676 | { | |
677 | struct efi_device_path_memory *mdp; | |
678 | void *buf, *start; | |
679 | ||
680 | start = buf = dp_alloc(sizeof(*mdp) + sizeof(END)); | |
681 | ||
682 | mdp = buf; | |
683 | mdp->dp.type = DEVICE_PATH_TYPE_HARDWARE_DEVICE; | |
684 | mdp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MEMORY; | |
685 | mdp->dp.length = sizeof(*mdp); | |
686 | mdp->memory_type = memory_type; | |
687 | mdp->start_address = start_address; | |
688 | mdp->end_address = end_address; | |
689 | buf = &mdp[1]; | |
690 | ||
691 | *((struct efi_device_path *)buf) = END; | |
692 | ||
693 | return start; | |
694 | } | |
695 | ||
b66c60dd RC |
696 | /* |
697 | * Helper to split a full device path (containing both device and file | |
698 | * parts) into it's constituent parts. | |
699 | */ | |
700 | void efi_dp_split_file_path(struct efi_device_path *full_path, | |
701 | struct efi_device_path **device_path, | |
702 | struct efi_device_path **file_path) | |
703 | { | |
704 | struct efi_device_path *p, *dp, *fp; | |
705 | ||
706 | dp = efi_dp_dup(full_path); | |
707 | p = dp; | |
708 | while (!EFI_DP_TYPE(p, MEDIA_DEVICE, FILE_PATH)) | |
709 | p = efi_dp_next(p); | |
710 | fp = efi_dp_dup(p); | |
711 | ||
712 | p->type = DEVICE_PATH_TYPE_END; | |
713 | p->sub_type = DEVICE_PATH_SUB_TYPE_END; | |
714 | p->length = sizeof(*p); | |
715 | ||
716 | *device_path = dp; | |
717 | *file_path = fp; | |
718 | } |