package edu.mit.csail.tc;
import javacard.framework.Util;
/**
* NVRAM-backed implementation of the TEM persistent store abstraction.
* @author Victor Costan
*
* This module implements the persistent store abstraction in the TEM
* specification. The persistent store is an associative memory, where the
* keys/addresses are huge (in comparison to regular addresses) integers. Huge
* keys serve double purpose, as memory addresses and authorization values
* (guessing a persistent store address is just as hard as guessing a symmetric
* encryption key).
*/
public class TEMStore {
/** The size, in bytes, of a persistent store value. */
public static final short VALUE_SIZE = 20;
/** The size, in bytes, of a persistent store key(address). */
public static final short ADDRESS_SIZE = 20;
/** The number of cells in the NVRAM-backed implementation. */
public static final short NUM_CELLS = 64;
/** The size of a cell in the TEM store. */
private static final short CELL_SIZE = 40; // KEY_LENGTH + VALUE_LENGTH
/** Special value to indicate inexistent cell. */
public static final short INVALID_CELL = (short)-1;
/**
* The NVRAM-backed permanent store data.
*
* The NVRAM is partitioned into cells. Each cell backs a location, and stores
* the location's address (ADDRESS_SIZE bytes) and the value at the location
* (VALUE_SIZE bytes).
*/
private static byte data[];
/** Bitfield indicating which entries are free in the store data. */
private static byte free[];
/**
* Initializes the TEM persistent store.
*
* Called when the TEM is activated.
*/
public static void init() {
data = new byte[(short)(NUM_CELLS * CELL_SIZE)];
free = new byte[(short)(NUM_CELLS / (short)8)];
Util.arrayFillNonAtomic(free, (short)0, (short)(NUM_CELLS / 8), (byte)0xFF);
}
/**
* Releases all the resources held by the TEM persistent store module.
*
* Called when the TEM is de-activated.
*/
public static void deinit() {
data = null;
}
/**
* Finds the cell containing an association for an address.
*
* @param address the buffer containing the address to look for
* @param addressOffset the position in the buffer containing the first byte
* of the address
* @return the ID of a cell containing an association for the given address,
* or {@link #INVALID_CELL} if no such association exists
*/
public static short findCell(byte[] address, short addressOffset) {
for (short i = 0, cellOffset = 0; i < NUM_CELLS;
i++, cellOffset += CELL_SIZE) {
if ((free[i >> 3] & (1 << (i & 7))) != 0)
continue;
if (Util.arrayCompare(address, addressOffset, data, cellOffset,
ADDRESS_SIZE) == 0)
return i;
}
return INVALID_CELL;
}
/**
* Finds an empty cell and marks it as allocated.
*
* @return the ID of a cell that was free before {@link #allocCell()} was
* called, or {@link #INVALID_CELL} if all the cells are taken
*/
private static short allocCell() {
for (short i = 0, cellOffset = 0; i < NUM_CELLS;
i++, cellOffset += CELL_SIZE) {
if ((free[i >> 3] & (1 << (i & 7))) == 0)
continue;
free[i >> 3] ^= 1 << (i & 7);
return i;
}
return INVALID_CELL;
}
/** Reads/Writes a value from the TEM store. */
public static boolean readOrWrite(byte[] address, short addressOffset,
byte[] value, short valueOffset,
boolean opIsRead, boolean create) {
short cellNumber = findCell(address, addressOffset);
if (cellNumber == INVALID_CELL) {
if (create == false)
return false;
cellNumber = allocCell();
if (cellNumber == INVALID_CELL)
return false; // TODO: driver-managed cells or page faults
Util.arrayCopyNonAtomic(address, addressOffset, data,
(short)(CELL_SIZE * cellNumber), ADDRESS_SIZE);
}
if (opIsRead)
Util.arrayCopyNonAtomic(data,
(short)(CELL_SIZE * cellNumber + ADDRESS_SIZE),
value, valueOffset, VALUE_SIZE);
else
Util.arrayCopyNonAtomic(value, valueOffset, data,
(short)(CELL_SIZE * cellNumber + ADDRESS_SIZE),
VALUE_SIZE);
return true;
}
/** Clobbers a TEM store entry. */
public static boolean releaseCell(byte[] address, short addressOffset) {
short cellNumber = findCell(address, addressOffset);
if (cellNumber == INVALID_CELL)
return false;
free[cellNumber >> 3] |= 1 << (cellNumber & 7);
return true;
}
}