598 lines
17 KiB
C#
598 lines
17 KiB
C#
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// Microsoft Research Singularity
|
||
|
//
|
||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
//
|
||
|
// File: TPM.cs
|
||
|
//
|
||
|
// Note:
|
||
|
//
|
||
|
// Useful refs:
|
||
|
|
||
|
//
|
||
|
|
||
|
//#define DEBUG_DISPATCH_IO
|
||
|
//#define DEBUG_IO
|
||
|
|
||
|
|
||
|
using Microsoft.Singularity.Io;
|
||
|
|
||
|
using System;
|
||
|
using System.Runtime.CompilerServices;
|
||
|
using System.Diagnostics;
|
||
|
using System.Threading;
|
||
|
|
||
|
using Microsoft.Singularity.Configuration;
|
||
|
|
||
|
namespace Microsoft.Singularity.Hal
|
||
|
{
|
||
|
|
||
|
// create the resource object for LTR to fill in
|
||
|
[DriverCategory]
|
||
|
[Signature("/pseudobus0/tpm12")]
|
||
|
public sealed class TpmResources : DriverCategoryDeclaration
|
||
|
{
|
||
|
[IoMemoryRange(1, Default = 0xfed40000, Length = 0x4000)]
|
||
|
public IoMemoryRange pcrs;
|
||
|
}
|
||
|
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// Underlying device implementation.
|
||
|
//
|
||
|
internal class Tpm
|
||
|
{
|
||
|
private class TpmRegisterOffsets
|
||
|
{
|
||
|
public const int TpmRegAccess = 0x0;
|
||
|
public const int TpmStatus = 0x18;
|
||
|
public const int TpmBurst = 0x19;
|
||
|
public const int TpmDataFifo = 0x24;
|
||
|
};
|
||
|
|
||
|
private class TpmRegAccessMasks
|
||
|
{
|
||
|
public const byte TpmAccessValidStatus = 0x80;
|
||
|
public const byte TpmAccessActiveLocality = 0x20;
|
||
|
public const byte TpmAccessHasBeenSeized = 0x10;
|
||
|
public const byte TpmAccessRequestUse = 0x2;
|
||
|
};
|
||
|
|
||
|
private class TpmRegStatusMasks
|
||
|
{
|
||
|
public const byte TpmStatusValid = 0x80;
|
||
|
public const byte TpmStatusCommandReady = 0x40;
|
||
|
public const byte TpmStatusGo = 0x20;
|
||
|
public const byte TpmStatusDataAvailable = 0x10;
|
||
|
public const byte TpmStatusExpect = 0x8;
|
||
|
public const byte TpmStatusRetry = 0x2;
|
||
|
|
||
|
};
|
||
|
|
||
|
// Retries assuming 1 ms wait between retry
|
||
|
//
|
||
|
// TODO: those are the default values, grab the actual ones from the device
|
||
|
|
||
|
private class Retries
|
||
|
{
|
||
|
public const int MaxStatusRetries = 200;
|
||
|
public const int MaxDataRetries = 400;
|
||
|
|
||
|
}
|
||
|
|
||
|
byte[] capability_command = {0, 193,
|
||
|
0, 0, 0, 18,
|
||
|
0, 0, 0, 101,
|
||
|
0, 0, 0, 6,
|
||
|
0, 0, 0, 0 };
|
||
|
|
||
|
private int locality = 0;
|
||
|
|
||
|
private IoMemory tpmIoMemory;
|
||
|
|
||
|
private byte[] minResponse;
|
||
|
|
||
|
//private const int minResponseLen = 6;
|
||
|
private const int minResponseLen = 6;
|
||
|
private const int dataLenOffset = 2;
|
||
|
|
||
|
|
||
|
// Constructor
|
||
|
internal Tpm(/*TpmResources! res*/)
|
||
|
{
|
||
|
Tracing.Log(Tracing.Debug, "Tpm: Initialize() called\n");
|
||
|
|
||
|
DebugStub.WriteLine("Tpm: Initialize() called");
|
||
|
|
||
|
tpmIoMemory = IoMemory.MapPhysicalMemory(new UIntPtr(0xfed40000),
|
||
|
new UIntPtr(0x4000),
|
||
|
true,
|
||
|
true);
|
||
|
|
||
|
if (tpmIoMemory == null) {
|
||
|
DebugStub.WriteLine("Tpm: Initialize(), MapPhysicalMemory failed");
|
||
|
|
||
|
}
|
||
|
|
||
|
minResponse = new byte[minResponseLen];
|
||
|
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
internal void PrintRegisters()
|
||
|
{
|
||
|
byte TpmAccess = tpmIoMemory.Read8(locality + TpmRegisterOffsets.TpmRegAccess);
|
||
|
byte TpmStatus = tpmIoMemory.Read8(locality + TpmRegisterOffsets.TpmStatus);
|
||
|
|
||
|
DebugStub.WriteLine("Tpm: PrintRegisters() Access={0:x2}, Status={1:x2}", __arglist(TpmAccess, TpmStatus));
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
ushort GetBurst()
|
||
|
{
|
||
|
ushort burst = 0;
|
||
|
for (int j = 0; j < Retries.MaxDataRetries; j++) {
|
||
|
burst = tpmIoMemory.Read16(locality + TpmRegisterOffsets.TpmBurst);
|
||
|
|
||
|
if (burst != 0)
|
||
|
break;
|
||
|
|
||
|
Thread.Sleep(1);
|
||
|
}
|
||
|
|
||
|
return burst;
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
//byte Read8(int byteOffset)
|
||
|
|
||
|
internal bool WaitOnBit(int address, byte mask, int MaxRetries)
|
||
|
{
|
||
|
for (int i = 0; i < MaxRetries; i++) {
|
||
|
byte data = tpmIoMemory.Read8(address);
|
||
|
|
||
|
if ((data & mask) > 0)
|
||
|
return true;
|
||
|
|
||
|
Thread.Sleep(1);
|
||
|
}
|
||
|
return false;
|
||
|
|
||
|
}
|
||
|
|
||
|
internal bool Send(byte[] command)
|
||
|
{
|
||
|
bool Status = true;
|
||
|
|
||
|
DebugStub.WriteLine("Tpm: Send() called");
|
||
|
PrintRegisters();
|
||
|
|
||
|
|
||
|
byte TpmAccess = tpmIoMemory.Read8(locality + TpmRegisterOffsets.TpmRegAccess);
|
||
|
|
||
|
|
||
|
//
|
||
|
// check the access register
|
||
|
//
|
||
|
|
||
|
//
|
||
|
// check if the TPM has been disabled
|
||
|
//
|
||
|
|
||
|
if (TpmAccess == 0xff) {
|
||
|
DebugStub.WriteLine("Tpm: Send() TPM disabled");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if ((TpmAccess & TpmRegAccessMasks.TpmAccessHasBeenSeized) > 0) {
|
||
|
DebugStub.WriteLine("Tpm: Send() TPM seized");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if ((TpmAccess & TpmRegAccessMasks.TpmAccessValidStatus) == 0) {
|
||
|
DebugStub.WriteLine("Tpm: Send() TPM had invalid status");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// request locality use
|
||
|
//
|
||
|
|
||
|
if ((TpmAccess & TpmRegAccessMasks.TpmAccessActiveLocality) == 0) {
|
||
|
//
|
||
|
TpmAccess |= TpmRegAccessMasks.TpmAccessActiveLocality;
|
||
|
tpmIoMemory.Write8(locality + TpmRegisterOffsets.TpmRegAccess, TpmAccess);
|
||
|
|
||
|
if (!WaitOnBit(locality + TpmRegisterOffsets.TpmRegAccess,
|
||
|
TpmRegAccessMasks.TpmAccessActiveLocality,
|
||
|
Retries.MaxStatusRetries))
|
||
|
{
|
||
|
DebugStub.WriteLine("Tpm: Send() could not set locality");
|
||
|
return false;
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// check if the device is ready
|
||
|
//
|
||
|
|
||
|
byte TpmStatus = tpmIoMemory.Read8(locality + TpmRegisterOffsets.TpmStatus);
|
||
|
|
||
|
if ((TpmStatus & TpmRegStatusMasks.TpmStatusCommandReady) == 0) {
|
||
|
TpmStatus |= TpmRegStatusMasks.TpmStatusCommandReady;
|
||
|
tpmIoMemory.Write8(locality + TpmRegisterOffsets.TpmStatus, TpmStatus);
|
||
|
|
||
|
if (!WaitOnBit(locality + TpmRegisterOffsets.TpmStatus,
|
||
|
TpmRegStatusMasks.TpmStatusCommandReady,
|
||
|
Retries.MaxStatusRetries))
|
||
|
{
|
||
|
DebugStub.WriteLine("Tpm: Send() device not ready");
|
||
|
PrintRegisters();
|
||
|
return false;
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
DebugStub.WriteLine("Tpm: Send() command length={0:x2}", __arglist(command.Length));
|
||
|
|
||
|
//
|
||
|
// write the data to the data register
|
||
|
//
|
||
|
|
||
|
for (int i = 0; i < command.Length;) {
|
||
|
ushort burst = 0;
|
||
|
|
||
|
burst = GetBurst();
|
||
|
|
||
|
if (burst == 0) {
|
||
|
DebugStub.WriteLine("Tpm: Send() timed out waiting for burst");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
while (burst > 0) {
|
||
|
burst--;
|
||
|
|
||
|
if (i == command.Length)
|
||
|
break;
|
||
|
|
||
|
//
|
||
|
// last byte, check if we still expect
|
||
|
//
|
||
|
|
||
|
if (i == command.Length - 1) {
|
||
|
TpmStatus = tpmIoMemory.Read8(locality + TpmRegisterOffsets.TpmStatus);
|
||
|
|
||
|
if ((TpmStatus & TpmRegStatusMasks.TpmStatusExpect) == 0) {
|
||
|
//
|
||
|
// something went wrong
|
||
|
//
|
||
|
DebugStub.WriteLine("Tpm: Send() not expecting more data before last byte");
|
||
|
return false;
|
||
|
|
||
|
}
|
||
|
DebugStub.WriteLine("Tpm: Send() last byte expected ok");
|
||
|
|
||
|
}
|
||
|
|
||
|
tpmIoMemory.Write8(locality + TpmRegisterOffsets.TpmDataFifo, command[i]);
|
||
|
|
||
|
|
||
|
i++;
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
if (!WaitOnBit(locality + TpmRegisterOffsets.TpmStatus,
|
||
|
TpmRegStatusMasks.TpmStatusValid,
|
||
|
Retries.MaxDataRetries))
|
||
|
{
|
||
|
DebugStub.WriteLine("Tpm: Send(), WaitOnTpmStatusValid timed out after command write");
|
||
|
PrintRegisters();
|
||
|
return false;
|
||
|
|
||
|
}
|
||
|
|
||
|
TpmStatus = tpmIoMemory.Read8(locality + TpmRegisterOffsets.TpmStatus);
|
||
|
|
||
|
if ((TpmStatus & TpmRegStatusMasks.TpmStatusExpect) > 0) {
|
||
|
//
|
||
|
// more data needed
|
||
|
//
|
||
|
DebugStub.WriteLine("Tpm: Send(), more data needed");
|
||
|
return false;
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// launch the command
|
||
|
//
|
||
|
TpmStatus |= TpmRegStatusMasks.TpmStatusGo;
|
||
|
tpmIoMemory.Write8(locality + TpmRegisterOffsets.TpmStatus, TpmStatus);
|
||
|
|
||
|
DebugStub.WriteLine("Tpm: Send(), success");
|
||
|
|
||
|
return Status;
|
||
|
|
||
|
}
|
||
|
|
||
|
private bool ReadBytes(byte[] buffer, int offset, int numBytes, ushort prevBurst, out ushort burstRemainder)
|
||
|
{
|
||
|
byte TpmStatus;
|
||
|
burstRemainder = 0;
|
||
|
//
|
||
|
// read the remaining burst bytes
|
||
|
//
|
||
|
|
||
|
|
||
|
if (prevBurst > 0) {
|
||
|
for (int i = 0; i < prevBurst; i++) {
|
||
|
if (numBytes > 0) {
|
||
|
TpmStatus = tpmIoMemory.Read8(locality + TpmRegisterOffsets.TpmStatus);
|
||
|
|
||
|
if ((TpmStatus & TpmRegStatusMasks.TpmStatusDataAvailable) == 0) {
|
||
|
DebugStub.WriteLine("Tpm: ReadBytes() no more data avaliable, ={0:d4}", __arglist(numBytes));
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
if (numBytes <= 0)
|
||
|
return true;
|
||
|
|
||
|
|
||
|
buffer[offset] = tpmIoMemory.Read8(locality + TpmRegisterOffsets.TpmDataFifo);
|
||
|
offset++;
|
||
|
numBytes--;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ushort burst = 0;
|
||
|
|
||
|
while (numBytes > 0) {
|
||
|
//get next burst
|
||
|
|
||
|
burst = GetBurst();
|
||
|
|
||
|
if (burst == 0) {
|
||
|
DebugStub.WriteLine("Tpm: ReadBytes() timed out waiting for burst");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
while (burst > 0) {
|
||
|
burst--;
|
||
|
if (numBytes == 1) {
|
||
|
TpmStatus = tpmIoMemory.Read8(locality + TpmRegisterOffsets.TpmStatus);
|
||
|
if ((TpmStatus & TpmRegStatusMasks.TpmStatusDataAvailable) == 0) {
|
||
|
DebugStub.WriteLine("Tpm: ReadBytes() no more data avaliable2");
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
buffer[offset] = tpmIoMemory.Read8(locality + TpmRegisterOffsets.TpmDataFifo);
|
||
|
offset++;
|
||
|
numBytes--;
|
||
|
|
||
|
if (numBytes == 0) {
|
||
|
burstRemainder = burst;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
}
|
||
|
return false;
|
||
|
|
||
|
}
|
||
|
|
||
|
bool ReceiveAttempt(out byte[] response)
|
||
|
{
|
||
|
bool Ready = false;
|
||
|
bool DataAvailable = false;
|
||
|
bool Status = true;
|
||
|
response = null;
|
||
|
byte TpmStatus;
|
||
|
|
||
|
DebugStub.WriteLine("Tpm: ReceiveAttempt() called");
|
||
|
|
||
|
for (int i = 0; i < Retries.MaxDataRetries; i++) {
|
||
|
if (WaitOnBit(locality + TpmRegisterOffsets.TpmStatus,
|
||
|
TpmRegStatusMasks.TpmStatusValid,
|
||
|
Retries.MaxDataRetries))
|
||
|
{
|
||
|
Ready = true;
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!Ready) {
|
||
|
DebugStub.WriteLine("Tpm: ReceiveAttempt() device not ready");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
for (int i = 0; i < Retries.MaxDataRetries; i++) {
|
||
|
|
||
|
TpmStatus = tpmIoMemory.Read8(locality + TpmRegisterOffsets.TpmStatus);
|
||
|
|
||
|
if ((TpmStatus & TpmRegStatusMasks.TpmStatusDataAvailable) > 0) {
|
||
|
DataAvailable = true;
|
||
|
break;
|
||
|
|
||
|
}
|
||
|
Thread.Sleep(1);
|
||
|
}
|
||
|
|
||
|
if (!DataAvailable) {
|
||
|
DebugStub.WriteLine("Tpm: ReceiveAttempt(), data not available");
|
||
|
return false;
|
||
|
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// read enough bytes to determine the length
|
||
|
//
|
||
|
|
||
|
ushort burstRemainder;
|
||
|
|
||
|
Status = ReadBytes(minResponse, 0, minResponseLen, 0, out burstRemainder);
|
||
|
|
||
|
if (!Status) {
|
||
|
DebugStub.WriteLine("Tpm: ReceiveAttempt(), ReadBytes failed");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
int dataLen = 0;
|
||
|
|
||
|
//
|
||
|
// acquire data length
|
||
|
//
|
||
|
|
||
|
dataLen = (minResponse[dataLenOffset] << 24) | (minResponse[dataLenOffset + 1] << 16) |
|
||
|
(minResponse[dataLenOffset + 2] << 8) | (minResponse[dataLenOffset + 3]);
|
||
|
|
||
|
DebugStub.WriteLine("Tpm: ReceiveAttempt() data length={0:d4}", __arglist(dataLen));
|
||
|
|
||
|
response = new byte[dataLen];
|
||
|
|
||
|
for (int i = 0; i < minResponseLen; i++) {
|
||
|
response[i] = minResponse[i];
|
||
|
|
||
|
}
|
||
|
|
||
|
ushort nextBurstRem;
|
||
|
|
||
|
|
||
|
Status = ReadBytes(response, minResponseLen, dataLen - minResponseLen, burstRemainder, out nextBurstRem);
|
||
|
|
||
|
if (!Status) {
|
||
|
DebugStub.WriteLine("Tpm: ReceiveAttempt(), ReadBytes failed2");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
|
||
|
if (!WaitOnBit(locality + TpmRegisterOffsets.TpmStatus,
|
||
|
TpmRegStatusMasks.TpmStatusValid,
|
||
|
Retries.MaxDataRetries))
|
||
|
{
|
||
|
DebugStub.WriteLine("Tpm: ReceiveAttempt(), valid bit not set after done reading");
|
||
|
//PrintRegisters();
|
||
|
return false;
|
||
|
|
||
|
}
|
||
|
|
||
|
return Status;
|
||
|
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
bool Receive(out byte[] response)
|
||
|
{
|
||
|
bool Status;
|
||
|
response = null;
|
||
|
|
||
|
for (int i = 0; i < Retries.MaxDataRetries; i++) {
|
||
|
Status = ReceiveAttempt(out response);
|
||
|
if (Status)
|
||
|
return Status;
|
||
|
|
||
|
byte TpmStatus = tpmIoMemory.Read8(locality + TpmRegisterOffsets.TpmStatus);
|
||
|
TpmStatus |= TpmRegStatusMasks.TpmStatusRetry;
|
||
|
tpmIoMemory.Write8(locality + TpmRegisterOffsets.TpmStatus, TpmStatus);
|
||
|
Thread.Sleep(10);
|
||
|
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
internal bool SendReceive(byte[] request, out byte[] response)
|
||
|
{
|
||
|
bool Status;
|
||
|
|
||
|
response = null;
|
||
|
|
||
|
Status = Send(request);
|
||
|
if (!Status)
|
||
|
return Status;
|
||
|
|
||
|
Status = Receive(out response);
|
||
|
|
||
|
if (Status) {
|
||
|
DebugStub.WriteLine("Tpm: SendReceive(), success");
|
||
|
|
||
|
}
|
||
|
|
||
|
return Status;
|
||
|
|
||
|
}
|
||
|
|
||
|
// Device methods
|
||
|
public void Initialize()
|
||
|
{
|
||
|
|
||
|
}
|
||
|
//
|
||
|
// ///////////////////////////////////////////////////////////////////////
|
||
|
// //
|
||
|
// // Register accessors / modifiers / utilities
|
||
|
// //
|
||
|
//
|
||
|
// private uint Read(uint offset)
|
||
|
// {
|
||
|
// return ioMemory.Read32((int) offset);
|
||
|
// }
|
||
|
//
|
||
|
// private void Write(uint offset, uint value)
|
||
|
// {
|
||
|
// ioMemory.Write32((int) offset, value);
|
||
|
// }
|
||
|
//
|
||
|
// private void SetBits(int offset, uint bits)
|
||
|
// {
|
||
|
// ioMemory.Write32(offset, ioMemory.Read32(offset) | bits);
|
||
|
// }
|
||
|
//
|
||
|
// private void ClearBits(int offset, uint bits)
|
||
|
// {
|
||
|
// ioMemory.Write32(offset, ioMemory.Read32(offset) & ~bits);
|
||
|
// }
|
||
|
//
|
||
|
|
||
|
public void ReleaseResources()
|
||
|
{
|
||
|
Tracing.Log(Tracing.Debug, "Tpm: Finalize() called\n");
|
||
|
}
|
||
|
|
||
|
// how do we pass a string back?
|
||
|
|
||
|
public string Name
|
||
|
{
|
||
|
get { return "TPM 1.2 compliant device"; }
|
||
|
}
|
||
|
|
||
|
public string Version
|
||
|
{
|
||
|
get { return "0.1"; }
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|