/* Copyright 2015 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 "internal.h"
#include "flash_log.h"
#include "dcrypto_regs.h"

/**
 * Define KEYMGR SHA/HMAC access structure.
 */
static volatile struct trng_reg *reg_trng = (void *)(GC_TRNG_BASE_ADDR);

/**
 * The H1 TRNG uses the collapse time of a ring oscillator (RO) that is
 * initialized in a 3x mode (three enable pulses) and eventually collapses
 * to a stable 1x mode as a result of accumulated jitter (thermal noise).
 * A Phase-Frequency Detector (PFD) compares the 3x RO to a reference
 * RO (1.5x) and captures the state of a counter that is incremented
 * from the reference RO. The resulting reference-cycles-to-collapse
 * distribution is log-normal, and truncation of the counter bits results in
 * a distribution that approaches uniform.
 *
 * TRNG_SAMPLE_BITS defines how many bits to use from the 16 bit counter
 * output coming from the analog unit. Entropy is highest in least significant
 * bits of counter. For FIPS-certified code use just Bit 0 - it provides
 * highest entropy, allows better security settings for TRNG and simplifies
 * implementation of continuous health tests.
 */
#ifndef TRNG_SAMPLE_BITS
#define TRNG_SAMPLE_BITS 1
#endif

/**
 * Attempts to read TRNG_EMPTY before reporting a stall. Practically data should
 * be available in less than 0x7ff cycles under normal conditions. 0x7ff was
 * chosen to match the hardware TRNG TIMEOUT_COUNTER. Test on boards with slow
 * TRNG before reducing this number.
 */
#define TRNG_EMPTY_COUNT 0x7ff

/**
 * Number of attempts to reset TRNG after stall is detected.
 */
#define TRNG_RESET_COUNT 8

void fips_init_trng(void)
{
	/*
	 * Most of the trng initialization requires high permissions. If RO has
	 * dropped the permission level, dont try to read or write these high
	 * permission registers because it will cause rolling reboots. RO
	 * should do the TRNG initialization before dropping the level.
	 *
	 * For Cr50 RO doesn't drop permission level and init_trng() is called
	 * by board_init() before dropping permissions.
	 */

	/**
	 * According to NIST SP 800-90B only vetted conditioning mechanism
	 * should be used for post-processing raw entropy.
	 * See SP 800-90B, 3.1.5.1 Using Vetted Conditioning Components.
	 * Use of non-vetted algorithms is governed in 3.1.5.2, but
	 * assumes conservative coefficient 0.85 for entropy estimate,
	 * which increase number of requests to TRNG to get desirable
	 * entropy and prevents from getting full entropy.
	 */
	reg_trng->post_processing  = 0;

	/**
	 * TRNG can return up to 16 bits at a time, but highest bits
	 * have lower entropy. Practically on Cr50 only 13 bits can be
	 * used - setting to higher value makes TRNG_EMPTY always set.
	 * Entropy assessed to be reasonable (one bit H > 0.85)
	 * for up to 8 bits [7..0].
	 * Time to get 32bit random is roughly 160/TRNG_SAMPLE_BITS us.
	 */
	reg_trng->slice_max_upper_limit = TRNG_SAMPLE_BITS - 1;

	/* lowest bit have highest entropy, so always start from it */
	reg_trng->slice_min_lower_limit = 0;

	/**
	 * Analog logic cannot create a value < 8 under normal operating
	 * conditions, but there's a chance that an attacker could coax
	 * them out.
	 * Bit 0 - Enable rejection for values outside of range specified
	 * by TRNG_ALLOWED_VALUES register
	 */
	reg_trng->secure_post_processing = 0x1;

	/**
	 * Since for FIPS settings we use TRNG_SAMPLE_BITS = 1,
	 * and take only bit 0 from internal 16 bit reading, no bias is
	 * created for bit 0 if allowed_min is set to 6, which
	 * actually means min accepted value is 8 (RTL adds +2).
	 * TRNG_ALLOWED_VALUES_MAX=0x04 (accept all values up to 2^16-1).
	 * So, range will be [8..65535], with probability for bit 0 and 1
	 * remaining 50/50.
	 */
	reg_trng->allowed_values = 0x26;

	reg_trng->timeout_counter = 0x7ff;
	reg_trng->timeout_max_try = 4;
	reg_trng->power_down_b = 1;
	reg_trng->go_event = 1;
}

