Commit 31eca697276ef9aced10b802a869e7783439482e

Authored by Simon Glass
1 parent 65851fcec3

x86: ifdtool: Add support for early microcode access

Some Intel CPUs use an 'FSP' binary blob which provides an inflexible
means of starting up the CPU. One result is that microcode updates can only
be done before RAM is available and therefore parsing of the device tree
is impracticle.

Worse, the addess of the microcode update must be stored in ROM since a
pointer to its start address and size is passed to the 'FSP' blob. It is
not possible to perform any calculations to obtain the address and size.

To work around this, ifdtool is enhanced to work out the address and size of
the first microcode update it finds in the supplied device tree. It then
writes these into the correct place in the ROM. U-Boot can then start up
the FSP correctly.

Signed-off-by: Simon Glass <sjg@chromium.org>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>

Showing 2 changed files with 103 additions and 8 deletions Side-by-side Diff

... ... @@ -126,6 +126,7 @@
126 126 hostprogs-$(CONFIG_EXYNOS5420) += mkexynosspl
127 127 HOSTCFLAGS_mkexynosspl.o := -pedantic
128 128  
  129 +ifdtool-objs := $(LIBFDT_OBJS) ifdtool.o
129 130 hostprogs-$(CONFIG_X86) += ifdtool
130 131  
131 132 hostprogs-$(CONFIG_MX23) += mxsboot
... ... @@ -18,6 +18,7 @@
18 18 #include <unistd.h>
19 19 #include <sys/types.h>
20 20 #include <sys/stat.h>
  21 +#include <libfdt.h>
