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 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
,
125 const void *load_data
;
126 hwaddr load_addr
, entry_addr
;
131 name
= fdt_getprop(itb
, cfg
, "kernel", NULL
);
133 error_setg(errp
, "no kernel specified by FIT configuration");
137 load_data
= data
= fit_load_image_alloc(itb
, name
, &img_off
, &sz
, errp
);
139 error_prepend(errp
, "unable to load kernel image from FIT: ");
143 err
= fit_image_addr(itb
, img_off
, "load", &load_addr
, errp
);
145 error_prepend(errp
, "unable to read kernel load address from FIT: ");
150 err
= fit_image_addr(itb
, img_off
, "entry", &entry_addr
, errp
);
152 error_prepend(errp
, "unable to read kernel entry address from FIT: ");
157 if (ldr
->kernel_filter
) {
158 load_data
= ldr
->kernel_filter(opaque
, data
, &load_addr
, &entry_addr
);
162 *pend
= load_addr
+ sz
;
165 load_addr
= ldr
->addr_to_phys(opaque
, load_addr
);
166 rom_add_blob_fixed(name
, load_data
, sz
, load_addr
);
170 g_free((void *) data
);
171 if (data
!= load_data
) {
172 g_free((void *) load_data
);
177 static int fit_load_fdt(const struct fit_loader
*ldr
, const void *itb
,
178 int cfg
, void *opaque
, const void *match_data
,
179 hwaddr kernel_end
, Error
**errp
)
184 const void *load_data
;
190 name
= fdt_getprop(itb
, cfg
, "fdt", NULL
);
195 load_data
= data
= fit_load_image_alloc(itb
, name
, &img_off
, &sz
, errp
);
197 error_prepend(errp
, "unable to load FDT image from FIT: ");
201 ret
= fit_image_addr(itb
, img_off
, "load", &load_addr
, &err
);
202 if (ret
== -ENOENT
) {
203 load_addr
= ROUND_UP(kernel_end
, 64 * KiB
) + (10 * MiB
);
206 error_propagate_prepend(errp
, err
,
207 "unable to read FDT load address from FIT: ");
211 if (ldr
->fdt_filter
) {
212 load_data
= ldr
->fdt_filter(opaque
, data
, match_data
, &load_addr
);
215 load_addr
= ldr
->addr_to_phys(opaque
, load_addr
);
216 sz
= fdt_totalsize(load_data
);
217 rom_add_blob_fixed(name
, load_data
, sz
, load_addr
);
221 g_free((void *) data
);
222 if (data
!= load_data
) {
223 g_free((void *) load_data
);
228 static bool fit_cfg_compatible(const void *itb
, int cfg
, const char *compat
)
231 const char *fdt_name
;
234 fdt_name
= fdt_getprop(itb
, cfg
, "fdt", NULL
);
239 fdt
= fit_load_image_alloc(itb
, fdt_name
, NULL
, NULL
, NULL
);
244 if (fdt_check_header(fdt
)) {
249 if (fdt_node_check_compatible(fdt
, 0, compat
)) {
256 g_free((void *) fdt
);
260 int load_fit(const struct fit_loader
*ldr
, const char *filename
, void *opaque
)
263 const struct fit_loader_match
*match
;
264 const void *itb
, *match_data
= NULL
;
265 const char *def_cfg_name
;
266 char path
[FIT_LOADER_MAX_PATH
];
267 int itb_size
, configs
, cfg_off
, off
;
271 itb
= load_device_tree(filename
, &itb_size
);
276 configs
= fdt_path_offset(itb
, "/configurations");
278 error_report("can't find node /configurations");
283 cfg_off
= -FDT_ERR_NOTFOUND
;
286 for (match
= ldr
->matches
; match
->compatible
; match
++) {
287 off
= fdt_first_subnode(itb
, configs
);
289 if (fit_cfg_compatible(itb
, off
, match
->compatible
)) {
291 match_data
= match
->data
;
295 off
= fdt_next_subnode(itb
, off
);
305 def_cfg_name
= fdt_getprop(itb
, configs
, "default", NULL
);
307 snprintf(path
, sizeof(path
), "/configurations/%s", def_cfg_name
);
308 cfg_off
= fdt_path_offset(itb
, path
);
313 error_report("can't find configuration");
318 ret
= fit_load_kernel(ldr
, itb
, cfg_off
, opaque
, &kernel_end
, &err
);
320 error_report_err(err
);
324 ret
= fit_load_fdt(ldr
, itb
, cfg_off
, opaque
, match_data
, kernel_end
,
327 error_report_err(err
);
333 g_free((void *) itb
);