pkg/nvme: add NVMe package

This adds a NVMe package for performing various low-level operations on
NVMe devices. Only the most important (to us) calls are implemented as
NVMe has a vast API surface.

Change-Id: I532894c3c2eb780309993a1688226c92c91cdedf
Reviewed-on: https://review.monogon.dev/c/monogon/+/999
Reviewed-by: Mateusz Zalega <mateusz@monogon.tech>
Tested-by: Jenkins CI
diff --git a/metropolis/pkg/nvme/format.go b/metropolis/pkg/nvme/format.go
new file mode 100644
index 0000000..8bde44a
--- /dev/null
+++ b/metropolis/pkg/nvme/format.go
@@ -0,0 +1,75 @@
+package nvme
+
+// SecureEraseType specifices what type of secure erase should be performed by
+// by the controller. The zero value requests no secure erase.
+type SecureEraseType uint8
+
+const (
+	// SecureEraseTypeNone specifies that no secure erase operation is
+	// requested.
+	SecureEraseTypeNone SecureEraseType = 0
+	// SecureEraseTypeUserData specifies that all user data should be securely
+	// erased. The controller is allowed to perform a cryptographic erase
+	// instead.
+	SecureEraseTypeUserData SecureEraseType = 1
+	// SecureEraseTypeCryptographic specifies that the encryption key for user
+	// data should be erased. This in turn causes all current user data to
+	// become unreadable.
+	SecureEraseTypeCryptographic SecureEraseType = 2
+)
+
+// ProtectionInformationType selects the type of end-to-end protection tags to
+// use. NVMe supports the same types as T10 DIF (SCSI).
+type ProtectionInformationType uint8
+
+const (
+	ProtectionInformationTypeNone ProtectionInformationType = 0
+	ProtectionInformationType1    ProtectionInformationType = 1
+	ProtectionInformationType2    ProtectionInformationType = 2
+	ProtectionInformationType3    ProtectionInformationType = 3
+)
+
+type FormatRequest struct {
+	// NamespaceID contains the ID of the namespace to format.
+	// NamespaceGlobal formats all namespaces.
+	NamespaceID uint32
+	// SecureEraseSettings specifies the type of secure erase to perform.
+	SecureEraseSettings SecureEraseType
+	// ProtectionInformationLocation selects where protection information is
+	// transmitted. If true, it is transmitted as the first 8 bytes of metadata.
+	// If false, it is transmitted as the last 8 bytes of metadata.
+	ProtectionInformationLocation bool
+	// ProtectionInformation specifies the type of T10 DIF Protection
+	// Information to use.
+	ProtectionInformation ProtectionInformationType
+	// MetadataInline selects whether metadata is transferred as part of an
+	// extended data LBA. If false, metadata is returned in a separate buffer.
+	// If true, metadata is appended to the data buffer.
+	MetadataInline bool
+	// LBAFormat specifies the LBA format to use. This needs to be selected
+	// from the list of supported LBA formats in the Identify response.
+	LBAFormat uint8
+}
+
+// Format performs a low-level format of the NVM media. This is used for
+// changing the block and/or metadata size. This command causes all data
+// on the specified namespace to be lost. By setting SecureEraseSettings
+// to the appropriate value it can also be used to securely erase data.
+// See also the Sanitize command for just wiping the device.
+func (d *Device) Format(req *FormatRequest) error {
+	var cdw10 uint32
+	cdw10 |= uint32(req.SecureEraseSettings&0x7) << 9
+	cdw10 |= uint32(req.ProtectionInformation&0x7) << 5
+	cdw10 |= uint32(req.LBAFormat & 0x7)
+	if req.ProtectionInformationLocation {
+		cdw10 |= 1 << 8
+	}
+	if req.MetadataInline {
+		cdw10 |= 1 << 4
+	}
+	return d.RawCommand(&Command{
+		Opcode:      0x80,
+		NamespaceID: req.NamespaceID,
+		CDW10:       cdw10,
+	})
+}