21 22 #include "ifdtool.h"
22 23  
23 24 #undef DEBUG
... ... @@ -34,6 +35,8 @@
34 35  
35 36 enum input_file_type_t {
36 37 IF_normal,
  38 + IF_fdt,
  39 + IF_uboot,
37 40 };
38 41  
39 42 struct input_file {
... ... @@ -703,7 +706,7 @@
703 706 * 0xffffffff so use an address relative to that. For an
704 707 * 8MB ROM the start address is 0xfff80000.
705 708 * @write_fname: Filename to add to the image
706   - * @return 0 if OK, -ve on error
  709 + * @return number of bytes written if OK, -ve on error
707 710 */
708 711 static int write_data(char *image, int size, unsigned int addr,
709 712 const char *write_fname)
... ... @@ -715,7 +718,7 @@
715 718 if (write_fd < 0)
716 719 return write_fd;
717 720  
718   - offset = addr + size;
  721 + offset = (uint32_t)(addr + size);
719 722 debug("Writing %s to offset %#x\n", write_fname, offset);
720 723  
721 724 if (offset < 0 || offset + write_size > size) {
... ... @@ -731,6 +734,68 @@
731 734  
732 735 close(write_fd);
733 736  
  737 + return write_size;
  738 +}
  739 +
  740 +/**
  741 + * write_uboot() - Write U-Boot, device tree and microcode pointer
  742 + *
  743 + * This writes U-Boot into a place in the flash, followed by its device tree.
  744 + * The microcode pointer is written so that U-Boot can find the microcode in
  745 + * the device tree very early in boot.
  746 + *
  747 + * @image: Pointer to image
  748 + * @size: Size of image in bytes
  749 + * @uboot: Input file information for u-boot.bin
  750 + * @fdt: Input file information for u-boot.dtb
  751 + * @ucode_ptr: Address in U-Boot where the microcode pointer should be placed
  752 + * @return 0 if OK, -ve on error
  753 + */
  754 +static int write_uboot(char *image, int size, struct input_file *uboot,
  755 + struct input_file *fdt, unsigned int ucode_ptr)
  756 +{
  757 + const void *blob;
  758 + const char *data;
  759 + int uboot_size;
  760 + uint32_t *ptr;
  761 + int data_size;
  762 + int offset;
  763 + int node;
  764 + int ret;
  765 +
  766 + uboot_size = write_data(image, size, uboot->addr, uboot->fname);
  767 + if (uboot_size < 0)
  768 + return uboot_size;
  769 + fdt->addr = uboot->addr + uboot_size;
  770 + debug("U-Boot size %#x, FDT at %#x\n", uboot_size, fdt->addr);
  771 + ret = write_data(image, size, fdt->addr, fdt->fname);
  772 + if (ret < 0)
  773 + return ret;
  774 +
  775 + if (ucode_ptr) {
  776 + blob = (void *)image + (uint32_t)(fdt->addr + size);
  777 + debug("DTB at %lx\n", (char *)blob - image);
  778 + node = fdt_node_offset_by_compatible(blob, 0,
  779 + "intel,microcode");
  780 + if (node < 0) {
  781 + debug("No microcode found in FDT: %s\n",
  782 + fdt_strerror(node));
  783 + return -ENOENT;
  784 + }
  785 + data = fdt_getprop(blob, node, "data", &data_size);
  786 + if (!data) {
  787 + debug("No microcode data found in FDT: %s\n",
  788 + fdt_strerror(data_size));
  789 + return -ENOENT;
  790 + }
  791 + offset = ucode_ptr - uboot->addr;
  792 + ptr = (void *)image + offset;
  793 + ptr[0] = uboot->addr + (data - image);
  794 + ptr[1] = data_size;
  795 + debug("Wrote microcode pointer at %x: addr=%x, size=%x\n",
  796 + ucode_ptr, ptr[0], ptr[1]);
  797 + }
  798 +
734 799 return 0;
735 800 }
736 801  
... ... @@ -800,7 +865,7 @@
800 865 char *desc_fname = NULL, *addr_str = NULL;
801 866 int region_type = -1, inputfreq = 0;
802 867 enum spi_frequency spifreq = SPI_FREQUENCY_20MHZ;
803   - struct input_file input_file[WRITE_MAX], *ifile;
  868 + struct input_file input_file[WRITE_MAX], *ifile, *fdt = NULL;
804 869 unsigned char wr_idx, wr_num = 0;
805 870 int rom_size = -1;
806 871 bool write_it;
... ... @@ -808,6 +873,8 @@
808 873 char *outfile = NULL;
809 874 struct stat buf;
810 875 int size = 0;
  876 + unsigned int ucode_ptr = 0;
  877 + bool have_uboot = false;
811 878 int bios_fd;
812 879 char *image;
813 880 int ret;
814 881  
815 882  
816 883  
... ... @@ -817,18 +884,21 @@
817 884 {"descriptor", 1, NULL, 'D'},
818 885 {"em100", 0, NULL, 'e'},
819 886 {"extract", 0, NULL, 'x'},
  887 + {"fdt", 1, NULL, 'f'},
820 888 {"inject", 1, NULL, 'i'},
821 889 {"lock", 0, NULL, 'l'},
  890 + {"microcode", 1, NULL, 'm'},
822 891 {"romsize", 1, NULL, 'r'},
823 892 {"spifreq", 1, NULL, 's'},
824 893 {"unlock", 0, NULL, 'u'},
  894 + {"uboot", 1, NULL, 'U'},
825 895 {"write", 1, NULL, 'w'},
826 896 {"version", 0, NULL, 'v'},
827 897 {"help", 0, NULL, 'h'},
828 898 {0, 0, 0, 0}
829 899 };
830 900  
831   - while ((opt = getopt_long(argc, argv, "cdD:ehi:lr:s:uvw:x?",
  901 + while ((opt = getopt_long(argc, argv, "cdD:ef:hi:lm:r:s:uU:vw:x?",
832 902 long_options, &option_index)) != EOF) {
833 903 switch (opt) {
834 904 case 'c':
... ... @@ -871,6 +941,9 @@
871 941 case 'l':
872 942 mode_locked = 1;
873 943 break;
  944 + case 'm':
  945 + ucode_ptr = strtoul(optarg, NULL, 0);
  946 + break;
874 947 case 'r':
875 948 rom_size = strtol(optarg, NULL, 0);
876 949 debug("ROM size %d\n", rom_size);
... ... @@ -904,6 +977,8 @@
904 977 exit(EXIT_SUCCESS);
905 978 break;
906 979 case 'w':
  980 + case 'U':
  981 + case 'f':
907 982 ifile = &input_file[wr_num];
908 983 mode_write = 1;
909 984 if (wr_num < WRITE_MAX) {
... ... @@ -913,7 +988,12 @@
913 988 exit(EXIT_FAILURE);
914 989 }
915 990 ifile->addr = strtol(optarg, NULL, 0);
916   - ifile->type = IF_normal;
  991 + ifile->type = opt == 'f' ? IF_fdt :
  992 + opt == 'U' ? IF_uboot : IF_normal;
  993 + if (ifile->type == IF_fdt)
  994 + fdt = ifile;
  995 + else if (ifile->type == IF_uboot)
  996 + have_uboot = true;
917 997 wr_num++;
918 998 } else {
919 999 fprintf(stderr,
... ... @@ -970,6 +1050,13 @@
970 1050 exit(EXIT_FAILURE);
971 1051 }
972 1052  
  1053 + if (have_uboot && !fdt) {
  1054 + fprintf(stderr,
  1055 + "You must supply a device tree file for U-Boot\n\n");
  1056 + print_usage(argv[0]);
  1057 + exit(EXIT_FAILURE);
  1058 + }
  1059 +
973 1060 filename = argv[optind];
974 1061 if (optind + 2 != argc)
975 1062 outfile = argv[optind + 1];
976 1063  
... ... @@ -1034,9 +1121,16 @@
1034 1121 if (mode_write) {
1035 1122 for (wr_idx = 0; wr_idx < wr_num; wr_idx++) {
1036 1123 ifile = &input_file[wr_idx];
1037   - ret = write_data(image, size, ifile->addr,
  1124 + if (ifile->type == IF_fdt) {
  1125 + continue;
  1126 + } else if (ifile->type == IF_uboot) {
  1127 + ret = write_uboot(image, size, ifile, fdt,
  1128 + ucode_ptr);
  1129 + } else {
  1130 + ret = write_data(image, size, ifile->addr,
1038 1131 ifile->fname);
1039   - if (ret)
  1132 + }
  1133 + if (ret < 0)
1040 1134 break;
1041 1135 }
1042 1136 }
... ... @@ -1071,6 +1165,6 @@
1071 1165 free(image);
1072 1166 close(bios_fd);
1073 1167  
1074   - return ret ? 1 : 0;
  1168 + return ret < 0 ? 1 : 0;
1075 1169 }