summaryrefslogtreecommitdiff
path: root/util/kbc1126
diff options
context:
space:
mode:
authorIru Cai <mytbk920423@gmail.com>2017-03-26 12:05:32 +0800
committerPatrick Georgi <pgeorgi@google.com>2017-05-11 16:48:40 +0200
commit5fd00ce71a1bfd5cd36f9d42ab657a8e7d6c54f2 (patch)
tree0b57a49a7095eb93d15a8aecf450bce2f7649c5a /util/kbc1126
parent5f9fe7232a24e4abf88b6b642f4df462142cfd85 (diff)
util: Add tools for dumping and inserting KBC1126 firmware images.
Change-Id: Ic521b177b9602ff042312cccaaa89371db7c5855 Signed-off-by: Iru Cai <mytbk920423@gmail.com> Reviewed-on: https://review.coreboot.org/19071 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Arthur Heymans <arthur@aheymans.xyz>
Diffstat (limited to 'util/kbc1126')
-rw-r--r--util/kbc1126/README.md59
-rw-r--r--util/kbc1126/kbc1126_ec_dump.c124
-rw-r--r--util/kbc1126/kbc1126_ec_insert.c108
-rw-r--r--util/kbc1126/makefile12
4 files changed, 303 insertions, 0 deletions
diff --git a/util/kbc1126/README.md b/util/kbc1126/README.md
new file mode 100644
index 0000000000..8f39069793
--- /dev/null
+++ b/util/kbc1126/README.md
@@ -0,0 +1,59 @@
+KBC1126 firmware tools
+======================
+
+Many HP laptops use 8051-based SMSC KBC1098/KBC1126 as embedded
+controller. Two blobs can be found in the HP firmware images. The
+`kbc1126_ec_dump` and `kbc1126_ec_insert` tools are used to dump the
+two blobs from the factory firmware and insert them to the firmware
+image.
+
+
+Firmware format
+---------------
+
+We can easily find the BIOS region of the HP laptop firmware from the
+HP firmware update tool, which can be downloaded from the HP
+website. Now I take HP Elitebook 8470p as an example. This laptop has
+a 16MB flash chip, the last 5MB of which is the BIOS region.
+
+I use [radare2](https://radare.org) to analyze the firmware. Open the
+firmware image, and we can see 8 bytes at `$s-0x100` (`$s` means the
+image size).
+
+ [0x00000000]> x @ $s-0x100
+ - offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
+ 0x00ffff00 fff7 0008 f700 08ff 0000 0000 0000 0000 ................
+
+X86 machines map the firmware at the end of the memory address
+space. These 8 bytes tell the address of the two blobs, which we call
+FW1 (uses bytes 0-3) and FW2 (uses bytes 4-7).
+
+Let's look at FW1. The first two bytes mean the address of FW1 is
+0xfff700 (these two bytes use big endian), i.e. `$s-0x900`. Byte 2 and
+3 are just complements of byte 1 and 2 (in this case,
+0x0008=0xffff-0xfff7).
+
+ [0x00000000]> x @ $s-0x900
+ - offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
+ 0x00fff700 fc07 c13e 02ff 1000 0000 0000 0000 0000 ...>............
+
+Both FW1 and FW2 use the same format: the first two bytes is payload
+length, then a two-byte checksum, then the payload. The payload length
+and checksum are both in little endian. The checksum is
+[SYSV checksum](https://en.wikipedia.org/wiki/SYSV_checksum).
+
+
+How to use the tools
+--------------------
+
+`kbc1126_ec_dump` is used to dump FW1 and FW2. Run `kbc1126_ec_dump
+bios.rom`, then bios.rom.fw1 and bios.rom.fw2 are generated in the
+working directory.
+
+`kbc1126_ec_insert` will overwrite a firmware image by inserting FW1
+and FW2 in it. Please run it for its usage. You need to specify the
+offsets for FW1 and FW2. Using negative offset is recommended, which
+means the distance to the end of the image. For example, if we want to
+insert FW1 and FW2 at `$s-0x900` and `$s-0x90000` as the hp/8470p
+factory firmware to coreboot.rom, you can run `kbc1126_ec_insert
+coreboot.rom bios.rom.fw1 bios.rom.fw2 -0x900 -0x90000`.
diff --git a/util/kbc1126/kbc1126_ec_dump.c b/util/kbc1126/kbc1126_ec_dump.c
new file mode 100644
index 0000000000..7470012799
--- /dev/null
+++ b/util/kbc1126/kbc1126_ec_dump.c
@@ -0,0 +1,124 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2017 Iru Cai <mytbk920423@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static void usage(const char *s)
+{
+ printf("%s <rom file>\n", s);
+ exit(1);
+}
+
+static void FseekEnd(FILE *fp, long o)
+{
+ if (fseek(fp, o, SEEK_END) != 0) {
+ puts("fseek() error!\n");
+ exit(1);
+ }
+}
+
+void dump_fw(FILE *dst, FILE *src, long offset)
+{
+ static unsigned char buf[65536];
+
+ if (offset > 0)
+ offset -= 0x1000000;
+
+ printf("Dumping firmware at -0x%lx...", -offset);
+
+ FseekEnd(src, offset);
+ unsigned short len;
+ unsigned short cksum;
+ unsigned short _cksum = 0;
+ fread(&len, 2, 1, src);
+ fread(&cksum, 2, 1, src);
+ fread(buf, len, 1, src);
+
+ for (size_t i = 0; i < len; i++) {
+ _cksum += buf[i];
+ }
+ if (_cksum == cksum) {
+ puts("checksum ok");
+ } else {
+ puts("checksum fail");
+ exit(1);
+ }
+
+ fwrite(&len, 2, 1, dst);
+ fwrite(&cksum, 2, 1, dst);
+ fwrite(buf, len, 1, dst);
+}
+
+int main(int argc, char *argv[])
+{
+ if (argc != 2)
+ usage(argv[0]);
+
+ FILE *fp = fopen(argv[1], "rb+");
+
+ if (fp == NULL) {
+ puts("Error opening file!");
+ exit(1);
+ }
+
+ char *basename = strrchr(argv[1], '/');
+ if (basename == NULL)
+ basename = argv[1];
+ else
+ basename = basename + 1;
+
+ int len = strlen(basename);
+ char fn1[len + 5], fn2[len + 5];
+ strcpy(fn1, basename);
+ strcpy(fn2, basename);
+ strcat(fn1, ".fw1");
+ strcat(fn2, ".fw2");
+
+ FILE *fw1 = fopen(fn1, "wb+");
+ FILE *fw2 = fopen(fn2, "wb+");
+
+ long romsz;
+ FseekEnd(fp, -1);
+ romsz = ftell(fp) + 1;
+ printf("size of %s: 0x%lx\n", argv[1], romsz);
+
+ if (romsz & 0xff) {
+ puts("The ROM size must be multiple of 0x100");
+ exit(1);
+ }
+
+ /* read offset of fw1 and fw2 */
+ char offs[8];
+ FseekEnd(fp, -0x100);
+ fread(offs, 8, 1, fp);
+
+ assert(offs[0] + offs[2] == '\xff');
+ assert(offs[1] + offs[3] == '\xff');
+ assert(offs[4] + offs[6] == '\xff');
+ assert(offs[5] + offs[7] == '\xff');
+ long offw1 = (offs[0] << 16) | (offs[1] << 8);
+ long offw2 = (offs[4] << 16) | (offs[5] << 8);
+
+ dump_fw(fw1, fp, offw1);
+ dump_fw(fw2, fp, offw2);
+
+ fclose(fp);
+ fclose(fw1);
+ fclose(fw2);
+ return 0;
+}
diff --git a/util/kbc1126/kbc1126_ec_insert.c b/util/kbc1126/kbc1126_ec_insert.c
new file mode 100644
index 0000000000..6c19c0e97f
--- /dev/null
+++ b/util/kbc1126/kbc1126_ec_insert.c
@@ -0,0 +1,108 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2017 Iru Cai <mytbk920423@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+static void usage(const char *s)
+{
+ printf("%s <rom file> <fw1> <fw2> <fw1 offset> <fw2 offset>\n", s);
+ exit(1);
+}
+
+static void FseekEnd(FILE *fp, long o)
+{
+ if (fseek(fp, o, SEEK_END) != 0) {
+ puts("fseek() error!\n");
+ exit(1);
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ if (argc < 6)
+ usage(argv[0]);
+
+ FILE *fp = fopen(argv[1], "rb+");
+ FILE *fw1 = fopen(argv[2], "rb");
+ FILE *fw2 = fopen(argv[3], "rb");
+ long offset1 = strtol(argv[4], NULL, 0);
+ long offset2 = strtol(argv[5], NULL, 0);
+
+ if (fp == NULL || fw1 == NULL || fw2 == NULL) {
+ puts("Error opening file!");
+ exit(1);
+ }
+
+ if ((offset1 & 0xff) || (offset2 & 0xff)) {
+ puts("The offsets must be aligned to 0x100");
+ exit(1);
+ }
+
+ long romsz;
+ FseekEnd(fp, -1);
+ romsz = ftell(fp) + 1;
+ printf("size of %s: 0x%lx\n", argv[1], romsz);
+
+ if (romsz & 0xff) {
+ puts("The ROM size must be multiple of 0x100");
+ exit(1);
+ }
+
+ if (offset1 > 0)
+ offset1 = offset1 - romsz;
+
+ if (offset2 > 0)
+ offset2 = offset2 - romsz;
+
+ /* write two offsets to $s-0x100 */
+ char offs[8];
+ long os;
+ os = 0x1000000 + offset1;
+ offs[0] = os >> 16;
+ offs[1] = os >> 8;
+ offs[2] = 0xff - offs[0];
+ offs[3] = 0xff - offs[1];
+ os = 0x1000000 + offset2;
+ offs[4] = os >> 16;
+ offs[5] = os >> 8;
+ offs[6] = 0xff - offs[4];
+ offs[7] = 0xff - offs[5];
+ for (size_t i = 0; i < 8; i++) {
+ printf("%02hhx ", offs[i]);
+ }
+ puts("");
+ FseekEnd(fp, -0x100);
+ printf("writing to 0x%lx\n", ftell(fp));
+ fwrite(offs, 1, 8, fp);
+
+ /* write fw1 and fw2 */
+ char c;
+ FseekEnd(fp, offset1);
+ printf("writing to 0x%lx\n", ftell(fp));
+ while (fread(&c, 1, 1, fw1) == 1) {
+ fwrite(&c, 1, 1, fp);
+ }
+ FseekEnd(fp, offset2);
+ printf("writing to 0x%lx\n", ftell(fp));
+ while (fread(&c, 1, 1, fw2) == 1) {
+ fwrite(&c, 1, 1, fp);
+ }
+
+ fclose(fp);
+ fclose(fw1);
+ fclose(fw2);
+ return 0;
+}
diff --git a/util/kbc1126/makefile b/util/kbc1126/makefile
new file mode 100644
index 0000000000..48268747d6
--- /dev/null
+++ b/util/kbc1126/makefile
@@ -0,0 +1,12 @@
+obj = kbc1126_ec_dump kbc1126_ec_insert
+HOSTCC := $(if $(shell type gcc 2>/dev/null),gcc,cc)
+
+all: $(obj)
+
+%: %.c
+ $(HOSTCC) -Wall -o $@ $<
+
+clean:
+ rm -f kbc1126_ec_dump kbc1126_ec_insert
+
+.PHONY: all clean