blob: 5b4d03639c35f9aa5585fc0e76ad8db1f2ff89a7 [file] [log] [blame]
Patrick Delaunayd6e53c72018-10-26 09:02:52 +02001// SPDX-License-Identifier: GPL-2.0
2 /*
Tien Fong Chee789aa9c2019-03-05 23:29:38 +08003 * Copyright (C) 2018-2019 Intel Corporation <www.intel.com>
Tien Fong Chee5ca878b2018-07-06 16:28:03 +08004 *
Tien Fong Chee5ca878b2018-07-06 16:28:03 +08005 */
Patrick Delaunay81313352021-04-27 11:02:19 +02006
7#define LOG_CATEGORY UCLASS_FS_FIRMWARE_LOADER
8
Tien Fong Chee5ca878b2018-07-06 16:28:03 +08009#include <common.h>
10#include <dm.h>
Simon Glass0af6e2d2019-08-01 09:46:52 -060011#include <env.h>
Tien Fong Chee5ca878b2018-07-06 16:28:03 +080012#include <errno.h>
13#include <blk.h>
14#include <fs.h>
15#include <fs_loader.h>
Simon Glass0f2af882020-05-10 11:40:05 -060016#include <log.h>
Simon Glass3ba929a2020-10-30 21:38:53 -060017#include <asm/global_data.h>
Tien Fong Chee5ca878b2018-07-06 16:28:03 +080018#include <linux/string.h>
19#include <mapmem.h>
20#include <malloc.h>
21#include <spl.h>
22
Pali Rohár69813112022-04-29 16:36:23 +020023#ifdef CONFIG_CMD_UBIFS
24#include <ubi_uboot.h>
25#endif
26
Tien Fong Chee5ca878b2018-07-06 16:28:03 +080027DECLARE_GLOBAL_DATA_PTR;
28
Tien Fong Cheee8b79702018-12-10 21:29:44 +080029/**
30 * struct firmware - A place for storing firmware and its attribute data.
31 *
32 * This holds information about a firmware and its content.
33 *
34 * @size: Size of a file
35 * @data: Buffer for file
36 * @priv: Firmware loader private fields
37 * @name: Filename
38 * @offset: Offset of reading a file
39 */
40struct firmware {
41 size_t size;
42 const u8 *data;
43 const char *name;
44 u32 offset;
Tien Fong Chee5ca878b2018-07-06 16:28:03 +080045};
46
47#ifdef CONFIG_CMD_UBIFS
48static int mount_ubifs(char *mtdpart, char *ubivol)
49{
50 int ret = ubi_part(mtdpart, NULL);
51
52 if (ret) {
53 debug("Cannot find mtd partition %s\n", mtdpart);
54 return ret;
55 }
56
57 return cmd_ubifs_mount(ubivol);
58}
59
60static int umount_ubifs(void)
61{
62 return cmd_ubifs_umount();
63}
64#else
65static int mount_ubifs(char *mtdpart, char *ubivol)
66{
67 debug("Error: Cannot load image: no UBIFS support\n");
68 return -ENOSYS;
69}
70#endif
71
Simon Glassb75b15b2020-12-03 16:55:23 -070072static int select_fs_dev(struct device_plat *plat)
Tien Fong Chee5ca878b2018-07-06 16:28:03 +080073{
74 int ret;
75
76 if (plat->phandlepart.phandle) {
77 ofnode node;
78
79 node = ofnode_get_by_phandle(plat->phandlepart.phandle);
80
Tien Fong Chee5ca878b2018-07-06 16:28:03 +080081 struct udevice *dev;
82
Keerthyb5cdbd82018-11-05 11:34:53 +053083 ret = device_get_global_by_ofnode(node, &dev);
Tien Fong Chee5ca878b2018-07-06 16:28:03 +080084 if (!ret) {
85 struct blk_desc *desc = blk_get_by_device(dev);
86 if (desc) {
87 ret = fs_set_blk_dev_with_part(desc,
88 plat->phandlepart.partition);
89 } else {
90 debug("%s: No device found\n", __func__);
91 return -ENODEV;
92 }
93 }
94 } else if (plat->mtdpart && plat->ubivol) {
95 ret = mount_ubifs(plat->mtdpart, plat->ubivol);
96 if (ret)
97 return ret;
98
99 ret = fs_set_blk_dev("ubi", NULL, FS_TYPE_UBIFS);
100 } else {
101 debug("Error: unsupported storage device.\n");
102 return -ENODEV;
103 }
104
105 if (ret)
106 debug("Error: could not access storage.\n");
107
108 return ret;
109}
110
111/**
112 * _request_firmware_prepare - Prepare firmware struct.
113 *
Tien Fong Cheee8b79702018-12-10 21:29:44 +0800114 * @dev: An instance of a driver.
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800115 * @name: Name of firmware file.
116 * @dbuf: Address of buffer to load firmware into.
117 * @size: Size of buffer.
118 * @offset: Offset of a file for start reading into buffer.
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800119 *
120 * Return: Negative value if fail, 0 for successful.
121 */
Tien Fong Cheee8b79702018-12-10 21:29:44 +0800122static int _request_firmware_prepare(struct udevice *dev,
123 const char *name, void *dbuf,
124 size_t size, u32 offset)
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800125{
126 if (!name || name[0] == '\0')
127 return -EINVAL;
128
Tien Fong Cheee8b79702018-12-10 21:29:44 +0800129 struct firmware *firmwarep = dev_get_priv(dev);
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800130
Tien Fong Cheee8b79702018-12-10 21:29:44 +0800131 if (!firmwarep)
132 return -ENOMEM;
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800133
Tien Fong Cheee8b79702018-12-10 21:29:44 +0800134 firmwarep->name = name;
135 firmwarep->offset = offset;
136 firmwarep->data = dbuf;
137 firmwarep->size = size;
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800138
139 return 0;
140}
141
142/**
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800143 * fw_get_filesystem_firmware - load firmware into an allocated buffer.
Tien Fong Cheee8b79702018-12-10 21:29:44 +0800144 * @dev: An instance of a driver.
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800145 *
146 * Return: Size of total read, negative value when error.
147 */
Tien Fong Cheee8b79702018-12-10 21:29:44 +0800148static int fw_get_filesystem_firmware(struct udevice *dev)
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800149{
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800150 loff_t actread;
151 char *storage_interface, *dev_part, *ubi_mtdpart, *ubi_volume;
152 int ret;
153
154 storage_interface = env_get("storage_interface");
155 dev_part = env_get("fw_dev_part");
156 ubi_mtdpart = env_get("fw_ubi_mtdpart");
157 ubi_volume = env_get("fw_ubi_volume");
158
159 if (storage_interface && dev_part) {
160 ret = fs_set_blk_dev(storage_interface, dev_part, FS_TYPE_ANY);
161 } else if (storage_interface && ubi_mtdpart && ubi_volume) {
162 ret = mount_ubifs(ubi_mtdpart, ubi_volume);
163 if (ret)
164 return ret;
165
166 if (!strcmp("ubi", storage_interface))
167 ret = fs_set_blk_dev(storage_interface, NULL,
168 FS_TYPE_UBIFS);
169 else
170 ret = -ENODEV;
171 } else {
Simon Glass95588622020-12-22 19:30:28 -0700172 ret = select_fs_dev(dev_get_plat(dev));
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800173 }
174
175 if (ret)
176 goto out;
177
Tien Fong Cheee8b79702018-12-10 21:29:44 +0800178 struct firmware *firmwarep = dev_get_priv(dev);
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800179
Tien Fong Cheee8b79702018-12-10 21:29:44 +0800180 if (!firmwarep)
181 return -ENOMEM;
182
183 ret = fs_read(firmwarep->name, (ulong)map_to_sysmem(firmwarep->data),
184 firmwarep->offset, firmwarep->size, &actread);
Keerthyb5cdbd82018-11-05 11:34:53 +0530185
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800186 if (ret) {
Keerthy0bc20852018-11-05 11:34:54 +0530187 debug("Error: %d Failed to read %s from flash %lld != %zu.\n",
Tien Fong Cheee8b79702018-12-10 21:29:44 +0800188 ret, firmwarep->name, actread, firmwarep->size);
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800189 } else {
190 ret = actread;
191 }
192
193out:
194#ifdef CONFIG_CMD_UBIFS
195 umount_ubifs();
196#endif
197 return ret;
198}
199
200/**
201 * request_firmware_into_buf - Load firmware into a previously allocated buffer.
Tien Fong Cheee8b79702018-12-10 21:29:44 +0800202 * @dev: An instance of a driver.
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800203 * @name: Name of firmware file.
204 * @buf: Address of buffer to load firmware into.
205 * @size: Size of buffer.
206 * @offset: Offset of a file for start reading into buffer.
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800207 *
Tien Fong Cheee8b79702018-12-10 21:29:44 +0800208 * The firmware is loaded directly into the buffer pointed to by @buf.
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800209 *
210 * Return: Size of total read, negative value when error.
211 */
Tien Fong Cheee8b79702018-12-10 21:29:44 +0800212int request_firmware_into_buf(struct udevice *dev,
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800213 const char *name,
Tien Fong Cheee8b79702018-12-10 21:29:44 +0800214 void *buf, size_t size, u32 offset)
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800215{
216 int ret;
217
Tien Fong Cheee8b79702018-12-10 21:29:44 +0800218 if (!dev)
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800219 return -EINVAL;
220
Tien Fong Cheee8b79702018-12-10 21:29:44 +0800221 ret = _request_firmware_prepare(dev, name, buf, size, offset);
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800222 if (ret < 0) /* error */
223 return ret;
224
Tien Fong Cheee8b79702018-12-10 21:29:44 +0800225 ret = fw_get_filesystem_firmware(dev);
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800226
227 return ret;
228}
229
Simon Glassaad29ae2020-12-03 16:55:21 -0700230static int fs_loader_of_to_plat(struct udevice *dev)
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800231{
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800232 u32 phandlepart[2];
233
Tien Fong Chee789aa9c2019-03-05 23:29:38 +0800234 ofnode fs_loader_node = dev_ofnode(dev);
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800235
Tien Fong Chee789aa9c2019-03-05 23:29:38 +0800236 if (ofnode_valid(fs_loader_node)) {
Simon Glassb75b15b2020-12-03 16:55:23 -0700237 struct device_plat *plat;
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800238
Simon Glass95588622020-12-22 19:30:28 -0700239 plat = dev_get_plat(dev);
Tien Fong Chee789aa9c2019-03-05 23:29:38 +0800240 if (!ofnode_read_u32_array(fs_loader_node,
241 "phandlepart",
242 phandlepart, 2)) {
243 plat->phandlepart.phandle = phandlepart[0];
244 plat->phandlepart.partition = phandlepart[1];
245 }
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800246
Tien Fong Chee789aa9c2019-03-05 23:29:38 +0800247 plat->mtdpart = (char *)ofnode_read_string(
248 fs_loader_node, "mtdpart");
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800249
Tien Fong Chee789aa9c2019-03-05 23:29:38 +0800250 plat->ubivol = (char *)ofnode_read_string(
251 fs_loader_node, "ubivol");
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800252 }
253
254 return 0;
255}
256
257static int fs_loader_probe(struct udevice *dev)
258{
Tien Fong Chee5689ff12019-01-31 19:34:13 +0800259#if CONFIG_IS_ENABLED(DM) && CONFIG_IS_ENABLED(BLK)
260 int ret;
Simon Glass95588622020-12-22 19:30:28 -0700261 struct device_plat *plat = dev_get_plat(dev);
Tien Fong Chee5689ff12019-01-31 19:34:13 +0800262
263 if (plat->phandlepart.phandle) {
264 ofnode node = ofnode_get_by_phandle(plat->phandlepart.phandle);
265 struct udevice *parent_dev = NULL;
266
267 ret = device_get_global_by_ofnode(node, &parent_dev);
268 if (!ret) {
269 struct udevice *dev;
270
271 ret = blk_get_from_parent(parent_dev, &dev);
272 if (ret) {
273 debug("fs_loader: No block device: %d\n",
274 ret);
275
276 return ret;
277 }
278 }
279 }
280#endif
281
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800282 return 0;
283};
284
285static const struct udevice_id fs_loader_ids[] = {
286 { .compatible = "u-boot,fs-loader"},
287 { }
288};
289
290U_BOOT_DRIVER(fs_loader) = {
291 .name = "fs-loader",
292 .id = UCLASS_FS_FIRMWARE_LOADER,
293 .of_match = fs_loader_ids,
294 .probe = fs_loader_probe,
Simon Glassaad29ae2020-12-03 16:55:21 -0700295 .of_to_plat = fs_loader_of_to_plat,
Simon Glassb75b15b2020-12-03 16:55:23 -0700296 .plat_auto = sizeof(struct device_plat),
Simon Glass8a2b47f2020-12-03 16:55:17 -0700297 .priv_auto = sizeof(struct firmware),
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800298};
299
300UCLASS_DRIVER(fs_loader) = {
301 .id = UCLASS_FS_FIRMWARE_LOADER,
302 .name = "fs-loader",
303};