/** @file
|
EDKII Device Security library for PCI device.
|
It follows the Intel PCIe Security Specification.
|
|
Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
**/
|
|
#include <Uefi.h>
|
#include <IndustryStandard/Spdm.h>
|
#include <IndustryStandard/IntelPciSecurity.h>
|
#include <IndustryStandard/Pci.h>
|
#include <IndustryStandard/Tpm20.h>
|
#include <IndustryStandard/UefiTcgPlatform.h>
|
#include <Library/BaseLib.h>
|
#include <Library/DebugLib.h>
|
#include <Library/BaseMemoryLib.h>
|
#include <Library/UefiBootServicesTableLib.h>
|
#include <Library/MemoryAllocationLib.h>
|
#include <Library/TpmMeasurementLib.h>
|
#include <Protocol/PciIo.h>
|
#include <Protocol/DeviceSecurity.h>
|
#include <Protocol/PlatformDeviceSecurityPolicy.h>
|
|
typedef struct {
|
TCG_DEVICE_SECURITY_EVENT_DATA_HEADER EventData;
|
SPDM_MEASUREMENT_BLOCK_COMMON_HEADER CommonHeader;
|
SPDM_MEASUREMENT_BLOCK_DMTF_HEADER DmtfHeader;
|
UINT8 Digest[SHA256_DIGEST_SIZE];
|
TCG_DEVICE_SECURITY_EVENT_DATA_PCI_CONTEXT PciContext;
|
} EDKII_DEVICE_SECURITY_PCI_EVENT_DATA;
|
|
typedef struct {
|
UINTN Signature;
|
LIST_ENTRY Link;
|
UINTN PciSegment;
|
UINTN PciBus;
|
UINTN PciDevice;
|
UINTN PciFunction;
|
} PCI_DEVICE_INSTANCE;
|
|
#define PCI_DEVICE_INSTANCE_SIGNATURE SIGNATURE_32 ('P', 'D', 'I', 'S')
|
#define PCI_DEVICE_INSTANCE_FROM_LINK(a) CR (a, PCI_DEVICE_INSTANCE, Link, PCI_DEVICE_INSTANCE_SIGNATURE)
|
|
LIST_ENTRY mSecurityEventMeasurementDeviceList = INITIALIZE_LIST_HEAD_VARIABLE(mSecurityEventMeasurementDeviceList);;
|
EDKII_DEVICE_SECURITY_POLICY_PROTOCOL *mDeviceSecurityPolicy;
|
|
/**
|
Record a PCI device into device list.
|
|
@param PciIo PciIo instance of the device
|
@param PciDeviceList The list to record the the device
|
**/
|
VOID
|
RecordPciDeviceInList(
|
IN EFI_PCI_IO_PROTOCOL *PciIo,
|
IN LIST_ENTRY *PciDeviceList
|
)
|
{
|
UINTN PciSegment;
|
UINTN PciBus;
|
UINTN PciDevice;
|
UINTN PciFunction;
|
EFI_STATUS Status;
|
PCI_DEVICE_INSTANCE *NewPciDevice;
|
|
Status = PciIo->GetLocation (PciIo, &PciSegment, &PciBus, &PciDevice, &PciFunction);
|
ASSERT_EFI_ERROR(Status);
|
|
NewPciDevice = AllocateZeroPool(sizeof(*NewPciDevice));
|
ASSERT(NewPciDevice != NULL);
|
|
NewPciDevice->Signature = PCI_DEVICE_INSTANCE_SIGNATURE;
|
NewPciDevice->PciSegment = PciSegment;
|
NewPciDevice->PciBus = PciBus;
|
NewPciDevice->PciDevice = PciDevice;
|
NewPciDevice->PciFunction = PciFunction;
|
|
InsertTailList(PciDeviceList, &NewPciDevice->Link);
|
}
|
|
/**
|
Check if a PCI device is recorded in device list.
|
|
@param PciIo PciIo instance of the device
|
@param PciDeviceList The list to record the the device
|
|
@retval TRUE The PCI device is in the list.
|
@retval FALSE The PCI device is NOT in the list.
|
**/
|
BOOLEAN
|
IsPciDeviceInList(
|
IN EFI_PCI_IO_PROTOCOL *PciIo,
|
IN LIST_ENTRY *PciDeviceList
|
)
|
{
|
UINTN PciSegment;
|
UINTN PciBus;
|
UINTN PciDevice;
|
UINTN PciFunction;
|
EFI_STATUS Status;
|
LIST_ENTRY *Link;
|
PCI_DEVICE_INSTANCE *CurrentPciDevice;
|
|
Status = PciIo->GetLocation (PciIo, &PciSegment, &PciBus, &PciDevice, &PciFunction);
|
ASSERT_EFI_ERROR(Status);
|
|
Link = GetFirstNode(PciDeviceList);
|
while (!IsNull(PciDeviceList, Link)) {
|
CurrentPciDevice = PCI_DEVICE_INSTANCE_FROM_LINK(Link);
|
|
if (CurrentPciDevice->PciSegment == PciSegment && CurrentPciDevice->PciBus == PciBus &&
|
CurrentPciDevice->PciDevice == PciDevice && CurrentPciDevice->PciFunction == PciFunction) {
|
DEBUG((DEBUG_INFO, "PCI device duplicated (Loc - %04x:%02x:%02x:%02x)\n", PciSegment, PciBus, PciDevice, PciFunction));
|
return TRUE;
|
}
|
|
Link = GetNextNode(PciDeviceList, Link);
|
}
|
|
return FALSE;
|
}
|
|
/*
|
return Offset of the PCI Cap ID.
|
|
@param PciIo PciIo instance of the device
|
@param CapId The Capability ID of the Pci device
|
|
@return The PCI Capability ID Offset
|
*/
|
UINT32
|
GetPciCapId (
|
IN EFI_PCI_IO_PROTOCOL *PciIo,
|
IN UINT8 CapId
|
)
|
{
|
EFI_PCI_CAPABILITY_HDR PciCapIdHdr;
|
UINT32 PciCapIdOffset;
|
EFI_STATUS Status;
|
|
PciCapIdHdr.CapabilityID = ~CapId;
|
Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint8, PCI_CAPBILITY_POINTER_OFFSET, 1, &PciCapIdHdr.NextItemPtr);
|
ASSERT_EFI_ERROR(Status);
|
if (PciCapIdHdr.NextItemPtr == 0 || PciCapIdHdr.NextItemPtr == 0xFF) {
|
return 0;
|
}
|
PciCapIdOffset = 0;
|
do {
|
if (PciCapIdHdr.CapabilityID == CapId) {
|
break;
|
}
|
PciCapIdOffset = PciCapIdHdr.NextItemPtr;
|
Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint16, PciCapIdOffset, 1, &PciCapIdHdr);
|
ASSERT_EFI_ERROR(Status);
|
} while (PciCapIdHdr.NextItemPtr != 0 && PciCapIdHdr.NextItemPtr != 0xFF);
|
|
if (PciCapIdHdr.CapabilityID == CapId) {
|
return PciCapIdOffset;
|
} else {
|
return 0;
|
}
|
}
|
|
/*
|
return Offset of the PCIe Ext Cap ID.
|
|
@param PciIo PciIo instance of the device
|
@param CapId The Ext Capability ID of the Pci device
|
|
@return The PCIe Ext Capability ID Offset
|
*/
|
UINT32
|
GetPciExpressExtCapId (
|
IN EFI_PCI_IO_PROTOCOL *PciIo,
|
IN UINT16 CapId
|
)
|
{
|
UINT32 PcieCapIdOffset;
|
PCI_EXPRESS_EXTENDED_CAPABILITIES_HEADER PciExpressExtCapIdHdr;
|
EFI_STATUS Status;
|
|
PcieCapIdOffset = GetPciCapId (PciIo, EFI_PCI_CAPABILITY_ID_PCIEXP);
|
if (PcieCapIdOffset == 0) {
|
return 0;
|
}
|
|
PciExpressExtCapIdHdr.CapabilityId = ~CapId;
|
PciExpressExtCapIdHdr.CapabilityVersion = 0xF;
|
PciExpressExtCapIdHdr.NextCapabilityOffset = 0x100;
|
PcieCapIdOffset = 0;
|
do {
|
if (PciExpressExtCapIdHdr.CapabilityId == CapId) {
|
break;
|
}
|
PcieCapIdOffset = PciExpressExtCapIdHdr.NextCapabilityOffset;
|
Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, PcieCapIdOffset, 1, &PciExpressExtCapIdHdr);
|
ASSERT_EFI_ERROR(Status);
|
} while (PciExpressExtCapIdHdr.NextCapabilityOffset != 0 && PciExpressExtCapIdHdr.NextCapabilityOffset != 0xFFF);
|
|
if (PciExpressExtCapIdHdr.CapabilityId == CapId) {
|
return PcieCapIdOffset;
|
} else {
|
return 0;
|
}
|
}
|
|
/**
|
Read byte of the PCI device configuration space.
|
|
@param PciIo PciIo instance of the device
|
@param Offset The offset of the Pci device configuration space
|
|
@return Byte value of the PCI device configuration space.
|
**/
|
UINT8
|
DvSecPciRead8 (
|
IN EFI_PCI_IO_PROTOCOL *PciIo,
|
IN UINT32 Offset
|
)
|
{
|
EFI_STATUS Status;
|
UINT8 Data;
|
|
Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint8, Offset, 1, &Data);
|
ASSERT_EFI_ERROR(Status);
|
|
return Data;
|
}
|
|
/**
|
Write byte of the PCI device configuration space.
|
|
@param PciIo PciIo instance of the device
|
@param Offset The offset of the Pci device configuration space
|
@param Data Byte value of the PCI device configuration space.
|
**/
|
VOID
|
DvSecPciWrite8 (
|
IN EFI_PCI_IO_PROTOCOL *PciIo,
|
IN UINT32 Offset,
|
IN UINT8 Data
|
)
|
{
|
EFI_STATUS Status;
|
|
Status = PciIo->Pci.Write (PciIo, EfiPciIoWidthUint8, Offset, 1, &Data);
|
ASSERT_EFI_ERROR(Status);
|
}
|
|
/**
|
Get the Digest size from the TCG hash Algorithm ID.
|
|
@param TcgAlgId TCG hash Algorithm ID
|
|
@return Digest size of the TCG hash Algorithm ID
|
**/
|
UINTN
|
DigestSizeFromTcgAlgId (
|
IN UINT16 TcgAlgId
|
)
|
{
|
switch (TcgAlgId) {
|
case TPM_ALG_SHA256:
|
return SHA256_DIGEST_SIZE;
|
case TPM_ALG_SHA384:
|
case TPM_ALG_SHA512:
|
case TPM_ALG_SM3_256:
|
default:
|
break;
|
}
|
return 0;
|
}
|
|
/**
|
Convert the SPDM hash algo ID from the TCG hash Algorithm ID.
|
|
@param TcgAlgId TCG hash Algorithm ID
|
|
@return SPDM hash algo ID
|
**/
|
UINT32
|
TcgAlgIdToSpdmHashAlgo (
|
IN UINT16 TcgAlgId
|
)
|
{
|
switch (TcgAlgId) {
|
case TPM_ALG_SHA256:
|
return SPDM_ALGORITHMS_BASE_HASH_ALGO_TPM_ALG_SHA_256;
|
case TPM_ALG_SHA384:
|
case TPM_ALG_SHA512:
|
case TPM_ALG_SM3_256:
|
default:
|
break;
|
}
|
return 0;
|
}
|
|
/**
|
This function extend the PCI digest from the DvSec register.
|
|
@param[in] PciIo The PciIo of the device.
|
@param[in] DeviceSecurityPolicy The Device Security Policy associated with the device.
|
@param[in] TcgAlgId TCG hash Algorithm ID
|
@param[in] DigestSel The digest selector
|
@param[in] Digest The digest buffer
|
@param[out] DeviceSecurityState The Device Security state associated with the device.
|
**/
|
VOID
|
ExtendDigestRegister (
|
IN EFI_PCI_IO_PROTOCOL *PciIo,
|
IN EDKII_DEVICE_SECURITY_POLICY *DeviceSecurityPolicy,
|
IN UINT16 TcgAlgId,
|
IN UINT8 DigestSel,
|
IN UINT8 *Digest,
|
OUT EDKII_DEVICE_SECURITY_STATE *DeviceSecurityState
|
)
|
{
|
UINT32 PcrIndex;
|
UINT32 EventType;
|
EDKII_DEVICE_SECURITY_PCI_EVENT_DATA EventLog;
|
EFI_STATUS Status;
|
PCI_TYPE00 PciData;
|
|
Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint8, 0, sizeof(PciData), &PciData);
|
ASSERT_EFI_ERROR(Status);
|
|
//
|
// Use PCR 2 for Firmware Blob code.
|
//
|
PcrIndex = 2;
|
EventType = EV_EFI_SPDM_FIRMWARE_BLOB;
|
|
CopyMem (EventLog.EventData.Signature, TCG_DEVICE_SECURITY_EVENT_DATA_SIGNATURE, sizeof(EventLog.EventData.Signature));
|
EventLog.EventData.Version = TCG_DEVICE_SECURITY_EVENT_DATA_VERSION;
|
EventLog.EventData.Length = sizeof(EDKII_DEVICE_SECURITY_PCI_EVENT_DATA);
|
EventLog.EventData.SpdmHashAlgo = TcgAlgIdToSpdmHashAlgo (TcgAlgId);
|
EventLog.EventData.DeviceType = TCG_DEVICE_SECURITY_EVENT_DATA_DEVICE_TYPE_PCI;
|
|
EventLog.CommonHeader.Index = DigestSel;
|
EventLog.CommonHeader.MeasurementSpecification = SPDM_MEASUREMENT_BLOCK_HEADER_SPECIFICATION_DMTF;
|
EventLog.CommonHeader.MeasurementSize = sizeof(SPDM_MEASUREMENT_BLOCK_DMTF_HEADER) + SHA256_DIGEST_SIZE;
|
EventLog.DmtfHeader.DMTFSpecMeasurementValueType = SPDM_MEASUREMENT_BLOCK_MEASUREMENT_TYPE_MUTABLE_FIRMWARE;
|
EventLog.DmtfHeader.DMTFSpecMeasurementValueSize = SHA256_DIGEST_SIZE;
|
CopyMem (&EventLog.Digest, Digest, SHA256_DIGEST_SIZE);
|
|
EventLog.PciContext.Version = TCG_DEVICE_SECURITY_EVENT_DATA_PCI_CONTEXT_VERSION;
|
EventLog.PciContext.Length = sizeof(TCG_DEVICE_SECURITY_EVENT_DATA_PCI_CONTEXT);
|
EventLog.PciContext.VendorId = PciData.Hdr.VendorId;
|
EventLog.PciContext.DeviceId = PciData.Hdr.DeviceId;
|
EventLog.PciContext.RevisionID = PciData.Hdr.RevisionID;
|
EventLog.PciContext.ClassCode[0] = PciData.Hdr.ClassCode[0];
|
EventLog.PciContext.ClassCode[1] = PciData.Hdr.ClassCode[1];
|
EventLog.PciContext.ClassCode[2] = PciData.Hdr.ClassCode[2];
|
if ((PciData.Hdr.HeaderType & HEADER_LAYOUT_CODE) == HEADER_TYPE_DEVICE) {
|
EventLog.PciContext.SubsystemVendorID = PciData.Device.SubsystemVendorID;
|
EventLog.PciContext.SubsystemID = PciData.Device.SubsystemID;
|
} else {
|
EventLog.PciContext.SubsystemVendorID = 0;
|
EventLog.PciContext.SubsystemID = 0;
|
}
|
|
Status = TpmMeasureAndLogData (
|
PcrIndex,
|
EventType,
|
&EventLog,
|
EventLog.EventData.Length,
|
Digest,
|
SHA256_DIGEST_SIZE
|
);
|
DEBUG((DEBUG_INFO, "TpmMeasureAndLogData - %r\n", Status));
|
if (EFI_ERROR(Status)) {
|
DeviceSecurityState->MeasurementState = EDKII_DEVICE_SECURITY_STATE_ERROR_TCG_EXTEND_TPM_PCR;
|
} else {
|
RecordPciDeviceInList (PciIo, &mSecurityEventMeasurementDeviceList);
|
}
|
}
|
|
/**
|
This function reads the PCI digest from the DvSec register and extend to TPM.
|
|
@param[in] PciIo The PciIo of the device.
|
@param[in] DvSecOffset The DvSec register offset of the device.
|
@param[in] DeviceSecurityPolicy The Device Security Policy associated with the device.
|
@param[out] DeviceSecurityState The Device Security state associated with the device.
|
**/
|
VOID
|
DoMeasurementsFromDigestRegister (
|
IN EFI_PCI_IO_PROTOCOL *PciIo,
|
IN UINT32 DvSecOffset,
|
IN EDKII_DEVICE_SECURITY_POLICY *DeviceSecurityPolicy,
|
OUT EDKII_DEVICE_SECURITY_STATE *DeviceSecurityState
|
)
|
{
|
UINT8 Modified;
|
UINT8 Valid;
|
UINT16 TcgAlgId;
|
UINT8 NumDigest;
|
UINT8 DigestSel;
|
UINT8 Digest[SHA256_DIGEST_SIZE];
|
UINTN DigestSize;
|
EFI_STATUS Status;
|
|
TcgAlgId = DvSecPciRead8 (
|
PciIo,
|
DvSecOffset + sizeof(INTEL_PCI_DIGEST_CAPABILITY_HEADER) + OFFSET_OF(INTEL_PCI_DIGEST_CAPABILITY_STRUCTURE, TcgAlgId)
|
);
|
DEBUG((DEBUG_INFO, " TcgAlgId - 0x%04x\n", TcgAlgId));
|
DigestSize = DigestSizeFromTcgAlgId (TcgAlgId);
|
if (DigestSize == 0) {
|
DEBUG((DEBUG_INFO, "Unsupported Algorithm - 0x%04x\n", TcgAlgId));
|
DeviceSecurityState->MeasurementState = EDKII_DEVICE_SECURITY_STATE_ERROR_PCI_NO_CAPABILITIES;
|
return ;
|
}
|
DEBUG((DEBUG_INFO, " (DigestSize: 0x%x)\n", DigestSize));
|
|
DeviceSecurityState->MeasurementState = EDKII_DEVICE_SECURITY_STATE_SUCCESS;
|
|
NumDigest = DvSecPciRead8 (
|
PciIo,
|
DvSecOffset + sizeof(INTEL_PCI_DIGEST_CAPABILITY_HEADER) + OFFSET_OF(INTEL_PCI_DIGEST_CAPABILITY_STRUCTURE, FirmwareID)
|
);
|
DEBUG((DEBUG_INFO, " NumDigest - 0x%02x\n", NumDigest));
|
|
Valid = DvSecPciRead8 (
|
PciIo,
|
DvSecOffset + sizeof(INTEL_PCI_DIGEST_CAPABILITY_HEADER) + OFFSET_OF(INTEL_PCI_DIGEST_CAPABILITY_STRUCTURE, Valid)
|
);
|
DEBUG((DEBUG_INFO, " Valid - 0x%02x\n", Valid));
|
|
//
|
// Only 2 are supported as maximum.
|
// But hardware may report 3.
|
//
|
if (NumDigest > 2) {
|
NumDigest = 2;
|
}
|
|
for (DigestSel = 0; DigestSel < NumDigest; DigestSel++) {
|
DEBUG((DEBUG_INFO, " DigestSel - 0x%02x\n", DigestSel));
|
if ((DigestSel == 0) && ((Valid & INTEL_PCI_DIGEST_0_VALID) == 0)) {
|
continue;
|
}
|
if ((DigestSel == 1) && ((Valid & INTEL_PCI_DIGEST_1_VALID) == 0)) {
|
continue;
|
}
|
while (TRUE) {
|
//
|
// Host MUST clear DIGEST_MODIFIED before read DIGEST.
|
//
|
DvSecPciWrite8 (
|
PciIo,
|
DvSecOffset + sizeof(INTEL_PCI_DIGEST_CAPABILITY_HEADER) + OFFSET_OF(INTEL_PCI_DIGEST_CAPABILITY_STRUCTURE, Modified),
|
INTEL_PCI_DIGEST_MODIFIED
|
);
|
|
Status = PciIo->Pci.Read (
|
PciIo,
|
EfiPciIoWidthUint8,
|
(UINT32)(DvSecOffset + sizeof(INTEL_PCI_DIGEST_CAPABILITY_HEADER) + sizeof(INTEL_PCI_DIGEST_CAPABILITY_STRUCTURE) + DigestSize * DigestSel),
|
DigestSize,
|
Digest
|
);
|
ASSERT_EFI_ERROR(Status);
|
|
//
|
// After read DIGEST, Host MUST consult DIGEST_MODIFIED.
|
//
|
Modified = DvSecPciRead8 (
|
PciIo,
|
DvSecOffset + sizeof(INTEL_PCI_DIGEST_CAPABILITY_HEADER) + OFFSET_OF(INTEL_PCI_DIGEST_CAPABILITY_STRUCTURE, Modified)
|
);
|
if ((Modified & INTEL_PCI_DIGEST_MODIFIED) == 0) {
|
break;
|
}
|
}
|
|
//
|
// Dump Digest
|
//
|
{
|
UINTN Index;
|
DEBUG((DEBUG_INFO, " Digest - "));
|
for (Index = 0; Index < DigestSize; Index++) {
|
DEBUG((DEBUG_INFO, "%02x", *(Digest + Index)));
|
}
|
DEBUG((DEBUG_INFO, "\n"));
|
}
|
|
DEBUG((DEBUG_INFO, "ExtendDigestRegister...\n", ExtendDigestRegister));
|
ExtendDigestRegister (PciIo, DeviceSecurityPolicy, TcgAlgId, DigestSel, Digest, DeviceSecurityState);
|
}
|
}
|
|
/**
|
The device driver uses this service to measure a PCI device.
|
|
@param[in] PciIo The PciIo of the device.
|
@param[in] DeviceSecurityPolicy The Device Security Policy associated with the device.
|
@param[out] DeviceSecurityState The Device Security state associated with the device.
|
**/
|
VOID
|
DoDeviceMeasurement (
|
IN EFI_PCI_IO_PROTOCOL *PciIo,
|
IN EDKII_DEVICE_SECURITY_POLICY *DeviceSecurityPolicy,
|
OUT EDKII_DEVICE_SECURITY_STATE *DeviceSecurityState
|
)
|
{
|
UINT32 DvSecOffset;
|
INTEL_PCI_DIGEST_CAPABILITY_HEADER DvSecHdr;
|
EFI_STATUS Status;
|
|
if (IsPciDeviceInList (PciIo, &mSecurityEventMeasurementDeviceList)) {
|
DeviceSecurityState->MeasurementState = EDKII_DEVICE_SECURITY_STATE_SUCCESS;
|
return ;
|
}
|
|
DvSecOffset = GetPciExpressExtCapId (PciIo, INTEL_PCI_CAPID_DVSEC);
|
DEBUG((DEBUG_INFO, "DvSec Capability - 0x%x\n", DvSecOffset));
|
if (DvSecOffset == 0) {
|
DeviceSecurityState->MeasurementState = EDKII_DEVICE_SECURITY_STATE_ERROR_PCI_NO_CAPABILITIES;
|
return ;
|
}
|
Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint16, DvSecOffset, sizeof(DvSecHdr)/sizeof(UINT16), &DvSecHdr);
|
ASSERT_EFI_ERROR(Status);
|
DEBUG((DEBUG_INFO, " CapId - 0x%04x\n", DvSecHdr.CapId));
|
DEBUG((DEBUG_INFO, " CapVersion - 0x%01x\n", DvSecHdr.CapVersion));
|
DEBUG((DEBUG_INFO, " NextOffset - 0x%03x\n", DvSecHdr.NextOffset));
|
DEBUG((DEBUG_INFO, " DvSecVendorId - 0x%04x\n", DvSecHdr.DvSecVendorId));
|
DEBUG((DEBUG_INFO, " DvSecRevision - 0x%01x\n", DvSecHdr.DvSecRevision));
|
DEBUG((DEBUG_INFO, " DvSecLength - 0x%03x\n", DvSecHdr.DvSecLength));
|
DEBUG((DEBUG_INFO, " DvSecId - 0x%04x\n", DvSecHdr.DvSecId));
|
if ((DvSecHdr.DvSecVendorId != INTEL_PCI_DVSEC_VENDORID_INTEL) &&
|
(DvSecHdr.DvSecId != INTEL_PCI_DVSEC_DVSECID_MEASUREMENT)) {
|
DeviceSecurityState->MeasurementState = EDKII_DEVICE_SECURITY_STATE_ERROR_PCI_NO_CAPABILITIES;
|
return ;
|
}
|
|
DoMeasurementsFromDigestRegister (PciIo, DvSecOffset, DeviceSecurityPolicy, DeviceSecurityState);
|
}
|
|
/**
|
The device driver uses this service to verify a PCI device.
|
|
@param[in] PciIo The PciIo of the device.
|
@param[in] DeviceSecurityPolicy The Device Security Policy associated with the device.
|
@param[out] DeviceSecurityState The Device Security state associated with the device.
|
**/
|
VOID
|
DoDeviceAuthentication (
|
IN EFI_PCI_IO_PROTOCOL *PciIo,
|
IN EDKII_DEVICE_SECURITY_POLICY *DeviceSecurityPolicy,
|
OUT EDKII_DEVICE_SECURITY_STATE *DeviceSecurityState
|
)
|
{
|
DeviceSecurityState->AuthenticationState = EDKII_DEVICE_SECURITY_STATE_ERROR_UEFI_UNSUPPORTED;
|
}
|
|
/**
|
The device driver uses this service to measure and/or verify a device.
|
|
The flow in device driver is:
|
1) Device driver discovers a new device.
|
2) Device driver creates an EFI_DEVICE_PATH_PROTOCOL.
|
3) Device driver creates a device access protocol. e.g.
|
EFI_PCI_IO_PROTOCOL for PCI device.
|
EFI_USB_IO_PROTOCOL for USB device.
|
EFI_EXT_SCSI_PASS_THRU_PROTOCOL for SCSI device.
|
EFI_ATA_PASS_THRU_PROTOCOL for ATA device.
|
EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL for NVMe device.
|
EFI_SD_MMC_PASS_THRU_PROTOCOL for SD/MMC device.
|
4) Device driver installs the EFI_DEVICE_PATH_PROTOCOL with EFI_DEVICE_PATH_PROTOCOL_GUID,
|
and the device access protocol with EDKII_DEVICE_IDENTIFIER_TYPE_xxx_GUID.
|
Once it is done, a DeviceHandle is returned.
|
5) Device driver creates EDKII_DEVICE_IDENTIFIER with EDKII_DEVICE_IDENTIFIER_TYPE_xxx_GUID
|
and the DeviceHandle.
|
6) Device driver calls DeviceAuthenticate().
|
7) If DeviceAuthenticate() returns EFI_SECURITY_VIOLATION, the device driver uninstalls
|
all protocols on this handle.
|
8) If DeviceAuthenticate() returns EFI_SUCCESS, the device driver installs the device access
|
protocol with a real protocol GUID. e.g.
|
EFI_PCI_IO_PROTOCOL with EFI_PCI_IO_PROTOCOL_GUID.
|
EFI_USB_IO_PROTOCOL with EFI_USB_IO_PROTOCOL_GUID.
|
|
@param[in] This The protocol instance pointer.
|
@param[in] DeviceId The Identifier for the device.
|
|
@retval EFI_SUCCESS The device specified by the DeviceId passed the measurement
|
and/or authentication based upon the platform policy.
|
If TCG measurement is required, the measurement is extended to TPM PCR.
|
@retval EFI_SECURITY_VIOLATION The device fails to return the measurement data.
|
@retval EFI_SECURITY_VIOLATION The device fails to response the authentication request.
|
@retval EFI_SECURITY_VIOLATION The system fails to verify the device based upon the authentication response.
|
@retval EFI_SECURITY_VIOLATION The system fails to extend the measurement to TPM PCR.
|
**/
|
EFI_STATUS
|
EFIAPI
|
DeviceAuthentication (
|
IN EDKII_DEVICE_SECURITY_PROTOCOL *This,
|
IN EDKII_DEVICE_IDENTIFIER *DeviceId
|
)
|
{
|
EDKII_DEVICE_SECURITY_POLICY DeviceSecurityPolicy;
|
EDKII_DEVICE_SECURITY_STATE DeviceSecurityState;
|
EFI_PCI_IO_PROTOCOL *PciIo;
|
EFI_STATUS Status;
|
|
if (mDeviceSecurityPolicy == NULL) {
|
return EFI_SUCCESS;
|
}
|
|
if (!CompareGuid (&DeviceId->DeviceType, &gEdkiiDeviceIdentifierTypePciGuid)) {
|
return EFI_SUCCESS;
|
}
|
|
Status = gBS->HandleProtocol (
|
DeviceId->DeviceHandle,
|
&gEdkiiDeviceIdentifierTypePciGuid,
|
(VOID **)&PciIo
|
);
|
if (EFI_ERROR(Status)) {
|
DEBUG ((DEBUG_ERROR, "Locate - DeviceIdentifierTypePci - %r\n", Status));
|
return EFI_SUCCESS;
|
}
|
|
DeviceSecurityState.Revision = EDKII_DEVICE_SECURITY_STATE_REVISION;
|
DeviceSecurityState.MeasurementState = 0x0;
|
DeviceSecurityState.AuthenticationState = 0x0;
|
|
Status = mDeviceSecurityPolicy->GetDevicePolicy (mDeviceSecurityPolicy, DeviceId, &DeviceSecurityPolicy);
|
if (EFI_ERROR(Status)) {
|
DEBUG((DEBUG_ERROR, "mDeviceSecurityPolicy->GetDevicePolicy - %r\n", Status));
|
DeviceSecurityState.MeasurementState = EDKII_DEVICE_SECURITY_STATE_ERROR_UEFI_GET_POLICY_PROTOCOL;
|
DeviceSecurityState.AuthenticationState = EDKII_DEVICE_SECURITY_STATE_ERROR_UEFI_GET_POLICY_PROTOCOL;
|
} else {
|
if ((DeviceSecurityPolicy.MeasurementPolicy & EDKII_DEVICE_MEASUREMENT_REQUIRED) != 0) {
|
DoDeviceMeasurement (PciIo, &DeviceSecurityPolicy, &DeviceSecurityState);
|
DEBUG((DEBUG_ERROR, "MeasurementState - 0x%08x\n", DeviceSecurityState.MeasurementState));
|
}
|
if ((DeviceSecurityPolicy.AuthenticationPolicy & EDKII_DEVICE_AUTHENTICATION_REQUIRED) != 0) {
|
DoDeviceAuthentication (PciIo, &DeviceSecurityPolicy, &DeviceSecurityState);
|
DEBUG((DEBUG_ERROR, "AuthenticationState - 0x%08x\n", DeviceSecurityState.AuthenticationState));
|
}
|
}
|
|
Status = mDeviceSecurityPolicy->NotifyDeviceState (mDeviceSecurityPolicy, DeviceId, &DeviceSecurityState);
|
if (EFI_ERROR(Status)) {
|
DEBUG((DEBUG_ERROR, "mDeviceSecurityPolicy->NotifyDeviceState - %r\n", Status));
|
}
|
|
if ((DeviceSecurityState.MeasurementState == 0) &&
|
(DeviceSecurityState.AuthenticationState == 0)) {
|
return EFI_SUCCESS;
|
} else {
|
return EFI_SECURITY_VIOLATION;
|
}
|
}
|
|
EDKII_DEVICE_SECURITY_PROTOCOL mDeviceSecurity = {
|
EDKII_DEVICE_SECURITY_PROTOCOL_REVISION,
|
DeviceAuthentication
|
};
|
|
/**
|
Entrypoint of the device security driver.
|
|
@param[in] ImageHandle ImageHandle of the loaded driver
|
@param[in] SystemTable Pointer to the System Table
|
|
@retval EFI_SUCCESS The Protocol is installed.
|
@retval EFI_OUT_OF_RESOURCES Not enough resources available to initialize driver.
|
@retval EFI_DEVICE_ERROR A device error occurred attempting to initialize the driver.
|
|
**/
|
EFI_STATUS
|
EFIAPI
|
MainEntryPoint (
|
IN EFI_HANDLE ImageHandle,
|
IN EFI_SYSTEM_TABLE *SystemTable
|
)
|
{
|
EFI_HANDLE Handle;
|
EFI_STATUS Status;
|
|
Status = gBS->LocateProtocol (&gEdkiiDeviceSecurityPolicyProtocolGuid, NULL, (VOID **)&mDeviceSecurityPolicy);
|
ASSERT_EFI_ERROR(Status);
|
|
Handle = NULL;
|
Status = gBS->InstallProtocolInterface (
|
&Handle,
|
&gEdkiiDeviceSecurityProtocolGuid,
|
EFI_NATIVE_INTERFACE,
|
(VOID **)&mDeviceSecurity
|
);
|
ASSERT_EFI_ERROR(Status);
|
|
return Status;
|
}
|