blob: a5ca0592018f462f4bd1899e942ee405c3d45858 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0+
/*
* Access to internal vboot data for debugging / development
*
* Copyright 2020 Google LLC
*/
#define LOG_CATEGORY LOGC_VBOOT
#include <common.h>
#include <cros/vboot.h>
#include <u-boot/crc.h>
#include <vb2_internals_please_do_not_use.h>
/* Used for secdatak */
#define MAJOR_VER(x) (((x) & 0xf0) >> 4)
#define MINOR_VER(x) ((x) & 0x0f)
static const struct nvdata_info {
u8 ofs;
u8 mask;
const char *name;
} nvdata_info[] = {
{ VB2_NV_OFFS_HEADER, VB2_NV_HEADER_WIPEOUT, "wipeout" },
{ VB2_NV_OFFS_HEADER, VB2_NV_HEADER_KERNEL_SETTINGS_RESET,
"kernel settings reset" },
{ VB2_NV_OFFS_HEADER, VB2_NV_HEADER_FW_SETTINGS_RESET,
"firmware settings reset" },
{ VB2_NV_OFFS_BOOT, VB2_NV_BOOT_BACKUP_NVRAM, "backup nvram" },
{ VB2_NV_OFFS_BOOT, VB2_NV_BOOT_DISPLAY_REQUEST, "oprom needed" },
{ VB2_NV_OFFS_BOOT, VB2_NV_BOOT_DISABLE_DEV, "disable dev" },
{ VB2_NV_OFFS_BOOT, VB2_NV_BOOT_DEBUG_RESET, "debug reset" },
{ VB2_NV_OFFS_BOOT2, VB2_NV_BOOT2_TRIED, "tried" },
{ VB2_NV_OFFS_BOOT2, VB2_NV_BOOT2_TRY_NEXT, "try next" },
{ VB2_NV_OFFS_BOOT2, VB2_NV_BOOT2_PREV_TRIED, "prev tried" },
{ VB2_NV_OFFS_BOOT2, VB2_NV_BOOT2_REQ_DIAG, "diag req" },
{ VB2_NV_OFFS_DEV, VB2_NV_DEV_FLAG_EXTERNAL, "dev external" },
{ VB2_NV_OFFS_DEV, VB2_NV_DEV_FLAG_SIGNED_ONLY, "dev signed only" },
{ VB2_NV_OFFS_DEV, VB2_NV_DEV_FLAG_LEGACY, "dev legacy" },
{ VB2_NV_OFFS_DEV, VB2_NV_DEV_FLAG_UDC, "dev udc" },
{ VB2_NV_OFFS_TPM, VB2_NV_TPM_CLEAR_OWNER_REQUEST,
"TPM clear owner request needed" },
{ VB2_NV_OFFS_TPM, VB2_NV_TPM_CLEAR_OWNER_DONE, "TPM clear owner done" },
{ VB2_NV_OFFS_TPM, VB2_NV_TPM_REBOOTED, "TPM rebooted" },
{ VB2_NV_OFFS_MISC, VB2_NV_MISC_BOOT_ON_AC_DETECT,
"boot-on-AC detect" },
{ VB2_NV_OFFS_MISC, VB2_NV_MISC_TRY_RO_SYNC, "try RO sync" },
{ VB2_NV_OFFS_MISC, VB2_NV_MISC_BATTERY_CUTOFF, "battery cutoff" },
{ VB2_NV_OFFS_MISC, VB2_NV_MISC_POST_EC_SYNC_DELAY,
"post EC-sync delay" },
{}
};
int vboot_nvdata_dump(const void *nvdata, int size)
{
const u8 *data = nvdata;
uint sig, val, val2, crc, crc_ofs, ch;
const struct nvdata_info *inf;
bool is_v2, crc_ok;
uint expect_size;
ch = data[VB2_NV_OFFS_HEADER];
sig = ch & VB2_NV_HEADER_SIGNATURE_MASK;
is_v2 = sig == VB2_NV_HEADER_SIGNATURE_V2;
crc_ofs = is_v2 ? VB2_NV_OFFS_CRC_V2 : VB2_NV_OFFS_CRC_V1;
crc = crc8(0, data, crc_ofs);
crc_ok = crc == data[crc_ofs];
printf("Vboot nvdata:\n");
printf(" Signature %s, ", sig == VB2_NV_HEADER_SIGNATURE_V1 ? "v1" :
sig == VB2_NV_HEADER_SIGNATURE_V2 ? "v2" : "invalid");
expect_size = sig == VB2_NV_HEADER_SIGNATURE_V1 ? VB2_NVDATA_SIZE :
sig == VB2_NV_HEADER_SIGNATURE_V2 ? VB2_NVDATA_SIZE_V2 : -1;
printf("size %d (%svalid), ", size, size == expect_size ? "" : "in");
printf("CRC %x (calc %x, %svalid)\n", data[crc_ofs], crc,
crc_ok ? "" : "in");
for (inf = nvdata_info; inf->name; inf++) {
if (data[inf->ofs] & inf->mask)
printf(" - %s\n", inf->name);
}
ch = data[VB2_NV_OFFS_BOOT2];
printf(" Result %d, prev %d\n", ch & VB2_NV_BOOT2_RESULT_MASK,
(ch & VB2_NV_BOOT2_PREV_RESULT_MASK) >>
VB2_NV_BOOT2_PREV_RESULT_SHIFT);
printf(" Recovery %x, subcode %x\n", data[VB2_NV_OFFS_RECOVERY],
data[VB2_NV_OFFS_RECOVERY_SUBCODE]);
ch = data[VB2_NV_OFFS_DEV];
val = data[VB2_NV_OFFS_KERNEL1] | data[VB2_NV_OFFS_KERNEL2] << 8;
val2 = data[VB2_NV_OFFS_KERNEL_MAX_ROLLFORWARD1] |
data[VB2_NV_OFFS_KERNEL_MAX_ROLLFORWARD2] << 8 |
data[VB2_NV_OFFS_KERNEL_MAX_ROLLFORWARD3] << 16 |
data[VB2_NV_OFFS_KERNEL_MAX_ROLLFORWARD4] << 24;
printf(" Localization %d, default boot %d, kernel %x, max roll-forward %x\n",
data[VB2_NV_OFFS_LOCALIZATION],
(ch & VB2_NV_DEV_FLAG_DEFAULT_BOOT) >>
VB2_NV_DEV_DEFAULT_BOOT_SHIFT, val, val2);
return crc_ok ? 0 : -EINVAL;
}
int vboot_secdataf_dump(const void *data, int size)
{
const struct vb2_secdata_firmware *sec = data;
bool crc_ok;
uint crc;
crc = crc8(0, data, offsetof(struct vb2_secdata_firmware, crc8));
crc_ok = crc == sec->crc8;
printf("Vboot secdataf:\n");
print_buffer(0, data, 1, size, 0);
printf(" Size %d : %svalid\n", size,
size == VB2_SECDATA_FIRMWARE_SIZE ? "" : "in");
printf(" CRC %x (calc %x): %svalid\n", sec->crc8, crc,
crc_ok ? "" : "in");
printf(" Version %d\n", sec->struct_version);
if (sec->flags & VB2_SECDATA_FIRMWARE_FLAG_LAST_BOOT_DEVELOPER)
printf(" - last boot was dev mode\n");
if (sec->flags & VB2_SECDATA_FIRMWARE_FLAG_DEV_MODE)
printf(" - dev mode\n");
printf(" Firmware versions %x\n", sec->fw_versions);
return crc_ok ? 0 : -EINVAL;
}
static void update_flag(u8 *flagp, uint mask, uint val)
{
if (val)
*flagp |= mask;
else
*flagp &= ~mask;
}
int vboot_secdataf_set(void *data, int size, enum secdata_t field, int val)
{
struct vb2_secdata_firmware *sec = data;
switch (field) {
case SECDATA_LAST_BOOT_DEV:
update_flag(&sec->flags,
VB2_SECDATA_FIRMWARE_FLAG_LAST_BOOT_DEVELOPER, val);
break;
case SECDATA_DEV_MODE:
update_flag(&sec->flags, VB2_SECDATA_FIRMWARE_FLAG_DEV_MODE,
val);
break;
default:
return -ENOENT;
}
/* Update the CRC */
sec->crc8 = crc8(0, data, offsetof(struct vb2_secdata_firmware, crc8));
return 0;
}
int vboot_secdataf_get(const void *data, int size, enum secdata_t field)
{
const struct vb2_secdata_firmware *sec = data;
switch (field) {
case SECDATA_LAST_BOOT_DEV:
return sec->flags & VB2_SECDATA_FIRMWARE_FLAG_LAST_BOOT_DEVELOPER ?
true : false;
case SECDATA_DEV_MODE:
return sec->flags & VB2_SECDATA_FIRMWARE_FLAG_DEV_MODE ? true :
false;
default:
return -ENOENT;
}
}
static const struct secdatak_info {
u8 mask;
const char *name;
} secdatak_info[] = {
{ VB2_SECDATA_KERNEL_FLAG_PHONE_RECOVERY_DISABLED,
"phone-rec-disable" },
{ VB2_SECDATA_KERNEL_FLAG_PHONE_RECOVERY_UI_DISABLED,
"phone-rec-ui-disable" },
{ VB2_SECDATA_KERNEL_FLAG_DIAGNOSTIC_UI_DISABLED,
"diag-ui-disabled" },
{ VB2_SECDATA_KERNEL_FLAG_HWCRYPTO_ALLOWED,
"hw-crypto-allowed" },
{}
};
int vboot_secdatak_dump(const void *data, int size)
{
const struct vb2_secdata_kernel_v0 *v0 = data;
const struct vb2_secdata_kernel_v1 *v1 = data;
bool is_v0, is_v1, valid;
int cofs, csize, cexp;
uint major, minor;
bool crc_ok;
uint crc;
printf("Vboot secdatak:\n");
major = MAJOR_VER(v0->struct_version);
minor = MINOR_VER(v0->struct_version);
is_v1 = major == MAJOR_VER(VB2_SECDATA_KERNEL_VERSION_V10);
is_v0 = !major && minor == MINOR_VER(VB2_SECDATA_KERNEL_VERSION_V02);
valid = is_v0 || is_v1;
print_buffer(0, data, 1, size, 0);
printf(" Version %02x (major %x, minor %x) - %svalid\n",
v0->struct_version, major, minor, valid ? "" : "in");
if (is_v0) {
cofs = 0;
csize = offsetof(struct vb2_secdata_kernel_v0, crc8);
cexp = v0->crc8;
} else {
cofs = offsetof(struct vb2_secdata_kernel_v1, flags);
csize = v1->struct_size - cofs;
cexp = v1->crc8;
}
crc = crc8(0, data + cofs, csize);
crc_ok = crc == cexp;
printf(" CRC %x (calc %x): %svalid\n", cexp, crc,
crc_ok ? "" : "in");
if (is_v0) {
printf(" UID %08x, versions %x\n", v0->uid,
v0->kernel_versions);
} else {
const struct secdatak_info *inf;
int i;
printf(" size %x, versions %x\n", v1->struct_size,
v1->kernel_versions);
for (inf = secdatak_info; inf->name; inf++) {
if (v1->flags & inf->mask)
printf(" - %s\n", inf->name);
}
printf(" EC hash ");
for (i = 0; i < VB2_SHA256_DIGEST_SIZE; i++)
printf("%02x", v1->ec_hash[i]);
printf("\n");
}
return valid && crc_ok ? 0 : -EINVAL;
}
static const struct fwmp_info {
u8 mask;
const char *name;
} fwmp_info[] = {
{ VB2_SECDATA_FWMP_DEV_DISABLE_BOOT, "dev-boot-disable" },
{ VB2_SECDATA_FWMP_DEV_DISABLE_RECOVERY, "dev-rec-disable" },
{ VB2_SECDATA_FWMP_DEV_ENABLE_EXTERNAL, "dev-external-enable" },
{ VB2_SECDATA_FWMP_DEV_ENABLE_ALTFW, "dev-altfw-enable" },
{ VB2_SECDATA_FWMP_DEV_ENABLE_OFFICIAL_ONLY, "official-only" },
{ VB2_SECDATA_FWMP_DEV_USE_KEY_HASH, "use-key-hash" },
{ VB2_SECDATA_FWMP_DEV_DISABLE_CCD_UNLOCK, "ccd-unlock-disable" },
{ VB2_SECDATA_FWMP_DEV_FIPS_MODE, "fips-mode" },
{}
};
int vboot_fwmp_dump(const void *data, int size)
{
const struct vb2_secdata_fwmp *fwmp = data;
const struct fwmp_info *inf;
uint major, minor;
bool valid, size_ok;
bool crc_ok;
uint crc;
int cofs;
int i;
printf("Vboot fwmp:\n");
major = MAJOR_VER(fwmp->struct_version);
minor = MINOR_VER(fwmp->struct_version);
valid = major == MAJOR_VER(VB2_SECDATA_FWMP_VERSION);
print_buffer(0, data, 1, size, 0);
printf(" Version %02x (major %x, minor %x) - %svalid\n",
fwmp->struct_version, major, minor, valid ? "" : "in");
printf(" Size %x: ", fwmp->struct_size);
if (fwmp->struct_size < VB2_SECDATA_FWMP_MIN_SIZE)
printf("too small");
else if (fwmp->struct_size > size)
printf("missing %x bytes", fwmp->struct_size - size);
else if (fwmp->struct_size > VB2_SECDATA_FWMP_MAX_SIZE)
printf("too large");
else
size_ok = true;
printf("%s\n", size_ok ? "OK" : "");
cofs = offsetof(struct vb2_secdata_fwmp, struct_version);
crc = crc8(0, data + cofs, fwmp->struct_size - cofs);
crc_ok = crc == fwmp->crc8;
printf(" CRC %x (calc %x): %svalid\n", fwmp->crc8, crc,
crc_ok ? "" : "in");
for (inf = fwmp_info; inf->name; inf++) {
if (fwmp->flags & inf->mask)
printf(" - %s\n", inf->name);
}
printf(" Dev kernel key ");
for (i = 0; i < VB2_SECDATA_FWMP_HASH_SIZE; i++)
printf("%02x", fwmp->dev_key_hash[i]);
printf("\n");
return valid && crc_ok ? 0 : -EINVAL;
}