| Tim Windelschmidt | 6d33a43 | 2025-02-04 14:34:25 +0100 | [diff] [blame] | 1 | // Copyright The Monogon Project Authors. |
| 2 | // SPDX-License-Identifier: Apache-2.0 |
| 3 | |
| Lorenz Brun | fba5da0 | 2022-12-15 11:20:47 +0000 | [diff] [blame] | 4 | package nvme |
| 5 | |
| 6 | import ( |
| 7 | "bytes" |
| 8 | "encoding/binary" |
| 9 | "fmt" |
| 10 | "math/big" |
| 11 | ) |
| 12 | |
| 13 | // Figure 109 |
| 14 | type identifyData struct { |
| 15 | // Controller Capabilities and Features |
| 16 | PCIVendorID uint16 |
| 17 | PCISubsystemVendorID uint16 |
| 18 | SerialNumber [20]byte |
| 19 | ModelNumber [40]byte |
| 20 | FirmwareRevision [8]byte |
| 21 | RecommendedArbitrationBurst uint8 |
| 22 | IEEEOUI [3]byte |
| 23 | CMIC uint8 |
| 24 | MaximumDataTransferSize uint8 |
| 25 | ControllerID uint16 |
| 26 | Version uint32 |
| 27 | RuntimeD3ResumeLatency uint32 |
| 28 | RuntimeD3EntryLatency uint32 |
| 29 | OAES uint32 |
| 30 | CTRATT uint32 |
| 31 | _ [12]byte |
| 32 | FRUGUID [16]byte |
| 33 | _ [128]byte |
| 34 | // Admin Command Set Attributes & Optional Controller Capabilities |
| 35 | OACS uint16 |
| 36 | AbortCommandLimit uint8 |
| 37 | AsynchronousEventRequestLimit uint8 |
| 38 | FRMW uint8 |
| 39 | LPA uint8 |
| 40 | ErrorLogPageEntries uint8 |
| 41 | NumberOfPowerStatesSupport uint8 |
| 42 | AdminVendorSpecificCmdConfig uint8 |
| 43 | AutonomousPowerStateTransitionAttrs uint8 |
| 44 | WarningCompositeTempThreshold uint16 |
| 45 | CriticalCompositeTempThreshold uint16 |
| 46 | MaximumTimeForFirmwareActivation uint16 |
| 47 | HostMemoryBufferPreferredSize uint32 |
| 48 | HostMemoryBufferMinimumSize uint32 |
| 49 | TotalNVMCapacity uint128le |
| 50 | UnallocatedNVMCapacity uint128le |
| 51 | ReplyProtectedMemoryBlockSupport uint32 |
| 52 | ExtendedDeviceSelfTestTime uint16 |
| 53 | DeviceSelfTestOptions uint8 |
| 54 | FirmwareUpdateGranularity uint8 |
| 55 | KeepAliveSupport uint16 |
| 56 | HostControlledThermalMgmtAttrs uint16 |
| 57 | MinimumThermalMgmntTemp uint16 |
| 58 | MaximumThermalMgmntTemp uint16 |
| 59 | SanitizeCapabilities uint32 |
| 60 | _ [180]byte |
| 61 | // NVM Command Set Attributes |
| 62 | SubmissionQueueEntrySize uint8 |
| 63 | CompletionQueueEntrySize uint8 |
| 64 | MaximumOutstandingCommands uint16 |
| 65 | NumberOfNamespaces uint32 |
| 66 | OptionalNVMCommandSupport uint16 |
| 67 | FusedOperationSupport uint16 |
| 68 | FormatNVMAttributes uint8 |
| 69 | VolatileWriteCache uint8 |
| 70 | AtomicWriteUnitNormal uint16 |
| 71 | AtomicWriteUnitPowerFail uint16 |
| 72 | NVMVendorSepcificCommandConfig uint8 |
| 73 | AtomicCompareAndWriteUnit uint16 |
| 74 | _ [2]byte |
| 75 | SGLSupport uint32 |
| 76 | _ [228]byte |
| 77 | NVMSubsystemNVMeQualifiedName [256]byte |
| 78 | _ [1024]byte |
| 79 | // Power State Descriptors |
| 80 | PowerStateDescriptors [32][32]byte |
| 81 | } |
| 82 | |
| 83 | // IdentifyData contains various identifying information about a NVMe |
| 84 | // controller. Because the actual data structure is very large, currently not |
| 85 | // all fields are exposed as properly-typed individual fields. If you need |
| 86 | // a new field, please add it to this structure. |
| 87 | type IdentifyData struct { |
| 88 | // PCIVendorID contains the company vendor identifier assigned by the PCI |
| 89 | // SIG. |
| 90 | PCIVendorID uint16 |
| 91 | // PCISubsystemVendorID contains the company vendor identifier that is |
| 92 | // assigned by the PCI SIG for the subsystem. |
| 93 | PCISubsystemVendorID uint16 |
| 94 | // SerialNumber contains the serial number for the NVM subsystem that is |
| 95 | // assigned by the vendor. |
| 96 | SerialNumber string |
| 97 | // ModelNumber contains the model number for the NVM subsystem that is |
| 98 | // assigned by the vendor. |
| 99 | ModelNumber string |
| 100 | // FirmwareRevision contains the currently active firmware revision for the |
| 101 | // NVM subsystem. |
| 102 | FirmwareRevision string |
| 103 | // IEEEOUI contains the Organization Unique Identifier for the controller |
| 104 | // vendor as assigned by the IEEE. |
| 105 | IEEEOUI [3]byte |
| 106 | |
| 107 | // IsPCIVirtualFunction indicates if the controller is a virtual controller |
| 108 | // as part of a PCI virtual function. |
| 109 | IsPCIVirtualFunction bool |
| 110 | |
| 111 | // SpecVersionMajor/Minor contain the version of the NVMe specification the |
| 112 | // controller supports. Only mandatory from spec version 1.2 onwards. |
| 113 | SpecVersionMajor uint16 |
| 114 | SpecVersionMinor uint8 |
| 115 | |
| 116 | // FRUGloballyUniqueIdentifier contains a 128-bit value that is globally |
| 117 | // unique for a given Field Replaceable Unit (FRU). Contains all-zeroes if |
| 118 | // unavailable. |
| 119 | FRUGloballyUniqueIdentifier [16]byte |
| 120 | // VirtualizationManagementSupported indicates if the controller |
| 121 | // supports the Virtualization Management command. |
| 122 | VirtualizationManagementSupported bool |
| 123 | // NVMeMISupported indicates if the controller supports the NVMe-MI |
| 124 | // Send and Receive commands. |
| 125 | NVMeMISupported bool |
| 126 | // DirectivesSupported indicates if the controller supports the |
| 127 | // Directive Send and Receive commands. |
| 128 | DirectivesSupported bool |
| 129 | // SelfTestSupported indicates if the controller supports the Device Self- |
| 130 | // test command. |
| 131 | SelfTestSupported bool |
| 132 | // NamespaceManagementSupported indicates if the controller supports the |
| 133 | // Namespace Management and Attachment commands. |
| 134 | NamespaceManagementSupported bool |
| 135 | // FirmwareUpdateSupported indicates if the controller supports the |
| 136 | // Firmware Commit and Image Download commands. |
| 137 | FirmwareUpdateSupported bool |
| 138 | // FormattingSupported indicates if the controller supports the Format |
| 139 | // command. |
| 140 | FormattingSupported bool |
| 141 | // SecuritySupported indicates if the controller supports the Security Send |
| 142 | // and Receive commands. |
| 143 | SecuritySupported bool |
| 144 | |
| 145 | // TotalNVMCapacity contains the total NVM capacity in bytes in the NVM |
| 146 | // subsystem. This can be 0 on devices without NamespaceManagementSupported. |
| 147 | TotalNVMCapacity *big.Int |
| 148 | // UnallocatedNVMCapacity contains the unallocated NVM capacity in bytes in |
| 149 | // the NVM subsystem. This can be 0 on devices without |
| 150 | // NamespaceManagementSupported. |
| 151 | UnallocatedNVMCapacity *big.Int |
| 152 | |
| 153 | // MaximumNumberOfNamespace defines the maximum number of namespaces |
| 154 | // supported by the controller. |
| 155 | MaximumNumberOfNamespaces uint32 |
| 156 | } |
| 157 | |
| 158 | func (d *Device) Identify() (*IdentifyData, error) { |
| 159 | var resp [4096]byte |
| 160 | |
| 161 | if err := d.RawCommand(&Command{ |
| 162 | Opcode: 0x06, |
| 163 | Data: resp[:], |
| 164 | CDW10: 1, |
| 165 | }); err != nil { |
| 166 | return nil, fmt.Errorf("Identify command failed: %w", err) |
| 167 | } |
| 168 | var raw identifyData |
| 169 | binary.Read(bytes.NewReader(resp[:]), binary.LittleEndian, &raw) |
| 170 | |
| 171 | var res IdentifyData |
| 172 | res.PCIVendorID = raw.PCIVendorID |
| 173 | res.PCISubsystemVendorID = raw.PCISubsystemVendorID |
| 174 | res.SerialNumber = string(bytes.TrimRight(raw.SerialNumber[:], " ")) |
| 175 | res.ModelNumber = string(bytes.TrimRight(raw.ModelNumber[:], " ")) |
| 176 | res.FirmwareRevision = string(bytes.TrimRight(raw.FirmwareRevision[:], " ")) |
| 177 | // OUIs are traditionally big-endian, but NVMe exposes them in little-endian |
| 178 | res.IEEEOUI[0], res.IEEEOUI[1], res.IEEEOUI[2] = raw.IEEEOUI[2], raw.IEEEOUI[1], raw.IEEEOUI[0] |
| 179 | res.IsPCIVirtualFunction = raw.CMIC&(1<<2) != 0 |
| 180 | res.SpecVersionMajor = uint16(raw.Version >> 16) |
| 181 | res.SpecVersionMinor = uint8((raw.Version >> 8) & 0xFF) |
| 182 | res.FRUGloballyUniqueIdentifier = raw.FRUGUID |
| 183 | res.VirtualizationManagementSupported = raw.OACS&(1<<7) != 0 |
| 184 | res.NVMeMISupported = raw.OACS&(1<<6) != 0 |
| 185 | res.DirectivesSupported = raw.OACS&(1<<5) != 0 |
| 186 | res.SelfTestSupported = raw.OACS&(1<<4) != 0 |
| 187 | res.NamespaceManagementSupported = raw.OACS&(1<<3) != 0 |
| 188 | res.FirmwareUpdateSupported = raw.OACS&(1<<2) != 0 |
| 189 | res.FormattingSupported = raw.OACS&(1<<1) != 0 |
| 190 | res.SecuritySupported = raw.OACS&(1<<0) != 0 |
| 191 | |
| 192 | res.TotalNVMCapacity = raw.TotalNVMCapacity.BigInt() |
| 193 | res.UnallocatedNVMCapacity = raw.UnallocatedNVMCapacity.BigInt() |
| 194 | res.MaximumNumberOfNamespaces = raw.NumberOfNamespaces |
| 195 | return &res, nil |
| 196 | } |