]>
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 | ||
9dfd84da HS |
9 | #define LOG_CATEGORY LOGL_ERR |
10 | ||
b66c60dd RC |
11 | #include <common.h> |
12 | #include <blk.h> | |
13 | #include <dm.h> | |
14 | #include <usb.h> | |
15 | #include <mmc.h> | |
16 | #include <efi_loader.h> | |
17 | #include <inttypes.h> | |
18 | #include <part.h> | |
19 | ||
20 | /* template END node: */ | |
21 | static const struct efi_device_path END = { | |
22 | .type = DEVICE_PATH_TYPE_END, | |
23 | .sub_type = DEVICE_PATH_SUB_TYPE_END, | |
24 | .length = sizeof(END), | |
25 | }; | |
26 | ||
27 | #define U_BOOT_GUID \ | |
28 | EFI_GUID(0xe61d73b9, 0xa384, 0x4acc, \ | |
29 | 0xae, 0xab, 0x82, 0xe8, 0x28, 0xf3, 0x62, 0x8b) | |
30 | ||
31 | /* template ROOT node: */ | |
32 | static const struct efi_device_path_vendor ROOT = { | |
33 | .dp = { | |
34 | .type = DEVICE_PATH_TYPE_HARDWARE_DEVICE, | |
35 | .sub_type = DEVICE_PATH_SUB_TYPE_VENDOR, | |
36 | .length = sizeof(ROOT), | |
37 | }, | |
38 | .guid = U_BOOT_GUID, | |
39 | }; | |
40 | ||
66b051d5 HS |
41 | #if defined(CONFIG_DM_MMC) && defined(CONFIG_MMC) |
42 | /* | |
43 | * Determine if an MMC device is an SD card. | |
44 | * | |
45 | * @desc block device descriptor | |
46 | * @return true if the device is an SD card | |
47 | */ | |
48 | static bool is_sd(struct blk_desc *desc) | |
49 | { | |
50 | struct mmc *mmc = find_mmc_device(desc->devnum); | |
51 | ||
52 | if (!mmc) | |
53 | return false; | |
54 | ||
55 | return IS_SD(mmc) != 0U; | |
56 | } | |
57 | #endif | |
58 | ||
b66c60dd RC |
59 | static void *dp_alloc(size_t sz) |
60 | { | |
61 | void *buf; | |
62 | ||
04298686 HS |
63 | if (efi_allocate_pool(EFI_ALLOCATE_ANY_PAGES, sz, &buf) != |
64 | EFI_SUCCESS) { | |
65 | debug("EFI: ERROR: out of memory in %s\n", __func__); | |
b66c60dd | 66 | return NULL; |
04298686 | 67 | } |
b66c60dd RC |
68 | |
69 | return buf; | |
70 | } | |
71 | ||
72 | /* | |
73 | * Iterate to next block in device-path, terminating (returning NULL) | |
74 | * at /End* node. | |
75 | */ | |
76 | struct efi_device_path *efi_dp_next(const struct efi_device_path *dp) | |
77 | { | |
78 | if (dp == NULL) | |
79 | return NULL; | |
80 | if (dp->type == DEVICE_PATH_TYPE_END) | |
81 | return NULL; | |
82 | dp = ((void *)dp) + dp->length; | |
83 | if (dp->type == DEVICE_PATH_TYPE_END) | |
84 | return NULL; | |
85 | return (struct efi_device_path *)dp; | |
86 | } | |
87 | ||
88 | /* | |
89 | * Compare two device-paths, stopping when the shorter of the two hits | |
90 | * an End* node. This is useful to, for example, compare a device-path | |
91 | * representing a device with one representing a file on the device, or | |
92 | * a device with a parent device. | |
93 | */ | |
ff401d3f HS |
94 | int efi_dp_match(const struct efi_device_path *a, |
95 | const struct efi_device_path *b) | |
b66c60dd RC |
96 | { |
97 | while (1) { | |
98 | int ret; | |
99 | ||
100 | ret = memcmp(&a->length, &b->length, sizeof(a->length)); | |
101 | if (ret) | |
102 | return ret; | |
103 | ||
104 | ret = memcmp(a, b, a->length); | |
105 | if (ret) | |
106 | return ret; | |
107 | ||
108 | a = efi_dp_next(a); | |
109 | b = efi_dp_next(b); | |
110 | ||
111 | if (!a || !b) | |
112 | return 0; | |
113 | } | |
114 | } | |
115 | ||
b66c60dd RC |
116 | /* |
117 | * See UEFI spec (section 3.1.2, about short-form device-paths.. | |
118 | * tl;dr: we can have a device-path that starts with a USB WWID | |
119 | * or USB Class node, and a few other cases which don't encode | |
120 | * the full device path with bus hierarchy: | |
121 | * | |
122 | * - MESSAGING:USB_WWID | |
123 | * - MESSAGING:USB_CLASS | |
124 | * - MEDIA:FILE_PATH | |
125 | * - MEDIA:HARD_DRIVE | |
126 | * - MESSAGING:URI | |
127 | */ | |
128 | static struct efi_device_path *shorten_path(struct efi_device_path *dp) | |
129 | { | |
130 | while (dp) { | |
131 | /* | |
132 | * TODO: Add MESSAGING:USB_WWID and MESSAGING:URI.. | |
133 | * in practice fallback.efi just uses MEDIA:HARD_DRIVE | |
134 | * so not sure when we would see these other cases. | |
135 | */ | |
136 | if (EFI_DP_TYPE(dp, MESSAGING_DEVICE, MSG_USB_CLASS) || | |
137 | EFI_DP_TYPE(dp, MEDIA_DEVICE, HARD_DRIVE_PATH) || | |
138 | EFI_DP_TYPE(dp, MEDIA_DEVICE, FILE_PATH)) | |
139 | return dp; | |
140 | ||
141 | dp = efi_dp_next(dp); | |
142 | } | |
143 | ||
144 | return dp; | |
145 | } | |
146 | ||
147 | static struct efi_object *find_obj(struct efi_device_path *dp, bool short_path, | |
148 | struct efi_device_path **rem) | |
149 | { | |
150 | struct efi_object *efiobj; | |
905cb9e1 | 151 | unsigned int dp_size = efi_dp_size(dp); |
b66c60dd RC |
152 | |
153 | list_for_each_entry(efiobj, &efi_obj_list, link) { | |
72292aba HS |
154 | struct efi_handler *handler; |
155 | struct efi_device_path *obj_dp; | |
156 | efi_status_t ret; | |
157 | ||
158 | ret = efi_search_protocol(efiobj->handle, | |
159 | &efi_guid_device_path, &handler); | |
160 | if (ret != EFI_SUCCESS) | |
161 | continue; | |
162 | obj_dp = handler->protocol_interface; | |
163 | ||
164 | do { | |
165 | if (efi_dp_match(dp, obj_dp) == 0) { | |
166 | if (rem) { | |
905cb9e1 AG |
167 | /* |
168 | * Allow partial matches, but inform | |
169 | * the caller. | |
170 | */ | |
72292aba HS |
171 | *rem = ((void *)dp) + |
172 | efi_dp_size(obj_dp); | |
905cb9e1 AG |
173 | return efiobj; |
174 | } else { | |
175 | /* Only return on exact matches */ | |
176 | if (efi_dp_size(obj_dp) == dp_size) | |
177 | return efiobj; | |
b66c60dd | 178 | } |
72292aba | 179 | } |
b66c60dd | 180 | |
72292aba HS |
181 | obj_dp = shorten_path(efi_dp_next(obj_dp)); |
182 | } while (short_path && obj_dp); | |
b66c60dd RC |
183 | } |
184 | ||
185 | return NULL; | |
186 | } | |
187 | ||
b66c60dd RC |
188 | /* |
189 | * Find an efiobj from device-path, if 'rem' is not NULL, returns the | |
190 | * remaining part of the device path after the matched object. | |
191 | */ | |
192 | struct efi_object *efi_dp_find_obj(struct efi_device_path *dp, | |
193 | struct efi_device_path **rem) | |
194 | { | |
195 | struct efi_object *efiobj; | |
196 | ||
905cb9e1 AG |
197 | /* Search for an exact match first */ |
198 | efiobj = find_obj(dp, false, NULL); | |
b66c60dd | 199 | |
905cb9e1 AG |
200 | /* Then for a fuzzy match */ |
201 | if (!efiobj) | |
202 | efiobj = find_obj(dp, false, rem); | |
203 | ||
204 | /* And now for a fuzzy short match */ | |
b66c60dd RC |
205 | if (!efiobj) |
206 | efiobj = find_obj(dp, true, rem); | |
207 | ||
208 | return efiobj; | |
209 | } | |
210 | ||
65436f91 HS |
211 | /* |
212 | * Determine the last device path node that is not the end node. | |
213 | * | |
214 | * @dp device path | |
215 | * @return last node before the end node if it exists | |
216 | * otherwise NULL | |
217 | */ | |
218 | const struct efi_device_path *efi_dp_last_node(const struct efi_device_path *dp) | |
219 | { | |
220 | struct efi_device_path *ret; | |
221 | ||
222 | if (!dp || dp->type == DEVICE_PATH_TYPE_END) | |
223 | return NULL; | |
224 | while (dp) { | |
225 | ret = (struct efi_device_path *)dp; | |
226 | dp = efi_dp_next(dp); | |
227 | } | |
228 | return ret; | |
229 | } | |
230 | ||
b66c60dd RC |
231 | /* return size not including End node: */ |
232 | unsigned efi_dp_size(const struct efi_device_path *dp) | |
233 | { | |
234 | unsigned sz = 0; | |
235 | ||
236 | while (dp) { | |
237 | sz += dp->length; | |
238 | dp = efi_dp_next(dp); | |
239 | } | |
240 | ||
241 | return sz; | |
242 | } | |
243 | ||
244 | struct efi_device_path *efi_dp_dup(const struct efi_device_path *dp) | |
245 | { | |
246 | struct efi_device_path *ndp; | |
247 | unsigned sz = efi_dp_size(dp) + sizeof(END); | |
248 | ||
249 | if (!dp) | |
250 | return NULL; | |
251 | ||
252 | ndp = dp_alloc(sz); | |
04298686 HS |
253 | if (!ndp) |
254 | return NULL; | |
b66c60dd RC |
255 | memcpy(ndp, dp, sz); |
256 | ||
257 | return ndp; | |
258 | } | |
259 | ||
260 | struct efi_device_path *efi_dp_append(const struct efi_device_path *dp1, | |
261 | const struct efi_device_path *dp2) | |
262 | { | |
263 | struct efi_device_path *ret; | |
264 | ||
265 | if (!dp1) { | |
266 | ret = efi_dp_dup(dp2); | |
267 | } else if (!dp2) { | |
268 | ret = efi_dp_dup(dp1); | |
269 | } else { | |
270 | /* both dp1 and dp2 are non-null */ | |
271 | unsigned sz1 = efi_dp_size(dp1); | |
272 | unsigned sz2 = efi_dp_size(dp2); | |
273 | void *p = dp_alloc(sz1 + sz2 + sizeof(END)); | |
04298686 HS |
274 | if (!p) |
275 | return NULL; | |
b66c60dd RC |
276 | memcpy(p, dp1, sz1); |
277 | memcpy(p + sz1, dp2, sz2); | |
278 | memcpy(p + sz1 + sz2, &END, sizeof(END)); | |
279 | ret = p; | |
280 | } | |
281 | ||
282 | return ret; | |
283 | } | |
284 | ||
285 | struct efi_device_path *efi_dp_append_node(const struct efi_device_path *dp, | |
286 | const struct efi_device_path *node) | |
287 | { | |
288 | struct efi_device_path *ret; | |
289 | ||
290 | if (!node && !dp) { | |
291 | ret = efi_dp_dup(&END); | |
292 | } else if (!node) { | |
293 | ret = efi_dp_dup(dp); | |
294 | } else if (!dp) { | |
295 | unsigned sz = node->length; | |
296 | void *p = dp_alloc(sz + sizeof(END)); | |
04298686 HS |
297 | if (!p) |
298 | return NULL; | |
b66c60dd RC |
299 | memcpy(p, node, sz); |
300 | memcpy(p + sz, &END, sizeof(END)); | |
301 | ret = p; | |
302 | } else { | |
303 | /* both dp and node are non-null */ | |
304 | unsigned sz = efi_dp_size(dp); | |
305 | void *p = dp_alloc(sz + node->length + sizeof(END)); | |
04298686 HS |
306 | if (!p) |
307 | return NULL; | |
b66c60dd RC |
308 | memcpy(p, dp, sz); |
309 | memcpy(p + sz, node, node->length); | |
310 | memcpy(p + sz + node->length, &END, sizeof(END)); | |
311 | ret = p; | |
312 | } | |
313 | ||
314 | return ret; | |
315 | } | |
316 | ||
317 | #ifdef CONFIG_DM | |
318 | /* size of device-path not including END node for device and all parents | |
319 | * up to the root device. | |
320 | */ | |
321 | static unsigned dp_size(struct udevice *dev) | |
322 | { | |
323 | if (!dev || !dev->driver) | |
324 | return sizeof(ROOT); | |
325 | ||
326 | switch (dev->driver->id) { | |
327 | case UCLASS_ROOT: | |
328 | case UCLASS_SIMPLE_BUS: | |
329 | /* stop traversing parents at this point: */ | |
330 | return sizeof(ROOT); | |
9dfd84da HS |
331 | case UCLASS_ETH: |
332 | return dp_size(dev->parent) + | |
333 | sizeof(struct efi_device_path_mac_addr); | |
af3106a1 HS |
334 | #ifdef CONFIG_BLK |
335 | case UCLASS_BLK: | |
336 | switch (dev->parent->uclass->uc_drv->id) { | |
337 | #ifdef CONFIG_IDE | |
338 | case UCLASS_IDE: | |
339 | return dp_size(dev->parent) + | |
340 | sizeof(struct efi_device_path_atapi); | |
341 | #endif | |
342 | #if defined(CONFIG_SCSI) && defined(CONFIG_DM_SCSI) | |
343 | case UCLASS_SCSI: | |
344 | return dp_size(dev->parent) + | |
345 | sizeof(struct efi_device_path_scsi); | |
9dfd84da HS |
346 | #endif |
347 | #if defined(CONFIG_DM_MMC) && defined(CONFIG_MMC) | |
348 | case UCLASS_MMC: | |
349 | return dp_size(dev->parent) + | |
350 | sizeof(struct efi_device_path_sd_mmc_path); | |
af3106a1 HS |
351 | #endif |
352 | default: | |
353 | return dp_size(dev->parent); | |
354 | } | |
355 | #endif | |
9dfd84da | 356 | #if defined(CONFIG_DM_MMC) && defined(CONFIG_MMC) |
b66c60dd RC |
357 | case UCLASS_MMC: |
358 | return dp_size(dev->parent) + | |
359 | sizeof(struct efi_device_path_sd_mmc_path); | |
9dfd84da | 360 | #endif |
b66c60dd RC |
361 | case UCLASS_MASS_STORAGE: |
362 | case UCLASS_USB_HUB: | |
363 | return dp_size(dev->parent) + | |
364 | sizeof(struct efi_device_path_usb_class); | |
365 | default: | |
366 | /* just skip over unknown classes: */ | |
367 | return dp_size(dev->parent); | |
368 | } | |
369 | } | |
370 | ||
af3106a1 HS |
371 | /* |
372 | * Recursively build a device path. | |
373 | * | |
374 | * @buf pointer to the end of the device path | |
375 | * @dev device | |
376 | * @return pointer to the end of the device path | |
377 | */ | |
b66c60dd RC |
378 | static void *dp_fill(void *buf, struct udevice *dev) |
379 | { | |
380 | if (!dev || !dev->driver) | |
381 | return buf; | |
382 | ||
383 | switch (dev->driver->id) { | |
384 | case UCLASS_ROOT: | |
385 | case UCLASS_SIMPLE_BUS: { | |
386 | /* stop traversing parents at this point: */ | |
387 | struct efi_device_path_vendor *vdp = buf; | |
388 | *vdp = ROOT; | |
389 | return &vdp[1]; | |
390 | } | |
9dfd84da HS |
391 | #ifdef CONFIG_DM_ETH |
392 | case UCLASS_ETH: { | |
393 | struct efi_device_path_mac_addr *dp = | |
394 | dp_fill(buf, dev->parent); | |
395 | struct eth_pdata *pdata = dev->platdata; | |
396 | ||
397 | dp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; | |
398 | dp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_MAC_ADDR; | |
399 | dp->dp.length = sizeof(*dp); | |
400 | memset(&dp->mac, 0, sizeof(dp->mac)); | |
401 | /* We only support IPv4 */ | |
402 | memcpy(&dp->mac, &pdata->enetaddr, ARP_HLEN); | |
403 | /* Ethernet */ | |
404 | dp->if_type = 1; | |
405 | return &dp[1]; | |
406 | } | |
407 | #endif | |
af3106a1 HS |
408 | #ifdef CONFIG_BLK |
409 | case UCLASS_BLK: | |
410 | switch (dev->parent->uclass->uc_drv->id) { | |
411 | #ifdef CONFIG_IDE | |
412 | case UCLASS_IDE: { | |
413 | struct efi_device_path_atapi *dp = | |
414 | dp_fill(buf, dev->parent); | |
415 | struct blk_desc *desc = dev_get_uclass_platdata(dev); | |
416 | ||
417 | dp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; | |
418 | dp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_ATAPI; | |
419 | dp->dp.length = sizeof(*dp); | |
420 | dp->logical_unit_number = desc->devnum; | |
421 | dp->primary_secondary = IDE_BUS(desc->devnum); | |
422 | dp->slave_master = desc->devnum % | |
423 | (CONFIG_SYS_IDE_MAXDEVICE / | |
424 | CONFIG_SYS_IDE_MAXBUS); | |
425 | return &dp[1]; | |
426 | } | |
427 | #endif | |
428 | #if defined(CONFIG_SCSI) && defined(CONFIG_DM_SCSI) | |
429 | case UCLASS_SCSI: { | |
430 | struct efi_device_path_scsi *dp = | |
431 | dp_fill(buf, dev->parent); | |
432 | struct blk_desc *desc = dev_get_uclass_platdata(dev); | |
433 | ||
434 | dp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; | |
435 | dp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_SCSI; | |
436 | dp->dp.length = sizeof(*dp); | |
437 | dp->logical_unit_number = desc->lun; | |
438 | dp->target_id = desc->target; | |
439 | return &dp[1]; | |
440 | } | |
9dfd84da HS |
441 | #endif |
442 | #if defined(CONFIG_DM_MMC) && defined(CONFIG_MMC) | |
443 | case UCLASS_MMC: { | |
444 | struct efi_device_path_sd_mmc_path *sddp = | |
445 | dp_fill(buf, dev->parent); | |
446 | struct blk_desc *desc = dev_get_uclass_platdata(dev); | |
447 | ||
448 | sddp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; | |
449 | sddp->dp.sub_type = is_sd(desc) ? | |
450 | DEVICE_PATH_SUB_TYPE_MSG_SD : | |
451 | DEVICE_PATH_SUB_TYPE_MSG_MMC; | |
452 | sddp->dp.length = sizeof(*sddp); | |
453 | sddp->slot_number = dev->seq; | |
454 | return &sddp[1]; | |
455 | } | |
af3106a1 HS |
456 | #endif |
457 | default: | |
9dfd84da HS |
458 | debug("%s(%u) %s: unhandled parent class: %s (%u)\n", |
459 | __FILE__, __LINE__, __func__, | |
460 | dev->name, dev->parent->uclass->uc_drv->id); | |
af3106a1 HS |
461 | return dp_fill(buf, dev->parent); |
462 | } | |
463 | #endif | |
b66c60dd RC |
464 | #if defined(CONFIG_DM_MMC) && defined(CONFIG_MMC) |
465 | case UCLASS_MMC: { | |
466 | struct efi_device_path_sd_mmc_path *sddp = | |
467 | dp_fill(buf, dev->parent); | |
468 | struct mmc *mmc = mmc_get_mmc_dev(dev); | |
469 | struct blk_desc *desc = mmc_get_blk_desc(mmc); | |
470 | ||
471 | sddp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; | |
66b051d5 HS |
472 | sddp->dp.sub_type = is_sd(desc) ? |
473 | DEVICE_PATH_SUB_TYPE_MSG_SD : | |
474 | DEVICE_PATH_SUB_TYPE_MSG_MMC; | |
b66c60dd RC |
475 | sddp->dp.length = sizeof(*sddp); |
476 | sddp->slot_number = dev->seq; | |
477 | ||
478 | return &sddp[1]; | |
479 | } | |
480 | #endif | |
481 | case UCLASS_MASS_STORAGE: | |
482 | case UCLASS_USB_HUB: { | |
483 | struct efi_device_path_usb_class *udp = | |
484 | dp_fill(buf, dev->parent); | |
485 | struct usb_device *udev = dev_get_parent_priv(dev); | |
486 | struct usb_device_descriptor *desc = &udev->descriptor; | |
487 | ||
488 | udp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; | |
489 | udp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_USB_CLASS; | |
490 | udp->dp.length = sizeof(*udp); | |
491 | udp->vendor_id = desc->idVendor; | |
492 | udp->product_id = desc->idProduct; | |
493 | udp->device_class = desc->bDeviceClass; | |
494 | udp->device_subclass = desc->bDeviceSubClass; | |
495 | udp->device_protocol = desc->bDeviceProtocol; | |
496 | ||
497 | return &udp[1]; | |
498 | } | |
499 | default: | |
9dfd84da HS |
500 | debug("%s(%u) %s: unhandled device class: %s (%u)\n", |
501 | __FILE__, __LINE__, __func__, | |
b66c60dd RC |
502 | dev->name, dev->driver->id); |
503 | return dp_fill(buf, dev->parent); | |
504 | } | |
505 | } | |
506 | ||
507 | /* Construct a device-path from a device: */ | |
508 | struct efi_device_path *efi_dp_from_dev(struct udevice *dev) | |
509 | { | |
510 | void *buf, *start; | |
511 | ||
512 | start = buf = dp_alloc(dp_size(dev) + sizeof(END)); | |
04298686 HS |
513 | if (!buf) |
514 | return NULL; | |
b66c60dd RC |
515 | buf = dp_fill(buf, dev); |
516 | *((struct efi_device_path *)buf) = END; | |
517 | ||
518 | return start; | |
519 | } | |
520 | #endif | |
521 | ||
522 | static unsigned dp_part_size(struct blk_desc *desc, int part) | |
523 | { | |
524 | unsigned dpsize; | |
525 | ||
526 | #ifdef CONFIG_BLK | |
2bc61b83 HS |
527 | { |
528 | struct udevice *dev; | |
529 | int ret = blk_find_device(desc->if_type, desc->devnum, &dev); | |
530 | ||
531 | if (ret) | |
532 | dev = desc->bdev->parent; | |
533 | dpsize = dp_size(dev); | |
534 | } | |
b66c60dd RC |
535 | #else |
536 | dpsize = sizeof(ROOT) + sizeof(struct efi_device_path_usb); | |
537 | #endif | |
538 | ||
539 | if (part == 0) /* the actual disk, not a partition */ | |
540 | return dpsize; | |
541 | ||
542 | if (desc->part_type == PART_TYPE_ISO) | |
543 | dpsize += sizeof(struct efi_device_path_cdrom_path); | |
544 | else | |
545 | dpsize += sizeof(struct efi_device_path_hard_drive_path); | |
546 | ||
547 | return dpsize; | |
548 | } | |
549 | ||
bde6bfe4 | 550 | /* |
98d48bdf | 551 | * Create a device node for a block device partition. |
bde6bfe4 HS |
552 | * |
553 | * @buf buffer to which the device path is wirtten | |
554 | * @desc block device descriptor | |
555 | * @part partition number, 0 identifies a block device | |
556 | */ | |
98d48bdf | 557 | static void *dp_part_node(void *buf, struct blk_desc *desc, int part) |
b66c60dd RC |
558 | { |
559 | disk_partition_t info; | |
560 | ||
b66c60dd RC |
561 | part_get_info(desc, part, &info); |
562 | ||
563 | if (desc->part_type == PART_TYPE_ISO) { | |
564 | struct efi_device_path_cdrom_path *cddp = buf; | |
565 | ||
7b982f00 | 566 | cddp->boot_entry = part; |
b66c60dd RC |
567 | cddp->dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE; |
568 | cddp->dp.sub_type = DEVICE_PATH_SUB_TYPE_CDROM_PATH; | |
569 | cddp->dp.length = sizeof(*cddp); | |
570 | cddp->partition_start = info.start; | |
571 | cddp->partition_end = info.size; | |
572 | ||
573 | buf = &cddp[1]; | |
574 | } else { | |
575 | struct efi_device_path_hard_drive_path *hddp = buf; | |
576 | ||
577 | hddp->dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE; | |
578 | hddp->dp.sub_type = DEVICE_PATH_SUB_TYPE_HARD_DRIVE_PATH; | |
579 | hddp->dp.length = sizeof(*hddp); | |
7b982f00 | 580 | hddp->partition_number = part; |
b66c60dd RC |
581 | hddp->partition_start = info.start; |
582 | hddp->partition_end = info.size; | |
583 | if (desc->part_type == PART_TYPE_EFI) | |
584 | hddp->partmap_type = 2; | |
585 | else | |
586 | hddp->partmap_type = 1; | |
b6e9e097 JG |
587 | |
588 | switch (desc->sig_type) { | |
589 | case SIG_TYPE_NONE: | |
590 | default: | |
591 | hddp->signature_type = 0; | |
592 | memset(hddp->partition_signature, 0, | |
593 | sizeof(hddp->partition_signature)); | |
594 | break; | |
595 | case SIG_TYPE_MBR: | |
596 | hddp->signature_type = 1; | |
597 | memset(hddp->partition_signature, 0, | |
598 | sizeof(hddp->partition_signature)); | |
599 | memcpy(hddp->partition_signature, &desc->mbr_sig, | |
600 | sizeof(desc->mbr_sig)); | |
601 | break; | |
602 | case SIG_TYPE_GUID: | |
603 | hddp->signature_type = 2; | |
b66c60dd RC |
604 | memcpy(hddp->partition_signature, &desc->guid_sig, |
605 | sizeof(hddp->partition_signature)); | |
b6e9e097 JG |
606 | break; |
607 | } | |
b66c60dd RC |
608 | |
609 | buf = &hddp[1]; | |
610 | } | |
611 | ||
612 | return buf; | |
613 | } | |
614 | ||
98d48bdf HS |
615 | /* |
616 | * Create a device path for a block device or one of its partitions. | |
617 | * | |
618 | * @buf buffer to which the device path is wirtten | |
619 | * @desc block device descriptor | |
620 | * @part partition number, 0 identifies a block device | |
621 | */ | |
622 | static void *dp_part_fill(void *buf, struct blk_desc *desc, int part) | |
623 | { | |
624 | #ifdef CONFIG_BLK | |
625 | { | |
626 | struct udevice *dev; | |
627 | int ret = blk_find_device(desc->if_type, desc->devnum, &dev); | |
628 | ||
629 | if (ret) | |
630 | dev = desc->bdev->parent; | |
631 | buf = dp_fill(buf, dev); | |
632 | } | |
633 | #else | |
634 | /* | |
635 | * We *could* make a more accurate path, by looking at if_type | |
636 | * and handling all the different cases like we do for non- | |
637 | * legacy (ie CONFIG_BLK=y) case. But most important thing | |
638 | * is just to have a unique device-path for if_type+devnum. | |
639 | * So map things to a fictitious USB device. | |
640 | */ | |
641 | struct efi_device_path_usb *udp; | |
642 | ||
643 | memcpy(buf, &ROOT, sizeof(ROOT)); | |
644 | buf += sizeof(ROOT); | |
645 | ||
646 | udp = buf; | |
647 | udp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; | |
648 | udp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_USB; | |
649 | udp->dp.length = sizeof(*udp); | |
650 | udp->parent_port_number = desc->if_type; | |
651 | udp->usb_interface = desc->devnum; | |
652 | buf = &udp[1]; | |
653 | #endif | |
654 | ||
655 | if (part == 0) /* the actual disk, not a partition */ | |
656 | return buf; | |
657 | ||
658 | return dp_part_node(buf, desc, part); | |
659 | } | |
b66c60dd RC |
660 | |
661 | /* Construct a device-path from a partition on a blk device: */ | |
662 | struct efi_device_path *efi_dp_from_part(struct blk_desc *desc, int part) | |
663 | { | |
664 | void *buf, *start; | |
665 | ||
666 | start = buf = dp_alloc(dp_part_size(desc, part) + sizeof(END)); | |
04298686 HS |
667 | if (!buf) |
668 | return NULL; | |
b66c60dd RC |
669 | |
670 | buf = dp_part_fill(buf, desc, part); | |
671 | ||
672 | *((struct efi_device_path *)buf) = END; | |
673 | ||
674 | return start; | |
675 | } | |
676 | ||
98d48bdf HS |
677 | /* |
678 | * Create a device node for a block device partition. | |
679 | * | |
680 | * @buf buffer to which the device path is wirtten | |
681 | * @desc block device descriptor | |
682 | * @part partition number, 0 identifies a block device | |
683 | */ | |
684 | struct efi_device_path *efi_dp_part_node(struct blk_desc *desc, int part) | |
685 | { | |
686 | efi_uintn_t dpsize; | |
687 | void *buf; | |
688 | ||
689 | if (desc->part_type == PART_TYPE_ISO) | |
690 | dpsize = sizeof(struct efi_device_path_cdrom_path); | |
691 | else | |
692 | dpsize = sizeof(struct efi_device_path_hard_drive_path); | |
693 | buf = dp_alloc(dpsize); | |
694 | ||
695 | dp_part_node(buf, desc, part); | |
696 | ||
697 | return buf; | |
698 | } | |
699 | ||
b66c60dd RC |
700 | /* convert path to an UEFI style path (ie. DOS style backslashes and utf16) */ |
701 | static void path_to_uefi(u16 *uefi, const char *path) | |
702 | { | |
703 | while (*path) { | |
704 | char c = *(path++); | |
705 | if (c == '/') | |
706 | c = '\\'; | |
707 | *(uefi++) = c; | |
708 | } | |
709 | *uefi = '\0'; | |
710 | } | |
711 | ||
712 | /* | |
713 | * If desc is NULL, this creates a path with only the file component, | |
714 | * otherwise it creates a full path with both device and file components | |
715 | */ | |
716 | struct efi_device_path *efi_dp_from_file(struct blk_desc *desc, int part, | |
717 | const char *path) | |
718 | { | |
719 | struct efi_device_path_file_path *fp; | |
720 | void *buf, *start; | |
721 | unsigned dpsize = 0, fpsize; | |
722 | ||
723 | if (desc) | |
724 | dpsize = dp_part_size(desc, part); | |
725 | ||
726 | fpsize = sizeof(struct efi_device_path) + 2 * (strlen(path) + 1); | |
727 | dpsize += fpsize; | |
728 | ||
729 | start = buf = dp_alloc(dpsize + sizeof(END)); | |
04298686 HS |
730 | if (!buf) |
731 | return NULL; | |
b66c60dd RC |
732 | |
733 | if (desc) | |
734 | buf = dp_part_fill(buf, desc, part); | |
735 | ||
736 | /* add file-path: */ | |
737 | fp = buf; | |
738 | fp->dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE; | |
739 | fp->dp.sub_type = DEVICE_PATH_SUB_TYPE_FILE_PATH; | |
740 | fp->dp.length = fpsize; | |
741 | path_to_uefi(fp->str, path); | |
742 | buf += fpsize; | |
743 | ||
744 | *((struct efi_device_path *)buf) = END; | |
745 | ||
746 | return start; | |
747 | } | |
748 | ||
3b3ea2c5 | 749 | #ifdef CONFIG_CMD_NET |
b66c60dd RC |
750 | struct efi_device_path *efi_dp_from_eth(void) |
751 | { | |
752 | struct efi_device_path_mac_addr *ndp; | |
753 | void *buf, *start; | |
754 | unsigned dpsize = 0; | |
755 | ||
756 | assert(eth_get_dev()); | |
757 | ||
758 | #ifdef CONFIG_DM_ETH | |
759 | dpsize += dp_size(eth_get_dev()); | |
760 | #else | |
761 | dpsize += sizeof(ROOT); | |
762 | #endif | |
763 | dpsize += sizeof(*ndp); | |
764 | ||
765 | start = buf = dp_alloc(dpsize + sizeof(END)); | |
04298686 HS |
766 | if (!buf) |
767 | return NULL; | |
b66c60dd RC |
768 | |
769 | #ifdef CONFIG_DM_ETH | |
770 | buf = dp_fill(buf, eth_get_dev()); | |
771 | #else | |
772 | memcpy(buf, &ROOT, sizeof(ROOT)); | |
773 | buf += sizeof(ROOT); | |
774 | #endif | |
775 | ||
776 | ndp = buf; | |
777 | ndp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; | |
778 | ndp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_MAC_ADDR; | |
779 | ndp->dp.length = sizeof(*ndp); | |
780 | memcpy(ndp->mac.addr, eth_get_ethaddr(), ARP_HLEN); | |
781 | buf = &ndp[1]; | |
782 | ||
783 | *((struct efi_device_path *)buf) = END; | |
784 | ||
785 | return start; | |
786 | } | |
787 | #endif | |
788 | ||
bf19273e RC |
789 | /* Construct a device-path for memory-mapped image */ |
790 | struct efi_device_path *efi_dp_from_mem(uint32_t memory_type, | |
791 | uint64_t start_address, | |
792 | uint64_t end_address) | |
793 | { | |
794 | struct efi_device_path_memory *mdp; | |
795 | void *buf, *start; | |
796 | ||
797 | start = buf = dp_alloc(sizeof(*mdp) + sizeof(END)); | |
04298686 HS |
798 | if (!buf) |
799 | return NULL; | |
bf19273e RC |
800 | |
801 | mdp = buf; | |
802 | mdp->dp.type = DEVICE_PATH_TYPE_HARDWARE_DEVICE; | |
803 | mdp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MEMORY; | |
804 | mdp->dp.length = sizeof(*mdp); | |
805 | mdp->memory_type = memory_type; | |
806 | mdp->start_address = start_address; | |
807 | mdp->end_address = end_address; | |
808 | buf = &mdp[1]; | |
809 | ||
810 | *((struct efi_device_path *)buf) = END; | |
811 | ||
812 | return start; | |
813 | } | |
814 | ||
b66c60dd RC |
815 | /* |
816 | * Helper to split a full device path (containing both device and file | |
817 | * parts) into it's constituent parts. | |
818 | */ | |
04298686 HS |
819 | efi_status_t efi_dp_split_file_path(struct efi_device_path *full_path, |
820 | struct efi_device_path **device_path, | |
821 | struct efi_device_path **file_path) | |
b66c60dd RC |
822 | { |
823 | struct efi_device_path *p, *dp, *fp; | |
824 | ||
04298686 HS |
825 | *device_path = NULL; |
826 | *file_path = NULL; | |
b66c60dd | 827 | dp = efi_dp_dup(full_path); |
04298686 HS |
828 | if (!dp) |
829 | return EFI_OUT_OF_RESOURCES; | |
b66c60dd | 830 | p = dp; |
04298686 | 831 | while (!EFI_DP_TYPE(p, MEDIA_DEVICE, FILE_PATH)) { |
b66c60dd | 832 | p = efi_dp_next(p); |
04298686 HS |
833 | if (!p) |
834 | return EFI_OUT_OF_RESOURCES; | |
835 | } | |
b66c60dd | 836 | fp = efi_dp_dup(p); |
04298686 HS |
837 | if (!fp) |
838 | return EFI_OUT_OF_RESOURCES; | |
b66c60dd RC |
839 | p->type = DEVICE_PATH_TYPE_END; |
840 | p->sub_type = DEVICE_PATH_SUB_TYPE_END; | |
841 | p->length = sizeof(*p); | |
842 | ||
843 | *device_path = dp; | |
844 | *file_path = fp; | |
04298686 | 845 | return EFI_SUCCESS; |
b66c60dd | 846 | } |