diff options
-rw-r--r-- | util/ifdtool/ifdtool.c | 234 | ||||
-rw-r--r-- | util/ifdtool/ifdtool.h | 19 |
2 files changed, 231 insertions, 22 deletions
diff --git a/util/ifdtool/ifdtool.c b/util/ifdtool/ifdtool.c index 07cc26823b..e020a8ae07 100644 --- a/util/ifdtool/ifdtool.c +++ b/util/ifdtool/ifdtool.c @@ -44,6 +44,32 @@ #define PLATFORM_HAS_10GBE_0_REGION (platform == PLATFORM_DNV) #define PLATFORM_HAS_10GBE_1_REGION (platform == PLATFORM_DNV) +union gprd { + struct bit_field { + /* + * Start Address: bit 0-14 of the GPRD represents the + * protected region start address, where bit 0-11 of + * the start address are assumed to be zero. + */ + uint32_t start:15; + + /* Specifies read protection is enabled */ + uint32_t read_protect_en:1; + + /* + * End Address: bit 16-30 of the GPRD represents the + * protected region end address, where bit 0-11 of + * the end address are assumed to be 0xfff. + */ + uint32_t end:15; + + /* Specifies write protection is enabled */ + uint32_t write_protect_en:1; + } __packed data; + + uint32_t value; +}; + static int max_regions_from_fdbar(const struct fdbar *fdb); static int ifd_version; @@ -1551,14 +1577,39 @@ static void unlock_descriptor(const char *filename, char *image, int size) write_image(filename, image, size); } -static void disable_gpr0(const char *filename, char *image, int size) +static void print_gpr0_range(union gprd reg) { - struct fpsba *fpsba = find_fpsba(image, size); - if (!fpsba) - exit(EXIT_FAILURE); + printf("--------- GPR0 Protected Range --------------\n"); + printf("Start address = 0x%08x\n", reg.data.start << 12); + printf("End address = 0x%08x\n", (reg.data.end << 12) | 0xfff); +} + +static uint8_t get_cse_data_partition_offset(void) +{ + uint8_t data_offset = 0xff; + + switch (platform) { + case PLATFORM_CNL: + case PLATFORM_JSL: + data_offset = 0x10; + break; + case PLATFORM_TGL: + case PLATFORM_ADL: + case PLATFORM_MTL: + data_offset = 0x18; + break; + default: + break; + } + + return data_offset; +} +static uint32_t get_gpr0_offset(void) +{ /* Offset expressed as number of 32-bit fields from FPSBA */ - uint32_t gpr0_offset; + uint32_t gpr0_offset = 0xffffffff; + switch (platform) { case PLATFORM_CNL: gpr0_offset = 0x10; @@ -1574,32 +1625,163 @@ static void disable_gpr0(const char *filename, char *image, int size) gpr0_offset = 0x40; break; default: + break; + } + + return gpr0_offset; +} + +static void disable_gpr0(const char *filename, char *image, int size) +{ + struct fpsba *fpsba = find_fpsba(image, size); + if (!fpsba) + exit(EXIT_FAILURE); + + uint32_t gpr0_offset = get_gpr0_offset(); + if (gpr0_offset == 0xffffffff) { fprintf(stderr, "Disabling GPR0 not supported on this platform\n"); exit(EXIT_FAILURE); } + + union gprd reg; /* If bit 31 is set then GPR0 protection is enable */ - bool gpr0_status = fpsba->pchstrp[gpr0_offset] & 0x80000000; - if (!gpr0_status) { + reg.value = fpsba->pchstrp[gpr0_offset]; + if (!reg.data.write_protect_en) { printf("GPR0 protection is already disabled\n"); return; } - printf("Value at GPRD offset (%d) is 0x%08x\n", gpr0_offset, fpsba->pchstrp[gpr0_offset]); - printf("--------- GPR0 Protected Range --------------\n"); + printf("Value at GPRD offset (%d) is 0x%08x\n", gpr0_offset, reg.value); + print_gpr0_range(reg); + /* 0 means GPR0 protection is disabled */ + fpsba->pchstrp[gpr0_offset] = 0; + write_image(filename, image, size); + printf("GPR0 protection is now disabled\n"); +} + +/* + * Helper function to parse the FPT to retrieve the FITC start offset and size. + * FITC is a sub-partition table inside CSE data partition known as FPT. + * + * CSE Region + * |-----> CSE Data Partition Offset + * | |-------> FPT Entry + * | | |-> Sub Partition 1 + * | | |-> Sub Partition 2 + * | | |-> FITC + * | | | | -> FITC Offset + * | | | | -> FITC Length + */ +static int parse_fitc_table(struct cse_fpt *fpt, uint32_t *offset, + size_t *size) +{ + size_t num_part_header = fpt->count; + /* Move to the next structure which is FPT sub-partition entries */ + struct cse_fpt_sub_part *fpt_sub_part = (struct cse_fpt_sub_part *)(fpt + 1); + for (size_t index = 0; index < num_part_header; index++) { + if (!strncmp(fpt_sub_part->signature, "FITC", 4)) { + *offset = fpt_sub_part->offset; + *size = fpt_sub_part->length; + return 0; + } + fpt_sub_part++; + } + + return -1; +} + +/* + * Formula to calculate the GPR0 protection range as below: + * Start: CSE Region Base Offset + * End: Till the end of FITC sub-partition + */ +static int calculate_gpr0_range(char *image, int size, + uint32_t *gpr0_start, uint32_t *gpr0_end) +{ + struct frba *frba = find_frba(image, size); + if (!frba) + return -1; + + struct region region = get_region(frba, REGION_ME); + if (region.size <= 0) { + fprintf(stderr, "Region %s is disabled in target\n", + region_name(REGION_ME)); + return -1; + } + + /* CSE Region Start */ + uint32_t cse_region_start = region.base; + /* Get CSE Data Partition Offset */ + uint8_t cse_data_offset = get_cse_data_partition_offset(); + if (cse_data_offset == 0xff) { + fprintf(stderr, "Unsupported platform\n"); + exit(EXIT_FAILURE); + } + uint32_t data_part_offset = *((uint32_t *)(image + cse_region_start + cse_data_offset)); + /* Start reading the CSE Data Partition Table, also known as FPT */ + uint32_t data_part_start = data_part_offset + cse_region_start; + + uint32_t fitc_region_start = 0; + size_t fitc_region_size = 0; /* - * Start Address: bit 0-15 of the GPRD represents the protected region start address, - * where bit 0-11 of the start address are assumed to be zero. + * FPT holds entry for own FPT data structure also bunch of sub-partitions. + * `FITC` is one of such sub-partition entry. */ - printf("Start address = 0x%08x\n", (fpsba->pchstrp[gpr0_offset] & 0xffff) << 12); + if (parse_fitc_table(((struct cse_fpt *)(image + data_part_start)), + &fitc_region_start, &fitc_region_size) < 0) { + fprintf(stderr, "Unable to find FITC entry\n"); + return -1; + } + /* - * End Address: bit 16-30 of the GPRD represents the protected region end address, - * where bit 0-11 of the end address are assumed to be 0xfff. + * GPR0 protection is configured to the following range: + * start: CSE region base offset + * end: Till the end of FITC sub-partition (i.e. CSE region + data partition offset + + * FITC sub partition offset + FITC sub partition size) */ - printf("End address = 0x%08x\n", - ((fpsba->pchstrp[gpr0_offset] >> 16) & 0x7fff) << 12 | 0xfff); - /* 0 means GPR0 protection is disabled */ - fpsba->pchstrp[gpr0_offset] = 0; + *gpr0_start = cse_region_start; + *gpr0_end = (cse_region_start + data_part_offset + + fitc_region_start + fitc_region_size) - 1; + + return 0; +} + +static void enable_gpr0(const char *filename, char *image, int size) +{ + struct fpsba *fpsba = find_fpsba(image, size); + if (!fpsba) + exit(EXIT_FAILURE); + + uint32_t gpr0_offset = get_gpr0_offset(); + if (gpr0_offset == 0xffffffff) { + fprintf(stderr, "Enabling GPR0 not supported on this platform\n"); + exit(EXIT_FAILURE); + } + + union gprd reg; + /* If bit 31 is set then GPR0 protection is enable */ + reg.value = fpsba->pchstrp[gpr0_offset]; + if (reg.data.write_protect_en) { + printf("GPR0 protection is already enabled\n"); + print_gpr0_range(reg); + return; + } + + uint32_t gpr0_range_start, gpr0_range_end; + + if (calculate_gpr0_range(image, size, &gpr0_range_start, &gpr0_range_end)) + exit(EXIT_FAILURE); + + reg.data.start = (gpr0_range_start >> 12) & 0x7fff; + reg.data.end = (gpr0_range_end >> 12) & 0x7fff; + reg.data.read_protect_en = 0; + reg.data.write_protect_en = 1; + + fpsba->pchstrp[gpr0_offset] = reg.value; + printf("Value at GPRD offset (%d) is 0x%08x\n", gpr0_offset, reg.value); + print_gpr0_range(reg); write_image(filename, image, size); + printf("GPR0 protection is now enabled\n"); } static void set_pchstrap(struct fpsba *fpsba, const struct fdbar *fdb, const int strap, @@ -1942,6 +2124,7 @@ static void print_usage(const char *name) " -r | --read Enable CPU/BIOS read access for ME region\n" " -u | --unlock Unlock firmware descriptor and ME region\n" " -g | --gpr0-disable Disable GPR0 (Global Protected Range) register\n" + " -E | --gpr0-enable Enable GPR0 (Global Protected Range) register\n" " -M | --altmedisable <0|1> Set the MeDisable and AltMeDisable (or HAP for skylake or newer platform)\n" " bits to disable ME\n" " -p | --platform Add platform-specific quirks\n" @@ -1975,7 +2158,7 @@ int main(int argc, char *argv[]) int mode_em100 = 0, mode_locked = 0, mode_unlocked = 0, mode_validate = 0; int mode_layout = 0, mode_newlayout = 0, mode_density = 0, mode_setstrap = 0; int mode_read = 0, mode_altmedisable = 0, altmedisable = 0, mode_fmap_template = 0; - int mode_gpr0_disable = 0; + int mode_gpr0_disable = 0, mode_gpr0_enable = 0; char *region_type_string = NULL, *region_fname = NULL; const char *layout_fname = NULL; char *new_filename = NULL; @@ -2002,6 +2185,7 @@ int main(int argc, char *argv[]) {"read", 0, NULL, 'r'}, {"unlock", 0, NULL, 'u'}, {"gpr0-disable", 0, NULL, 'g'}, + {"gpr0-enable", 0, NULL, 'E'}, {"version", 0, NULL, 'v'}, {"help", 0, NULL, 'h'}, {"platform", 0, NULL, 'p'}, @@ -2011,7 +2195,7 @@ int main(int argc, char *argv[]) {0, 0, 0, 0} }; - while ((opt = getopt_long(argc, argv, "S:V:df:F:D:C:M:xi:n:O:s:p:elrugvth?", + while ((opt = getopt_long(argc, argv, "S:V:df:F:D:C:M:xi:n:O:s:p:elrugEvth?", long_options, &option_index)) != EOF) { switch (opt) { case 'd': @@ -2217,6 +2401,9 @@ int main(int argc, char *argv[]) case 'g': mode_gpr0_disable = 1; break; + case 'E': + mode_gpr0_enable = 1; + break; case 'p': if (!strcmp(optarg, "aplk")) { platform = PLATFORM_APL; @@ -2270,7 +2457,7 @@ int main(int argc, char *argv[]) if ((mode_dump + mode_layout + mode_fmap_template + mode_extract + mode_inject + mode_setstrap + mode_newlayout + (mode_spifreq | mode_em100 | mode_unlocked | mode_locked) + mode_altmedisable + mode_validate + - mode_gpr0_disable) > 1) { + (mode_gpr0_disable | mode_gpr0_enable)) > 1) { fprintf(stderr, "You may not specify more than one mode.\n\n"); fprintf(stderr, "run '%s -h' for usage\n", argv[0]); exit(EXIT_FAILURE); @@ -2279,7 +2466,7 @@ int main(int argc, char *argv[]) if ((mode_dump + mode_layout + mode_fmap_template + mode_extract + mode_inject + mode_setstrap + mode_newlayout + mode_spifreq + mode_em100 + mode_locked + mode_unlocked + mode_density + mode_altmedisable + - mode_validate + mode_gpr0_disable) == 0) { + mode_validate + (mode_gpr0_disable | mode_gpr0_enable)) == 0) { fprintf(stderr, "You need to specify a mode.\n\n"); fprintf(stderr, "run '%s -h' for usage\n", argv[0]); exit(EXIT_FAILURE); @@ -2379,6 +2566,9 @@ int main(int argc, char *argv[]) if (mode_gpr0_disable) disable_gpr0(new_filename, image, size); + if (mode_gpr0_enable) + enable_gpr0(new_filename, image, size); + if (mode_setstrap) { struct fpsba *fpsba = find_fpsba(image, size); const struct fdbar *fdb = find_fd(image, size); diff --git a/util/ifdtool/ifdtool.h b/util/ifdtool/ifdtool.h index 219634b21d..79fe0abc06 100644 --- a/util/ifdtool/ifdtool.h +++ b/util/ifdtool/ifdtool.h @@ -208,3 +208,22 @@ struct region_name { const char *filename; const char *fmapname; }; + +struct cse_fpt { + const char signature[4]; + uint32_t count; + uint8_t header_version; + uint8_t entry_version; + uint8_t length; + uint8_t crc; + uint8_t reserved[20]; +}; + +struct cse_fpt_sub_part { + const char signature[4]; + uint32_t reserved_1; + uint32_t offset; + uint32_t length; + uint8_t reserved_2[12]; + uint32_t flags; +}; |