2 * Flattened Image Tree loader.
4 * Copyright (c) 2016 Imagination Technologies
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
20 #include "qemu/osdep.h"
21 #include "qapi/error.h"
22 #include "qemu/units.h"
23 #include "exec/memory.h"
24 #include "hw/loader.h"
25 #include "hw/loader-fit.h"
26 #include "qemu/cutils.h"
27 #include "qemu/error-report.h"
28 #include "sysemu/device_tree.h"
33 #define FIT_LOADER_MAX_PATH (128)
35 static const void *fit_load_image_alloc(const void *itb
, const char *name
,
36 int *poff
, size_t *psz
, Error
**errp
)
41 char path
[FIT_LOADER_MAX_PATH
];
45 snprintf(path
, sizeof(path
), "/images/%s", name
);
47 off
= fdt_path_offset(itb
, path
);
49 error_setg(errp
, "can't find node %s", path
);
56 data
= fdt_getprop(itb
, off
, "data", &sz
);
58 error_setg(errp
, "can't get %s/data", path
);
62 comp
= fdt_getprop(itb
, off
, "compression", NULL
);
63 if (!comp
|| !strcmp(comp
, "none")) {
67 uncomp_data
= g_malloc(sz
);
68 memmove(uncomp_data
, data
, sz
);
72 if (!strcmp(comp
, "gzip")) {
73 uncomp_len
= UBOOT_MAX_GUNZIP_BYTES
;
74 uncomp_data
= g_malloc(uncomp_len
);
76 uncomp_len
= gunzip(uncomp_data
, uncomp_len
, (void *) data
, sz
);
78 error_setg(errp
, "unable to decompress %s image", name
);
83 data
= g_realloc(uncomp_data
, uncomp_len
);
90 error_setg(errp
, "unknown compression '%s'", comp
);
94 static int fit_image_addr(const void *itb
, int img
, const char *name
,
95 hwaddr
*addr
, Error
**errp
)
100 prop
= fdt_getprop(itb
, img
, name
, &len
);
102 error_setg(errp
, "can't find %s address", name
);
108 *addr
= fdt32_to_cpu(*(fdt32_t
*)prop
);
111 *addr
= fdt64_to_cpu(*(fdt64_t
*)prop
);
114 error_setg(errp
, "invalid %s address length %d", name
, len
);
119 static int fit_load_kernel(const struct fit_loader
*ldr
, const void *itb
,
120 int cfg
, void *opaque
, hwaddr
*pend
,
126 const void *load_data
;
127 hwaddr load_addr
, entry_addr
;
132 name
= fdt_getprop(itb
, cfg
, "kernel", NULL
);
134 error_setg(errp
, "no kernel specified by FIT configuration");
138 load_data
= data
= fit_load_image_alloc(itb
, name
, &img_off
, &sz
, errp
);
140 error_prepend(errp
, "unable to load kernel image from FIT: ");
144 err
= fit_image_addr(itb
, img_off
, "load", &load_addr
, errp
);
146 error_prepend(errp
, "unable to read kernel load address from FIT: ");
151 err
= fit_image_addr(itb
, img_off
, "entry", &entry_addr
, errp
);
153 error_prepend(errp
, "unable to read kernel entry address from FIT: ");
158 if (ldr
->kernel_filter
) {
159 load_data
= ldr
->kernel_filter(opaque
, data
, &load_addr
, &entry_addr
);
163 *pend
= load_addr
+ sz
;
166 load_addr
= ldr
->addr_to_phys(opaque
, load_addr
);
167 rom_add_blob_fixed(name
, load_data
, sz
, load_addr
);
171 g_free((void *) data
);
172 if (data
!= load_data
) {
173 g_free((void *) load_data
);
178 static int fit_load_fdt(const struct fit_loader
*ldr
, const void *itb
,
179 int cfg
, void *opaque
, const void *match_data
,
180 hwaddr kernel_end
, Error
**errp
)
186 const void *load_data
;
192 name
= fdt_getprop(itb
, cfg
, "fdt", NULL
);
197 load_data
= data
= fit_load_image_alloc(itb
, name
, &img_off
, &sz
, errp
);
199 error_prepend(errp
, "unable to load FDT image from FIT: ");
203 ret
= fit_image_addr(itb
, img_off
, "load", &load_addr
, &err
);
204 if (ret
== -ENOENT
) {
205 load_addr
= ROUND_UP(kernel_end
, 64 * KiB
) + (10 * MiB
);
208 error_propagate_prepend(errp
, err
,
209 "unable to read FDT load address from FIT: ");
213 if (ldr
->fdt_filter
) {
214 load_data
= ldr
->fdt_filter(opaque
, data
, match_data
, &load_addr
);
217 load_addr
= ldr
->addr_to_phys(opaque
, load_addr
);
218 sz
= fdt_totalsize(load_data
);
219 rom_add_blob_fixed(name
, load_data
, sz
, load_addr
);
223 g_free((void *) data
);
224 if (data
!= load_data
) {
225 g_free((void *) load_data
);
230 static bool fit_cfg_compatible(const void *itb
, int cfg
, const char *compat
)
233 const char *fdt_name
;
236 fdt_name
= fdt_getprop(itb
, cfg
, "fdt", NULL
);
241 fdt
= fit_load_image_alloc(itb
, fdt_name
, NULL
, NULL
, NULL
);
246 if (fdt_check_header(fdt
)) {
251 if (fdt_node_check_compatible(fdt
, 0, compat
)) {
258 g_free((void *) fdt
);
262 int load_fit(const struct fit_loader
*ldr
, const char *filename
, void *opaque
)
265 const struct fit_loader_match
*match
;
266 const void *itb
, *match_data
= NULL
;
267 const char *def_cfg_name
;
268 char path
[FIT_LOADER_MAX_PATH
];
269 int itb_size
, configs
, cfg_off
, off
;
270 hwaddr kernel_end
= 0;
273 itb
= load_device_tree(filename
, &itb_size
);
278 configs
= fdt_path_offset(itb
, "/configurations");
280 error_report("can't find node /configurations");
285 cfg_off
= -FDT_ERR_NOTFOUND
;
288 for (match
= ldr
->matches
; match
->compatible
; match
++) {
289 off
= fdt_first_subnode(itb
, configs
);
291 if (fit_cfg_compatible(itb
, off
, match
->compatible
)) {
293 match_data
= match
->data
;
297 off
= fdt_next_subnode(itb
, off
);
307 def_cfg_name
= fdt_getprop(itb
, configs
, "default", NULL
);
309 snprintf(path
, sizeof(path
), "/configurations/%s", def_cfg_name
);
310 cfg_off
= fdt_path_offset(itb
, path
);
315 error_report("can't find configuration");
320 ret
= fit_load_kernel(ldr
, itb
, cfg_off
, opaque
, &kernel_end
, &err
);
322 error_report_err(err
);
326 ret
= fit_load_fdt(ldr
, itb
, cfg_off
, opaque
, match_data
, kernel_end
,
329 error_report_err(err
);
335 g_free((void *) itb
);