/* Copyright (c) 2010 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.
 */

/* This program mimicks the TPM usage from read-only firmware.  It exercises
 * the TPM functionality needed in the read-only firmware.  It is meant to be
 * integrated with the rest of the read-only firmware.  It is also provided as
 * a test.
 */

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>

#include "tlcl.h"
#include "utility.h"

/* These index values are used to create NVRAM spaces.  They only need to be
 * unique.
 */
#define INDEX0 0xda70
#define INDEX1 0xda71
#define INDEX2 0xda72
#define INDEX3 0xda73

#define INDEX_INITIALIZED 0xda80

/* This is called once at initialization time.  It may be called again from
 * recovery mode to rebuild the spaces if something incomprehensible happened
 * and the spaces are gone or messed up.  This is called after TPM_Startup and
 * before the spaces are write-locked, so there is a chance that they can be
 * recreated (but who knows---if anything can happen, there are plenty of ways
 * of making this FUBAR).
 */
void InitializeSpaces(void) {
	uint32_t zero = 0;
	uint32_t perm = TPM_NV_PER_WRITE_STCLEAR | TPM_NV_PER_PPWRITE;

	printf("Initializing spaces\n");
	TlclSetNvLocked();  /* useful only the first time */

	TlclDefineSpace(INDEX0, perm, 4);
	TlclWrite(INDEX0, (uint8_t *) &zero, 4);
	TlclDefineSpace(INDEX1, perm, 4);
	TlclWrite(INDEX1, (uint8_t *) &zero, 4);
	TlclDefineSpace(INDEX2, perm, 4);
	TlclWrite(INDEX2, (uint8_t *) &zero, 4);
	TlclDefineSpace(INDEX3, perm, 4);
	TlclWrite(INDEX3, (uint8_t *) &zero, 4);

	perm = (TPM_NV_PER_READ_STCLEAR | TPM_NV_PER_WRITE_STCLEAR |
		TPM_NV_PER_PPWRITE);
	TlclDefineSpace(INDEX_INITIALIZED, perm, 1);
}


void EnterRecoveryMode(void) {
	printf("entering recovery mode");
	exit(0);
}


int main(int argc, char** argv) {
	uint8_t c;
	uint32_t index_0, index_1, index_2, index_3;

	TlclLibInit();

	TlclStartup();
	TlclSelfTestFull();

	TlclAssertPhysicalPresence();

	/* Checks if initialization has completed by trying to read-lock a space
	 * that's created at the end of initialization.
	 */
	if (TlclRead(INDEX_INITIALIZED, &c, 0) == TPM_E_BADINDEX) {
		/* The initialization did not complete.
		*/
		InitializeSpaces();
	}

	/* Checks if spaces are OK or messed up.
	 */
	if (TlclRead(INDEX0, (uint8_t*) &index_0,
		     sizeof(index_0)) != TPM_SUCCESS ||
	    TlclRead(INDEX1, (uint8_t*) &index_1,
		     sizeof(index_1)) != TPM_SUCCESS ||
	    TlclRead(INDEX2, (uint8_t*) &index_2,
		     sizeof(index_2)) != TPM_SUCCESS ||
	    TlclRead(INDEX3, (uint8_t*) &index_3,
		     sizeof(index_3)) != TPM_SUCCESS) {
		EnterRecoveryMode();
	}

	/* Writes space, and locks it.  Then attempts to write again.  I really wish
	 * I could use the imperative.
	 */
	index_0 += 1;
	if (TlclWrite(INDEX0, (uint8_t*) &index_0,
		      sizeof(index_0) != TPM_SUCCESS)) {
		error("could not write index 0\n");
	}
	TlclWriteLock(INDEX0);
	if (TlclWrite(INDEX0, (uint8_t*) &index_0,
		      sizeof(index_0)) == TPM_SUCCESS) {
		error("index 0 is not locked\n");
	}

	/* Done for now.
	 */
	printf("TEST SUCCEEDED\n");
	exit(0);
}
