Merge branch 'master' of git://git.denx.de/u-boot
[oweals/u-boot.git] / common / splash_source.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * (C) Copyright 2014 CompuLab, Ltd. <www.compulab.co.il>
4  *
5  * Authors: Igor Grinberg <grinberg@compulab.co.il>
6  */
7
8 #include <common.h>
9 #include <bmp_layout.h>
10 #include <command.h>
11 #include <env.h>
12 #include <errno.h>
13 #include <fs.h>
14 #include <fdt_support.h>
15 #include <image.h>
16 #include <nand.h>
17 #include <sata.h>
18 #include <spi.h>
19 #include <spi_flash.h>
20 #include <splash.h>
21 #include <usb.h>
22
23 DECLARE_GLOBAL_DATA_PTR;
24
25 #ifdef CONFIG_SPI_FLASH
26 static struct spi_flash *sf;
27 static int splash_sf_read_raw(u32 bmp_load_addr, int offset, size_t read_size)
28 {
29         if (!sf) {
30                 sf = spi_flash_probe(CONFIG_SF_DEFAULT_BUS,
31                                      CONFIG_SF_DEFAULT_CS,
32                                      CONFIG_SF_DEFAULT_SPEED,
33                                      CONFIG_SF_DEFAULT_MODE);
34                 if (!sf)
35                         return -ENODEV;
36         }
37
38         return spi_flash_read(sf, offset, read_size, (void *)bmp_load_addr);
39 }
40 #else
41 static int splash_sf_read_raw(u32 bmp_load_addr, int offset, size_t read_size)
42 {
43         debug("%s: sf support not available\n", __func__);
44         return -ENOSYS;
45 }
46 #endif
47
48 #ifdef CONFIG_CMD_NAND
49 static int splash_nand_read_raw(u32 bmp_load_addr, int offset, size_t read_size)
50 {
51         struct mtd_info *mtd = get_nand_dev_by_index(nand_curr_device);
52         return nand_read_skip_bad(mtd, offset,
53                                   &read_size, NULL,
54                                   mtd->size,
55                                   (u_char *)bmp_load_addr);
56 }
57 #else
58 static int splash_nand_read_raw(u32 bmp_load_addr, int offset, size_t read_size)
59 {
60         debug("%s: nand support not available\n", __func__);
61         return -ENOSYS;
62 }
63 #endif
64
65 static int splash_storage_read_raw(struct splash_location *location,
66                                u32 bmp_load_addr, size_t read_size)
67 {
68         u32 offset;
69
70         if (!location)
71                 return -EINVAL;
72
73         offset = location->offset;
74         switch (location->storage) {
75         case SPLASH_STORAGE_NAND:
76                 return splash_nand_read_raw(bmp_load_addr, offset, read_size);
77         case SPLASH_STORAGE_SF:
78                 return splash_sf_read_raw(bmp_load_addr, offset, read_size);
79         default:
80                 printf("Unknown splash location\n");
81         }
82
83         return -EINVAL;
84 }
85
86 static int splash_load_raw(struct splash_location *location, u32 bmp_load_addr)
87 {
88         struct bmp_header *bmp_hdr;
89         int res;
90         size_t bmp_size, bmp_header_size = sizeof(struct bmp_header);
91
92         if (bmp_load_addr + bmp_header_size >= gd->start_addr_sp)
93                 goto splash_address_too_high;
94
95         res = splash_storage_read_raw(location, bmp_load_addr, bmp_header_size);
96         if (res < 0)
97                 return res;
98
99         bmp_hdr = (struct bmp_header *)bmp_load_addr;
100         bmp_size = le32_to_cpu(bmp_hdr->file_size);
101
102         if (bmp_load_addr + bmp_size >= gd->start_addr_sp)
103                 goto splash_address_too_high;
104
105         return splash_storage_read_raw(location, bmp_load_addr, bmp_size);
106
107 splash_address_too_high:
108         printf("Error: splashimage address too high. Data overwrites U-Boot and/or placed beyond DRAM boundaries.\n");
109
110         return -EFAULT;
111 }
112
113 static int splash_select_fs_dev(struct splash_location *location)
114 {
115         int res;
116
117         switch (location->storage) {
118         case SPLASH_STORAGE_MMC:
119                 res = fs_set_blk_dev("mmc", location->devpart, FS_TYPE_ANY);
120                 break;
121         case SPLASH_STORAGE_USB:
122                 res = fs_set_blk_dev("usb", location->devpart, FS_TYPE_ANY);
123                 break;
124         case SPLASH_STORAGE_SATA:
125                 res = fs_set_blk_dev("sata", location->devpart, FS_TYPE_ANY);
126                 break;
127         case SPLASH_STORAGE_NAND:
128                 if (location->ubivol != NULL)
129                         res = fs_set_blk_dev("ubi", NULL, FS_TYPE_UBIFS);
130                 else
131                         res = -ENODEV;
132                 break;
133         default:
134                 printf("Error: unsupported location storage.\n");
135                 return -ENODEV;
136         }
137
138         if (res)
139                 printf("Error: could not access storage.\n");
140
141         return res;
142 }
143
144 #ifdef CONFIG_USB_STORAGE
145 static int splash_init_usb(void)
146 {
147         int err;
148
149         err = usb_init();
150         if (err)
151                 return err;
152
153 #ifndef CONFIG_DM_USB
154         err = usb_stor_scan(1) < 0 ? -ENODEV : 0;
155 #endif
156
157         return err;
158 }
159 #else
160 static inline int splash_init_usb(void)
161 {
162         printf("Cannot load splash image: no USB support\n");
163         return -ENOSYS;
164 }
165 #endif
166
167 #ifdef CONFIG_SATA
168 static int splash_init_sata(void)
169 {
170         return sata_probe(0);
171 }
172 #else
173 static inline int splash_init_sata(void)
174 {
175         printf("Cannot load splash image: no SATA support\n");
176         return -ENOSYS;
177 }
178 #endif
179
180 #ifdef CONFIG_CMD_UBIFS
181 static int splash_mount_ubifs(struct splash_location *location)
182 {
183         int res;
184         char cmd[32];
185
186         sprintf(cmd, "ubi part %s", location->mtdpart);
187         res = run_command(cmd, 0);
188         if (res)
189                 return res;
190
191         sprintf(cmd, "ubifsmount %s", location->ubivol);
192         res = run_command(cmd, 0);
193
194         return res;
195 }
196
197 static inline int splash_umount_ubifs(void)
198 {
199         return run_command("ubifsumount", 0);
200 }
201 #else
202 static inline int splash_mount_ubifs(struct splash_location *location)
203 {
204         printf("Cannot load splash image: no UBIFS support\n");
205         return -ENOSYS;
206 }
207
208 static inline int splash_umount_ubifs(void)
209 {
210         printf("Cannot unmount UBIFS: no UBIFS support\n");
211         return -ENOSYS;
212 }
213 #endif
214
215 #define SPLASH_SOURCE_DEFAULT_FILE_NAME         "splash.bmp"
216
217 static int splash_load_fs(struct splash_location *location, u32 bmp_load_addr)
218 {
219         int res = 0;
220         loff_t bmp_size;
221         loff_t actread;
222         char *splash_file;
223
224         splash_file = env_get("splashfile");
225         if (!splash_file)
226                 splash_file = SPLASH_SOURCE_DEFAULT_FILE_NAME;
227
228         if (location->storage == SPLASH_STORAGE_USB)
229                 res = splash_init_usb();
230
231         if (location->storage == SPLASH_STORAGE_SATA)
232                 res = splash_init_sata();
233
234         if (location->ubivol != NULL)
235                 res = splash_mount_ubifs(location);
236
237         if (res)
238                 return res;
239
240         res = splash_select_fs_dev(location);
241         if (res)
242                 goto out;
243
244         res = fs_size(splash_file, &bmp_size);
245         if (res) {
246                 printf("Error (%d): cannot determine file size\n", res);
247                 goto out;
248         }
249
250         if (bmp_load_addr + bmp_size >= gd->start_addr_sp) {
251                 printf("Error: splashimage address too high. Data overwrites U-Boot and/or placed beyond DRAM boundaries.\n");
252                 res = -EFAULT;
253                 goto out;
254         }
255
256         splash_select_fs_dev(location);
257         res = fs_read(splash_file, bmp_load_addr, 0, 0, &actread);
258
259 out:
260         if (location->ubivol != NULL)
261                 splash_umount_ubifs();
262
263         return res;
264 }
265
266 /**
267  * select_splash_location - return the splash location based on board support
268  *                          and env variable "splashsource".
269  *
270  * @locations:          An array of supported splash locations.
271  * @size:               Size of splash_locations array.
272  *
273  * @return: If a null set of splash locations is given, or
274  *          splashsource env variable is set to unsupported value
275  *                      return NULL.
276  *          If splashsource env variable is not defined
277  *                      return the first entry in splash_locations as default.
278  *          If splashsource env variable contains a supported value
279  *                      return the location selected by splashsource.
280  */
281 static struct splash_location *select_splash_location(
282                             struct splash_location *locations, uint size)
283 {
284         int i;
285         char *env_splashsource;
286
287         if (!locations || size == 0)
288                 return NULL;
289
290         env_splashsource = env_get("splashsource");
291         if (env_splashsource == NULL)
292                 return &locations[0];
293
294         for (i = 0; i < size; i++) {
295                 if (!strcmp(locations[i].name, env_splashsource))
296                         return &locations[i];
297         }
298
299         printf("splashsource env variable set to unsupported value\n");
300         return NULL;
301 }
302
303 #ifdef CONFIG_FIT
304 static int splash_load_fit(struct splash_location *location, u32 bmp_load_addr)
305 {
306         int res;
307         int node_offset;
308         const char *splash_file;
309         const void *internal_splash_data;
310         size_t internal_splash_size;
311         int external_splash_addr;
312         int external_splash_size;
313         bool is_splash_external = false;
314         struct image_header *img_header;
315         const u32 *fit_header;
316         u32 fit_size;
317         const size_t header_size = sizeof(struct image_header);
318
319         /* Read in image header */
320         res = splash_storage_read_raw(location, bmp_load_addr, header_size);
321         if (res < 0)
322                 return res;
323
324         img_header = (struct image_header *)bmp_load_addr;
325         if (image_get_magic(img_header) != FDT_MAGIC) {
326                 printf("Could not find FDT magic\n");
327                 return -EINVAL;
328         }
329
330         fit_size = fdt_totalsize(img_header);
331
332         /* Read in entire FIT */
333         fit_header = (const u32 *)(bmp_load_addr + header_size);
334         res = splash_storage_read_raw(location, (u32)fit_header, fit_size);
335         if (res < 0)
336                 return res;
337
338         res = fit_check_format(fit_header);
339         if (!res) {
340                 debug("Could not find valid FIT image\n");
341                 return -EINVAL;
342         }
343
344         /* Get the splash image node */
345         splash_file = env_get("splashfile");
346         if (!splash_file)
347                 splash_file = SPLASH_SOURCE_DEFAULT_FILE_NAME;
348
349         node_offset = fit_image_get_node(fit_header, splash_file);
350         if (node_offset < 0) {
351                 debug("Could not find splash image '%s' in FIT\n",
352                       splash_file);
353                 return -ENOENT;
354         }
355
356         /* Extract the splash data from FIT */
357         /* 1. Test if splash is in FIT internal data. */
358         if (!fit_image_get_data(fit_header, node_offset, &internal_splash_data, &internal_splash_size))
359                 memmove((void *)bmp_load_addr, internal_splash_data, internal_splash_size);
360         /* 2. Test if splash is in FIT external data with fixed position. */
361         else if (!fit_image_get_data_position(fit_header, node_offset, &external_splash_addr))
362                 is_splash_external = true;
363         /* 3. Test if splash is in FIT external data with offset. */
364         else if (!fit_image_get_data_offset(fit_header, node_offset, &external_splash_addr)) {
365                 /* Align data offset to 4-byte boundary */
366                 fit_size = ALIGN(fdt_totalsize(fit_header), 4);
367                 /* External splash offset means the offset by end of FIT header */
368                 external_splash_addr += location->offset + fit_size;
369                 is_splash_external = true;
370         } else {
371                 printf("Failed to get splash image from FIT\n");
372                 return -ENODATA;
373         }
374
375         if (is_splash_external) {
376                 res = fit_image_get_data_size(fit_header, node_offset, &external_splash_size);
377                 if (res < 0) {
378                         printf("Failed to get size of splash image (err=%d)\n", res);
379                         return res;
380                 }
381
382                 /* Read in the splash data */
383                 location->offset = external_splash_addr;
384                 res = splash_storage_read_raw(location, bmp_load_addr, external_splash_size);
385                 if (res < 0)
386                         return res;
387         }
388
389         return 0;
390 }
391 #endif /* CONFIG_FIT */
392
393 /**
394  * splash_source_load - load splash image from a supported location.
395  *
396  * Select a splash image location based on the value of splashsource environment
397  * variable and the board supported splash source locations, and load a
398  * splashimage to the address pointed to by splashimage environment variable.
399  *
400  * @locations:          An array of supported splash locations.
401  * @size:               Size of splash_locations array.
402  *
403  * @return: 0 on success, negative value on failure.
404  */
405 int splash_source_load(struct splash_location *locations, uint size)
406 {
407         struct splash_location *splash_location;
408         char *env_splashimage_value;
409         u32 bmp_load_addr;
410
411         env_splashimage_value = env_get("splashimage");
412         if (env_splashimage_value == NULL)
413                 return -ENOENT;
414
415         bmp_load_addr = simple_strtoul(env_splashimage_value, 0, 16);
416         if (bmp_load_addr == 0) {
417                 printf("Error: bad splashimage address specified\n");
418                 return -EFAULT;
419         }
420
421         splash_location = select_splash_location(locations, size);
422         if (!splash_location)
423                 return -EINVAL;
424
425         if (splash_location->flags == SPLASH_STORAGE_RAW)
426                 return splash_load_raw(splash_location, bmp_load_addr);
427         else if (splash_location->flags == SPLASH_STORAGE_FS)
428                 return splash_load_fs(splash_location, bmp_load_addr);
429 #ifdef CONFIG_FIT
430         else if (splash_location->flags == SPLASH_STORAGE_FIT)
431                 return splash_load_fit(splash_location, bmp_load_addr);
432 #endif
433         return -EINVAL;
434 }