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