xref: /openbmc/u-boot/drivers/misc/fs_loader.c (revision e807f6b5f9a164dc1fc35e1c733fa343acf335c0)
122929e12SPatrick Delaunay // SPDX-License-Identifier: GPL-2.0
262030004STien Fong Chee  /*
362030004STien Fong Chee  * Copyright (C) 2018 Intel Corporation <www.intel.com>
462030004STien Fong Chee  *
562030004STien Fong Chee  */
662030004STien Fong Chee #include <common.h>
762030004STien Fong Chee #include <dm.h>
862030004STien Fong Chee #include <errno.h>
962030004STien Fong Chee #include <blk.h>
1062030004STien Fong Chee #include <fs.h>
1162030004STien Fong Chee #include <fs_loader.h>
1262030004STien Fong Chee #include <linux/string.h>
1362030004STien Fong Chee #include <mapmem.h>
1462030004STien Fong Chee #include <malloc.h>
1562030004STien Fong Chee #include <spl.h>
1662030004STien Fong Chee 
1762030004STien Fong Chee DECLARE_GLOBAL_DATA_PTR;
1862030004STien Fong Chee 
19*31a2cf1cSTien Fong Chee /**
20*31a2cf1cSTien Fong Chee  * struct firmware - A place for storing firmware and its attribute data.
21*31a2cf1cSTien Fong Chee  *
22*31a2cf1cSTien Fong Chee  * This holds information about a firmware and its content.
23*31a2cf1cSTien Fong Chee  *
24*31a2cf1cSTien Fong Chee  * @size: Size of a file
25*31a2cf1cSTien Fong Chee  * @data: Buffer for file
26*31a2cf1cSTien Fong Chee  * @priv: Firmware loader private fields
27*31a2cf1cSTien Fong Chee  * @name: Filename
28*31a2cf1cSTien Fong Chee  * @offset: Offset of reading a file
29*31a2cf1cSTien Fong Chee  */
30*31a2cf1cSTien Fong Chee struct firmware {
31*31a2cf1cSTien Fong Chee 	size_t size;
32*31a2cf1cSTien Fong Chee 	const u8 *data;
33*31a2cf1cSTien Fong Chee 	const char *name;
34*31a2cf1cSTien Fong Chee 	u32 offset;
3562030004STien Fong Chee };
3662030004STien Fong Chee 
3762030004STien Fong Chee #ifdef CONFIG_CMD_UBIFS
mount_ubifs(char * mtdpart,char * ubivol)3862030004STien Fong Chee static int mount_ubifs(char *mtdpart, char *ubivol)
3962030004STien Fong Chee {
4062030004STien Fong Chee 	int ret = ubi_part(mtdpart, NULL);
4162030004STien Fong Chee 
4262030004STien Fong Chee 	if (ret) {
4362030004STien Fong Chee 		debug("Cannot find mtd partition %s\n", mtdpart);
4462030004STien Fong Chee 		return ret;
4562030004STien Fong Chee 	}
4662030004STien Fong Chee 
4762030004STien Fong Chee 	return cmd_ubifs_mount(ubivol);
4862030004STien Fong Chee }
4962030004STien Fong Chee 
umount_ubifs(void)5062030004STien Fong Chee static int umount_ubifs(void)
5162030004STien Fong Chee {
5262030004STien Fong Chee 	return cmd_ubifs_umount();
5362030004STien Fong Chee }
5462030004STien Fong Chee #else
mount_ubifs(char * mtdpart,char * ubivol)5562030004STien Fong Chee static int mount_ubifs(char *mtdpart, char *ubivol)
5662030004STien Fong Chee {
5762030004STien Fong Chee 	debug("Error: Cannot load image: no UBIFS support\n");
5862030004STien Fong Chee 	return -ENOSYS;
5962030004STien Fong Chee }
6062030004STien Fong Chee #endif
6162030004STien Fong Chee 
select_fs_dev(struct device_platdata * plat)6262030004STien Fong Chee static int select_fs_dev(struct device_platdata *plat)
6362030004STien Fong Chee {
6462030004STien Fong Chee 	int ret;
6562030004STien Fong Chee 
6662030004STien Fong Chee 	if (plat->phandlepart.phandle) {
6762030004STien Fong Chee 		ofnode node;
6862030004STien Fong Chee 
6962030004STien Fong Chee 		node = ofnode_get_by_phandle(plat->phandlepart.phandle);
7062030004STien Fong Chee 
7162030004STien Fong Chee 		struct udevice *dev;
7262030004STien Fong Chee 
737c096ea4SKeerthy 		ret = device_get_global_by_ofnode(node, &dev);
7462030004STien Fong Chee 		if (!ret) {
7562030004STien Fong Chee 			struct blk_desc *desc = blk_get_by_device(dev);
7662030004STien Fong Chee 			if (desc) {
7762030004STien Fong Chee 				ret = fs_set_blk_dev_with_part(desc,
7862030004STien Fong Chee 					plat->phandlepart.partition);
7962030004STien Fong Chee 			} else {
8062030004STien Fong Chee 				debug("%s: No device found\n", __func__);
8162030004STien Fong Chee 				return -ENODEV;
8262030004STien Fong Chee 			}
8362030004STien Fong Chee 		}
8462030004STien Fong Chee 	} else if (plat->mtdpart && plat->ubivol) {
8562030004STien Fong Chee 		ret = mount_ubifs(plat->mtdpart, plat->ubivol);
8662030004STien Fong Chee 		if (ret)
8762030004STien Fong Chee 			return ret;
8862030004STien Fong Chee 
8962030004STien Fong Chee 		ret = fs_set_blk_dev("ubi", NULL, FS_TYPE_UBIFS);
9062030004STien Fong Chee 	} else {
9162030004STien Fong Chee 		debug("Error: unsupported storage device.\n");
9262030004STien Fong Chee 		return -ENODEV;
9362030004STien Fong Chee 	}
9462030004STien Fong Chee 
9562030004STien Fong Chee 	if (ret)
9662030004STien Fong Chee 		debug("Error: could not access storage.\n");
9762030004STien Fong Chee 
9862030004STien Fong Chee 	return ret;
9962030004STien Fong Chee }
10062030004STien Fong Chee 
10162030004STien Fong Chee /**
10262030004STien Fong Chee  * _request_firmware_prepare - Prepare firmware struct.
10362030004STien Fong Chee  *
104*31a2cf1cSTien Fong Chee  * @dev: An instance of a driver.
10562030004STien Fong Chee  * @name: Name of firmware file.
10662030004STien Fong Chee  * @dbuf: Address of buffer to load firmware into.
10762030004STien Fong Chee  * @size: Size of buffer.
10862030004STien Fong Chee  * @offset: Offset of a file for start reading into buffer.
10962030004STien Fong Chee  *
11062030004STien Fong Chee  * Return: Negative value if fail, 0 for successful.
11162030004STien Fong Chee  */
_request_firmware_prepare(struct udevice * dev,const char * name,void * dbuf,size_t size,u32 offset)112*31a2cf1cSTien Fong Chee static int _request_firmware_prepare(struct udevice *dev,
113*31a2cf1cSTien Fong Chee 				    const char *name, void *dbuf,
114*31a2cf1cSTien Fong Chee 				    size_t size, u32 offset)
11562030004STien Fong Chee {
11662030004STien Fong Chee 	if (!name || name[0] == '\0')
11762030004STien Fong Chee 		return -EINVAL;
11862030004STien Fong Chee 
119*31a2cf1cSTien Fong Chee 	struct firmware *firmwarep = dev_get_priv(dev);
120*31a2cf1cSTien Fong Chee 
121*31a2cf1cSTien Fong Chee 	if (!firmwarep)
12262030004STien Fong Chee 		return -ENOMEM;
12362030004STien Fong Chee 
124*31a2cf1cSTien Fong Chee 	firmwarep->name = name;
125*31a2cf1cSTien Fong Chee 	firmwarep->offset = offset;
126*31a2cf1cSTien Fong Chee 	firmwarep->data = dbuf;
127*31a2cf1cSTien Fong Chee 	firmwarep->size = size;
12862030004STien Fong Chee 
12962030004STien Fong Chee 	return 0;
13062030004STien Fong Chee }
13162030004STien Fong Chee 
13262030004STien Fong Chee /**
13362030004STien Fong Chee  * fw_get_filesystem_firmware - load firmware into an allocated buffer.
134*31a2cf1cSTien Fong Chee  * @dev: An instance of a driver.
13562030004STien Fong Chee  *
13662030004STien Fong Chee  * Return: Size of total read, negative value when error.
13762030004STien Fong Chee  */
fw_get_filesystem_firmware(struct udevice * dev)138*31a2cf1cSTien Fong Chee static int fw_get_filesystem_firmware(struct udevice *dev)
13962030004STien Fong Chee {
14062030004STien Fong Chee 	loff_t actread;
14162030004STien Fong Chee 	char *storage_interface, *dev_part, *ubi_mtdpart, *ubi_volume;
14262030004STien Fong Chee 	int ret;
14362030004STien Fong Chee 
14462030004STien Fong Chee 	storage_interface = env_get("storage_interface");
14562030004STien Fong Chee 	dev_part = env_get("fw_dev_part");
14662030004STien Fong Chee 	ubi_mtdpart = env_get("fw_ubi_mtdpart");
14762030004STien Fong Chee 	ubi_volume = env_get("fw_ubi_volume");
14862030004STien Fong Chee 
14962030004STien Fong Chee 	if (storage_interface && dev_part) {
15062030004STien Fong Chee 		ret = fs_set_blk_dev(storage_interface, dev_part, FS_TYPE_ANY);
15162030004STien Fong Chee 	} else if (storage_interface && ubi_mtdpart && ubi_volume) {
15262030004STien Fong Chee 		ret = mount_ubifs(ubi_mtdpart, ubi_volume);
15362030004STien Fong Chee 		if (ret)
15462030004STien Fong Chee 			return ret;
15562030004STien Fong Chee 
15662030004STien Fong Chee 		if (!strcmp("ubi", storage_interface))
15762030004STien Fong Chee 			ret = fs_set_blk_dev(storage_interface, NULL,
15862030004STien Fong Chee 				FS_TYPE_UBIFS);
15962030004STien Fong Chee 		else
16062030004STien Fong Chee 			ret = -ENODEV;
16162030004STien Fong Chee 	} else {
162*31a2cf1cSTien Fong Chee 		ret = select_fs_dev(dev->platdata);
16362030004STien Fong Chee 	}
16462030004STien Fong Chee 
16562030004STien Fong Chee 	if (ret)
16662030004STien Fong Chee 		goto out;
16762030004STien Fong Chee 
168*31a2cf1cSTien Fong Chee 	struct firmware *firmwarep = dev_get_priv(dev);
16962030004STien Fong Chee 
170*31a2cf1cSTien Fong Chee 	if (!firmwarep)
171*31a2cf1cSTien Fong Chee 		return -ENOMEM;
172*31a2cf1cSTien Fong Chee 
173*31a2cf1cSTien Fong Chee 	ret = fs_read(firmwarep->name, (ulong)map_to_sysmem(firmwarep->data),
174*31a2cf1cSTien Fong Chee 			firmwarep->offset, firmwarep->size, &actread);
1757c096ea4SKeerthy 
17662030004STien Fong Chee 	if (ret) {
177907837d6SKeerthy 		debug("Error: %d Failed to read %s from flash %lld != %zu.\n",
178*31a2cf1cSTien Fong Chee 		      ret, firmwarep->name, actread, firmwarep->size);
17962030004STien Fong Chee 	} else {
18062030004STien Fong Chee 		ret = actread;
18162030004STien Fong Chee 	}
18262030004STien Fong Chee 
18362030004STien Fong Chee out:
18462030004STien Fong Chee #ifdef CONFIG_CMD_UBIFS
18562030004STien Fong Chee 	umount_ubifs();
18662030004STien Fong Chee #endif
18762030004STien Fong Chee 	return ret;
18862030004STien Fong Chee }
18962030004STien Fong Chee 
19062030004STien Fong Chee /**
19162030004STien Fong Chee  * request_firmware_into_buf - Load firmware into a previously allocated buffer.
192*31a2cf1cSTien Fong Chee  * @dev: An instance of a driver.
19362030004STien Fong Chee  * @name: Name of firmware file.
19462030004STien Fong Chee  * @buf: Address of buffer to load firmware into.
19562030004STien Fong Chee  * @size: Size of buffer.
19662030004STien Fong Chee  * @offset: Offset of a file for start reading into buffer.
19762030004STien Fong Chee  *
198*31a2cf1cSTien Fong Chee  * The firmware is loaded directly into the buffer pointed to by @buf.
19962030004STien Fong Chee  *
20062030004STien Fong Chee  * Return: Size of total read, negative value when error.
20162030004STien Fong Chee  */
request_firmware_into_buf(struct udevice * dev,const char * name,void * buf,size_t size,u32 offset)202*31a2cf1cSTien Fong Chee int request_firmware_into_buf(struct udevice *dev,
20362030004STien Fong Chee 			      const char *name,
204*31a2cf1cSTien Fong Chee 			      void *buf, size_t size, u32 offset)
20562030004STien Fong Chee {
20662030004STien Fong Chee 	int ret;
20762030004STien Fong Chee 
208*31a2cf1cSTien Fong Chee 	if (!dev)
20962030004STien Fong Chee 		return -EINVAL;
21062030004STien Fong Chee 
211*31a2cf1cSTien Fong Chee 	ret = _request_firmware_prepare(dev, name, buf, size, offset);
21262030004STien Fong Chee 	if (ret < 0) /* error */
21362030004STien Fong Chee 		return ret;
21462030004STien Fong Chee 
215*31a2cf1cSTien Fong Chee 	ret = fw_get_filesystem_firmware(dev);
21662030004STien Fong Chee 
21762030004STien Fong Chee 	return ret;
21862030004STien Fong Chee }
21962030004STien Fong Chee 
fs_loader_ofdata_to_platdata(struct udevice * dev)22062030004STien Fong Chee static int fs_loader_ofdata_to_platdata(struct udevice *dev)
22162030004STien Fong Chee {
22262030004STien Fong Chee 	const char *fs_loader_path;
22362030004STien Fong Chee 	u32 phandlepart[2];
22462030004STien Fong Chee 
22562030004STien Fong Chee 	fs_loader_path = ofnode_get_chosen_prop("firmware-loader");
22662030004STien Fong Chee 
22762030004STien Fong Chee 	if (fs_loader_path) {
22862030004STien Fong Chee 		ofnode fs_loader_node;
22962030004STien Fong Chee 
23062030004STien Fong Chee 		fs_loader_node = ofnode_path(fs_loader_path);
23162030004STien Fong Chee 		if (ofnode_valid(fs_loader_node)) {
23262030004STien Fong Chee 			struct device_platdata *plat;
23362030004STien Fong Chee 			plat = dev->platdata;
23462030004STien Fong Chee 
23562030004STien Fong Chee 			if (!ofnode_read_u32_array(fs_loader_node,
23662030004STien Fong Chee 						  "phandlepart",
23762030004STien Fong Chee 						  phandlepart, 2)) {
23862030004STien Fong Chee 				plat->phandlepart.phandle = phandlepart[0];
23962030004STien Fong Chee 				plat->phandlepart.partition = phandlepart[1];
24062030004STien Fong Chee 			}
24162030004STien Fong Chee 
24262030004STien Fong Chee 			plat->mtdpart = (char *)ofnode_read_string(
24362030004STien Fong Chee 					 fs_loader_node, "mtdpart");
24462030004STien Fong Chee 
24562030004STien Fong Chee 			plat->ubivol = (char *)ofnode_read_string(
24662030004STien Fong Chee 					 fs_loader_node, "ubivol");
24762030004STien Fong Chee 		}
24862030004STien Fong Chee 	}
24962030004STien Fong Chee 
25062030004STien Fong Chee 	return 0;
25162030004STien Fong Chee }
25262030004STien Fong Chee 
fs_loader_probe(struct udevice * dev)25362030004STien Fong Chee static int fs_loader_probe(struct udevice *dev)
25462030004STien Fong Chee {
25562030004STien Fong Chee 	return 0;
25662030004STien Fong Chee };
25762030004STien Fong Chee 
25862030004STien Fong Chee static const struct udevice_id fs_loader_ids[] = {
25962030004STien Fong Chee 	{ .compatible = "u-boot,fs-loader"},
26062030004STien Fong Chee 	{ }
26162030004STien Fong Chee };
26262030004STien Fong Chee 
26362030004STien Fong Chee U_BOOT_DRIVER(fs_loader) = {
26462030004STien Fong Chee 	.name			= "fs-loader",
26562030004STien Fong Chee 	.id			= UCLASS_FS_FIRMWARE_LOADER,
26662030004STien Fong Chee 	.of_match		= fs_loader_ids,
26762030004STien Fong Chee 	.probe			= fs_loader_probe,
26862030004STien Fong Chee 	.ofdata_to_platdata	= fs_loader_ofdata_to_platdata,
26962030004STien Fong Chee 	.platdata_auto_alloc_size	= sizeof(struct device_platdata),
270*31a2cf1cSTien Fong Chee 	.priv_auto_alloc_size	= sizeof(struct firmware),
27162030004STien Fong Chee };
27262030004STien Fong Chee 
27362030004STien Fong Chee UCLASS_DRIVER(fs_loader) = {
27462030004STien Fong Chee 	.id		= UCLASS_FS_FIRMWARE_LOADER,
27562030004STien Fong Chee 	.name		= "fs-loader",
27662030004STien Fong Chee };
277