GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
A38X BOARD
|
||||
M: Dirk Eibach <dirk.eibach@gdsys.cc>
|
||||
M: Mario Six <mario.six@gdsys.cc>
|
||||
S: Maintained
|
||||
F: board/gdsys/a38x/
|
||||
F: include/configs/controlcenterdc.h
|
||||
F: configs/controlcenterdc_defconfig
|
||||
@@ -0,0 +1,11 @@
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
# Copyright (C) 2015 Stefan Roese <sr@denx.de>
|
||||
# Copyright (C) 2015 Reinhard Pfau <reinhard.pfau@gdsys.cc>
|
||||
# Copyright (C) 2016 Mario Six <mario.six@gdsys.cc>
|
||||
|
||||
obj-$(CONFIG_TARGET_CONTROLCENTERDC) += controlcenterdc.o hre.o spl.o keyprogram.o dt_helpers.o
|
||||
|
||||
ifeq ($(CONFIG_SPL_BUILD),)
|
||||
obj-$(CONFIG_TARGET_CONTROLCENTERDC) += hydra.o ihs_phys.o
|
||||
endif
|
||||
@@ -0,0 +1,297 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2015 Stefan Roese <sr@denx.de>
|
||||
* Copyright (C) 2016 Mario Six <mario.six@gdsys.cc>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <init.h>
|
||||
#include <miiphy.h>
|
||||
#include <tpm-v1.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/cpu.h>
|
||||
#include <asm-generic/gpio.h>
|
||||
|
||||
#include "../drivers/ddr/marvell/a38x/ddr3_init.h"
|
||||
#include "../arch/arm/mach-mvebu/serdes/a38x/high_speed_env_spec.h"
|
||||
|
||||
#include "keyprogram.h"
|
||||
#include "dt_helpers.h"
|
||||
#include "hydra.h"
|
||||
#include "ihs_phys.h"
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
#define DB_GP_88F68XX_GPP_OUT_ENA_LOW 0x7fffffff
|
||||
#define DB_GP_88F68XX_GPP_OUT_ENA_MID 0xffffefff
|
||||
|
||||
#define DB_GP_88F68XX_GPP_OUT_VAL_LOW 0x0
|
||||
#define DB_GP_88F68XX_GPP_OUT_VAL_MID 0x00001000
|
||||
#define DB_GP_88F68XX_GPP_POL_LOW 0x0
|
||||
#define DB_GP_88F68XX_GPP_POL_MID 0x0
|
||||
|
||||
static int get_tpm(struct udevice **devp)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = uclass_first_device_err(UCLASS_TPM, devp);
|
||||
if (rc) {
|
||||
printf("Could not find TPM (ret=%d)\n", rc);
|
||||
return CMD_RET_FAILURE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Define the DDR layout / topology here in the board file. This will
|
||||
* be used by the DDR3 init code in the SPL U-Boot version to configure
|
||||
* the DDR3 controller.
|
||||
*/
|
||||
static struct mv_ddr_topology_map ddr_topology_map = {
|
||||
DEBUG_LEVEL_ERROR,
|
||||
0x1, /* active interfaces */
|
||||
/* cs_mask, mirror, dqs_swap, ck_swap X PUPs */
|
||||
{ { { {0x1, 0, 0, 0},
|
||||
{0x1, 0, 0, 0},
|
||||
{0x1, 0, 0, 0},
|
||||
{0x1, 0, 0, 0},
|
||||
{0x1, 0, 0, 0} },
|
||||
SPEED_BIN_DDR_1600K, /* speed_bin */
|
||||
MV_DDR_DEV_WIDTH_16BIT, /* memory_width */
|
||||
MV_DDR_DIE_CAP_4GBIT, /* mem_size */
|
||||
MV_DDR_FREQ_533, /* frequency */
|
||||
0, 0, /* cas_wl cas_l */
|
||||
MV_DDR_TEMP_LOW, /* temperature */
|
||||
MV_DDR_TIM_DEFAULT} }, /* timing */
|
||||
BUS_MASK_32BIT, /* Busses mask */
|
||||
MV_DDR_CFG_DEFAULT, /* ddr configuration data source */
|
||||
{ {0} }, /* raw spd data */
|
||||
{0} /* timing parameters */
|
||||
|
||||
};
|
||||
|
||||
static struct serdes_map serdes_topology_map[] = {
|
||||
{SGMII0, SERDES_SPEED_1_25_GBPS, SERDES_DEFAULT_MODE, 0, 0},
|
||||
{USB3_HOST0, SERDES_SPEED_5_GBPS, SERDES_DEFAULT_MODE, 0, 0},
|
||||
/* SATA tx polarity is inverted */
|
||||
{SATA1, SERDES_SPEED_3_GBPS, SERDES_DEFAULT_MODE, 0, 1},
|
||||
{SGMII2, SERDES_SPEED_1_25_GBPS, SERDES_DEFAULT_MODE, 0, 0},
|
||||
{DEFAULT_SERDES, SERDES_SPEED_3_GBPS, SERDES_DEFAULT_MODE, 0, 0},
|
||||
{PEX2, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X1, 0, 0}
|
||||
};
|
||||
|
||||
int hws_board_topology_load(struct serdes_map **serdes_map_array, u8 *count)
|
||||
{
|
||||
*serdes_map_array = serdes_topology_map;
|
||||
*count = ARRAY_SIZE(serdes_topology_map);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void board_pex_config(void)
|
||||
{
|
||||
#ifdef CONFIG_SPL_BUILD
|
||||
uint k;
|
||||
struct gpio_desc gpio = {};
|
||||
|
||||
if (!request_gpio_by_name(&gpio, "pca9698@22", 31, "fpga-program-gpio")) {
|
||||
/* prepare FPGA reconfiguration */
|
||||
dm_gpio_set_dir_flags(&gpio, GPIOD_IS_OUT);
|
||||
dm_gpio_set_value(&gpio, 0);
|
||||
|
||||
/* give lunatic PCIe clock some time to stabilize */
|
||||
mdelay(500);
|
||||
|
||||
/* start FPGA reconfiguration */
|
||||
dm_gpio_set_dir_flags(&gpio, GPIOD_IS_IN);
|
||||
}
|
||||
|
||||
/* wait for FPGA done */
|
||||
if (!request_gpio_by_name(&gpio, "pca9698@22", 19, "fpga-done-gpio")) {
|
||||
for (k = 0; k < 20; ++k) {
|
||||
if (dm_gpio_get_value(&gpio)) {
|
||||
printf("FPGA done after %u rounds\n", k);
|
||||
break;
|
||||
}
|
||||
mdelay(100);
|
||||
}
|
||||
}
|
||||
|
||||
/* disable FPGA reset */
|
||||
if (!request_gpio_by_name(&gpio, "gpio@18100", 6, "cpu-to-fpga-reset")) {
|
||||
dm_gpio_set_dir_flags(&gpio, GPIOD_IS_OUT);
|
||||
dm_gpio_set_value(&gpio, 1);
|
||||
}
|
||||
|
||||
/* wait for FPGA ready */
|
||||
if (!request_gpio_by_name(&gpio, "pca9698@22", 27, "fpga-ready-gpio")) {
|
||||
for (k = 0; k < 2; ++k) {
|
||||
if (!dm_gpio_get_value(&gpio))
|
||||
break;
|
||||
mdelay(100);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
struct mv_ddr_topology_map *mv_ddr_topology_map_get(void)
|
||||
{
|
||||
return &ddr_topology_map;
|
||||
}
|
||||
|
||||
int board_early_init_f(void)
|
||||
{
|
||||
#ifdef CONFIG_SPL_BUILD
|
||||
/* Configure MPP */
|
||||
writel(0x00111111, MVEBU_MPP_BASE + 0x00);
|
||||
writel(0x40040000, MVEBU_MPP_BASE + 0x04);
|
||||
writel(0x00466444, MVEBU_MPP_BASE + 0x08);
|
||||
writel(0x00043300, MVEBU_MPP_BASE + 0x0c);
|
||||
writel(0x44400000, MVEBU_MPP_BASE + 0x10);
|
||||
writel(0x20000334, MVEBU_MPP_BASE + 0x14);
|
||||
writel(0x40000000, MVEBU_MPP_BASE + 0x18);
|
||||
writel(0x00004444, MVEBU_MPP_BASE + 0x1c);
|
||||
|
||||
/* Set GPP Out value */
|
||||
writel(DB_GP_88F68XX_GPP_OUT_VAL_LOW, MVEBU_GPIO0_BASE + 0x00);
|
||||
writel(DB_GP_88F68XX_GPP_OUT_VAL_MID, MVEBU_GPIO1_BASE + 0x00);
|
||||
|
||||
/* Set GPP Polarity */
|
||||
writel(DB_GP_88F68XX_GPP_POL_LOW, MVEBU_GPIO0_BASE + 0x0c);
|
||||
writel(DB_GP_88F68XX_GPP_POL_MID, MVEBU_GPIO1_BASE + 0x0c);
|
||||
|
||||
/* Set GPP Out Enable */
|
||||
writel(DB_GP_88F68XX_GPP_OUT_ENA_LOW, MVEBU_GPIO0_BASE + 0x04);
|
||||
writel(DB_GP_88F68XX_GPP_OUT_ENA_MID, MVEBU_GPIO1_BASE + 0x04);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int board_init(void)
|
||||
{
|
||||
/* Address of boot parameters */
|
||||
gd->bd->bi_boot_params = mvebu_sdram_bar(0) + 0x100;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_SPL_BUILD
|
||||
void init_host_phys(struct mii_dev *bus)
|
||||
{
|
||||
uint k;
|
||||
|
||||
for (k = 0; k < 2; ++k) {
|
||||
struct phy_device *phydev;
|
||||
|
||||
phydev = phy_find_by_mask(bus, 1 << k,
|
||||
PHY_INTERFACE_MODE_SGMII);
|
||||
|
||||
if (phydev)
|
||||
phy_config(phydev);
|
||||
}
|
||||
}
|
||||
|
||||
int ccdc_eth_init(void)
|
||||
{
|
||||
uint k;
|
||||
uint octo_phy_mask = 0;
|
||||
int ret;
|
||||
struct mii_dev *bus;
|
||||
|
||||
/* Init SoC's phys */
|
||||
bus = miiphy_get_dev_by_name("ethernet@34000");
|
||||
|
||||
if (bus)
|
||||
init_host_phys(bus);
|
||||
|
||||
bus = miiphy_get_dev_by_name("ethernet@70000");
|
||||
|
||||
if (bus)
|
||||
init_host_phys(bus);
|
||||
|
||||
/* Init octo phys */
|
||||
octo_phy_mask = calculate_octo_phy_mask();
|
||||
|
||||
printf("IHS PHYS: %08x", octo_phy_mask);
|
||||
|
||||
ret = init_octo_phys(octo_phy_mask);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
printf("\n");
|
||||
|
||||
if (!get_fpga()) {
|
||||
puts("fpga was NULL\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* reset all FPGA-QSGMII instances */
|
||||
for (k = 0; k < 80; ++k)
|
||||
writel(1 << 31, get_fpga()->qsgmii_port_state[k]);
|
||||
|
||||
udelay(100);
|
||||
|
||||
for (k = 0; k < 80; ++k)
|
||||
writel(0, get_fpga()->qsgmii_port_state[k]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int board_late_init(void)
|
||||
{
|
||||
#ifndef CONFIG_SPL_BUILD
|
||||
hydra_initialize();
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
int board_fix_fdt(void *rw_fdt_blob)
|
||||
{
|
||||
struct udevice *bus = NULL;
|
||||
uint k;
|
||||
char name[64];
|
||||
int err;
|
||||
|
||||
err = uclass_get_device_by_name(UCLASS_I2C, "i2c@11000", &bus);
|
||||
|
||||
if (err) {
|
||||
printf("Could not get I2C bus.\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
for (k = 0x21; k <= 0x26; k++) {
|
||||
snprintf(name, 64,
|
||||
"/soc/internal-regs/i2c@11000/pca9698@%02x", k);
|
||||
|
||||
if (!dm_i2c_simple_probe(bus, k))
|
||||
fdt_disable_by_ofname(rw_fdt_blob, name);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int last_stage_init(void)
|
||||
{
|
||||
struct udevice *tpm;
|
||||
int ret;
|
||||
|
||||
#ifndef CONFIG_SPL_BUILD
|
||||
ccdc_eth_init();
|
||||
#endif
|
||||
ret = get_tpm(&tpm);
|
||||
if (ret || tpm_init(tpm) || tpm_startup(tpm, TPM_ST_CLEAR) ||
|
||||
tpm_continue_self_test(tpm)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
mdelay(37);
|
||||
|
||||
flush_keys(tpm);
|
||||
load_and_run_keyprog(tpm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2016
|
||||
* Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <i2c.h>
|
||||
#include <fdt_support.h>
|
||||
#include <asm-generic/gpio.h>
|
||||
#include <dm.h>
|
||||
|
||||
int fdt_disable_by_ofname(void *rw_fdt_blob, char *ofname)
|
||||
{
|
||||
int offset = fdt_path_offset(rw_fdt_blob, ofname);
|
||||
|
||||
return fdt_status_disabled(rw_fdt_blob, offset);
|
||||
}
|
||||
|
||||
bool dm_i2c_simple_probe(struct udevice *bus, uint chip_addr)
|
||||
{
|
||||
struct udevice *dev;
|
||||
|
||||
return !dm_i2c_probe(bus, chip_addr, DM_I2C_CHIP_RD_ADDRESS |
|
||||
DM_I2C_CHIP_WR_ADDRESS, &dev);
|
||||
}
|
||||
|
||||
int request_gpio_by_name(struct gpio_desc *gpio, const char *gpio_dev_name,
|
||||
uint offset, char *gpio_name)
|
||||
{
|
||||
struct udevice *gpio_dev = NULL;
|
||||
|
||||
if (uclass_get_device_by_name(UCLASS_GPIO, gpio_dev_name, &gpio_dev))
|
||||
return 1;
|
||||
|
||||
gpio->dev = gpio_dev;
|
||||
gpio->offset = offset;
|
||||
gpio->flags = 0;
|
||||
|
||||
return dm_gpio_request(gpio, gpio_name);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* (C) Copyright 2016
|
||||
* Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
|
||||
*/
|
||||
|
||||
#ifndef __DT_HELPERS_H
|
||||
#define __DT_HELPERS_H
|
||||
|
||||
int fdt_disable_by_ofname(void *rw_fdt_blob, char *ofname);
|
||||
bool dm_i2c_simple_probe(struct udevice *bus, uint chip_addr);
|
||||
int request_gpio_by_name(struct gpio_desc *gpio, const char *gpio_dev_name,
|
||||
uint offset, char *gpio_name);
|
||||
|
||||
#endif /* __DT_HELPERS_H */
|
||||
@@ -0,0 +1,528 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2013
|
||||
* Reinhard Pfau, Guntermann & Drunck GmbH, reinhard.pfau@gdsys.cc
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <malloc.h>
|
||||
#include <fs.h>
|
||||
#include <i2c.h>
|
||||
#include <mmc.h>
|
||||
#include <tpm-v1.h>
|
||||
#include <u-boot/crc.h>
|
||||
#include <u-boot/sha1.h>
|
||||
#include <asm/byteorder.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include <pca9698.h>
|
||||
|
||||
#include "hre.h"
|
||||
|
||||
/* other constants */
|
||||
enum {
|
||||
ESDHC_BOOT_IMAGE_SIG_OFS = 0x40,
|
||||
ESDHC_BOOT_IMAGE_SIZE_OFS = 0x48,
|
||||
ESDHC_BOOT_IMAGE_ADDR_OFS = 0x50,
|
||||
ESDHC_BOOT_IMAGE_TARGET_OFS = 0x58,
|
||||
ESDHC_BOOT_IMAGE_ENTRY_OFS = 0x60,
|
||||
};
|
||||
|
||||
enum {
|
||||
I2C_SOC_0 = 0,
|
||||
I2C_SOC_1 = 1,
|
||||
};
|
||||
|
||||
enum access_mode {
|
||||
HREG_NONE = 0,
|
||||
HREG_RD = 1,
|
||||
HREG_WR = 2,
|
||||
HREG_RDWR = 3,
|
||||
};
|
||||
|
||||
/* register constants */
|
||||
enum {
|
||||
FIX_HREG_DEVICE_ID_HASH = 0,
|
||||
FIX_HREG_UNUSED1 = 1,
|
||||
FIX_HREG_UNUSED2 = 2,
|
||||
FIX_HREG_VENDOR = 3,
|
||||
COUNT_FIX_HREGS
|
||||
};
|
||||
|
||||
static struct h_reg pcr_hregs[24];
|
||||
static struct h_reg fix_hregs[COUNT_FIX_HREGS];
|
||||
static struct h_reg var_hregs[8];
|
||||
|
||||
/* hre opcodes */
|
||||
enum {
|
||||
/* opcodes w/o data */
|
||||
HRE_NOP = 0x00,
|
||||
HRE_SYNC = HRE_NOP,
|
||||
HRE_CHECK0 = 0x01,
|
||||
/* opcodes w/o data, w/ sync dst */
|
||||
/* opcodes w/ data */
|
||||
HRE_LOAD = 0x81,
|
||||
/* opcodes w/data, w/sync dst */
|
||||
HRE_XOR = 0xC1,
|
||||
HRE_AND = 0xC2,
|
||||
HRE_OR = 0xC3,
|
||||
HRE_EXTEND = 0xC4,
|
||||
HRE_LOADKEY = 0xC5,
|
||||
};
|
||||
|
||||
/* hre errors */
|
||||
enum {
|
||||
HRE_E_OK = 0,
|
||||
HRE_E_TPM_FAILURE,
|
||||
HRE_E_INVALID_HREG,
|
||||
};
|
||||
|
||||
static uint64_t device_id;
|
||||
static uint64_t device_cl;
|
||||
static uint64_t device_type;
|
||||
|
||||
static uint32_t platform_key_handle;
|
||||
|
||||
static uint32_t hre_tpm_err;
|
||||
static int hre_err = HRE_E_OK;
|
||||
|
||||
#define IS_PCR_HREG(spec) ((spec) & 0x20)
|
||||
#define IS_FIX_HREG(spec) (((spec) & 0x38) == 0x08)
|
||||
#define IS_VAR_HREG(spec) (((spec) & 0x38) == 0x10)
|
||||
#define HREG_IDX(spec) ((spec) & (IS_PCR_HREG(spec) ? 0x1f : 0x7))
|
||||
|
||||
static const uint8_t vendor[] = "Guntermann & Drunck";
|
||||
|
||||
/**
|
||||
* @brief get the size of a given (TPM) NV area
|
||||
* @param tpm TPM device
|
||||
* @param index NV index of the area to get size for
|
||||
* @param size pointer to the size
|
||||
* @return 0 on success, != 0 on error
|
||||
*/
|
||||
static int get_tpm_nv_size(struct udevice *tpm, uint32_t index, uint32_t *size)
|
||||
{
|
||||
uint32_t err;
|
||||
uint8_t info[72];
|
||||
uint8_t *ptr;
|
||||
uint16_t v16;
|
||||
|
||||
err = tpm_get_capability(tpm, TPM_CAP_NV_INDEX, index,
|
||||
info, sizeof(info));
|
||||
if (err) {
|
||||
printf("tpm_get_capability(CAP_NV_INDEX, %08x) failed: %u\n",
|
||||
index, err);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* skip tag and nvIndex */
|
||||
ptr = info + 6;
|
||||
/* skip 2 pcr info fields */
|
||||
v16 = get_unaligned_be16(ptr);
|
||||
ptr += 2 + v16 + 1 + 20;
|
||||
v16 = get_unaligned_be16(ptr);
|
||||
ptr += 2 + v16 + 1 + 20;
|
||||
/* skip permission and flags */
|
||||
ptr += 6 + 3;
|
||||
|
||||
*size = get_unaligned_be32(ptr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief search for a key by usage auth and pub key hash.
|
||||
* @param tpm TPM device
|
||||
* @param auth usage auth of the key to search for
|
||||
* @param pubkey_digest (SHA1) hash of the pub key structure of the key
|
||||
* @param[out] handle the handle of the key iff found
|
||||
* @return 0 if key was found in TPM; != 0 if not.
|
||||
*/
|
||||
static int find_key(struct udevice *tpm, const uint8_t auth[20],
|
||||
const uint8_t pubkey_digest[20], uint32_t *handle)
|
||||
{
|
||||
uint16_t key_count;
|
||||
uint32_t key_handles[10];
|
||||
uint8_t buf[288];
|
||||
uint8_t *ptr;
|
||||
uint32_t err;
|
||||
uint8_t digest[20];
|
||||
size_t buf_len;
|
||||
unsigned int i;
|
||||
|
||||
/* fetch list of already loaded keys in the TPM */
|
||||
err = tpm_get_capability(tpm, TPM_CAP_HANDLE, TPM_RT_KEY, buf,
|
||||
sizeof(buf));
|
||||
if (err)
|
||||
return -1;
|
||||
key_count = get_unaligned_be16(buf);
|
||||
ptr = buf + 2;
|
||||
for (i = 0; i < key_count; ++i, ptr += 4)
|
||||
key_handles[i] = get_unaligned_be32(ptr);
|
||||
|
||||
/* now search a(/ the) key which we can access with the given auth */
|
||||
for (i = 0; i < key_count; ++i) {
|
||||
buf_len = sizeof(buf);
|
||||
err = tpm_get_pub_key_oiap(tpm, key_handles[i], auth, buf,
|
||||
&buf_len);
|
||||
if (err && err != TPM_AUTHFAIL)
|
||||
return -1;
|
||||
if (err)
|
||||
continue;
|
||||
sha1_csum(buf, buf_len, digest);
|
||||
if (!memcmp(digest, pubkey_digest, 20)) {
|
||||
*handle = key_handles[i];
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief read CCDM common data from TPM NV
|
||||
* @param tpm TPM device
|
||||
* @return 0 if CCDM common data was found and read, !=0 if something failed.
|
||||
*/
|
||||
static int read_common_data(struct udevice *tpm)
|
||||
{
|
||||
uint32_t size = 0;
|
||||
uint32_t err;
|
||||
uint8_t buf[256];
|
||||
sha1_context ctx;
|
||||
|
||||
if (get_tpm_nv_size(tpm, NV_COMMON_DATA_INDEX, &size) ||
|
||||
size < NV_COMMON_DATA_MIN_SIZE)
|
||||
return 1;
|
||||
err = tpm_nv_read_value(tpm, NV_COMMON_DATA_INDEX,
|
||||
buf, min(sizeof(buf), size));
|
||||
if (err) {
|
||||
printf("tpm_nv_read_value() failed: %u\n", err);
|
||||
return 1;
|
||||
}
|
||||
|
||||
device_id = get_unaligned_be64(buf);
|
||||
device_cl = get_unaligned_be64(buf + 8);
|
||||
device_type = get_unaligned_be64(buf + 16);
|
||||
|
||||
sha1_starts(&ctx);
|
||||
sha1_update(&ctx, buf, 24);
|
||||
sha1_finish(&ctx, fix_hregs[FIX_HREG_DEVICE_ID_HASH].digest);
|
||||
fix_hregs[FIX_HREG_DEVICE_ID_HASH].valid = true;
|
||||
|
||||
platform_key_handle = get_unaligned_be32(buf + 24);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief get pointer to hash register by specification
|
||||
* @param spec specification of a hash register
|
||||
* @return pointer to hash register or NULL if @a spec does not qualify a
|
||||
* valid hash register; NULL else.
|
||||
*/
|
||||
static struct h_reg *get_hreg(uint8_t spec)
|
||||
{
|
||||
uint8_t idx;
|
||||
|
||||
idx = HREG_IDX(spec);
|
||||
if (IS_FIX_HREG(spec)) {
|
||||
if (idx < ARRAY_SIZE(fix_hregs))
|
||||
return fix_hregs + idx;
|
||||
hre_err = HRE_E_INVALID_HREG;
|
||||
} else if (IS_PCR_HREG(spec)) {
|
||||
if (idx < ARRAY_SIZE(pcr_hregs))
|
||||
return pcr_hregs + idx;
|
||||
hre_err = HRE_E_INVALID_HREG;
|
||||
} else if (IS_VAR_HREG(spec)) {
|
||||
if (idx < ARRAY_SIZE(var_hregs))
|
||||
return var_hregs + idx;
|
||||
hre_err = HRE_E_INVALID_HREG;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief get pointer of a hash register by specification and usage.
|
||||
* @param tpm TPM device
|
||||
* @param spec specification of a hash register
|
||||
* @param mode access mode (read or write or read/write)
|
||||
* @return pointer to hash register if found and valid; NULL else.
|
||||
*
|
||||
* This func uses @a get_reg() to determine the hash register for a given spec.
|
||||
* If a register is found it is validated according to the desired access mode.
|
||||
* The value of automatic registers (PCR register and fixed registers) is
|
||||
* loaded or computed on read access.
|
||||
*/
|
||||
static struct h_reg *access_hreg(struct udevice *tpm, uint8_t spec,
|
||||
enum access_mode mode)
|
||||
{
|
||||
struct h_reg *result;
|
||||
|
||||
result = get_hreg(spec);
|
||||
if (!result)
|
||||
return NULL;
|
||||
|
||||
if (mode & HREG_WR) {
|
||||
if (IS_FIX_HREG(spec)) {
|
||||
hre_err = HRE_E_INVALID_HREG;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
if (mode & HREG_RD) {
|
||||
if (!result->valid) {
|
||||
if (IS_PCR_HREG(spec)) {
|
||||
hre_tpm_err = tpm_pcr_read(tpm, HREG_IDX(spec),
|
||||
result->digest, 20);
|
||||
result->valid = (hre_tpm_err == TPM_SUCCESS);
|
||||
} else if (IS_FIX_HREG(spec)) {
|
||||
switch (HREG_IDX(spec)) {
|
||||
case FIX_HREG_DEVICE_ID_HASH:
|
||||
read_common_data(tpm);
|
||||
break;
|
||||
case FIX_HREG_VENDOR:
|
||||
memcpy(result->digest, vendor, 20);
|
||||
result->valid = true;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
result->valid = true;
|
||||
}
|
||||
}
|
||||
if (!result->valid) {
|
||||
hre_err = HRE_E_INVALID_HREG;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void *compute_and(void *_dst, const void *_src, size_t n)
|
||||
{
|
||||
uint8_t *dst = _dst;
|
||||
const uint8_t *src = _src;
|
||||
size_t i;
|
||||
|
||||
for (i = n; i-- > 0; )
|
||||
*dst++ &= *src++;
|
||||
|
||||
return _dst;
|
||||
}
|
||||
|
||||
static void *compute_or(void *_dst, const void *_src, size_t n)
|
||||
{
|
||||
uint8_t *dst = _dst;
|
||||
const uint8_t *src = _src;
|
||||
size_t i;
|
||||
|
||||
for (i = n; i-- > 0; )
|
||||
*dst++ |= *src++;
|
||||
|
||||
return _dst;
|
||||
}
|
||||
|
||||
static void *compute_xor(void *_dst, const void *_src, size_t n)
|
||||
{
|
||||
uint8_t *dst = _dst;
|
||||
const uint8_t *src = _src;
|
||||
size_t i;
|
||||
|
||||
for (i = n; i-- > 0; )
|
||||
*dst++ ^= *src++;
|
||||
|
||||
return _dst;
|
||||
}
|
||||
|
||||
static void *compute_extend(void *_dst, const void *_src, size_t n)
|
||||
{
|
||||
uint8_t digest[20];
|
||||
sha1_context ctx;
|
||||
|
||||
sha1_starts(&ctx);
|
||||
sha1_update(&ctx, _dst, n);
|
||||
sha1_update(&ctx, _src, n);
|
||||
sha1_finish(&ctx, digest);
|
||||
memcpy(_dst, digest, min(n, sizeof(digest)));
|
||||
|
||||
return _dst;
|
||||
}
|
||||
|
||||
static int hre_op_loadkey(struct udevice *tpm, struct h_reg *src_reg,
|
||||
struct h_reg *dst_reg, const void *key,
|
||||
size_t key_size)
|
||||
{
|
||||
uint32_t parent_handle;
|
||||
uint32_t key_handle;
|
||||
|
||||
if (!src_reg || !dst_reg || !src_reg->valid || !dst_reg->valid)
|
||||
return -1;
|
||||
if (find_key(tpm, src_reg->digest, dst_reg->digest, &parent_handle))
|
||||
return -1;
|
||||
hre_tpm_err = tpm_load_key2_oiap(tpm, parent_handle, key, key_size,
|
||||
src_reg->digest, &key_handle);
|
||||
if (hre_tpm_err) {
|
||||
hre_err = HRE_E_TPM_FAILURE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief executes the next opcode on the hash register engine.
|
||||
* @param tpm TPM device
|
||||
* @param[in,out] ip pointer to the opcode (instruction pointer)
|
||||
* @param[in,out] code_size (remaining) size of the code
|
||||
* @return new instruction pointer on success, NULL on error.
|
||||
*/
|
||||
static const uint8_t *hre_execute_op(struct udevice *tpm, const uint8_t **ip,
|
||||
size_t *code_size)
|
||||
{
|
||||
bool dst_modified = false;
|
||||
uint32_t ins;
|
||||
uint8_t opcode;
|
||||
uint8_t src_spec;
|
||||
uint8_t dst_spec;
|
||||
uint16_t data_size;
|
||||
struct h_reg *src_reg, *dst_reg;
|
||||
uint8_t buf[20];
|
||||
const uint8_t *src_buf, *data;
|
||||
uint8_t *ptr;
|
||||
int i;
|
||||
void * (*bin_func)(void *, const void *, size_t);
|
||||
|
||||
if (*code_size < 4)
|
||||
return NULL;
|
||||
|
||||
ins = get_unaligned_be32(*ip);
|
||||
opcode = **ip;
|
||||
data = *ip + 4;
|
||||
src_spec = (ins >> 18) & 0x3f;
|
||||
dst_spec = (ins >> 12) & 0x3f;
|
||||
data_size = (ins & 0x7ff);
|
||||
|
||||
debug("HRE: ins=%08x (op=%02x, s=%02x, d=%02x, L=%d)\n", ins,
|
||||
opcode, src_spec, dst_spec, data_size);
|
||||
|
||||
if ((opcode & 0x80) && (data_size + 4) > *code_size)
|
||||
return NULL;
|
||||
|
||||
src_reg = access_hreg(tpm, src_spec, HREG_RD);
|
||||
if (hre_err || hre_tpm_err)
|
||||
return NULL;
|
||||
dst_reg = access_hreg(tpm, dst_spec,
|
||||
(opcode & 0x40) ? HREG_RDWR : HREG_WR);
|
||||
if (hre_err || hre_tpm_err)
|
||||
return NULL;
|
||||
|
||||
switch (opcode) {
|
||||
case HRE_NOP:
|
||||
goto end;
|
||||
case HRE_CHECK0:
|
||||
if (src_reg) {
|
||||
for (i = 0; i < 20; ++i) {
|
||||
if (src_reg->digest[i])
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case HRE_LOAD:
|
||||
bin_func = memcpy;
|
||||
goto do_bin_func;
|
||||
case HRE_XOR:
|
||||
bin_func = compute_xor;
|
||||
goto do_bin_func;
|
||||
case HRE_AND:
|
||||
bin_func = compute_and;
|
||||
goto do_bin_func;
|
||||
case HRE_OR:
|
||||
bin_func = compute_or;
|
||||
goto do_bin_func;
|
||||
case HRE_EXTEND:
|
||||
bin_func = compute_extend;
|
||||
do_bin_func:
|
||||
if (!dst_reg)
|
||||
return NULL;
|
||||
if (src_reg) {
|
||||
src_buf = src_reg->digest;
|
||||
} else {
|
||||
if (!data_size) {
|
||||
memset(buf, 0, 20);
|
||||
src_buf = buf;
|
||||
} else if (data_size == 1) {
|
||||
memset(buf, *data, 20);
|
||||
src_buf = buf;
|
||||
} else if (data_size >= 20) {
|
||||
src_buf = data;
|
||||
} else {
|
||||
src_buf = buf;
|
||||
for (ptr = (uint8_t *)src_buf, i = 20; i > 0;
|
||||
i -= data_size, ptr += data_size)
|
||||
memcpy(ptr, data,
|
||||
min_t(size_t, i, data_size));
|
||||
}
|
||||
}
|
||||
bin_func(dst_reg->digest, src_buf, 20);
|
||||
dst_reg->valid = true;
|
||||
dst_modified = true;
|
||||
break;
|
||||
case HRE_LOADKEY:
|
||||
if (hre_op_loadkey(tpm, src_reg, dst_reg, data, data_size))
|
||||
return NULL;
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (dst_reg && dst_modified && IS_PCR_HREG(dst_spec)) {
|
||||
hre_tpm_err = tpm_extend(tpm, HREG_IDX(dst_spec),
|
||||
dst_reg->digest, dst_reg->digest);
|
||||
if (hre_tpm_err) {
|
||||
hre_err = HRE_E_TPM_FAILURE;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
end:
|
||||
*ip += 4;
|
||||
*code_size -= 4;
|
||||
if (opcode & 0x80) {
|
||||
*ip += data_size;
|
||||
*code_size -= data_size;
|
||||
}
|
||||
|
||||
return *ip;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief runs a program on the hash register engine.
|
||||
* @param tpm TPM device
|
||||
* @param code pointer to the (HRE) code.
|
||||
* @param code_size size of the code (in bytes).
|
||||
* @return 0 on success, != 0 on failure.
|
||||
*/
|
||||
int hre_run_program(struct udevice *tpm, const uint8_t *code, size_t code_size)
|
||||
{
|
||||
size_t code_left;
|
||||
const uint8_t *ip = code;
|
||||
|
||||
code_left = code_size;
|
||||
hre_tpm_err = 0;
|
||||
hre_err = HRE_E_OK;
|
||||
while (code_left > 0)
|
||||
if (!hre_execute_op(tpm, &ip, &code_left))
|
||||
return -1;
|
||||
|
||||
return hre_err;
|
||||
}
|
||||
|
||||
int hre_verify_program(struct key_program *prg)
|
||||
{
|
||||
uint32_t crc;
|
||||
|
||||
crc = crc32(0, prg->code, prg->code_size);
|
||||
|
||||
if (crc != prg->code_crc) {
|
||||
printf("HRC crc mismatch: %08x != %08x\n",
|
||||
crc, prg->code_crc);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* (C) Copyright 2013
|
||||
* Reinhard Pfau, Guntermann & Drunck GmbH, reinhard.pfau@gdsys.cc
|
||||
*/
|
||||
|
||||
#ifndef __HRE_H
|
||||
#define __HRE_H
|
||||
|
||||
struct key_program {
|
||||
uint32_t magic;
|
||||
uint32_t code_crc;
|
||||
uint32_t code_size;
|
||||
uint8_t code[];
|
||||
};
|
||||
|
||||
struct h_reg {
|
||||
bool valid;
|
||||
uint8_t digest[20];
|
||||
};
|
||||
|
||||
/* CCDM specific contants */
|
||||
enum {
|
||||
/* NV indices */
|
||||
NV_COMMON_DATA_INDEX = 0x40000001,
|
||||
/* magics for key blob chains */
|
||||
MAGIC_KEY_PROGRAM = 0x68726500,
|
||||
MAGIC_HMAC = 0x68616300,
|
||||
MAGIC_END_OF_CHAIN = 0x00000000,
|
||||
/* sizes */
|
||||
NV_COMMON_DATA_MIN_SIZE = 3 * sizeof(uint64_t) + 2 * sizeof(uint16_t),
|
||||
};
|
||||
|
||||
int hre_verify_program(struct key_program *prg);
|
||||
int hre_run_program(struct udevice *tpm, const uint8_t *code, size_t code_size);
|
||||
|
||||
#endif /* __HRE_H */
|
||||
@@ -0,0 +1,138 @@
|
||||
#include <common.h>
|
||||
#include <console.h> /* ctrlc */
|
||||
#include <asm/io.h>
|
||||
|
||||
#include "hydra.h"
|
||||
|
||||
enum {
|
||||
HWVER_100 = 0,
|
||||
HWVER_110 = 1,
|
||||
HWVER_120 = 2,
|
||||
};
|
||||
|
||||
static struct pci_device_id hydra_supported[] = {
|
||||
{ 0x6d5e, 0xcdc1 },
|
||||
{}
|
||||
};
|
||||
|
||||
static struct ihs_fpga *fpga;
|
||||
|
||||
struct ihs_fpga *get_fpga(void)
|
||||
{
|
||||
return fpga;
|
||||
}
|
||||
|
||||
void print_hydra_version(uint index)
|
||||
{
|
||||
u32 versions = readl(&fpga->versions);
|
||||
u32 fpga_version = readl(&fpga->fpga_version);
|
||||
|
||||
uint hardware_version = versions & 0xf;
|
||||
|
||||
printf("FPGA%u: mapped to %p\n ", index, fpga);
|
||||
|
||||
switch (hardware_version) {
|
||||
case HWVER_100:
|
||||
printf("HW-Ver 1.00\n");
|
||||
break;
|
||||
|
||||
case HWVER_110:
|
||||
printf("HW-Ver 1.10\n");
|
||||
break;
|
||||
|
||||
case HWVER_120:
|
||||
printf("HW-Ver 1.20\n");
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("HW-Ver %d(not supported)\n",
|
||||
hardware_version);
|
||||
break;
|
||||
}
|
||||
|
||||
printf(" FPGA V %d.%02d\n",
|
||||
fpga_version / 100, fpga_version % 100);
|
||||
}
|
||||
|
||||
void hydra_initialize(void)
|
||||
{
|
||||
uint i;
|
||||
pci_dev_t devno;
|
||||
|
||||
/* Find and probe all the matching PCI devices */
|
||||
for (i = 0; (devno = pci_find_devices(hydra_supported, i)) >= 0; i++) {
|
||||
u32 val;
|
||||
|
||||
/* Try to enable I/O accesses and bus-mastering */
|
||||
val = PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER;
|
||||
pci_write_config_dword(devno, PCI_COMMAND, val);
|
||||
|
||||
/* Make sure it worked */
|
||||
pci_read_config_dword(devno, PCI_COMMAND, &val);
|
||||
if (!(val & PCI_COMMAND_MEMORY)) {
|
||||
puts("Can't enable I/O memory\n");
|
||||
continue;
|
||||
}
|
||||
if (!(val & PCI_COMMAND_MASTER)) {
|
||||
puts("Can't enable bus-mastering\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
/* read FPGA details */
|
||||
fpga = pci_map_bar(devno, PCI_BASE_ADDRESS_0,
|
||||
PCI_REGION_MEM);
|
||||
|
||||
print_hydra_version(i);
|
||||
}
|
||||
}
|
||||
|
||||
#define REFL_PATTERN (0xdededede)
|
||||
#define REFL_PATTERN_INV (~REFL_PATTERN)
|
||||
|
||||
int do_hydrate(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
|
||||
{
|
||||
uint k = 0;
|
||||
void __iomem *pcie2_base = (void __iomem *)(MVEBU_REG_PCIE_BASE +
|
||||
0x4000);
|
||||
|
||||
if (!fpga)
|
||||
return -1;
|
||||
|
||||
while (1) {
|
||||
u32 res;
|
||||
|
||||
writel(REFL_PATTERN, &fpga->reflection_low);
|
||||
res = readl(&fpga->reflection_low);
|
||||
if (res != REFL_PATTERN_INV)
|
||||
printf("round %u: read %08x, expected %08x\n",
|
||||
k, res, REFL_PATTERN_INV);
|
||||
writel(REFL_PATTERN_INV, &fpga->reflection_low);
|
||||
res = readl(&fpga->reflection_low);
|
||||
if (res != REFL_PATTERN)
|
||||
printf("round %u: read %08x, expected %08x\n",
|
||||
k, res, REFL_PATTERN);
|
||||
|
||||
res = readl(pcie2_base + 0x118) & 0x1f;
|
||||
if (res)
|
||||
printf("FrstErrPtr %u\n", res);
|
||||
res = readl(pcie2_base + 0x104);
|
||||
if (res) {
|
||||
printf("Uncorrectable Error Status 0x%08x\n", res);
|
||||
writel(res, pcie2_base + 0x104);
|
||||
}
|
||||
|
||||
if (!(++k % 10000))
|
||||
printf("round %u\n", k);
|
||||
|
||||
if (ctrlc())
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
U_BOOT_CMD(
|
||||
hydrate, 1, 0, do_hydrate,
|
||||
"hydra reflection test",
|
||||
"hydra reflection test"
|
||||
);
|
||||
@@ -0,0 +1,14 @@
|
||||
struct ihs_fpga {
|
||||
u32 reflection_low; /* 0x0000 */
|
||||
u32 versions; /* 0x0004 */
|
||||
u32 fpga_version; /* 0x0008 */
|
||||
u32 fpga_features; /* 0x000c */
|
||||
u32 reserved0[4]; /* 0x0010 */
|
||||
u32 control; /* 0x0020 */
|
||||
u32 reserved1[375]; /* 0x0024 */
|
||||
u32 qsgmii_port_state[80]; /* 0x0600 */
|
||||
};
|
||||
|
||||
void print_hydra_version(uint index);
|
||||
void hydra_initialize(void);
|
||||
struct ihs_fpga *get_fpga(void);
|
||||
@@ -0,0 +1,355 @@
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <miiphy.h>
|
||||
#include <asm-generic/gpio.h>
|
||||
|
||||
#include "ihs_phys.h"
|
||||
#include "dt_helpers.h"
|
||||
|
||||
enum {
|
||||
PORTTYPE_MAIN_CAT,
|
||||
PORTTYPE_TOP_CAT,
|
||||
PORTTYPE_16C_16F,
|
||||
PORTTYPE_UNKNOWN
|
||||
};
|
||||
|
||||
static struct porttype {
|
||||
bool phy_invert_in_pol;
|
||||
bool phy_invert_out_pol;
|
||||
} porttypes[] = {
|
||||
{ true, false },
|
||||
{ false, true },
|
||||
{ false, false },
|
||||
};
|
||||
|
||||
static void ihs_phy_config(struct phy_device *phydev, bool qinpn, bool qoutpn)
|
||||
{
|
||||
u16 reg;
|
||||
|
||||
phy_config(phydev);
|
||||
|
||||
/* enable QSGMII autonegotiation with flow control */
|
||||
phy_write(phydev, MDIO_DEVAD_NONE, 22, 0x0004);
|
||||
reg = phy_read(phydev, MDIO_DEVAD_NONE, 16);
|
||||
reg |= (3 << 6);
|
||||
phy_write(phydev, MDIO_DEVAD_NONE, 16, reg);
|
||||
|
||||
/*
|
||||
* invert QSGMII Q_INP/N and Q_OUTP/N if required
|
||||
* and perform global reset
|
||||
*/
|
||||
reg = phy_read(phydev, MDIO_DEVAD_NONE, 26);
|
||||
if (qinpn)
|
||||
reg |= (1 << 13);
|
||||
if (qoutpn)
|
||||
reg |= (1 << 12);
|
||||
reg |= (1 << 15);
|
||||
phy_write(phydev, MDIO_DEVAD_NONE, 26, reg);
|
||||
|
||||
/* advertise 1000BASE-T full-duplex only */
|
||||
phy_write(phydev, MDIO_DEVAD_NONE, 22, 0x0000);
|
||||
reg = phy_read(phydev, MDIO_DEVAD_NONE, 4);
|
||||
reg &= ~0x1e0;
|
||||
phy_write(phydev, MDIO_DEVAD_NONE, 4, reg);
|
||||
reg = phy_read(phydev, MDIO_DEVAD_NONE, 9);
|
||||
reg = (reg & ~0x300) | 0x200;
|
||||
phy_write(phydev, MDIO_DEVAD_NONE, 9, reg);
|
||||
|
||||
/* copper power up */
|
||||
reg = phy_read(phydev, MDIO_DEVAD_NONE, 16);
|
||||
reg &= ~0x0004;
|
||||
phy_write(phydev, MDIO_DEVAD_NONE, 16, reg);
|
||||
}
|
||||
|
||||
uint calculate_octo_phy_mask(void)
|
||||
{
|
||||
uint k;
|
||||
uint octo_phy_mask = 0;
|
||||
struct gpio_desc gpio = {};
|
||||
char gpio_name[64];
|
||||
static const char * const dev_name[] = {"pca9698@23", "pca9698@21",
|
||||
"pca9698@24", "pca9698@25",
|
||||
"pca9698@26"};
|
||||
|
||||
/* mark all octo phys that should be present */
|
||||
for (k = 0; k < 5; ++k) {
|
||||
snprintf(gpio_name, 64, "cat-gpio-%u", k);
|
||||
|
||||
if (request_gpio_by_name(&gpio, dev_name[k], 0x20, gpio_name))
|
||||
continue;
|
||||
|
||||
/* check CAT flag */
|
||||
if (dm_gpio_get_value(&gpio))
|
||||
octo_phy_mask |= (1 << (k * 2));
|
||||
else
|
||||
/* If CAT == 0, there's no second octo phy -> skip */
|
||||
continue;
|
||||
|
||||
snprintf(gpio_name, 64, "second-octo-gpio-%u", k);
|
||||
|
||||
if (request_gpio_by_name(&gpio, dev_name[k], 0x27, gpio_name)) {
|
||||
/* default: second octo phy is present */
|
||||
octo_phy_mask |= (1 << (k * 2 + 1));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (dm_gpio_get_value(&gpio) == 0)
|
||||
octo_phy_mask |= (1 << (k * 2 + 1));
|
||||
}
|
||||
|
||||
return octo_phy_mask;
|
||||
}
|
||||
|
||||
int register_miiphy_bus(uint k, struct mii_dev **bus)
|
||||
{
|
||||
int retval;
|
||||
struct mii_dev *mdiodev = mdio_alloc();
|
||||
char *name = bb_miiphy_buses[k].name;
|
||||
|
||||
if (!mdiodev)
|
||||
return -ENOMEM;
|
||||
strncpy(mdiodev->name,
|
||||
name,
|
||||
MDIO_NAME_LEN);
|
||||
mdiodev->read = bb_miiphy_read;
|
||||
mdiodev->write = bb_miiphy_write;
|
||||
|
||||
retval = mdio_register(mdiodev);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
*bus = miiphy_get_dev_by_name(name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct porttype *get_porttype(uint octo_phy_mask, uint k)
|
||||
{
|
||||
uint octo_index = k * 4;
|
||||
|
||||
if (!k) {
|
||||
if (octo_phy_mask & 0x01)
|
||||
return &porttypes[PORTTYPE_MAIN_CAT];
|
||||
else if (!(octo_phy_mask & 0x03))
|
||||
return &porttypes[PORTTYPE_16C_16F];
|
||||
} else {
|
||||
if (octo_phy_mask & (1 << octo_index))
|
||||
return &porttypes[PORTTYPE_TOP_CAT];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int init_single_phy(struct porttype *porttype, struct mii_dev *bus,
|
||||
uint bus_idx, uint m, uint phy_idx)
|
||||
{
|
||||
struct phy_device *phydev = phy_find_by_mask(
|
||||
bus, 1 << (m * 8 + phy_idx),
|
||||
PHY_INTERFACE_MODE_MII);
|
||||
|
||||
printf(" %u", bus_idx * 32 + m * 8 + phy_idx);
|
||||
|
||||
if (!phydev)
|
||||
puts("!");
|
||||
else
|
||||
ihs_phy_config(phydev, porttype->phy_invert_in_pol,
|
||||
porttype->phy_invert_out_pol);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int init_octo_phys(uint octo_phy_mask)
|
||||
{
|
||||
uint bus_idx;
|
||||
|
||||
/* there are up to four octo-phys on each mdio bus */
|
||||
for (bus_idx = 0; bus_idx < bb_miiphy_buses_num; ++bus_idx) {
|
||||
uint m;
|
||||
uint octo_index = bus_idx * 4;
|
||||
struct mii_dev *bus = NULL;
|
||||
struct porttype *porttype = NULL;
|
||||
int ret;
|
||||
|
||||
porttype = get_porttype(octo_phy_mask, bus_idx);
|
||||
|
||||
if (!porttype)
|
||||
continue;
|
||||
|
||||
for (m = 0; m < 4; ++m) {
|
||||
uint phy_idx;
|
||||
|
||||
/**
|
||||
* Register a bus device if there is at least one phy
|
||||
* on the current bus
|
||||
*/
|
||||
if (!m && octo_phy_mask & (0xf << octo_index)) {
|
||||
ret = register_miiphy_bus(bus_idx, &bus);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!(octo_phy_mask & BIT(octo_index + m)))
|
||||
continue;
|
||||
|
||||
for (phy_idx = 0; phy_idx < 8; ++phy_idx)
|
||||
init_single_phy(porttype, bus, bus_idx, m,
|
||||
phy_idx);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* MII GPIO bitbang implementation
|
||||
* MDC MDIO bus
|
||||
* 13 14 PHY1-4
|
||||
* 25 45 PHY5-8
|
||||
* 46 24 PHY9-10
|
||||
*/
|
||||
|
||||
struct gpio_mii {
|
||||
int index;
|
||||
struct gpio_desc mdc_gpio;
|
||||
struct gpio_desc mdio_gpio;
|
||||
int mdc_num;
|
||||
int mdio_num;
|
||||
int mdio_value;
|
||||
} gpio_mii_set[] = {
|
||||
{ 0, {}, {}, 13, 14, 1 },
|
||||
{ 1, {}, {}, 25, 45, 1 },
|
||||
{ 2, {}, {}, 46, 24, 1 },
|
||||
};
|
||||
|
||||
static int mii_mdio_init(struct bb_miiphy_bus *bus)
|
||||
{
|
||||
struct gpio_mii *gpio_mii = bus->priv;
|
||||
char name[32] = {};
|
||||
struct udevice *gpio_dev1 = NULL;
|
||||
struct udevice *gpio_dev2 = NULL;
|
||||
|
||||
if (uclass_get_device_by_name(UCLASS_GPIO, "gpio@18100", &gpio_dev1) ||
|
||||
uclass_get_device_by_name(UCLASS_GPIO, "gpio@18140", &gpio_dev2)) {
|
||||
printf("Could not get GPIO device.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (gpio_mii->mdc_num > 31) {
|
||||
gpio_mii->mdc_gpio.dev = gpio_dev2;
|
||||
gpio_mii->mdc_gpio.offset = gpio_mii->mdc_num - 32;
|
||||
} else {
|
||||
gpio_mii->mdc_gpio.dev = gpio_dev1;
|
||||
gpio_mii->mdc_gpio.offset = gpio_mii->mdc_num;
|
||||
}
|
||||
gpio_mii->mdc_gpio.flags = 0;
|
||||
snprintf(name, 32, "bb_miiphy_bus-%d-mdc", gpio_mii->index);
|
||||
dm_gpio_request(&gpio_mii->mdc_gpio, name);
|
||||
|
||||
if (gpio_mii->mdio_num > 31) {
|
||||
gpio_mii->mdio_gpio.dev = gpio_dev2;
|
||||
gpio_mii->mdio_gpio.offset = gpio_mii->mdio_num - 32;
|
||||
} else {
|
||||
gpio_mii->mdio_gpio.dev = gpio_dev1;
|
||||
gpio_mii->mdio_gpio.offset = gpio_mii->mdio_num;
|
||||
}
|
||||
gpio_mii->mdio_gpio.flags = 0;
|
||||
snprintf(name, 32, "bb_miiphy_bus-%d-mdio", gpio_mii->index);
|
||||
dm_gpio_request(&gpio_mii->mdio_gpio, name);
|
||||
|
||||
dm_gpio_set_dir_flags(&gpio_mii->mdc_gpio, GPIOD_IS_OUT);
|
||||
dm_gpio_set_value(&gpio_mii->mdc_gpio, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mii_mdio_active(struct bb_miiphy_bus *bus)
|
||||
{
|
||||
struct gpio_mii *gpio_mii = bus->priv;
|
||||
|
||||
dm_gpio_set_value(&gpio_mii->mdc_gpio, gpio_mii->mdio_value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mii_mdio_tristate(struct bb_miiphy_bus *bus)
|
||||
{
|
||||
struct gpio_mii *gpio_mii = bus->priv;
|
||||
|
||||
dm_gpio_set_dir_flags(&gpio_mii->mdio_gpio, GPIOD_IS_IN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mii_set_mdio(struct bb_miiphy_bus *bus, int v)
|
||||
{
|
||||
struct gpio_mii *gpio_mii = bus->priv;
|
||||
|
||||
dm_gpio_set_dir_flags(&gpio_mii->mdio_gpio, GPIOD_IS_OUT);
|
||||
dm_gpio_set_value(&gpio_mii->mdio_gpio, v);
|
||||
gpio_mii->mdio_value = v;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mii_get_mdio(struct bb_miiphy_bus *bus, int *v)
|
||||
{
|
||||
struct gpio_mii *gpio_mii = bus->priv;
|
||||
|
||||
dm_gpio_set_dir_flags(&gpio_mii->mdio_gpio, GPIOD_IS_IN);
|
||||
*v = (dm_gpio_get_value(&gpio_mii->mdio_gpio));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mii_set_mdc(struct bb_miiphy_bus *bus, int v)
|
||||
{
|
||||
struct gpio_mii *gpio_mii = bus->priv;
|
||||
|
||||
dm_gpio_set_value(&gpio_mii->mdc_gpio, v);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mii_delay(struct bb_miiphy_bus *bus)
|
||||
{
|
||||
udelay(1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct bb_miiphy_bus bb_miiphy_buses[] = {
|
||||
{
|
||||
.name = "ihs0",
|
||||
.init = mii_mdio_init,
|
||||
.mdio_active = mii_mdio_active,
|
||||
.mdio_tristate = mii_mdio_tristate,
|
||||
.set_mdio = mii_set_mdio,
|
||||
.get_mdio = mii_get_mdio,
|
||||
.set_mdc = mii_set_mdc,
|
||||
.delay = mii_delay,
|
||||
.priv = &gpio_mii_set[0],
|
||||
},
|
||||
{
|
||||
.name = "ihs1",
|
||||
.init = mii_mdio_init,
|
||||
.mdio_active = mii_mdio_active,
|
||||
.mdio_tristate = mii_mdio_tristate,
|
||||
.set_mdio = mii_set_mdio,
|
||||
.get_mdio = mii_get_mdio,
|
||||
.set_mdc = mii_set_mdc,
|
||||
.delay = mii_delay,
|
||||
.priv = &gpio_mii_set[1],
|
||||
},
|
||||
{
|
||||
.name = "ihs2",
|
||||
.init = mii_mdio_init,
|
||||
.mdio_active = mii_mdio_active,
|
||||
.mdio_tristate = mii_mdio_tristate,
|
||||
.set_mdio = mii_set_mdio,
|
||||
.get_mdio = mii_get_mdio,
|
||||
.set_mdc = mii_set_mdc,
|
||||
.delay = mii_delay,
|
||||
.priv = &gpio_mii_set[2],
|
||||
},
|
||||
};
|
||||
|
||||
int bb_miiphy_buses_num = ARRAY_SIZE(bb_miiphy_buses);
|
||||
@@ -0,0 +1,2 @@
|
||||
uint calculate_octo_phy_mask(void);
|
||||
int init_octo_phys(uint octo_phy_mask);
|
||||
@@ -0,0 +1,161 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2016
|
||||
* Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <command.h>
|
||||
#include <env.h>
|
||||
#include <tpm-v1.h>
|
||||
#include <malloc.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#include "hre.h"
|
||||
|
||||
int flush_keys(struct udevice *tpm)
|
||||
{
|
||||
u16 key_count;
|
||||
u8 buf[288];
|
||||
u8 *ptr;
|
||||
u32 err;
|
||||
uint i;
|
||||
|
||||
/* fetch list of already loaded keys in the TPM */
|
||||
err = tpm_get_capability(tpm, TPM_CAP_HANDLE, TPM_RT_KEY, buf,
|
||||
sizeof(buf));
|
||||
if (err)
|
||||
return -1;
|
||||
key_count = get_unaligned_be16(buf);
|
||||
ptr = buf + 2;
|
||||
for (i = 0; i < key_count; ++i, ptr += 4) {
|
||||
err = tpm_flush_specific(tpm, get_unaligned_be32(ptr),
|
||||
TPM_RT_KEY);
|
||||
if (err && err != TPM_KEY_OWNER_CONTROL)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int decode_hexstr(char *hexstr, u8 **result)
|
||||
{
|
||||
int len = strlen(hexstr);
|
||||
int bytes = len / 2;
|
||||
int i;
|
||||
u8 acc = 0;
|
||||
|
||||
if (len % 2 == 1)
|
||||
return 1;
|
||||
|
||||
*result = (u8 *)malloc(bytes);
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
char cur = tolower(hexstr[i]);
|
||||
u8 val;
|
||||
|
||||
if ((cur >= 'a' && cur <= 'f') || (cur >= '0' && cur <= '9')) {
|
||||
val = cur - (cur > '9' ? 87 : 48);
|
||||
|
||||
if (i % 2 == 0)
|
||||
acc = 16 * val;
|
||||
else
|
||||
(*result)[i / 2] = acc + val;
|
||||
} else {
|
||||
free(*result);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int extract_subprogram(u8 **progdata, u32 expected_magic,
|
||||
struct key_program **result)
|
||||
{
|
||||
struct key_program *prog = *result;
|
||||
u32 magic, code_crc, code_size;
|
||||
|
||||
magic = get_unaligned_be32(*progdata);
|
||||
code_crc = get_unaligned_be32(*progdata + 4);
|
||||
code_size = get_unaligned_be32(*progdata + 8);
|
||||
|
||||
*progdata += 12;
|
||||
|
||||
if (magic != expected_magic)
|
||||
return -1;
|
||||
|
||||
*result = malloc(sizeof(struct key_program) + code_size);
|
||||
|
||||
if (!*result)
|
||||
return -1;
|
||||
|
||||
prog->magic = magic;
|
||||
prog->code_crc = code_crc;
|
||||
prog->code_size = code_size;
|
||||
memcpy(prog->code, *progdata, code_size);
|
||||
|
||||
*progdata += code_size;
|
||||
|
||||
if (hre_verify_program(prog)) {
|
||||
free(prog);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct key_program *parse_and_check_keyprog(u8 *progdata)
|
||||
{
|
||||
struct key_program *result = NULL, *hmac = NULL;
|
||||
|
||||
/* Part 1: Load key program */
|
||||
|
||||
if (extract_subprogram(&progdata, MAGIC_KEY_PROGRAM, &result))
|
||||
return NULL;
|
||||
|
||||
/* Part 2: Load hmac program */
|
||||
|
||||
if (extract_subprogram(&progdata, MAGIC_HMAC, &hmac))
|
||||
return NULL;
|
||||
|
||||
free(hmac);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int load_and_run_keyprog(struct udevice *tpm)
|
||||
{
|
||||
char *cmd = NULL;
|
||||
u8 *binprog = NULL;
|
||||
char *hexprog;
|
||||
struct key_program *prog;
|
||||
|
||||
cmd = env_get("loadkeyprogram");
|
||||
|
||||
if (!cmd || run_command(cmd, 0))
|
||||
return 1;
|
||||
|
||||
hexprog = env_get("keyprogram");
|
||||
|
||||
if (decode_hexstr(hexprog, &binprog))
|
||||
return 1;
|
||||
|
||||
prog = parse_and_check_keyprog(binprog);
|
||||
free(binprog);
|
||||
|
||||
if (!prog)
|
||||
return 1;
|
||||
|
||||
if (hre_run_program(tpm, prog->code, prog->code_size)) {
|
||||
free(prog);
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("\nSD code ran successfully\n");
|
||||
|
||||
free(prog);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* (C) Copyright 2016
|
||||
* Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
|
||||
*/
|
||||
|
||||
#ifndef __KEYPROGRAM_H
|
||||
#define __KEYPROGRAM_H
|
||||
|
||||
int load_and_run_keyprog(struct udevice *tpm);
|
||||
int flush_keys(struct udevice *tpm);
|
||||
|
||||
#endif /* __KEYPROGRAM_H */
|
||||
@@ -0,0 +1,20 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2016
|
||||
* Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <config.h>
|
||||
#include <asm/arch/cpu.h>
|
||||
|
||||
void spl_board_init(void)
|
||||
{
|
||||
#if CONFIG_SPL_BOOT_DEVICE == SPL_BOOT_SPI_NOR_FLASH
|
||||
u32 *bootrom_save = (u32 *)CONFIG_SPL_BOOTROM_SAVE;
|
||||
u32 *regs = (u32 *)(*bootrom_save);
|
||||
|
||||
printf("Returning to BootROM (return address %08x)...\n", regs[13]);
|
||||
return_to_bootrom();
|
||||
#endif
|
||||
}
|
||||
Reference in New Issue
Block a user