Vladimir Serbinenko has uploaded this change for review. ( https://review.coreboot.org/c/coreboot/+/82722?usp=email )
Change subject: intelvbtupgrader: New tool to upgrade VBT files to newer versions ......................................................................
intelvbtupgrader: New tool to upgrade VBT files to newer versions
Change-Id: Ie677403898b7b8ab9f57ad77668155bb55188769 --- A util/intelvbtupgrader/.gitignore A util/intelvbtupgrader/Makefile A util/intelvbtupgrader/intelvbtupgrader.c 3 files changed, 219 insertions(+), 0 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/22/82722/1
diff --git a/util/intelvbtupgrader/.gitignore b/util/intelvbtupgrader/.gitignore new file mode 100644 index 0000000..e39d748 --- /dev/null +++ b/util/intelvbtupgrader/.gitignore @@ -0,0 +1 @@ +intelvbtupgrader diff --git a/util/intelvbtupgrader/Makefile b/util/intelvbtupgrader/Makefile new file mode 100644 index 0000000..ee4259e --- /dev/null +++ b/util/intelvbtupgrader/Makefile @@ -0,0 +1,20 @@ +## +## SPDX-License-Identifier: GPL-2.0-only + +PROGRAM = intelvbtupgrader +CC ?= gcc +CFLAGS ?= -O2 -g +CFLAGS += -Wall -Werror +CFLAGS += -I../../src/commonlib/include -I ../../src/commonlib/bsd/include + +all: $(PROGRAM) + +$(PROGRAM): $(PROGRAM).c + $(CC) $(CFLAGS) $(CPPFLAGS) $(PROGRAM).c -o $(PROGRAM) + +clean: + rm -f $(PROGRAM) + +distclean: clean + +.PHONY: all clean distclean diff --git a/util/intelvbtupgrader/intelvbtupgrader.c b/util/intelvbtupgrader/intelvbtupgrader.c new file mode 100644 index 0000000..d47addb --- /dev/null +++ b/util/intelvbtupgrader/intelvbtupgrader.c @@ -0,0 +1,198 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#include <stdlib.h> +#include <commonlib/helpers.h> + +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; + +struct vbt_header { + u8 signature[20]; + u16 version; + u16 header_size; + u16 vbt_size; + u8 vbt_checksum; + u8 reserved0; + u32 bdb_offset; + u32 aim_offset[4]; +} __attribute__((packed)); + +struct bdb_header { + u8 signature[16]; + u16 version; + u16 header_size; + u16 bdb_size; +}; + +static u8 csum = 0; +static FILE *fout = 0; +static FILE *fin; +static u8 buf[1 << 15]; + +static void write_block(void *data, size_t sz) +{ + u8 *b = data; + for (size_t i = 0; i < sz; i++) + csum += b[i]; + fwrite(b, 1, sz, fout); +} + +static void copy_chunk(size_t sz) +{ + if (sz > sizeof(buf)) { + printf("Copy too large"); + exit(1); + return; + } + fread(buf, 1, sz, fin); + write_block(buf, sz); +} + +static void zero_fill(size_t sz) +{ + if (sz > sizeof(buf)) { + printf("Zero fill too large"); + exit(1); + return; + } + memset(buf, 0, sz); + write_block(buf, sz); +} + +static u16 sec_sizes_240[256] = { + [254] = 0xea, [1] = 0x5, [253] = 0x42, [2] = 0x164, [252] = 0x7a, + [9] = 0x64, [10] = 0xcb, [12] = 0x13, [20] = 0xaa, [27] = 0x30c, + [40] = 0x22, [42] = 0x554, [43] = 0x131, [44] = 0x3a, [46] = 0x130, + [51] = 0x9, [52] = 0x336, [56] = 0xd2, [57] = 0xa52, [58] = 0x2a2, +}; + +static u16 sec_sizes_250[256] = { + [254] = 0xea, [1] = 0xa, [253] = 0x42, [2] = 0x164, [252] = 0x7a, + [9] = 0x64, [10] = 0xcb, [12] = 0x13, [20] = 0xaa, [27] = 0x32c, + [40] = 0x22, [42] = 0x556, [43] = 0x131, [44] = 0x4e, [46] = 0x130, + [51] = 0x9, [52] = 0x336, [56] = 0xd2, [57] = 0xa52, [58] = 0x2a2, +}; + +static struct { + const char *name; + int version; + const u16 *size_table; +} targets[] = { + {"2.40", 240, sec_sizes_240}, + {"2.50", 250, sec_sizes_250}, +}; + +int main(int argc, char **argv) +{ + if (argc < 4) { + printf("Usage: %s INPUT OUTPUT VERSION\n", argv[0]); + return 0; + } + + int targetid = -1; + + for (int i = 0; i < ARRAY_SIZE(targets); i++) + if (strcmp(targets[i].name, argv[3]) == 0) { + targetid = i; + break; + } + if (targetid == -1) { + printf("Unknown version %s\n", argv[3]); + return 0; + } + int target = targets[targetid].version; + fin = fopen(argv[1], "rb"); + struct vbt_header head; + fread(&head, sizeof(head), 1, fin); + printf("version: %d.%02d\n", head.version / 100, head.version % 100); + if (head.version != 100) { + printf("Head version not 1.00\n"); + return 0; + } + + u16 vbt_size = head.vbt_size; + head.vbt_size = 0; + head.vbt_checksum = 0; + + ssize_t extra_size = head.bdb_offset - sizeof(head); + if (extra_size < 0 || extra_size >= sizeof(buf)) { + printf("invalid BDB offset\n"); + return 0; + } + + fout = fopen(argv[2], "wb"); + + write_block(&head, sizeof(head)); + fread(buf, 1, extra_size, fin); + write_block(buf, extra_size); + struct bdb_header bdb_head; + size_t bdb_offset = ftell(fin); + + fread(&bdb_head, 1, sizeof(bdb_head), fin); + + if (bdb_head.version >= target) { + printf("Already at target or higher. Nothing to do\n"); + return 0; + } + + bdb_head.version = target; + + u16 bdb_size = bdb_head.bdb_size + 1; + + bdb_head.bdb_size = 0; + ssize_t extra_size2 = bdb_head.header_size - sizeof(bdb_head); + if (extra_size2 < 0 || extra_size2 >= sizeof(buf)) { + printf("invalid BDB header size\n"); + return 0; + } + write_block(&bdb_head, sizeof(bdb_head)); + copy_chunk(extra_size2); + + size_t bdb_contents_size = 0; + + for (int off = bdb_head.header_size; off < bdb_size;) { + fread(buf, 1, 3, fin); + u8 sectype = buf[0]; + u16 secsz = (buf[1] | (buf[2] << 8)); + int intended = targets[targetid].size_table[sectype]; + off += 3; + bdb_contents_size += 3; + if (intended <= secsz) { + write_block(buf, 3); + copy_chunk(secsz); + bdb_contents_size += secsz; + off += secsz; + } else { + buf[1] = intended; + buf[2] = intended >> 8; + write_block(buf, 3); + copy_chunk(secsz); + zero_fill(intended - secsz); + bdb_contents_size += intended; + off += intended; + } + } + ssize_t extra_size3 = vbt_size - bdb_size - head.bdb_offset; + copy_chunk(extra_size3); + + fseek(fout, bdb_offset + 20, SEEK_SET); + size_t bdbout_size = bdb_contents_size + extra_size2 + sizeof(bdb_head); + u16 b = bdbout_size - 1; + buf[0] = b; + buf[1] = b >> 8; + write_block(buf, 2); + fseek(fout, 24, SEEK_SET); + size_t vbtout_size = bdbout_size + extra_size + sizeof(head) + extra_size3; + buf[0] = vbtout_size; + buf[1] = vbtout_size >> 8; + write_block(buf, 2); + buf[0] = -csum; + write_block(buf, 1); + fclose(fin); + fclose(fout); + return 0; +}