blob: 1ffc199ba1e4939b737feb83f3208d00b6339bfc [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>
Sean Anderson5cd0cb32022-12-29 11:52:59 -050018#include <dm/device-internal.h>
19#include <dm/root.h>
Tien Fong Chee5ca878b2018-07-06 16:28:03 +080020#include <linux/string.h>
21#include <mapmem.h>
22#include <malloc.h>
23#include <spl.h>
24
Pali Rohár69813112022-04-29 16:36:23 +020025#ifdef CONFIG_CMD_UBIFS
26#include <ubi_uboot.h>
27#endif
28
Tien Fong Chee5ca878b2018-07-06 16:28:03 +080029DECLARE_GLOBAL_DATA_PTR;
30
Tien Fong Cheee8b79702018-12-10 21:29:44 +080031/**
32 * struct firmware - A place for storing firmware and its attribute data.
33 *
34 * This holds information about a firmware and its content.
35 *
36 * @size: Size of a file
37 * @data: Buffer for file
38 * @priv: Firmware loader private fields
39 * @name: Filename
40 * @offset: Offset of reading a file
41 */
42struct firmware {
43 size_t size;
44 const u8 *data;
45 const char *name;
46 u32 offset;
Tien Fong Chee5ca878b2018-07-06 16:28:03 +080047};
48
49#ifdef CONFIG_CMD_UBIFS
50static int mount_ubifs(char *mtdpart, char *ubivol)
51{
52 int ret = ubi_part(mtdpart, NULL);
53
54 if (ret) {
55 debug("Cannot find mtd partition %s\n", mtdpart);
56 return ret;
57 }
58
59 return cmd_ubifs_mount(ubivol);
60}
61
62static int umount_ubifs(void)
63{
64 return cmd_ubifs_umount();
65}
66#else
67static int mount_ubifs(char *mtdpart, char *ubivol)
68{
69 debug("Error: Cannot load image: no UBIFS support\n");
70 return -ENOSYS;
71}
72#endif
73
Simon Glassb75b15b2020-12-03 16:55:23 -070074static int select_fs_dev(struct device_plat *plat)
Tien Fong Chee5ca878b2018-07-06 16:28:03 +080075{
76 int ret;
77
78 if (plat->phandlepart.phandle) {
79 ofnode node;
80
81 node = ofnode_get_by_phandle(plat->phandlepart.phandle);
82
Tien Fong Chee5ca878b2018-07-06 16:28:03 +080083 struct udevice *dev;
84
Keerthyb5cdbd82018-11-05 11:34:53 +053085 ret = device_get_global_by_ofnode(node, &dev);
Tien Fong Chee5ca878b2018-07-06 16:28:03 +080086 if (!ret) {
87 struct blk_desc *desc = blk_get_by_device(dev);
88 if (desc) {
89 ret = fs_set_blk_dev_with_part(desc,
90 plat->phandlepart.partition);
91 } else {
92 debug("%s: No device found\n", __func__);
93 return -ENODEV;
94 }
95 }
96 } else if (plat->mtdpart && plat->ubivol) {
97 ret = mount_ubifs(plat->mtdpart, plat->ubivol);
98 if (ret)
99 return ret;
100
101 ret = fs_set_blk_dev("ubi", NULL, FS_TYPE_UBIFS);
102 } else {
103 debug("Error: unsupported storage device.\n");
104 return -ENODEV;
105 }
106
107 if (ret)
108 debug("Error: could not access storage.\n");
109
110 return ret;
111}
112
113/**
114 * _request_firmware_prepare - Prepare firmware struct.
115 *
Tien Fong Cheee8b79702018-12-10 21:29:44 +0800116 * @dev: An instance of a driver.
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800117 * @name: Name of firmware file.
118 * @dbuf: Address of buffer to load firmware into.
119 * @size: Size of buffer.
120 * @offset: Offset of a file for start reading into buffer.
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800121 *
122 * Return: Negative value if fail, 0 for successful.
123 */
Tien Fong Cheee8b79702018-12-10 21:29:44 +0800124static int _request_firmware_prepare(struct udevice *dev,
125 const char *name, void *dbuf,
126 size_t size, u32 offset)
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800127{
128 if (!name || name[0] == '\0')
129 return -EINVAL;
130
Tien Fong Cheee8b79702018-12-10 21:29:44 +0800131 struct firmware *firmwarep = dev_get_priv(dev);
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800132
Tien Fong Cheee8b79702018-12-10 21:29:44 +0800133 if (!firmwarep)
134 return -ENOMEM;
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800135
Tien Fong Cheee8b79702018-12-10 21:29:44 +0800136 firmwarep->name = name;
137 firmwarep->offset = offset;
138 firmwarep->data = dbuf;
139 firmwarep->size = size;
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800140
141 return 0;
142}
143
144/**
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800145 * fw_get_filesystem_firmware - load firmware into an allocated buffer.
Tien Fong Cheee8b79702018-12-10 21:29:44 +0800146 * @dev: An instance of a driver.
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800147 *
148 * Return: Size of total read, negative value when error.
149 */
Tien Fong Cheee8b79702018-12-10 21:29:44 +0800150static int fw_get_filesystem_firmware(struct udevice *dev)
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800151{
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800152 loff_t actread;
153 char *storage_interface, *dev_part, *ubi_mtdpart, *ubi_volume;
154 int ret;
155
156 storage_interface = env_get("storage_interface");
157 dev_part = env_get("fw_dev_part");
158 ubi_mtdpart = env_get("fw_ubi_mtdpart");
159 ubi_volume = env_get("fw_ubi_volume");
160
161 if (storage_interface && dev_part) {
162 ret = fs_set_blk_dev(storage_interface, dev_part, FS_TYPE_ANY);
163 } else if (storage_interface && ubi_mtdpart && ubi_volume) {
164 ret = mount_ubifs(ubi_mtdpart, ubi_volume);
165 if (ret)
166 return ret;
167
168 if (!strcmp("ubi", storage_interface))
169 ret = fs_set_blk_dev(storage_interface, NULL,
170 FS_TYPE_UBIFS);
171 else
172 ret = -ENODEV;
173 } else {
Simon Glass95588622020-12-22 19:30:28 -0700174 ret = select_fs_dev(dev_get_plat(dev));
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800175 }
176
177 if (ret)
178 goto out;
179
Tien Fong Cheee8b79702018-12-10 21:29:44 +0800180 struct firmware *firmwarep = dev_get_priv(dev);
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800181
Tien Fong Cheee8b79702018-12-10 21:29:44 +0800182 if (!firmwarep)
183 return -ENOMEM;
184
185 ret = fs_read(firmwarep->name, (ulong)map_to_sysmem(firmwarep->data),
186 firmwarep->offset, firmwarep->size, &actread);
Keerthyb5cdbd82018-11-05 11:34:53 +0530187
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800188 if (ret) {
Keerthy0bc20852018-11-05 11:34:54 +0530189 debug("Error: %d Failed to read %s from flash %lld != %zu.\n",
Tien Fong Cheee8b79702018-12-10 21:29:44 +0800190 ret, firmwarep->name, actread, firmwarep->size);
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800191 } else {
192 ret = actread;
193 }
194
195out:
196#ifdef CONFIG_CMD_UBIFS
197 umount_ubifs();
198#endif
199 return ret;
200}
201
202/**
203 * request_firmware_into_buf - Load firmware into a previously allocated buffer.
Tien Fong Cheee8b79702018-12-10 21:29:44 +0800204 * @dev: An instance of a driver.
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800205 * @name: Name of firmware file.
206 * @buf: Address of buffer to load firmware into.
207 * @size: Size of buffer.
208 * @offset: Offset of a file for start reading into buffer.
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800209 *
Tien Fong Cheee8b79702018-12-10 21:29:44 +0800210 * The firmware is loaded directly into the buffer pointed to by @buf.
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800211 *
212 * Return: Size of total read, negative value when error.
213 */
Tien Fong Cheee8b79702018-12-10 21:29:44 +0800214int request_firmware_into_buf(struct udevice *dev,
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800215 const char *name,
Tien Fong Cheee8b79702018-12-10 21:29:44 +0800216 void *buf, size_t size, u32 offset)
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800217{
218 int ret;
219
Tien Fong Cheee8b79702018-12-10 21:29:44 +0800220 if (!dev)
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800221 return -EINVAL;
222
Tien Fong Cheee8b79702018-12-10 21:29:44 +0800223 ret = _request_firmware_prepare(dev, name, buf, size, offset);
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800224 if (ret < 0) /* error */
225 return ret;
226
Tien Fong Cheee8b79702018-12-10 21:29:44 +0800227 ret = fw_get_filesystem_firmware(dev);
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800228
229 return ret;
230}
231
Simon Glassaad29ae2020-12-03 16:55:21 -0700232static int fs_loader_of_to_plat(struct udevice *dev)
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800233{
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800234 u32 phandlepart[2];
235
Tien Fong Chee789aa9c2019-03-05 23:29:38 +0800236 ofnode fs_loader_node = dev_ofnode(dev);
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800237
Tien Fong Chee789aa9c2019-03-05 23:29:38 +0800238 if (ofnode_valid(fs_loader_node)) {
Simon Glassb75b15b2020-12-03 16:55:23 -0700239 struct device_plat *plat;
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800240
Simon Glass95588622020-12-22 19:30:28 -0700241 plat = dev_get_plat(dev);
Tien Fong Chee789aa9c2019-03-05 23:29:38 +0800242 if (!ofnode_read_u32_array(fs_loader_node,
243 "phandlepart",
244 phandlepart, 2)) {
245 plat->phandlepart.phandle = phandlepart[0];
246 plat->phandlepart.partition = phandlepart[1];
247 }
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800248
Tien Fong Chee789aa9c2019-03-05 23:29:38 +0800249 plat->mtdpart = (char *)ofnode_read_string(
250 fs_loader_node, "mtdpart");
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800251
Tien Fong Chee789aa9c2019-03-05 23:29:38 +0800252 plat->ubivol = (char *)ofnode_read_string(
253 fs_loader_node, "ubivol");
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800254 }
255
256 return 0;
257}
258
259static int fs_loader_probe(struct udevice *dev)
260{
Tien Fong Chee5689ff12019-01-31 19:34:13 +0800261#if CONFIG_IS_ENABLED(DM) && CONFIG_IS_ENABLED(BLK)
262 int ret;
Simon Glass95588622020-12-22 19:30:28 -0700263 struct device_plat *plat = dev_get_plat(dev);
Tien Fong Chee5689ff12019-01-31 19:34:13 +0800264
265 if (plat->phandlepart.phandle) {
266 ofnode node = ofnode_get_by_phandle(plat->phandlepart.phandle);
267 struct udevice *parent_dev = NULL;
268
269 ret = device_get_global_by_ofnode(node, &parent_dev);
270 if (!ret) {
271 struct udevice *dev;
272
273 ret = blk_get_from_parent(parent_dev, &dev);
274 if (ret) {
275 debug("fs_loader: No block device: %d\n",
276 ret);
277
278 return ret;
279 }
280 }
281 }
282#endif
283
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800284 return 0;
285};
286
287static const struct udevice_id fs_loader_ids[] = {
288 { .compatible = "u-boot,fs-loader"},
289 { }
290};
291
292U_BOOT_DRIVER(fs_loader) = {
293 .name = "fs-loader",
294 .id = UCLASS_FS_FIRMWARE_LOADER,
295 .of_match = fs_loader_ids,
296 .probe = fs_loader_probe,
Simon Glassaad29ae2020-12-03 16:55:21 -0700297 .of_to_plat = fs_loader_of_to_plat,
Simon Glassb75b15b2020-12-03 16:55:23 -0700298 .plat_auto = sizeof(struct device_plat),
Simon Glass8a2b47f2020-12-03 16:55:17 -0700299 .priv_auto = sizeof(struct firmware),
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800300};
301
Sean Anderson5cd0cb32022-12-29 11:52:59 -0500302static struct device_plat default_plat = { 0 };
303
304int get_fs_loader(struct udevice **dev)
305{
306 int ret;
307 ofnode node = ofnode_get_chosen_node("firmware-loader");
308
309 if (ofnode_valid(node))
310 return uclass_get_device_by_ofnode(UCLASS_FS_FIRMWARE_LOADER,
311 node, dev);
312
313 /* Try the first device if none was chosen */
314 ret = uclass_first_device_err(UCLASS_FS_FIRMWARE_LOADER, dev);
315 if (ret != -ENODEV)
316 return ret;
317
318 /* Just create a new device */
Sean Anderson7edccc42023-09-30 16:45:46 -0400319 ret = device_bind(dm_root(), DM_DRIVER_REF(fs_loader), "default-loader",
Sean Anderson5cd0cb32022-12-29 11:52:59 -0500320 &default_plat, ofnode_null(), dev);
321 if (ret)
322 return ret;
323
324 return device_probe(*dev);
325}
326
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800327UCLASS_DRIVER(fs_loader) = {
328 .id = UCLASS_FS_FIRMWARE_LOADER,
329 .name = "fs-loader",
330};