Commit 31eca697276ef9aced10b802a869e7783439482e
1 parent
65851fcec3
Exists in
v2017.01-smarct4x
and in
37 other branches
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
tools/Makefile
tools/ifdtool.c
... | ... | @@ -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 | } |