blob: f8573e2912b292a9c45d47fea7f42f48314a0573 [file] [log] [blame]
Tim Windelschmidt6d33a432025-02-04 14:34:25 +01001// Copyright The Monogon Project Authors.
2// SPDX-License-Identifier: Apache-2.0
3
Lorenz Brunfba5da02022-12-15 11:20:47 +00004// 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.
8package nvme
9
10import (
11 "errors"
12 "fmt"
13 "os"
14 "syscall"
15 "time"
16)
17
18// Device is a handle for a NVMe device.
19type Device struct {
20 fd syscall.Conn
21}
22
23// NewFromFd creates a new NVMe device handle from a system handle.
24func 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).
33func 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.
44func (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
52const (
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.
60type 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
71func (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}