| 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 provides methods and data structures for issuing commands to |
| 5 | // device speaking the NVMe protocol. |
| 6 | // This package is written against the NVMe Specification Revision 1.3 and |
| 7 | // all references to figures or other parts of the spec refer to this version. |
| 8 | package nvme |
| 9 | |
| 10 | import ( |
| 11 | "errors" |
| 12 | "fmt" |
| 13 | "os" |
| 14 | "syscall" |
| 15 | "time" |
| 16 | ) |
| 17 | |
| 18 | // Device is a handle for a NVMe device. |
| 19 | type Device struct { |
| 20 | fd syscall.Conn |
| 21 | } |
| 22 | |
| 23 | // NewFromFd creates a new NVMe device handle from a system handle. |
| 24 | func NewFromFd(fd syscall.Conn) (*Device, error) { |
| 25 | d := &Device{fd: fd} |
| 26 | // There is no good way to validate that a file descriptor indeed points to |
| 27 | // a NVMe device. For future compatibility let this return an error so that |
| 28 | // code is already prepared to handle it. |
| 29 | return d, nil |
| 30 | } |
| 31 | |
| 32 | // Open opens a new NVMe device handle from a device path (like /dev/nvme0). |
| 33 | func Open(path string) (*Device, error) { |
| 34 | f, err := os.Open(path) |
| 35 | if err != nil { |
| 36 | return nil, fmt.Errorf("unable to open path: %w", err) |
| 37 | } |
| 38 | return NewFromFd(f) |
| 39 | } |
| 40 | |
| 41 | // Close closes the NVMe device handle. It returns an error if the handle was |
| 42 | // not created by Open. Please close the handle passed to NewFromFd yourself |
| 43 | // in that case. |
| 44 | func (d *Device) Close() error { |
| 45 | if f, ok := d.fd.(*os.File); ok { |
| 46 | return f.Close() |
| 47 | } else { |
| 48 | return errors.New("unable to close device not opened via Open, please close it yourself") |
| 49 | } |
| 50 | } |
| 51 | |
| 52 | const ( |
| 53 | // GlobalNamespace is the namespace ID for operations not on a specific |
| 54 | // namespace. |
| 55 | GlobalNamespace = 0xffffffff |
| 56 | ) |
| 57 | |
| 58 | // Command represents a generic NVMe command. Only use this if the command |
| 59 | // you need is not already wrapped by this library. |
| 60 | type Command struct { |
| 61 | Opcode uint8 |
| 62 | Flags uint8 |
| 63 | NamespaceID uint32 |
| 64 | CDW2, CDW3 uint32 |
| 65 | Metadata []byte |
| 66 | Data []byte |
| 67 | CDW10, CDW11, CDW12, CDW13, CDW14, CDW15 uint32 |
| 68 | Timeout time.Duration |
| 69 | } |
| 70 | |
| 71 | func (d *Device) GetLogPage(ns uint32, logPageIdentifier uint8, logSpecificField uint8, logPageOffset uint64, pageBuf []byte) error { |
| 72 | numberOfDwords := len(pageBuf) / 4 |
| 73 | return d.RawCommand(&Command{ |
| 74 | Opcode: 0x02, |
| 75 | NamespaceID: ns, |
| 76 | Data: pageBuf, |
| 77 | CDW10: uint32(logPageIdentifier) | uint32(logSpecificField&0xF)<<8 | uint32(numberOfDwords)<<16, // TODO: RAE |
| 78 | CDW11: uint32(numberOfDwords >> 16 & 0xffff), |
| 79 | CDW12: uint32(logPageOffset & 0xffffffff), |
| 80 | CDW13: uint32(logPageOffset >> 32), |
| 81 | }) |
| 82 | } |