blob: 66803f4b9975f4792c70adf16625cdf0e5bf1799 [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 <dm.h>
Simon Glass0af6e2d2019-08-01 09:46:52 -060010#include <env.h>
Tien Fong Chee5ca878b2018-07-06 16:28:03 +080011#include <errno.h>
12#include <blk.h>
13#include <fs.h>
14#include <fs_loader.h>
Simon Glass0f2af882020-05-10 11:40:05 -060015#include <log.h>
Simon Glass3ba929a2020-10-30 21:38:53 -060016#include <asm/global_data.h>
Sean Anderson5cd0cb32022-12-29 11:52:59 -050017#include <dm/device-internal.h>
18#include <dm/root.h>
Tien Fong Chee5ca878b2018-07-06 16:28:03 +080019#include <linux/string.h>
20#include <mapmem.h>
21#include <malloc.h>
22#include <spl.h>
23
Pali Rohár69813112022-04-29 16:36:23 +020024#ifdef CONFIG_CMD_UBIFS
25#include <ubi_uboot.h>
26#endif
27
Tien Fong Chee5ca878b2018-07-06 16:28:03 +080028DECLARE_GLOBAL_DATA_PTR;
29
Tien Fong Cheee8b79702018-12-10 21:29:44 +080030/**
31 * struct firmware - A place for storing firmware and its attribute data.
32 *
33 * This holds information about a firmware and its content.
34 *
35 * @size: Size of a file
36 * @data: Buffer for file
37 * @priv: Firmware loader private fields
38 * @name: Filename
39 * @offset: Offset of reading a file
40 */
41struct firmware {
42 size_t size;
43 const u8 *data;
44 const char *name;
45 u32 offset;
Tien Fong Chee5ca878b2018-07-06 16:28:03 +080046};
47
48#ifdef CONFIG_CMD_UBIFS
49static int mount_ubifs(char *mtdpart, char *ubivol)
50{
51 int ret = ubi_part(mtdpart, NULL);
52
53 if (ret) {
54 debug("Cannot find mtd partition %s\n", mtdpart);
55 return ret;
56 }
57
58 return cmd_ubifs_mount(ubivol);
59}
60
61static int umount_ubifs(void)
62{
63 return cmd_ubifs_umount();
64}
65#else
66static int mount_ubifs(char *mtdpart, char *ubivol)
67{
68 debug("Error: Cannot load image: no UBIFS support\n");
69 return -ENOSYS;
70}
71#endif
72
Simon Glassb75b15b2020-12-03 16:55:23 -070073static int select_fs_dev(struct device_plat *plat)
Tien Fong Chee5ca878b2018-07-06 16:28:03 +080074{
75 int ret;
76
77 if (plat->phandlepart.phandle) {
78 ofnode node;
79
80 node = ofnode_get_by_phandle(plat->phandlepart.phandle);
81
Tien Fong Chee5ca878b2018-07-06 16:28:03 +080082 struct udevice *dev;
83
Keerthyb5cdbd82018-11-05 11:34:53 +053084 ret = device_get_global_by_ofnode(node, &dev);
Tien Fong Chee5ca878b2018-07-06 16:28:03 +080085 if (!ret) {
86 struct blk_desc *desc = blk_get_by_device(dev);
87 if (desc) {
88 ret = fs_set_blk_dev_with_part(desc,
89 plat->phandlepart.partition);
90 } else {
91 debug("%s: No device found\n", __func__);
92 return -ENODEV;
93 }
94 }
95 } else if (plat->mtdpart && plat->ubivol) {
96 ret = mount_ubifs(plat->mtdpart, plat->ubivol);
97 if (ret)
98 return ret;
99
100 ret = fs_set_blk_dev("ubi", NULL, FS_TYPE_UBIFS);
101 } else {
102 debug("Error: unsupported storage device.\n");
103 return -ENODEV;
104 }
105
106 if (ret)
107 debug("Error: could not access storage.\n");
108
109 return ret;
110}
111
112/**
113 * _request_firmware_prepare - Prepare firmware struct.
114 *
Tien Fong Cheee8b79702018-12-10 21:29:44 +0800115 * @dev: An instance of a driver.
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800116 * @name: Name of firmware file.
117 * @dbuf: Address of buffer to load firmware into.
118 * @size: Size of buffer.
119 * @offset: Offset of a file for start reading into buffer.
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800120 *
121 * Return: Negative value if fail, 0 for successful.
122 */
Tien Fong Cheee8b79702018-12-10 21:29:44 +0800123static int _request_firmware_prepare(struct udevice *dev,
124 const char *name, void *dbuf,
125 size_t size, u32 offset)
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800126{
127 if (!name || name[0] == '\0')
128 return -EINVAL;
129
Tien Fong Cheee8b79702018-12-10 21:29:44 +0800130 struct firmware *firmwarep = dev_get_priv(dev);
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800131
Tien Fong Cheee8b79702018-12-10 21:29:44 +0800132 if (!firmwarep)
133 return -ENOMEM;
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800134
Tien Fong Cheee8b79702018-12-10 21:29:44 +0800135 firmwarep->name = name;
136 firmwarep->offset = offset;
137 firmwarep->data = dbuf;
138 firmwarep->size = size;
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800139
140 return 0;
141}
142
143/**
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800144 * fw_get_filesystem_firmware - load firmware into an allocated buffer.
Tien Fong Cheee8b79702018-12-10 21:29:44 +0800145 * @dev: An instance of a driver.
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800146 *
147 * Return: Size of total read, negative value when error.
148 */
Tien Fong Cheee8b79702018-12-10 21:29:44 +0800149static int fw_get_filesystem_firmware(struct udevice *dev)
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800150{
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800151 loff_t actread;
152 char *storage_interface, *dev_part, *ubi_mtdpart, *ubi_volume;
153 int ret;
154
155 storage_interface = env_get("storage_interface");
156 dev_part = env_get("fw_dev_part");
157 ubi_mtdpart = env_get("fw_ubi_mtdpart");
158 ubi_volume = env_get("fw_ubi_volume");
159
160 if (storage_interface && dev_part) {
161 ret = fs_set_blk_dev(storage_interface, dev_part, FS_TYPE_ANY);
162 } else if (storage_interface && ubi_mtdpart && ubi_volume) {
163 ret = mount_ubifs(ubi_mtdpart, ubi_volume);
164 if (ret)
165 return ret;
166
167 if (!strcmp("ubi", storage_interface))
168 ret = fs_set_blk_dev(storage_interface, NULL,
169 FS_TYPE_UBIFS);
170 else
171 ret = -ENODEV;
172 } else {
Simon Glass95588622020-12-22 19:30:28 -0700173 ret = select_fs_dev(dev_get_plat(dev));
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800174 }
175
176 if (ret)
177 goto out;
178
Tien Fong Cheee8b79702018-12-10 21:29:44 +0800179 struct firmware *firmwarep = dev_get_priv(dev);
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800180
Tien Fong Cheee8b79702018-12-10 21:29:44 +0800181 if (!firmwarep)
182 return -ENOMEM;
183
184 ret = fs_read(firmwarep->name, (ulong)map_to_sysmem(firmwarep->data),
185 firmwarep->offset, firmwarep->size, &actread);
Keerthyb5cdbd82018-11-05 11:34:53 +0530186
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800187 if (ret) {
Keerthy0bc20852018-11-05 11:34:54 +0530188 debug("Error: %d Failed to read %s from flash %lld != %zu.\n",
Tien Fong Cheee8b79702018-12-10 21:29:44 +0800189 ret, firmwarep->name, actread, firmwarep->size);
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800190 } else {
191 ret = actread;
192 }
193
194out:
195#ifdef CONFIG_CMD_UBIFS
196 umount_ubifs();
197#endif
198 return ret;
199}
200
201/**
202 * request_firmware_into_buf - Load firmware into a previously allocated buffer.
Tien Fong Cheee8b79702018-12-10 21:29:44 +0800203 * @dev: An instance of a driver.
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800204 * @name: Name of firmware file.
205 * @buf: Address of buffer to load firmware into.
206 * @size: Size of buffer.
207 * @offset: Offset of a file for start reading into buffer.
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800208 *
Tien Fong Cheee8b79702018-12-10 21:29:44 +0800209 * The firmware is loaded directly into the buffer pointed to by @buf.
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800210 *
211 * Return: Size of total read, negative value when error.
212 */
Tien Fong Cheee8b79702018-12-10 21:29:44 +0800213int request_firmware_into_buf(struct udevice *dev,
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800214 const char *name,
Tien Fong Cheee8b79702018-12-10 21:29:44 +0800215 void *buf, size_t size, u32 offset)
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800216{
217 int ret;
218
Tien Fong Cheee8b79702018-12-10 21:29:44 +0800219 if (!dev)
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800220 return -EINVAL;
221
Tien Fong Cheee8b79702018-12-10 21:29:44 +0800222 ret = _request_firmware_prepare(dev, name, buf, size, offset);
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800223 if (ret < 0) /* error */
224 return ret;
225
Tien Fong Cheee8b79702018-12-10 21:29:44 +0800226 ret = fw_get_filesystem_firmware(dev);
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800227
228 return ret;
229}
230
Simon Glassaad29ae2020-12-03 16:55:21 -0700231static int fs_loader_of_to_plat(struct udevice *dev)
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800232{
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800233 u32 phandlepart[2];
234
Tien Fong Chee789aa9c2019-03-05 23:29:38 +0800235 ofnode fs_loader_node = dev_ofnode(dev);
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800236
Tien Fong Chee789aa9c2019-03-05 23:29:38 +0800237 if (ofnode_valid(fs_loader_node)) {
Simon Glassb75b15b2020-12-03 16:55:23 -0700238 struct device_plat *plat;
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800239
Simon Glass95588622020-12-22 19:30:28 -0700240 plat = dev_get_plat(dev);
Tien Fong Chee789aa9c2019-03-05 23:29:38 +0800241 if (!ofnode_read_u32_array(fs_loader_node,
242 "phandlepart",
243 phandlepart, 2)) {
244 plat->phandlepart.phandle = phandlepart[0];
245 plat->phandlepart.partition = phandlepart[1];
246 }
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800247
Tien Fong Chee789aa9c2019-03-05 23:29:38 +0800248 plat->mtdpart = (char *)ofnode_read_string(
249 fs_loader_node, "mtdpart");
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800250
Tien Fong Chee789aa9c2019-03-05 23:29:38 +0800251 plat->ubivol = (char *)ofnode_read_string(
252 fs_loader_node, "ubivol");
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800253 }
254
255 return 0;
256}
257
258static int fs_loader_probe(struct udevice *dev)
259{
Tien Fong Chee5689ff12019-01-31 19:34:13 +0800260#if CONFIG_IS_ENABLED(DM) && CONFIG_IS_ENABLED(BLK)
261 int ret;
Simon Glass95588622020-12-22 19:30:28 -0700262 struct device_plat *plat = dev_get_plat(dev);
Tien Fong Chee5689ff12019-01-31 19:34:13 +0800263
264 if (plat->phandlepart.phandle) {
265 ofnode node = ofnode_get_by_phandle(plat->phandlepart.phandle);
266 struct udevice *parent_dev = NULL;
267
268 ret = device_get_global_by_ofnode(node, &parent_dev);
269 if (!ret) {
270 struct udevice *dev;
271
272 ret = blk_get_from_parent(parent_dev, &dev);
273 if (ret) {
274 debug("fs_loader: No block device: %d\n",
275 ret);
276
277 return ret;
278 }
279 }
280 }
281#endif
282
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800283 return 0;
284};
285
286static const struct udevice_id fs_loader_ids[] = {
287 { .compatible = "u-boot,fs-loader"},
288 { }
289};
290
291U_BOOT_DRIVER(fs_loader) = {
292 .name = "fs-loader",
293 .id = UCLASS_FS_FIRMWARE_LOADER,
294 .of_match = fs_loader_ids,
295 .probe = fs_loader_probe,
Simon Glassaad29ae2020-12-03 16:55:21 -0700296 .of_to_plat = fs_loader_of_to_plat,
Simon Glassb75b15b2020-12-03 16:55:23 -0700297 .plat_auto = sizeof(struct device_plat),
Simon Glass8a2b47f2020-12-03 16:55:17 -0700298 .priv_auto = sizeof(struct firmware),
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800299};
300
Sean Anderson5cd0cb32022-12-29 11:52:59 -0500301static struct device_plat default_plat = { 0 };
302
303int get_fs_loader(struct udevice **dev)
304{
305 int ret;
306 ofnode node = ofnode_get_chosen_node("firmware-loader");
307
308 if (ofnode_valid(node))
309 return uclass_get_device_by_ofnode(UCLASS_FS_FIRMWARE_LOADER,
310 node, dev);
311
312 /* Try the first device if none was chosen */
313 ret = uclass_first_device_err(UCLASS_FS_FIRMWARE_LOADER, dev);
314 if (ret != -ENODEV)
315 return ret;
316
317 /* Just create a new device */
Sean Anderson7edccc42023-09-30 16:45:46 -0400318 ret = device_bind(dm_root(), DM_DRIVER_REF(fs_loader), "default-loader",
Sean Anderson5cd0cb32022-12-29 11:52:59 -0500319 &default_plat, ofnode_null(), dev);
320 if (ret)
321 return ret;
322
323 return device_probe(*dev);
324}
325
Tien Fong Chee5ca878b2018-07-06 16:28:03 +0800326UCLASS_DRIVER(fs_loader) = {
327 .id = UCLASS_FS_FIRMWARE_LOADER,
328 .name = "fs-loader",
329};