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>
This commit is contained in:
Iru Cai 2017-03-26 12:05:32 +08:00 committed by Patrick Georgi
parent 5f9fe7232a
commit 5fd00ce71a
5 changed files with 305 additions and 0 deletions

2
.gitignore vendored
View File

@ -115,6 +115,8 @@ util/superiotool/superiotool
util/vgabios/testbios util/vgabios/testbios
util/viatool/viatool util/viatool/viatool
util/autoport/autoport util/autoport/autoport
util/kbc1126/kbc1126_ec_dump
util/kbc1126/kbc1126_ec_insert
documentation/*.aux documentation/*.aux
documentation/*.idx documentation/*.idx

59
util/kbc1126/README.md Normal file
View File

@ -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`.

View File

@ -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;
}

View File

@ -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;
}

12
util/kbc1126/makefile Normal file
View File

@ -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