]>
Commit | Line | Data |
---|---|---|
22929e12 PD |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
62030004 TFC |
3 | * Copyright (C) 2018 Intel Corporation <www.intel.com> |
4 | * | |
62030004 TFC |
5 | */ |
6 | #include <common.h> | |
7 | #include <dm.h> | |
8 | #include <errno.h> | |
9 | #include <blk.h> | |
10 | #include <fs.h> | |
11 | #include <fs_loader.h> | |
12 | #include <linux/string.h> | |
13 | #include <mapmem.h> | |
14 | #include <malloc.h> | |
15 | #include <spl.h> | |
16 | ||
17 | DECLARE_GLOBAL_DATA_PTR; | |
18 | ||
19 | struct firmware_priv { | |
20 | const char *name; /* Filename */ | |
21 | u32 offset; /* Offset of reading a file */ | |
22 | }; | |
23 | ||
24 | #ifdef CONFIG_CMD_UBIFS | |
25 | static int mount_ubifs(char *mtdpart, char *ubivol) | |
26 | { | |
27 | int ret = ubi_part(mtdpart, NULL); | |
28 | ||
29 | if (ret) { | |
30 | debug("Cannot find mtd partition %s\n", mtdpart); | |
31 | return ret; | |
32 | } | |
33 | ||
34 | return cmd_ubifs_mount(ubivol); | |
35 | } | |
36 | ||
37 | static int umount_ubifs(void) | |
38 | { | |
39 | return cmd_ubifs_umount(); | |
40 | } | |
41 | #else | |
42 | static int mount_ubifs(char *mtdpart, char *ubivol) | |
43 | { | |
44 | debug("Error: Cannot load image: no UBIFS support\n"); | |
45 | return -ENOSYS; | |
46 | } | |
47 | #endif | |
48 | ||
49 | static int select_fs_dev(struct device_platdata *plat) | |
50 | { | |
51 | int ret; | |
52 | ||
53 | if (plat->phandlepart.phandle) { | |
54 | ofnode node; | |
55 | ||
56 | node = ofnode_get_by_phandle(plat->phandlepart.phandle); | |
57 | ||
62030004 TFC |
58 | struct udevice *dev; |
59 | ||
7c096ea4 | 60 | ret = device_get_global_by_ofnode(node, &dev); |
62030004 TFC |
61 | if (!ret) { |
62 | struct blk_desc *desc = blk_get_by_device(dev); | |
63 | if (desc) { | |
64 | ret = fs_set_blk_dev_with_part(desc, | |
65 | plat->phandlepart.partition); | |
66 | } else { | |
67 | debug("%s: No device found\n", __func__); | |
68 | return -ENODEV; | |
69 | } | |
70 | } | |
71 | } else if (plat->mtdpart && plat->ubivol) { | |
72 | ret = mount_ubifs(plat->mtdpart, plat->ubivol); | |
73 | if (ret) | |
74 | return ret; | |
75 | ||
76 | ret = fs_set_blk_dev("ubi", NULL, FS_TYPE_UBIFS); | |
77 | } else { | |
78 | debug("Error: unsupported storage device.\n"); | |
79 | return -ENODEV; | |
80 | } | |
81 | ||
82 | if (ret) | |
83 | debug("Error: could not access storage.\n"); | |
84 | ||
85 | return ret; | |
86 | } | |
87 | ||
88 | /** | |
89 | * _request_firmware_prepare - Prepare firmware struct. | |
90 | * | |
91 | * @name: Name of firmware file. | |
92 | * @dbuf: Address of buffer to load firmware into. | |
93 | * @size: Size of buffer. | |
94 | * @offset: Offset of a file for start reading into buffer. | |
95 | * @firmwarep: Pointer to pointer to firmware image. | |
96 | * | |
97 | * Return: Negative value if fail, 0 for successful. | |
98 | */ | |
99 | static int _request_firmware_prepare(const char *name, void *dbuf, | |
100 | size_t size, u32 offset, | |
101 | struct firmware **firmwarep) | |
102 | { | |
103 | if (!name || name[0] == '\0') | |
104 | return -EINVAL; | |
105 | ||
106 | /* No memory allocation is required if *firmwarep is allocated */ | |
107 | if (!(*firmwarep)) { | |
108 | (*firmwarep) = calloc(1, sizeof(struct firmware)); | |
109 | if (!(*firmwarep)) | |
110 | return -ENOMEM; | |
111 | ||
112 | (*firmwarep)->priv = calloc(1, sizeof(struct firmware_priv)); | |
113 | if (!(*firmwarep)->priv) { | |
114 | free(*firmwarep); | |
115 | return -ENOMEM; | |
116 | } | |
117 | } else if (!(*firmwarep)->priv) { | |
118 | (*firmwarep)->priv = calloc(1, sizeof(struct firmware_priv)); | |
119 | if (!(*firmwarep)->priv) { | |
120 | free(*firmwarep); | |
121 | return -ENOMEM; | |
122 | } | |
123 | } | |
124 | ||
125 | ((struct firmware_priv *)((*firmwarep)->priv))->name = name; | |
126 | ((struct firmware_priv *)((*firmwarep)->priv))->offset = offset; | |
127 | (*firmwarep)->data = dbuf; | |
128 | (*firmwarep)->size = size; | |
129 | ||
130 | return 0; | |
131 | } | |
132 | ||
133 | /** | |
134 | * release_firmware - Release the resource associated with a firmware image | |
135 | * @firmware: Firmware resource to release | |
136 | */ | |
137 | void release_firmware(struct firmware *firmware) | |
138 | { | |
139 | if (firmware) { | |
140 | if (firmware->priv) { | |
141 | free(firmware->priv); | |
142 | firmware->priv = NULL; | |
143 | } | |
144 | free(firmware); | |
145 | } | |
146 | } | |
147 | ||
148 | /** | |
149 | * fw_get_filesystem_firmware - load firmware into an allocated buffer. | |
150 | * @plat: Platform data such as storage and partition firmware loading from. | |
151 | * @firmware: pointer to firmware image. | |
152 | * | |
153 | * Return: Size of total read, negative value when error. | |
154 | */ | |
155 | static int fw_get_filesystem_firmware(struct device_platdata *plat, | |
156 | struct firmware *firmware) | |
157 | { | |
158 | struct firmware_priv *fw_priv = NULL; | |
159 | loff_t actread; | |
160 | char *storage_interface, *dev_part, *ubi_mtdpart, *ubi_volume; | |
161 | int ret; | |
162 | ||
163 | storage_interface = env_get("storage_interface"); | |
164 | dev_part = env_get("fw_dev_part"); | |
165 | ubi_mtdpart = env_get("fw_ubi_mtdpart"); | |
166 | ubi_volume = env_get("fw_ubi_volume"); | |
167 | ||
168 | if (storage_interface && dev_part) { | |
169 | ret = fs_set_blk_dev(storage_interface, dev_part, FS_TYPE_ANY); | |
170 | } else if (storage_interface && ubi_mtdpart && ubi_volume) { | |
171 | ret = mount_ubifs(ubi_mtdpart, ubi_volume); | |
172 | if (ret) | |
173 | return ret; | |
174 | ||
175 | if (!strcmp("ubi", storage_interface)) | |
176 | ret = fs_set_blk_dev(storage_interface, NULL, | |
177 | FS_TYPE_UBIFS); | |
178 | else | |
179 | ret = -ENODEV; | |
180 | } else { | |
181 | ret = select_fs_dev(plat); | |
182 | } | |
183 | ||
184 | if (ret) | |
185 | goto out; | |
186 | ||
187 | fw_priv = firmware->priv; | |
188 | ||
189 | ret = fs_read(fw_priv->name, (ulong)map_to_sysmem(firmware->data), | |
190 | fw_priv->offset, firmware->size, &actread); | |
7c096ea4 | 191 | |
62030004 | 192 | if (ret) { |
907837d6 | 193 | debug("Error: %d Failed to read %s from flash %lld != %zu.\n", |
62030004 TFC |
194 | ret, fw_priv->name, actread, firmware->size); |
195 | } else { | |
196 | ret = actread; | |
197 | } | |
198 | ||
199 | out: | |
200 | #ifdef CONFIG_CMD_UBIFS | |
201 | umount_ubifs(); | |
202 | #endif | |
203 | return ret; | |
204 | } | |
205 | ||
206 | /** | |
207 | * request_firmware_into_buf - Load firmware into a previously allocated buffer. | |
208 | * @plat: Platform data such as storage and partition firmware loading from. | |
209 | * @name: Name of firmware file. | |
210 | * @buf: Address of buffer to load firmware into. | |
211 | * @size: Size of buffer. | |
212 | * @offset: Offset of a file for start reading into buffer. | |
213 | * @firmwarep: Pointer to firmware image. | |
214 | * | |
215 | * The firmware is loaded directly into the buffer pointed to by @buf and | |
216 | * the @firmwarep data member is pointed at @buf. | |
217 | * | |
218 | * Return: Size of total read, negative value when error. | |
219 | */ | |
220 | int request_firmware_into_buf(struct device_platdata *plat, | |
221 | const char *name, | |
222 | void *buf, size_t size, u32 offset, | |
223 | struct firmware **firmwarep) | |
224 | { | |
225 | int ret; | |
226 | ||
227 | if (!plat) | |
228 | return -EINVAL; | |
229 | ||
230 | ret = _request_firmware_prepare(name, buf, size, offset, firmwarep); | |
231 | if (ret < 0) /* error */ | |
232 | return ret; | |
233 | ||
234 | ret = fw_get_filesystem_firmware(plat, *firmwarep); | |
235 | ||
236 | return ret; | |
237 | } | |
238 | ||
239 | static int fs_loader_ofdata_to_platdata(struct udevice *dev) | |
240 | { | |
241 | const char *fs_loader_path; | |
242 | u32 phandlepart[2]; | |
243 | ||
244 | fs_loader_path = ofnode_get_chosen_prop("firmware-loader"); | |
245 | ||
246 | if (fs_loader_path) { | |
247 | ofnode fs_loader_node; | |
248 | ||
249 | fs_loader_node = ofnode_path(fs_loader_path); | |
250 | if (ofnode_valid(fs_loader_node)) { | |
251 | struct device_platdata *plat; | |
252 | plat = dev->platdata; | |
253 | ||
254 | if (!ofnode_read_u32_array(fs_loader_node, | |
255 | "phandlepart", | |
256 | phandlepart, 2)) { | |
257 | plat->phandlepart.phandle = phandlepart[0]; | |
258 | plat->phandlepart.partition = phandlepart[1]; | |
259 | } | |
260 | ||
261 | plat->mtdpart = (char *)ofnode_read_string( | |
262 | fs_loader_node, "mtdpart"); | |
263 | ||
264 | plat->ubivol = (char *)ofnode_read_string( | |
265 | fs_loader_node, "ubivol"); | |
266 | } | |
267 | } | |
268 | ||
269 | return 0; | |
270 | } | |
271 | ||
272 | static int fs_loader_probe(struct udevice *dev) | |
273 | { | |
274 | return 0; | |
275 | }; | |
276 | ||
277 | static const struct udevice_id fs_loader_ids[] = { | |
278 | { .compatible = "u-boot,fs-loader"}, | |
279 | { } | |
280 | }; | |
281 | ||
282 | U_BOOT_DRIVER(fs_loader) = { | |
283 | .name = "fs-loader", | |
284 | .id = UCLASS_FS_FIRMWARE_LOADER, | |
285 | .of_match = fs_loader_ids, | |
286 | .probe = fs_loader_probe, | |
287 | .ofdata_to_platdata = fs_loader_ofdata_to_platdata, | |
288 | .platdata_auto_alloc_size = sizeof(struct device_platdata), | |
289 | }; | |
290 | ||
291 | UCLASS_DRIVER(fs_loader) = { | |
292 | .id = UCLASS_FS_FIRMWARE_LOADER, | |
293 | .name = "fs-loader", | |
294 | }; |