]> git.kernelconcepts.de Git - karo-tx-uboot.git/blobdiff - tools/ifdtool.c
karo: tx6: rename CONFIG_SYS_BOOT_CMD_NAND to FDTSAVE_CMD_STR
[karo-tx-uboot.git] / tools / ifdtool.c
index a4b481fb60b45d7769fb48d5098a889d68d09f57..1f95203eea9ed12c88ccf7af5aee75c5eee1f098 100644 (file)
@@ -18,6 +18,7 @@
 #include <unistd.h>
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <libfdt.h>
 #include "ifdtool.h"
 
 #undef DEBUG
 #define FLREG_BASE(reg)                ((reg & 0x00000fff) << 12);
 #define FLREG_LIMIT(reg)       (((reg & 0x0fff0000) >> 4) | 0xfff);
 
+enum input_file_type_t {
+       IF_normal,
+       IF_fdt,
+       IF_uboot,
+};
+
+struct input_file {
+       char *fname;
+       unsigned int addr;
+       enum input_file_type_t type;
+};
+
 /**
  * find_fd() - Find the flash description in the ROM image
  *
@@ -54,7 +67,8 @@ static struct fdbar_t *find_fd(char *image, int size)
                return NULL;
        }
 
-       debug("Found Flash Descriptor signature at 0x%08x\n", i);
+       debug("Found Flash Descriptor signature at 0x%08lx\n",
+             (char *)ptr - image);
 
        return (struct fdbar_t *)ptr;
 }
@@ -448,7 +462,7 @@ static int write_regions(char *image, int size)
                if (ret)
                        return ret;
                dump_region(i, frba);
-               if (region.size == 0)
+               if (region.size <= 0)
                        continue;
                region_fd = open(region_filename(i),
                                 O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR |
@@ -464,6 +478,16 @@ static int write_regions(char *image, int size)
        return ret;
 }
 
+static int perror_fname(const char *fmt, const char *fname)
+{
+       char msg[strlen(fmt) + strlen(fname) + 1];
+
+       sprintf(msg, fmt, fname);
+       perror(msg);
+
+       return -1;
+}
+
 /**
  * write_image() - Write the image to a file
  *
@@ -480,10 +504,10 @@ static int write_image(char *filename, char *image, int size)
 
        new_fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR |
                      S_IWUSR | S_IRGRP | S_IROTH);
-       if (write(new_fd, image, size) != size) {
-               perror("Error while writing");
-               return -1;
-       }
+       if (new_fd < 0)
+               return perror_fname("Could not open file '%s'", filename);
+       if (write(new_fd, image, size) != size)
+               return perror_fname("Could not write file '%s'", filename);
        close(new_fd);
 
        return 0;
@@ -585,14 +609,10 @@ int open_for_read(const char *fname, int *sizep)
        int fd = open(fname, O_RDONLY);
        struct stat buf;
 
-       if (fd == -1) {
-               perror("Could not open file");
-               return -1;
-       }
-       if (fstat(fd, &buf) == -1) {
-               perror("Could not stat file");
-               return -1;
-       }
+       if (fd == -1)
+               return perror_fname("Could not open file '%s'", fname);
+       if (fstat(fd, &buf) == -1)
+               return perror_fname("Could not stat file '%s'", fname);
        *sizep = buf.st_size;
        debug("File %s is %d bytes\n", fname, *sizep);
 
@@ -686,10 +706,11 @@ int inject_region(char *image, int size, int region_type, char *region_fname)
  *                     0xffffffff so use an address relative to that. For an
  *                     8MB ROM the start address is 0xfff80000.
  * @write_fname:       Filename to add to the image
- * @return 0 if OK, -ve on error
+ * @offset_uboot_top:  Offset of the top of U-Boot
+ * @return number of bytes written if OK, -ve on error
  */
 static int write_data(char *image, int size, unsigned int addr,
-                     const char *write_fname)
+                     const char *write_fname, int offset_uboot_top)
 {
        int write_fd, write_size;
        int offset;
@@ -698,7 +719,15 @@ static int write_data(char *image, int size, unsigned int addr,
        if (write_fd < 0)
                return write_fd;
 
-       offset = addr + size;
+       offset = (uint32_t)(addr + size);
+       if (offset_uboot_top && offset_uboot_top >= offset) {
+               fprintf(stderr, "U-Boot image overlaps with region '%s'\n",
+                       write_fname);
+               fprintf(stderr,
+                       "U-Boot finishes at offset %x, file starts at %x\n",
+                       offset_uboot_top, offset);
+               return -EXDEV;
+       }
        debug("Writing %s to offset %#x\n", write_fname, offset);
 
        if (offset < 0 || offset + write_size > size) {
@@ -714,7 +743,211 @@ static int write_data(char *image, int size, unsigned int addr,
 
        close(write_fd);
 
-       return 0;
+       return write_size;
+}
+
+static int scan_ucode(const void *blob, char *ucode_base, int *countp,
+                     const char **datap, int *data_sizep)
+{
+       const char *data = NULL;
+       int node, count;
+       int data_size;
+       char *ucode;
+
+       for (node = 0, count = 0, ucode = ucode_base; node >= 0; count++) {
+               node = fdt_node_offset_by_compatible(blob, node,
+                                                    "intel,microcode");
+               if (node < 0)
+                       break;
+
+               data = fdt_getprop(blob, node, "data", &data_size);
+               if (!data) {
+                       debug("Missing microcode data in FDT '%s': %s\n",
+                             fdt_get_name(blob, node, NULL),
+                             fdt_strerror(data_size));
+                       return -ENOENT;
+               }
+
+               if (ucode_base)
+                       memcpy(ucode, data, data_size);
+               ucode += data_size;
+       }
+
+       if (countp)
+               *countp = count;
+       if (datap)
+               *datap = data;
+       if (data_sizep)
+               *data_sizep = data_size;
+
+       return ucode - ucode_base;
+}
+
+static int remove_ucode(char *blob)
+{
+       int node, count;
+       int ret;
+
+       /* Keep going until we find no more microcode to remove */
+       do {
+               for (node = 0, count = 0; node >= 0;) {
+                       int ret;
+
+                       node = fdt_node_offset_by_compatible(blob, node,
+                                                            "intel,microcode");
+                       if (node < 0)
+                               break;
+
+                       ret = fdt_delprop(blob, node, "data");
+
+                       /*
+                        * -FDT_ERR_NOTFOUND means we already removed the
+                        * data for this one, so we just continue.
+                        * 0 means we did remove it, so offsets may have
+                        * changed and we need to restart our scan.
+                        * Anything else indicates an error we should report.
+                        */
+                       if (ret == -FDT_ERR_NOTFOUND)
+                               continue;
+                       else if (!ret)
+                               node = 0;
+                       else
+                               return ret;
+               }
+       } while (count);
+
+       /* Pack down to remove excees space */
+       ret = fdt_pack(blob);
+       if (ret)
+               return ret;
+
+       return fdt_totalsize(blob);
+}
+
+static int write_ucode(char *image, int size, struct input_file *fdt,
+                      int fdt_size, unsigned int ucode_ptr,
+                      int collate_ucode)
+{
+       const char *data = NULL;
+       char *ucode_buf;
+       const void *blob;
+       char *ucode_base;
+       uint32_t *ptr;
+       int ucode_size;
+       int data_size;
+       int offset;
+       int count;
+       int ret;
+
+       blob = (void *)image + (uint32_t)(fdt->addr + size);
+
+       debug("DTB at %lx\n", (char *)blob - image);
+
+       /* Find out about the micrcode we have */
+       ucode_size = scan_ucode(blob, NULL, &count, &data, &data_size);
+       if (ucode_size < 0)
+               return ucode_size;
+       if (!count) {
+               debug("No microcode found in FDT\n");
+               return -ENOENT;
+       }
+
+       if (count > 1 && !collate_ucode) {
+               fprintf(stderr,
+                       "Cannot handle multiple microcode blocks - please use -C flag to collate them\n");
+               return -EMLINK;
+       }
+
+       /*
+        * Collect the microcode into a buffer, remove it from the device
+        * tree and place it immediately above the (now smaller) device tree.
+        */
+       if (collate_ucode && count > 1) {
+               ucode_buf = malloc(ucode_size);
+               if (!ucode_buf) {
+                       fprintf(stderr,
+                               "Out of memory for microcode (%d bytes)\n",
+                               ucode_size);
+                       return -ENOMEM;
+               }
+               ret = scan_ucode(blob, ucode_buf, NULL, NULL, NULL);
+               if (ret < 0)
+                       return ret;
+
+               /* Remove the microcode from the device tree */
+               ret = remove_ucode((char *)blob);
+               if (ret < 0) {
+                       debug("Could not remove FDT microcode: %s\n",
+                             fdt_strerror(ret));
+                       return -EINVAL;
+               }
+               debug("Collated %d microcode block(s)\n", count);
+               debug("Device tree reduced from %x to %x bytes\n",
+                     fdt_size, ret);
+               fdt_size = ret;
+
+               /*
+                * Place microcode area immediately above the FDT, aligned
+                * to a 16-byte boundary.
+                */
+               ucode_base = (char *)(((unsigned long)blob + fdt_size + 15) &
+                               ~15);
+
+               data = ucode_base;
+               data_size = ucode_size;
+               memcpy(ucode_base, ucode_buf, ucode_size);
+               free(ucode_buf);
+       }
+
+       offset = (uint32_t)(ucode_ptr + size);
+       ptr = (void *)image + offset;
+
+       ptr[0] = (data - image) - size;
+       ptr[1] = data_size;
+       debug("Wrote microcode pointer at %x: addr=%x, size=%x\n", ucode_ptr,
+             ptr[0], ptr[1]);
+
+       return (collate_ucode ? data + data_size : (char *)blob + fdt_size) -
+                       image;
+}
+
+/**
+ * write_uboot() - Write U-Boot, device tree and microcode pointer
+ *
+ * This writes U-Boot into a place in the flash, followed by its device tree.
+ * The microcode pointer is written so that U-Boot can find the microcode in
+ * the device tree very early in boot.
+ *
+ * @image:     Pointer to image
+ * @size:      Size of image in bytes
+ * @uboot:     Input file information for u-boot.bin
+ * @fdt:       Input file information for u-boot.dtb
+ * @ucode_ptr: Address in U-Boot where the microcode pointer should be placed
+ * @return 0 if OK, -ve on error
+ */
+static int write_uboot(char *image, int size, struct input_file *uboot,
+                      struct input_file *fdt, unsigned int ucode_ptr,
+                      int collate_ucode)
+{
+       const void *blob;
+       int uboot_size, fdt_size;
+
+       uboot_size = write_data(image, size, uboot->addr, uboot->fname, 0);
+       if (uboot_size < 0)
+               return uboot_size;
+       fdt->addr = uboot->addr + uboot_size;
+       debug("U-Boot size %#x, FDT at %#x\n", uboot_size, fdt->addr);
+       fdt_size = write_data(image, size, fdt->addr, fdt->fname, 0);
+       if (fdt_size < 0)
+               return fdt_size;
+       blob = (void *)image + (uint32_t)(fdt->addr + size);
+
+       if (ucode_ptr) {
+               return write_ucode(image, size, fdt, fdt_size, ucode_ptr,
+                                  collate_ucode);
+       }
+
+       return ((char *)blob + fdt_size) - image;
 }
 
 static void print_version(void)
@@ -732,6 +965,7 @@ static void print_usage(const char *name)
               "   -x | --extract:                   extract intel fd modules\n"
               "   -i | --inject <region>:<module>   inject file <module> into region <region>\n"
               "   -w | --write <addr>:<file>        write file to appear at memory address <addr>\n"
+              "                                     multiple files can be written simultaneously\n"
               "   -s | --spifreq <20|33|50>         set the SPI frequency\n"
               "   -e | --em100                      set SPI frequency to 20MHz and disable\n"
               "                                     Dual Output Fast Read Support\n"
@@ -777,57 +1011,67 @@ int main(int argc, char *argv[])
        int mode_dump = 0, mode_extract = 0, mode_inject = 0;
        int mode_spifreq = 0, mode_em100 = 0, mode_locked = 0;
        int mode_unlocked = 0, mode_write = 0, mode_write_descriptor = 0;
-       int create = 0;
-       char *region_type_string = NULL, *src_fname = NULL;
-       char *addr_str = NULL;
+       int create = 0, collate_ucode = 0;
+       char *region_type_string = NULL, *inject_fname = NULL;
+       char *desc_fname = NULL, *addr_str = NULL;
        int region_type = -1, inputfreq = 0;
        enum spi_frequency spifreq = SPI_FREQUENCY_20MHZ;
-       unsigned int addr = 0;
+       struct input_file input_file[WRITE_MAX], *ifile, *fdt = NULL;
+       unsigned char wr_idx, wr_num = 0;
        int rom_size = -1;
        bool write_it;
        char *filename;
        char *outfile = NULL;
        struct stat buf;
        int size = 0;
+       unsigned int ucode_ptr = 0;
+       bool have_uboot = false;
        int bios_fd;
        char *image;
        int ret;
        static struct option long_options[] = {
                {"create", 0, NULL, 'c'},
+               {"collate-microcode", 0, NULL, 'C'},
                {"dump", 0, NULL, 'd'},
                {"descriptor", 1, NULL, 'D'},
                {"em100", 0, NULL, 'e'},
                {"extract", 0, NULL, 'x'},
+               {"fdt", 1, NULL, 'f'},
                {"inject", 1, NULL, 'i'},
                {"lock", 0, NULL, 'l'},
+               {"microcode", 1, NULL, 'm'},
                {"romsize", 1, NULL, 'r'},
                {"spifreq", 1, NULL, 's'},
                {"unlock", 0, NULL, 'u'},
+               {"uboot", 1, NULL, 'U'},
                {"write", 1, NULL, 'w'},
                {"version", 0, NULL, 'v'},
                {"help", 0, NULL, 'h'},
                {0, 0, 0, 0}
        };
 
-       while ((opt = getopt_long(argc, argv, "cdD:ehi:lr:s:uvw:x?",
+       while ((opt = getopt_long(argc, argv, "cCdD:ef:hi:lm:r:s:uU:vw:x?",
                                  long_options, &option_index)) != EOF) {
                switch (opt) {
                case 'c':
                        create = 1;
                        break;
+               case 'C':
+                       collate_ucode = 1;
+                       break;
                case 'd':
                        mode_dump = 1;
                        break;
                case 'D':
                        mode_write_descriptor = 1;
-                       src_fname = optarg;
+                       desc_fname = optarg;
                        break;
                case 'e':
                        mode_em100 = 1;
                        break;
                case 'i':
                        if (get_two_words(optarg, &region_type_string,
-                                         &src_fname)) {
+                                         &inject_fname)) {
                                print_usage(argv[0]);
                                exit(EXIT_FAILURE);
                        }
@@ -852,6 +1096,9 @@ int main(int argc, char *argv[])
                case 'l':
                        mode_locked = 1;
                        break;
+               case 'm':
+                       ucode_ptr = strtoul(optarg, NULL, 0);
+                       break;
                case 'r':
                        rom_size = strtol(optarg, NULL, 0);
                        debug("ROM size %d\n", rom_size);
@@ -885,12 +1132,29 @@ int main(int argc, char *argv[])
                        exit(EXIT_SUCCESS);
                        break;
                case 'w':
+               case 'U':
+               case 'f':
+                       ifile = &input_file[wr_num];
                        mode_write = 1;
-                       if (get_two_words(optarg, &addr_str, &src_fname)) {
-                               print_usage(argv[0]);
-                               exit(EXIT_FAILURE);
+                       if (wr_num < WRITE_MAX) {
+                               if (get_two_words(optarg, &addr_str,
+                                                 &ifile->fname)) {
+                                       print_usage(argv[0]);
+                                       exit(EXIT_FAILURE);
+                               }
+                               ifile->addr = strtoll(optarg, NULL, 0);
+                               ifile->type = opt == 'f' ? IF_fdt :
+                                       opt == 'U' ? IF_uboot : IF_normal;
+                               if (ifile->type == IF_fdt)
+                                       fdt = ifile;
+                               else if (ifile->type == IF_uboot)
+                                       have_uboot = true;
+                               wr_num++;
+                       } else {
+                               fprintf(stderr,
+                                       "The number of files to write simultaneously exceeds the limitation (%d)\n",
+                                       WRITE_MAX);
                        }
-                       addr = strtol(optarg, NULL, 0);
                        break;
                case 'x':
                        mode_extract = 1;
@@ -941,6 +1205,13 @@ int main(int argc, char *argv[])
                exit(EXIT_FAILURE);
        }
 
+       if (have_uboot && !fdt) {
+               fprintf(stderr,
+                       "You must supply a device tree file for U-Boot\n\n");
+               print_usage(argv[0]);
+               exit(EXIT_FAILURE);
+       }
+
        filename = argv[optind];
        if (optind + 2 != argc)
                outfile = argv[optind + 1];
@@ -997,13 +1268,30 @@ int main(int argc, char *argv[])
        }
 
        if (mode_write_descriptor)
-               ret = write_data(image, size, -size, src_fname);
+               ret = write_data(image, size, -size, desc_fname, 0);
 
        if (mode_inject)
-               ret = inject_region(image, size, region_type, src_fname);
-
-       if (mode_write)
-               ret = write_data(image, size, addr, src_fname);
+               ret = inject_region(image, size, region_type, inject_fname);
+
+       if (mode_write) {
+               int offset_uboot_top = 0;
+
+               for (wr_idx = 0; wr_idx < wr_num; wr_idx++) {
+                       ifile = &input_file[wr_idx];
+                       if (ifile->type == IF_fdt) {
+                               continue;
+                       } else if (ifile->type == IF_uboot) {
+                               ret = write_uboot(image, size, ifile, fdt,
+                                                 ucode_ptr, collate_ucode);
+                               offset_uboot_top = ret;
+                       } else {
+                               ret = write_data(image, size, ifile->addr,
+                                        ifile->fname, offset_uboot_top);
+                       }
+                       if (ret < 0)
+                               break;
+               }
+       }
 
        if (mode_spifreq)
                set_spi_frequency(image, size, spifreq);
@@ -1035,5 +1323,5 @@ int main(int argc, char *argv[])
        free(image);
        close(bios_fd);
 
-       return ret ? 1 : 0;
+       return ret < 0 ? 1 : 0;
 }