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:
parent
5f9fe7232a
commit
5fd00ce71a
|
@ -115,6 +115,8 @@ util/superiotool/superiotool
|
|||
util/vgabios/testbios
|
||||
util/viatool/viatool
|
||||
util/autoport/autoport
|
||||
util/kbc1126/kbc1126_ec_dump
|
||||
util/kbc1126/kbc1126_ec_insert
|
||||
|
||||
documentation/*.aux
|
||||
documentation/*.idx
|
||||
|
|
|
@ -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`.
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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
|
Loading…
Reference in New Issue