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