mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2025-11-24 18:25:10 +00:00
futility: put all the BIOS stuff into a separate file
This moves the functions that handle BIOS file types into a separate set of source files. BIOSes are constructed from other components arranged in particular ways, so they shouldn't be mixed in with the code specifically for those components. BUG=chromium:231574 BRANCH=none TEST=make runtests Signed-off-by: Bill Richardson <wfrichar@chromium.org> Change-Id: I15c3fec61498925f9b8c672092fd97e7ea2d90e9 Reviewed-on: https://chromium-review.googlesource.com/262898 Reviewed-by: Randall Spangler <rspangler@chromium.org>
This commit is contained in:
committed by
ChromeOS Commit Bot
parent
08d52b9bf5
commit
091c7b17f0
2
Makefile
2
Makefile
@@ -625,7 +625,7 @@ FUTIL_SRCS = \
|
|||||||
futility/cmd_vbutil_key.c \
|
futility/cmd_vbutil_key.c \
|
||||||
futility/cmd_vbutil_keyblock.c \
|
futility/cmd_vbutil_keyblock.c \
|
||||||
futility/file_type.c \
|
futility/file_type.c \
|
||||||
futility/traversal.c \
|
futility/file_type_bios.c \
|
||||||
futility/vb1_helper.c \
|
futility/vb1_helper.c \
|
||||||
futility/vb2_helper.c
|
futility/vb2_helper.c
|
||||||
|
|
||||||
|
|||||||
@@ -20,14 +20,12 @@
|
|||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "bmpblk_header.h"
|
|
||||||
#include "file_type.h"
|
#include "file_type.h"
|
||||||
|
#include "file_type_bios.h"
|
||||||
#include "fmap.h"
|
#include "fmap.h"
|
||||||
#include "futility.h"
|
#include "futility.h"
|
||||||
#include "futility_options.h"
|
#include "futility_options.h"
|
||||||
#include "gbb_header.h"
|
|
||||||
#include "host_common.h"
|
#include "host_common.h"
|
||||||
#include "traversal.h"
|
|
||||||
#include "util_misc.h"
|
#include "util_misc.h"
|
||||||
#include "vb1_helper.h"
|
#include "vb1_helper.h"
|
||||||
#include "vboot_common.h"
|
#include "vboot_common.h"
|
||||||
@@ -38,42 +36,7 @@ struct show_option_s show_option = {
|
|||||||
.type = FILE_TYPE_UNKNOWN,
|
.type = FILE_TYPE_UNKNOWN,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Stuff for BIOS images. */
|
void show_pubkey(VbPublicKey *pubkey, const char *sp)
|
||||||
|
|
||||||
/* Forward declarations */
|
|
||||||
static int fmap_fw_main(const char *name, uint8_t *buf, uint32_t len,
|
|
||||||
void *data);
|
|
||||||
|
|
||||||
/* These are the functions we'll call for each FMAP area. */
|
|
||||||
static int (*fmap_func[])(const char *name, uint8_t *buf, uint32_t len,
|
|
||||||
void *data) = {
|
|
||||||
ft_show_gbb,
|
|
||||||
fmap_fw_main,
|
|
||||||
fmap_fw_main,
|
|
||||||
ft_show_fw_preamble,
|
|
||||||
ft_show_fw_preamble,
|
|
||||||
};
|
|
||||||
BUILD_ASSERT(ARRAY_SIZE(fmap_func) == NUM_BIOS_COMPONENTS);
|
|
||||||
|
|
||||||
/* Where is the component we're looking at? */
|
|
||||||
struct bios_area_s {
|
|
||||||
uint32_t offset; /* to avoid pointer math */
|
|
||||||
uint8_t *buf;
|
|
||||||
uint32_t len;
|
|
||||||
uint32_t is_valid;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* When looking at the FMAP areas, we need to gather some state for later. */
|
|
||||||
struct show_state_s {
|
|
||||||
/* Current component */
|
|
||||||
enum bios_component c;
|
|
||||||
/* Other activites, possibly before or after the current one */
|
|
||||||
struct bios_area_s area[NUM_BIOS_COMPONENTS];
|
|
||||||
struct bios_area_s recovery_key;
|
|
||||||
struct bios_area_s rootkey;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void show_key(VbPublicKey *pubkey, const char *sp)
|
|
||||||
{
|
{
|
||||||
printf("%sVboot API: 1.0\n", sp);
|
printf("%sVboot API: 1.0\n", sp);
|
||||||
printf("%sAlgorithm: %" PRIu64 " %s\n", sp, pubkey->algorithm,
|
printf("%sAlgorithm: %" PRIu64 " %s\n", sp, pubkey->algorithm,
|
||||||
@@ -129,7 +92,7 @@ int ft_show_pubkey(const char *name, uint8_t *buf, uint32_t len, void *data)
|
|||||||
}
|
}
|
||||||
|
|
||||||
printf("Public Key file: %s\n", name);
|
printf("Public Key file: %s\n", name);
|
||||||
show_key(pubkey, " ");
|
show_pubkey(pubkey, " ");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -166,108 +129,6 @@ int ft_show_privkey(const char *name, uint8_t *buf, uint32_t len, void *data)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ft_show_gbb(const char *name, uint8_t *buf, uint32_t len, void *data)
|
|
||||||
{
|
|
||||||
GoogleBinaryBlockHeader *gbb = (GoogleBinaryBlockHeader *)buf;
|
|
||||||
struct show_state_s *state = (struct show_state_s *)data;
|
|
||||||
VbPublicKey *pubkey;
|
|
||||||
BmpBlockHeader *bmp;
|
|
||||||
int retval = 0;
|
|
||||||
uint32_t maxlen = 0;
|
|
||||||
|
|
||||||
if (!len) {
|
|
||||||
printf("GBB header: %s <invalid>\n", name);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* It looks like a GBB or we wouldn't be called. */
|
|
||||||
if (!futil_valid_gbb_header(gbb, len, &maxlen))
|
|
||||||
retval = 1;
|
|
||||||
|
|
||||||
printf("GBB header: %s\n", name);
|
|
||||||
printf(" Version: %d.%d\n",
|
|
||||||
gbb->major_version, gbb->minor_version);
|
|
||||||
printf(" Flags: 0x%08x\n", gbb->flags);
|
|
||||||
printf(" Regions: offset size\n");
|
|
||||||
printf(" hwid 0x%08x 0x%08x\n",
|
|
||||||
gbb->hwid_offset, gbb->hwid_size);
|
|
||||||
printf(" bmpvf 0x%08x 0x%08x\n",
|
|
||||||
gbb->bmpfv_offset, gbb->bmpfv_size);
|
|
||||||
printf(" rootkey 0x%08x 0x%08x\n",
|
|
||||||
gbb->rootkey_offset, gbb->rootkey_size);
|
|
||||||
printf(" recovery_key 0x%08x 0x%08x\n",
|
|
||||||
gbb->recovery_key_offset, gbb->recovery_key_size);
|
|
||||||
|
|
||||||
printf(" Size: 0x%08x / 0x%08x%s\n",
|
|
||||||
maxlen, len, maxlen > len ? " (not enough)" : "");
|
|
||||||
|
|
||||||
if (retval) {
|
|
||||||
printf("GBB header is invalid, ignoring content\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("GBB content:\n");
|
|
||||||
printf(" HWID: %s\n", buf + gbb->hwid_offset);
|
|
||||||
print_hwid_digest(gbb, " digest: ", "\n");
|
|
||||||
|
|
||||||
pubkey = (VbPublicKey *)(buf + gbb->rootkey_offset);
|
|
||||||
if (PublicKeyLooksOkay(pubkey, gbb->rootkey_size)) {
|
|
||||||
if (state) {
|
|
||||||
state->rootkey.offset =
|
|
||||||
state->area[BIOS_FMAP_GBB].offset +
|
|
||||||
gbb->rootkey_offset;
|
|
||||||
state->rootkey.buf = buf + gbb->rootkey_offset;
|
|
||||||
state->rootkey.len = gbb->rootkey_size;
|
|
||||||
state->rootkey.is_valid = 1;
|
|
||||||
}
|
|
||||||
printf(" Root Key:\n");
|
|
||||||
show_key(pubkey, " ");
|
|
||||||
} else {
|
|
||||||
retval = 1;
|
|
||||||
printf(" Root Key: <invalid>\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
pubkey = (VbPublicKey *)(buf + gbb->recovery_key_offset);
|
|
||||||
if (PublicKeyLooksOkay(pubkey, gbb->recovery_key_size)) {
|
|
||||||
if (state) {
|
|
||||||
state->recovery_key.offset =
|
|
||||||
state->area[BIOS_FMAP_GBB].offset +
|
|
||||||
gbb->recovery_key_offset;
|
|
||||||
state->recovery_key.buf = buf +
|
|
||||||
gbb->recovery_key_offset;
|
|
||||||
state->recovery_key.len = gbb->recovery_key_size;
|
|
||||||
state->recovery_key.is_valid = 1;
|
|
||||||
}
|
|
||||||
printf(" Recovery Key:\n");
|
|
||||||
show_key(pubkey, " ");
|
|
||||||
} else {
|
|
||||||
retval = 1;
|
|
||||||
printf(" Recovery Key: <invalid>\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
bmp = (BmpBlockHeader *)(buf + gbb->bmpfv_offset);
|
|
||||||
if (0 != memcmp(bmp, BMPBLOCK_SIGNATURE, BMPBLOCK_SIGNATURE_SIZE)) {
|
|
||||||
printf(" BmpBlock: <invalid>\n");
|
|
||||||
/* We don't support older BmpBlock formats, so we can't
|
|
||||||
* be strict about this. */
|
|
||||||
} else {
|
|
||||||
printf(" BmpBlock:\n");
|
|
||||||
printf(" Version: %d.%d\n",
|
|
||||||
bmp->major_version, bmp->minor_version);
|
|
||||||
printf(" Localizations: %d\n",
|
|
||||||
bmp->number_of_localizations);
|
|
||||||
printf(" Screen layouts: %d\n",
|
|
||||||
bmp->number_of_screenlayouts);
|
|
||||||
printf(" Image infos: %d\n",
|
|
||||||
bmp->number_of_imageinfos);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!retval && state)
|
|
||||||
state->area[BIOS_FMAP_GBB].is_valid = 1;
|
|
||||||
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ft_show_keyblock(const char *name, uint8_t *buf, uint32_t len, void *data)
|
int ft_show_keyblock(const char *name, uint8_t *buf, uint32_t len, void *data)
|
||||||
{
|
{
|
||||||
VbKeyBlockHeader *block = (VbKeyBlockHeader *)buf;
|
VbKeyBlockHeader *block = (VbKeyBlockHeader *)buf;
|
||||||
@@ -294,38 +155,11 @@ int ft_show_keyblock(const char *name, uint8_t *buf, uint32_t len, void *data)
|
|||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* This handles FW_MAIN_A and FW_MAIN_B while processing a BIOS image.
|
|
||||||
*
|
|
||||||
* The data is just the RW firmware blob, so there's nothing useful to show
|
|
||||||
* about it. We'll just mark it as present so when we encounter corresponding
|
|
||||||
* VBLOCK area, we'll have this to verify.
|
|
||||||
*/
|
|
||||||
static int fmap_fw_main(const char *name, uint8_t *buf, uint32_t len,
|
|
||||||
void *data)
|
|
||||||
{
|
|
||||||
struct show_state_s *state = (struct show_state_s *)data;
|
|
||||||
|
|
||||||
if (!len) {
|
|
||||||
printf("Firmware body: %s <invalid>\n", name);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("Firmware body: %s\n", name);
|
|
||||||
printf(" Offset: 0x%08x\n",
|
|
||||||
state->area[state->c].offset);
|
|
||||||
printf(" Size: 0x%08x\n", len);
|
|
||||||
|
|
||||||
state->area[state->c].is_valid = 1;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ft_show_fw_preamble(const char *name, uint8_t *buf, uint32_t len,
|
int ft_show_fw_preamble(const char *name, uint8_t *buf, uint32_t len,
|
||||||
void *data)
|
void *data)
|
||||||
{
|
{
|
||||||
VbKeyBlockHeader *key_block = (VbKeyBlockHeader *)buf;
|
VbKeyBlockHeader *key_block = (VbKeyBlockHeader *)buf;
|
||||||
struct show_state_s *state = (struct show_state_s *)data;
|
struct bios_state_s *state = (struct bios_state_s *)data;
|
||||||
VbPublicKey *sign_key = show_option.k;
|
VbPublicKey *sign_key = show_option.k;
|
||||||
uint8_t *fv_data = show_option.fv;
|
uint8_t *fv_data = show_option.fv;
|
||||||
uint64_t fv_size = show_option.fv_size;
|
uint64_t fv_size = show_option.fv_size;
|
||||||
@@ -346,7 +180,6 @@ int ft_show_fw_preamble(const char *name, uint8_t *buf, uint32_t len,
|
|||||||
* so we'll have to get any keys or data from options.
|
* so we'll have to get any keys or data from options.
|
||||||
*/
|
*/
|
||||||
if (state) {
|
if (state) {
|
||||||
|
|
||||||
if (!sign_key && state->rootkey.is_valid)
|
if (!sign_key && state->rootkey.is_valid)
|
||||||
/* BIOS should have a rootkey in the GBB */
|
/* BIOS should have a rootkey in the GBB */
|
||||||
sign_key = (VbPublicKey *)state->rootkey.buf;
|
sign_key = (VbPublicKey *)state->rootkey.buf;
|
||||||
@@ -449,55 +282,6 @@ done:
|
|||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ft_show_bios(const char *name, uint8_t *buf, uint32_t len, void *data)
|
|
||||||
{
|
|
||||||
FmapHeader *fmap;
|
|
||||||
FmapAreaHeader *ah = 0;
|
|
||||||
char ah_name[FMAP_NAMELEN + 1];
|
|
||||||
int i;
|
|
||||||
int retval = 0;
|
|
||||||
struct show_state_s state;
|
|
||||||
|
|
||||||
memset(&state, 0, sizeof(state));
|
|
||||||
|
|
||||||
printf("BIOS: %s\n", name);
|
|
||||||
|
|
||||||
/* We've already checked, so we know this will work. */
|
|
||||||
fmap = fmap_find(buf, len);
|
|
||||||
for (i = 0; i < NUM_BIOS_COMPONENTS; i++) {
|
|
||||||
/* We know one of these will work, too */
|
|
||||||
if (fmap_find_by_name(buf, len, fmap,
|
|
||||||
bios_area[i].name, &ah) ||
|
|
||||||
fmap_find_by_name(buf, len, fmap,
|
|
||||||
bios_area[i].oldname, &ah)) {
|
|
||||||
/* But the file might be truncated */
|
|
||||||
fmap_limit_area(ah, len);
|
|
||||||
/* The name is not necessarily null-terminated */
|
|
||||||
snprintf(ah_name, sizeof(ah_name), "%s", ah->area_name);
|
|
||||||
|
|
||||||
/* Update the state we're passing around */
|
|
||||||
state.c = i;
|
|
||||||
state.area[i].offset = ah->area_offset;
|
|
||||||
state.area[i].buf = buf + ah->area_offset;
|
|
||||||
state.area[i].len = ah->area_size;
|
|
||||||
|
|
||||||
Debug("%s() showing FMAP area %d (%s),"
|
|
||||||
" offset=0x%08x len=0x%08x\n",
|
|
||||||
__func__, i, ah_name,
|
|
||||||
ah->area_offset, ah->area_size);
|
|
||||||
|
|
||||||
/* Go look at it. */
|
|
||||||
if (fmap_func[i])
|
|
||||||
retval += fmap_func[i](ah_name,
|
|
||||||
state.area[i].buf,
|
|
||||||
state.area[i].len,
|
|
||||||
&state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ft_show_kernel_preamble(const char *name, uint8_t *buf, uint32_t len,
|
int ft_show_kernel_preamble(const char *name, uint8_t *buf, uint32_t len,
|
||||||
void *data)
|
void *data)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -17,15 +17,14 @@
|
|||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "bmpblk_header.h"
|
|
||||||
#include "file_type.h"
|
#include "file_type.h"
|
||||||
|
#include "file_type_bios.h"
|
||||||
#include "fmap.h"
|
#include "fmap.h"
|
||||||
#include "futility.h"
|
#include "futility.h"
|
||||||
#include "futility_options.h"
|
#include "futility_options.h"
|
||||||
#include "gbb_header.h"
|
#include "gbb_header.h"
|
||||||
#include "host_common.h"
|
#include "host_common.h"
|
||||||
#include "kernel_blob.h"
|
#include "kernel_blob.h"
|
||||||
#include "traversal.h"
|
|
||||||
#include "util_misc.h"
|
#include "util_misc.h"
|
||||||
#include "vb1_helper.h"
|
#include "vb1_helper.h"
|
||||||
#include "vboot_common.h"
|
#include "vboot_common.h"
|
||||||
@@ -49,41 +48,6 @@ static int no_opt_if(int expr, const char *optname)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Stuff for BIOS images */
|
|
||||||
|
|
||||||
/* Forward declarations */
|
|
||||||
static int fmap_fw_main(const char *name, uint8_t *buf, uint32_t len,
|
|
||||||
void *data);
|
|
||||||
static int fmap_fw_preamble(const char *name, uint8_t *buf, uint32_t len,
|
|
||||||
void *data);
|
|
||||||
|
|
||||||
/* These are the functions we'll call for each FMAP area. */
|
|
||||||
static int (*fmap_func[])(const char *name, uint8_t *buf, uint32_t len,
|
|
||||||
void *data) = {
|
|
||||||
0,
|
|
||||||
fmap_fw_main,
|
|
||||||
fmap_fw_main,
|
|
||||||
fmap_fw_preamble,
|
|
||||||
fmap_fw_preamble,
|
|
||||||
};
|
|
||||||
BUILD_ASSERT(ARRAY_SIZE(fmap_func) == NUM_BIOS_COMPONENTS);
|
|
||||||
|
|
||||||
/* Where is the component we're looking at? */
|
|
||||||
struct bios_area_s {
|
|
||||||
uint8_t *buf;
|
|
||||||
uint32_t len;
|
|
||||||
uint32_t is_valid;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* When looking at the FMAP areas, we need to gather some state for later. */
|
|
||||||
struct sign_state_s {
|
|
||||||
/* Current component */
|
|
||||||
enum bios_component c;
|
|
||||||
/* Other activites, possibly before or after the current one */
|
|
||||||
struct bios_area_s area[NUM_BIOS_COMPONENTS];
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/* This wraps/signs a public key, producing a keyblock. */
|
/* This wraps/signs a public key, producing a keyblock. */
|
||||||
int ft_sign_pubkey(const char *name, uint8_t *buf, uint32_t len, void *data)
|
int ft_sign_pubkey(const char *name, uint8_t *buf, uint32_t len, void *data)
|
||||||
{
|
{
|
||||||
@@ -124,85 +88,6 @@ int ft_sign_pubkey(const char *name, uint8_t *buf, uint32_t len, void *data)
|
|||||||
NULL, 0);
|
NULL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* This handles FW_MAIN_A and FW_MAIN_B while signing a BIOS image. The data is
|
|
||||||
* just the RW firmware blob so there's nothing useful to do with it, but we'll
|
|
||||||
* mark it as valid so that we'll know that this FMAP area exists and can
|
|
||||||
* be signed.
|
|
||||||
*/
|
|
||||||
static int fmap_fw_main(const char *name, uint8_t *buf, uint32_t len,
|
|
||||||
void *data)
|
|
||||||
{
|
|
||||||
struct sign_state_s *state = (struct sign_state_s *)data;
|
|
||||||
state->area[state->c].is_valid = 1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This handles VBLOCK_A and VBLOCK_B while processing a BIOS image. We don't
|
|
||||||
* do any signing here. We just check to see if the existing FMAP area contains
|
|
||||||
* a firmware preamble so we can preserve its contents. We do the signing once
|
|
||||||
* we've looked over all the components.
|
|
||||||
*/
|
|
||||||
static int fmap_fw_preamble(const char *name, uint8_t *buf, uint32_t len,
|
|
||||||
void *data)
|
|
||||||
{
|
|
||||||
VbKeyBlockHeader *key_block = (VbKeyBlockHeader *)buf;
|
|
||||||
struct sign_state_s *state = (struct sign_state_s *)data;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If we have a valid keyblock and fw_preamble, then we can use them to
|
|
||||||
* determine the size of the firmware body. Otherwise, we'll have to
|
|
||||||
* just sign the whole region.
|
|
||||||
*/
|
|
||||||
if (VBOOT_SUCCESS != KeyBlockVerify(key_block, len, NULL, 1)) {
|
|
||||||
fprintf(stderr, "Warning: %s keyblock is invalid. "
|
|
||||||
"Signing the entire FW FMAP region...\n", name);
|
|
||||||
goto whatever;
|
|
||||||
}
|
|
||||||
|
|
||||||
RSAPublicKey *rsa = PublicKeyToRSA(&key_block->data_key);
|
|
||||||
if (!rsa) {
|
|
||||||
fprintf(stderr, "Warning: %s public key is invalid. "
|
|
||||||
"Signing the entire FW FMAP region...\n", name);
|
|
||||||
goto whatever;
|
|
||||||
}
|
|
||||||
uint32_t more = key_block->key_block_size;
|
|
||||||
VbFirmwarePreambleHeader *preamble =
|
|
||||||
(VbFirmwarePreambleHeader *)(buf + more);
|
|
||||||
uint32_t fw_size = preamble->body_signature.data_size;
|
|
||||||
struct bios_area_s *fw_body_area = 0;
|
|
||||||
|
|
||||||
switch (state->c) {
|
|
||||||
case BIOS_FMAP_VBLOCK_A:
|
|
||||||
fw_body_area = &state->area[BIOS_FMAP_FW_MAIN_A];
|
|
||||||
/* Preserve the flags if they're not specified */
|
|
||||||
if (!sign_option.flags_specified)
|
|
||||||
sign_option.flags = preamble->flags;
|
|
||||||
break;
|
|
||||||
case BIOS_FMAP_VBLOCK_B:
|
|
||||||
fw_body_area = &state->area[BIOS_FMAP_FW_MAIN_B];
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
DIE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fw_size > fw_body_area->len) {
|
|
||||||
fprintf(stderr,
|
|
||||||
"%s says the firmware is larger than we have\n",
|
|
||||||
name);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Update the firmware size */
|
|
||||||
fw_body_area->len = fw_size;
|
|
||||||
|
|
||||||
whatever:
|
|
||||||
state->area[state->c].is_valid = 1;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ft_sign_raw_kernel(const char *name, uint8_t *buf, uint32_t len,
|
int ft_sign_raw_kernel(const char *name, uint8_t *buf, uint32_t len,
|
||||||
void *data)
|
void *data)
|
||||||
{
|
{
|
||||||
@@ -383,176 +268,6 @@ int ft_sign_raw_firmware(const char *name, uint8_t *buf, uint32_t len,
|
|||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int write_new_preamble(struct bios_area_s *vblock,
|
|
||||||
struct bios_area_s *fw_body,
|
|
||||||
VbPrivateKey *signkey,
|
|
||||||
VbKeyBlockHeader *keyblock)
|
|
||||||
{
|
|
||||||
VbSignature *body_sig;
|
|
||||||
VbFirmwarePreambleHeader *preamble;
|
|
||||||
|
|
||||||
body_sig = CalculateSignature(fw_body->buf, fw_body->len, signkey);
|
|
||||||
if (!body_sig) {
|
|
||||||
fprintf(stderr, "Error calculating body signature\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
preamble = CreateFirmwarePreamble(sign_option.version,
|
|
||||||
sign_option.kernel_subkey,
|
|
||||||
body_sig,
|
|
||||||
signkey,
|
|
||||||
sign_option.flags);
|
|
||||||
if (!preamble) {
|
|
||||||
fprintf(stderr, "Error creating firmware preamble.\n");
|
|
||||||
free(body_sig);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Write the new keyblock */
|
|
||||||
uint32_t more = keyblock->key_block_size;
|
|
||||||
memcpy(vblock->buf, keyblock, more);
|
|
||||||
/* and the new preamble */
|
|
||||||
memcpy(vblock->buf + more, preamble, preamble->preamble_size);
|
|
||||||
|
|
||||||
free(preamble);
|
|
||||||
free(body_sig);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int write_loem(const char *ab, struct bios_area_s *vblock)
|
|
||||||
{
|
|
||||||
char filename[PATH_MAX];
|
|
||||||
int n;
|
|
||||||
n = snprintf(filename, sizeof(filename), "%s/vblock_%s.%s",
|
|
||||||
sign_option.loemdir ? sign_option.loemdir : ".",
|
|
||||||
ab, sign_option.loemid);
|
|
||||||
if (n >= sizeof(filename)) {
|
|
||||||
fprintf(stderr, "LOEM args produce bogus filename\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
FILE *fp = fopen(filename, "w");
|
|
||||||
if (!fp) {
|
|
||||||
fprintf(stderr, "Can't open %s for writing: %s\n",
|
|
||||||
filename, strerror(errno));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (1 != fwrite(vblock->buf, vblock->len, 1, fp)) {
|
|
||||||
fprintf(stderr, "Can't write to %s: %s\n",
|
|
||||||
filename, strerror(errno));
|
|
||||||
fclose(fp);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (fclose(fp)) {
|
|
||||||
fprintf(stderr, "Failed closing loem output: %s\n",
|
|
||||||
strerror(errno));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* This signs a full BIOS image after it's been traversed. */
|
|
||||||
static int sign_bios_at_end(struct sign_state_s *state)
|
|
||||||
{
|
|
||||||
struct bios_area_s *vblock_a = &state->area[BIOS_FMAP_VBLOCK_A];
|
|
||||||
struct bios_area_s *vblock_b = &state->area[BIOS_FMAP_VBLOCK_B];
|
|
||||||
struct bios_area_s *fw_a = &state->area[BIOS_FMAP_FW_MAIN_A];
|
|
||||||
struct bios_area_s *fw_b = &state->area[BIOS_FMAP_FW_MAIN_B];
|
|
||||||
int retval = 0;
|
|
||||||
|
|
||||||
if (!vblock_a->is_valid || !vblock_b->is_valid ||
|
|
||||||
!fw_a->is_valid || !fw_b->is_valid) {
|
|
||||||
fprintf(stderr, "Something's wrong. Not changing anything\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Do A & B differ ? */
|
|
||||||
if (fw_a->len != fw_b->len ||
|
|
||||||
memcmp(fw_a->buf, fw_b->buf, fw_a->len)) {
|
|
||||||
/* Yes, must use DEV keys for A */
|
|
||||||
if (!sign_option.devsignprivate || !sign_option.devkeyblock) {
|
|
||||||
fprintf(stderr,
|
|
||||||
"FW A & B differ. DEV keys are required.\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
retval |= write_new_preamble(vblock_a, fw_a,
|
|
||||||
sign_option.devsignprivate,
|
|
||||||
sign_option.devkeyblock);
|
|
||||||
} else {
|
|
||||||
retval |= write_new_preamble(vblock_a, fw_a,
|
|
||||||
sign_option.signprivate,
|
|
||||||
sign_option.keyblock);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* FW B is always normal keys */
|
|
||||||
retval |= write_new_preamble(vblock_b, fw_b,
|
|
||||||
sign_option.signprivate,
|
|
||||||
sign_option.keyblock);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (sign_option.loemid) {
|
|
||||||
retval |= write_loem("A", vblock_a);
|
|
||||||
retval |= write_loem("B", vblock_b);
|
|
||||||
}
|
|
||||||
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int ft_sign_bios(const char *name, uint8_t *buf, uint32_t len, void *data)
|
|
||||||
{
|
|
||||||
FmapHeader *fmap;
|
|
||||||
FmapAreaHeader *ah = 0;
|
|
||||||
char ah_name[FMAP_NAMELEN + 1];
|
|
||||||
int i;
|
|
||||||
int retval = 0;
|
|
||||||
struct sign_state_s state;
|
|
||||||
|
|
||||||
memset(&state, 0, sizeof(state));
|
|
||||||
|
|
||||||
/* We've already checked, so we know this will work. */
|
|
||||||
fmap = fmap_find(buf, len);
|
|
||||||
for (i = 0; i < NUM_BIOS_COMPONENTS; i++) {
|
|
||||||
/* We know one of these will work, too */
|
|
||||||
if (fmap_find_by_name(buf, len, fmap,
|
|
||||||
bios_area[i].name, &ah) ||
|
|
||||||
fmap_find_by_name(buf, len, fmap,
|
|
||||||
bios_area[i].oldname, &ah)) {
|
|
||||||
/* But the file might be truncated */
|
|
||||||
fmap_limit_area(ah, len);
|
|
||||||
/* The name is not necessarily null-terminated */
|
|
||||||
snprintf(ah_name, sizeof(ah_name), "%s", ah->area_name);
|
|
||||||
|
|
||||||
/* Update the state we're passing around */
|
|
||||||
state.c = i;
|
|
||||||
state.area[i].buf = buf + ah->area_offset;
|
|
||||||
state.area[i].len = ah->area_size;
|
|
||||||
|
|
||||||
Debug("%s() examining FMAP area %d (%s),"
|
|
||||||
" offset=0x%08x len=0x%08x\n",
|
|
||||||
__func__, i, ah_name,
|
|
||||||
ah->area_offset, ah->area_size);
|
|
||||||
|
|
||||||
/* Go look at it, but abort on error */
|
|
||||||
if (fmap_func[i])
|
|
||||||
retval += fmap_func[i](ah_name,
|
|
||||||
state.area[i].buf,
|
|
||||||
state.area[i].len,
|
|
||||||
&state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
retval += sign_bios_at_end(&state);
|
|
||||||
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char usage_pubkey[] = "\n"
|
static const char usage_pubkey[] = "\n"
|
||||||
"To sign a public key / create a new keyblock:\n"
|
"To sign a public key / create a new keyblock:\n"
|
||||||
"\n"
|
"\n"
|
||||||
|
|||||||
521
futility/file_type_bios.c
Normal file
521
futility/file_type_bios.c
Normal file
@@ -0,0 +1,521 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2014 The Chromium OS Authors. All rights reserved.
|
||||||
|
* Use of this source code is governed by a BSD-style license that can be
|
||||||
|
* found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
#include <errno.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "bmpblk_header.h"
|
||||||
|
#include "fmap.h"
|
||||||
|
#include "file_type.h"
|
||||||
|
#include "file_type_bios.h"
|
||||||
|
#include "futility.h"
|
||||||
|
#include "futility_options.h"
|
||||||
|
#include "gbb_header.h"
|
||||||
|
#include "host_common.h"
|
||||||
|
#include "vb1_helper.h"
|
||||||
|
|
||||||
|
static const char * const fmap_name[] = {
|
||||||
|
"GBB", /* BIOS_FMAP_GBB */
|
||||||
|
"FW_MAIN_A", /* BIOS_FMAP_FW_MAIN_A */
|
||||||
|
"FW_MAIN_B", /* BIOS_FMAP_FW_MAIN_B */
|
||||||
|
"VBLOCK_A", /* BIOS_FMAP_VBLOCK_A */
|
||||||
|
"VBLOCK_B", /* BIOS_FMAP_VBLOCK_B */
|
||||||
|
};
|
||||||
|
BUILD_ASSERT(ARRAY_SIZE(fmap_name) == NUM_BIOS_COMPONENTS);
|
||||||
|
|
||||||
|
static const char * const fmap_oldname[] = {
|
||||||
|
"GBB Area", /* BIOS_FMAP_GBB */
|
||||||
|
"Firmware A Data", /* BIOS_FMAP_FW_MAIN_A */
|
||||||
|
"Firmware B Data", /* BIOS_FMAP_FW_MAIN_B */
|
||||||
|
"Firmware A Key", /* BIOS_FMAP_VBLOCK_A */
|
||||||
|
"Firmware B Key", /* BIOS_FMAP_VBLOCK_B */
|
||||||
|
};
|
||||||
|
BUILD_ASSERT(ARRAY_SIZE(fmap_oldname) == NUM_BIOS_COMPONENTS);
|
||||||
|
|
||||||
|
static void fmap_limit_area(FmapAreaHeader *ah, uint32_t len)
|
||||||
|
{
|
||||||
|
uint32_t sum = ah->area_offset + ah->area_size;
|
||||||
|
if (sum < ah->area_size || sum > len) {
|
||||||
|
Debug("%s(%s) 0x%x + 0x%x > 0x%x\n",
|
||||||
|
__func__, ah->area_name,
|
||||||
|
ah->area_offset, ah->area_size, len);
|
||||||
|
ah->area_offset = 0;
|
||||||
|
ah->area_size = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Show functions **/
|
||||||
|
|
||||||
|
int ft_show_gbb(const char *name, uint8_t *buf, uint32_t len, void *data)
|
||||||
|
{
|
||||||
|
GoogleBinaryBlockHeader *gbb = (GoogleBinaryBlockHeader *)buf;
|
||||||
|
struct bios_state_s *state = (struct bios_state_s *)data;
|
||||||
|
VbPublicKey *pubkey;
|
||||||
|
BmpBlockHeader *bmp;
|
||||||
|
int retval = 0;
|
||||||
|
uint32_t maxlen = 0;
|
||||||
|
|
||||||
|
if (!len) {
|
||||||
|
printf("GBB header: %s <invalid>\n", name);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* It looks like a GBB or we wouldn't be called. */
|
||||||
|
if (!futil_valid_gbb_header(gbb, len, &maxlen))
|
||||||
|
retval = 1;
|
||||||
|
|
||||||
|
printf("GBB header: %s\n", name);
|
||||||
|
printf(" Version: %d.%d\n",
|
||||||
|
gbb->major_version, gbb->minor_version);
|
||||||
|
printf(" Flags: 0x%08x\n", gbb->flags);
|
||||||
|
printf(" Regions: offset size\n");
|
||||||
|
printf(" hwid 0x%08x 0x%08x\n",
|
||||||
|
gbb->hwid_offset, gbb->hwid_size);
|
||||||
|
printf(" bmpvf 0x%08x 0x%08x\n",
|
||||||
|
gbb->bmpfv_offset, gbb->bmpfv_size);
|
||||||
|
printf(" rootkey 0x%08x 0x%08x\n",
|
||||||
|
gbb->rootkey_offset, gbb->rootkey_size);
|
||||||
|
printf(" recovery_key 0x%08x 0x%08x\n",
|
||||||
|
gbb->recovery_key_offset, gbb->recovery_key_size);
|
||||||
|
|
||||||
|
printf(" Size: 0x%08x / 0x%08x%s\n",
|
||||||
|
maxlen, len, maxlen > len ? " (not enough)" : "");
|
||||||
|
|
||||||
|
if (retval) {
|
||||||
|
printf("GBB header is invalid, ignoring content\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("GBB content:\n");
|
||||||
|
printf(" HWID: %s\n", buf + gbb->hwid_offset);
|
||||||
|
print_hwid_digest(gbb, " digest: ", "\n");
|
||||||
|
|
||||||
|
pubkey = (VbPublicKey *)(buf + gbb->rootkey_offset);
|
||||||
|
if (PublicKeyLooksOkay(pubkey, gbb->rootkey_size)) {
|
||||||
|
if (state) {
|
||||||
|
state->rootkey.offset =
|
||||||
|
state->area[BIOS_FMAP_GBB].offset +
|
||||||
|
gbb->rootkey_offset;
|
||||||
|
state->rootkey.buf = buf + gbb->rootkey_offset;
|
||||||
|
state->rootkey.len = gbb->rootkey_size;
|
||||||
|
state->rootkey.is_valid = 1;
|
||||||
|
}
|
||||||
|
printf(" Root Key:\n");
|
||||||
|
show_pubkey(pubkey, " ");
|
||||||
|
} else {
|
||||||
|
retval = 1;
|
||||||
|
printf(" Root Key: <invalid>\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
pubkey = (VbPublicKey *)(buf + gbb->recovery_key_offset);
|
||||||
|
if (PublicKeyLooksOkay(pubkey, gbb->recovery_key_size)) {
|
||||||
|
if (state) {
|
||||||
|
state->recovery_key.offset =
|
||||||
|
state->area[BIOS_FMAP_GBB].offset +
|
||||||
|
gbb->recovery_key_offset;
|
||||||
|
state->recovery_key.buf = buf +
|
||||||
|
gbb->recovery_key_offset;
|
||||||
|
state->recovery_key.len = gbb->recovery_key_size;
|
||||||
|
state->recovery_key.is_valid = 1;
|
||||||
|
}
|
||||||
|
printf(" Recovery Key:\n");
|
||||||
|
show_pubkey(pubkey, " ");
|
||||||
|
} else {
|
||||||
|
retval = 1;
|
||||||
|
printf(" Recovery Key: <invalid>\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
bmp = (BmpBlockHeader *)(buf + gbb->bmpfv_offset);
|
||||||
|
if (0 != memcmp(bmp, BMPBLOCK_SIGNATURE, BMPBLOCK_SIGNATURE_SIZE)) {
|
||||||
|
printf(" BmpBlock: <invalid>\n");
|
||||||
|
/* We don't support older BmpBlock formats, so we can't
|
||||||
|
* be strict about this. */
|
||||||
|
} else {
|
||||||
|
printf(" BmpBlock:\n");
|
||||||
|
printf(" Version: %d.%d\n",
|
||||||
|
bmp->major_version, bmp->minor_version);
|
||||||
|
printf(" Localizations: %d\n",
|
||||||
|
bmp->number_of_localizations);
|
||||||
|
printf(" Screen layouts: %d\n",
|
||||||
|
bmp->number_of_screenlayouts);
|
||||||
|
printf(" Image infos: %d\n",
|
||||||
|
bmp->number_of_imageinfos);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!retval && state)
|
||||||
|
state->area[BIOS_FMAP_GBB].is_valid = 1;
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This handles FW_MAIN_A and FW_MAIN_B while processing a BIOS image.
|
||||||
|
*
|
||||||
|
* The data is just the RW firmware blob, so there's nothing useful to show
|
||||||
|
* about it. We'll just mark it as present so when we encounter corresponding
|
||||||
|
* VBLOCK area, we'll have this to verify.
|
||||||
|
*/
|
||||||
|
static int fmap_show_fw_main(const char *name, uint8_t *buf, uint32_t len,
|
||||||
|
void *data)
|
||||||
|
{
|
||||||
|
struct bios_state_s *state = (struct bios_state_s *)data;
|
||||||
|
|
||||||
|
if (!len) {
|
||||||
|
printf("Firmware body: %s <invalid>\n", name);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Firmware body: %s\n", name);
|
||||||
|
printf(" Offset: 0x%08x\n",
|
||||||
|
state->area[state->c].offset);
|
||||||
|
printf(" Size: 0x%08x\n", len);
|
||||||
|
|
||||||
|
state->area[state->c].is_valid = 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Functions to call to show the bios components */
|
||||||
|
static int (*fmap_show_fn[])(const char *name, uint8_t *buf, uint32_t len,
|
||||||
|
void *data) = {
|
||||||
|
ft_show_gbb,
|
||||||
|
fmap_show_fw_main,
|
||||||
|
fmap_show_fw_main,
|
||||||
|
ft_show_fw_preamble,
|
||||||
|
ft_show_fw_preamble,
|
||||||
|
};
|
||||||
|
BUILD_ASSERT(ARRAY_SIZE(fmap_show_fn) == NUM_BIOS_COMPONENTS);
|
||||||
|
|
||||||
|
int ft_show_bios(const char *name, uint8_t *buf, uint32_t len, void *data)
|
||||||
|
{
|
||||||
|
FmapHeader *fmap;
|
||||||
|
FmapAreaHeader *ah = 0;
|
||||||
|
char ah_name[FMAP_NAMELEN + 1];
|
||||||
|
enum bios_component c;
|
||||||
|
int retval = 0;
|
||||||
|
struct bios_state_s state;
|
||||||
|
|
||||||
|
memset(&state, 0, sizeof(state));
|
||||||
|
|
||||||
|
printf("BIOS: %s\n", name);
|
||||||
|
|
||||||
|
/* We've already checked, so we know this will work. */
|
||||||
|
fmap = fmap_find(buf, len);
|
||||||
|
for (c = 0; c < NUM_BIOS_COMPONENTS; c++) {
|
||||||
|
/* We know one of these will work, too */
|
||||||
|
if (fmap_find_by_name(buf, len, fmap, fmap_name[c], &ah) ||
|
||||||
|
fmap_find_by_name(buf, len, fmap, fmap_oldname[c], &ah)) {
|
||||||
|
/* But the file might be truncated */
|
||||||
|
fmap_limit_area(ah, len);
|
||||||
|
/* The name is not necessarily null-terminated */
|
||||||
|
snprintf(ah_name, sizeof(ah_name), "%s", ah->area_name);
|
||||||
|
|
||||||
|
/* Update the state we're passing around */
|
||||||
|
state.c = c;
|
||||||
|
state.area[c].offset = ah->area_offset;
|
||||||
|
state.area[c].buf = buf + ah->area_offset;
|
||||||
|
state.area[c].len = ah->area_size;
|
||||||
|
|
||||||
|
Debug("%s() showing FMAP area %d (%s),"
|
||||||
|
" offset=0x%08x len=0x%08x\n",
|
||||||
|
__func__, c, ah_name,
|
||||||
|
ah->area_offset, ah->area_size);
|
||||||
|
|
||||||
|
/* Go look at it. */
|
||||||
|
if (fmap_show_fn[c])
|
||||||
|
retval += fmap_show_fn[c](ah_name,
|
||||||
|
state.area[c].buf,
|
||||||
|
state.area[c].len,
|
||||||
|
&state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Sign functions **/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This handles FW_MAIN_A and FW_MAIN_B while signing a BIOS image. The data is
|
||||||
|
* just the RW firmware blob so there's nothing useful to do with it, but we'll
|
||||||
|
* mark it as valid so that we'll know that this FMAP area exists and can
|
||||||
|
* be signed.
|
||||||
|
*/
|
||||||
|
static int fmap_sign_fw_main(const char *name, uint8_t *buf, uint32_t len,
|
||||||
|
void *data)
|
||||||
|
{
|
||||||
|
struct bios_state_s *state = (struct bios_state_s *)data;
|
||||||
|
state->area[state->c].is_valid = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This handles VBLOCK_A and VBLOCK_B while processing a BIOS image. We don't
|
||||||
|
* do any signing here. We just check to see if the existing FMAP area contains
|
||||||
|
* a firmware preamble so we can preserve its contents. We do the signing once
|
||||||
|
* we've looked over all the components.
|
||||||
|
*/
|
||||||
|
static int fmap_sign_fw_preamble(const char *name, uint8_t *buf, uint32_t len,
|
||||||
|
void *data)
|
||||||
|
{
|
||||||
|
VbKeyBlockHeader *key_block = (VbKeyBlockHeader *)buf;
|
||||||
|
struct bios_state_s *state = (struct bios_state_s *)data;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we have a valid keyblock and fw_preamble, then we can use them to
|
||||||
|
* determine the size of the firmware body. Otherwise, we'll have to
|
||||||
|
* just sign the whole region.
|
||||||
|
*/
|
||||||
|
if (VBOOT_SUCCESS != KeyBlockVerify(key_block, len, NULL, 1)) {
|
||||||
|
fprintf(stderr, "Warning: %s keyblock is invalid. "
|
||||||
|
"Signing the entire FW FMAP region...\n", name);
|
||||||
|
goto whatever;
|
||||||
|
}
|
||||||
|
|
||||||
|
RSAPublicKey *rsa = PublicKeyToRSA(&key_block->data_key);
|
||||||
|
if (!rsa) {
|
||||||
|
fprintf(stderr, "Warning: %s public key is invalid. "
|
||||||
|
"Signing the entire FW FMAP region...\n", name);
|
||||||
|
goto whatever;
|
||||||
|
}
|
||||||
|
uint32_t more = key_block->key_block_size;
|
||||||
|
VbFirmwarePreambleHeader *preamble =
|
||||||
|
(VbFirmwarePreambleHeader *)(buf + more);
|
||||||
|
uint32_t fw_size = preamble->body_signature.data_size;
|
||||||
|
struct bios_area_s *fw_body_area = 0;
|
||||||
|
|
||||||
|
switch (state->c) {
|
||||||
|
case BIOS_FMAP_VBLOCK_A:
|
||||||
|
fw_body_area = &state->area[BIOS_FMAP_FW_MAIN_A];
|
||||||
|
/* Preserve the flags if they're not specified */
|
||||||
|
if (!sign_option.flags_specified)
|
||||||
|
sign_option.flags = preamble->flags;
|
||||||
|
break;
|
||||||
|
case BIOS_FMAP_VBLOCK_B:
|
||||||
|
fw_body_area = &state->area[BIOS_FMAP_FW_MAIN_B];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
DIE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fw_size > fw_body_area->len) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"%s says the firmware is larger than we have\n",
|
||||||
|
name);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update the firmware size */
|
||||||
|
fw_body_area->len = fw_size;
|
||||||
|
|
||||||
|
whatever:
|
||||||
|
state->area[state->c].is_valid = 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int write_new_preamble(struct bios_area_s *vblock,
|
||||||
|
struct bios_area_s *fw_body,
|
||||||
|
VbPrivateKey *signkey,
|
||||||
|
VbKeyBlockHeader *keyblock)
|
||||||
|
{
|
||||||
|
VbSignature *body_sig;
|
||||||
|
VbFirmwarePreambleHeader *preamble;
|
||||||
|
|
||||||
|
body_sig = CalculateSignature(fw_body->buf, fw_body->len, signkey);
|
||||||
|
if (!body_sig) {
|
||||||
|
fprintf(stderr, "Error calculating body signature\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
preamble = CreateFirmwarePreamble(sign_option.version,
|
||||||
|
sign_option.kernel_subkey,
|
||||||
|
body_sig,
|
||||||
|
signkey,
|
||||||
|
sign_option.flags);
|
||||||
|
if (!preamble) {
|
||||||
|
fprintf(stderr, "Error creating firmware preamble.\n");
|
||||||
|
free(body_sig);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write the new keyblock */
|
||||||
|
uint32_t more = keyblock->key_block_size;
|
||||||
|
memcpy(vblock->buf, keyblock, more);
|
||||||
|
/* and the new preamble */
|
||||||
|
memcpy(vblock->buf + more, preamble, preamble->preamble_size);
|
||||||
|
|
||||||
|
free(preamble);
|
||||||
|
free(body_sig);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int write_loem(const char *ab, struct bios_area_s *vblock)
|
||||||
|
{
|
||||||
|
char filename[PATH_MAX];
|
||||||
|
int n;
|
||||||
|
n = snprintf(filename, sizeof(filename), "%s/vblock_%s.%s",
|
||||||
|
sign_option.loemdir ? sign_option.loemdir : ".",
|
||||||
|
ab, sign_option.loemid);
|
||||||
|
if (n >= sizeof(filename)) {
|
||||||
|
fprintf(stderr, "LOEM args produce bogus filename\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *fp = fopen(filename, "w");
|
||||||
|
if (!fp) {
|
||||||
|
fprintf(stderr, "Can't open %s for writing: %s\n",
|
||||||
|
filename, strerror(errno));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (1 != fwrite(vblock->buf, vblock->len, 1, fp)) {
|
||||||
|
fprintf(stderr, "Can't write to %s: %s\n",
|
||||||
|
filename, strerror(errno));
|
||||||
|
fclose(fp);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (fclose(fp)) {
|
||||||
|
fprintf(stderr, "Failed closing loem output: %s\n",
|
||||||
|
strerror(errno));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This signs a full BIOS image after it's been traversed. */
|
||||||
|
static int sign_bios_at_end(struct bios_state_s *state)
|
||||||
|
{
|
||||||
|
struct bios_area_s *vblock_a = &state->area[BIOS_FMAP_VBLOCK_A];
|
||||||
|
struct bios_area_s *vblock_b = &state->area[BIOS_FMAP_VBLOCK_B];
|
||||||
|
struct bios_area_s *fw_a = &state->area[BIOS_FMAP_FW_MAIN_A];
|
||||||
|
struct bios_area_s *fw_b = &state->area[BIOS_FMAP_FW_MAIN_B];
|
||||||
|
int retval = 0;
|
||||||
|
|
||||||
|
if (!vblock_a->is_valid || !vblock_b->is_valid ||
|
||||||
|
!fw_a->is_valid || !fw_b->is_valid) {
|
||||||
|
fprintf(stderr, "Something's wrong. Not changing anything\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Do A & B differ ? */
|
||||||
|
if (fw_a->len != fw_b->len ||
|
||||||
|
memcmp(fw_a->buf, fw_b->buf, fw_a->len)) {
|
||||||
|
/* Yes, must use DEV keys for A */
|
||||||
|
if (!sign_option.devsignprivate || !sign_option.devkeyblock) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"FW A & B differ. DEV keys are required.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
retval |= write_new_preamble(vblock_a, fw_a,
|
||||||
|
sign_option.devsignprivate,
|
||||||
|
sign_option.devkeyblock);
|
||||||
|
} else {
|
||||||
|
retval |= write_new_preamble(vblock_a, fw_a,
|
||||||
|
sign_option.signprivate,
|
||||||
|
sign_option.keyblock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FW B is always normal keys */
|
||||||
|
retval |= write_new_preamble(vblock_b, fw_b,
|
||||||
|
sign_option.signprivate,
|
||||||
|
sign_option.keyblock);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (sign_option.loemid) {
|
||||||
|
retval |= write_loem("A", vblock_a);
|
||||||
|
retval |= write_loem("B", vblock_b);
|
||||||
|
}
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Functions to call while preparing to sign the bios */
|
||||||
|
static int (*fmap_sign_fn[])(const char *name, uint8_t *buf, uint32_t len,
|
||||||
|
void *data) = {
|
||||||
|
0,
|
||||||
|
fmap_sign_fw_main,
|
||||||
|
fmap_sign_fw_main,
|
||||||
|
fmap_sign_fw_preamble,
|
||||||
|
fmap_sign_fw_preamble,
|
||||||
|
};
|
||||||
|
BUILD_ASSERT(ARRAY_SIZE(fmap_sign_fn) == NUM_BIOS_COMPONENTS);
|
||||||
|
|
||||||
|
int ft_sign_bios(const char *name, uint8_t *buf, uint32_t len, void *data)
|
||||||
|
{
|
||||||
|
FmapHeader *fmap;
|
||||||
|
FmapAreaHeader *ah = 0;
|
||||||
|
char ah_name[FMAP_NAMELEN + 1];
|
||||||
|
enum bios_component c;
|
||||||
|
int retval = 0;
|
||||||
|
struct bios_state_s state;
|
||||||
|
|
||||||
|
memset(&state, 0, sizeof(state));
|
||||||
|
|
||||||
|
/* We've already checked, so we know this will work. */
|
||||||
|
fmap = fmap_find(buf, len);
|
||||||
|
for (c = 0; c < NUM_BIOS_COMPONENTS; c++) {
|
||||||
|
/* We know one of these will work, too */
|
||||||
|
if (fmap_find_by_name(buf, len, fmap, fmap_name[c], &ah) ||
|
||||||
|
fmap_find_by_name(buf, len, fmap, fmap_oldname[c], &ah)) {
|
||||||
|
/* But the file might be truncated */
|
||||||
|
fmap_limit_area(ah, len);
|
||||||
|
/* The name is not necessarily null-terminated */
|
||||||
|
snprintf(ah_name, sizeof(ah_name), "%s", ah->area_name);
|
||||||
|
|
||||||
|
/* Update the state we're passing around */
|
||||||
|
state.c = c;
|
||||||
|
state.area[c].buf = buf + ah->area_offset;
|
||||||
|
state.area[c].len = ah->area_size;
|
||||||
|
|
||||||
|
Debug("%s() examining FMAP area %d (%s),"
|
||||||
|
" offset=0x%08x len=0x%08x\n",
|
||||||
|
__func__, c, ah_name,
|
||||||
|
ah->area_offset, ah->area_size);
|
||||||
|
|
||||||
|
/* Go look at it, but abort on error */
|
||||||
|
if (fmap_sign_fn[c])
|
||||||
|
retval += fmap_sign_fn[c](ah_name,
|
||||||
|
state.area[c].buf,
|
||||||
|
state.area[c].len,
|
||||||
|
&state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
retval += sign_bios_at_end(&state);
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum futil_file_type ft_recognize_bios_image(uint8_t *buf, uint32_t len)
|
||||||
|
{
|
||||||
|
FmapHeader *fmap;
|
||||||
|
enum bios_component c;
|
||||||
|
|
||||||
|
fmap = fmap_find(buf, len);
|
||||||
|
if (!fmap)
|
||||||
|
return FILE_TYPE_UNKNOWN;
|
||||||
|
|
||||||
|
for (c = 0; c < NUM_BIOS_COMPONENTS; c++)
|
||||||
|
if (!fmap_find_by_name(buf, len, fmap, fmap_name[c], 0))
|
||||||
|
break;
|
||||||
|
if (c == NUM_BIOS_COMPONENTS)
|
||||||
|
return FILE_TYPE_BIOS_IMAGE;
|
||||||
|
|
||||||
|
for (c = 0; c < NUM_BIOS_COMPONENTS; c++)
|
||||||
|
if (!fmap_find_by_name(buf, len, fmap, fmap_oldname[c], 0))
|
||||||
|
break;
|
||||||
|
if (c == NUM_BIOS_COMPONENTS)
|
||||||
|
return FILE_TYPE_OLD_BIOS_IMAGE;
|
||||||
|
|
||||||
|
return FILE_TYPE_UNKNOWN;
|
||||||
|
}
|
||||||
42
futility/file_type_bios.h
Normal file
42
futility/file_type_bios.h
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
|
||||||
|
* Use of this source code is governed by a BSD-style license that can be
|
||||||
|
* found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
#ifndef VBOOT_REFERENCE_FUTILITY_FILE_TYPE_BIOS_H_
|
||||||
|
#define VBOOT_REFERENCE_FUTILITY_FILE_TYPE_BIOS_H_
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The Chrome OS BIOS must contain specific FMAP areas, which we want to look
|
||||||
|
* at in a certain order.
|
||||||
|
*/
|
||||||
|
enum bios_component {
|
||||||
|
BIOS_FMAP_GBB,
|
||||||
|
BIOS_FMAP_FW_MAIN_A,
|
||||||
|
BIOS_FMAP_FW_MAIN_B,
|
||||||
|
BIOS_FMAP_VBLOCK_A,
|
||||||
|
BIOS_FMAP_VBLOCK_B,
|
||||||
|
|
||||||
|
NUM_BIOS_COMPONENTS
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Location information for each component */
|
||||||
|
struct bios_area_s {
|
||||||
|
uint32_t offset; /* to avoid pointer math */
|
||||||
|
uint8_t *buf;
|
||||||
|
uint32_t len;
|
||||||
|
uint32_t is_valid;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* State to track as we visit all components */
|
||||||
|
struct bios_state_s {
|
||||||
|
/* Current component */
|
||||||
|
enum bios_component c;
|
||||||
|
/* Other activites, possibly before or after the current one */
|
||||||
|
struct bios_area_s area[NUM_BIOS_COMPONENTS];
|
||||||
|
struct bios_area_s recovery_key;
|
||||||
|
struct bios_area_s rootkey;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* VBOOT_REFERENCE_FUTILITY_FILE_TYPE_BIOS_H_ */
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
#include "fmap.h"
|
|
||||||
#include "file_type.h"
|
|
||||||
#include "futility.h"
|
|
||||||
#include "traversal.h"
|
|
||||||
|
|
||||||
/* These are the expected areas, in order of traversal */
|
|
||||||
struct bios_fmap_s bios_area[] = {
|
|
||||||
{BIOS_FMAP_GBB, "GBB", "GBB Area"},
|
|
||||||
{BIOS_FMAP_FW_MAIN_A, "FW_MAIN_A", "Firmware A Data"},
|
|
||||||
{BIOS_FMAP_FW_MAIN_B, "FW_MAIN_B", "Firmware B Data"},
|
|
||||||
{BIOS_FMAP_VBLOCK_A, "VBLOCK_A", "Firmware A Key"},
|
|
||||||
{BIOS_FMAP_VBLOCK_B, "VBLOCK_B", "Firmware B Key"},
|
|
||||||
};
|
|
||||||
BUILD_ASSERT(ARRAY_SIZE(bios_area) == NUM_BIOS_COMPONENTS);
|
|
||||||
|
|
||||||
|
|
||||||
void fmap_limit_area(FmapAreaHeader *ah, uint32_t len)
|
|
||||||
{
|
|
||||||
uint32_t sum = ah->area_offset + ah->area_size;
|
|
||||||
if (sum < ah->area_size || sum > len) {
|
|
||||||
Debug("%s(%s) 0x%x + 0x%x > 0x%x\n",
|
|
||||||
__func__, ah->area_name,
|
|
||||||
ah->area_offset, ah->area_size, len);
|
|
||||||
ah->area_offset = 0;
|
|
||||||
ah->area_size = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum futil_file_type ft_recognize_bios_image(uint8_t *buf, uint32_t len)
|
|
||||||
{
|
|
||||||
FmapHeader *fmap;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
fmap = fmap_find(buf, len);
|
|
||||||
if (!fmap)
|
|
||||||
return FILE_TYPE_UNKNOWN;
|
|
||||||
|
|
||||||
for (i = 0; i < NUM_BIOS_COMPONENTS; i++)
|
|
||||||
if (!fmap_find_by_name(buf, len, fmap,
|
|
||||||
bios_area[i].name, 0))
|
|
||||||
break;
|
|
||||||
if (i == NUM_BIOS_COMPONENTS)
|
|
||||||
return FILE_TYPE_BIOS_IMAGE;
|
|
||||||
|
|
||||||
for (i = 0; i < NUM_BIOS_COMPONENTS; i++)
|
|
||||||
if (!fmap_find_by_name(buf, len, fmap,
|
|
||||||
bios_area[i].oldname, 0))
|
|
||||||
break;
|
|
||||||
if (i == NUM_BIOS_COMPONENTS)
|
|
||||||
return FILE_TYPE_OLD_BIOS_IMAGE;
|
|
||||||
|
|
||||||
return FILE_TYPE_UNKNOWN;
|
|
||||||
}
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
|
|
||||||
* Use of this source code is governed by a BSD-style license that can be
|
|
||||||
* found in the LICENSE file.
|
|
||||||
*/
|
|
||||||
#ifndef VBOOT_REFERENCE_FUTILITY_TRAVERSAL_H_
|
|
||||||
#define VBOOT_REFERENCE_FUTILITY_TRAVERSAL_H_
|
|
||||||
#include <stdint.h>
|
|
||||||
#include "fmap.h"
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The Chrome OS BIOS must contain specific FMAP areas, and we generally want
|
|
||||||
* to look at each one in a certain order.
|
|
||||||
*/
|
|
||||||
enum bios_component {
|
|
||||||
BIOS_FMAP_GBB,
|
|
||||||
BIOS_FMAP_FW_MAIN_A,
|
|
||||||
BIOS_FMAP_FW_MAIN_B,
|
|
||||||
BIOS_FMAP_VBLOCK_A,
|
|
||||||
BIOS_FMAP_VBLOCK_B,
|
|
||||||
|
|
||||||
NUM_BIOS_COMPONENTS
|
|
||||||
};
|
|
||||||
|
|
||||||
/* These are the expected areas, in order of traversal */
|
|
||||||
extern struct bios_fmap_s {
|
|
||||||
enum bios_component component;
|
|
||||||
const char * const name;
|
|
||||||
/* The Cr-48 BIOS images have different FMAP names but work the same,
|
|
||||||
* so we allow those too. */
|
|
||||||
const char * const oldname;
|
|
||||||
} bios_area[];
|
|
||||||
|
|
||||||
|
|
||||||
void fmap_limit_area(FmapAreaHeader *ah, uint32_t len);
|
|
||||||
|
|
||||||
|
|
||||||
#endif /* VBOOT_REFERENCE_FUTILITY_TRAVERSAL_H_ */
|
|
||||||
@@ -6,6 +6,11 @@
|
|||||||
#ifndef VBOOT_REFERENCE_FUTILITY_VB1_HELPER_H_
|
#ifndef VBOOT_REFERENCE_FUTILITY_VB1_HELPER_H_
|
||||||
#define VBOOT_REFERENCE_FUTILITY_VB1_HELPER_H_
|
#define VBOOT_REFERENCE_FUTILITY_VB1_HELPER_H_
|
||||||
|
|
||||||
|
/* Display a public key with variable indentation */
|
||||||
|
void show_pubkey(VbPublicKey *pubkey, const char *sp);
|
||||||
|
|
||||||
|
/* Other random functions needed for backward compatibility */
|
||||||
|
|
||||||
uint8_t *ReadConfigFile(const char *config_file, uint64_t *config_size);
|
uint8_t *ReadConfigFile(const char *config_file, uint64_t *config_size);
|
||||||
|
|
||||||
uint8_t *CreateKernelBlob(uint8_t *vmlinuz_buf, uint64_t vmlinuz_size,
|
uint8_t *CreateKernelBlob(uint8_t *vmlinuz_buf, uint64_t vmlinuz_size,
|
||||||
|
|||||||
@@ -21,7 +21,6 @@
|
|||||||
|
|
||||||
#include "file_type.h"
|
#include "file_type.h"
|
||||||
#include "futility.h"
|
#include "futility.h"
|
||||||
#include "traversal.h"
|
|
||||||
|
|
||||||
enum futil_file_type ft_recognize_vb2_key(uint8_t *buf, uint32_t len)
|
enum futil_file_type ft_recognize_vb2_key(uint8_t *buf, uint32_t len)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user