xref: /openbmc/u-boot/drivers/misc/fs_loader.c (revision 620300043c95cc695585748ba6ef79da8b8095eb)
1*62030004STien Fong Chee /*
2*62030004STien Fong Chee  * Copyright (C) 2018 Intel Corporation <www.intel.com>
3*62030004STien Fong Chee  *
4*62030004STien Fong Chee  * SPDX-License-Identifier:    GPL-2.0
5*62030004STien Fong Chee  */
6*62030004STien Fong Chee #include <common.h>
7*62030004STien Fong Chee #include <dm.h>
8*62030004STien Fong Chee #include <errno.h>
9*62030004STien Fong Chee #include <blk.h>
10*62030004STien Fong Chee #include <fs.h>
11*62030004STien Fong Chee #include <fs_loader.h>
12*62030004STien Fong Chee #include <linux/string.h>
13*62030004STien Fong Chee #include <mapmem.h>
14*62030004STien Fong Chee #include <malloc.h>
15*62030004STien Fong Chee #include <spl.h>
16*62030004STien Fong Chee 
17*62030004STien Fong Chee DECLARE_GLOBAL_DATA_PTR;
18*62030004STien Fong Chee 
19*62030004STien Fong Chee struct firmware_priv {
20*62030004STien Fong Chee 	const char *name;	/* Filename */
21*62030004STien Fong Chee 	u32 offset;		/* Offset of reading a file */
22*62030004STien Fong Chee };
23*62030004STien Fong Chee 
24*62030004STien Fong Chee #ifdef CONFIG_CMD_UBIFS
25*62030004STien Fong Chee static int mount_ubifs(char *mtdpart, char *ubivol)
26*62030004STien Fong Chee {
27*62030004STien Fong Chee 	int ret = ubi_part(mtdpart, NULL);
28*62030004STien Fong Chee 
29*62030004STien Fong Chee 	if (ret) {
30*62030004STien Fong Chee 		debug("Cannot find mtd partition %s\n", mtdpart);
31*62030004STien Fong Chee 		return ret;
32*62030004STien Fong Chee 	}
33*62030004STien Fong Chee 
34*62030004STien Fong Chee 	return cmd_ubifs_mount(ubivol);
35*62030004STien Fong Chee }
36*62030004STien Fong Chee 
37*62030004STien Fong Chee static int umount_ubifs(void)
38*62030004STien Fong Chee {
39*62030004STien Fong Chee 	return cmd_ubifs_umount();
40*62030004STien Fong Chee }
41*62030004STien Fong Chee #else
42*62030004STien Fong Chee static int mount_ubifs(char *mtdpart, char *ubivol)
43*62030004STien Fong Chee {
44*62030004STien Fong Chee 	debug("Error: Cannot load image: no UBIFS support\n");
45*62030004STien Fong Chee 	return -ENOSYS;
46*62030004STien Fong Chee }
47*62030004STien Fong Chee #endif
48*62030004STien Fong Chee 
49*62030004STien Fong Chee static int select_fs_dev(struct device_platdata *plat)
50*62030004STien Fong Chee {
51*62030004STien Fong Chee 	int ret;
52*62030004STien Fong Chee 
53*62030004STien Fong Chee 	if (plat->phandlepart.phandle) {
54*62030004STien Fong Chee 		ofnode node;
55*62030004STien Fong Chee 
56*62030004STien Fong Chee 		node = ofnode_get_by_phandle(plat->phandlepart.phandle);
57*62030004STien Fong Chee 
58*62030004STien Fong Chee 		int of_offset = ofnode_to_offset(node);
59*62030004STien Fong Chee 
60*62030004STien Fong Chee 		struct udevice *dev;
61*62030004STien Fong Chee 
62*62030004STien Fong Chee 		ret = device_get_global_by_of_offset(of_offset, &dev);
63*62030004STien Fong Chee 		if (!ret) {
64*62030004STien Fong Chee 			struct blk_desc *desc = blk_get_by_device(dev);
65*62030004STien Fong Chee 			if (desc) {
66*62030004STien Fong Chee 				ret = fs_set_blk_dev_with_part(desc,
67*62030004STien Fong Chee 					plat->phandlepart.partition);
68*62030004STien Fong Chee 			} else {
69*62030004STien Fong Chee 				debug("%s: No device found\n", __func__);
70*62030004STien Fong Chee 				return -ENODEV;
71*62030004STien Fong Chee 			}
72*62030004STien Fong Chee 		}
73*62030004STien Fong Chee 	} else if (plat->mtdpart && plat->ubivol) {
74*62030004STien Fong Chee 		ret = mount_ubifs(plat->mtdpart, plat->ubivol);
75*62030004STien Fong Chee 		if (ret)
76*62030004STien Fong Chee 			return ret;
77*62030004STien Fong Chee 
78*62030004STien Fong Chee 		ret = fs_set_blk_dev("ubi", NULL, FS_TYPE_UBIFS);
79*62030004STien Fong Chee 	} else {
80*62030004STien Fong Chee 		debug("Error: unsupported storage device.\n");
81*62030004STien Fong Chee 		return -ENODEV;
82*62030004STien Fong Chee 	}
83*62030004STien Fong Chee 
84*62030004STien Fong Chee 	if (ret)
85*62030004STien Fong Chee 		debug("Error: could not access storage.\n");
86*62030004STien Fong Chee 
87*62030004STien Fong Chee 	return ret;
88*62030004STien Fong Chee }
89*62030004STien Fong Chee 
90*62030004STien Fong Chee /**
91*62030004STien Fong Chee  * _request_firmware_prepare - Prepare firmware struct.
92*62030004STien Fong Chee  *
93*62030004STien Fong Chee  * @name: Name of firmware file.
94*62030004STien Fong Chee  * @dbuf: Address of buffer to load firmware into.
95*62030004STien Fong Chee  * @size: Size of buffer.
96*62030004STien Fong Chee  * @offset: Offset of a file for start reading into buffer.
97*62030004STien Fong Chee  * @firmwarep: Pointer to pointer to firmware image.
98*62030004STien Fong Chee  *
99*62030004STien Fong Chee  * Return: Negative value if fail, 0 for successful.
100*62030004STien Fong Chee  */
101*62030004STien Fong Chee static int _request_firmware_prepare(const char *name, void *dbuf,
102*62030004STien Fong Chee 				    size_t size, u32 offset,
103*62030004STien Fong Chee 				    struct firmware **firmwarep)
104*62030004STien Fong Chee {
105*62030004STien Fong Chee 	if (!name || name[0] == '\0')
106*62030004STien Fong Chee 		return -EINVAL;
107*62030004STien Fong Chee 
108*62030004STien Fong Chee 	/* No memory allocation is required if *firmwarep is allocated */
109*62030004STien Fong Chee 	if (!(*firmwarep)) {
110*62030004STien Fong Chee 		(*firmwarep) = calloc(1, sizeof(struct firmware));
111*62030004STien Fong Chee 		if (!(*firmwarep))
112*62030004STien Fong Chee 			return -ENOMEM;
113*62030004STien Fong Chee 
114*62030004STien Fong Chee 		(*firmwarep)->priv = calloc(1, sizeof(struct firmware_priv));
115*62030004STien Fong Chee 		if (!(*firmwarep)->priv) {
116*62030004STien Fong Chee 			free(*firmwarep);
117*62030004STien Fong Chee 			return -ENOMEM;
118*62030004STien Fong Chee 		}
119*62030004STien Fong Chee 	} else if (!(*firmwarep)->priv) {
120*62030004STien Fong Chee 		(*firmwarep)->priv = calloc(1, sizeof(struct firmware_priv));
121*62030004STien Fong Chee 		if (!(*firmwarep)->priv) {
122*62030004STien Fong Chee 			free(*firmwarep);
123*62030004STien Fong Chee 			return -ENOMEM;
124*62030004STien Fong Chee 		}
125*62030004STien Fong Chee 	}
126*62030004STien Fong Chee 
127*62030004STien Fong Chee 	((struct firmware_priv *)((*firmwarep)->priv))->name = name;
128*62030004STien Fong Chee 	((struct firmware_priv *)((*firmwarep)->priv))->offset = offset;
129*62030004STien Fong Chee 	(*firmwarep)->data = dbuf;
130*62030004STien Fong Chee 	(*firmwarep)->size = size;
131*62030004STien Fong Chee 
132*62030004STien Fong Chee 	return 0;
133*62030004STien Fong Chee }
134*62030004STien Fong Chee 
135*62030004STien Fong Chee /**
136*62030004STien Fong Chee  * release_firmware - Release the resource associated with a firmware image
137*62030004STien Fong Chee  * @firmware: Firmware resource to release
138*62030004STien Fong Chee  */
139*62030004STien Fong Chee void release_firmware(struct firmware *firmware)
140*62030004STien Fong Chee {
141*62030004STien Fong Chee 	if (firmware) {
142*62030004STien Fong Chee 		if (firmware->priv) {
143*62030004STien Fong Chee 			free(firmware->priv);
144*62030004STien Fong Chee 			firmware->priv = NULL;
145*62030004STien Fong Chee 		}
146*62030004STien Fong Chee 		free(firmware);
147*62030004STien Fong Chee 	}
148*62030004STien Fong Chee }
149*62030004STien Fong Chee 
150*62030004STien Fong Chee /**
151*62030004STien Fong Chee  * fw_get_filesystem_firmware - load firmware into an allocated buffer.
152*62030004STien Fong Chee  * @plat: Platform data such as storage and partition firmware loading from.
153*62030004STien Fong Chee  * @firmware: pointer to firmware image.
154*62030004STien Fong Chee  *
155*62030004STien Fong Chee  * Return: Size of total read, negative value when error.
156*62030004STien Fong Chee  */
157*62030004STien Fong Chee static int fw_get_filesystem_firmware(struct device_platdata *plat,
158*62030004STien Fong Chee 				     struct firmware *firmware)
159*62030004STien Fong Chee {
160*62030004STien Fong Chee 	struct firmware_priv *fw_priv = NULL;
161*62030004STien Fong Chee 	loff_t actread;
162*62030004STien Fong Chee 	char *storage_interface, *dev_part, *ubi_mtdpart, *ubi_volume;
163*62030004STien Fong Chee 	int ret;
164*62030004STien Fong Chee 
165*62030004STien Fong Chee 	storage_interface = env_get("storage_interface");
166*62030004STien Fong Chee 	dev_part = env_get("fw_dev_part");
167*62030004STien Fong Chee 	ubi_mtdpart = env_get("fw_ubi_mtdpart");
168*62030004STien Fong Chee 	ubi_volume = env_get("fw_ubi_volume");
169*62030004STien Fong Chee 
170*62030004STien Fong Chee 	if (storage_interface && dev_part) {
171*62030004STien Fong Chee 		ret = fs_set_blk_dev(storage_interface, dev_part, FS_TYPE_ANY);
172*62030004STien Fong Chee 	} else if (storage_interface && ubi_mtdpart && ubi_volume) {
173*62030004STien Fong Chee 		ret = mount_ubifs(ubi_mtdpart, ubi_volume);
174*62030004STien Fong Chee 		if (ret)
175*62030004STien Fong Chee 			return ret;
176*62030004STien Fong Chee 
177*62030004STien Fong Chee 		if (!strcmp("ubi", storage_interface))
178*62030004STien Fong Chee 			ret = fs_set_blk_dev(storage_interface, NULL,
179*62030004STien Fong Chee 				FS_TYPE_UBIFS);
180*62030004STien Fong Chee 		else
181*62030004STien Fong Chee 			ret = -ENODEV;
182*62030004STien Fong Chee 	} else {
183*62030004STien Fong Chee 		ret = select_fs_dev(plat);
184*62030004STien Fong Chee 	}
185*62030004STien Fong Chee 
186*62030004STien Fong Chee 	if (ret)
187*62030004STien Fong Chee 		goto out;
188*62030004STien Fong Chee 
189*62030004STien Fong Chee 	fw_priv = firmware->priv;
190*62030004STien Fong Chee 
191*62030004STien Fong Chee 	ret = fs_read(fw_priv->name, (ulong)map_to_sysmem(firmware->data),
192*62030004STien Fong Chee 			fw_priv->offset, firmware->size, &actread);
193*62030004STien Fong Chee 	if (ret) {
194*62030004STien Fong Chee 		debug("Error: %d Failed to read %s from flash %lld != %d.\n",
195*62030004STien Fong Chee 		      ret, fw_priv->name, actread, firmware->size);
196*62030004STien Fong Chee 	} else {
197*62030004STien Fong Chee 		ret = actread;
198*62030004STien Fong Chee 	}
199*62030004STien Fong Chee 
200*62030004STien Fong Chee out:
201*62030004STien Fong Chee #ifdef CONFIG_CMD_UBIFS
202*62030004STien Fong Chee 	umount_ubifs();
203*62030004STien Fong Chee #endif
204*62030004STien Fong Chee 	return ret;
205*62030004STien Fong Chee }
206*62030004STien Fong Chee 
207*62030004STien Fong Chee /**
208*62030004STien Fong Chee  * request_firmware_into_buf - Load firmware into a previously allocated buffer.
209*62030004STien Fong Chee  * @plat: Platform data such as storage and partition firmware loading from.
210*62030004STien Fong Chee  * @name: Name of firmware file.
211*62030004STien Fong Chee  * @buf: Address of buffer to load firmware into.
212*62030004STien Fong Chee  * @size: Size of buffer.
213*62030004STien Fong Chee  * @offset: Offset of a file for start reading into buffer.
214*62030004STien Fong Chee  * @firmwarep: Pointer to firmware image.
215*62030004STien Fong Chee  *
216*62030004STien Fong Chee  * The firmware is loaded directly into the buffer pointed to by @buf and
217*62030004STien Fong Chee  * the @firmwarep data member is pointed at @buf.
218*62030004STien Fong Chee  *
219*62030004STien Fong Chee  * Return: Size of total read, negative value when error.
220*62030004STien Fong Chee  */
221*62030004STien Fong Chee int request_firmware_into_buf(struct device_platdata *plat,
222*62030004STien Fong Chee 			      const char *name,
223*62030004STien Fong Chee 			      void *buf, size_t size, u32 offset,
224*62030004STien Fong Chee 			      struct firmware **firmwarep)
225*62030004STien Fong Chee {
226*62030004STien Fong Chee 	int ret;
227*62030004STien Fong Chee 
228*62030004STien Fong Chee 	if (!plat)
229*62030004STien Fong Chee 		return -EINVAL;
230*62030004STien Fong Chee 
231*62030004STien Fong Chee 	ret = _request_firmware_prepare(name, buf, size, offset, firmwarep);
232*62030004STien Fong Chee 	if (ret < 0) /* error */
233*62030004STien Fong Chee 		return ret;
234*62030004STien Fong Chee 
235*62030004STien Fong Chee 	ret = fw_get_filesystem_firmware(plat, *firmwarep);
236*62030004STien Fong Chee 
237*62030004STien Fong Chee 	return ret;
238*62030004STien Fong Chee }
239*62030004STien Fong Chee 
240*62030004STien Fong Chee static int fs_loader_ofdata_to_platdata(struct udevice *dev)
241*62030004STien Fong Chee {
242*62030004STien Fong Chee 	const char *fs_loader_path;
243*62030004STien Fong Chee 	u32 phandlepart[2];
244*62030004STien Fong Chee 
245*62030004STien Fong Chee 	fs_loader_path = ofnode_get_chosen_prop("firmware-loader");
246*62030004STien Fong Chee 
247*62030004STien Fong Chee 	if (fs_loader_path) {
248*62030004STien Fong Chee 		ofnode fs_loader_node;
249*62030004STien Fong Chee 
250*62030004STien Fong Chee 		fs_loader_node = ofnode_path(fs_loader_path);
251*62030004STien Fong Chee 		if (ofnode_valid(fs_loader_node)) {
252*62030004STien Fong Chee 			struct device_platdata *plat;
253*62030004STien Fong Chee 			plat = dev->platdata;
254*62030004STien Fong Chee 
255*62030004STien Fong Chee 			if (!ofnode_read_u32_array(fs_loader_node,
256*62030004STien Fong Chee 						  "phandlepart",
257*62030004STien Fong Chee 						  phandlepart, 2)) {
258*62030004STien Fong Chee 				plat->phandlepart.phandle = phandlepart[0];
259*62030004STien Fong Chee 				plat->phandlepart.partition = phandlepart[1];
260*62030004STien Fong Chee 			}
261*62030004STien Fong Chee 
262*62030004STien Fong Chee 			plat->mtdpart = (char *)ofnode_read_string(
263*62030004STien Fong Chee 					 fs_loader_node, "mtdpart");
264*62030004STien Fong Chee 
265*62030004STien Fong Chee 			plat->ubivol = (char *)ofnode_read_string(
266*62030004STien Fong Chee 					 fs_loader_node, "ubivol");
267*62030004STien Fong Chee 		}
268*62030004STien Fong Chee 	}
269*62030004STien Fong Chee 
270*62030004STien Fong Chee 	return 0;
271*62030004STien Fong Chee }
272*62030004STien Fong Chee 
273*62030004STien Fong Chee static int fs_loader_probe(struct udevice *dev)
274*62030004STien Fong Chee {
275*62030004STien Fong Chee 	return 0;
276*62030004STien Fong Chee };
277*62030004STien Fong Chee 
278*62030004STien Fong Chee static const struct udevice_id fs_loader_ids[] = {
279*62030004STien Fong Chee 	{ .compatible = "u-boot,fs-loader"},
280*62030004STien Fong Chee 	{ }
281*62030004STien Fong Chee };
282*62030004STien Fong Chee 
283*62030004STien Fong Chee U_BOOT_DRIVER(fs_loader) = {
284*62030004STien Fong Chee 	.name			= "fs-loader",
285*62030004STien Fong Chee 	.id			= UCLASS_FS_FIRMWARE_LOADER,
286*62030004STien Fong Chee 	.of_match		= fs_loader_ids,
287*62030004STien Fong Chee 	.probe			= fs_loader_probe,
288*62030004STien Fong Chee 	.ofdata_to_platdata	= fs_loader_ofdata_to_platdata,
289*62030004STien Fong Chee 	.platdata_auto_alloc_size	= sizeof(struct device_platdata),
290*62030004STien Fong Chee };
291*62030004STien Fong Chee 
292*62030004STien Fong Chee UCLASS_DRIVER(fs_loader) = {
293*62030004STien Fong Chee 	.id		= UCLASS_FS_FIRMWARE_LOADER,
294*62030004STien Fong Chee 	.name		= "fs-loader",
295*62030004STien Fong Chee };
296