blob: 907d64365632e987a248431c85d2625980aa985b [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 Brunbffdda82023-01-10 05:00:36 +00004//go:build linux
5
6package scsi
7
8import (
9 "encoding/binary"
10 "errors"
11 "fmt"
12 "math"
13 "runtime"
14 "unsafe"
15
16 "golang.org/x/sys/unix"
17)
18
19// RawCommand issues a raw command against the device.
20func (d *Device) RawCommand(c *CommandDataBuffer) error {
21 cdb, err := c.Bytes()
22 if err != nil {
23 return fmt.Errorf("error encoding CDB: %w", err)
24 }
25 conn, err := d.fd.SyscallConn()
26 if err != nil {
27 return fmt.Errorf("unable to get RawConn: %w", err)
28 }
29 var dxferDir int32
30 switch c.DataTransferDirection {
31 case DataTransferNone:
32 dxferDir = SG_DXFER_NONE
33 case DataTransferFromDevice:
34 dxferDir = SG_DXFER_FROM_DEV
35 case DataTransferToDevice:
36 dxferDir = SG_DXFER_TO_DEV
37 case DataTransferBidirectional:
38 dxferDir = SG_DXFER_TO_FROM_DEV
39 default:
40 return errors.New("invalid DataTransferDirection")
41 }
42 var timeout uint32
43 if c.Timeout.Milliseconds() > math.MaxUint32 {
44 timeout = math.MaxUint32
45 }
46 if len(c.Data) > math.MaxUint32 {
47 return errors.New("payload larger than 2^32 bytes, unable to issue")
48 }
49 if len(cdb) > math.MaxUint8 {
50 return errors.New("CDB larger than 2^8 bytes, unable to issue")
51 }
52 var senseBuf [32]byte
Lorenz Brunb4f50242023-11-14 21:51:07 +010053
54 var ioctlPins runtime.Pinner
55 ioctlPins.Pin(&c.Data[0])
56 ioctlPins.Pin(&cdb[0])
57 ioctlPins.Pin(&senseBuf[0])
58 defer ioctlPins.Unpin()
59
Lorenz Brunbffdda82023-01-10 05:00:36 +000060 cmdRaw := sgIOHdr{
61 Interface_id: 'S',
62 Dxfer_direction: dxferDir,
63 Dxfer_len: uint32(len(c.Data)),
64 Dxferp: uintptr(unsafe.Pointer(&c.Data[0])),
65 Cmd_len: uint8(len(cdb)),
66 Cmdp: uintptr(unsafe.Pointer(&cdb[0])),
67 Mx_sb_len: uint8(len(senseBuf)),
68 Sbp: uintptr(unsafe.Pointer(&senseBuf[0])),
69 Timeout: timeout,
70 }
71 var errno unix.Errno
72 err = conn.Control(func(fd uintptr) {
73 _, _, errno = unix.Syscall(unix.SYS_IOCTL, fd, SG_IO, uintptr(unsafe.Pointer(&cmdRaw)))
74 })
75 runtime.KeepAlive(cmdRaw)
76 runtime.KeepAlive(c.Data)
77 runtime.KeepAlive(senseBuf)
78 runtime.KeepAlive(cdb)
79 if err != nil {
80 return fmt.Errorf("unable to get fd: %w", err)
81 }
82 if errno != 0 {
83 return errno
84 }
85 if cmdRaw.Masked_status != 0 {
86 if senseBuf[0] == 0x70 || senseBuf[0] == 0x71 {
87 err := &FixedError{
88 Deferred: senseBuf[0] == 0x71,
89 SenseKey: SenseKey(senseBuf[2] & 0b1111),
90 Information: binary.BigEndian.Uint32(senseBuf[3:7]),
91 }
92 length := int(senseBuf[7])
93 if length >= 4 {
94 err.CommandSpecificInformation = binary.BigEndian.Uint32(senseBuf[8:12])
95 if length >= 6 {
96 err.AdditionalSenseCode = AdditionalSenseCode(uint16(senseBuf[12])<<8 | uint16(senseBuf[13]))
97 }
98 }
99 return err
100 }
101 return &UnknownError{
102 RawSenseData: senseBuf[:],
103 }
104 }
105 return nil
106}