m/p/scsi: add SCSI package

This adds a SCSI package to interact with SCSI devices.
It implements a subset of commands from the SPC-5 and SBC-4 standard
useful for discovery and health assessment.
A follow-up will add SAT (SCSI-to-ATA translation) support.

Change-Id: I7f084d26f11d9c951f51051040160e351cf5594c
Reviewed-on: https://review.monogon.dev/c/monogon/+/1066
Reviewed-by: Serge Bazanski <serge@monogon.tech>
Tested-by: Jenkins CI
diff --git a/metropolis/pkg/scsi/BUILD.bazel b/metropolis/pkg/scsi/BUILD.bazel
new file mode 100644
index 0000000..f307a97
--- /dev/null
+++ b/metropolis/pkg/scsi/BUILD.bazel
@@ -0,0 +1,28 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_library")
+
+go_library(
+    name = "scsi",
+    srcs = [
+        "dev_block.go",
+        "health.go",
+        "inquiry.go",
+        "log.go",
+        "scsi.go",
+        "scsi_linux.go",
+        "scsi_linux_defs.go",
+        "scsi_linux_defs1.go",
+        "sensekeydata.go",
+    ],
+    cgo = True,
+    importpath = "source.monogon.dev/metropolis/pkg/scsi",
+    visibility = ["//visibility:public"],
+    deps = select({
+        "@io_bazel_rules_go//go/platform:android": [
+            "@org_golang_x_sys//unix",
+        ],
+        "@io_bazel_rules_go//go/platform:linux": [
+            "@org_golang_x_sys//unix",
+        ],
+        "//conditions:default": [],
+    }),
+)
diff --git a/metropolis/pkg/scsi/dev_block.go b/metropolis/pkg/scsi/dev_block.go
new file mode 100644
index 0000000..1941d83
--- /dev/null
+++ b/metropolis/pkg/scsi/dev_block.go
@@ -0,0 +1,155 @@
+package scsi
+
+// Written against SBC-4
+// Contains SCSI block device specific commands.
+
+import (
+	"bytes"
+	"encoding/binary"
+	"errors"
+	"fmt"
+	"math"
+)
+
+// ReadDefectDataLBA reads the primary (manufacturer) and/or grown defect list
+// in LBA format. This is commonly used on SSDs and generally returns an error
+// on spinning drives.
+func (d *Device) ReadDefectDataLBA(plist, glist bool) ([]uint64, error) {
+	data := make([]byte, 4096)
+	var req [8]byte
+	if plist {
+		req[1] |= 1 << 4
+	}
+	if glist {
+		req[1] |= 1 << 3
+	}
+	defectListFormat := 0b011
+	req[1] |= byte(defectListFormat)
+	binary.BigEndian.PutUint16(req[6:8], uint16(len(data)))
+	if err := d.RawCommand(&CommandDataBuffer{
+		OperationCode:         ReadDefectDataOp,
+		Request:               req[:],
+		Data:                  data,
+		DataTransferDirection: DataTransferFromDevice,
+	}); err != nil {
+		var fixedErr *FixedError
+		if errors.As(err, &fixedErr) && fixedErr.SenseKey == RecoveredError && fixedErr.AdditionalSenseCode == DefectListNotFound {
+			return nil, fmt.Errorf("error during LOG SENSE: unsupported defect list format, device returned %03bb", data[1]&0b111)
+		}
+		return nil, fmt.Errorf("error during LOG SENSE: %w", err)
+	}
+	if data[1]&0b111 != byte(defectListFormat) {
+		return nil, fmt.Errorf("device returned wrong defect list format, requested %03bb, got %03bb", defectListFormat, data[1]&0b111)
+	}
+	defectListLength := binary.BigEndian.Uint16(data[2:4])
+	if defectListLength%8 != 0 {
+		return nil, errors.New("returned defect list not divisible by array item size")
+	}
+	res := make([]uint64, defectListLength/8)
+	if err := binary.Read(bytes.NewReader(data[4:]), binary.BigEndian, &res); err != nil {
+		panic(err)
+	}
+	return res, nil
+}
+
+const (
+	// AllSectors is a magic sector number indicating that it applies to all
+	// sectors on the track.
+	AllSectors = math.MaxUint16
+)
+
+// PhysicalSectorFormatAddress represents a physical sector (or the the whole
+// track if SectorNumber == AllSectors) on a spinning hard drive.
+type PhysicalSectorFormatAddress struct {
+	CylinderNumber              uint32
+	HeadNumber                  uint8
+	SectorNumber                uint32
+	MultiAddressDescriptorStart bool
+}
+
+func parseExtendedPhysicalSectorFormatAddress(buf []byte) (p PhysicalSectorFormatAddress) {
+	p.CylinderNumber = uint32(buf[0])<<16 | uint32(buf[1])<<8 | uint32(buf[2])
+	p.HeadNumber = buf[3]
+	p.MultiAddressDescriptorStart = buf[4]&(1<<7) != 0
+	p.SectorNumber = uint32(buf[4]&0b1111)<<24 | uint32(buf[5])<<16 | uint32(buf[6])<<8 | uint32(buf[7])
+	return
+}
+
+func parsePhysicalSectorFormatAddress(buf []byte) (p PhysicalSectorFormatAddress) {
+	p.CylinderNumber = uint32(buf[0])<<16 | uint32(buf[1])<<8 | uint32(buf[2])
+	p.HeadNumber = buf[3]
+	p.SectorNumber = binary.BigEndian.Uint32(buf[4:8])
+	return
+}
+
+// ReadDefectDataPhysical reads the primary (manufacturer) and/or grown defect
+// list in physical format.
+// This is only defined for spinning drives, returning an error on SSDs.
+func (d *Device) ReadDefectDataPhysical(plist, glist bool) ([]PhysicalSectorFormatAddress, error) {
+	data := make([]byte, 4096)
+	var req [8]byte
+	if plist {
+		req[1] |= 1 << 4
+	}
+	if glist {
+		req[1] |= 1 << 3
+	}
+	defectListFormat := 0b101
+	req[1] |= byte(defectListFormat)
+	binary.BigEndian.PutUint16(req[6:8], uint16(len(data)))
+	if err := d.RawCommand(&CommandDataBuffer{
+		OperationCode:         ReadDefectDataOp,
+		Request:               req[:],
+		Data:                  data,
+		DataTransferDirection: DataTransferFromDevice,
+	}); err != nil {
+		var fixedErr *FixedError
+		if errors.As(err, &fixedErr) && fixedErr.SenseKey == RecoveredError && fixedErr.AdditionalSenseCode == DefectListNotFound {
+			return nil, fmt.Errorf("error during LOG SENSE: unsupported defect list format, device returned %03bb", data[1]&0b111)
+		}
+		return nil, fmt.Errorf("error during LOG SENSE: %w", err)
+	}
+	if data[1]&0b111 != byte(defectListFormat) {
+		return nil, fmt.Errorf("device returned wrong defect list format, requested %03bb, got %03bb", defectListFormat, data[1]&0b111)
+	}
+	defectListLength := binary.BigEndian.Uint16(data[2:4])
+	if defectListLength%8 != 0 {
+		return nil, errors.New("returned defect list not divisible by array item size")
+	}
+	if len(data) < int(defectListLength)+4 {
+		return nil, errors.New("returned defect list longer than buffer")
+	}
+	res := make([]PhysicalSectorFormatAddress, defectListLength/8)
+	data = data[4:]
+	for i := 0; i < int(defectListLength)/8; i++ {
+		res[i] = parsePhysicalSectorFormatAddress(data[i*8 : (i+1)*8])
+	}
+	return res, nil
+}
+
+type SolidStateMediaHealth struct {
+	// PercentageUsedEnduranceIndicator is a value which represents a
+	// vendor-specific wear estimate of the solid state medium.
+	// A new device starts at 0, at 100 the device is considered end-of-life.
+	// Values up to 255 are possible.
+	PercentageUsedEnduranceIndicator uint8
+}
+
+// SolidStateMediaHealth reports parameters about the health of the solid-state
+// media of a SCSI block device.
+func (d *Device) SolidStateMediaHealth() (*SolidStateMediaHealth, error) {
+	raw, err := d.LogSenseParameters(LogSenseRequest{PageCode: 0x11})
+	if err != nil {
+		return nil, err
+	}
+	if len(raw[0x1]) == 0 {
+		return nil, errors.New("mandatory parameter 0001h missing")
+	}
+	param1 := raw[0x01][0]
+	if len(param1.Data) < 4 {
+		return nil, errors.New("parameter 0001h too short")
+	}
+	return &SolidStateMediaHealth{
+		PercentageUsedEnduranceIndicator: param1.Data[3],
+	}, nil
+}
diff --git a/metropolis/pkg/scsi/health.go b/metropolis/pkg/scsi/health.go
new file mode 100644
index 0000000..724fd75
--- /dev/null
+++ b/metropolis/pkg/scsi/health.go
@@ -0,0 +1,26 @@
+package scsi
+
+import "errors"
+
+type InformationalExceptions struct {
+	InformationalSenseCode AdditionalSenseCode
+	Temperature            uint8
+}
+
+func (d *Device) GetInformationalExceptions() (*InformationalExceptions, error) {
+	raw, err := d.LogSenseParameters(LogSenseRequest{PageCode: 0x0b})
+	if err != nil {
+		return nil, err
+	}
+	if len(raw[0x1]) == 0 {
+		return nil, errors.New("mandatory parameter 0001h missing")
+	}
+	param1 := raw[0x01][0]
+	if len(param1.Data) < 3 {
+		return nil, errors.New("parameter 0001h too short")
+	}
+	return &InformationalExceptions{
+		InformationalSenseCode: AdditionalSenseCode(uint16(param1.Data[0])<<8 | uint16(param1.Data[1])),
+		Temperature:            param1.Data[2],
+	}, nil
+}
diff --git a/metropolis/pkg/scsi/inquiry.go b/metropolis/pkg/scsi/inquiry.go
new file mode 100644
index 0000000..45c1603
--- /dev/null
+++ b/metropolis/pkg/scsi/inquiry.go
@@ -0,0 +1,299 @@
+package scsi
+
+import (
+	"bytes"
+	"encoding/binary"
+	"fmt"
+	"io"
+	"math"
+)
+
+// Inquiry queries the device for various metadata about its identity and
+// supported features.
+func (d Device) Inquiry() (*InquiryData, error) {
+	data := make([]byte, 96)
+	var req [4]byte
+	binary.BigEndian.PutUint16(req[2:4], uint16(len(data)))
+	if err := d.RawCommand(&CommandDataBuffer{
+		OperationCode:         InquiryOp,
+		Request:               req[:],
+		Data:                  data,
+		DataTransferDirection: DataTransferFromDevice,
+	}); err != nil {
+		return nil, fmt.Errorf("error during INQUIRY: %w", err)
+	}
+	resLen := int64(data[4]) + 5
+	// Use LimitReader to not have to deal with out-of-bounds slices
+	rawReader := io.LimitReader(bytes.NewReader(data), resLen)
+	var raw inquiryDataRaw
+	if err := binary.Read(rawReader, binary.BigEndian, &raw); err != nil {
+		if err == io.ErrUnexpectedEOF {
+			return nil, fmt.Errorf("response to INQUIRY is smaller than %d bytes, very old or broken device", binary.Size(raw))
+		}
+		panic(err) // Read from memory, shouldn't be possible to hit
+	}
+
+	var res InquiryData
+	res.PeriperalQualifier = (raw.PeripheralData >> 5) & 0b111
+	res.PeripheralDeviceType = DeviceType(raw.PeripheralData & 0b11111)
+	res.RemovableMedium = (raw.Flags1 & 1 << 0) != 0
+	res.LogicalUnitConglomerate = (raw.Flags1 & 1 << 1) != 0
+	res.CommandSetVersion = Version(raw.Version)
+	res.NormalACASupported = (raw.Flags2 & 1 << 5) != 0
+	res.HistoricalSupport = (raw.Flags2 & 1 << 4) != 0
+	res.ResponseDataFormat = raw.Flags2 & 0b1111
+	res.SCCSupported = (raw.Flags3 & 1 << 7) != 0
+	res.TargetPortGroupSupport = (raw.Flags3 >> 4) & 0b11
+	res.ThirdPartyCopySupport = (raw.Flags3 & 1 << 3) != 0
+	res.HasProtectionInfo = (raw.Flags3 & 1 << 0) != 0
+	res.HasEnclosureServices = (raw.Flags4 & 1 << 6) != 0
+	res.VendorFeature1 = (raw.Flags4 & 1 << 5) != 0
+	res.HasMultipleSCSIPorts = (raw.Flags4 & 1 << 4) != 0
+	res.CmdQueue = (raw.Flags5 & 1 << 1) != 0
+	res.VendorFeature2 = (raw.Flags5 & 1 << 0) != 0
+	res.Vendor = string(bytes.TrimRight(raw.Vendor[:], " "))
+	res.Product = string(bytes.TrimRight(raw.Product[:], " "))
+	res.ProductRevisionLevel = string(bytes.TrimRight(raw.ProductRevisionLevel[:], " "))
+
+	// Read rest conditionally, as it might not be present on every device
+	var vendorSpecific bytes.Buffer
+	_, err := io.CopyN(&vendorSpecific, rawReader, 20)
+	res.VendorSpecific = vendorSpecific.Bytes()
+	if err == io.EOF {
+		return &res, nil
+	}
+	if err != nil {
+		panic(err) // Mem2Mem copy, can't really happen
+	}
+	var padding [2]byte
+	if _, err := io.ReadFull(rawReader, padding[:]); err != nil {
+		if err == io.ErrUnexpectedEOF {
+			return &res, nil
+		}
+	}
+	for i := 0; i < 8; i++ {
+		var versionDesc uint16
+		if err := binary.Read(rawReader, binary.BigEndian, &versionDesc); err != nil {
+			if err == io.EOF || err == io.ErrUnexpectedEOF {
+				return &res, nil
+			}
+		}
+		res.VersionDescriptors = append(res.VersionDescriptors, versionDesc)
+	}
+
+	return &res, nil
+}
+
+// Table 148, only first 36 mandatory bytes
+type inquiryDataRaw struct {
+	PeripheralData       uint8
+	Flags1               uint8
+	Version              uint8
+	Flags2               uint8
+	AdditionalLength     uint8 // n-4
+	Flags3               uint8
+	Flags4               uint8
+	Flags5               uint8
+	Vendor               [8]byte
+	Product              [16]byte
+	ProductRevisionLevel [4]byte
+}
+
+// DeviceType represents a SCSI peripheral device type, which
+// can be used to determine the command set to use to control
+// the device. See Table 150 in the standard.
+type DeviceType uint8
+
+const (
+	TypeBlockDevice              DeviceType = 0x00
+	TypeSequentialAccessDevice   DeviceType = 0x01
+	TypeProcessor                DeviceType = 0x03
+	TypeOpticalDrive             DeviceType = 0x05
+	TypeOpticalMemory            DeviceType = 0x07
+	TypeMediaChanger             DeviceType = 0x08
+	TypeArrayController          DeviceType = 0x0c
+	TypeEncloseServices          DeviceType = 0x0d
+	TypeOpticalCardRWDevice      DeviceType = 0x0f
+	TypeObjectStorageDevice      DeviceType = 0x11
+	TypeAutomationDriveInterface DeviceType = 0x12
+	TypeZonedBlockDevice         DeviceType = 0x14
+	TypeUnknownDevice            DeviceType = 0x1f
+)
+
+var deviceTypeDesc = map[DeviceType]string{
+	TypeBlockDevice:              "Block Device",
+	TypeSequentialAccessDevice:   "Sequential Access Device",
+	TypeProcessor:                "Processor",
+	TypeOpticalDrive:             "Optical Drive",
+	TypeOpticalMemory:            "Optical Memory",
+	TypeMediaChanger:             "Media Changer",
+	TypeArrayController:          "Array Controller",
+	TypeEncloseServices:          "Enclosure Services",
+	TypeOpticalCardRWDevice:      "Optical Card reader/writer device",
+	TypeObjectStorageDevice:      "Object-based Storage Device",
+	TypeAutomationDriveInterface: "Automation/Drive Interface",
+	TypeZonedBlockDevice:         "Zoned Block Device",
+	TypeUnknownDevice:            "Unknown or no device",
+}
+
+func (d DeviceType) String() string {
+	if str, ok := deviceTypeDesc[d]; ok {
+		return str
+	}
+	return fmt.Sprintf("unknown device type %xh", uint8(d))
+}
+
+// Version represents a specific standardized version of the SCSI
+// primary command set (SPC). The enum values are sorted, so
+// for example version >= SPC3 is true for SPC-3 and all later
+// standards. See table 151 in the standard.
+type Version uint8
+
+const (
+	SPC1 = 0x03
+	SPC2 = 0x04
+	SPC3 = 0x05
+	SPC4 = 0x06
+	SPC5 = 0x07
+)
+
+var versionDesc = map[Version]string{
+	SPC1: "SPC-1 (INCITS 301-1997)",
+	SPC2: "SPC-2 (INCITS 351-2001)",
+	SPC3: "SPC-3 (INCITS 408-2005)",
+	SPC4: "SPC-4 (INCITS 513-2015)",
+	SPC5: "SPC-5 (INCITS 502-2019)",
+}
+
+func (v Version) String() string {
+	if str, ok := versionDesc[v]; ok {
+		return str
+	}
+	return fmt.Sprintf("unknown version %xh", uint8(v))
+}
+
+// InquiryData contains data returned by the INQUIRY command.
+type InquiryData struct {
+	PeriperalQualifier      uint8
+	PeripheralDeviceType    DeviceType
+	RemovableMedium         bool
+	LogicalUnitConglomerate bool
+	CommandSetVersion       Version
+	NormalACASupported      bool
+	HistoricalSupport       bool
+	ResponseDataFormat      uint8
+	SCCSupported            bool
+	TargetPortGroupSupport  uint8
+	ThirdPartyCopySupport   bool
+	HasProtectionInfo       bool
+	HasEnclosureServices    bool
+	VendorFeature1          bool
+	HasMultipleSCSIPorts    bool
+	CmdQueue                bool
+	VendorFeature2          bool
+	Vendor                  string
+	Product                 string
+	ProductRevisionLevel    string
+	VendorSpecific          []byte
+	VersionDescriptors      []uint16
+}
+
+// Table 498
+type VPDPageCode uint8
+
+const (
+	SupportedVPDs                      VPDPageCode = 0x00
+	UnitSerialNumberVPD                VPDPageCode = 0x80
+	DeviceIdentificationVPD            VPDPageCode = 0x83
+	SoftwareInterfaceIdentificationVPD VPDPageCode = 0x84
+	ManagementNetworkAddressesVPD      VPDPageCode = 0x85
+	ExtendedINQUIRYDataVPD             VPDPageCode = 0x86
+	ModePagePolicyVPD                  VPDPageCode = 0x87
+	SCSIPortsVPD                       VPDPageCode = 0x88
+	ATAInformationVPD                  VPDPageCode = 0x89
+	PowerConditionVPD                  VPDPageCode = 0x8a
+	DeviceConstituentsVPD              VPDPageCode = 0x8b
+)
+
+var vpdPageCodeDesc = map[VPDPageCode]string{
+	SupportedVPDs:                      "Supported VPD Pages",
+	UnitSerialNumberVPD:                "Unit Serial Number",
+	DeviceIdentificationVPD:            "Device Identification",
+	SoftwareInterfaceIdentificationVPD: "Software Interface Identification",
+	ManagementNetworkAddressesVPD:      "Management Network Addresses",
+	ExtendedINQUIRYDataVPD:             "Extended INQUIRY Data",
+	ModePagePolicyVPD:                  "Mode Page Policy",
+	SCSIPortsVPD:                       "SCSI Ports",
+	ATAInformationVPD:                  "ATA Information",
+	PowerConditionVPD:                  "Power Condition",
+	DeviceConstituentsVPD:              "Device Constituents",
+}
+
+func (v VPDPageCode) String() string {
+	if str, ok := vpdPageCodeDesc[v]; ok {
+		return str
+	}
+	return fmt.Sprintf("Page %xh", uint8(v))
+}
+
+// InquiryVPD requests a specified Vital Product Description Page from the
+// device. If the size of the page is known in advance, initialSize should be
+// set to a non-zero value to make the query more efficient.
+func (d *Device) InquiryVPD(pageCode VPDPageCode, initialSize uint16) ([]byte, error) {
+	var bufferSize uint16 = 254
+	if initialSize > 0 {
+		bufferSize = initialSize
+	}
+	for {
+		data := make([]byte, bufferSize)
+		var req [4]byte
+		req[0] = 0b1 // Enable Vital Product Data
+		req[1] = uint8(pageCode)
+		binary.BigEndian.PutUint16(req[2:4], uint16(len(data)))
+		if err := d.RawCommand(&CommandDataBuffer{
+			OperationCode:         InquiryOp,
+			Request:               req[:],
+			Data:                  data,
+			DataTransferDirection: DataTransferFromDevice,
+		}); err != nil {
+			return nil, fmt.Errorf("error during INQUIRY VPD: %w", err)
+		}
+		if data[1] != uint8(pageCode) {
+			return nil, fmt.Errorf("requested VPD page %x, got %x", pageCode, data[1])
+		}
+		pageLength := binary.BigEndian.Uint16(data[2:4])
+		if pageLength > math.MaxUint16-4 {
+			// Guard against uint16 overflows, this cannot be requested anyway
+			return nil, fmt.Errorf("device VPD page is too long (%d bytes)", pageLength)
+		}
+		if pageLength > uint16(len(data)-4) {
+			bufferSize = pageLength + 4
+			continue
+		}
+		return data[4 : pageLength+4], nil
+	}
+}
+
+// SupportedVPDPages returns the list of supported vital product data pages
+// supported by the device.
+func (d *Device) SupportedVPDPages() (map[VPDPageCode]bool, error) {
+	res, err := d.InquiryVPD(SupportedVPDs, 0)
+	if err != nil {
+		return nil, err
+	}
+	supportedPages := make(map[VPDPageCode]bool)
+	for _, p := range res {
+		supportedPages[VPDPageCode(p)] = true
+	}
+	return supportedPages, nil
+}
+
+// UnitSerialNumber returns the serial number of the device. Only available if
+// UnitSerialNumberVPD is a supported VPD page.
+func (d *Device) UnitSerialNumber() (string, error) {
+	serial, err := d.InquiryVPD(UnitSerialNumberVPD, 0)
+	if err != nil {
+		return "", err
+	}
+	return string(bytes.Trim(serial, " \x00")), nil
+}
diff --git a/metropolis/pkg/scsi/log.go b/metropolis/pkg/scsi/log.go
new file mode 100644
index 0000000..3aecd5e
--- /dev/null
+++ b/metropolis/pkg/scsi/log.go
@@ -0,0 +1,174 @@
+package scsi
+
+import (
+	"encoding/binary"
+	"errors"
+	"fmt"
+	"math"
+)
+
+type LogSenseRequest struct {
+	// PageCode contains the identifier of the requested page
+	PageCode uint8
+	// SubpageCode contains the identifier of the requested subpage
+	// or the zero value if no subpage is requested.
+	SubpageCode uint8
+	// PageControl specifies what type of values should be returned for bounded
+	// and unbounded log parameters. See also Table 156 in the standard.
+	PageControl uint8
+	// ParameterPointer allows requesting parameter data beginning from a
+	// specific parameter code. The zero value starts from the beginning.
+	ParameterPointer uint16
+	// SaveParameters requests the device to save all parameters without
+	// DisableUpdate set to non-volatile storage.
+	SaveParameters bool
+	// InitialSize is an optional hint how big the buffer for the log page
+	// should be for the initial request. The zero value sets this to 4096.
+	InitialSize uint16
+}
+
+// LogSenseRaw requests a raw log page. For log pages with parameters
+// LogSenseParameters is better-suited.
+func (d *Device) LogSenseRaw(r LogSenseRequest) ([]byte, error) {
+	var bufferSize uint16 = 4096
+	for {
+		data := make([]byte, bufferSize)
+		var req [8]byte
+		if r.SaveParameters {
+			req[0] = 0b1
+		}
+		req[1] = r.PageControl<<6 | r.PageCode
+		req[2] = r.SubpageCode
+		binary.BigEndian.PutUint16(req[4:6], r.ParameterPointer)
+		binary.BigEndian.PutUint16(req[6:8], uint16(len(data)))
+		if err := d.RawCommand(&CommandDataBuffer{
+			OperationCode:         LogSenseOp,
+			Request:               req[:],
+			Data:                  data,
+			DataTransferDirection: DataTransferFromDevice,
+		}); err != nil {
+			return nil, fmt.Errorf("error during LOG SENSE: %w", err)
+		}
+		if data[0]&0b111111 != r.PageCode {
+			return nil, fmt.Errorf("requested log page %x, got %x", r.PageCode, data[1])
+		}
+		if data[1] != r.SubpageCode {
+			return nil, fmt.Errorf("requested log subpage %x, got %x", r.SubpageCode, data[1])
+		}
+		pageLength := binary.BigEndian.Uint16(data[2:4])
+		if pageLength > math.MaxUint16-4 {
+			// Guard against uint16 overflows, this cannot be requested anyways
+			return nil, fmt.Errorf("device log page is too long (%d bytes)", pageLength)
+		}
+		if pageLength > uint16(len(data)-4) {
+			bufferSize = pageLength + 4
+			continue
+		}
+		return data[4 : pageLength+4], nil
+	}
+}
+
+// SupportedLogPages returns a map with all supported log pages.
+// This can return an error if the device does not support logs at all.
+func (d *Device) SupportedLogPages() (map[uint8]bool, error) {
+	raw, err := d.LogSenseRaw(LogSenseRequest{PageCode: 0})
+	if err != nil {
+		return nil, err
+	}
+	res := make(map[uint8]bool)
+	for _, r := range raw {
+		res[r] = true
+	}
+	return res, nil
+}
+
+// PageAndSubpage identifies a log page uniquely.
+type PageAndSubpage uint16
+
+func NewPageAndSubpage(page, subpage uint8) PageAndSubpage {
+	return PageAndSubpage(uint16(page)<<8 | uint16(subpage))
+}
+
+func (p PageAndSubpage) Page() uint8 {
+	return uint8(p >> 8)
+}
+func (p PageAndSubpage) Subpage() uint8 {
+	return uint8(p & 0xFF)
+}
+
+func (p PageAndSubpage) String() string {
+	return fmt.Sprintf("Page %xh Subpage %xh", p.Page(), p.Subpage())
+}
+
+// SupportedLogPagesAndSubpages returns the list of supported pages and subpages.
+// This can return an error if the device does not support logs at all.
+func (d *Device) SupportedLogPagesAndSubpages() (map[PageAndSubpage]bool, error) {
+	raw, err := d.LogSenseRaw(LogSenseRequest{PageCode: 0x00, SubpageCode: 0xff})
+	if err != nil {
+		return nil, err
+	}
+	res := make(map[PageAndSubpage]bool)
+	for i := 0; i < len(raw)/2; i++ {
+		res[NewPageAndSubpage(raw[i*2], raw[(i*2)+1])] = true
+	}
+	return res, nil
+}
+
+// SupportedLogSubPages returns the list of subpages supported in a log page.
+func (d *Device) SupportedLogSubPages(pageCode uint8) (map[uint8]bool, error) {
+	raw, err := d.LogSenseRaw(LogSenseRequest{PageCode: pageCode, SubpageCode: 0xff})
+	if err != nil {
+		return nil, err
+	}
+	res := make(map[uint8]bool)
+	for _, r := range raw {
+		res[r] = true
+	}
+	return res, nil
+}
+
+type LogParameter struct {
+	// DisableUpdate indicates if the device is updating this parameter.
+	// If this is true the parameter has either overflown or updating has been
+	// manually disabled.
+	DisableUpdate bool
+	// TargetSaveDisable indicates if automatic saving of this parameter has
+	// been disabled.
+	TargetSaveDisable bool
+	// FormatAndLinking contains the format of the log parameter.
+	FormatAndLinking uint8
+	// Data contains the payload of the log parameter.
+	Data []byte
+}
+
+// LogSenseParameters returns the parameters of a log page. The returned map
+// contains one entry per parameter ID in the result. The order of parameters
+// of the same ID is kept.
+func (d *Device) LogSenseParameters(r LogSenseRequest) (map[uint16][]LogParameter, error) {
+	raw, err := d.LogSenseRaw(r)
+	if err != nil {
+		return nil, err
+	}
+	res := make(map[uint16][]LogParameter)
+	for {
+		if len(raw) == 0 {
+			break
+		}
+		if len(raw) < 4 {
+			return nil, errors.New("not enough data left to read full parameter metadata")
+		}
+		var param LogParameter
+		parameterCode := binary.BigEndian.Uint16(raw[0:2])
+		param.DisableUpdate = raw[2]&(1<<7) != 0
+		param.TargetSaveDisable = raw[2]&(1<<5) != 0
+		param.FormatAndLinking = raw[2] & 0b11
+		if int(raw[3]) > len(raw)-4 {
+			fmt.Println(raw[3], len(raw))
+			return nil, errors.New("unable to read parameter, not enough data for indicated length")
+		}
+		param.Data = raw[4 : int(raw[3])+4]
+		raw = raw[int(raw[3])+4:]
+		res[parameterCode] = append(res[parameterCode], param)
+	}
+	return res, nil
+}
diff --git a/metropolis/pkg/scsi/scsi.go b/metropolis/pkg/scsi/scsi.go
new file mode 100644
index 0000000..e2d236e
--- /dev/null
+++ b/metropolis/pkg/scsi/scsi.go
@@ -0,0 +1,256 @@
+// INCITS 502 Revision 19 / SPC-5 R19
+package scsi
+
+import (
+	"errors"
+	"fmt"
+	"os"
+	"syscall"
+	"time"
+)
+
+// Device is a handle for a SCSI device
+type Device struct {
+	fd syscall.Conn
+}
+
+// NewFromFd creates a new SCSI device handle from a system handle.
+func NewFromFd(fd syscall.Conn) (*Device, error) {
+	d := &Device{fd: fd}
+	// There is no good way to validate that a file descriptor indeed points to
+	// a SCSI device. For future compatibility let this return an error so that
+	// code is already prepared to handle it.
+	return d, nil
+}
+
+// Open creates a new SCSI device handle from a device path (like /dev/sda).
+func Open(path string) (*Device, error) {
+	f, err := os.Open(path)
+	if err != nil {
+		return nil, fmt.Errorf("unable to open path: %w", err)
+	}
+	return NewFromFd(f)
+}
+
+// Close closes the SCSI device handle if opened by Open()
+func (d *Device) Close() error {
+	if f, ok := d.fd.(*os.File); ok {
+		return f.Close()
+	} else {
+		return errors.New("unable to close device not opened via Open, please close it yourself")
+	}
+}
+
+type DataTransferDirection uint8
+
+const (
+	DataTransferNone DataTransferDirection = iota
+	DataTransferToDevice
+	DataTransferFromDevice
+	DataTransferBidirectional
+)
+
+type OperationCode uint8
+
+const (
+	InquiryOp        OperationCode = 0x12
+	ReadDefectDataOp OperationCode = 0x37
+	LogSenseOp       OperationCode = 0x4d
+)
+
+// CommandDataBuffer represents a command
+type CommandDataBuffer struct {
+	// OperationCode contains the code of the command to be called
+	OperationCode OperationCode
+	// Request contains the OperationCode-specific request parameters
+	Request []byte
+	// ServiceAction can (for certain CDB encodings) contain an additional
+	// qualification for the OperationCode.
+	ServiceAction *uint8
+	// Control contains common CDB metadata
+	Control uint8
+	// DataTransferDirection contains the direction(s) of the data transfer(s)
+	// to be made.
+	DataTransferDirection DataTransferDirection
+	// Data contains the data to be transferred. If data needs to be received
+	// from the device, a buffer needs to be provided here.
+	Data []byte
+	// Timeout can contain an optional timeout (0 = no timeout) for the command
+	Timeout time.Duration
+}
+
+// Bytes returns the raw CDB to be sent to the device
+func (c *CommandDataBuffer) Bytes() ([]byte, error) {
+	// Table 24
+	switch {
+	case c.OperationCode < 0x20:
+		// Use CDB6 as defined in Table 3
+		if c.ServiceAction != nil {
+			return nil, errors.New("ServiceAction field not available in CDB6")
+		}
+		if len(c.Request) != 4 {
+			return nil, fmt.Errorf("CDB6 request size is %d bytes, needs to be 4 bytes without LengthField", len(c.Request))
+		}
+
+		outBuf := make([]byte, 6)
+		outBuf[0] = uint8(c.OperationCode)
+
+		copy(outBuf[1:5], c.Request)
+		outBuf[5] = c.Control
+		return outBuf, nil
+	case c.OperationCode < 0x60:
+		// Use CDB10 as defined in Table 5
+		if len(c.Request) != 8 {
+			return nil, fmt.Errorf("CDB10 request size is %d bytes, needs to be 4 bytes", len(c.Request))
+		}
+
+		outBuf := make([]byte, 10)
+		outBuf[0] = uint8(c.OperationCode)
+		copy(outBuf[1:9], c.Request)
+		if c.ServiceAction != nil {
+			outBuf[1] |= *c.ServiceAction & 0b11111
+		}
+		outBuf[9] = c.Control
+		return outBuf, nil
+	case c.OperationCode < 0x7e:
+		return nil, errors.New("OperationCode is reserved")
+	case c.OperationCode == 0x7e:
+		// Use variable extended
+		return nil, errors.New("variable extended CDBs are unimplemented")
+	case c.OperationCode == 0x7f:
+		// Use variable
+		return nil, errors.New("variable CDBs are unimplemented")
+	case c.OperationCode < 0xa0:
+		// Use CDB16 as defined in Table 13
+		if len(c.Request) != 14 {
+			return nil, fmt.Errorf("CDB16 request size is %d bytes, needs to be 14 bytes", len(c.Request))
+		}
+
+		outBuf := make([]byte, 16)
+		outBuf[0] = uint8(c.OperationCode)
+		copy(outBuf[1:15], c.Request)
+		if c.ServiceAction != nil {
+			outBuf[1] |= *c.ServiceAction & 0b11111
+		}
+		outBuf[15] = c.Control
+		return outBuf, nil
+	case c.OperationCode < 0xc0:
+		// Use CDB12 as defined in Table 7
+		if len(c.Request) != 10 {
+			return nil, fmt.Errorf("CDB12 request size is %d bytes, needs to be 10 bytes", len(c.Request))
+		}
+
+		outBuf := make([]byte, 12)
+		outBuf[0] = uint8(c.OperationCode)
+		copy(outBuf[1:11], c.Request)
+		if c.ServiceAction != nil {
+			outBuf[1] |= *c.ServiceAction & 0b11111
+		}
+		outBuf[11] = c.Control
+		return outBuf, nil
+	default:
+		return nil, errors.New("unable to encode CDB for given OperationCode")
+	}
+}
+
+// SenseKey represents the top-level status code of a SCSI sense response.
+type SenseKey uint8
+
+const (
+	NoSense        SenseKey = 0x0
+	RecoveredError SenseKey = 0x1
+	NotReady       SenseKey = 0x2
+	MediumError    SenseKey = 0x3
+	HardwareError  SenseKey = 0x4
+	IllegalRequest SenseKey = 0x5
+	UnitAttention  SenseKey = 0x6
+	DataProtect    SenseKey = 0x7
+	BlankCheck     SenseKey = 0x8
+	VendorSpecific SenseKey = 0x9
+	CopyAborted    SenseKey = 0xa
+	AbortedCommand SenseKey = 0xb
+	VolumeOverflow SenseKey = 0xd
+	Miscompare     SenseKey = 0xe
+	Completed      SenseKey = 0xf
+)
+
+var senseKeyDesc = map[SenseKey]string{
+	NoSense:        "no sense information",
+	RecoveredError: "recovered error",
+	NotReady:       "not ready",
+	MediumError:    "medium error",
+	HardwareError:  "hardware error",
+	IllegalRequest: "illegal request",
+	UnitAttention:  "unit attention",
+	DataProtect:    "data protected",
+	BlankCheck:     "blank check failed",
+	VendorSpecific: "vendor-specific error",
+	CopyAborted:    "third-party copy aborted",
+	AbortedCommand: "command aborted",
+	VolumeOverflow: "volume overflow",
+	Miscompare:     "miscompare",
+	Completed:      "completed",
+}
+
+func (s SenseKey) String() string {
+	if str, ok := senseKeyDesc[s]; ok {
+		return str
+	}
+	return fmt.Sprintf("sense key %xh", uint8(s))
+}
+
+// AdditionalSenseCode contains the additional sense key and qualifier in one
+// 16-bit value. The high 8 bits are the sense key, the bottom 8 bits the
+// qualifier.
+type AdditionalSenseCode uint16
+
+// ASK returns the raw Additional Sense Key
+func (a AdditionalSenseCode) ASK() uint8 {
+	return uint8(a >> 8)
+}
+
+// ASKQ returns the raw Additional Sense Key Qualifier
+func (a AdditionalSenseCode) ASKQ() uint8 {
+	return uint8(a & 0xFF)
+}
+
+// IsKey checks if the ASK portion of a is the same as the ASK portion of b.
+func (a AdditionalSenseCode) IsKey(b AdditionalSenseCode) bool {
+	return a.ASK() == b.ASK()
+}
+
+// String returns the textual representation of this ASK
+func (s AdditionalSenseCode) String() string {
+	if str, ok := additionalSenseCodeDesc[s]; ok {
+		return str
+	}
+	return fmt.Sprintf("unknown additional sense code %xh %xh", s.ASK(), s.ASKQ())
+}
+
+// FixedError is one type of error returned by a SCSI CHECK_CONDITION.
+// See also Table 48 in the standard.
+type FixedError struct {
+	Deferred                   bool
+	SenseKey                   SenseKey
+	Information                uint32
+	CommandSpecificInformation uint32
+	AdditionalSenseCode        AdditionalSenseCode
+}
+
+func (e FixedError) Error() string {
+	if e.AdditionalSenseCode == 0 {
+		return fmt.Sprintf("%v", e.SenseKey)
+	}
+	return fmt.Sprintf("%v: %v", e.SenseKey, e.AdditionalSenseCode)
+
+}
+
+// UnknownError is a type of error returned by SCSI which is not understood by this
+// library. This can be a vendor-specific or future error.
+type UnknownError struct {
+	RawSenseData []byte
+}
+
+func (e *UnknownError) Error() string {
+	return fmt.Sprintf("unknown SCSI error, raw sense data follows: %x", e.RawSenseData)
+}
diff --git a/metropolis/pkg/scsi/scsi_linux.go b/metropolis/pkg/scsi/scsi_linux.go
new file mode 100644
index 0000000..be937d0
--- /dev/null
+++ b/metropolis/pkg/scsi/scsi_linux.go
@@ -0,0 +1,96 @@
+//go:build linux
+
+package scsi
+
+import (
+	"encoding/binary"
+	"errors"
+	"fmt"
+	"math"
+	"runtime"
+	"unsafe"
+
+	"golang.org/x/sys/unix"
+)
+
+// RawCommand issues a raw command against the device.
+func (d *Device) RawCommand(c *CommandDataBuffer) error {
+	cdb, err := c.Bytes()
+	if err != nil {
+		return fmt.Errorf("error encoding CDB: %w", err)
+	}
+	conn, err := d.fd.SyscallConn()
+	if err != nil {
+		return fmt.Errorf("unable to get RawConn: %w", err)
+	}
+	var dxferDir int32
+	switch c.DataTransferDirection {
+	case DataTransferNone:
+		dxferDir = SG_DXFER_NONE
+	case DataTransferFromDevice:
+		dxferDir = SG_DXFER_FROM_DEV
+	case DataTransferToDevice:
+		dxferDir = SG_DXFER_TO_DEV
+	case DataTransferBidirectional:
+		dxferDir = SG_DXFER_TO_FROM_DEV
+	default:
+		return errors.New("invalid DataTransferDirection")
+	}
+	var timeout uint32
+	if c.Timeout.Milliseconds() > math.MaxUint32 {
+		timeout = math.MaxUint32
+	}
+	if len(c.Data) > math.MaxUint32 {
+		return errors.New("payload larger than 2^32 bytes, unable to issue")
+	}
+	if len(cdb) > math.MaxUint8 {
+		return errors.New("CDB larger than 2^8 bytes, unable to issue")
+	}
+	var senseBuf [32]byte
+	cmdRaw := sgIOHdr{
+		Interface_id:    'S',
+		Dxfer_direction: dxferDir,
+		Dxfer_len:       uint32(len(c.Data)),
+		Dxferp:          uintptr(unsafe.Pointer(&c.Data[0])),
+		Cmd_len:         uint8(len(cdb)),
+		Cmdp:            uintptr(unsafe.Pointer(&cdb[0])),
+		Mx_sb_len:       uint8(len(senseBuf)),
+		Sbp:             uintptr(unsafe.Pointer(&senseBuf[0])),
+		Timeout:         timeout,
+	}
+	var errno unix.Errno
+	err = conn.Control(func(fd uintptr) {
+		_, _, errno = unix.Syscall(unix.SYS_IOCTL, fd, SG_IO, uintptr(unsafe.Pointer(&cmdRaw)))
+	})
+	runtime.KeepAlive(cmdRaw)
+	runtime.KeepAlive(c.Data)
+	runtime.KeepAlive(senseBuf)
+	runtime.KeepAlive(cdb)
+	if err != nil {
+		return fmt.Errorf("unable to get fd: %w", err)
+	}
+	if errno != 0 {
+		return errno
+	}
+	if cmdRaw.Masked_status != 0 {
+		if senseBuf[0] == 0x70 || senseBuf[0] == 0x71 {
+			err := &FixedError{
+				Deferred:    senseBuf[0] == 0x71,
+				SenseKey:    SenseKey(senseBuf[2] & 0b1111),
+				Information: binary.BigEndian.Uint32(senseBuf[3:7]),
+			}
+			length := int(senseBuf[7])
+			if length >= 4 {
+				err.CommandSpecificInformation = binary.BigEndian.Uint32(senseBuf[8:12])
+				if length >= 6 {
+					err.AdditionalSenseCode = AdditionalSenseCode(uint16(senseBuf[12])<<8 | uint16(senseBuf[13]))
+				}
+			}
+			return err
+		}
+		return &UnknownError{
+			RawSenseData: senseBuf[:],
+		}
+	}
+	return nil
+}
diff --git a/metropolis/pkg/scsi/scsi_linux_defs.go b/metropolis/pkg/scsi/scsi_linux_defs.go
new file mode 100644
index 0000000..2d84e91
--- /dev/null
+++ b/metropolis/pkg/scsi/scsi_linux_defs.go
@@ -0,0 +1,40 @@
+//go:build linux
+
+// Code generated by cmd/cgo -godefs; DO NOT EDIT.
+// cgo -godefs scsi_linux_defs1.go
+
+package scsi
+
+const (
+	SG_IO                = 0x2285
+	SG_DXFER_NONE        = -0x1
+	SG_DXFER_TO_DEV      = -0x2
+	SG_DXFER_FROM_DEV    = -0x3
+	SG_DXFER_TO_FROM_DEV = -0x4
+)
+
+type sgIOHdr struct {
+	Interface_id    int32
+	Dxfer_direction int32
+	Cmd_len         uint8
+	Mx_sb_len       uint8
+	Iovec_count     uint16
+	Dxfer_len       uint32
+	Dxferp          uintptr
+	Cmdp            uintptr
+	Sbp             uintptr
+	Timeout         uint32
+	Flags           uint32
+	Pack_id         int32
+	Usr_ptr         uintptr
+	Status          uint8
+	Masked_status   uint8
+	Msg_status      uint8
+	Sb_len_wr       uint8
+	Host_status     uint16
+	Driver_status   uint16
+	Resid           int32
+	Duration        uint32
+	Info            uint32
+	Pad_cgo_0       [4]byte
+}
diff --git a/metropolis/pkg/scsi/scsi_linux_defs1.go b/metropolis/pkg/scsi/scsi_linux_defs1.go
new file mode 100644
index 0000000..e2e410d
--- /dev/null
+++ b/metropolis/pkg/scsi/scsi_linux_defs1.go
@@ -0,0 +1,16 @@
+//go:build ignore
+
+package scsi
+
+// #include <scsi/sg.h>
+import "C"
+
+const (
+	SG_IO                = C.SG_IO
+	SG_DXFER_NONE        = C.SG_DXFER_NONE
+	SG_DXFER_TO_DEV      = C.SG_DXFER_TO_DEV
+	SG_DXFER_FROM_DEV    = C.SG_DXFER_FROM_DEV
+	SG_DXFER_TO_FROM_DEV = C.SG_DXFER_TO_FROM_DEV
+)
+
+type sgIOHdr C.sg_io_hdr_t
diff --git a/metropolis/pkg/scsi/sensekeydata.go b/metropolis/pkg/scsi/sensekeydata.go
new file mode 100644
index 0000000..37a31c2
--- /dev/null
+++ b/metropolis/pkg/scsi/sensekeydata.go
@@ -0,0 +1,1506 @@
+package scsi
+
+// Generated from Table F.1
+const (
+	NoAdditionalSenseInformation                            AdditionalSenseCode = 0x0000
+	FilemarkDetected                                        AdditionalSenseCode = 0x0001
+	EndOfPartitionmediumDetected                            AdditionalSenseCode = 0x0002
+	SetmarkDetected                                         AdditionalSenseCode = 0x0003
+	BeginningOfPartitionmediumDetected                      AdditionalSenseCode = 0x0004
+	EndOfDataDetected                                       AdditionalSenseCode = 0x0005
+	IoProcessTerminated                                     AdditionalSenseCode = 0x0006
+	ProgrammableEarlyWarningDetected                        AdditionalSenseCode = 0x0007
+	AudioPlayOperationInProgress                            AdditionalSenseCode = 0x0011
+	AudioPlayOperationPaused                                AdditionalSenseCode = 0x0012
+	AudioPlayOperationSuccessfullyCompleted                 AdditionalSenseCode = 0x0013
+	AudioPlayOperationStoppedDueToError                     AdditionalSenseCode = 0x0014
+	NoCurrentAudioStatusToReturn                            AdditionalSenseCode = 0x0015
+	OperationInProgress                                     AdditionalSenseCode = 0x0016
+	CleaningRequested                                       AdditionalSenseCode = 0x0017
+	EraseOperationInProgress                                AdditionalSenseCode = 0x0018
+	LocateOperationInProgress                               AdditionalSenseCode = 0x0019
+	RewindOperationInProgress                               AdditionalSenseCode = 0x001a
+	SetCapacityOperationInProgress                          AdditionalSenseCode = 0x001b
+	VerifyOperationInProgress                               AdditionalSenseCode = 0x001c
+	AtaPassThroughInformationAvailable                      AdditionalSenseCode = 0x001d
+	ConflictingSaCreationRequest                            AdditionalSenseCode = 0x001e
+	LogicalUnitTransitioningToAnotherPowerCondition         AdditionalSenseCode = 0x001f
+	ExtendedCopyInformationAvailable                        AdditionalSenseCode = 0x0020
+	AtomicCommandAbortedDueToAca                            AdditionalSenseCode = 0x0021
+	DeferredMicrocodeIsPending                              AdditionalSenseCode = 0x0022
+	NoIndexsectorSignal                                     AdditionalSenseCode = 0x0100
+	NoSeekComplete                                          AdditionalSenseCode = 0x0200
+	PeripheralDeviceWriteFault                              AdditionalSenseCode = 0x0300
+	NoWriteCurrent                                          AdditionalSenseCode = 0x0301
+	ExcessiveWriteErrors                                    AdditionalSenseCode = 0x0302
+	LogicalUnitNotReadyCauseNotReportable                   AdditionalSenseCode = 0x0400
+	LogicalUnitIsInProcessOfBecomingReady                   AdditionalSenseCode = 0x0401
+	LogicalUnitNotReadyInitializingCommandRequired          AdditionalSenseCode = 0x0402
+	LogicalUnitNotReadyManualInterventionRequired           AdditionalSenseCode = 0x0403
+	LogicalUnitNotReadyFormatInProgress                     AdditionalSenseCode = 0x0404
+	LogicalUnitNotReadyRebuildInProgress                    AdditionalSenseCode = 0x0405
+	LogicalUnitNotReadyRecalculationInProgress              AdditionalSenseCode = 0x0406
+	LogicalUnitNotReadyOperationInProgress                  AdditionalSenseCode = 0x0407
+	LogicalUnitNotReadyLongWriteInProgress                  AdditionalSenseCode = 0x0408
+	LogicalUnitNotReadySelfTestInProgress                   AdditionalSenseCode = 0x0409
+	LogicalUnitNotAccessibleAsymmetricAccessStateTransition AdditionalSenseCode = 0x040a
+	LogicalUnitNotAccessibleTargetPortInStandbyState        AdditionalSenseCode = 0x040b
+	LogicalUnitNotAccessibleTargetPortInUnavailableState    AdditionalSenseCode = 0x040c
+	LogicalUnitNotReadyStructureCheckRequired               AdditionalSenseCode = 0x040d
+	LogicalUnitNotReadySecuritySessionInProgress            AdditionalSenseCode = 0x040e
+	LogicalUnitNotReadyAuxiliaryMemoryNotAccessible         AdditionalSenseCode = 0x0410
+	LogicalUnitNotReadyNotifyenableSpinupRequired           AdditionalSenseCode = 0x0411
+	LogicalUnitNotReadyOffline                              AdditionalSenseCode = 0x0412
+	LogicalUnitNotReadySaCreationInProgress                 AdditionalSenseCode = 0x0413
+	LogicalUnitNotReadySpaceAllocationInProgress            AdditionalSenseCode = 0x0414
+	LogicalUnitNotReadyRoboticsDisabled                     AdditionalSenseCode = 0x0415
+	LogicalUnitNotReadyConfigurationRequired                AdditionalSenseCode = 0x0416
+	LogicalUnitNotReadyCalibrationRequired                  AdditionalSenseCode = 0x0417
+	LogicalUnitNotReadyADoorIsOpen                          AdditionalSenseCode = 0x0418
+	LogicalUnitNotReadyOperatingInSequentialMode            AdditionalSenseCode = 0x0419
+	LogicalUnitNotReadyStartStopUnitCommandInProgress       AdditionalSenseCode = 0x041a
+	LogicalUnitNotReadySanitizeInProgress                   AdditionalSenseCode = 0x041b
+	LogicalUnitNotReadyAdditionalPowerUseNotYetGranted      AdditionalSenseCode = 0x041c
+	LogicalUnitNotReadyConfigurationInProgress              AdditionalSenseCode = 0x041d
+	LogicalUnitNotReadyMicrocodeActivationRequired          AdditionalSenseCode = 0x041e
+	LogicalUnitNotReadyMicrocodeDownloadRequired            AdditionalSenseCode = 0x041f
+	LogicalUnitNotReadyLogicalUnitResetRequired             AdditionalSenseCode = 0x0420
+	LogicalUnitNotReadyHardResetRequired                    AdditionalSenseCode = 0x0421
+	LogicalUnitNotReadyPowerCycleRequired                   AdditionalSenseCode = 0x0422
+	LogicalUnitNotReadyAffiliationRequired                  AdditionalSenseCode = 0x0423
+	DepopulationInProgress                                  AdditionalSenseCode = 0x0424
+	LogicalUnitDoesNotRespondToSelection                    AdditionalSenseCode = 0x0500
+	NoReferencePositionFound                                AdditionalSenseCode = 0x0600
+	MultiplePeripheralDevicesSelected                       AdditionalSenseCode = 0x0700
+	LogicalUnitCommunicationFailure                         AdditionalSenseCode = 0x0800
+	LogicalUnitCommunicationTimeOut                         AdditionalSenseCode = 0x0801
+	LogicalUnitCommunicationParityError                     AdditionalSenseCode = 0x0802
+	LogicalUnitCommunicationCrcErrorultraDma32              AdditionalSenseCode = 0x0803
+	UnreachableCopyTarget                                   AdditionalSenseCode = 0x0804
+	TrackFollowingError                                     AdditionalSenseCode = 0x0900
+	TrackingServoFailure                                    AdditionalSenseCode = 0x0901
+	FocusServoFailure                                       AdditionalSenseCode = 0x0902
+	SpindleServoFailure                                     AdditionalSenseCode = 0x0903
+	HeadSelectFault                                         AdditionalSenseCode = 0x0904
+	VibrationInducedTrackingError                           AdditionalSenseCode = 0x0905
+	ErrorLogOverflow                                        AdditionalSenseCode = 0x0a00
+	Warning                                                 AdditionalSenseCode = 0x0b00
+	WarningSpecifiedTemperatureExceeded                     AdditionalSenseCode = 0x0b01
+	WarningEnclosureDegraded                                AdditionalSenseCode = 0x0b02
+	WarningBackgroundSelfTestFailed                         AdditionalSenseCode = 0x0b03
+	WarningBackgroundPreScanDetectedMediumError             AdditionalSenseCode = 0x0b04
+	WarningBackgroundMediumScanDetectedMediumError          AdditionalSenseCode = 0x0b05
+	WarningNonVolatileCacheNowVolatile                      AdditionalSenseCode = 0x0b06
+	WarningDegradedPowerToNonVolatileCache                  AdditionalSenseCode = 0x0b07
+	WarningPowerLossExpected                                AdditionalSenseCode = 0x0b08
+	WarningDeviceStatisticsNotificationActive               AdditionalSenseCode = 0x0b09
+	WarningHighCriticalTemperatureLimitExceeded             AdditionalSenseCode = 0x0b0a
+	WarningLowCriticalTemperatureLimitExceeded              AdditionalSenseCode = 0x0b0b
+	WarningHighOperatingTemperatureLimitExceeded            AdditionalSenseCode = 0x0b0c
+	WarningLowOperatingTemperatureLimitExceeded             AdditionalSenseCode = 0x0b0d
+	WarningHighCriticalHumidityLimitExceeded                AdditionalSenseCode = 0x0b0e
+	WarningLowCriticalHumidityLimitExceeded                 AdditionalSenseCode = 0x0b0f
+	WarningHighOperatingHumidityLimitExceeded               AdditionalSenseCode = 0x0b10
+	WarningLowOperatingHumidityLimitExceeded                AdditionalSenseCode = 0x0b11
+	WarningMicrocodeSecurityAtRisk                          AdditionalSenseCode = 0x0b12
+	WarningMicrocodeDigitalSignatureValidationFailure       AdditionalSenseCode = 0x0b13
+	WarningPhysicalElementStatusChange                      AdditionalSenseCode = 0x0b14
+	WriteError                                              AdditionalSenseCode = 0x0c00
+	WriteErrorRecoveredWithAutoReallocation                 AdditionalSenseCode = 0x0c01
+	WriteErrorAutoReallocationFailed                        AdditionalSenseCode = 0x0c02
+	WriteErrorRecommendReassignment                         AdditionalSenseCode = 0x0c03
+	CompressionCheckMiscompareError                         AdditionalSenseCode = 0x0c04
+	DataExpansionOccurredDuringCompression                  AdditionalSenseCode = 0x0c05
+	BlockNotCompressible                                    AdditionalSenseCode = 0x0c06
+	WriteErrorRecoveryNeeded                                AdditionalSenseCode = 0x0c07
+	WriteErrorRecoveryFailed                                AdditionalSenseCode = 0x0c08
+	WriteErrorLossOfStreaming                               AdditionalSenseCode = 0x0c09
+	WriteErrorPaddingBlocksAdded                            AdditionalSenseCode = 0x0c0a
+	AuxiliaryMemoryWriteError                               AdditionalSenseCode = 0x0c0b
+	WriteErrorUnexpectedUnsolicitedData                     AdditionalSenseCode = 0x0c0c
+	WriteErrorNotEnoughUnsolicitedData                      AdditionalSenseCode = 0x0c0d
+	MultipleWriteErrors                                     AdditionalSenseCode = 0x0c0e
+	DefectsInErrorWindow                                    AdditionalSenseCode = 0x0c0f
+	IncompleteMultipleAtomicWriteOperations                 AdditionalSenseCode = 0x0c10
+	WriteErrorRecoveryScanNeeded                            AdditionalSenseCode = 0x0c11
+	WriteErrorInsufficientZoneResources                     AdditionalSenseCode = 0x0c12
+	ErrorDetectedByThirdPartyTemporaryInitiator             AdditionalSenseCode = 0x0d00
+	ThirdPartyDeviceFailure                                 AdditionalSenseCode = 0x0d01
+	CopyTargetDeviceNotReachable                            AdditionalSenseCode = 0x0d02
+	IncorrectCopyTargetDeviceType                           AdditionalSenseCode = 0x0d03
+	CopyTargetDeviceDataUnderrun                            AdditionalSenseCode = 0x0d04
+	CopyTargetDeviceDataOverrun                             AdditionalSenseCode = 0x0d05
+	InvalidInformationUnit                                  AdditionalSenseCode = 0x0e00
+	InformationUnitTooShort                                 AdditionalSenseCode = 0x0e01
+	InformationUnitTooLong                                  AdditionalSenseCode = 0x0e02
+	InvalidFieldInCommandInformationUnit                    AdditionalSenseCode = 0x0e03
+	IdCrcOrEccError                                         AdditionalSenseCode = 0x1000
+	LogicalBlockGuardCheckFailed                            AdditionalSenseCode = 0x1001
+	LogicalBlockApplicationTagCheckFailed                   AdditionalSenseCode = 0x1002
+	LogicalBlockReferenceTagCheckFailed                     AdditionalSenseCode = 0x1003
+	LogicalBlockProtectionErrorOnRecoverBufferedData        AdditionalSenseCode = 0x1004
+	LogicalBlockProtectionMethodError                       AdditionalSenseCode = 0x1005
+	UnrecoveredReadError                                    AdditionalSenseCode = 0x1100
+	ReadRetriesExhausted                                    AdditionalSenseCode = 0x1101
+	ErrorTooLongToCorrect                                   AdditionalSenseCode = 0x1102
+	MultipleReadErrors                                      AdditionalSenseCode = 0x1103
+	UnrecoveredReadErrorAutoReallocateFailed                AdditionalSenseCode = 0x1104
+	LEcUncorrectableError                                   AdditionalSenseCode = 0x1105
+	CircUnrecoveredError                                    AdditionalSenseCode = 0x1106
+	DataReSynchronizationError                              AdditionalSenseCode = 0x1107
+	IncompleteBlockRead                                     AdditionalSenseCode = 0x1108
+	NoGapFound                                              AdditionalSenseCode = 0x1109
+	MiscorrectedError                                       AdditionalSenseCode = 0x110a
+	UnrecoveredReadErrorRecommendReassignment               AdditionalSenseCode = 0x110b
+	UnrecoveredReadErrorRecommendRewriteTheData             AdditionalSenseCode = 0x110c
+	DeCompressionCrcError                                   AdditionalSenseCode = 0x110d
+	CannotDecompressUsingDeclaredAlgorithm                  AdditionalSenseCode = 0x110e
+	ErrorReadingUpceanNumber                                AdditionalSenseCode = 0x110f
+	ErrorReadingIsrcNumber                                  AdditionalSenseCode = 0x1110
+	ReadErrorLossOfStreaming                                AdditionalSenseCode = 0x1111
+	AuxiliaryMemoryReadError                                AdditionalSenseCode = 0x1112
+	ReadErrorFailedRetransmissionRequest                    AdditionalSenseCode = 0x1113
+	ReadErrorLbaMarkedBadByApplicationClient                AdditionalSenseCode = 0x1114
+	WriteAfterSanitizeRequired                              AdditionalSenseCode = 0x1115
+	AddressMarkNotFoundForIdField                           AdditionalSenseCode = 0x1200
+	AddressMarkNotFoundForDataField                         AdditionalSenseCode = 0x1300
+	RecordedEntityNotFound                                  AdditionalSenseCode = 0x1400
+	RecordNotFound                                          AdditionalSenseCode = 0x1401
+	FilemarkOrSetmarkNotFound                               AdditionalSenseCode = 0x1402
+	EndOfDataNotFound                                       AdditionalSenseCode = 0x1403
+	BlockSequenceError                                      AdditionalSenseCode = 0x1404
+	RecordNotFoundRecommendReassignment                     AdditionalSenseCode = 0x1405
+	RecordNotFoundDataAutoReallocated                       AdditionalSenseCode = 0x1406
+	LocateOperationFailure                                  AdditionalSenseCode = 0x1407
+	RandomPositioningError                                  AdditionalSenseCode = 0x1500
+	MechanicalPositioningError                              AdditionalSenseCode = 0x1501
+	PositioningErrorDetectedByReadOfMedium                  AdditionalSenseCode = 0x1502
+	DataSynchronizationMarkError                            AdditionalSenseCode = 0x1600
+	DataSyncErrorDataRewritten                              AdditionalSenseCode = 0x1601
+	DataSyncErrorRecommendRewrite                           AdditionalSenseCode = 0x1602
+	DataSyncErrorDataAutoReallocated                        AdditionalSenseCode = 0x1603
+	DataSyncErrorRecommendReassignment                      AdditionalSenseCode = 0x1604
+	RecoveredDataWithNoErrorCorrectionApplied               AdditionalSenseCode = 0x1700
+	RecoveredDataWithRetries                                AdditionalSenseCode = 0x1701
+	RecoveredDataWithPositiveHeadOffset                     AdditionalSenseCode = 0x1702
+	RecoveredDataWithNegativeHeadOffset                     AdditionalSenseCode = 0x1703
+	RecoveredDataWithRetriesAndorCircApplied                AdditionalSenseCode = 0x1704
+	RecoveredDataUsingPreviousSectorId                      AdditionalSenseCode = 0x1705
+	RecoveredDataWithoutEccDataAutoReallocated              AdditionalSenseCode = 0x1706
+	RecoveredDataWithoutEccRecommendReassignment            AdditionalSenseCode = 0x1707
+	RecoveredDataWithoutEccRecommendRewrite                 AdditionalSenseCode = 0x1708
+	RecoveredDataWithoutEccDataRewritten                    AdditionalSenseCode = 0x1709
+	RecoveredDataWithErrorCorrectionApplied                 AdditionalSenseCode = 0x1800
+	RecoveredDataWithErrorCorrRetriesApplied                AdditionalSenseCode = 0x1801
+	RecoveredDataDataAutoReallocated                        AdditionalSenseCode = 0x1802
+	RecoveredDataWithCirc                                   AdditionalSenseCode = 0x1803
+	RecoveredDataWithLEc                                    AdditionalSenseCode = 0x1804
+	RecoveredDataRecommendReassignment                      AdditionalSenseCode = 0x1805
+	RecoveredDataRecommendRewrite                           AdditionalSenseCode = 0x1806
+	RecoveredDataWithEccDataRewritten                       AdditionalSenseCode = 0x1807
+	RecoveredDataWithLinking                                AdditionalSenseCode = 0x1808
+	DefectListError                                         AdditionalSenseCode = 0x1900
+	DefectListNotAvailable                                  AdditionalSenseCode = 0x1901
+	DefectListErrorInPrimaryList                            AdditionalSenseCode = 0x1902
+	DefectListErrorInGrownList                              AdditionalSenseCode = 0x1903
+	ParameterListLengthError                                AdditionalSenseCode = 0x1a00
+	SynchronousDataTransferError                            AdditionalSenseCode = 0x1b00
+	DefectListNotFound                                      AdditionalSenseCode = 0x1c00
+	PrimaryDefectListNotFound                               AdditionalSenseCode = 0x1c01
+	GrownDefectListNotFound                                 AdditionalSenseCode = 0x1c02
+	MiscompareDuringVerifyOperation                         AdditionalSenseCode = 0x1d00
+	MiscompareVerifyOfUnmappedLba                           AdditionalSenseCode = 0x1d01
+	RecoveredIdWithEccCorrection                            AdditionalSenseCode = 0x1e00
+	PartialDefectListTransfer                               AdditionalSenseCode = 0x1f00
+	InvalidCommandOperationCode                             AdditionalSenseCode = 0x2000
+	AccessDeniedInitiatorPendingEnrolled                    AdditionalSenseCode = 0x2001
+	AccessDeniedNoAccessRights                              AdditionalSenseCode = 0x2002
+	AccessDeniedInvalidMgmtIdKey                            AdditionalSenseCode = 0x2003
+	IllegalCommandWhileInWriteCapableState                  AdditionalSenseCode = 0x2004
+	IllegalCommandWhileInExplicitAddressMode                AdditionalSenseCode = 0x2006
+	IllegalCommandWhileInImplicitAddressMode                AdditionalSenseCode = 0x2007
+	AccessDeniedEnrollmentConflict                          AdditionalSenseCode = 0x2008
+	AccessDeniedInvalidLuIdentifier                         AdditionalSenseCode = 0x2009
+	AccessDeniedInvalidProxyToken                           AdditionalSenseCode = 0x200a
+	AccessDeniedAclLunConflict                              AdditionalSenseCode = 0x200b
+	IllegalCommandWhenNotInAppendOnlyMode                   AdditionalSenseCode = 0x200c
+	NotAnAdministrativeLogicalUnit                          AdditionalSenseCode = 0x200d
+	NotASubsidiaryLogicalUnit                               AdditionalSenseCode = 0x200e
+	NotAConglomerateLogicalUnit                             AdditionalSenseCode = 0x200f
+	LogicalBlockAddressOutOfRange                           AdditionalSenseCode = 0x2100
+	InvalidElementAddress                                   AdditionalSenseCode = 0x2101
+	InvalidAddressForWrite                                  AdditionalSenseCode = 0x2102
+	InvalidWriteCrossingLayerJump                           AdditionalSenseCode = 0x2103
+	UnalignedWriteCommand                                   AdditionalSenseCode = 0x2104
+	WriteBoundaryViolation                                  AdditionalSenseCode = 0x2105
+	AttemptToReadInvalidData                                AdditionalSenseCode = 0x2106
+	ReadBoundaryViolation                                   AdditionalSenseCode = 0x2107
+	MisalignedWriteCommand                                  AdditionalSenseCode = 0x2108
+	IllegalFunctionuse20002400Or2600                        AdditionalSenseCode = 0x2200
+	InvalidTokenOperationCauseNotReportable                 AdditionalSenseCode = 0x2300
+	InvalidTokenOperationUnsupportedTokenType               AdditionalSenseCode = 0x2301
+	InvalidTokenOperationRemoteTokenUsageNotSupported       AdditionalSenseCode = 0x2302
+	InvalidTokenOperationRemoteRodTokenCreationNotSupported AdditionalSenseCode = 0x2303
+	InvalidTokenOperationTokenUnknown                       AdditionalSenseCode = 0x2304
+	InvalidTokenOperationTokenCorrupt                       AdditionalSenseCode = 0x2305
+	InvalidTokenOperationTokenRevoked                       AdditionalSenseCode = 0x2306
+	InvalidTokenOperationTokenExpired                       AdditionalSenseCode = 0x2307
+	InvalidTokenOperationTokenCancelled                     AdditionalSenseCode = 0x2308
+	InvalidTokenOperationTokenDeleted                       AdditionalSenseCode = 0x2309
+	InvalidTokenOperationInvalidTokenLength                 AdditionalSenseCode = 0x230a
+	InvalidFieldInCdb                                       AdditionalSenseCode = 0x2400
+	CdbDecryptionError                                      AdditionalSenseCode = 0x2401
+	SecurityAuditValueFrozen                                AdditionalSenseCode = 0x2404
+	SecurityWorkingKeyFrozen                                AdditionalSenseCode = 0x2405
+	NonceNotUnique                                          AdditionalSenseCode = 0x2406
+	NonceTimestampOutOfRange                                AdditionalSenseCode = 0x2407
+	InvalidXcdb                                             AdditionalSenseCode = 0x2408
+	InvalidFastFormat                                       AdditionalSenseCode = 0x2409
+	LogicalUnitNotSupported                                 AdditionalSenseCode = 0x2500
+	InvalidFieldInParameterList                             AdditionalSenseCode = 0x2600
+	ParameterNotSupported                                   AdditionalSenseCode = 0x2601
+	ParameterValueInvalid                                   AdditionalSenseCode = 0x2602
+	ThresholdParametersNotSupported                         AdditionalSenseCode = 0x2603
+	InvalidReleaseOfPersistentReservation                   AdditionalSenseCode = 0x2604
+	DataDecryptionError                                     AdditionalSenseCode = 0x2605
+	TooManyTargetDescriptors                                AdditionalSenseCode = 0x2606
+	UnsupportedTargetDescriptorTypeCode                     AdditionalSenseCode = 0x2607
+	TooManySegmentDescriptors                               AdditionalSenseCode = 0x2608
+	UnsupportedSegmentDescriptorTypeCode                    AdditionalSenseCode = 0x2609
+	UnexpectedInexactSegment                                AdditionalSenseCode = 0x260a
+	InlineDataLengthExceeded                                AdditionalSenseCode = 0x260b
+	InvalidOperationForCopySourceOrDestination              AdditionalSenseCode = 0x260c
+	CopySegmentGranularityViolation                         AdditionalSenseCode = 0x260d
+	InvalidParameterWhilePortIsEnabled                      AdditionalSenseCode = 0x260e
+	InvalidDataOutBufferIntegrityCheckValue                 AdditionalSenseCode = 0x260f
+	DataDecryptionKeyFailLimitReached                       AdditionalSenseCode = 0x2610
+	IncompleteKeyAssociatedDataSet                          AdditionalSenseCode = 0x2611
+	VendorSpecificKeyReferenceNotFound                      AdditionalSenseCode = 0x2612
+	ApplicationTagModePageIsInvalid                         AdditionalSenseCode = 0x2613
+	TapeStreamMirroringPrevented                            AdditionalSenseCode = 0x2614
+	CopySourceOrCopyDestinationNotAuthorized                AdditionalSenseCode = 0x2615
+	WriteProtected                                          AdditionalSenseCode = 0x2700
+	HardwareWriteProtected                                  AdditionalSenseCode = 0x2701
+	LogicalUnitSoftwareWriteProtected                       AdditionalSenseCode = 0x2702
+	AssociatedWriteProtect                                  AdditionalSenseCode = 0x2703
+	PersistentWriteProtect                                  AdditionalSenseCode = 0x2704
+	PermanentWriteProtect                                   AdditionalSenseCode = 0x2705
+	ConditionalWriteProtect                                 AdditionalSenseCode = 0x2706
+	SpaceAllocationFailedWriteProtect                       AdditionalSenseCode = 0x2707
+	ZoneIsReadOnly                                          AdditionalSenseCode = 0x2708
+	NotReadyToReadyChangeMediumMayHaveChanged               AdditionalSenseCode = 0x2800
+	ImportOrExportElementAccessed                           AdditionalSenseCode = 0x2801
+	FormatLayerMayHaveChanged                               AdditionalSenseCode = 0x2802
+	ImportexportElementAccessedMediumChanged                AdditionalSenseCode = 0x2803
+	PowerOnResetOrBusDeviceResetOccurred                    AdditionalSenseCode = 0x2900
+	PowerOnOccurred                                         AdditionalSenseCode = 0x2901
+	ScsiBusResetOccurred                                    AdditionalSenseCode = 0x2902
+	BusDeviceResetFunctionOccurred                          AdditionalSenseCode = 0x2903
+	DeviceInternalReset                                     AdditionalSenseCode = 0x2904
+	TransceiverModeChangedToSingleEnded                     AdditionalSenseCode = 0x2905
+	TransceiverModeChangedToLvd                             AdditionalSenseCode = 0x2906
+	ITNexusLossOccurred                                     AdditionalSenseCode = 0x2907
+	ParametersChanged                                       AdditionalSenseCode = 0x2a00
+	ModeParametersChanged                                   AdditionalSenseCode = 0x2a01
+	LogParametersChanged                                    AdditionalSenseCode = 0x2a02
+	ReservationsPreempted                                   AdditionalSenseCode = 0x2a03
+	ReservationsReleased                                    AdditionalSenseCode = 0x2a04
+	RegistrationsPreempted                                  AdditionalSenseCode = 0x2a05
+	AsymmetricAccessStateChanged                            AdditionalSenseCode = 0x2a06
+	ImplicitAsymmetricAccessStateTransitionFailed           AdditionalSenseCode = 0x2a07
+	PriorityChanged                                         AdditionalSenseCode = 0x2a08
+	CapacityDataHasChanged                                  AdditionalSenseCode = 0x2a09
+	ErrorHistoryITNexusCleared                              AdditionalSenseCode = 0x2a0a
+	ErrorHistorySnapshotReleased                            AdditionalSenseCode = 0x2a0b
+	ErrorRecoveryAttributesHaveChanged                      AdditionalSenseCode = 0x2a0c
+	DataEncryptionCapabilitiesChanged                       AdditionalSenseCode = 0x2a0d
+	TimestampChanged                                        AdditionalSenseCode = 0x2a10
+	DataEncryptionParametersChangedByAnotherITNexus         AdditionalSenseCode = 0x2a11
+	DataEncryptionParametersChangedByVendorSpecificEvent    AdditionalSenseCode = 0x2a12
+	DataEncryptionKeyInstanceCounterHasChanged              AdditionalSenseCode = 0x2a13
+	SaCreationCapabilitiesDataHasChanged                    AdditionalSenseCode = 0x2a14
+	MediumRemovalPreventionPreempted                        AdditionalSenseCode = 0x2a15
+	ZoneResetWritePointerRecommended                        AdditionalSenseCode = 0x2a16
+	CopyCannotExecuteSinceHostCannotDisconnect              AdditionalSenseCode = 0x2b00
+	CommandSequenceError                                    AdditionalSenseCode = 0x2c00
+	TooManyWindowsSpecified                                 AdditionalSenseCode = 0x2c01
+	InvalidCombinationOfWindowsSpecified                    AdditionalSenseCode = 0x2c02
+	CurrentProgramAreaIsNotEmpty                            AdditionalSenseCode = 0x2c03
+	CurrentProgramAreaIsEmpty                               AdditionalSenseCode = 0x2c04
+	IllegalPowerConditionRequest                            AdditionalSenseCode = 0x2c05
+	PersistentPreventConflict                               AdditionalSenseCode = 0x2c06
+	PreviousBusyStatus                                      AdditionalSenseCode = 0x2c07
+	PreviousTaskSetFullStatus                               AdditionalSenseCode = 0x2c08
+	PreviousReservationConflictStatus                       AdditionalSenseCode = 0x2c09
+	PartitionOrCollectionContainsUserObjects                AdditionalSenseCode = 0x2c0a
+	NotReserved                                             AdditionalSenseCode = 0x2c0b
+	OrwriteGenerationDoesNotMatch                           AdditionalSenseCode = 0x2c0c
+	ResetWritePointerNotAllowed                             AdditionalSenseCode = 0x2c0d
+	ZoneIsOffline                                           AdditionalSenseCode = 0x2c0e
+	StreamNotOpen                                           AdditionalSenseCode = 0x2c0f
+	UnwrittenDataInZone                                     AdditionalSenseCode = 0x2c10
+	DescriptorFormatSenseDataRequired                       AdditionalSenseCode = 0x2c11
+	OverwriteErrorOnUpdateInPlace                           AdditionalSenseCode = 0x2d00
+	InsufficientTimeForOperation                            AdditionalSenseCode = 0x2e00
+	CommandTimeoutBeforeProcessing                          AdditionalSenseCode = 0x2e01
+	CommandTimeoutDuringProcessing                          AdditionalSenseCode = 0x2e02
+	CommandTimeoutDuringProcessingDueToErrorRecovery        AdditionalSenseCode = 0x2e03
+	CommandsClearedByAnotherInitiator                       AdditionalSenseCode = 0x2f00
+	CommandsClearedByPowerLossNotification                  AdditionalSenseCode = 0x2f01
+	CommandsClearedByDeviceServer                           AdditionalSenseCode = 0x2f02
+	SomeCommandsClearedByQueuingLayerEvent                  AdditionalSenseCode = 0x2f03
+	IncompatibleMediumInstalled                             AdditionalSenseCode = 0x3000
+	CannotReadMediumUnknownFormat                           AdditionalSenseCode = 0x3001
+	CannotReadMediumIncompatibleFormat                      AdditionalSenseCode = 0x3002
+	CleaningCartridgeInstalled                              AdditionalSenseCode = 0x3003
+	CannotWriteMediumUnknownFormat                          AdditionalSenseCode = 0x3004
+	CannotWriteMediumIncompatibleFormat                     AdditionalSenseCode = 0x3005
+	CannotFormatMediumIncompatibleMedium                    AdditionalSenseCode = 0x3006
+	CleaningFailure                                         AdditionalSenseCode = 0x3007
+	CannotWriteApplicationCodeMismatch                      AdditionalSenseCode = 0x3008
+	CurrentSessionNotFixatedForAppend                       AdditionalSenseCode = 0x3009
+	CleaningRequestRejected                                 AdditionalSenseCode = 0x300a
+	WormMediumOverwriteAttempted                            AdditionalSenseCode = 0x300c
+	WormMediumIntegrityCheck                                AdditionalSenseCode = 0x300d
+	MediumNotFormatted                                      AdditionalSenseCode = 0x3010
+	IncompatibleVolumeType                                  AdditionalSenseCode = 0x3011
+	IncompatibleVolumeQualifier                             AdditionalSenseCode = 0x3012
+	CleaningVolumeExpired                                   AdditionalSenseCode = 0x3013
+	MediumFormatCorrupted                                   AdditionalSenseCode = 0x3100
+	FormatCommandFailed                                     AdditionalSenseCode = 0x3101
+	ZonedFormattingFailedDueToSpareLinking                  AdditionalSenseCode = 0x3102
+	SanitizeCommandFailed                                   AdditionalSenseCode = 0x3103
+	DepopulationFailed                                      AdditionalSenseCode = 0x3104
+	NoDefectSpareLocationAvailable                          AdditionalSenseCode = 0x3200
+	DefectListUpdateFailure                                 AdditionalSenseCode = 0x3201
+	TapeLengthError                                         AdditionalSenseCode = 0x3300
+	EnclosureFailure                                        AdditionalSenseCode = 0x3400
+	EnclosureServicesFailure                                AdditionalSenseCode = 0x3500
+	UnsupportedEnclosureFunction                            AdditionalSenseCode = 0x3501
+	EnclosureServicesUnavailable                            AdditionalSenseCode = 0x3502
+	EnclosureServicesTransferFailure                        AdditionalSenseCode = 0x3503
+	EnclosureServicesTransferRefused                        AdditionalSenseCode = 0x3504
+	EnclosureServicesChecksumError                          AdditionalSenseCode = 0x3505
+	RibbonInkOrTonerFailure                                 AdditionalSenseCode = 0x3600
+	RoundedParameter                                        AdditionalSenseCode = 0x3700
+	EventStatusNotification                                 AdditionalSenseCode = 0x3800
+	EsnPowerManagementClassEvent                            AdditionalSenseCode = 0x3802
+	EsnMediaClassEvent                                      AdditionalSenseCode = 0x3804
+	EsnDeviceBusyClassEvent                                 AdditionalSenseCode = 0x3806
+	ThinProvisioningSoftThresholdReached                    AdditionalSenseCode = 0x3807
+	SavingParametersNotSupported                            AdditionalSenseCode = 0x3900
+	MediumNotPresent                                        AdditionalSenseCode = 0x3a00
+	MediumNotPresentTrayClosed                              AdditionalSenseCode = 0x3a01
+	MediumNotPresentTrayOpen                                AdditionalSenseCode = 0x3a02
+	MediumNotPresentLoadable                                AdditionalSenseCode = 0x3a03
+	MediumNotPresentMediumAuxiliaryMemoryAccessible         AdditionalSenseCode = 0x3a04
+	SequentialPositioningError                              AdditionalSenseCode = 0x3b00
+	TapePositionErrorAtBeginningOfMedium                    AdditionalSenseCode = 0x3b01
+	TapePositionErrorAtEndOfMedium                          AdditionalSenseCode = 0x3b02
+	TapeOrElectronicVerticalFormsUnitNotReady               AdditionalSenseCode = 0x3b03
+	SlewFailure                                             AdditionalSenseCode = 0x3b04
+	PaperJam                                                AdditionalSenseCode = 0x3b05
+	FailedToSenseTopOfForm                                  AdditionalSenseCode = 0x3b06
+	FailedToSenseBottomOfForm                               AdditionalSenseCode = 0x3b07
+	RepositionError                                         AdditionalSenseCode = 0x3b08
+	ReadPastEndOfMedium                                     AdditionalSenseCode = 0x3b09
+	ReadPastBeginningOfMedium                               AdditionalSenseCode = 0x3b0a
+	PositionPastEndOfMedium                                 AdditionalSenseCode = 0x3b0b
+	PositionPastBeginningOfMedium                           AdditionalSenseCode = 0x3b0c
+	MediumDestinationElementFull                            AdditionalSenseCode = 0x3b0d
+	MediumSourceElementEmpty                                AdditionalSenseCode = 0x3b0e
+	EndOfMediumReached                                      AdditionalSenseCode = 0x3b0f
+	MediumMagazineNotAccessible                             AdditionalSenseCode = 0x3b11
+	MediumMagazineRemoved                                   AdditionalSenseCode = 0x3b12
+	MediumMagazineInserted                                  AdditionalSenseCode = 0x3b13
+	MediumMagazineLocked                                    AdditionalSenseCode = 0x3b14
+	MediumMagazineUnlocked                                  AdditionalSenseCode = 0x3b15
+	MechanicalPositioningOrChangerError                     AdditionalSenseCode = 0x3b16
+	ReadPastEndOfUserObject                                 AdditionalSenseCode = 0x3b17
+	ElementDisabled                                         AdditionalSenseCode = 0x3b18
+	ElementEnabled                                          AdditionalSenseCode = 0x3b19
+	DataTransferDeviceRemoved                               AdditionalSenseCode = 0x3b1a
+	DataTransferDeviceInserted                              AdditionalSenseCode = 0x3b1b
+	TooManyLogicalObjectsOnPartitionToSupportOperation      AdditionalSenseCode = 0x3b1c
+	InvalidBitsInIdentifyMessage                            AdditionalSenseCode = 0x3d00
+	LogicalUnitHasNotSelfConfiguredYet                      AdditionalSenseCode = 0x3e00
+	LogicalUnitFailure                                      AdditionalSenseCode = 0x3e01
+	TimeoutOnLogicalUnit                                    AdditionalSenseCode = 0x3e02
+	LogicalUnitFailedSelfTest                               AdditionalSenseCode = 0x3e03
+	LogicalUnitUnableToUpdateSelfTestLog                    AdditionalSenseCode = 0x3e04
+	TargetOperatingConditionsHaveChanged                    AdditionalSenseCode = 0x3f00
+	MicrocodeHasBeenChanged                                 AdditionalSenseCode = 0x3f01
+	ChangedOperatingDefinition                              AdditionalSenseCode = 0x3f02
+	InquiryDataHasChanged                                   AdditionalSenseCode = 0x3f03
+	ComponentDeviceAttached                                 AdditionalSenseCode = 0x3f04
+	DeviceIdentifierChanged                                 AdditionalSenseCode = 0x3f05
+	RedundancyGroupCreatedOrModified                        AdditionalSenseCode = 0x3f06
+	RedundancyGroupDeleted                                  AdditionalSenseCode = 0x3f07
+	SpareCreatedOrModified                                  AdditionalSenseCode = 0x3f08
+	SpareDeleted                                            AdditionalSenseCode = 0x3f09
+	VolumeSetCreatedOrModified                              AdditionalSenseCode = 0x3f0a
+	VolumeSetDeleted                                        AdditionalSenseCode = 0x3f0b
+	VolumeSetDeassigned                                     AdditionalSenseCode = 0x3f0c
+	VolumeSetReassigned                                     AdditionalSenseCode = 0x3f0d
+	ReportedLunsDataHasChanged                              AdditionalSenseCode = 0x3f0e
+	EchoBufferOverwritten                                   AdditionalSenseCode = 0x3f0f
+	MediumLoadable                                          AdditionalSenseCode = 0x3f10
+	MediumAuxiliaryMemoryAccessible                         AdditionalSenseCode = 0x3f11
+	IscsiIpAddressAdded                                     AdditionalSenseCode = 0x3f12
+	IscsiIpAddressRemoved                                   AdditionalSenseCode = 0x3f13
+	IscsiIpAddressChanged                                   AdditionalSenseCode = 0x3f14
+	InspectReferralsSenseDescriptors                        AdditionalSenseCode = 0x3f15
+	MicrocodeHasBeenChangedWithoutReset                     AdditionalSenseCode = 0x3f16
+	ZoneTransitionToFull                                    AdditionalSenseCode = 0x3f17
+	BindCompleted                                           AdditionalSenseCode = 0x3f18
+	BindRedirected                                          AdditionalSenseCode = 0x3f19
+	SubsidiaryBindingChanged                                AdditionalSenseCode = 0x3f1a
+	RamFailureshouldUse40Nn                                 AdditionalSenseCode = 0x4000
+	DataPathFailureshouldUse40Nn                            AdditionalSenseCode = 0x4100
+	PowerOnOrSelfTestFailureshouldUse40Nn                   AdditionalSenseCode = 0x4200
+	MessageError                                            AdditionalSenseCode = 0x4300
+	InternalTargetFailure                                   AdditionalSenseCode = 0x4400
+	PersistentReservationInformationLost                    AdditionalSenseCode = 0x4401
+	AtaDeviceFailedSetFeatures                              AdditionalSenseCode = 0x4471
+	SelectOrReselectFailure                                 AdditionalSenseCode = 0x4500
+	UnsuccessfulSoftReset                                   AdditionalSenseCode = 0x4600
+	ScsiParityError                                         AdditionalSenseCode = 0x4700
+	DataPhaseCrcErrorDetected                               AdditionalSenseCode = 0x4701
+	ScsiParityErrorDetectedDuringStDataPhase                AdditionalSenseCode = 0x4702
+	InformationUnitIucrcErrorDetected                       AdditionalSenseCode = 0x4703
+	AsynchronousInformationProtectionErrorDetected          AdditionalSenseCode = 0x4704
+	ProtocolServiceCrcError                                 AdditionalSenseCode = 0x4705
+	PhyTestFunctionInProgress                               AdditionalSenseCode = 0x4706
+	SomeCommandsClearedByIscsiProtocolEvent                 AdditionalSenseCode = 0x477f
+	InitiatorDetectedErrorMessageReceived                   AdditionalSenseCode = 0x4800
+	InvalidMessageError                                     AdditionalSenseCode = 0x4900
+	CommandPhaseError                                       AdditionalSenseCode = 0x4a00
+	DataPhaseError                                          AdditionalSenseCode = 0x4b00
+	InvalidTargetPortTransferTagReceived                    AdditionalSenseCode = 0x4b01
+	TooMuchWriteData                                        AdditionalSenseCode = 0x4b02
+	AcknakTimeout                                           AdditionalSenseCode = 0x4b03
+	NakReceived                                             AdditionalSenseCode = 0x4b04
+	DataOffsetError                                         AdditionalSenseCode = 0x4b05
+	InitiatorResponseTimeout                                AdditionalSenseCode = 0x4b06
+	ConnectionLost                                          AdditionalSenseCode = 0x4b07
+	DataInBufferOverflowDataBufferSize                      AdditionalSenseCode = 0x4b08
+	DataInBufferOverflowDataBufferDescriptorArea            AdditionalSenseCode = 0x4b09
+	DataInBufferError                                       AdditionalSenseCode = 0x4b0a
+	DataOutBufferOverflowDataBufferSize                     AdditionalSenseCode = 0x4b0b
+	DataOutBufferOverflowDataBufferDescriptorArea           AdditionalSenseCode = 0x4b0c
+	DataOutBufferError                                      AdditionalSenseCode = 0x4b0d
+	PcieFabricError                                         AdditionalSenseCode = 0x4b0e
+	PcieCompletionTimeout                                   AdditionalSenseCode = 0x4b0f
+	PcieCompleterAbort                                      AdditionalSenseCode = 0x4b10
+	PciePoisonedTlpReceived                                 AdditionalSenseCode = 0x4b11
+	PcieEcrcCheckFailed                                     AdditionalSenseCode = 0x4b12
+	PcieUnsupportedRequest                                  AdditionalSenseCode = 0x4b13
+	PcieAcsViolation                                        AdditionalSenseCode = 0x4b14
+	PcieTlpPrefixBlocked                                    AdditionalSenseCode = 0x4b15
+	LogicalUnitFailedSelfConfiguration                      AdditionalSenseCode = 0x4c00
+	OverlappedCommandsAttempted                             AdditionalSenseCode = 0x4e00
+	WriteAppendError                                        AdditionalSenseCode = 0x5000
+	WriteAppendPositionError                                AdditionalSenseCode = 0x5001
+	PositionErrorRelatedToTiming                            AdditionalSenseCode = 0x5002
+	EraseFailure                                            AdditionalSenseCode = 0x5100
+	EraseFailureIncompleteEraseOperationDetected            AdditionalSenseCode = 0x5101
+	CartridgeFault                                          AdditionalSenseCode = 0x5200
+	MediaLoadOrEjectFailed                                  AdditionalSenseCode = 0x5300
+	UnloadTapeFailure                                       AdditionalSenseCode = 0x5301
+	MediumRemovalPrevented                                  AdditionalSenseCode = 0x5302
+	MediumRemovalPreventedByDataTransferElement             AdditionalSenseCode = 0x5303
+	MediumThreadOrUnthreadFailure                           AdditionalSenseCode = 0x5304
+	VolumeIdentifierInvalid                                 AdditionalSenseCode = 0x5305
+	VolumeIdentifierMissing                                 AdditionalSenseCode = 0x5306
+	DuplicateVolumeIdentifier                               AdditionalSenseCode = 0x5307
+	ElementStatusUnknown                                    AdditionalSenseCode = 0x5308
+	DataTransferDeviceErrorLoadFailed                       AdditionalSenseCode = 0x5309
+	DataTransferDeviceErrorUnloadFailed                     AdditionalSenseCode = 0x530a
+	DataTransferDeviceErrorUnloadMissing                    AdditionalSenseCode = 0x530b
+	DataTransferDeviceErrorEjectFailed                      AdditionalSenseCode = 0x530c
+	DataTransferDeviceErrorLibraryCommunicationFailed       AdditionalSenseCode = 0x530d
+	ScsiToHostSystemInterfaceFailure                        AdditionalSenseCode = 0x5400
+	SystemResourceFailure                                   AdditionalSenseCode = 0x5500
+	SystemBufferFull                                        AdditionalSenseCode = 0x5501
+	InsufficientReservationResources                        AdditionalSenseCode = 0x5502
+	InsufficientResources                                   AdditionalSenseCode = 0x5503
+	InsufficientRegistrationResources                       AdditionalSenseCode = 0x5504
+	InsufficientAccessControlResources                      AdditionalSenseCode = 0x5505
+	AuxiliaryMemoryOutOfSpace                               AdditionalSenseCode = 0x5506
+	QuotaError                                              AdditionalSenseCode = 0x5507
+	MaximumNumberOfSupplementalDecryptionKeysExceeded       AdditionalSenseCode = 0x5508
+	MediumAuxiliaryMemoryNotAccessible                      AdditionalSenseCode = 0x5509
+	DataCurrentlyUnavailable                                AdditionalSenseCode = 0x550a
+	InsufficientPowerForOperation                           AdditionalSenseCode = 0x550b
+	InsufficientResourcesToCreateRod                        AdditionalSenseCode = 0x550c
+	InsufficientResourcesToCreateRodToken                   AdditionalSenseCode = 0x550d
+	InsufficientZoneResources                               AdditionalSenseCode = 0x550e
+	InsufficientZoneResourcesToCompleteWrite                AdditionalSenseCode = 0x550f
+	MaximumNumberOfStreamsOpen                              AdditionalSenseCode = 0x5510
+	InsufficientResourcesToBind                             AdditionalSenseCode = 0x5511
+	UnableToRecoverTableOfContents                          AdditionalSenseCode = 0x5700
+	GenerationDoesNotExist                                  AdditionalSenseCode = 0x5800
+	UpdatedBlockRead                                        AdditionalSenseCode = 0x5900
+	OperatorRequestOrStateChangeInput                       AdditionalSenseCode = 0x5a00
+	OperatorMediumRemovalRequest                            AdditionalSenseCode = 0x5a01
+	OperatorSelectedWriteProtect                            AdditionalSenseCode = 0x5a02
+	OperatorSelectedWritePermit                             AdditionalSenseCode = 0x5a03
+	LogException                                            AdditionalSenseCode = 0x5b00
+	ThresholdConditionMet                                   AdditionalSenseCode = 0x5b01
+	LogCounterAtMaximum                                     AdditionalSenseCode = 0x5b02
+	LogListCodesExhausted                                   AdditionalSenseCode = 0x5b03
+	RplStatusChange                                         AdditionalSenseCode = 0x5c00
+	SpindlesSynchronized                                    AdditionalSenseCode = 0x5c01
+	SpindlesNotSynchronized                                 AdditionalSenseCode = 0x5c02
+	FailurePredictionThresholdExceeded                      AdditionalSenseCode = 0x5d00
+	MediaFailurePredictionThresholdExceeded                 AdditionalSenseCode = 0x5d01
+	LogicalUnitFailurePredictionThresholdExceeded           AdditionalSenseCode = 0x5d02
+	SpareAreaExhaustionPredictionThresholdExceeded          AdditionalSenseCode = 0x5d03
+	HardwareImpendingFailureGeneralHardDriveFailure         AdditionalSenseCode = 0x5d10
+	HardwareImpendingFailureDriveErrorRateTooHigh           AdditionalSenseCode = 0x5d11
+	HardwareImpendingFailureDataErrorRateTooHigh            AdditionalSenseCode = 0x5d12
+	HardwareImpendingFailureSeekErrorRateTooHigh            AdditionalSenseCode = 0x5d13
+	HardwareImpendingFailureTooManyBlockReassigns           AdditionalSenseCode = 0x5d14
+	HardwareImpendingFailureAccessTimesTooHigh              AdditionalSenseCode = 0x5d15
+	HardwareImpendingFailureStartUnitTimesTooHigh           AdditionalSenseCode = 0x5d16
+	HardwareImpendingFailureChannelParametrics              AdditionalSenseCode = 0x5d17
+	HardwareImpendingFailureControllerDetected              AdditionalSenseCode = 0x5d18
+	HardwareImpendingFailureThroughputPerformance           AdditionalSenseCode = 0x5d19
+	HardwareImpendingFailureSeekTimePerformance             AdditionalSenseCode = 0x5d1a
+	HardwareImpendingFailureSpinUpRetryCount                AdditionalSenseCode = 0x5d1b
+	HardwareImpendingFailureDriveCalibrationRetryCount      AdditionalSenseCode = 0x5d1c
+	HardwareImpendingFailurePowerLossProtectionCircuit      AdditionalSenseCode = 0x5d1d
+	ControllerImpendingFailureGeneralHardDriveFailure       AdditionalSenseCode = 0x5d20
+	ControllerImpendingFailureDriveErrorRateTooHigh         AdditionalSenseCode = 0x5d21
+	ControllerImpendingFailureDataErrorRateTooHigh          AdditionalSenseCode = 0x5d22
+	ControllerImpendingFailureSeekErrorRateTooHigh          AdditionalSenseCode = 0x5d23
+	ControllerImpendingFailureTooManyBlockReassigns         AdditionalSenseCode = 0x5d24
+	ControllerImpendingFailureAccessTimesTooHigh            AdditionalSenseCode = 0x5d25
+	ControllerImpendingFailureStartUnitTimesTooHigh         AdditionalSenseCode = 0x5d26
+	ControllerImpendingFailureChannelParametrics            AdditionalSenseCode = 0x5d27
+	ControllerImpendingFailureControllerDetected            AdditionalSenseCode = 0x5d28
+	ControllerImpendingFailureThroughputPerformance         AdditionalSenseCode = 0x5d29
+	ControllerImpendingFailureSeekTimePerformance           AdditionalSenseCode = 0x5d2a
+	ControllerImpendingFailureSpinUpRetryCount              AdditionalSenseCode = 0x5d2b
+	ControllerImpendingFailureDriveCalibrationRetryCount    AdditionalSenseCode = 0x5d2c
+	DataChannelImpendingFailureGeneralHardDriveFailure      AdditionalSenseCode = 0x5d30
+	DataChannelImpendingFailureDriveErrorRateTooHigh        AdditionalSenseCode = 0x5d31
+	DataChannelImpendingFailureDataErrorRateTooHigh         AdditionalSenseCode = 0x5d32
+	DataChannelImpendingFailureSeekErrorRateTooHigh         AdditionalSenseCode = 0x5d33
+	DataChannelImpendingFailureTooManyBlockReassigns        AdditionalSenseCode = 0x5d34
+	DataChannelImpendingFailureAccessTimesTooHigh           AdditionalSenseCode = 0x5d35
+	DataChannelImpendingFailureStartUnitTimesTooHigh        AdditionalSenseCode = 0x5d36
+	DataChannelImpendingFailureChannelParametrics           AdditionalSenseCode = 0x5d37
+	DataChannelImpendingFailureControllerDetected           AdditionalSenseCode = 0x5d38
+	DataChannelImpendingFailureThroughputPerformance        AdditionalSenseCode = 0x5d39
+	DataChannelImpendingFailureSeekTimePerformance          AdditionalSenseCode = 0x5d3a
+	DataChannelImpendingFailureSpinUpRetryCount             AdditionalSenseCode = 0x5d3b
+	DataChannelImpendingFailureDriveCalibrationRetryCount   AdditionalSenseCode = 0x5d3c
+	ServoImpendingFailureGeneralHardDriveFailure            AdditionalSenseCode = 0x5d40
+	ServoImpendingFailureDriveErrorRateTooHigh              AdditionalSenseCode = 0x5d41
+	ServoImpendingFailureDataErrorRateTooHigh               AdditionalSenseCode = 0x5d42
+	ServoImpendingFailureSeekErrorRateTooHigh               AdditionalSenseCode = 0x5d43
+	ServoImpendingFailureTooManyBlockReassigns              AdditionalSenseCode = 0x5d44
+	ServoImpendingFailureAccessTimesTooHigh                 AdditionalSenseCode = 0x5d45
+	ServoImpendingFailureStartUnitTimesTooHigh              AdditionalSenseCode = 0x5d46
+	ServoImpendingFailureChannelParametrics                 AdditionalSenseCode = 0x5d47
+	ServoImpendingFailureControllerDetected                 AdditionalSenseCode = 0x5d48
+	ServoImpendingFailureThroughputPerformance              AdditionalSenseCode = 0x5d49
+	ServoImpendingFailureSeekTimePerformance                AdditionalSenseCode = 0x5d4a
+	ServoImpendingFailureSpinUpRetryCount                   AdditionalSenseCode = 0x5d4b
+	ServoImpendingFailureDriveCalibrationRetryCount         AdditionalSenseCode = 0x5d4c
+	SpindleImpendingFailureGeneralHardDriveFailure          AdditionalSenseCode = 0x5d50
+	SpindleImpendingFailureDriveErrorRateTooHigh            AdditionalSenseCode = 0x5d51
+	SpindleImpendingFailureDataErrorRateTooHigh             AdditionalSenseCode = 0x5d52
+	SpindleImpendingFailureSeekErrorRateTooHigh             AdditionalSenseCode = 0x5d53
+	SpindleImpendingFailureTooManyBlockReassigns            AdditionalSenseCode = 0x5d54
+	SpindleImpendingFailureAccessTimesTooHigh               AdditionalSenseCode = 0x5d55
+	SpindleImpendingFailureStartUnitTimesTooHigh            AdditionalSenseCode = 0x5d56
+	SpindleImpendingFailureChannelParametrics               AdditionalSenseCode = 0x5d57
+	SpindleImpendingFailureControllerDetected               AdditionalSenseCode = 0x5d58
+	SpindleImpendingFailureThroughputPerformance            AdditionalSenseCode = 0x5d59
+	SpindleImpendingFailureSeekTimePerformance              AdditionalSenseCode = 0x5d5a
+	SpindleImpendingFailureSpinUpRetryCount                 AdditionalSenseCode = 0x5d5b
+	SpindleImpendingFailureDriveCalibrationRetryCount       AdditionalSenseCode = 0x5d5c
+	FirmwareImpendingFailureGeneralHardDriveFailure         AdditionalSenseCode = 0x5d60
+	FirmwareImpendingFailureDriveErrorRateTooHigh           AdditionalSenseCode = 0x5d61
+	FirmwareImpendingFailureDataErrorRateTooHigh            AdditionalSenseCode = 0x5d62
+	FirmwareImpendingFailureSeekErrorRateTooHigh            AdditionalSenseCode = 0x5d63
+	FirmwareImpendingFailureTooManyBlockReassigns           AdditionalSenseCode = 0x5d64
+	FirmwareImpendingFailureAccessTimesTooHigh              AdditionalSenseCode = 0x5d65
+	FirmwareImpendingFailureStartUnitTimesTooHigh           AdditionalSenseCode = 0x5d66
+	FirmwareImpendingFailureChannelParametrics              AdditionalSenseCode = 0x5d67
+	FirmwareImpendingFailureControllerDetected              AdditionalSenseCode = 0x5d68
+	FirmwareImpendingFailureThroughputPerformance           AdditionalSenseCode = 0x5d69
+	FirmwareImpendingFailureSeekTimePerformance             AdditionalSenseCode = 0x5d6a
+	FirmwareImpendingFailureSpinUpRetryCount                AdditionalSenseCode = 0x5d6b
+	FirmwareImpendingFailureDriveCalibrationRetryCount      AdditionalSenseCode = 0x5d6c
+	MediaImpendingFailureEnduranceLimitMet                  AdditionalSenseCode = 0x5d73
+	FailurePredictionThresholdExceededfalse                 AdditionalSenseCode = 0x5dff
+	LowPowerConditionOn                                     AdditionalSenseCode = 0x5e00
+	IdleConditionActivatedByTimer                           AdditionalSenseCode = 0x5e01
+	StandbyConditionActivatedByTimer                        AdditionalSenseCode = 0x5e02
+	IdleConditionActivatedByCommand                         AdditionalSenseCode = 0x5e03
+	StandbyConditionActivatedByCommand                      AdditionalSenseCode = 0x5e04
+	IdleBConditionActivatedByTimer                          AdditionalSenseCode = 0x5e05
+	IdleBConditionActivatedByCommand                        AdditionalSenseCode = 0x5e06
+	IdleCConditionActivatedByTimer                          AdditionalSenseCode = 0x5e07
+	IdleCConditionActivatedByCommand                        AdditionalSenseCode = 0x5e08
+	StandbyYConditionActivatedByTimer                       AdditionalSenseCode = 0x5e09
+	StandbyYConditionActivatedByCommand                     AdditionalSenseCode = 0x5e0a
+	PowerStateChangeToActive                                AdditionalSenseCode = 0x5e41
+	PowerStateChangeToIdle                                  AdditionalSenseCode = 0x5e42
+	PowerStateChangeToStandby                               AdditionalSenseCode = 0x5e43
+	PowerStateChangeToSleep                                 AdditionalSenseCode = 0x5e45
+	PowerStateChangeToDeviceControl                         AdditionalSenseCode = 0x5e47
+	LampFailure                                             AdditionalSenseCode = 0x6000
+	VideoAcquisitionError                                   AdditionalSenseCode = 0x6100
+	UnableToAcquireVideo                                    AdditionalSenseCode = 0x6101
+	OutOfFocus                                              AdditionalSenseCode = 0x6102
+	ScanHeadPositioningError                                AdditionalSenseCode = 0x6200
+	EndOfUserAreaEncounteredOnThisTrack                     AdditionalSenseCode = 0x6300
+	PacketDoesNotFitInAvailableSpace                        AdditionalSenseCode = 0x6301
+	IllegalModeForThisTrack                                 AdditionalSenseCode = 0x6400
+	InvalidPacketSize                                       AdditionalSenseCode = 0x6401
+	VoltageFault                                            AdditionalSenseCode = 0x6500
+	AutomaticDocumentFeederCoverUp                          AdditionalSenseCode = 0x6600
+	AutomaticDocumentFeederLiftUp                           AdditionalSenseCode = 0x6601
+	DocumentJamInAutomaticDocumentFeeder                    AdditionalSenseCode = 0x6602
+	DocumentMissFeedAutomaticInDocumentFeeder               AdditionalSenseCode = 0x6603
+	ConfigurationFailure                                    AdditionalSenseCode = 0x6700
+	ConfigurationOfIncapableLogicalUnitsFailed              AdditionalSenseCode = 0x6701
+	AddLogicalUnitFailed                                    AdditionalSenseCode = 0x6702
+	ModificationOfLogicalUnitFailed                         AdditionalSenseCode = 0x6703
+	ExchangeOfLogicalUnitFailed                             AdditionalSenseCode = 0x6704
+	RemoveOfLogicalUnitFailed                               AdditionalSenseCode = 0x6705
+	AttachmentOfLogicalUnitFailed                           AdditionalSenseCode = 0x6706
+	CreationOfLogicalUnitFailed                             AdditionalSenseCode = 0x6707
+	AssignFailureOccurred                                   AdditionalSenseCode = 0x6708
+	MultiplyAssignedLogicalUnit                             AdditionalSenseCode = 0x6709
+	SetTargetPortGroupsCommandFailed                        AdditionalSenseCode = 0x670a
+	AtaDeviceFeatureNotEnabled                              AdditionalSenseCode = 0x670b
+	CommandRejected                                         AdditionalSenseCode = 0x670c
+	ExplicitBindNotAllowed                                  AdditionalSenseCode = 0x670d
+	LogicalUnitNotConfigured                                AdditionalSenseCode = 0x6800
+	SubsidiaryLogicalUnitNotConfigured                      AdditionalSenseCode = 0x6801
+	DataLossOnLogicalUnit                                   AdditionalSenseCode = 0x6900
+	MultipleLogicalUnitFailures                             AdditionalSenseCode = 0x6901
+	ParitydataMismatch                                      AdditionalSenseCode = 0x6902
+	InformationalReferToLog                                 AdditionalSenseCode = 0x6a00
+	StateChangeHasOccurred                                  AdditionalSenseCode = 0x6b00
+	RedundancyLevelGotBetter                                AdditionalSenseCode = 0x6b01
+	RedundancyLevelGotWorse                                 AdditionalSenseCode = 0x6b02
+	RebuildFailureOccurred                                  AdditionalSenseCode = 0x6c00
+	RecalculateFailureOccurred                              AdditionalSenseCode = 0x6d00
+	CommandToLogicalUnitFailed                              AdditionalSenseCode = 0x6e00
+	CopyProtectionKeyExchangeFailureAuthentication          AdditionalSenseCode = 0x6f00
+	CopyProtectionKeyExchangeFailureKeyNotPresent           AdditionalSenseCode = 0x6f01
+	CopyProtectionKeyExchangeFailureKeyNotEstablished       AdditionalSenseCode = 0x6f02
+	ReadOfScrambledSectorWithoutAuthentication              AdditionalSenseCode = 0x6f03
+	MediaRegionCodeIsMismatchedToLogicalUnitRegion          AdditionalSenseCode = 0x6f04
+	DriveRegionMustBePermanentregionResetCountError         AdditionalSenseCode = 0x6f05
+	InsufficientBlockCountForBindingNonceRecording          AdditionalSenseCode = 0x6f06
+	ConflictInBindingNonceRecording                         AdditionalSenseCode = 0x6f07
+	InsufficientPermission                                  AdditionalSenseCode = 0x6f08
+	InvalidDriveHostPairingServer                           AdditionalSenseCode = 0x6f09
+	DriveHostPairingSuspended                               AdditionalSenseCode = 0x6f0a
+	DecompressionExceptionLongAlgorithmId                   AdditionalSenseCode = 0x7100
+	SessionFixationError                                    AdditionalSenseCode = 0x7200
+	SessionFixationErrorWritingLeadIn                       AdditionalSenseCode = 0x7201
+	SessionFixationErrorWritingLeadOut                      AdditionalSenseCode = 0x7202
+	SessionFixationErrorIncompleteTrackInSession            AdditionalSenseCode = 0x7203
+	EmptyOrPartiallyWrittenReservedTrack                    AdditionalSenseCode = 0x7204
+	NoMoreTrackReservationsAllowed                          AdditionalSenseCode = 0x7205
+	RmzExtensionIsNotAllowed                                AdditionalSenseCode = 0x7206
+	NoMoreTestZoneExtensionsAreAllowed                      AdditionalSenseCode = 0x7207
+	CdControlError                                          AdditionalSenseCode = 0x7300
+	PowerCalibrationAreaAlmostFull                          AdditionalSenseCode = 0x7301
+	PowerCalibrationAreaIsFull                              AdditionalSenseCode = 0x7302
+	PowerCalibrationAreaError                               AdditionalSenseCode = 0x7303
+	ProgramMemoryAreaUpdateFailure                          AdditionalSenseCode = 0x7304
+	ProgramMemoryAreaIsFull                                 AdditionalSenseCode = 0x7305
+	RmapmaIsAlmostFull                                      AdditionalSenseCode = 0x7306
+	CurrentPowerCalibrationAreaAlmostFull                   AdditionalSenseCode = 0x7310
+	CurrentPowerCalibrationAreaIsFull                       AdditionalSenseCode = 0x7311
+	RdzIsFull                                               AdditionalSenseCode = 0x7317
+	SecurityError                                           AdditionalSenseCode = 0x7400
+	UnableToDecryptData                                     AdditionalSenseCode = 0x7401
+	UnencryptedDataEncounteredWhileDecrypting               AdditionalSenseCode = 0x7402
+	IncorrectDataEncryptionKey                              AdditionalSenseCode = 0x7403
+	CryptographicIntegrityValidationFailed                  AdditionalSenseCode = 0x7404
+	ErrorDecryptingData                                     AdditionalSenseCode = 0x7405
+	UnknownSignatureVerificationKey                         AdditionalSenseCode = 0x7406
+	EncryptionParametersNotUseable                          AdditionalSenseCode = 0x7407
+	DigitalSignatureValidationFailure                       AdditionalSenseCode = 0x7408
+	EncryptionModeMismatchOnRead                            AdditionalSenseCode = 0x7409
+	EncryptedBlockNotRawReadEnabled                         AdditionalSenseCode = 0x740a
+	IncorrectEncryptionParameters                           AdditionalSenseCode = 0x740b
+	UnableToDecryptParameterList                            AdditionalSenseCode = 0x740c
+	EncryptionAlgorithmDisabled                             AdditionalSenseCode = 0x740d
+	SaCreationParameterValueInvalid                         AdditionalSenseCode = 0x7410
+	SaCreationParameterValueRejected                        AdditionalSenseCode = 0x7411
+	InvalidSaUsage                                          AdditionalSenseCode = 0x7412
+	DataEncryptionConfigurationPrevented                    AdditionalSenseCode = 0x7421
+	SaCreationParameterNotSupported                         AdditionalSenseCode = 0x7430
+	AuthenticationFailed                                    AdditionalSenseCode = 0x7440
+	ExternalDataEncryptionKeyManagerAccessError             AdditionalSenseCode = 0x7461
+	ExternalDataEncryptionKeyManagerError                   AdditionalSenseCode = 0x7462
+	ExternalDataEncryptionKeyNotFound                       AdditionalSenseCode = 0x7463
+	ExternalDataEncryptionRequestNotAuthorized              AdditionalSenseCode = 0x7464
+	ExternalDataEncryptionControlTimeout                    AdditionalSenseCode = 0x746e
+	ExternalDataEncryptionControlError                      AdditionalSenseCode = 0x746f
+	LogicalUnitAccessNotAuthorized                          AdditionalSenseCode = 0x7471
+	SecurityConflictInTranslatedDevice                      AdditionalSenseCode = 0x7479
+)
+
+var additionalSenseCodeDesc = map[AdditionalSenseCode]string{
+	0x0000: "no additional sense information",
+	0x0001: "filemark detected",
+	0x0002: "end-of-partition/medium detected",
+	0x0003: "setmark detected",
+	0x0004: "beginning-of-partition/medium detected",
+	0x0005: "end-of-data detected",
+	0x0006: "i/o process terminated",
+	0x0007: "programmable early warning detected",
+	0x0011: "audio play operation in progress",
+	0x0012: "audio play operation paused",
+	0x0013: "audio play operation successfully completed",
+	0x0014: "audio play operation stopped due to error",
+	0x0015: "no current audio status to return",
+	0x0016: "operation in progress",
+	0x0017: "cleaning requested",
+	0x0018: "erase operation in progress",
+	0x0019: "locate operation in progress",
+	0x001a: "rewind operation in progress",
+	0x001b: "set capacity operation in progress",
+	0x001c: "verify operation in progress",
+	0x001d: "ata pass through information available",
+	0x001e: "conflicting sa creation request",
+	0x001f: "logical unit transitioning to another power condition",
+	0x0020: "extended copy information available",
+	0x0021: "atomic command aborted due to aca",
+	0x0022: "deferred microcode is pending",
+	0x0100: "no index/sector signal",
+	0x0200: "no seek complete",
+	0x0300: "peripheral device write fault",
+	0x0301: "no write current",
+	0x0302: "excessive write errors",
+	0x0400: "logical unit not ready, cause not reportable",
+	0x0401: "logical unit is in process of becoming ready",
+	0x0402: "logical unit not ready, initializing command required",
+	0x0403: "logical unit not ready, manual intervention required",
+	0x0404: "logical unit not ready, format in progress",
+	0x0405: "logical unit not ready, rebuild in progress",
+	0x0406: "logical unit not ready, recalculation in progress",
+	0x0407: "logical unit not ready, operation in progress",
+	0x0408: "logical unit not ready, long write in progress",
+	0x0409: "logical unit not ready, self-test in progress",
+	0x040a: "logical unit not accessible, asymmetric access state transition",
+	0x040b: "logical unit not accessible, target port in standby state",
+	0x040c: "logical unit not accessible, target port in unavailable state",
+	0x040d: "logical unit not ready, structure check required",
+	0x040e: "logical unit not ready, security session in progress",
+	0x0410: "logical unit not ready, auxiliary memory not accessible",
+	0x0411: "logical unit not ready, notify (enable spinup) required",
+	0x0412: "logical unit not ready, offline",
+	0x0413: "logical unit not ready, sa creation in progress",
+	0x0414: "logical unit not ready, space allocation in progress",
+	0x0415: "logical unit not ready, robotics disabled",
+	0x0416: "logical unit not ready, configuration required",
+	0x0417: "logical unit not ready, calibration required",
+	0x0418: "logical unit not ready, a door is open",
+	0x0419: "logical unit not ready, operating in sequential mode",
+	0x041a: "logical unit not ready, start stop unit command in progress",
+	0x041b: "logical unit not ready, sanitize in progress",
+	0x041c: "logical unit not ready, additional power use not yet granted",
+	0x041d: "logical unit not ready, configuration in progress",
+	0x041e: "logical unit not ready, microcode activation required",
+	0x041f: "logical unit not ready, microcode download required",
+	0x0420: "logical unit not ready, logical unit reset required",
+	0x0421: "logical unit not ready, hard reset required",
+	0x0422: "logical unit not ready, power cycle required",
+	0x0423: "logical unit not ready, affiliation required",
+	0x0424: "depopulation in progress",
+	0x0500: "logical unit does not respond to selection",
+	0x0600: "no reference position found",
+	0x0700: "multiple peripheral devices selected",
+	0x0800: "logical unit communication failure",
+	0x0801: "logical unit communication time-out",
+	0x0802: "logical unit communication parity error",
+	0x0803: "logical unit communication crc error (ultra-dma/32)",
+	0x0804: "unreachable copy target",
+	0x0900: "track following error",
+	0x0901: "tracking servo failure",
+	0x0902: "focus servo failure",
+	0x0903: "spindle servo failure",
+	0x0904: "head select fault",
+	0x0905: "vibration induced tracking error",
+	0x0a00: "error log overflow",
+	0x0b00: "warning",
+	0x0b01: "warning - specified temperature exceeded",
+	0x0b02: "warning - enclosure degraded",
+	0x0b03: "warning - background self-test failed",
+	0x0b04: "warning - background pre-scan detected medium error",
+	0x0b05: "warning - background medium scan detected medium error",
+	0x0b06: "warning - non-volatile cache now volatile",
+	0x0b07: "warning - degraded power to non-volatile cache",
+	0x0b08: "warning - power loss expected",
+	0x0b09: "warning - device statistics notification active",
+	0x0b0a: "warning - high critical temperature limit exceeded",
+	0x0b0b: "warning - low critical temperature limit exceeded",
+	0x0b0c: "warning - high operating temperature limit exceeded",
+	0x0b0d: "warning - low operating temperature limit exceeded",
+	0x0b0e: "warning - high critical humidity limit exceeded",
+	0x0b0f: "warning - low critical humidity limit exceeded",
+	0x0b10: "warning - high operating humidity limit exceeded",
+	0x0b11: "warning - low operating humidity limit exceeded",
+	0x0b12: "warning - microcode security at risk",
+	0x0b13: "warning - microcode digital signature validation failure",
+	0x0b14: "warning - physical element status change",
+	0x0c00: "write error",
+	0x0c01: "write error - recovered with auto reallocation",
+	0x0c02: "write error - auto reallocation failed",
+	0x0c03: "write error - recommend reassignment",
+	0x0c04: "compression check miscompare error",
+	0x0c05: "data expansion occurred during compression",
+	0x0c06: "block not compressible",
+	0x0c07: "write error - recovery needed",
+	0x0c08: "write error - recovery failed",
+	0x0c09: "write error - loss of streaming",
+	0x0c0a: "write error - padding blocks added",
+	0x0c0b: "auxiliary memory write error",
+	0x0c0c: "write error - unexpected unsolicited data",
+	0x0c0d: "write error - not enough unsolicited data",
+	0x0c0e: "multiple write errors",
+	0x0c0f: "defects in error window",
+	0x0c10: "incomplete multiple atomic write operations",
+	0x0c11: "write error - recovery scan needed",
+	0x0c12: "write error - insufficient zone resources",
+	0x0d00: "error detected by third party temporary initiator",
+	0x0d01: "third party device failure",
+	0x0d02: "copy target device not reachable",
+	0x0d03: "incorrect copy target device type",
+	0x0d04: "copy target device data underrun",
+	0x0d05: "copy target device data overrun",
+	0x0e00: "invalid information unit",
+	0x0e01: "information unit too short",
+	0x0e02: "information unit too long",
+	0x0e03: "invalid field in command information unit",
+	0x1000: "id crc or ecc error",
+	0x1001: "logical block guard check failed",
+	0x1002: "logical block application tag check failed",
+	0x1003: "logical block reference tag check failed",
+	0x1004: "logical block protection error on recover buffered data",
+	0x1005: "logical block protection method error",
+	0x1100: "unrecovered read error",
+	0x1101: "read retries exhausted",
+	0x1102: "error too long to correct",
+	0x1103: "multiple read errors",
+	0x1104: "unrecovered read error - auto reallocate failed",
+	0x1105: "l-ec uncorrectable error",
+	0x1106: "circ unrecovered error",
+	0x1107: "data re-synchronization error",
+	0x1108: "incomplete block read",
+	0x1109: "no gap found",
+	0x110a: "miscorrected error",
+	0x110b: "unrecovered read error - recommend reassignment",
+	0x110c: "unrecovered read error - recommend rewrite the data",
+	0x110d: "de-compression crc error",
+	0x110e: "cannot decompress using declared algorithm",
+	0x110f: "error reading upc/ean number",
+	0x1110: "error reading isrc number",
+	0x1111: "read error - loss of streaming",
+	0x1112: "auxiliary memory read error",
+	0x1113: "read error - failed retransmission request",
+	0x1114: "read error - lba marked bad by application client",
+	0x1115: "write after sanitize required",
+	0x1200: "address mark not found for id field",
+	0x1300: "address mark not found for data field",
+	0x1400: "recorded entity not found",
+	0x1401: "record not found",
+	0x1402: "filemark or setmark not found",
+	0x1403: "end-of-data not found",
+	0x1404: "block sequence error",
+	0x1405: "record not found - recommend reassignment",
+	0x1406: "record not found - data auto-reallocated",
+	0x1407: "locate operation failure",
+	0x1500: "random positioning error",
+	0x1501: "mechanical positioning error",
+	0x1502: "positioning error detected by read of medium",
+	0x1600: "data synchronization mark error",
+	0x1601: "data sync error - data rewritten",
+	0x1602: "data sync error - recommend rewrite",
+	0x1603: "data sync error - data auto-reallocated",
+	0x1604: "data sync error - recommend reassignment",
+	0x1700: "recovered data with no error correction applied",
+	0x1701: "recovered data with retries",
+	0x1702: "recovered data with positive head offset",
+	0x1703: "recovered data with negative head offset",
+	0x1704: "recovered data with retries and/or circ applied",
+	0x1705: "recovered data using previous sector id",
+	0x1706: "recovered data without ecc - data auto-reallocated",
+	0x1707: "recovered data without ecc - recommend reassignment",
+	0x1708: "recovered data without ecc - recommend rewrite",
+	0x1709: "recovered data without ecc - data rewritten",
+	0x1800: "recovered data with error correction applied",
+	0x1801: "recovered data with error corr. & retries applied",
+	0x1802: "recovered data - data auto-reallocated",
+	0x1803: "recovered data with circ",
+	0x1804: "recovered data with l-ec",
+	0x1805: "recovered data - recommend reassignment",
+	0x1806: "recovered data - recommend rewrite",
+	0x1807: "recovered data with ecc - data rewritten",
+	0x1808: "recovered data with linking",
+	0x1900: "defect list error",
+	0x1901: "defect list not available",
+	0x1902: "defect list error in primary list",
+	0x1903: "defect list error in grown list",
+	0x1a00: "parameter list length error",
+	0x1b00: "synchronous data transfer error",
+	0x1c00: "defect list not found",
+	0x1c01: "primary defect list not found",
+	0x1c02: "grown defect list not found",
+	0x1d00: "miscompare during verify operation",
+	0x1d01: "miscompare verify of unmapped lba",
+	0x1e00: "recovered id with ecc correction",
+	0x1f00: "partial defect list transfer",
+	0x2000: "invalid command operation code",
+	0x2001: "access denied - initiator pending-enrolled",
+	0x2002: "access denied - no access rights",
+	0x2003: "access denied - invalid mgmt id key",
+	0x2004: "illegal command while in write capable state",
+	0x2006: "illegal command while in explicit address mode",
+	0x2007: "illegal command while in implicit address mode",
+	0x2008: "access denied - enrollment conflict",
+	0x2009: "access denied - invalid lu identifier",
+	0x200a: "access denied - invalid proxy token",
+	0x200b: "access denied - acl lun conflict",
+	0x200c: "illegal command when not in append-only mode",
+	0x200d: "not an administrative logical unit",
+	0x200e: "not a subsidiary logical unit",
+	0x200f: "not a conglomerate logical unit",
+	0x2100: "logical block address out of range",
+	0x2101: "invalid element address",
+	0x2102: "invalid address for write",
+	0x2103: "invalid write crossing layer jump",
+	0x2104: "unaligned write command",
+	0x2105: "write boundary violation",
+	0x2106: "attempt to read invalid data",
+	0x2107: "read boundary violation",
+	0x2108: "misaligned write command",
+	0x2200: "illegal function (use 20 00, 24 00, or 26 00)",
+	0x2300: "invalid token operation, cause not reportable",
+	0x2301: "invalid token operation, unsupported token type",
+	0x2302: "invalid token operation, remote token usage not supported",
+	0x2303: "invalid token operation, remote rod token creation not supported",
+	0x2304: "invalid token operation, token unknown",
+	0x2305: "invalid token operation, token corrupt",
+	0x2306: "invalid token operation, token revoked",
+	0x2307: "invalid token operation, token expired",
+	0x2308: "invalid token operation, token cancelled",
+	0x2309: "invalid token operation, token deleted",
+	0x230a: "invalid token operation, invalid token length",
+	0x2400: "invalid field in cdb",
+	0x2401: "cdb decryption error",
+	0x2404: "security audit value frozen",
+	0x2405: "security working key frozen",
+	0x2406: "nonce not unique",
+	0x2407: "nonce timestamp out of range",
+	0x2408: "invalid xcdb",
+	0x2409: "invalid fast format",
+	0x2500: "logical unit not supported",
+	0x2600: "invalid field in parameter list",
+	0x2601: "parameter not supported",
+	0x2602: "parameter value invalid",
+	0x2603: "threshold parameters not supported",
+	0x2604: "invalid release of persistent reservation",
+	0x2605: "data decryption error",
+	0x2606: "too many target descriptors",
+	0x2607: "unsupported target descriptor type code",
+	0x2608: "too many segment descriptors",
+	0x2609: "unsupported segment descriptor type code",
+	0x260a: "unexpected inexact segment",
+	0x260b: "inline data length exceeded",
+	0x260c: "invalid operation for copy source or destination",
+	0x260d: "copy segment granularity violation",
+	0x260e: "invalid parameter while port is enabled",
+	0x260f: "invalid data-out buffer integrity check value",
+	0x2610: "data decryption key fail limit reached",
+	0x2611: "incomplete key-associated data set",
+	0x2612: "vendor specific key reference not found",
+	0x2613: "application tag mode page is invalid",
+	0x2614: "tape stream mirroring prevented",
+	0x2615: "copy source or copy destination not authorized",
+	0x2700: "write protected",
+	0x2701: "hardware write protected",
+	0x2702: "logical unit software write protected",
+	0x2703: "associated write protect",
+	0x2704: "persistent write protect",
+	0x2705: "permanent write protect",
+	0x2706: "conditional write protect",
+	0x2707: "space allocation failed write protect",
+	0x2708: "zone is read only",
+	0x2800: "not ready to ready change, medium may have changed",
+	0x2801: "import or export element accessed",
+	0x2802: "format-layer may have changed",
+	0x2803: "import/export element accessed, medium changed",
+	0x2900: "power on, reset, or bus device reset occurred",
+	0x2901: "power on occurred",
+	0x2902: "scsi bus reset occurred",
+	0x2903: "bus device reset function occurred",
+	0x2904: "device internal reset",
+	0x2905: "transceiver mode changed to single-ended",
+	0x2906: "transceiver mode changed to lvd",
+	0x2907: "i_t nexus loss occurred",
+	0x2a00: "parameters changed",
+	0x2a01: "mode parameters changed",
+	0x2a02: "log parameters changed",
+	0x2a03: "reservations preempted",
+	0x2a04: "reservations released",
+	0x2a05: "registrations preempted",
+	0x2a06: "asymmetric access state changed",
+	0x2a07: "implicit asymmetric access state transition failed",
+	0x2a08: "priority changed",
+	0x2a09: "capacity data has changed",
+	0x2a0a: "error history i_t nexus cleared",
+	0x2a0b: "error history snapshot released",
+	0x2a0c: "error recovery attributes have changed",
+	0x2a0d: "data encryption capabilities changed",
+	0x2a10: "timestamp changed",
+	0x2a11: "data encryption parameters changed by another i_t nexus",
+	0x2a12: "data encryption parameters changed by vendor specific event",
+	0x2a13: "data encryption key instance counter has changed",
+	0x2a14: "sa creation capabilities data has changed",
+	0x2a15: "medium removal prevention preempted",
+	0x2a16: "zone reset write pointer recommended",
+	0x2b00: "copy cannot execute since host cannot disconnect",
+	0x2c00: "command sequence error",
+	0x2c01: "too many windows specified",
+	0x2c02: "invalid combination of windows specified",
+	0x2c03: "current program area is not empty",
+	0x2c04: "current program area is empty",
+	0x2c05: "illegal power condition request",
+	0x2c06: "persistent prevent conflict",
+	0x2c07: "previous busy status",
+	0x2c08: "previous task set full status",
+	0x2c09: "previous reservation conflict status",
+	0x2c0a: "partition or collection contains user objects",
+	0x2c0b: "not reserved",
+	0x2c0c: "orwrite generation does not match",
+	0x2c0d: "reset write pointer not allowed",
+	0x2c0e: "zone is offline",
+	0x2c0f: "stream not open",
+	0x2c10: "unwritten data in zone",
+	0x2c11: "descriptor format sense data required",
+	0x2d00: "overwrite error on update in place",
+	0x2e00: "insufficient time for operation",
+	0x2e01: "command timeout before processing",
+	0x2e02: "command timeout during processing",
+	0x2e03: "command timeout during processing due to error recovery",
+	0x2f00: "commands cleared by another initiator",
+	0x2f01: "commands cleared by power loss notification",
+	0x2f02: "commands cleared by device server",
+	0x2f03: "some commands cleared by queuing layer event",
+	0x3000: "incompatible medium installed",
+	0x3001: "cannot read medium - unknown format",
+	0x3002: "cannot read medium - incompatible format",
+	0x3003: "cleaning cartridge installed",
+	0x3004: "cannot write medium - unknown format",
+	0x3005: "cannot write medium - incompatible format",
+	0x3006: "cannot format medium - incompatible medium",
+	0x3007: "cleaning failure",
+	0x3008: "cannot write - application code mismatch",
+	0x3009: "current session not fixated for append",
+	0x300a: "cleaning request rejected",
+	0x300c: "worm medium - overwrite attempted",
+	0x300d: "worm medium - integrity check",
+	0x3010: "medium not formatted",
+	0x3011: "incompatible volume type",
+	0x3012: "incompatible volume qualifier",
+	0x3013: "cleaning volume expired",
+	0x3100: "medium format corrupted",
+	0x3101: "format command failed",
+	0x3102: "zoned formatting failed due to spare linking",
+	0x3103: "sanitize command failed",
+	0x3104: "depopulation failed",
+	0x3200: "no defect spare location available",
+	0x3201: "defect list update failure",
+	0x3300: "tape length error",
+	0x3400: "enclosure failure",
+	0x3500: "enclosure services failure",
+	0x3501: "unsupported enclosure function",
+	0x3502: "enclosure services unavailable",
+	0x3503: "enclosure services transfer failure",
+	0x3504: "enclosure services transfer refused",
+	0x3505: "enclosure services checksum error",
+	0x3600: "ribbon, ink, or toner failure",
+	0x3700: "rounded parameter",
+	0x3800: "event status notification",
+	0x3802: "esn - power management class event",
+	0x3804: "esn - media class event",
+	0x3806: "esn - device busy class event",
+	0x3807: "thin provisioning soft threshold reached",
+	0x3900: "saving parameters not supported",
+	0x3a00: "medium not present",
+	0x3a01: "medium not present - tray closed",
+	0x3a02: "medium not present - tray open",
+	0x3a03: "medium not present - loadable",
+	0x3a04: "medium not present - medium auxiliary memory accessible",
+	0x3b00: "sequential positioning error",
+	0x3b01: "tape position error at beginning-of-medium",
+	0x3b02: "tape position error at end-of-medium",
+	0x3b03: "tape or electronic vertical forms unit not ready",
+	0x3b04: "slew failure",
+	0x3b05: "paper jam",
+	0x3b06: "failed to sense top-of-form",
+	0x3b07: "failed to sense bottom-of-form",
+	0x3b08: "reposition error",
+	0x3b09: "read past end of medium",
+	0x3b0a: "read past beginning of medium",
+	0x3b0b: "position past end of medium",
+	0x3b0c: "position past beginning of medium",
+	0x3b0d: "medium destination element full",
+	0x3b0e: "medium source element empty",
+	0x3b0f: "end of medium reached",
+	0x3b11: "medium magazine not accessible",
+	0x3b12: "medium magazine removed",
+	0x3b13: "medium magazine inserted",
+	0x3b14: "medium magazine locked",
+	0x3b15: "medium magazine unlocked",
+	0x3b16: "mechanical positioning or changer error",
+	0x3b17: "read past end of user object",
+	0x3b18: "element disabled",
+	0x3b19: "element enabled",
+	0x3b1a: "data transfer device removed",
+	0x3b1b: "data transfer device inserted",
+	0x3b1c: "too many logical objects on partition to support operation",
+	0x3d00: "invalid bits in identify message",
+	0x3e00: "logical unit has not self-configured yet",
+	0x3e01: "logical unit failure",
+	0x3e02: "timeout on logical unit",
+	0x3e03: "logical unit failed self-test",
+	0x3e04: "logical unit unable to update self-test log",
+	0x3f00: "target operating conditions have changed",
+	0x3f01: "microcode has been changed",
+	0x3f02: "changed operating definition",
+	0x3f03: "inquiry data has changed",
+	0x3f04: "component device attached",
+	0x3f05: "device identifier changed",
+	0x3f06: "redundancy group created or modified",
+	0x3f07: "redundancy group deleted",
+	0x3f08: "spare created or modified",
+	0x3f09: "spare deleted",
+	0x3f0a: "volume set created or modified",
+	0x3f0b: "volume set deleted",
+	0x3f0c: "volume set deassigned",
+	0x3f0d: "volume set reassigned",
+	0x3f0e: "reported luns data has changed",
+	0x3f0f: "echo buffer overwritten",
+	0x3f10: "medium loadable",
+	0x3f11: "medium auxiliary memory accessible",
+	0x3f12: "iscsi ip address added",
+	0x3f13: "iscsi ip address removed",
+	0x3f14: "iscsi ip address changed",
+	0x3f15: "inspect referrals sense descriptors",
+	0x3f16: "microcode has been changed without reset",
+	0x3f17: "zone transition to full",
+	0x3f18: "bind completed",
+	0x3f19: "bind redirected",
+	0x3f1a: "subsidiary binding changed",
+	0x4000: "ram failure (should use 40 nn)",
+	0x4100: "data path failure (should use 40 nn)",
+	0x4200: "power-on or self-test failure (should use 40 nn)",
+	0x4300: "message error",
+	0x4400: "internal target failure",
+	0x4401: "persistent reservation information lost",
+	0x4471: "ata device failed set features",
+	0x4500: "select or reselect failure",
+	0x4600: "unsuccessful soft reset",
+	0x4700: "scsi parity error",
+	0x4701: "data phase crc error detected",
+	0x4702: "scsi parity error detected during st data phase",
+	0x4703: "information unit iucrc error detected",
+	0x4704: "asynchronous information protection error detected",
+	0x4705: "protocol service crc error",
+	0x4706: "phy test function in progress",
+	0x477f: "some commands cleared by iscsi protocol event",
+	0x4800: "initiator detected error message received",
+	0x4900: "invalid message error",
+	0x4a00: "command phase error",
+	0x4b00: "data phase error",
+	0x4b01: "invalid target port transfer tag received",
+	0x4b02: "too much write data",
+	0x4b03: "ack/nak timeout",
+	0x4b04: "nak received",
+	0x4b05: "data offset error",
+	0x4b06: "initiator response timeout",
+	0x4b07: "connection lost",
+	0x4b08: "data-in buffer overflow - data buffer size",
+	0x4b09: "data-in buffer overflow - data buffer descriptor area",
+	0x4b0a: "data-in buffer error",
+	0x4b0b: "data-out buffer overflow - data buffer size",
+	0x4b0c: "data-out buffer overflow - data buffer descriptor area",
+	0x4b0d: "data-out buffer error",
+	0x4b0e: "pcie fabric error",
+	0x4b0f: "pcie completion timeout",
+	0x4b10: "pcie completer abort",
+	0x4b11: "pcie poisoned tlp received",
+	0x4b12: "pcie ecrc check failed",
+	0x4b13: "pcie unsupported request",
+	0x4b14: "pcie acs violation",
+	0x4b15: "pcie tlp prefix blocked",
+	0x4c00: "logical unit failed self-configuration",
+	0x4e00: "overlapped commands attempted",
+	0x5000: "write append error",
+	0x5001: "write append position error",
+	0x5002: "position error related to timing",
+	0x5100: "erase failure",
+	0x5101: "erase failure - incomplete erase operation detected",
+	0x5200: "cartridge fault",
+	0x5300: "media load or eject failed",
+	0x5301: "unload tape failure",
+	0x5302: "medium removal prevented",
+	0x5303: "medium removal prevented by data transfer element",
+	0x5304: "medium thread or unthread failure",
+	0x5305: "volume identifier invalid",
+	0x5306: "volume identifier missing",
+	0x5307: "duplicate volume identifier",
+	0x5308: "element status unknown",
+	0x5309: "data transfer device error - load failed",
+	0x530a: "data transfer device error - unload failed",
+	0x530b: "data transfer device error - unload missing",
+	0x530c: "data transfer device error - eject failed",
+	0x530d: "data transfer device error - library communication failed",
+	0x5400: "scsi to host system interface failure",
+	0x5500: "system resource failure",
+	0x5501: "system buffer full",
+	0x5502: "insufficient reservation resources",
+	0x5503: "insufficient resources",
+	0x5504: "insufficient registration resources",
+	0x5505: "insufficient access control resources",
+	0x5506: "auxiliary memory out of space",
+	0x5507: "quota error",
+	0x5508: "maximum number of supplemental decryption keys exceeded",
+	0x5509: "medium auxiliary memory not accessible",
+	0x550a: "data currently unavailable",
+	0x550b: "insufficient power for operation",
+	0x550c: "insufficient resources to create rod",
+	0x550d: "insufficient resources to create rod token",
+	0x550e: "insufficient zone resources",
+	0x550f: "insufficient zone resources to complete write",
+	0x5510: "maximum number of streams open",
+	0x5511: "insufficient resources to bind",
+	0x5700: "unable to recover table-of-contents",
+	0x5800: "generation does not exist",
+	0x5900: "updated block read",
+	0x5a00: "operator request or state change input",
+	0x5a01: "operator medium removal request",
+	0x5a02: "operator selected write protect",
+	0x5a03: "operator selected write permit",
+	0x5b00: "log exception",
+	0x5b01: "threshold condition met",
+	0x5b02: "log counter at maximum",
+	0x5b03: "log list codes exhausted",
+	0x5c00: "rpl status change",
+	0x5c01: "spindles synchronized",
+	0x5c02: "spindles not synchronized",
+	0x5d00: "failure prediction threshold exceeded",
+	0x5d01: "media failure prediction threshold exceeded",
+	0x5d02: "logical unit failure prediction threshold exceeded",
+	0x5d03: "spare area exhaustion prediction threshold exceeded",
+	0x5d10: "hardware impending failure general hard drive failure",
+	0x5d11: "hardware impending failure drive error rate too high",
+	0x5d12: "hardware impending failure data error rate too high",
+	0x5d13: "hardware impending failure seek error rate too high",
+	0x5d14: "hardware impending failure too many block reassigns",
+	0x5d15: "hardware impending failure access times too high",
+	0x5d16: "hardware impending failure start unit times too high",
+	0x5d17: "hardware impending failure channel parametrics",
+	0x5d18: "hardware impending failure controller detected",
+	0x5d19: "hardware impending failure throughput performance",
+	0x5d1a: "hardware impending failure seek time performance",
+	0x5d1b: "hardware impending failure spin-up retry count",
+	0x5d1c: "hardware impending failure drive calibration retry count",
+	0x5d1d: "hardware impending failure power loss protection circuit",
+	0x5d20: "controller impending failure general hard drive failure",
+	0x5d21: "controller impending failure drive error rate too high",
+	0x5d22: "controller impending failure data error rate too high",
+	0x5d23: "controller impending failure seek error rate too high",
+	0x5d24: "controller impending failure too many block reassigns",
+	0x5d25: "controller impending failure access times too high",
+	0x5d26: "controller impending failure start unit times too high",
+	0x5d27: "controller impending failure channel parametrics",
+	0x5d28: "controller impending failure controller detected",
+	0x5d29: "controller impending failure throughput performance",
+	0x5d2a: "controller impending failure seek time performance",
+	0x5d2b: "controller impending failure spin-up retry count",
+	0x5d2c: "controller impending failure drive calibration retry count",
+	0x5d30: "data channel impending failure general hard drive failure",
+	0x5d31: "data channel impending failure drive error rate too high",
+	0x5d32: "data channel impending failure data error rate too high",
+	0x5d33: "data channel impending failure seek error rate too high",
+	0x5d34: "data channel impending failure too many block reassigns",
+	0x5d35: "data channel impending failure access times too high",
+	0x5d36: "data channel impending failure start unit times too high",
+	0x5d37: "data channel impending failure channel parametrics",
+	0x5d38: "data channel impending failure controller detected",
+	0x5d39: "data channel impending failure throughput performance",
+	0x5d3a: "data channel impending failure seek time performance",
+	0x5d3b: "data channel impending failure spin-up retry count",
+	0x5d3c: "data channel impending failure drive calibration retry count",
+	0x5d40: "servo impending failure general hard drive failure",
+	0x5d41: "servo impending failure drive error rate too high",
+	0x5d42: "servo impending failure data error rate too high",
+	0x5d43: "servo impending failure seek error rate too high",
+	0x5d44: "servo impending failure too many block reassigns",
+	0x5d45: "servo impending failure access times too high",
+	0x5d46: "servo impending failure start unit times too high",
+	0x5d47: "servo impending failure channel parametrics",
+	0x5d48: "servo impending failure controller detected",
+	0x5d49: "servo impending failure throughput performance",
+	0x5d4a: "servo impending failure seek time performance",
+	0x5d4b: "servo impending failure spin-up retry count",
+	0x5d4c: "servo impending failure drive calibration retry count",
+	0x5d50: "spindle impending failure general hard drive failure",
+	0x5d51: "spindle impending failure drive error rate too high",
+	0x5d52: "spindle impending failure data error rate too high",
+	0x5d53: "spindle impending failure seek error rate too high",
+	0x5d54: "spindle impending failure too many block reassigns",
+	0x5d55: "spindle impending failure access times too high",
+	0x5d56: "spindle impending failure start unit times too high",
+	0x5d57: "spindle impending failure channel parametrics",
+	0x5d58: "spindle impending failure controller detected",
+	0x5d59: "spindle impending failure throughput performance",
+	0x5d5a: "spindle impending failure seek time performance",
+	0x5d5b: "spindle impending failure spin-up retry count",
+	0x5d5c: "spindle impending failure drive calibration retry count",
+	0x5d60: "firmware impending failure general hard drive failure",
+	0x5d61: "firmware impending failure drive error rate too high",
+	0x5d62: "firmware impending failure data error rate too high",
+	0x5d63: "firmware impending failure seek error rate too high",
+	0x5d64: "firmware impending failure too many block reassigns",
+	0x5d65: "firmware impending failure access times too high",
+	0x5d66: "firmware impending failure start unit times too high",
+	0x5d67: "firmware impending failure channel parametrics",
+	0x5d68: "firmware impending failure controller detected",
+	0x5d69: "firmware impending failure throughput performance",
+	0x5d6a: "firmware impending failure seek time performance",
+	0x5d6b: "firmware impending failure spin-up retry count",
+	0x5d6c: "firmware impending failure drive calibration retry count",
+	0x5d73: "media impending failure endurance limit met",
+	0x5dff: "failure prediction threshold exceeded (false)",
+	0x5e00: "low power condition on",
+	0x5e01: "idle condition activated by timer",
+	0x5e02: "standby condition activated by timer",
+	0x5e03: "idle condition activated by command",
+	0x5e04: "standby condition activated by command",
+	0x5e05: "idle_b condition activated by timer",
+	0x5e06: "idle_b condition activated by command",
+	0x5e07: "idle_c condition activated by timer",
+	0x5e08: "idle_c condition activated by command",
+	0x5e09: "standby_y condition activated by timer",
+	0x5e0a: "standby_y condition activated by command",
+	0x5e41: "power state change to active",
+	0x5e42: "power state change to idle",
+	0x5e43: "power state change to standby",
+	0x5e45: "power state change to sleep",
+	0x5e47: "power state change to device control",
+	0x6000: "lamp failure",
+	0x6100: "video acquisition error",
+	0x6101: "unable to acquire video",
+	0x6102: "out of focus",
+	0x6200: "scan head positioning error",
+	0x6300: "end of user area encountered on this track",
+	0x6301: "packet does not fit in available space",
+	0x6400: "illegal mode for this track",
+	0x6401: "invalid packet size",
+	0x6500: "voltage fault",
+	0x6600: "automatic document feeder cover up",
+	0x6601: "automatic document feeder lift up",
+	0x6602: "document jam in automatic document feeder",
+	0x6603: "document miss feed automatic in document feeder",
+	0x6700: "configuration failure",
+	0x6701: "configuration of incapable logical units failed",
+	0x6702: "add logical unit failed",
+	0x6703: "modification of logical unit failed",
+	0x6704: "exchange of logical unit failed",
+	0x6705: "remove of logical unit failed",
+	0x6706: "attachment of logical unit failed",
+	0x6707: "creation of logical unit failed",
+	0x6708: "assign failure occurred",
+	0x6709: "multiply assigned logical unit",
+	0x670a: "set target port groups command failed",
+	0x670b: "ata device feature not enabled",
+	0x670c: "command rejected",
+	0x670d: "explicit bind not allowed",
+	0x6800: "logical unit not configured",
+	0x6801: "subsidiary logical unit not configured",
+	0x6900: "data loss on logical unit",
+	0x6901: "multiple logical unit failures",
+	0x6902: "parity/data mismatch",
+	0x6a00: "informational, refer to log",
+	0x6b00: "state change has occurred",
+	0x6b01: "redundancy level got better",
+	0x6b02: "redundancy level got worse",
+	0x6c00: "rebuild failure occurred",
+	0x6d00: "recalculate failure occurred",
+	0x6e00: "command to logical unit failed",
+	0x6f00: "copy protection key exchange failure - authentication",
+	0x6f01: "copy protection key exchange failure - key not present",
+	0x6f02: "copy protection key exchange failure - key not established",
+	0x6f03: "read of scrambled sector without authentication",
+	0x6f04: "media region code is mismatched to logical unit region",
+	0x6f05: "drive region must be permanent/region reset count error",
+	0x6f06: "insufficient block count for binding nonce recording",
+	0x6f07: "conflict in binding nonce recording",
+	0x6f08: "insufficient permission",
+	0x6f09: "invalid drive-host pairing server",
+	0x6f0a: "drive-host pairing suspended",
+	0x7100: "decompression exception long algorithm id",
+	0x7200: "session fixation error",
+	0x7201: "session fixation error writing lead-in",
+	0x7202: "session fixation error writing lead-out",
+	0x7203: "session fixation error - incomplete track in session",
+	0x7204: "empty or partially written reserved track",
+	0x7205: "no more track reservations allowed",
+	0x7206: "rmz extension is not allowed",
+	0x7207: "no more test zone extensions are allowed",
+	0x7300: "cd control error",
+	0x7301: "power calibration area almost full",
+	0x7302: "power calibration area is full",
+	0x7303: "power calibration area error",
+	0x7304: "program memory area update failure",
+	0x7305: "program memory area is full",
+	0x7306: "rma/pma is almost full",
+	0x7310: "current power calibration area almost full",
+	0x7311: "current power calibration area is full",
+	0x7317: "rdz is full",
+	0x7400: "security error",
+	0x7401: "unable to decrypt data",
+	0x7402: "unencrypted data encountered while decrypting",
+	0x7403: "incorrect data encryption key",
+	0x7404: "cryptographic integrity validation failed",
+	0x7405: "error decrypting data",
+	0x7406: "unknown signature verification key",
+	0x7407: "encryption parameters not useable",
+	0x7408: "digital signature validation failure",
+	0x7409: "encryption mode mismatch on read",
+	0x740a: "encrypted block not raw read enabled",
+	0x740b: "incorrect encryption parameters",
+	0x740c: "unable to decrypt parameter list",
+	0x740d: "encryption algorithm disabled",
+	0x7410: "sa creation parameter value invalid",
+	0x7411: "sa creation parameter value rejected",
+	0x7412: "invalid sa usage",
+	0x7421: "data encryption configuration prevented",
+	0x7430: "sa creation parameter not supported",
+	0x7440: "authentication failed",
+	0x7461: "external data encryption key manager access error",
+	0x7462: "external data encryption key manager error",
+	0x7463: "external data encryption key not found",
+	0x7464: "external data encryption request not authorized",
+	0x746e: "external data encryption control timeout",
+	0x746f: "external data encryption control error",
+	0x7471: "logical unit access not authorized",
+	0x7479: "security conflict in translated device",
+}