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