uint64_t read_rand(void)
{
	uint32_t empty_count = 0;
	uint32_t reset_count = 0;

#ifdef CRYPTO_TEST_SETUP
	/* Do we need to simulate error? */
	if (fips_break_cmd == FIPS_BREAK_TRNG)
		return (uint64_t)1UL << 32; /* Valid result, but value = 0. */
#endif

	/**
	 * Make sure we never hang in the loop - try at max TRNG_RESET_COUNT
	 * reset attempts, then return error
	 */
	while (reg_trng->empty && (reset_count < TRNG_RESET_COUNT)) {
		if ((reg_trng->fsm_state & GC_TRNG_FSM_STATE_FSM_IDLE_MASK) ||
		    empty_count > TRNG_EMPTY_COUNT) {
			/* TRNG timed out, restart */
			reg_trng->stop_work = 1;
#ifdef CONFIG_FLASH_LOG
			fips_vtable->flash_log_add_event(FE_LOG_TRNG_STALL, 0,
							 NULL);
#endif
			reg_trng->go_event = 1;
			empty_count = 0;
			reset_count++;
		}
		empty_count++;
	}
	/**
	 * High 32-bits set to zero in case of error;
	 * otherwise value >> 32 == 1
	 */
	return (uint64_t)reg_trng->read_data |
	       ((uint64_t)(reset_count < TRNG_RESET_COUNT) << 32);
}

/* Local switch to test command. Enable when work on it. */
#ifndef CRYPTO_TEST_CMD_RAND
#define CRYPTO_TEST_CMD_RAND 0
#endif

#if !defined(SECTION_IS_RO) && defined(CRYPTO_TEST_SETUP)
#include "console.h"
#include "endian.h"
#include "extension.h"
#include "timer.h"
#include "watchdog.h"

#if CRYPTO_TEST_CMD_RAND
static void  print_rand_stat(uint32_t *histogram, size_t size)
{
	struct pair {
		uint32_t value;
		uint32_t count;
	};
	struct pair min;
	struct pair max;
	size_t count;

	min.count = ~0;
	max.count = 0;
	max.value = ~0;
	min.value = ~0;

	for (count = 0; count < size; count++) {
		if (histogram[count] > max.count) {
			max.count = histogram[count];
			max.value = count;
		}
		if (histogram[count] < min.count) {
			min.count = histogram[count];
			min.value = count;
		}
	}

	ccprintf("min %d(%d), max %d(%d)", min.count, min.value,
		 max.count, max.value);

	for (count = 0; count < size; count++) {
		if (!(count % 8)) {
			ccprintf("\n");
			cflush();
		}
		ccprintf(" %6d", histogram[count]);
	}
	ccprintf("\n");
}

/* histogram at byte level */
static uint32_t histogram[256];
/* histogram at level of TRNG samples */
static uint32_t histogram_trng[1 << TRNG_SAMPLE_BITS];

