diff --git a/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpress.c b/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpress.c index 069da12a9b..c8d8be32b0 100644 --- a/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpress.c +++ b/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpress.c @@ -3,6 +3,7 @@ NVM Express specification. Copyright (c) 2013 - 2017, Intel Corporation. All rights reserved.
+ Copyright (c) Microsoft Corporation.
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 ); diff --git a/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpress.h b/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpress.h index 2b9ab8a08e..11207afa63 100644 --- a/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpress.h +++ b/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpress.h @@ -4,6 +4,7 @@ (C) Copyright 2016 Hewlett Packard Enterprise Development LP
Copyright (c) 2013 - 2019, Intel Corporation. All rights reserved.
+ Copyright (c) Microsoft Corporation.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ @@ -29,6 +30,7 @@ #include #include #include +#include #include #include @@ -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. // diff --git a/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressDxe.inf b/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressDxe.inf index 1a29c0b907..5fd0e468a9 100644 --- a/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressDxe.inf +++ b/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressDxe.inf @@ -5,7 +5,7 @@ # NVM Express specification. # # Copyright (c) 2013 - 2019, Intel Corporation. All rights reserved.
-# +# Copyright (c) Microsoft Corporation.
# 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] diff --git a/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressMediaSanitize.c b/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressMediaSanitize.c new file mode 100644 index 0000000000..8632924899 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressMediaSanitize.c @@ -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.
+ 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; +} diff --git a/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressMediaSanitize.h b/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressMediaSanitize.h new file mode 100644 index 0000000000..197b923977 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressMediaSanitize.h @@ -0,0 +1,191 @@ +/** @file + Header file for MEDIA_SANITIZE_PROTOCOL interface. + + Copyright (c) Microsoft Corporation.
+ 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 diff --git a/MdeModulePkg/Bus/Pci/NvmExpressDxe/UnitTest/MediaSanitizeUnitTest.c b/MdeModulePkg/Bus/Pci/NvmExpressDxe/UnitTest/MediaSanitizeUnitTest.c new file mode 100644 index 0000000000..b8728580c5 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/NvmExpressDxe/UnitTest/MediaSanitizeUnitTest.c @@ -0,0 +1,1128 @@ +/** @file -- MediaSanitizeUnitTest.c + Placeholder/framework for developing a Media Sanitize unit test package. + + Copyright (c) Microsoft Corporation.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../NvmExpress.h" +#include "../NvmExpressBlockIo.h" +#include "../NvmExpressMediaSanitize.h" +#include "../NvmExpressHci.h" + +/** + Helper function for Nvme pass thru. + + @param[in] This Private Data. + @param[in] NamespaceId Name Space Id. + @param[in,out] Packet Transfer Buffer. + @param[in] Event Event handle. + + **/ +EFI_STATUS +EFIAPI +NvmeDeviceUnitTestPassthru ( + IN EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL *This, + IN UINT32 NamespaceId, + IN OUT EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET *Packet, + IN EFI_EVENT Event OPTIONAL + ) +{ + // + // Parse command packet for unit testing + // + EFI_NVM_EXPRESS_COMMAND *Command; + EFI_NVM_EXPRESS_COMPLETION *Completion; + NVME_CQ *Cqe; + NVME_ADMIN_FORMAT_NVM FormatNvmCdw10; + NVME_ADMIN_SANITIZE SanitizeCdw1011; + + ASSERT (This); + ASSERT (Packet); + + Command = Packet->NvmeCmd; + Completion = Packet->NvmeCompletion; + Cqe = (NVME_CQ *)Completion; + + ZeroMem (&FormatNvmCdw10, sizeof (NVME_ADMIN_FORMAT_NVM)); + ZeroMem (&SanitizeCdw1011, sizeof (NVME_ADMIN_SANITIZE)); + + switch (Command->Cdw0.Opcode) { + case NVME_ADMIN_FORMAT_NVM_CMD: + UT_LOG_VERBOSE ("%a: Opcode = NVME_ADMIN_FORMAT_NVM_CMD\n", __func__); + + CopyMem (&FormatNvmCdw10, &Command->Cdw10, sizeof (NVME_ADMIN_FORMAT_NVM)); + + // + // FormatNVM Check 1: Validate SES parameter + // + if (FormatNvmCdw10.Ses > 0x2) { + Cqe->Sct = NVME_CQE_SCT_GENERIC_CMD_STATUS; + Cqe->Sc = NVME_CQE_SC_INVALID_FIELD_IN_CMD; + + return EFI_INVALID_PARAMETER; + } + + // + // FormatNVM Check 2: Validate LbaIndex parameter + // + if (FormatNvmCdw10.Lbaf > 0x1) { + Cqe->Sct = NVME_CQE_SCT_GENERIC_CMD_STATUS; + Cqe->Sc = NVME_CQE_SC_INVALID_FIELD_IN_CMD; + + return EFI_INVALID_PARAMETER; + } + + break; + case NVME_ADMIN_SANITIZE_CMD: + UT_LOG_VERBOSE ("%a: Opcode = NVME_ADMIN_SANITIZE_CMD\n", __func__); + + CopyMem (&SanitizeCdw1011, &Command->Cdw10, sizeof (NVME_ADMIN_SANITIZE)); + + // + // Sanitize Check 1: Validate Sanitize Action parameter + // + if (SanitizeCdw1011.Sanact > 0x4) { + Cqe->Sct = NVME_CQE_SCT_GENERIC_CMD_STATUS; + Cqe->Sc = NVME_CQE_SC_INVALID_FIELD_IN_CMD; + + return EFI_INVALID_PARAMETER; + } + + // + // Sanitize Check 2: Validate overwrite action with non-NULL overwrite pattern + // + if (((SanitizeCdw1011.Sanact == SANITIZE_ACTION_OVERWRITE) && (SanitizeCdw1011.Ovrpat != 0xDEADBEEF)) || + ((SanitizeCdw1011.Sanact != SANITIZE_ACTION_OVERWRITE) && (SanitizeCdw1011.Ovrpat != 0))) + { + Cqe->Sct = NVME_CQE_SCT_GENERIC_CMD_STATUS; + Cqe->Sc = NVME_CQE_SC_INVALID_FIELD_IN_CMD; + + return EFI_INVALID_PARAMETER; + } + + break; + default: + UT_LOG_VERBOSE ("%a: Invalid Opcode = 0x%x!!!\n", __func__, Command->Cdw0.Opcode); + break; + } + + // + // Populate CQE (completion queue entry based on opcode and parameters + // + + return EFI_SUCCESS; +} + +/** + Helper function to simulate read. + + @param[in] Private Private Data. + @param[in] NamespaceId Name Space Id. + @param[in] Buffer Transfer Buffer. + + **/ +EFI_STATUS +NvmeIdentifyNamespace ( + IN NVME_CONTROLLER_PRIVATE_DATA *Private, + IN UINT32 NamespaceId, + IN VOID *Buffer + ) +{ + EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket; + EFI_NVM_EXPRESS_COMMAND Command; + EFI_NVM_EXPRESS_COMPLETION Completion; + + ZeroMem (&CommandPacket, sizeof (EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET)); + ZeroMem (&Command, sizeof (EFI_NVM_EXPRESS_COMMAND)); + ZeroMem (&Completion, sizeof (EFI_NVM_EXPRESS_COMPLETION)); + + CommandPacket.NvmeCmd = &Command; + CommandPacket.NvmeCompletion = &Completion; + Command.Cdw0.Opcode = NVME_ADMIN_IDENTIFY_CMD; + Command.Nsid = NamespaceId; + CommandPacket.TransferBuffer = Buffer; + CommandPacket.TransferLength = sizeof (NVME_ADMIN_NAMESPACE_DATA); + CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT; + CommandPacket.QueueType = NVME_ADMIN_QUEUE; + + // + // Set bit 0 (Cns bit) to 1 to identify a namespace + // + CommandPacket.NvmeCmd->Cdw10 = 0; + CommandPacket.NvmeCmd->Flags = CDW10_VALID; + + return EFI_SUCCESS; +} + +/** + Helper function to simulate read. + + @param[in] Device Private Data. + @param[out] Buffer Buffer to read into. + @param[in] Lba Logical Block Addess to read from. + @param[in] Blocks Number of blocks. + + **/ +EFI_STATUS +NvmeUnitTestRead ( + IN NVME_DEVICE_PRIVATE_DATA *Device, + OUT VOID *Buffer, + IN UINT64 Lba, + IN UINTN Blocks + ) +{ + UT_ASSERT_NOT_NULL (Device); + Buffer = NULL; + Lba = 0; + Blocks = 0; + + return EFI_SUCCESS; +} + +/** + Helper function to simulate write. + + @param[in] Device Private Data. + @param[in] Buffer Buffer to write. + @param[in] Lba Logical Block Addess to write. + @param[in] Blocks Number of blocks. + + **/ +EFI_STATUS +NvmeUnitTestWrite ( + IN NVME_DEVICE_PRIVATE_DATA *Device, + IN VOID *Buffer, + IN UINT64 Lba, + IN UINTN Blocks + ) +{ + UT_ASSERT_NOT_NULL (Device); + Buffer = NULL; + Lba = 0; + Blocks = 0; + + return EFI_SUCCESS; +} + +/** + Simulated BlockIo read block function. + + @param[in] This BlockIo Protocol. + @param[in] MediaId Id of the media. + @param[in] Lba Logical Block Address. + @param[in] BufferSize Size of Buffer. + @param[out] Buffer Actual buffer to use to read. + + **/ +EFI_STATUS +EFIAPI +NvmeBlockIoReadBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN UINTN BufferSize, + OUT VOID *Buffer + ) +{ + NVME_DEVICE_PRIVATE_DATA *Device; + EFI_STATUS Status; + EFI_BLOCK_IO_MEDIA *Media; + UINTN BlockSize; + UINTN NumberOfBlocks; + UINTN IoAlign; + + // + // Check parameters. + // + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Media = This->Media; + + if (MediaId != Media->MediaId) { + return EFI_MEDIA_CHANGED; + } + + if (Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (BufferSize == 0) { + return EFI_SUCCESS; + } + + BlockSize = Media->BlockSize; + if ((BufferSize % BlockSize) != 0) { + return EFI_BAD_BUFFER_SIZE; + } + + NumberOfBlocks = BufferSize / BlockSize; + if ((Lba + NumberOfBlocks - 1) > Media->LastBlock) { + return EFI_INVALID_PARAMETER; + } + + IoAlign = Media->IoAlign; + if ((IoAlign > 0) && (((UINTN)Buffer & (IoAlign - 1)) != 0)) { + return EFI_INVALID_PARAMETER; + } + + Device = NVME_DEVICE_PRIVATE_DATA_FROM_BLOCK_IO (This); + Status = NvmeUnitTestRead (Device, Buffer, Lba, NumberOfBlocks); + + return Status; +} + +/** + Simulated BlockIo write block function. + + @param[in] This BlockIo Protocol. + @param[in] MediaId Id of the media. + @param[in] Lba Logical Block Address. + @param[in] BufferSize Size of Buffer. + @param[in] Buffer Actual buffer to use to write. + + **/ +EFI_STATUS +EFIAPI +NvmeBlockIoWriteBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN UINTN BufferSize, + IN VOID *Buffer + ) +{ + NVME_DEVICE_PRIVATE_DATA *Device; + EFI_STATUS Status; + EFI_BLOCK_IO_MEDIA *Media; + UINTN BlockSize; + UINTN NumberOfBlocks; + UINTN IoAlign; + + // + // Check parameters. + // + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Media = This->Media; + + if (MediaId != Media->MediaId) { + return EFI_MEDIA_CHANGED; + } + + if (Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (BufferSize == 0) { + return EFI_SUCCESS; + } + + BlockSize = Media->BlockSize; + if ((BufferSize % BlockSize) != 0) { + return EFI_BAD_BUFFER_SIZE; + } + + NumberOfBlocks = BufferSize / BlockSize; + if ((Lba + NumberOfBlocks - 1) > Media->LastBlock) { + return EFI_INVALID_PARAMETER; + } + + IoAlign = Media->IoAlign; + if ((IoAlign > 0) && (((UINTN)Buffer & (IoAlign - 1)) != 0)) { + return EFI_INVALID_PARAMETER; + } + + Device = NVME_DEVICE_PRIVATE_DATA_FROM_BLOCK_IO (This); + Status = NvmeUnitTestWrite (Device, Buffer, Lba, NumberOfBlocks); + + return Status; +} + +/** + Simulated BlockIo read block ex function. + + @param[in] This BlockIo2 Protocol. + @param[in] MediaId Id of the media. + @param[in] Lba Logical Block Address. + @param[in,out] Token Block Io2 token. + + @param[in] BufferSize Size of Buffer. + @param[out] Buffer Actual buffer to use to read. + + **/ +EFI_STATUS +EFIAPI +NvmeBlockIoReadBlocksEx ( + IN EFI_BLOCK_IO2_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN OUT EFI_BLOCK_IO2_TOKEN *Token, + IN UINTN BufferSize, + OUT VOID *Buffer + ) +{ + NVME_DEVICE_PRIVATE_DATA *Device; + EFI_BLOCK_IO_MEDIA *Media; + UINTN BlockSize; + UINTN NumberOfBlocks; + UINTN IoAlign; + EFI_STATUS Status; + + // + // Check parameters. + // + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Media = This->Media; + + if (MediaId != Media->MediaId) { + return EFI_MEDIA_CHANGED; + } + + if (Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + BlockSize = Media->BlockSize; + if ((BufferSize % BlockSize) != 0) { + return EFI_BAD_BUFFER_SIZE; + } + + NumberOfBlocks = BufferSize / BlockSize; + if ((Lba + NumberOfBlocks - 1) > Media->LastBlock) { + return EFI_INVALID_PARAMETER; + } + + IoAlign = Media->IoAlign; + if ((IoAlign > 0) && (((UINTN)Buffer & (IoAlign - 1)) != 0)) { + return EFI_INVALID_PARAMETER; + } + + Device = NVME_DEVICE_PRIVATE_DATA_FROM_BLOCK_IO2 (This); + Status = NvmeUnitTestRead (Device, Buffer, Lba, NumberOfBlocks); + + return Status; +} + +/** + Simulated BlockIo write block ex function. + + @param[in] This BlockIo2 Protocol. + @param[in] MediaId Id of the media. + @param[in] Lba Logical Block Address. + @param[in,out] Token Block Io2 token. + @param[in] BufferSize Size of Buffer. + + @param[in] Buffer Actual buffer to use to write. + + **/ +EFI_STATUS +EFIAPI +NvmeBlockIoWriteBlocksEx ( + IN EFI_BLOCK_IO2_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN OUT EFI_BLOCK_IO2_TOKEN *Token, + IN UINTN BufferSize, + IN VOID *Buffer + ) +{ + NVME_DEVICE_PRIVATE_DATA *Device; + EFI_BLOCK_IO_MEDIA *Media; + UINTN BlockSize; + UINTN NumberOfBlocks; + UINTN IoAlign; + EFI_STATUS Status; + + // + // Check parameters. + // + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Media = This->Media; + + if (MediaId != Media->MediaId) { + return EFI_MEDIA_CHANGED; + } + + if (Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + BlockSize = Media->BlockSize; + if ((BufferSize % BlockSize) != 0) { + return EFI_BAD_BUFFER_SIZE; + } + + NumberOfBlocks = BufferSize / BlockSize; + if ((Lba + NumberOfBlocks - 1) > Media->LastBlock) { + return EFI_INVALID_PARAMETER; + } + + IoAlign = Media->IoAlign; + if ((IoAlign > 0) && (((UINTN)Buffer & (IoAlign - 1)) != 0)) { + return EFI_INVALID_PARAMETER; + } + + Device = NVME_DEVICE_PRIVATE_DATA_FROM_BLOCK_IO2 (This); + Status = NvmeUnitTestWrite (Device, Buffer, Lba, NumberOfBlocks); + + return Status; +} + +/** + MediaSanitizePurgeUnitTest to initialize a Private Namespace instance. + + @param[in] ppDevice Nvme Private Data structure to destory and free. + **/ +UNIT_TEST_STATUS +EFIAPI +NvmeDestroyDeviceInstance ( + NVME_DEVICE_PRIVATE_DATA **ppDevice + ) +{ + // + // Free in following order to to avoid dangling pointers: + // + // 1 - NVME_ADMIN_CONTROLLER_DATA + // 2 - NVME_CONTROLLER_PRIVATE_DATA + // 3 - NVME_DEVICE_PRIVATE_DATA + // + FreePool ((*ppDevice)->Controller->ControllerData); + (*ppDevice)->Controller->ControllerData = NULL; + + FreePool ((*ppDevice)->Controller); + (*ppDevice)->Controller = NULL; + + FreePool ((*ppDevice)); + *ppDevice = NULL; + + return UNIT_TEST_PASSED; +} + +/** + MediaSanitizePurgeUnitTest to initialize a Private Namespace instance. + + @param[in] ppDevice Nvme Private Data structure to initialize. + **/ +UNIT_TEST_STATUS +EFIAPI +NvmeCreateDeviceInstance ( + NVME_DEVICE_PRIVATE_DATA **ppDevice + ) +{ + NVME_ADMIN_NAMESPACE_DATA *NamespaceData; + NVME_CONTROLLER_PRIVATE_DATA *Private; + NVME_DEVICE_PRIVATE_DATA *Device; + + Private = AllocateZeroPool (sizeof (NVME_CONTROLLER_PRIVATE_DATA)); + + Private->Signature = NVME_CONTROLLER_PRIVATE_DATA_SIGNATURE; + Private->Cid[0] = 0; + Private->Cid[1] = 0; + Private->Cid[2] = 0; + Private->Pt[0] = 0; + Private->Pt[1] = 0; + Private->Pt[2] = 0; + Private->SqTdbl[0].Sqt = 0; + Private->SqTdbl[1].Sqt = 0; + Private->SqTdbl[2].Sqt = 0; + Private->CqHdbl[0].Cqh = 0; + Private->CqHdbl[1].Cqh = 0; + Private->CqHdbl[2].Cqh = 0; + Private->AsyncSqHead = 0; + + Private->ControllerData = (NVME_ADMIN_CONTROLLER_DATA *)AllocateZeroPool (sizeof (NVME_ADMIN_CONTROLLER_DATA)); + + UT_LOG_VERBOSE ("%a: Allocated and Initialized NVME_CONTROLLER_PRIVATE_DATA\n", __func__); + UT_LOG_VERBOSE ("%a: Allocated and Initialized NVME_ADMIN_CONTROLLER_DATA\n", __func__); + + Private->ControllerData->Nn = 1; // One namespace + Private->ControllerData->Sanicap.Bes = 1; // Block Erase Supported + Private->ControllerData->Sanicap.Ces = 1; // Crypto Erase Supported + Private->ControllerData->Sanicap.Ows = 1; // Overwrite Supported + + NamespaceData = AllocateZeroPool (sizeof (NVME_ADMIN_NAMESPACE_DATA)); + UT_LOG_VERBOSE ("%a: Allocated and Initialized NVME_ADMIN_NAMESPACE_DATA\n", __func__); + + Device = (NVME_DEVICE_PRIVATE_DATA *)(AllocateZeroPool (sizeof (NVME_DEVICE_PRIVATE_DATA))); + + // + // Initialize SSD namespace instance data + // + Device->Signature = NVME_DEVICE_PRIVATE_DATA_SIGNATURE; + Device->NamespaceId = 0; + Device->NamespaceUuid = 1; + + Device->Controller = Private; + + // + // Build BlockIo media structure + // + Device->Media.MediaId = 0; + Device->Media.RemovableMedia = FALSE; + Device->Media.MediaPresent = TRUE; + Device->Media.LogicalPartition = FALSE; + Device->Media.ReadOnly = FALSE; + Device->Media.WriteCaching = FALSE; + Device->Media.BlockSize = (UINT32)(1 << 9); // 512 byte sector size + + Device->Media.LastBlock = 0x4000; // NamespaceData=>Nsze + Device->Media.LogicalBlocksPerPhysicalBlock = 1; + Device->Media.LowestAlignedLba = 1; + + Device->BlockIo.Revision = EFI_BLOCK_IO_PROTOCOL_REVISION2; + Device->BlockIo.Media = &Device->Media; + Device->BlockIo.ReadBlocks = NvmeBlockIoReadBlocks; + Device->BlockIo.WriteBlocks = NvmeBlockIoWriteBlocks; + + Device->BlockIo2.Media = &Device->Media; + Device->BlockIo2.ReadBlocksEx = NvmeBlockIoReadBlocksEx; + Device->BlockIo2.WriteBlocksEx = NvmeBlockIoWriteBlocksEx; + + Device->MediaSanitize.Revision = MEDIA_SANITIZE_PROTOCOL_REVISION; + Device->MediaSanitize.Media = &Device->Media; + Device->MediaSanitize.MediaClear = NvmExpressMediaClear; + Device->MediaSanitize.MediaPurge = NvmExpressMediaPurge; + Device->MediaSanitize.MediaFormat = NvmExpressMediaFormat; + + Device->Controller->Passthru.Mode = 0; + Device->Controller->Passthru.PassThru = NvmeDeviceUnitTestPassthru; + Device->Controller->Passthru.BuildDevicePath = NULL; + Device->Controller->Passthru.GetNamespace = NULL; + Device->Controller->Passthru.GetNextNamespace = NULL; + + CopyMem (&Device->NamespaceData, NamespaceData, sizeof (NVME_ADMIN_NAMESPACE_DATA)); + *ppDevice = Device; + + UT_LOG_VERBOSE ("%a: Allocated and Initialized NVME_DEVICE_PRIVATE_DATA\n", __func__); + + return UNIT_TEST_PASSED; +} + +/** + MediaSanitizePurgeUnitTest to Test calls to NvmExpressMediaPurge. + + @param[in] Context Unit test case context + **/ +UNIT_TEST_STATUS +EFIAPI +MediaSanitizePurgeUnitTest ( + IN UNIT_TEST_CONTEXT Context + ) +{ + UINT32 PurgeAction; + UINT32 OverwritePattern; + UNIT_TEST_STATUS UnitTestStatus; + NVME_DEVICE_PRIVATE_DATA *NvmeDevice; + EFI_STATUS Status; + + UnitTestStatus = UNIT_TEST_PASSED; + NvmeDevice = NULL; + Status = EFI_SUCCESS; + + UnitTestStatus = NvmeCreateDeviceInstance (&NvmeDevice); + + UT_ASSERT_STATUS_EQUAL (UnitTestStatus, UNIT_TEST_PASSED); + UT_ASSERT_NOT_NULL (NvmeDevice); + + UT_LOG_VERBOSE ("%a: Create Device Instance Status = 0x%x\n", __func__, UnitTestStatus); + UT_LOG_VERBOSE ("%a: Device = 0x%x\n", __func__, NvmeDevice); + UT_LOG_VERBOSE ("%a: Device->BlockIo = 0x%x\n", __func__, NvmeDevice->BlockIo); + UT_LOG_VERBOSE ("%a: Device->Signature = 0x%x\n", __func__, NvmeDevice->Signature); + + // + // Case 1: Block Erase + // + PurgeAction = SANITIZE_ACTION_BLOCK_ERASE; + OverwritePattern = 0; + + Status = NvmExpressMediaPurge ( + &NvmeDevice->MediaSanitize, + NvmeDevice->Media.MediaId, + PurgeAction, + OverwritePattern + ); + + UT_ASSERT_NOT_EFI_ERROR (Status); + + UnitTestStatus = NvmeDestroyDeviceInstance (&NvmeDevice); + + return UNIT_TEST_PASSED; +} + +/** + NvmeSanitizeUnitTest to Test calls to NvmExpressSanitize. + + @param[in] Context Unit test case context + **/ +UNIT_TEST_STATUS +EFIAPI +NvmeSanitizeUnitTest ( + IN UNIT_TEST_CONTEXT Context + ) +{ + UINT32 NamespaceId; + UINT32 SanitizeAction; + UINT32 NoDeallocateAfterSanitize; + UINT32 OverwritePattern; + UNIT_TEST_STATUS UnitTestStatus; + NVME_DEVICE_PRIVATE_DATA *NvmeDevice; + EFI_STATUS Status; + + NamespaceId = 0; + UnitTestStatus = UNIT_TEST_PASSED; + NvmeDevice = NULL; + Status = EFI_SUCCESS; + SanitizeAction = SANITIZE_ACTION_BLOCK_ERASE; + NoDeallocateAfterSanitize = 0; + OverwritePattern = 0; + + UnitTestStatus = NvmeCreateDeviceInstance (&NvmeDevice); + + UT_ASSERT_STATUS_EQUAL (UnitTestStatus, UNIT_TEST_PASSED); + UT_ASSERT_NOT_NULL (NvmeDevice); + + UT_LOG_VERBOSE ("%a: Create Device Instance Status = 0x%x\n", __func__, UnitTestStatus); + UT_LOG_VERBOSE ("%a: Device = 0x%x\n", __func__, NvmeDevice); + UT_LOG_VERBOSE ("%a: Device->BlockIo = 0x%x\n", __func__, NvmeDevice->BlockIo); + UT_LOG_VERBOSE ("%a: Device->Signature = 0x%x\n", __func__, NvmeDevice->Signature); + + // + // Case 1: Block Erase + // + SanitizeAction = SANITIZE_ACTION_BLOCK_ERASE; + NoDeallocateAfterSanitize = 0; + OverwritePattern = 0; + + Status = NvmExpressSanitize ( + &NvmeDevice->BlockIo, + NamespaceId, + SanitizeAction, + NoDeallocateAfterSanitize, + OverwritePattern + ); + + UT_ASSERT_NOT_EFI_ERROR (Status); + + // + // Case 2: Crypto Erase + // + SanitizeAction = SANITIZE_ACTION_CRYPTO_ERASE; + NoDeallocateAfterSanitize = 0; + OverwritePattern = 0; + + Status = NvmExpressSanitize ( + &NvmeDevice->BlockIo, + NamespaceId, + SanitizeAction, + NoDeallocateAfterSanitize, + OverwritePattern + ); + + UT_ASSERT_NOT_EFI_ERROR (Status); + + // + // Case 3: Overwrite + // + SanitizeAction = SANITIZE_ACTION_OVERWRITE; + NoDeallocateAfterSanitize = 0; + OverwritePattern = 0xDEADBEEF; + + Status = NvmExpressSanitize ( + &NvmeDevice->BlockIo, + NamespaceId, + SanitizeAction, + NoDeallocateAfterSanitize, + OverwritePattern + ); + + UT_ASSERT_NOT_EFI_ERROR (Status); + + // + // Case 4: Block Erase (invalid overwrite pattern) + // + SanitizeAction = SANITIZE_ACTION_BLOCK_ERASE; + NoDeallocateAfterSanitize = 0; + OverwritePattern = 0xDEADBEEF; + + Status = NvmExpressSanitize ( + &NvmeDevice->BlockIo, + NamespaceId, + SanitizeAction, + NoDeallocateAfterSanitize, + OverwritePattern + ); + + UT_ASSERT_STATUS_EQUAL (Status, EFI_INVALID_PARAMETER); + + // + // Case 5: Overwrite (invalid overwrite pattern) + // + SanitizeAction = SANITIZE_ACTION_OVERWRITE; + NoDeallocateAfterSanitize = 0; + OverwritePattern = 0; + + Status = NvmExpressSanitize ( + &NvmeDevice->BlockIo, + NamespaceId, + SanitizeAction, + NoDeallocateAfterSanitize, + OverwritePattern + ); + + UT_ASSERT_STATUS_EQUAL (Status, EFI_INVALID_PARAMETER); + + UnitTestStatus = NvmeDestroyDeviceInstance (&NvmeDevice); + + return UNIT_TEST_PASSED; +} + +/** + NvmeFormatNvmUnitTest to Test calls to NvmExpressFormatNvm. + + @param[in] Context Unit test case context + **/ +UNIT_TEST_STATUS +EFIAPI +NvmeFormatNvmUnitTest ( + IN UNIT_TEST_CONTEXT Context + ) +{ + UINT32 NamespaceId; + UINT32 Ses; + UINT32 Flbas; + NVME_DEVICE_PRIVATE_DATA *NvmeDevice; + UNIT_TEST_STATUS UnitTestStatus; + EFI_STATUS Status; + + NamespaceId = 0; + NvmeDevice = NULL; + UnitTestStatus = UNIT_TEST_PASSED; + Status = EFI_SUCCESS; + + UnitTestStatus = NvmeCreateDeviceInstance (&NvmeDevice); + + UT_ASSERT_STATUS_EQUAL (UnitTestStatus, UNIT_TEST_PASSED); + UT_ASSERT_NOT_NULL (NvmeDevice); + + UT_LOG_VERBOSE ("%a: Create Device Instance Status = 0x%x\n", __func__, UnitTestStatus); + UT_LOG_VERBOSE ("%a: Device = 0x%x\n", __func__, NvmeDevice); + UT_LOG_VERBOSE ("%a: Device->BlockIo = 0x%x\n", __func__, NvmeDevice->BlockIo); + UT_LOG_VERBOSE ("%a: Device->Signature = 0x%x\n", __func__, NvmeDevice->Signature); + + // + // Case 1: User Data Erase (Flbas = 0) + // + Ses = SES_USER_DATA_ERASE; + Flbas = 0; + Status = NvmExpressFormatNvm ( + &NvmeDevice->BlockIo, + NamespaceId, + Ses, + Flbas + ); + + UT_ASSERT_NOT_EFI_ERROR (Status); + + // + // Case 2: Crypto Erase (Flbas = 0) + // + Ses = SES_CRYPTO_ERASE; + Flbas = 0; + Status = NvmExpressFormatNvm ( + &NvmeDevice->BlockIo, + NamespaceId, + Ses, + Flbas + ); + + UT_ASSERT_NOT_EFI_ERROR (Status); + + // + // Case 3: User Data Erase (Invalid Flbas = 3) + // + Ses = SES_USER_DATA_ERASE; + Flbas = 3; + Status = NvmExpressFormatNvm ( + &NvmeDevice->BlockIo, + NamespaceId, + Ses, + Flbas + ); + + UT_ASSERT_STATUS_EQUAL (Status, EFI_INVALID_PARAMETER); + + // + // Case 4: Invalid SES (Flba = 0) + // + Ses = 0xFF; + Flbas = 0; + Status = NvmExpressFormatNvm ( + &NvmeDevice->BlockIo, + NamespaceId, + Ses, + Flbas + ); + + UT_ASSERT_STATUS_EQUAL (Status, EFI_INVALID_PARAMETER); + + UnitTestStatus = NvmeDestroyDeviceInstance (&NvmeDevice); + + return UNIT_TEST_PASSED; +} + +/** + Baseline Unit Test. + + @param[in] Context Unit test case context + **/ +UNIT_TEST_STATUS +EFIAPI +UnitTestBaseline ( + IN UNIT_TEST_CONTEXT Context + ) +{ + UINT32 A; + UINT32 B; + UINT32 C; + + A = 1; + B = 1; + C = A + B; + + UT_ASSERT_EQUAL (C, 2); + UT_ASSERT_NOT_EQUAL (0, 1); + + return UNIT_TEST_PASSED; +} + +/** + Test Case that locks a variable using the Variable Policy Protocol with a + policy other than LOCK_NOW then attempts to lock the same variable using the + Variable Lock Protocol. The call to Variable Policy is expected to succeed + and the call to Variable Lock is expected to fail. + + @retval EFI_SUCCES Success + @retval Other Error + **/ +EFI_STATUS +EFIAPI +MediaSanitizeUnitTestEntry ( + VOID + ) +{ + EFI_STATUS Status; + UNIT_TEST_FRAMEWORK_HANDLE Framework; + UNIT_TEST_SUITE_HANDLE NvmeFormatNvmTestSuite; + UNIT_TEST_SUITE_HANDLE NvmeSanitizeTestSuite; + UNIT_TEST_SUITE_HANDLE MediaSanitizeProtocolTestSuite; + + Framework = NULL; + + #define UNIT_TEST_NAME "Media Sanitize Protocol Unit Test" + #define UNIT_TEST_VERSION "1.0" + + DEBUG ((DEBUG_INFO, "%a v%a\n", UNIT_TEST_NAME, UNIT_TEST_VERSION)); + + // + // Start setting up the test framework for running the tests. + // + Status = InitUnitTestFramework ( + &Framework, + UNIT_TEST_NAME, + gEfiCallerBaseName, + UNIT_TEST_VERSION + ); + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed in InitUnitTestFramework. Status = %r\n", Status)); + goto EXIT; + } + + // + // Populate the NVM Express Format NVM Unit Test Suite. + // + Status = CreateUnitTestSuite ( + &NvmeFormatNvmTestSuite, + Framework, + "NVM Express Format NVM Test Suite", + "Nvm.Express.Format.Nvm", + NULL, + NULL + ); + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed in CreateUnitTestSuite for NvmeFormatNvmTestSuite. Status = %r\n", Status)); + Status = EFI_OUT_OF_RESOURCES; + goto EXIT; + } + + // + // Add baseline sanity test case + // + AddTestCase ( + NvmeFormatNvmTestSuite, // Test Suite Handle + "Baseline Format NVM Unit Test", // Test Description + "FormatNVM", // Test Class + UnitTestBaseline, // UNIT_TEST_FUNCTION() + NULL, // (Optional) UNIT_TEST_PREREQUISITE() + NULL, // (Optional) UNIT_TEST_CLEANUP() + NULL // (Optional) UNIT_TEST_CONTEXT + ); + + // + // Add test case for NvmExpressFormatNvm() + // + AddTestCase ( + NvmeFormatNvmTestSuite, // Test Suite Handle + "Admin Format NVM Command Unit Test", // Test Description + "FormatNVM", // Test Class + NvmeFormatNvmUnitTest, // UNIT_TEST_FUNCTION() + NULL, // (Optional) UNIT_TEST_PREREQUISITE() + NULL, // (Optional) UNIT_TEST_CLEANUP() + NULL // (Optional) UNIT_TEST_CONTEXT + ); + + // + // Populate the NVM Express Sanitize Unit Test Suite. + // + Status = CreateUnitTestSuite ( + &NvmeSanitizeTestSuite, + Framework, + "NVM Express Sanitize Test Suite", + "Nvm.Express.Sanitize", + NULL, + NULL + ); + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed in CreateUnitTestSuite for NvmeSanitizTestSuite. Status = %r\n", Status)); + Status = EFI_OUT_OF_RESOURCES; + goto EXIT; + } + + // + // Add baseline sanity test + // + AddTestCase ( + NvmeSanitizeTestSuite, // Test Suite Handle + "Baseline Sanitize Unit Test", // Test Description + "Sanitize", // Test Class + UnitTestBaseline, // UNIT_TEST_FUNCTION() + NULL, // (Optional) UNIT_TEST_PREREQUISITE() + NULL, // (Optional) UNIT_TEST_CLEANUP() + NULL // (Optional) UNIT_TEST_CONTEXT + ); + + // + // Add test case for NvmExressSanitize() + // + AddTestCase ( + NvmeSanitizeTestSuite, // Test Suite Handle + "Admin Sanitize Command Unit Test", // Test Description + "Sanitize", // Test Class + NvmeSanitizeUnitTest, // UNIT_TEST_FUNCTION() + NULL, // (Optional) UNIT_TEST_PREREQUISITE() + NULL, // (Optional) UNIT_TEST_CLEANUP() + NULL // (Optional) UNIT_TEST_CONTEXT + ); + + // + // Populate the Media Sanitize Protocol Unit Test Suite. + // + Status = CreateUnitTestSuite ( + &MediaSanitizeProtocolTestSuite, + Framework, + "Media Sanitize Protocol Test Suite", + "Media.Sanitize.Protocol", + NULL, + NULL + ); + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed in CreateUnitTestSuite for MediaSanitizeProtocolTestSuite. Status = %r\n", Status)); + Status = EFI_OUT_OF_RESOURCES; + goto EXIT; + } + + // + // Add test case for Media Purge + // + AddTestCase ( + MediaSanitizeProtocolTestSuite, // Test Suite Handle + "Baseline MediaSanitize Unit Test", // Test Description + "MediaSanitize", // Test Class + UnitTestBaseline, // UNIT_TEST_FUNCTION() + NULL, // (Optional) UNIT_TEST_PREREQUISITE() + NULL, // (Optional) UNIT_TEST_CLEANUP() + NULL // (Optional) UNIT_TEST_CONTEXT + ); + + // + // Add test case for Media Purge + // + AddTestCase ( + MediaSanitizeProtocolTestSuite, // Test Suite Handle + "Protocol Media Sanitize Unit Test", // Test Description + "MediaPurge", // Test Class + MediaSanitizePurgeUnitTest, // UNIT_TEST_FUNCTION() + NULL, // (Optional) UNIT_TEST_PREREQUISITE() + NULL, // (Optional) UNIT_TEST_CLEANUP() + NULL // (Optional) UNIT_TEST_CONTEXT + ); + + // + // Execute the tests. + // + Status = RunAllTestSuites (Framework); + +EXIT: + if (Framework) { + FreeUnitTestFramework (Framework); + } + + return Status; +} + +/// +/// Avoid ECC error for function name that starts with lower case letter +/// +#define MediaSanitizeUnitTestMain main + +/** + Standard POSIX C entry point for host based unit test execution. + + @param[in] Argc Number of arguments + @param[in] Argv Array of pointers to arguments + + @retval 0 Success + @retval other Error +**/ +INT32 +MediaSanitizeUnitTestMain ( + IN INT32 Argc, + IN CHAR8 *Argv[] + ) +{ + return MediaSanitizeUnitTestEntry (); +} diff --git a/MdeModulePkg/Bus/Pci/NvmExpressDxe/UnitTest/MediaSanitizeUnitTestHost.inf b/MdeModulePkg/Bus/Pci/NvmExpressDxe/UnitTest/MediaSanitizeUnitTestHost.inf new file mode 100644 index 0000000000..feeeea340f --- /dev/null +++ b/MdeModulePkg/Bus/Pci/NvmExpressDxe/UnitTest/MediaSanitizeUnitTestHost.inf @@ -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.
+# 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 diff --git a/MdeModulePkg/Include/Protocol/MediaSanitize.h b/MdeModulePkg/Include/Protocol/MediaSanitize.h new file mode 100644 index 0000000000..029b13561f --- /dev/null +++ b/MdeModulePkg/Include/Protocol/MediaSanitize.h @@ -0,0 +1,173 @@ +/** @file + This file defines the Media Sanitize Protocol. + + Copyright (c) Microsoft Corporation.
+ 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 diff --git a/MdeModulePkg/MdeModulePkg.ci.yaml b/MdeModulePkg/MdeModulePkg.ci.yaml index a3de60a12c..34d8b7e409 100644 --- a/MdeModulePkg/MdeModulePkg.ci.yaml +++ b/MdeModulePkg/MdeModulePkg.ci.yaml @@ -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": [ diff --git a/MdeModulePkg/MdeModulePkg.dec b/MdeModulePkg/MdeModulePkg.dec index a25f380d02..1324b6d100 100644 --- a/MdeModulePkg/MdeModulePkg.dec +++ b/MdeModulePkg/MdeModulePkg.dec @@ -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 }} diff --git a/MdeModulePkg/Test/MdeModulePkgHostTest.dsc b/MdeModulePkg/Test/MdeModulePkgHostTest.dsc index 198cdd814f..5ee505349f 100644 --- a/MdeModulePkg/Test/MdeModulePkgHostTest.dsc +++ b/MdeModulePkg/Test/MdeModulePkgHostTest.dsc @@ -60,6 +60,11 @@ PeCoffGetEntryPointLib|MdePkg/Library/BasePeCoffGetEntryPointLib/BasePeCoffGetEntryPointLib.inf } + MdeModulePkg/Bus/Pci/NvmExpressDxe/UnitTest/MediaSanitizeUnitTestHost.inf { + + NvmExpressDxe|MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressDxe.inf + } + # # Build HOST_APPLICATION Libraries #