qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

loader-fit.c (8633B)


      1 /*
      2  * Flattened Image Tree loader.
      3  *
      4  * Copyright (c) 2016 Imagination Technologies
      5  *
      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.
     10  *
     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.
     15  *
     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/>.
     18  */
     19 
     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"
     29 
     30 #include <libfdt.h>
     31 #include <zlib.h>
     32 
     33 #define FIT_LOADER_MAX_PATH (128)
     34 
     35 static const void *fit_load_image_alloc(const void *itb, const char *name,
     36                                         int *poff, size_t *psz, Error **errp)
     37 {
     38     const void *data;
     39     const char *comp;
     40     void *uncomp_data;
     41     char path[FIT_LOADER_MAX_PATH];
     42     int off, sz;
     43     ssize_t uncomp_len;
     44 
     45     snprintf(path, sizeof(path), "/images/%s", name);
     46 
     47     off = fdt_path_offset(itb, path);
     48     if (off < 0) {
     49         error_setg(errp, "can't find node %s", path);
     50         return NULL;
     51     }
     52     if (poff) {
     53         *poff = off;
     54     }
     55 
     56     data = fdt_getprop(itb, off, "data", &sz);
     57     if (!data) {
     58         error_setg(errp, "can't get %s/data", path);
     59         return NULL;
     60     }
     61 
     62     comp = fdt_getprop(itb, off, "compression", NULL);
     63     if (!comp || !strcmp(comp, "none")) {
     64         if (psz) {
     65             *psz = sz;
     66         }
     67         uncomp_data = g_malloc(sz);
     68         memmove(uncomp_data, data, sz);
     69         return uncomp_data;
     70     }
     71 
     72     if (!strcmp(comp, "gzip")) {
     73         uncomp_len = UBOOT_MAX_GUNZIP_BYTES;
     74         uncomp_data = g_malloc(uncomp_len);
     75 
     76         uncomp_len = gunzip(uncomp_data, uncomp_len, (void *) data, sz);
     77         if (uncomp_len < 0) {
     78             error_setg(errp, "unable to decompress %s image", name);
     79             g_free(uncomp_data);
     80             return NULL;
     81         }
     82 
     83         data = g_realloc(uncomp_data, uncomp_len);
     84         if (psz) {
     85             *psz = uncomp_len;
     86         }
     87         return data;
     88     }
     89 
     90     error_setg(errp, "unknown compression '%s'", comp);
     91     return NULL;
     92 }
     93 
     94 static int fit_image_addr(const void *itb, int img, const char *name,
     95                           hwaddr *addr, Error **errp)
     96 {
     97     const void *prop;
     98     int len;
     99 
    100     prop = fdt_getprop(itb, img, name, &len);
    101     if (!prop) {
    102         error_setg(errp, "can't find %s address", name);
    103         return -ENOENT;
    104     }
    105 
    106     switch (len) {
    107     case 4:
    108         *addr = fdt32_to_cpu(*(fdt32_t *)prop);
    109         return 0;
    110     case 8:
    111         *addr = fdt64_to_cpu(*(fdt64_t *)prop);
    112         return 0;
    113     default:
    114         error_setg(errp, "invalid %s address length %d", name, len);
    115         return -EINVAL;
    116     }
    117 }
    118 
    119 static int fit_load_kernel(const struct fit_loader *ldr, const void *itb,
    120                            int cfg, void *opaque, hwaddr *pend,
    121                            Error **errp)
    122 {
    123     const char *name;
    124     const void *data;
    125     const void *load_data;
    126     hwaddr load_addr, entry_addr;
    127     int img_off, err;
    128     size_t sz;
    129     int ret;
    130 
    131     name = fdt_getprop(itb, cfg, "kernel", NULL);
    132     if (!name) {
    133         error_setg(errp, "no kernel specified by FIT configuration");
    134         return -EINVAL;
    135     }
    136 
    137     load_data = data = fit_load_image_alloc(itb, name, &img_off, &sz, errp);
    138     if (!data) {
    139         error_prepend(errp, "unable to load kernel image from FIT: ");
    140         return -EINVAL;
    141     }
    142 
    143     err = fit_image_addr(itb, img_off, "load", &load_addr, errp);
    144     if (err) {
    145         error_prepend(errp, "unable to read kernel load address from FIT: ");
    146         ret = err;
    147         goto out;
    148     }
    149 
    150     err = fit_image_addr(itb, img_off, "entry", &entry_addr, errp);
    151     if (err) {
    152         error_prepend(errp, "unable to read kernel entry address from FIT: ");
    153         ret = err;
    154         goto out;
    155     }
    156 
    157     if (ldr->kernel_filter) {
    158         load_data = ldr->kernel_filter(opaque, data, &load_addr, &entry_addr);
    159     }
    160 
    161     if (pend) {
    162         *pend = load_addr + sz;
    163     }
    164 
    165     load_addr = ldr->addr_to_phys(opaque, load_addr);
    166     rom_add_blob_fixed(name, load_data, sz, load_addr);
    167 
    168     ret = 0;
    169 out:
    170     g_free((void *) data);
    171     if (data != load_data) {
    172         g_free((void *) load_data);
    173     }
    174     return ret;
    175 }
    176 
    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)
    180 {
    181     Error *err = NULL;
    182     const char *name;
    183     const void *data;
    184     const void *load_data;
    185     hwaddr load_addr;
    186     int img_off;
    187     size_t sz;
    188     int ret;
    189 
    190     name = fdt_getprop(itb, cfg, "fdt", NULL);
    191     if (!name) {
    192         return 0;
    193     }
    194 
    195     load_data = data = fit_load_image_alloc(itb, name, &img_off, &sz, errp);
    196     if (!data) {
    197         error_prepend(errp, "unable to load FDT image from FIT: ");
    198         return -EINVAL;
    199     }
    200 
    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);
    204         error_free(err);
    205     } else if (ret) {
    206         error_propagate_prepend(errp, err,
    207                                 "unable to read FDT load address from FIT: ");
    208         goto out;
    209     }
    210 
    211     if (ldr->fdt_filter) {
    212         load_data = ldr->fdt_filter(opaque, data, match_data, &load_addr);
    213     }
    214 
    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);
    218 
    219     ret = 0;
    220 out:
    221     g_free((void *) data);
    222     if (data != load_data) {
    223         g_free((void *) load_data);
    224     }
    225     return ret;
    226 }
    227 
    228 static bool fit_cfg_compatible(const void *itb, int cfg, const char *compat)
    229 {
    230     const void *fdt;
    231     const char *fdt_name;
    232     bool ret;
    233 
    234     fdt_name = fdt_getprop(itb, cfg, "fdt", NULL);
    235     if (!fdt_name) {
    236         return false;
    237     }
    238 
    239     fdt = fit_load_image_alloc(itb, fdt_name, NULL, NULL, NULL);
    240     if (!fdt) {
    241         return false;
    242     }
    243 
    244     if (fdt_check_header(fdt)) {
    245         ret = false;
    246         goto out;
    247     }
    248 
    249     if (fdt_node_check_compatible(fdt, 0, compat)) {
    250         ret = false;
    251         goto out;
    252     }
    253 
    254     ret = true;
    255 out:
    256     g_free((void *) fdt);
    257     return ret;
    258 }
    259 
    260 int load_fit(const struct fit_loader *ldr, const char *filename, void *opaque)
    261 {
    262     Error *err = NULL;
    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;
    268     hwaddr kernel_end;
    269     int ret;
    270 
    271     itb = load_device_tree(filename, &itb_size);
    272     if (!itb) {
    273         return -EINVAL;
    274     }
    275 
    276     configs = fdt_path_offset(itb, "/configurations");
    277     if (configs < 0) {
    278         error_report("can't find node /configurations");
    279         ret = configs;
    280         goto out;
    281     }
    282 
    283     cfg_off = -FDT_ERR_NOTFOUND;
    284 
    285     if (ldr->matches) {
    286         for (match = ldr->matches; match->compatible; match++) {
    287             off = fdt_first_subnode(itb, configs);
    288             while (off >= 0) {
    289                 if (fit_cfg_compatible(itb, off, match->compatible)) {
    290                     cfg_off = off;
    291                     match_data = match->data;
    292                     break;
    293                 }
    294 
    295                 off = fdt_next_subnode(itb, off);
    296             }
    297 
    298             if (cfg_off >= 0) {
    299                 break;
    300             }
    301         }
    302     }
    303 
    304     if (cfg_off < 0) {
    305         def_cfg_name = fdt_getprop(itb, configs, "default", NULL);
    306         if (def_cfg_name) {
    307             snprintf(path, sizeof(path), "/configurations/%s", def_cfg_name);
    308             cfg_off = fdt_path_offset(itb, path);
    309         }
    310     }
    311 
    312     if (cfg_off < 0) {
    313         error_report("can't find configuration");
    314         ret = cfg_off;
    315         goto out;
    316     }
    317 
    318     ret = fit_load_kernel(ldr, itb, cfg_off, opaque, &kernel_end, &err);
    319     if (ret) {
    320         error_report_err(err);
    321         goto out;
    322     }
    323 
    324     ret = fit_load_fdt(ldr, itb, cfg_off, opaque, match_data, kernel_end,
    325                        &err);
    326     if (ret) {
    327         error_report_err(err);
    328         goto out;
    329     }
    330 
    331     ret = 0;
    332 out:
    333     g_free((void *) itb);
    334     return ret;
    335 }