blob: 4bb481429f5435c1dc9a7b14cc5a71e91fdfa3ab [file] [log] [blame]
/*
* Copyright (c) 2011 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.
*
* Alternatively, this software may be distributed under the terms of the
* GNU General Public License ("GPL") version 2 as published by the Free
* Software Foundation.
*/
#include <common.h>
#include <gbb_header.h> /* for GoogleBinaryBlockHeader */
#include <chromeos/common.h>
#include <chromeos/crossystem_data.h>
#include <linux/string.h>
#ifdef CONFIG_OF_LIBFDT
#include <fdt_decode.h>
#include <fdt_support.h>
#include <libfdt.h>
#endif
#ifdef CONFIG_X86
#include <asm/ic/coreboot/sysinfo.h>
#endif
#define CROSSYSTEM_DATA_SIGNATURE "CHROMEOS"
/* This is used to keep bootstub and readwite main firmware in sync */
#define CROSSYSTEM_DATA_VERSION 1
#define PREFIX "crossystem_data: "
enum VdatFwIndex {
VDAT_RW_A = 0,
VDAT_RW_B = 1,
VDAT_RECOVERY = 0xFF
};
enum BinfFwIndex {
BINF_RECOVERY = 0,
BINF_RW_A = 1,
BINF_RW_B = 2
};
DECLARE_GLOBAL_DATA_PTR;
int crossystem_data_init(crossystem_data_t *cdata,
cros_gpio_t *write_protect_switch,
cros_gpio_t *recovery_switch,
cros_gpio_t *developer_switch,
uint32_t fmap_offset,
uint8_t active_ec_firmware,
uint8_t *hardware_id,
uint8_t *readonly_firmware_id)
{
VBDEBUG(PREFIX "crossystem data at %p\n", cdata);
memset(cdata, '\0', sizeof(*cdata));
cdata->total_size = sizeof(*cdata);
cdata->version = CROSSYSTEM_DATA_VERSION;
memcpy(cdata->signature, CROSSYSTEM_DATA_SIGNATURE,
sizeof(CROSSYSTEM_DATA_SIGNATURE));
cdata->boot_write_protect_switch = write_protect_switch->value;
cdata->boot_recovery_switch = recovery_switch->value;
cdata->boot_developer_switch = developer_switch->value;
cdata->polarity_write_protect_switch = write_protect_switch->polarity;
cdata->polarity_recovery_switch = recovery_switch->polarity;
cdata->polarity_developer_switch = developer_switch->polarity;
cdata->gpio_port_write_protect_switch = write_protect_switch->port;
cdata->gpio_port_recovery_switch = recovery_switch->port;
cdata->gpio_port_developer_switch = developer_switch->port;
cdata->fmap_offset = fmap_offset;
cdata->active_ec_firmware = active_ec_firmware;
memcpy(cdata->hardware_id, hardware_id, sizeof(cdata->hardware_id));
memcpy(cdata->readonly_firmware_id, readonly_firmware_id,
sizeof(cdata->readonly_firmware_id));
#ifdef CONFIG_TEGRA2
cdata->board.arm.nonvolatile_context_lba = CHROMEOS_VBNVCONTEXT_LBA;
cdata->board.arm.nonvolatile_context_offset = 0;
cdata->board.arm.nonvolatile_context_size = VBNV_BLOCK_SIZE;
#endif
return 0;
}
int crossystem_data_check_integrity(crossystem_data_t *cdata)
{
if (cdata->total_size != sizeof(*cdata)) {
VBDEBUG(PREFIX "blob size mismatch: %08x != %08x\n",
cdata->total_size, sizeof(*cdata));
return 1;
}
if (memcmp(cdata->signature, CROSSYSTEM_DATA_SIGNATURE,
sizeof(CROSSYSTEM_DATA_SIGNATURE))) {
VBDEBUG(PREFIX "invalid signature: \"%s\"\n", cdata->signature);
return 1;
}
if (cdata->version != CROSSYSTEM_DATA_VERSION) {
VBDEBUG(PREFIX "version mismatch: %08x != %08x\n",
cdata->version, CROSSYSTEM_DATA_VERSION);
return 1;
}
/* Okay, the crossystem data blob passes the sanity check */
return 0;
}
int crossystem_data_set_main_firmware(crossystem_data_t *cdata,
uint8_t firmware_type,
uint8_t *firmware_id)
{
cdata->firmware_type = firmware_type;
memcpy(cdata->firmware_id, firmware_id, sizeof(cdata->firmware_id));
return 0;
}
#ifdef CONFIG_OF_LIBFDT
int crossystem_data_embed_into_fdt(crossystem_data_t *cdata, void *fdt,
uint32_t *size_ptr)
{
char path[] = "/firmware/chromeos";
int nodeoffset, err;
int gpio_phandle;
int gpio_prop[3];
err = fdt_open_into(fdt, fdt,
fdt_totalsize(fdt) + sizeof(*cdata) + 4096);
if (err < 0) {
VBDEBUG(PREFIX "fail to resize fdt: %s\n", fdt_strerror(err));
return 1;
}
*size_ptr = fdt_totalsize(fdt);
/* TODO: Upstream device tree is moving from tegra250 to
* tegra20. Keep the check for 250 around for now but it can be
* removed once the changes have trickled down.
*/
nodeoffset = fdt_node_offset_by_compatible(fdt, 0,
"nvidia,tegra20-gpio");
if (nodeoffset <= 0)
nodeoffset = fdt_node_offset_by_compatible(fdt, 0,
"nvidia,tegra250-gpio");
gpio_phandle = fdt_get_phandle(fdt, nodeoffset);
if (gpio_phandle <= 0) {
gpio_phandle = fdt_alloc_phandle(fdt);
fdt_setprop_cell(fdt, nodeoffset,
"linux,phandle", gpio_phandle);
}
gpio_prop[0] = cpu_to_fdt32(gpio_phandle);
nodeoffset = fdt_add_subnodes_from_path(fdt, 0, path);
if (nodeoffset < 0) {
VBDEBUG(PREFIX "fail to create subnode %s: %s\n", path,
fdt_strerror(nodeoffset));
return 1;
}
fdt_setprop_string(fdt, nodeoffset, "compatible", "chromeos-firmware");
#define set_scalar_prop(name, f) \
fdt_setprop_cell(fdt, nodeoffset, name, cdata->f)
#define set_array_prop(name, f) \
fdt_setprop(fdt, nodeoffset, name, cdata->f, sizeof(cdata->f))
#define set_conststring_prop(name, str) \
fdt_setprop_string(fdt, nodeoffset, name, str)
#define set_bool_prop(name, f) \
((cdata->f) ? fdt_setprop(fdt, nodeoffset, name, NULL, 0) : 0)
err = 0;
err |= set_scalar_prop("total-size", total_size);
err |= set_array_prop("signature", signature);
err |= set_scalar_prop("version", version);
err |= set_bool_prop("boot-write-protect-switch",
boot_write_protect_switch);
err |= set_bool_prop("boot-recovery-switch",
boot_recovery_switch);
err |= set_bool_prop("boot-developer-switch",
boot_developer_switch);
gpio_prop[1] = cpu_to_fdt32(cdata->gpio_port_write_protect_switch);
gpio_prop[2] = cpu_to_fdt32(cdata->polarity_write_protect_switch);
err |= fdt_setprop(fdt, nodeoffset, "write-protect-switch",
gpio_prop, sizeof(gpio_prop));
gpio_prop[1] = cpu_to_fdt32(cdata->gpio_port_recovery_switch);
gpio_prop[2] = cpu_to_fdt32(cdata->polarity_recovery_switch);
err |= fdt_setprop(fdt, nodeoffset, "recovery-switch",
gpio_prop, sizeof(gpio_prop));
gpio_prop[1] = cpu_to_fdt32(cdata->gpio_port_developer_switch);
gpio_prop[2] = cpu_to_fdt32(cdata->polarity_developer_switch);
err |= fdt_setprop(fdt, nodeoffset, "developer-switch",
gpio_prop, sizeof(gpio_prop));
err |= set_scalar_prop("fmap-offset", fmap_offset);
switch (cdata->active_ec_firmware) {
case ACTIVE_EC_FIRMWARE_RO:
err |= set_conststring_prop("active-ec-firmware", "RO");
break;
case ACTIVE_EC_FIRMWARE_RW:
err |= set_conststring_prop("active-ec-firmware", "RW");
break;
}
switch (cdata->firmware_type) {
case FIRMWARE_TYPE_RECOVERY:
err |= set_conststring_prop("firmware-type", "recovery");
break;
case FIRMWARE_TYPE_NORMAL:
err |= set_conststring_prop("firmware-type", "normal");
break;
case FIRMWARE_TYPE_DEVELOPER:
err |= set_conststring_prop("firmware-type", "developer");
break;
}
err |= set_array_prop("hardware-id", hardware_id);
err |= set_array_prop("firmware-version", firmware_id);
err |= set_array_prop("readonly-firmware-version",
readonly_firmware_id);
#ifdef CONFIG_TEGRA2
err |= set_scalar_prop("nonvolatile-context-lba",
board.arm.nonvolatile_context_lba);
err |= set_scalar_prop("nonvolatile-context-offset",
board.arm.nonvolatile_context_offset);
err |= set_scalar_prop("nonvolatile-context-size",
board.arm.nonvolatile_context_size);
#endif
err |= set_array_prop("vboot-shared-data", vb_shared_data);
#undef set_scalar_prop
#undef set_array_prop
#undef set_conststring_prop
#undef set_bool_prop
if (err)
VBDEBUG(PREFIX "fail to store all properties into fdt\n");
return err;
}
#endif /* ^^^^ CONFIG_OF_LIBFDT NOT defined ^^^^ */
#ifdef CONFIG_X86
static int crossystem_fw_index_vdat_to_binf(int index)
{
switch (index) {
case VDAT_RW_A: return BINF_RW_A;
case VDAT_RW_B: return BINF_RW_B;
case VDAT_RECOVERY: return BINF_RECOVERY;
default: return BINF_RECOVERY;
}
};
int crossystem_data_update_acpi(crossystem_data_t *cdata)
{
const void *fdt = gd->blob;
int node_offset, len;
const uint32_t *cell;
chromeos_acpi_t *acpi_table;
VbSharedDataHeader *vdat = (VbSharedDataHeader *)lib_sysinfo.vdat_addr;
node_offset = fdt_path_offset(fdt, "/chromeos-config");
if (node_offset < 0) {
VBDEBUG("crossystem_data_update_acpi: Couldn't access "
"chromeos-config.\n");
return 1;
}
cell = fdt_getprop(fdt, node_offset, "gnvs-vboot-table", NULL);
if (!cell) {
VBDEBUG("crossystem_data_update_acpi: Couldn't access "
"gnvs-vboot-table.\n");
return 1;
}
acpi_table = (chromeos_acpi_t *)(uintptr_t)ntohl(*cell);
acpi_table->vbt0 = BOOT_REASON_OTHER;
acpi_table->vbt1 =
crossystem_fw_index_vdat_to_binf(vdat->firmware_index);
/* active_ec_firmware(vbt2) is set up by coreboot, so we don't
* set it up here on purpose. */
acpi_table->vbt3 =
(cdata->boot_write_protect_switch ? CHSW_FIRMWARE_WP_DIS : 0) |
(cdata->boot_recovery_switch ? CHSW_RECOVERY_X86 : 0) |
(cdata->boot_developer_switch ? CHSW_DEVELOPER_SWITCH : 0);
len = min(ID_LEN, sizeof(acpi_table->vbt4));
memcpy(acpi_table->vbt4, cdata->hardware_id, len);
len = min(ID_LEN, sizeof(acpi_table->vbt5));
memcpy(acpi_table->vbt5, cdata->firmware_id, len);
len = min(ID_LEN, sizeof(acpi_table->vbt6));
memcpy(acpi_table->vbt6, cdata->readonly_firmware_id, len);
acpi_table->vbt7 = cdata->firmware_type;
acpi_table->vbt8 = RECOVERY_REASON_NONE;
acpi_table->vbt9 = cdata->fmap_offset;
return 0;
}
#endif
void crossystem_data_dump(crossystem_data_t *cdata)
{
#define _p(format, field) \
VBDEBUG("crossystem_data_dump: %-30s: " format "\n", #field, cdata->field)
_p("%08x", total_size);
_p("\"%s\"", signature);
_p("%d", version);
_p("%d", boot_write_protect_switch);
_p("%d", boot_recovery_switch);
_p("%d", boot_developer_switch);
_p("%d", polarity_write_protect_switch);
_p("%d", polarity_recovery_switch);
_p("%d", polarity_developer_switch);
_p("%d", gpio_port_write_protect_switch);
_p("%d", gpio_port_recovery_switch);
_p("%d", gpio_port_developer_switch);
_p("%08x", fmap_offset);
_p("%d", active_ec_firmware);
_p("%d", firmware_type);
_p("\"%s\"", hardware_id);
_p("\"%s\"", readonly_firmware_id);
_p("\"%s\"", firmware_id);
#ifdef CONFIG_TEGRA2
_p("%08llx", board.arm.nonvolatile_context_lba);
_p("%08x", board.arm.nonvolatile_context_offset);
_p("%08x", board.arm.nonvolatile_context_size);
#endif
#undef _p
}