static int command_rand(int argc, char **argv)
{
	int count = 1000; /* Default number of cycles. */
	uint32_t val = 0, bits = 0;

	if (argc == 2)
		count = strtoi(argv[1], NULL, 10);

	memset(histogram, 0, sizeof(histogram));
	memset(histogram_trng, 0, sizeof(histogram_trng));
	ccprintf("Retrieving %d 32-bit random words.\n", count);
	while (count-- > 0) {
		uint64_t rnd;
		uint32_t rvalue;
		size_t size;

		rnd = fips_trng_rand32();
		if (!rand_valid(rnd)) {
			ccprintf("Failed reading TRNG.\n");
			return EC_ERROR_HW_INTERNAL;
		}
		rvalue = (uint32_t)rnd;
		/* update byte-level histogram */
		for (size = 0; size < sizeof(rvalue); size++)
			histogram[((uint8_t *)&rvalue)[size]]++;

		/* update histogram on TRNG sample size level */
		val = (val | (rvalue << bits)) & ((1 << TRNG_SAMPLE_BITS) - 1);
		rvalue >>= TRNG_SAMPLE_BITS - bits;
		bits += 32;
		while (bits >= TRNG_SAMPLE_BITS) {
			histogram_trng[val]++;
			val = rvalue & ((1 << TRNG_SAMPLE_BITS) - 1);
			rvalue >>= TRNG_SAMPLE_BITS;
			bits -= TRNG_SAMPLE_BITS;
		};

		if (!(count % 10000))
			watchdog_reload();
	}
	ccprintf("Byte-level histogram:\n");
	print_rand_stat(histogram, ARRAY_SIZE(histogram));
	ccprintf("\nSample-level (%d bits) histogram:\n", TRNG_SAMPLE_BITS);
	print_rand_stat(histogram_trng, ARRAY_SIZE(histogram_trng));

	return EC_SUCCESS;
}
DECLARE_SAFE_CONSOLE_COMMAND(rand, command_rand, NULL, NULL);

#endif /* CRYPTO_TEST_CMD_RAND */

/* For testing we need unchecked values from TRNG. */
static bool raw_rand_bytes(void *buffer, size_t len)
{
	size_t random_togo = 0;
	size_t buffer_index = 0;
	uint32_t random_value;
	uint8_t *buf = (uint8_t *) buffer;

	/*
	 * Retrieve random numbers in 4 byte quantities and pack as many bytes
	 * as needed into 'buffer'. If len is not divisible by 4, the
	 * remaining random bytes get dropped.
	 */
	while (buffer_index < len) {
		if (!random_togo) {
			uint64_t rnd = read_rand();

			if (!rand_valid(rnd))
				return false;

			random_value = (uint32_t)rnd;
			random_togo = sizeof(random_value);
		}
		buf[buffer_index++] = random_value >>
			((random_togo-- - 1) * 8);
	}
	return true;
}

/*
 * This extension command is similar to TPM2_GetRandom, but made
 * available for CRYPTO_TEST = 1 which disables TPM.
 * Command structure, shared out of band with the test driver running
 * on the host:
 *
 * field     |    size  |                  note
 * =========================================================================
 * text_len  |    2     | the number of random bytes to generate, big endian
 * type      |    1     | 0 - TRNG, 1 = FIPS TRNG, 2 = FIPS DRBG
 *           |          | 3 - TRNG after restart
 *           |          | other values reserved for extensions
 */
static enum vendor_cmd_rc trng_test(enum vendor_cmd_cc code, void *buf,
				    size_t input_size, size_t *response_size)
{
	uint16_t text_len;
	uint8_t *cmd = buf;
	uint8_t op_type = 0;

	if (input_size != sizeof(text_len) + 1) {
		*response_size = 0;
		return VENDOR_RC_BOGUS_ARGS;
	}

	text_len = be16toh(*(uint16_t *)cmd);
	op_type = cmd[sizeof(text_len)];

	if (text_len > *response_size) {
		*response_size = 0;
		return VENDOR_RC_BOGUS_ARGS;
	}

	switch (op_type) {
	case 3:
		/* Power down LDO, wait 1ms, power up. */
		reg_trng->power_down_b = 0;
		udelay(1000);
		reg_trng->power_down_b = 1;
		reg_trng->go_event = 1;
		/* Fall through */
	case 0:
		if (!raw_rand_bytes(buf, text_len))
			return VENDOR_RC_INTERNAL_ERROR;
		break;
	case 1:
		if (!fips_trng_bytes(buf, text_len))
			return VENDOR_RC_INTERNAL_ERROR;
		break;
	case 2:
		if (!fips_rand_bytes(buf, text_len))
			return VENDOR_RC_INTERNAL_ERROR;
		break;

	default:
		return VENDOR_RC_BOGUS_ARGS;
	}
	*response_size = text_len;
	return VENDOR_RC_SUCCESS;
}

DECLARE_VENDOR_COMMAND(VENDOR_CC_TRNG_TEST, trng_test);

#endif /* CRYPTO_TEST_SETUP */
