xref: /openbmc/u-boot/drivers/misc/fs_loader.c (revision 907837d65938fc95e68deaccfd8ee1802e6ba050)
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 
1962030004STien Fong Chee struct firmware_priv {
2062030004STien Fong Chee 	const char *name;	/* Filename */
2162030004STien Fong Chee 	u32 offset;		/* Offset of reading a file */
2262030004STien Fong Chee };
2362030004STien Fong Chee 
2462030004STien Fong Chee #ifdef CONFIG_CMD_UBIFS
2562030004STien Fong Chee static int mount_ubifs(char *mtdpart, char *ubivol)
2662030004STien Fong Chee {
2762030004STien Fong Chee 	int ret = ubi_part(mtdpart, NULL);
2862030004STien Fong Chee 
2962030004STien Fong Chee 	if (ret) {
3062030004STien Fong Chee 		debug("Cannot find mtd partition %s\n", mtdpart);
3162030004STien Fong Chee 		return ret;
3262030004STien Fong Chee 	}
3362030004STien Fong Chee 
3462030004STien Fong Chee 	return cmd_ubifs_mount(ubivol);
3562030004STien Fong Chee }
3662030004STien Fong Chee 
3762030004STien Fong Chee static int umount_ubifs(void)
3862030004STien Fong Chee {
3962030004STien Fong Chee 	return cmd_ubifs_umount();
4062030004STien Fong Chee }
4162030004STien Fong Chee #else
4262030004STien Fong Chee static int mount_ubifs(char *mtdpart, char *ubivol)
4362030004STien Fong Chee {
4462030004STien Fong Chee 	debug("Error: Cannot load image: no UBIFS support\n");
4562030004STien Fong Chee 	return -ENOSYS;
4662030004STien Fong Chee }
4762030004STien Fong Chee #endif
4862030004STien Fong Chee 
4962030004STien Fong Chee static int select_fs_dev(struct device_platdata *plat)
5062030004STien Fong Chee {
5162030004STien Fong Chee 	int ret;
5262030004STien Fong Chee 
5362030004STien Fong Chee 	if (plat->phandlepart.phandle) {
5462030004STien Fong Chee 		ofnode node;
5562030004STien Fong Chee 
5662030004STien Fong Chee 		node = ofnode_get_by_phandle(plat->phandlepart.phandle);
5762030004STien Fong Chee 
5862030004STien Fong Chee 		struct udevice *dev;
5962030004STien Fong Chee 
607c096ea4SKeerthy 		ret = device_get_global_by_ofnode(node, &dev);
6162030004STien Fong Chee 		if (!ret) {
6262030004STien Fong Chee 			struct blk_desc *desc = blk_get_by_device(dev);
6362030004STien Fong Chee 			if (desc) {
6462030004STien Fong Chee 				ret = fs_set_blk_dev_with_part(desc,
6562030004STien Fong Chee 					plat->phandlepart.partition);
6662030004STien Fong Chee 			} else {
6762030004STien Fong Chee 				debug("%s: No device found\n", __func__);
6862030004STien Fong Chee 				return -ENODEV;
6962030004STien Fong Chee 			}
7062030004STien Fong Chee 		}
7162030004STien Fong Chee 	} else if (plat->mtdpart && plat->ubivol) {
7262030004STien Fong Chee 		ret = mount_ubifs(plat->mtdpart, plat->ubivol);
7362030004STien Fong Chee 		if (ret)
7462030004STien Fong Chee 			return ret;
7562030004STien Fong Chee 
7662030004STien Fong Chee 		ret = fs_set_blk_dev("ubi", NULL, FS_TYPE_UBIFS);
7762030004STien Fong Chee 	} else {
7862030004STien Fong Chee 		debug("Error: unsupported storage device.\n");
7962030004STien Fong Chee 		return -ENODEV;
8062030004STien Fong Chee 	}
8162030004STien Fong Chee 
8262030004STien Fong Chee 	if (ret)
8362030004STien Fong Chee 		debug("Error: could not access storage.\n");
8462030004STien Fong Chee 
8562030004STien Fong Chee 	return ret;
8662030004STien Fong Chee }
8762030004STien Fong Chee 
8862030004STien Fong Chee /**
8962030004STien Fong Chee  * _request_firmware_prepare - Prepare firmware struct.
9062030004STien Fong Chee  *
9162030004STien Fong Chee  * @name: Name of firmware file.
9262030004STien Fong Chee  * @dbuf: Address of buffer to load firmware into.
9362030004STien Fong Chee  * @size: Size of buffer.
9462030004STien Fong Chee  * @offset: Offset of a file for start reading into buffer.
9562030004STien Fong Chee  * @firmwarep: Pointer to pointer to firmware image.
9662030004STien Fong Chee  *
9762030004STien Fong Chee  * Return: Negative value if fail, 0 for successful.
9862030004STien Fong Chee  */
9962030004STien Fong Chee static int _request_firmware_prepare(const char *name, void *dbuf,
10062030004STien Fong Chee 				    size_t size, u32 offset,
10162030004STien Fong Chee 				    struct firmware **firmwarep)
10262030004STien Fong Chee {
10362030004STien Fong Chee 	if (!name || name[0] == '\0')
10462030004STien Fong Chee 		return -EINVAL;
10562030004STien Fong Chee 
10662030004STien Fong Chee 	/* No memory allocation is required if *firmwarep is allocated */
10762030004STien Fong Chee 	if (!(*firmwarep)) {
10862030004STien Fong Chee 		(*firmwarep) = calloc(1, sizeof(struct firmware));
10962030004STien Fong Chee 		if (!(*firmwarep))
11062030004STien Fong Chee 			return -ENOMEM;
11162030004STien Fong Chee 
11262030004STien Fong Chee 		(*firmwarep)->priv = calloc(1, sizeof(struct firmware_priv));
11362030004STien Fong Chee 		if (!(*firmwarep)->priv) {
11462030004STien Fong Chee 			free(*firmwarep);
11562030004STien Fong Chee 			return -ENOMEM;
11662030004STien Fong Chee 		}
11762030004STien Fong Chee 	} else if (!(*firmwarep)->priv) {
11862030004STien Fong Chee 		(*firmwarep)->priv = calloc(1, sizeof(struct firmware_priv));
11962030004STien Fong Chee 		if (!(*firmwarep)->priv) {
12062030004STien Fong Chee 			free(*firmwarep);
12162030004STien Fong Chee 			return -ENOMEM;
12262030004STien Fong Chee 		}
12362030004STien Fong Chee 	}
12462030004STien Fong Chee 
12562030004STien Fong Chee 	((struct firmware_priv *)((*firmwarep)->priv))->name = name;
12662030004STien Fong Chee 	((struct firmware_priv *)((*firmwarep)->priv))->offset = offset;
12762030004STien Fong Chee 	(*firmwarep)->data = dbuf;
12862030004STien Fong Chee 	(*firmwarep)->size = size;
12962030004STien Fong Chee 
13062030004STien Fong Chee 	return 0;
13162030004STien Fong Chee }
13262030004STien Fong Chee 
13362030004STien Fong Chee /**
13462030004STien Fong Chee  * release_firmware - Release the resource associated with a firmware image
13562030004STien Fong Chee  * @firmware: Firmware resource to release
13662030004STien Fong Chee  */
13762030004STien Fong Chee void release_firmware(struct firmware *firmware)
13862030004STien Fong Chee {
13962030004STien Fong Chee 	if (firmware) {
14062030004STien Fong Chee 		if (firmware->priv) {
14162030004STien Fong Chee 			free(firmware->priv);
14262030004STien Fong Chee 			firmware->priv = NULL;
14362030004STien Fong Chee 		}
14462030004STien Fong Chee 		free(firmware);
14562030004STien Fong Chee 	}
14662030004STien Fong Chee }
14762030004STien Fong Chee 
14862030004STien Fong Chee /**
14962030004STien Fong Chee  * fw_get_filesystem_firmware - load firmware into an allocated buffer.
15062030004STien Fong Chee  * @plat: Platform data such as storage and partition firmware loading from.
15162030004STien Fong Chee  * @firmware: pointer to firmware image.
15262030004STien Fong Chee  *
15362030004STien Fong Chee  * Return: Size of total read, negative value when error.
15462030004STien Fong Chee  */
15562030004STien Fong Chee static int fw_get_filesystem_firmware(struct device_platdata *plat,
15662030004STien Fong Chee 				     struct firmware *firmware)
15762030004STien Fong Chee {
15862030004STien Fong Chee 	struct firmware_priv *fw_priv = NULL;
15962030004STien Fong Chee 	loff_t actread;
16062030004STien Fong Chee 	char *storage_interface, *dev_part, *ubi_mtdpart, *ubi_volume;
16162030004STien Fong Chee 	int ret;
16262030004STien Fong Chee 
16362030004STien Fong Chee 	storage_interface = env_get("storage_interface");
16462030004STien Fong Chee 	dev_part = env_get("fw_dev_part");
16562030004STien Fong Chee 	ubi_mtdpart = env_get("fw_ubi_mtdpart");
16662030004STien Fong Chee 	ubi_volume = env_get("fw_ubi_volume");
16762030004STien Fong Chee 
16862030004STien Fong Chee 	if (storage_interface && dev_part) {
16962030004STien Fong Chee 		ret = fs_set_blk_dev(storage_interface, dev_part, FS_TYPE_ANY);
17062030004STien Fong Chee 	} else if (storage_interface && ubi_mtdpart && ubi_volume) {
17162030004STien Fong Chee 		ret = mount_ubifs(ubi_mtdpart, ubi_volume);
17262030004STien Fong Chee 		if (ret)
17362030004STien Fong Chee 			return ret;
17462030004STien Fong Chee 
17562030004STien Fong Chee 		if (!strcmp("ubi", storage_interface))
17662030004STien Fong Chee 			ret = fs_set_blk_dev(storage_interface, NULL,
17762030004STien Fong Chee 				FS_TYPE_UBIFS);
17862030004STien Fong Chee 		else
17962030004STien Fong Chee 			ret = -ENODEV;
18062030004STien Fong Chee 	} else {
18162030004STien Fong Chee 		ret = select_fs_dev(plat);
18262030004STien Fong Chee 	}
18362030004STien Fong Chee 
18462030004STien Fong Chee 	if (ret)
18562030004STien Fong Chee 		goto out;
18662030004STien Fong Chee 
18762030004STien Fong Chee 	fw_priv = firmware->priv;
18862030004STien Fong Chee 
18962030004STien Fong Chee 	ret = fs_read(fw_priv->name, (ulong)map_to_sysmem(firmware->data),
19062030004STien Fong Chee 			fw_priv->offset, firmware->size, &actread);
1917c096ea4SKeerthy 
19262030004STien Fong Chee 	if (ret) {
193*907837d6SKeerthy 		debug("Error: %d Failed to read %s from flash %lld != %zu.\n",
19462030004STien Fong Chee 		      ret, fw_priv->name, actread, firmware->size);
19562030004STien Fong Chee 	} else {
19662030004STien Fong Chee 		ret = actread;
19762030004STien Fong Chee 	}
19862030004STien Fong Chee 
19962030004STien Fong Chee out:
20062030004STien Fong Chee #ifdef CONFIG_CMD_UBIFS
20162030004STien Fong Chee 	umount_ubifs();
20262030004STien Fong Chee #endif
20362030004STien Fong Chee 	return ret;
20462030004STien Fong Chee }
20562030004STien Fong Chee 
20662030004STien Fong Chee /**
20762030004STien Fong Chee  * request_firmware_into_buf - Load firmware into a previously allocated buffer.
20862030004STien Fong Chee  * @plat: Platform data such as storage and partition firmware loading from.
20962030004STien Fong Chee  * @name: Name of firmware file.
21062030004STien Fong Chee  * @buf: Address of buffer to load firmware into.
21162030004STien Fong Chee  * @size: Size of buffer.
21262030004STien Fong Chee  * @offset: Offset of a file for start reading into buffer.
21362030004STien Fong Chee  * @firmwarep: Pointer to firmware image.
21462030004STien Fong Chee  *
21562030004STien Fong Chee  * The firmware is loaded directly into the buffer pointed to by @buf and
21662030004STien Fong Chee  * the @firmwarep data member is pointed at @buf.
21762030004STien Fong Chee  *
21862030004STien Fong Chee  * Return: Size of total read, negative value when error.
21962030004STien Fong Chee  */
22062030004STien Fong Chee int request_firmware_into_buf(struct device_platdata *plat,
22162030004STien Fong Chee 			      const char *name,
22262030004STien Fong Chee 			      void *buf, size_t size, u32 offset,
22362030004STien Fong Chee 			      struct firmware **firmwarep)
22462030004STien Fong Chee {
22562030004STien Fong Chee 	int ret;
22662030004STien Fong Chee 
22762030004STien Fong Chee 	if (!plat)
22862030004STien Fong Chee 		return -EINVAL;
22962030004STien Fong Chee 
23062030004STien Fong Chee 	ret = _request_firmware_prepare(name, buf, size, offset, firmwarep);
23162030004STien Fong Chee 	if (ret < 0) /* error */
23262030004STien Fong Chee 		return ret;
23362030004STien Fong Chee 
23462030004STien Fong Chee 	ret = fw_get_filesystem_firmware(plat, *firmwarep);
23562030004STien Fong Chee 
23662030004STien Fong Chee 	return ret;
23762030004STien Fong Chee }
23862030004STien Fong Chee 
23962030004STien Fong Chee static int fs_loader_ofdata_to_platdata(struct udevice *dev)
24062030004STien Fong Chee {
24162030004STien Fong Chee 	const char *fs_loader_path;
24262030004STien Fong Chee 	u32 phandlepart[2];
24362030004STien Fong Chee 
24462030004STien Fong Chee 	fs_loader_path = ofnode_get_chosen_prop("firmware-loader");
24562030004STien Fong Chee 
24662030004STien Fong Chee 	if (fs_loader_path) {
24762030004STien Fong Chee 		ofnode fs_loader_node;
24862030004STien Fong Chee 
24962030004STien Fong Chee 		fs_loader_node = ofnode_path(fs_loader_path);
25062030004STien Fong Chee 		if (ofnode_valid(fs_loader_node)) {
25162030004STien Fong Chee 			struct device_platdata *plat;
25262030004STien Fong Chee 			plat = dev->platdata;
25362030004STien Fong Chee 
25462030004STien Fong Chee 			if (!ofnode_read_u32_array(fs_loader_node,
25562030004STien Fong Chee 						  "phandlepart",
25662030004STien Fong Chee 						  phandlepart, 2)) {
25762030004STien Fong Chee 				plat->phandlepart.phandle = phandlepart[0];
25862030004STien Fong Chee 				plat->phandlepart.partition = phandlepart[1];
25962030004STien Fong Chee 			}
26062030004STien Fong Chee 
26162030004STien Fong Chee 			plat->mtdpart = (char *)ofnode_read_string(
26262030004STien Fong Chee 					 fs_loader_node, "mtdpart");
26362030004STien Fong Chee 
26462030004STien Fong Chee 			plat->ubivol = (char *)ofnode_read_string(
26562030004STien Fong Chee 					 fs_loader_node, "ubivol");
26662030004STien Fong Chee 		}
26762030004STien Fong Chee 	}
26862030004STien Fong Chee 
26962030004STien Fong Chee 	return 0;
27062030004STien Fong Chee }
27162030004STien Fong Chee 
27262030004STien Fong Chee static int fs_loader_probe(struct udevice *dev)
27362030004STien Fong Chee {
27462030004STien Fong Chee 	return 0;
27562030004STien Fong Chee };
27662030004STien Fong Chee 
27762030004STien Fong Chee static const struct udevice_id fs_loader_ids[] = {
27862030004STien Fong Chee 	{ .compatible = "u-boot,fs-loader"},
27962030004STien Fong Chee 	{ }
28062030004STien Fong Chee };
28162030004STien Fong Chee 
28262030004STien Fong Chee U_BOOT_DRIVER(fs_loader) = {
28362030004STien Fong Chee 	.name			= "fs-loader",
28462030004STien Fong Chee 	.id			= UCLASS_FS_FIRMWARE_LOADER,
28562030004STien Fong Chee 	.of_match		= fs_loader_ids,
28662030004STien Fong Chee 	.probe			= fs_loader_probe,
28762030004STien Fong Chee 	.ofdata_to_platdata	= fs_loader_ofdata_to_platdata,
28862030004STien Fong Chee 	.platdata_auto_alloc_size	= sizeof(struct device_platdata),
28962030004STien Fong Chee };
29062030004STien Fong Chee 
29162030004STien Fong Chee UCLASS_DRIVER(fs_loader) = {
29262030004STien Fong Chee 	.id		= UCLASS_FS_FIRMWARE_LOADER,
29362030004STien Fong Chee 	.name		= "fs-loader",
29462030004STien Fong Chee };
295