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