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