MdeModulePkg/Bus/Pci/NvmExpressDxe: Nvm Express Media Sanitize Protocol.

Implementation of MEDIA_SANITIZE_PROTOCOL for NIST
purge/clear actions with mapping to NVM Express native
commands.

Signed-off-by: Aaron Pop <aaronpop@microsoft.com>
This commit is contained in:
Ray Robles 2024-07-18 10:26:05 -07:00 committed by mergify[bot]
parent 7801fe428b
commit b6c4708c4d
11 changed files with 2188 additions and 1 deletions

View File

@ -3,6 +3,7 @@
NVM Express specification.
Copyright (c) 2013 - 2017, Intel Corporation. All rights reserved.<BR>
Copyright (c) Microsoft Corporation.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
@ -181,6 +182,26 @@ EnumerateNvmeDevNamespace (
Device->BlockIo2.FlushBlocksEx = NvmeBlockIoFlushBlocksEx;
InitializeListHead (&Device->AsyncQueue);
//
// Create Media Sanitize Protocol instance
//
Device->MediaSanitize.Revision = MEDIA_SANITIZE_PROTOCOL_REVISION;
Device->MediaSanitize.Media = &Device->Media;
Device->MediaSanitize.MediaClear = NvmExpressMediaClear;
Device->MediaSanitize.MediaPurge = NvmExpressMediaPurge;
Device->MediaSanitize.MediaFormat = NvmExpressMediaFormat;
ASSERT (
sizeof (Device->MediaSanitize.SanitizeCapabilities) ==
sizeof (Device->Controller->ControllerData->Sanicap)
);
CopyMem (
&(Device->MediaSanitize.SanitizeCapabilities),
&(Device->Controller->ControllerData->Sanicap),
sizeof (Device->MediaSanitize.SanitizeCapabilities)
);
//
// Create StorageSecurityProtocol Instance
//
@ -241,6 +262,8 @@ EnumerateNvmeDevNamespace (
&Device->BlockIo2,
&gEfiDiskInfoProtocolGuid,
&Device->DiskInfo,
&gMediaSanitizeProtocolGuid,
&Device->MediaSanitize,
NULL
);
@ -269,6 +292,8 @@ EnumerateNvmeDevNamespace (
&Device->BlockIo2,
&gEfiDiskInfoProtocolGuid,
&Device->DiskInfo,
&gMediaSanitizeProtocolGuid,
&Device->MediaSanitize,
NULL
);
goto Exit;
@ -468,6 +493,8 @@ UnregisterNvmeNamespace (
&Device->BlockIo2,
&gEfiDiskInfoProtocolGuid,
&Device->DiskInfo,
&gMediaSanitizeProtocolGuid,
&Device->MediaSanitize,
NULL
);

View File

@ -4,6 +4,7 @@
(C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>
Copyright (c) 2013 - 2019, Intel Corporation. All rights reserved.<BR>
Copyright (c) Microsoft Corporation.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
@ -29,6 +30,7 @@
#include <Protocol/DriverSupportedEfiVersion.h>
#include <Protocol/StorageSecurityCommand.h>
#include <Protocol/ResetNotification.h>
#include <Protocol/MediaSanitize.h>
#include <Library/BaseLib.h>
#include <Library/BaseMemoryLib.h>
@ -49,6 +51,7 @@ typedef struct _NVME_DEVICE_PRIVATE_DATA NVME_DEVICE_PRIVATE_DATA;
#include "NvmExpressBlockIo.h"
#include "NvmExpressDiskInfo.h"
#include "NvmExpressHci.h"
#include "NvmExpressMediaSanitize.h"
extern EFI_DRIVER_BINDING_PROTOCOL gNvmExpressDriverBinding;
extern EFI_COMPONENT_NAME_PROTOCOL gNvmExpressComponentName;
@ -77,6 +80,30 @@ extern EFI_DRIVER_SUPPORTED_EFI_VERSION_PROTOCOL gNvmExpressDriverSupportedEfiV
#define NVME_MAX_QUEUES 3 // Number of queues supported by the driver
//
// FormatNVM Admin Command LBA Format (LBAF) Mask
//
#define NVME_LBA_FORMATNVM_LBAF_MASK 0xF
//
// NVMe Completion Queue Entry Bits, Fields, Masks
//
#define NVME_CQE_STATUS_FIELD_MASK 0xFFFF0000
#define NVME_CQE_STATUS_FIELD_OFFSET 16
#define NVME_CQE_STATUS_FIELD_SCT_MASK 0x0E00
#define NVME_CQE_STATUS_FIELD_SCT_OFFSET 0x9
#define NVME_CQE_STATUS_FIELD_SC_MASK 0x1FE
#define NVME_CQE_STATUS_FIELD_SC_OFFSET 0x01
#define NVME_CQE_SCT_GENERIC_CMD_STATUS 0x0
#define NVME_CQE_SCT_CMD_SPECIFIC_STATUS 0x1
#define NVME_CQE_SCT_MEDIA_DATA_INTEGRITY_ERRORS_STATUS 0x2
#define NVME_CQE_SCT_PATH_RELATED_STATUS 0x3
#define NVME_CQE_SC_SUCCESSFUL_COMPLETION 0x00
#define NVME_CQE_SC_INVALID_CMD_OPCODE 0x01
#define NVME_CQE_SC_INVALID_FIELD_IN_CMD 0x02
#define NVME_ALL_NAMESPACES 0xFFFFFFFF
#define NVME_CONTROLLER_ID 0
//
@ -202,6 +229,8 @@ struct _NVME_DEVICE_PRIVATE_DATA {
EFI_DISK_INFO_PROTOCOL DiskInfo;
EFI_STORAGE_SECURITY_COMMAND_PROTOCOL StorageSecurity;
MEDIA_SANITIZE_PROTOCOL MediaSanitize;
LIST_ENTRY AsyncQueue;
EFI_LBA NumBlocks;
@ -243,6 +272,13 @@ struct _NVME_DEVICE_PRIVATE_DATA {
NVME_DEVICE_PRIVATE_DATA_SIGNATURE \
)
#define NVME_DEVICE_PRIVATE_DATA_FROM_MEDIA_SANITIZE(a) \
CR (a, \
NVME_DEVICE_PRIVATE_DATA, \
MediaSanitize, \
NVME_DEVICE_PRIVATE_DATA_SIGNATURE \
)
//
// Nvme block I/O 2 request.
//

View File

@ -5,7 +5,7 @@
# NVM Express specification.
#
# Copyright (c) 2013 - 2019, Intel Corporation. All rights reserved.<BR>
#
# Copyright (c) Microsoft Corporation.<BR>
# SPDX-License-Identifier: BSD-2-Clause-Patent
#
##
@ -40,6 +40,8 @@
NvmExpressHci.c
NvmExpressHci.h
NvmExpressPassthru.c
NvmExpressMediaSanitize.c
NvmExpressMediaSanitize.h
[Guids]
gNVMeEnableStartEventGroupGuid
@ -72,6 +74,7 @@
gEfiDiskInfoProtocolGuid ## BY_START
gEfiStorageSecurityCommandProtocolGuid ## BY_START
gEfiDriverSupportedEfiVersionProtocolGuid ## PRODUCES
gMediaSanitizeProtocolGuid ## PRODUCES
gEfiResetNotificationProtocolGuid ## CONSUMES
# [Event]

View File

@ -0,0 +1,582 @@
/** @file -- NvmExpressMediaSanitize.c
This driver will implement sanitize operations on all NVMe mass storage devices
based on NIST purge and clear operations. These operations will then be mapped to
one of two NVMe admin commands:
-Format NVM
-Sanitize
Implementation based off NVMe spec revision 1.4c.
Copyright (c) Microsoft Corporation.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "NvmExpress.h"
/**
Send NVM Express FormatNVM Admin Command
The Format NVM command is used to low level format the NVM media. This command is used by
the host to change the LBA data size and/or metadata size.
A low level format may destroy all data and metadata associated with all namespaces or only
the specific namespace associated with the command (refer to the Format NVM Attributes field
in the Identify Controller data structure).
After the Format NVM command successfully completes, the controller shall not return any user
data that was previously contained in an affected namespace.
@param[in] This Indicates a pointer to the calling context (Block IO Protocol)
@param[in] NamespaceId The NVM Express namespace ID for which a device path node is to be
allocated and built. Caller must set the NamespaceId to zero if the
device path node will contain a valid UUID.
@param[in] Ses Secure Erase Setting (SES) value
- 000b: No secure erase operation requested
- 001b: User Data Erase
- 010b: Cryptographic Erase
- 011b to 111b: Reserved
@param[in] Flbas New LBA size (in terms of LBA Format size Index (bits 3:0) in NamespaceData).
If this param is 0 (NULL), then use existing LBA size.
@retval EFI_SUCCESS The device formatted correctly.
@retval EFI_WRITE_PROTECTED The device can not be formatted due to write protection.
@retval EFI_DEVICE_ERROR The device reported an error while performing the format.
@retval EFI_NO_MEDIA There is no media in the device.
@retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device.
@retval EFI_INVALID_PARAMETER The format request contains parameters that are not valid.
**/
EFI_STATUS
NvmExpressFormatNvm (
IN EFI_BLOCK_IO_PROTOCOL *This,
IN UINT32 NamespaceId,
IN UINT32 Ses,
IN UINT32 Flbas
)
{
NVME_DEVICE_PRIVATE_DATA *Device;
EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket;
EFI_NVM_EXPRESS_COMMAND Command;
EFI_NVM_EXPRESS_COMPLETION Completion;
NVME_ADMIN_FORMAT_NVM FormatNvmCdw10;
NVME_ADMIN_NAMESPACE_DATA *NewNamespaceData;
UINT32 Lbads;
UINT32 NewFlbas;
UINT32 LbaFmtIdx;
EFI_STATUS Status;
UINT32 LbaFormat;
UINT16 StatusField;
UINT16 Sct;
UINT16 Sc;
Status = EFI_NOT_STARTED;
LbaFormat = 0;
Device = NVME_DEVICE_PRIVATE_DATA_FROM_BLOCK_IO (This);
ZeroMem (&CommandPacket, sizeof (EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));
ZeroMem (&Command, sizeof (EFI_NVM_EXPRESS_COMMAND));
ZeroMem (&Completion, sizeof (EFI_NVM_EXPRESS_COMPLETION));
ZeroMem (&FormatNvmCdw10, sizeof (NVME_ADMIN_FORMAT_NVM));
NewNamespaceData = NULL;
Lbads = 0;
NewFlbas = 0;
LbaFmtIdx = 0;
StatusField = 0;
Sct = 0;
Sc = 0;
CommandPacket.NvmeCmd = &Command;
CommandPacket.NvmeCompletion = &Completion;
CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;
CommandPacket.QueueType = NVME_ADMIN_QUEUE;
Command.Cdw0.Opcode = NVME_ADMIN_FORMAT_NVM_CMD;
Command.Nsid = NamespaceId;
//
// SES (Secure Erase Settings)
//
FormatNvmCdw10.Ses = Ses;
//
// Change LBA size/format if LbaFormat param != NULL, otherwise keep same LBA format.
// Current supported LBA format size in Identify Namespace LBA Format Table, indexed by
// FLBAS (bits 3:0).
//
LbaFormat = (Flbas == 0 ? Device->NamespaceData.Flbas : Flbas);
FormatNvmCdw10.Lbaf = LbaFormat & NVME_LBA_FORMATNVM_LBAF_MASK;
CopyMem (&CommandPacket.NvmeCmd->Cdw10, &FormatNvmCdw10, sizeof (NVME_ADMIN_FORMAT_NVM));
//
// Send Format NVM command via passthru and wait for completion
//
// If LBA size changed successfully, then update private data structures and Block IO
// and Media protocols to reflect new LBA size.
//
Status = Device->Controller->Passthru.PassThru (
&(Device->Controller->Passthru),
NamespaceId,
&CommandPacket,
NULL
);
if (EFI_ERROR (Status)) {
StatusField = (UINT16)((CommandPacket.NvmeCompletion->DW3 & NVME_CQE_STATUS_FIELD_MASK) >>
NVME_CQE_STATUS_FIELD_OFFSET);
Sc = (StatusField & NVME_CQE_STATUS_FIELD_SC_MASK) >> NVME_CQE_STATUS_FIELD_SC_OFFSET;
Sct = (StatusField & NVME_CQE_STATUS_FIELD_SCT_MASK) >> NVME_CQE_STATUS_FIELD_SCT_OFFSET;
DEBUG ((DEBUG_ERROR, "%a: NVMe FormatNVM admin command failed SCT = 0x%x, SC = 0x%x\n", __func__, Sct, Sc));
} else {
//
// Update Block IO and Media Protocols only if Flbas parameter was not NULL.
// Call Identify Namespace again and update all protocols fields and local
// cached copies of fields related to block size.
//
if (Flbas != 0) {
NewNamespaceData = AllocateZeroPool (sizeof (NVME_ADMIN_NAMESPACE_DATA));
if (NewNamespaceData == NULL) {
Status = EFI_OUT_OF_RESOURCES;
} else {
Status = NvmeIdentifyNamespace (
Device->Controller,
NamespaceId,
(VOID *)NewNamespaceData
);
if (!EFI_ERROR (Status)) {
//
// Update all fields related to LBA size, allocation, and alignment
//
NewFlbas = NewNamespaceData->Flbas;
LbaFmtIdx = NewFlbas & NVME_LBA_FORMATNVM_LBAF_MASK;
Lbads = NewNamespaceData->LbaFormat[LbaFmtIdx].Lbads;
Device->Media.BlockSize = (UINT32)1 << Lbads;
Device->Media.LastBlock = NewNamespaceData->Nsze - 1;
CopyMem (&Device->NamespaceData, NewNamespaceData, sizeof (NVME_ADMIN_NAMESPACE_DATA));
}
}
}
}
return Status;
}
/**
Send NVM Express Sanitize Admin Command
The Sanitize command is used to start a sanitize operation or to recover from a previously
failed sanitize operation. The sanitize operation types that may be supported are Block
Erase, Crypto Erase, and Overwrite.
All sanitize operations are processed in the background (i.e., completion of the Sanitize
command does not indicate completion of the sanitize operation).
@param[in] This Indicates a pointer to the calling context (Block IO Protocol)
@param[in] NamespaceId The NVM Express namespace ID for which a device path node is to be
allocated and built. Caller must set the NamespaceId to zero if the
device path node will contain a valid UUID.
@param[in] SanitizeAction Sanitize action
@param[in] NoDeallocAfterSanitize No deallocate after sanitize option
@param[in] OverwritePattern Pattern to overwrite old user data
@retval EFI_SUCCESS The media was sanitized successfully on the device.
@retval EFI_WRITE_PROTECTED The device can not be sanitized due to write protection.
@retval EFI_DEVICE_ERROR The device reported an error while performing the sanitize.
@retval EFI_NO_MEDIA There is no media in the device.
@retval EFI_MEDIA_CHNAGED The MediaId does not match the current device.
@retval EFI_INVALID_PARAMETER The sanitize request contains parameters that are not valid.
**/
EFI_STATUS
NvmExpressSanitize (
IN EFI_BLOCK_IO_PROTOCOL *This,
IN UINT32 NamespaceId,
IN UINT32 SanitizeAction,
IN UINT32 NoDeallocAfterSanitize,
IN UINT32 OverwritePattern
)
{
NVME_DEVICE_PRIVATE_DATA *Device;
EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket;
EFI_NVM_EXPRESS_COMMAND Command;
EFI_NVM_EXPRESS_COMPLETION Completion;
NVME_ADMIN_SANITIZE SanitizeCdw10Cdw11;
EFI_STATUS Status;
UINT16 StatusField;
UINT16 Sct;
UINT16 Sc;
UINT32 FnvmSes;
Device = NVME_DEVICE_PRIVATE_DATA_FROM_BLOCK_IO (This);
ZeroMem (&CommandPacket, sizeof (EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));
ZeroMem (&Command, sizeof (EFI_NVM_EXPRESS_COMMAND));
ZeroMem (&Completion, sizeof (EFI_NVM_EXPRESS_COMPLETION));
ZeroMem (&SanitizeCdw10Cdw11, sizeof (NVME_ADMIN_SANITIZE));
StatusField = 0;
Sct = 0;
Sc = 0;
FnvmSes = 0;
CommandPacket.NvmeCmd = &Command;
CommandPacket.NvmeCompletion = &Completion;
CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;
CommandPacket.QueueType = NVME_ADMIN_QUEUE;
Command.Cdw0.Opcode = NVME_ADMIN_SANITIZE_CMD;
Command.Nsid = NamespaceId;
SanitizeCdw10Cdw11.Nodas = NoDeallocAfterSanitize;
SanitizeCdw10Cdw11.Sanact = SanitizeAction;
SanitizeCdw10Cdw11.Ovrpat = OverwritePattern;
CopyMem (&CommandPacket.NvmeCmd->Cdw10, &SanitizeCdw10Cdw11, sizeof (NVME_ADMIN_SANITIZE));
//
// Send Format NVM command via passthru and wait for completion
//
Status = Device->Controller->Passthru.PassThru (
&(Device->Controller->Passthru),
NamespaceId,
&CommandPacket,
NULL
);
if (EFI_ERROR (Status)) {
StatusField = (UINT16)((CommandPacket.NvmeCompletion->DW3 & NVME_CQE_STATUS_FIELD_MASK) >>
NVME_CQE_STATUS_FIELD_OFFSET);
Sc = (StatusField & NVME_CQE_STATUS_FIELD_SC_MASK) >> NVME_CQE_STATUS_FIELD_SC_OFFSET;
Sct = (StatusField & NVME_CQE_STATUS_FIELD_SCT_MASK) >> NVME_CQE_STATUS_FIELD_SCT_OFFSET;
DEBUG ((DEBUG_ERROR, "%a: NVMe Sanitize admin command failed SCT = 0x%x, SC = 0x%x\n", __func__, Sct, Sc));
//
// Check for an error status code of "Invalid Command Opcode" in case
// the NVM Express controller does not support Sanitize. If the NVM
// Exress Controller does not support Sanitize, then send a Format NVM
// admin command instead to perform the Purge operation.
//
if ((Sct == NVME_CQE_SCT_GENERIC_CMD_STATUS) &&
(Sc == NVME_CQE_SC_INVALID_CMD_OPCODE))
{
switch (SanitizeCdw10Cdw11.Sanact) {
case SANITIZE_ACTION_BLOCK_ERASE:
FnvmSes = SES_USER_DATA_ERASE; // User Data Erase (LBAs indeterminate after)
break;
case SANITIZE_ACTION_CRYPTO_ERASE:
FnvmSes = SES_CRYPTO_ERASE; // Crypto Erase
break;
case SANITIZE_ACTION_OVERWRITE:
case SANITIZE_ACTION_EXIT_FAILURE_MODE:
default:
//
// Cannot perform an equivalent FormatNVM action/operation
//
FnvmSes = SES_NO_SECURE_ERASE;
break;
}
if ((FnvmSes == SES_USER_DATA_ERASE) || (FnvmSes == SES_CRYPTO_ERASE)) {
Status = NvmExpressFormatNvm (
This,
NVME_ALL_NAMESPACES,
FnvmSes,
0 // Pass in NULL so existing LBA size is used in Format NVM
);
}
}
}
return Status;
}
/**
Clear Media utilizes transport native WRITE commands to write a fixed pattern
of non-sensitive data to the media.
NOTE: The caller shall send buffer of one sector/LBA size with overwrite data.
NOTE: This operation is a blocking call.
NOTE: This function must be called from TPL_APPLICATION or TPL_CALLBACK.
Functions are defined to erase and purge data at a block level from mass
storage devices as well as to manage such devices in the EFI boot services
environment.
@param[in] This Indicates a pointer to the calling context.
@param[in] MediaId The media ID that the write request is for.
@param[in] PassCount The number of passes to write over media.
@param[in] SectorOwBuffer A pointer to the overwrite buffer.
@retval EFI_SUCCESS The data was written correctly to the device.
@retval EFI_WRITE_PROTECTED The device can not be written to.
@retval EFI_DEVICE_ERROR The device reported an error while performing the write.
@retval EFI_NO_MEDIA There is no media in the device.
@retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device.
@retval EFI_INVALID_PARAMETER The write request contains LBAs that are not valid,
or the buffer is not on proper alignment.
**/
EFI_STATUS
EFIAPI
NvmExpressMediaClear (
IN MEDIA_SANITIZE_PROTOCOL *This,
IN UINT32 MediaId,
IN UINT32 PassCount,
IN VOID *SectorOwBuffer
)
{
NVME_DEVICE_PRIVATE_DATA *Device;
EFI_BLOCK_IO_MEDIA *Media;
EFI_LBA SectorOffset;
UINT32 TotalPassCount;
EFI_STATUS Status;
//
// Check parameters.
//
if (This == NULL) {
return EFI_INVALID_PARAMETER;
}
Device = NVME_DEVICE_PRIVATE_DATA_FROM_MEDIA_SANITIZE (This);
Media = &Device->Media;
SectorOffset = 0;
if ((MediaId != Media->MediaId) || (!Media->MediaPresent)) {
return EFI_MEDIA_CHANGED;
}
//
// If an invalid buffer or buffer size is sent, the Media Clear operation
// cannot be performed as it requires a native WRITE command. The overwrite
// buffer must have granularity of a namespace block size.
//
if (SectorOwBuffer == NULL) {
return EFI_INVALID_PARAMETER;
}
//
// Per NIST 800-88r1, one or more pass of writes may be alteratively used.
//
for (TotalPassCount = 0; TotalPassCount < PassCount; TotalPassCount++) {
for (SectorOffset = 0; SectorOffset < Media->LastBlock; SectorOffset++ ) {
Status = Device->BlockIo.WriteBlocks (
&Device->BlockIo,
MediaId,
SectorOffset, // Sector/LBA offset (increment each pass)
1, // Write one sector per write
SectorOwBuffer // overwrite buffer
);
}
//
// Reset SectorOffset back to zero if another pass on namespace is needed
//
SectorOffset = 0;
}
return Status;
}
/**
Purge Media utilizes transport native Sanitize operations. Sanitize specific
purge actions include: overwrite, block erase, or crypto erase.
Functions are defined to erase and purge data at a block level from mass
storage devices as well as to manage such devices in the EFI boot services
environment. Sanitization refers to a process that renders access to target
data on the media infeasible for a given level of effort.
NOTE: This operation is a blocking call.
NOTE: This function must be called from TPL_APPLICATION or TPL_CALLBACK.
@param[in] This Indicates a pointer to the calling context.
@param[in] MediaId The media ID that the write request is for.
@param[in] PurgeAction The purage action (overwrite, crypto erase, block erase).
@param[in] OverwritePattern 32-bit pattern to overwrite on media (for overwrite).
@retval EFI_SUCCESS The media was purged successfully on the device.
@retval EFI_WRITE_PROTECTED The device can not be purged due to write protection.
@retval EFI_DEVICE_ERROR The device reported an error while performing the purge.
@retval EFI_NO_MEDIA There is no media in the device.
@retval EFI_MEDIA_CHNAGED The MediaId does not match the current device.
@retval EFI_INVALID_PARAMETER The purge request contains parameters that are not valid.
**/
EFI_STATUS
EFIAPI
NvmExpressMediaPurge (
IN MEDIA_SANITIZE_PROTOCOL *This,
IN UINT32 MediaId,
IN UINT32 PurgeAction,
IN UINT32 OverwritePattern
)
{
NVME_DEVICE_PRIVATE_DATA *Device;
EFI_BLOCK_IO_MEDIA *Media;
NVME_SANICAP SaniCap;
UINT32 SanitizeAction;
UINT32 NoDeallocate;
UINT32 NamespaceId;
EFI_STATUS Status;
//
// Check parameters.
//
if (This == NULL) {
return EFI_INVALID_PARAMETER;
}
Device = NVME_DEVICE_PRIVATE_DATA_FROM_MEDIA_SANITIZE (This);
NamespaceId = Device->NamespaceId;
Media = &Device->Media;
SaniCap = Device->Controller->ControllerData->Sanicap;
NoDeallocate = 0;
if ((MediaId != Media->MediaId) || (!Media->MediaPresent)) {
return EFI_MEDIA_CHANGED;
}
//
// Purge action will directly map to sanitize action. If no valid purge
// action is selected, then default to no action and let the NVMe SSD handle
// the no-op sanitize action (as there may be other contingencies).
//
if (((PurgeAction & PURGE_ACTION_OVERWRITE) == PURGE_ACTION_OVERWRITE) && (SaniCap.Ows)) {
SanitizeAction = SANITIZE_ACTION_OVERWRITE;
} else if (((PurgeAction & PURGE_ACTION_BLOCK_ERASE) == PURGE_ACTION_BLOCK_ERASE) && (SaniCap.Bes)) {
SanitizeAction = SANITIZE_ACTION_BLOCK_ERASE;
} else if (((PurgeAction & PURGE_ACTION_CRYPTO_ERASE) == PURGE_ACTION_CRYPTO_ERASE) && (SaniCap.Ces)) {
SanitizeAction = SANITIZE_ACTION_CRYPTO_ERASE;
} else {
SanitizeAction = SANITIZE_ACTION_NO_ACTION;
}
if ((PurgeAction & PURGE_ACTION_NO_DEALLOCATE) == PURGE_ACTION_NO_DEALLOCATE) {
NoDeallocate = NVME_NO_DEALLOCATE_AFTER_SANITZE;
}
//
// Call NVM Express Admin command Sanitize (blocking call).
//
Status = NvmExpressSanitize (
&Device->BlockIo,
NamespaceId,
SanitizeAction,
NoDeallocate,
OverwritePattern
);
return Status;
}
/**
Format Media utilizes native format operations to modify sector/LBA size.
Secure erase actions are used to define how latent user data is erased.
NOTE: This function must be called from TPL_APPLICATION or TPL_CALLBACK.
@param[in] This Indicates a pointer to the calling context.
@param[in] MediaId The media ID that the clear request is for.
@param[in] LbaSize Size of LBA (in terms of power of two: 2^n).
@param[in] SecureEraseAction Secure erase action, if any, to apply to format.
- 000b: No secure erase operation requested
- 001b: User Data Erase
- 010b: Cryptographic Erase
- 011b to 111b: Reserved
@retval EFI_SUCCESS The media format request comopleted successfully on the device.
@retval EFI_WRITE_PROTECTED The device can't be formatted due to write protection.
@retval EFI_DEVICE_ERROR The device reported an error while attempting to perform the format operation.
@retval EFI_INVALID_PARAMETER The format request contains parameters that are not valid.
@retval EFI_NO_MEDIA There is no media in the device.
@retval EFI_MEDIA_CHANGED The MediaId is not for the current media.
**/
EFI_STATUS
EFIAPI
NvmExpressMediaFormat (
IN MEDIA_SANITIZE_PROTOCOL *This,
IN UINT32 MediaId,
IN UINT32 LbaSize,
IN UINT32 SecureEraseAction
)
{
NVME_DEVICE_PRIVATE_DATA *Device;
EFI_BLOCK_IO_MEDIA *Media;
UINT32 NamespaceId;
UINT32 SecureEraseSettings;
UINT32 FlbaIndex;
BOOLEAN LbaSizeIsSupported;
EFI_STATUS Status;
//
// Check parameters.
//
if (This == NULL) {
return EFI_INVALID_PARAMETER;
}
Device = NVME_DEVICE_PRIVATE_DATA_FROM_MEDIA_SANITIZE (This);
NamespaceId = Device->NamespaceId;
Media = &Device->Media;
SecureEraseSettings = FORMAT_SES_NO_SECURE_ERASE_REQUESTED;
FlbaIndex = 0;
if ((MediaId != Media->MediaId) || (!Media->MediaPresent)) {
return EFI_MEDIA_CHANGED;
}
//
// Convert secure erase action to NVMe secure erase setting
//
switch (SecureEraseAction) {
case FORMAT_SES_USER_DATA_ERASE:
SecureEraseSettings = SES_USER_DATA_ERASE;
break;
case FORMAT_SES_CRYPTOGRAPHIC_ERASE:
SecureEraseSettings = SES_CRYPTO_ERASE;
break;
case FORMAT_SES_NO_SECURE_ERASE_REQUESTED:
default:
//
// Cannot perform an equivalent FormatNVM action/operation
//
SecureEraseSettings = SES_NO_SECURE_ERASE;
break;
}
//
// The requested LBA size must be supported by the NVMe SSD as defined in Identify
// Namespace structure.
//
// Current supported LBA format sizes is in Identify Namespace LBA Format Table,
// indexed by FLBAS (bits 3:0). Loop through all supported LBADF sizes and check
// to see if requested LBA size is supported. If yes, send FormatNVM command.
//
LbaSizeIsSupported = FALSE;
for (FlbaIndex = 0; FlbaIndex < Device->NamespaceData.Nlbaf; FlbaIndex++) {
if (Device->NamespaceData.LbaFormat[FlbaIndex].Lbads == LbaSize) {
LbaSizeIsSupported = TRUE;
break;
}
}
if (LbaSizeIsSupported) {
Status = NvmExpressFormatNvm (
&Device->BlockIo,
NamespaceId,
SecureEraseSettings,
FlbaIndex
);
} else {
Status = EFI_INVALID_PARAMETER;
}
return Status;
}

View File

@ -0,0 +1,191 @@
/** @file
Header file for MEDIA_SANITIZE_PROTOCOL interface.
Copyright (c) Microsoft Corporation.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#ifndef NVME_MEDIA_SANITIZE_H_
#define NVME_MEDIA_SANITIZE_H_
#define NVME_NO_DEALLOCATE_AFTER_SANITZE 0x1
/**
Send NVM Express FormatNVM Admin Command
The Format NVM command is used to low level format the NVM media. This command is used by
the host to change the LBA data size and/or metadata size.
A low level format may destroy all data and metadata associated with all namespaces or only
the specific namespace associated with the command (refer to the Format NVM Attributes field
in the Identify Controller data structure).
After the Format NVM command successfully completes, the controller shall not return any user
data that was previously contained in an affected namespace.
@param[in] This Indicates a pointer to the calling context (Block IO Protocol)
@param[in] NamespaceId The NVM Express namespace ID for which a device path node is to be
allocated and built. Caller must set the NamespaceId to zero if the
device path node will contain a valid UUID.
@param[in] Ses Secure Erase Setting (SES) value
- 000b: No secure erase operation requested
- 001b: User Data Erase
- 010b: Cryptographic Erase
- 011b to 111b: Reserved
@param[in] Flbas New LBA size (in terms of LBA Format size Index (bits 3:0) in NamespaceData).
If this param is 0 (NULL), then use existing LBA size.
@retval EFI_SUCCESS The device formatted correctly.
@retval EFI_WRITE_PROTECTED The device can not be formatted due to write protection.
@retval EFI_DEVICE_ERROR The device reported an error while performing the format.
@retval EFI_NO_MEDIA There is no media in the device.
@retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device.
@retval EFI_INVALID_PARAMETER The format request contains parameters that are not valid.
**/
EFI_STATUS
NvmExpressFormatNvm (
IN EFI_BLOCK_IO_PROTOCOL *This,
IN UINT32 NamespaceId,
IN UINT32 Ses,
IN UINT32 Flbas
);
/**
Send NVM Express Sanitize Admin Command
The Sanitize command is used to start a sanitize operation or to recover from a previously
failed sanitize operation. The sanitize operation types that may be supported are Block
Erase, Crypto Erase, and Overwrite.
All sanitize operations are processed in the background (i.e., completion of the Sanitize
command does not indicate completion of the sanitize operation).
@param[in] This Indicates a pointer to the calling context (Block IO Protocol)
@param[in] NamespaceId The NVM Express namespace ID for which a device path node is to be
allocated and built. Caller must set the NamespaceId to zero if the
device path node will contain a valid UUID.
@param[in] SanitizeAction Sanitize action
@param[in] NoDeallocAfterSanitize No deallocate after sanitize option
@param[in] OverwritePattern Pattern to overwrite old user data
@retval EFI_SUCCESS The media was sanitized successfully on the device.
@retval EFI_WRITE_PROTECTED The device can not be sanitized due to write protection.
@retval EFI_DEVICE_ERROR The device reported an error while performing the sanitize.
@retval EFI_NO_MEDIA There is no media in the device.
@retval EFI_MEDIA_CHNAGED The MediaId does not match the current device.
@retval EFI_INVALID_PARAMETER The sanitize request contains parameters that are not valid.
**/
EFI_STATUS
NvmExpressSanitize (
IN EFI_BLOCK_IO_PROTOCOL *This,
IN UINT32 NamespaceId,
IN UINT32 SanitizeAction,
IN UINT32 NoDeallocAfterSanitize,
IN UINT32 OverwritePattern
);
/**
Clear Media utilizes transport native WRITE commands to write a fixed pattern
of non-sensitive data to the media.
NOTE: The caller shall send buffer of one sector/LBA size with overwrite data.
NOTE: This operation is a blocking call.
NOTE: This function must be called from TPL_APPLICATION or TPL_CALLBACK.
Functions are defined to erase and purge data at a block level from mass
storage devices as well as to manage such devices in the EFI boot services
environment.
@param[in] This Indicates a pointer to the calling context.
@param[in] MediaId The media ID that the write request is for.
@param[in] PassCount The number of passes to write over media.
@param[in] SectorOwBuffer A pointer to the overwrite buffer.
@retval EFI_SUCCESS The data was written correctly to the device.
@retval EFI_WRITE_PROTECTED The device can not be written to.
@retval EFI_DEVICE_ERROR The device reported an error while performing the write.
@retval EFI_NO_MEDIA There is no media in the device.
@retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device.
@retval EFI_INVALID_PARAMETER The write request contains LBAs that are not valid,
or the buffer is not on proper alignment.
**/
EFI_STATUS
EFIAPI
NvmExpressMediaClear (
IN MEDIA_SANITIZE_PROTOCOL *This,
IN UINT32 MediaId,
IN UINT32 PassCount,
IN VOID *SectorOwBuffer
);
/**
Purge Media utilizes transport native Sanitize operations. Sanitize specific
purge actions include: overwrite, block erase, or crypto erase.
Functions are defined to erase and purge data at a block level from mass
storage devices as well as to manage such devices in the EFI boot services
environment. Sanitization refers to a process that renders access to target
data on the media infeasible for a given level of effort.
NOTE: This operation is a blocking call.
NOTE: This function must be called from TPL_APPLICATION or TPL_CALLBACK.
@param[in] This Indicates a pointer to the calling context.
@param[in] MediaId The media ID that the write request is for.
@param[in] PurgeAction The purage action (overwrite, crypto erase, block erase).
@param[in] OverwritePattern 32-bit pattern to overwrite on media (for overwrite).
@retval EFI_SUCCESS The media was purged successfully on the device.
@retval EFI_WRITE_PROTECTED The device can not be purged due to write protection.
@retval EFI_DEVICE_ERROR The device reported an error while performing the purge.
@retval EFI_NO_MEDIA There is no media in the device.
@retval EFI_MEDIA_CHNAGED The MediaId does not match the current device.
@retval EFI_INVALID_PARAMETER The purge request contains parameters that are not valid.
**/
EFI_STATUS
EFIAPI
NvmExpressMediaPurge (
IN MEDIA_SANITIZE_PROTOCOL *This,
IN UINT32 MediaId,
IN UINT32 PurgeAction,
IN UINT32 OverwritePattern
);
/**
Format Media utilizes native format operations to modify sector/LBA size.
Secure erase actions are used to define how latent user data is erased.
NOTE: This function must be called from TPL_APPLICATION or TPL_CALLBACK.
@param[in] This Indicates a pointer to the calling context.
@param[in] MediaId The media ID that the clear request is for.
@param[in] LbaSize Size of LBA (in terms of power of two: 2^n).
@param[in] SecureEraseAction Secure erase action, if any, to apply to format.
- 000b: No secure erase operation requested
- 001b: User Data Erase
- 010b: Cryptographic Erase
- 011b to 111b: Reserved
@retval EFI_SUCCESS The media format request completed successfully on the device.
@retval EFI_WRITE_PROTECTED The device can't be formatted due to write protection.
@retval EFI_DEVICE_ERROR The device reported an error while attempting to perform the format operation.
@retval EFI_INVALID_PARAMETER The format request contains parameters that are not valid.
@retval EFI_NO_MEDIA There is no media in the device.
@retval EFI_MEDIA_CHANGED The MediaId is not for the current media.
**/
EFI_STATUS
EFIAPI
NvmExpressMediaFormat (
IN MEDIA_SANITIZE_PROTOCOL *This,
IN UINT32 MediaId,
IN UINT32 LbaSize,
IN UINT32 SecureEraseAction
);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,37 @@
## @file
# Unit tests for MEDIA_SANITIZE_PROTOCOL and mapping to NVM Express native commands (Sanitize and FormatNVM)
#
# Copyright (C) Microsoft Corporation.<BR>
# SPDX-License-Identifier: BSD-2-Clause-Patent
##
[Defines]
INF_VERSION = 0x00010006
BASE_NAME = MediaSanitizeUnitTestHost
FILE_GUID = AAE328E9-37C3-4F4A-A2C0-0BE0E681ADA6
MODULE_TYPE = HOST_APPLICATION
VERSION_STRING = 1.0
#
# The following information is for reference only and not required by the build tools.
#
# VALID_ARCHITECTURES = IA32 X64
#
[Sources]
MediaSanitizeUnitTest.c
../NvmExpressMediaSanitize.c
../NvmExpressMediaSanitize.h
[Packages]
MdePkg/MdePkg.dec
MdeModulePkg/MdeModulePkg.dec
UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec
[LibraryClasses]
BaseLib
BaseMemoryLib
DebugLib
UnitTestLib
PrintLib
MemoryAllocationLib

View File

@ -0,0 +1,173 @@
/** @file
This file defines the Media Sanitize Protocol.
Copyright (c) Microsoft Corporation.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#ifndef MEDIA_SANITIZE_PROTOCOL_H_
#define MEDIA_SANITIZE_PROTOCOL_H_
#define MEDIA_SANITIZE_PROTOCOL_GUID \
{ \
0x0d799a99, 0x25af, 0x429e, { 0x92, 0x72, 0xd0, 0xb2, 0x7d, 0x6d, 0x5f, 0x14 } \
}
typedef struct _MEDIA_SANITIZE_PROTOCOL MEDIA_SANITIZE_PROTOCOL;
#define MEDIA_SANITIZE_PROTOCOL_REVISION 0x00010000
///
/// Sanitize actions for purge operation.
///
/// NOTE: First four actions (no action, overwrite, block erase, crypto erase) cannot
/// be overlapped. All other fields may be overlapped as they apply.
///
#define PURGE_ACTION_NO_ACTION 0x00000000 // No purge action requested
#define PURGE_ACTION_OVERWRITE 0x00000001 // Overwrite with 32-bit pattern
#define PURGE_ACTION_BLOCK_ERASE 0x00000002 // Erase Blocks with indeterminate pattern
#define PURGE_ACTION_CRYPTO_ERASE 0x00000004 // Delete encryption keys only
#define PURGE_ACTION_RESET_REQUIRED 0x00000008 // Reset required after purge
#define PURGE_ACTION_NO_DEALLOCATE 0x00000010 // Do no deallocate (trim) flash medai after sanitize
#define PURGE_ACTION_INVERT_OW_PATTERN 0x00000020 // Invert overwrite pattern between passes
#define PURGE_ACTION_ALLOW_UNRESTRICTED_SANITIZE_EXIT 0x00000040 // Allow exit without restrictions
///
/// Secure erase action for media format operation
///
#define FORMAT_SES_NO_SECURE_ERASE_REQUESTED 0x0 // No secure erase operation requested
#define FORMAT_SES_USER_DATA_ERASE 0x1 // User Data Erase
#define FORMAT_SES_CRYPTOGRAPHIC_ERASE 0x2 // Cryptographic Erase
/**
Clear Media utilizes transport native WRITE commands to write a fixed pattern
of non-sensitive data. The size of the overwrite buffer shall be equal to the
one sector/LBA (in bytes).
NOTE: This function must be called from TPL aaplication or callback.
@param[in] This Indicates a pointer to the calling context.
@param[in] MediaId The media ID that the clear request is for.
@param[in] PassCount Number of passes to write over the media.
@param[in] SectorOwBuffer Pointer to overwrite pattern buffer.
@retval EFI_SUCCESS The media clear request completed successfully
on the device.
@retval EFI_WRITE_PROTECTED The device can't be cleared due to write
protection.
@retval EFI_DEVICE_ERROR The device reported an error while attempting
to perform the clear operation.
@retval EFI_INVALID_PARAMETER The clear request contains parameters that
are not valid.
@retval EFI_NO_MEDIA There is no media in the device.
@retval EFI_MEDIA_CHANGED The MediaId is not for the current media.
**/
typedef
EFI_STATUS
(EFIAPI *BLOCK_MEDIA_CLEAR)(
IN MEDIA_SANITIZE_PROTOCOL *This,
IN UINT32 MediaId,
IN UINT32 PassCount,
IN VOID *SectorOwBuffer
);
/**
Purge Media utilizes native Sanitize operations. Transport specific
overwrite, block erase, or crypto erase functions shall be invoked based
on transport.
NOTE: This function must be called from TPL aaplication or callback.
@param[in] This Indicates a pointer to the calling context.
@param[in] MediaId The media ID that the clear request is for.
@param[in] PurgeAction Purge action: overwrite, crypto or block erase.
@param[in] OverwritePattern 32-bit pattern to overwrite on media.
@retval EFI_SUCCESS The media purge request completed successfully
on the device.
@retval EFI_WRITE_PROTECTED The device can't be purged due to write
protection.
@retval EFI_DEVICE_ERROR The device reported an error while attempting
to perform the purge operation.
@retval EFI_INVALID_PARAMETER The purge request contains parameters that
are not valid.
@retval EFI_NO_MEDIA There is no media in the device.
@retval EFI_MEDIA_CHANGED The MediaId is not for the current media.
**/
typedef
EFI_STATUS
(EFIAPI *BLOCK_MEDIA_PURGE)(
IN MEDIA_SANITIZE_PROTOCOL *This,
IN UINT32 MediaId,
IN UINT32 PurgeAction,
IN UINT32 OverwritePattern
);
/**
Format Media utilizes native format operations to modify sector/LBA size.
Secure erase actions are used to define how latent user data is erased.
NOTE: This function must be called from TPL aaplication or callback.
@param[in] This Indicates a pointer to the calling context.
@param[in] MediaId The media ID that the clear request is for.
@param[in] LbaSize Size of LBA (in terms of power of two: 2^n).
@param[in] SecureEraseAction Secure erase action, if any, to apply to format.
@retval EFI_SUCCESS The media format request comopleted
successfully on the device.
@retval EFI_WRITE_PROTECTED The device can't be formatted due to write
protection.
@retval EFI_DEVICE_ERROR The device reported an error while attempting
to perform the format operation.
@retval EFI_INVALID_PARAMETER The format request contains parameters that
are not valid.
@retval EFI_NO_MEDIA There is no media in the device.
@retval EFI_MEDIA_CHANGED The MediaId is not for the current media.
**/
typedef
EFI_STATUS
(EFIAPI *BLOCK_MEDIA_FORMAT)(
IN MEDIA_SANITIZE_PROTOCOL *This,
IN UINT32 MediaId,
IN UINT32 LbaSize,
IN UINT32 SecureEraseAction
);
///
/// The Media Sanitize Protocol provides the ability for a device to expose
/// sanitize functionality. This optional protocol is installed on the same handle
/// as the EFI_BLOCK_IO_PROTOCOL or EFI_BLOCK_IO2_PROTOCOL.
///
struct _MEDIA_SANITIZE_PROTOCOL {
///
/// The revision to which the MEDIA_SANITIZE_PROTOCOL adheres. All future
/// revisions must be backwards compatible. If a future version is not
/// backwards compatible, it is not the same GUID.
///
UINT64 Revision;
///
/// A pointer to the EFI_BLOCK_IO_MEDIA data for this device.
/// Type EFI_BLOCK_IO_MEDIA is defined in BlockIo.h.
///
EFI_BLOCK_IO_MEDIA *Media;
///
/// SanitizeCapabilities shall which sanitize operations (crypto erase, block
/// erase, overwrite) is supported by this Block Io device.
///
UINT32 SanitizeCapabilities;
BLOCK_MEDIA_CLEAR MediaClear;
BLOCK_MEDIA_PURGE MediaPurge;
BLOCK_MEDIA_FORMAT MediaFormat;
};
extern EFI_GUID gMediaSanitizeProtocolGuid;
#endif

View File

@ -23,6 +23,7 @@
"8005", "UNIVERSAL_PAYLOAD_PCI_ROOT_BRIDGE.UID",
"8005", "UNIVERSAL_PAYLOAD_PCI_ROOT_BRIDGE.HID",
"8001", "UefiSortLibUnitTestMain",
"8001", "MediaSanitizeUnitTestMain",
],
## Both file path and directory path are accepted.
"IgnoreFiles": [

View File

@ -589,6 +589,10 @@
gEfiPrint2ProtocolGuid = { 0xf05976ef, 0x83f1, 0x4f3d, { 0x86, 0x19, 0xf7, 0x59, 0x5d, 0x41, 0xe5, 0x38 } }
gEfiPrint2SProtocolGuid = { 0xcc252d2, 0xc106, 0x4661, { 0xb5, 0xbd, 0x31, 0x47, 0xa4, 0xf8, 0x1f, 0x92 } }
## This protocol defines the Media Clear and Sanitize operations defined by NIST
# Include/Protocol/MediaSanitize.h
gMediaSanitizeProtocolGuid = { 0x0d799a99, 0x25af, 0x429e, {0x92, 0x72, 0xd0, 0xb2, 0x7d, 0x6d, 0x5f, 0x14 } }
## This protocol defines the generic memory test interfaces in Dxe phase.
# Include/Protocol/GenericMemoryTest.h
gEfiGenericMemTestProtocolGuid = { 0x309DE7F1, 0x7F5E, 0x4ACE, { 0xB4, 0x9C, 0x53, 0x1B, 0xE5, 0xAA, 0x95, 0xEF }}

View File

@ -60,6 +60,11 @@
PeCoffGetEntryPointLib|MdePkg/Library/BasePeCoffGetEntryPointLib/BasePeCoffGetEntryPointLib.inf
}
MdeModulePkg/Bus/Pci/NvmExpressDxe/UnitTest/MediaSanitizeUnitTestHost.inf {
<LibraryClasses>
NvmExpressDxe|MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressDxe.inf
}
#
# Build HOST_APPLICATION Libraries
#