Skip to content

Repository files navigation

scsiexec

Send SCSI Command Descriptor Blocks (CDBs) to a device using a BlueSCSI, PiSCSI, or Linux SG as an initiator.

$ scsiexec --target 1 --cdb 12:00:00:00:24:00
00000000  00:00:02:02:39:00:00:18:51:55:41:4e:54:55:4d:20  '....9...QUANTUM '
00000010  46:49:52:45:42:41:4c:4c:20:20:20:20:20:20:20:20  'FIREBALL        '
00000020  31:2e:30:20                                      '1.0 '

Inspired by the useful utility s2pexec from scsi2pi by Uwe Seimet.

Transfer length

You do not have to specify transfer length. scsiexec works it out from the opcode:

metadata length
fixed-length command a constant (READ CAPACITY(10) is 8, READ POSITION 20)
block-counted command field value x device block size
everything else the field value, in bytes

with the 6-byte special case that a READ(6)/WRITE(6) count of 0 means 256 blocks. Block size comes from a cached READ CAPACITY(10), overridable with --block-size. -n forces a length.

$ scsiexec --dry-run -c 28:00:00:00:00:00:00:00:01:00
READ(10) cdb=28:00:00:00:00:00:00:00:01:00 dir=in length=512 block_size=512

--dry-run needs no device, so a CDB can be checked before it reaches the bus.

Vendor commands

Opcodes 0xc0-0xff are decoded via a flat file lookup table:

$ scsiexec --cdb-meta bluescsi-toolbox -i 1 -c d0:00:00:00:00:00:00:00:02:00
# opcode len dir  xoff xw unit  loff lw name
d0 10 in   6 4 bytes 0 0 TOOLBOX LIST FILES
d8 10 none 0 0 bytes 0 0 TOOLBOX SET NEXT CD

Profiles are searched in $SCSIEXEC_PROFILE_PATH, $XDG_CONFIG_HOME/scsiexec/profiles, ~/.config/scsiexec/profiles, /etc/scsiexec/profiles and the installed share directory. profiles/ ships the BlueSCSI Toolbox and DaynaPORT sets.

Selecting a device

--device /dev/sg3 names a node directly and works everywhere. -i <ID> looks the target up on the host bus.

$ scsiexec -i 0 -c 00:00:00:00:00:00
scsiexec: SCSI id 0 is ambiguous, 8 controllers offer it:
  /dev/sg0  host2  ahci
  /dev/sg8  host13  aic7xxx
  ...
  narrow it with --hba <driver> or name the node with --device

BlueSCSI Initiator

--bluescsi <port> sends the command over the USB serial console of a BlueSCSI running in initiator mode.

$ scsiexec --bluescsi /dev/ttyACM0 -i 1 -c 12:00:00:00:24:00
00000000  00:00:02:02:39:00:00:18:51:55:41:4e:54:55:4d:20  '....9...QUANTUM '

$ scsiexec --bluescsi /dev/ttyACM0 --list-devices
ID  DEVICE        TYPE            BLOCK
 1  /dev/ttyACM0  disk (0)        512
 3  /dev/ttyACM0  cdrom (5)       2048

The wire protocol is line-oriented text, you can use the console yourself via any console app.

> scsi 1.0 12:00:00:00:24:00 in 36
scsi> status 00
scsi> len 36
scsi> data 00000202390000185155414e54554d20
scsi> data 2020202020312e3020000000000000
scsi> end 0

The board must be booted in initiator mode (InitiatorMode=1 in bluescsi.ini, or the DIP switch).

PiSCSI Initiator

--gpio makes scsiexec the initiator, bit-banging the SCSI bus through a PiSCSI GPIO.

$ sudo scsiexec --gpio --list-devices
ID  DEVICE  TYPE            BLOCK
 1  gpio    disk (0)        512
 3  gpio    cdrom (5)       2048

$ sudo scsiexec --gpio -i 1 -c 12:00:00:00:24:00
$ sudo scsiexec --gpio -i 1:1 -c 12:00:00:00:24:00     # LUN via IDENTIFY

--initiator-id sets the initiator's own SCSI id, default 7.

Running it without root

The GPIO registers are mapped to /dev/mem, which is root-only. But if your user is in the gpio group it can use /dev/gpiomem, though drive strength is not changable to 16ma, you will get a notice. Run as sudo if you have an issue.

$ scsiexec --gpio -i 1 -c 12:00:00:00:24:00
scsiexec: note: mapped /dev/gpiomem instead of /dev/mem, so GPIO drive strength
is left at the 8 mA default rather than raised to 16 mA. ...

Raspberry Pi 1 to 4 only. The Pi 5 puts its GPIO behind the RP1 southbridge over PCIe with a different register layout; the board is detected from /proc/device-tree/model.

Platforms

platform backend state
any POSIX --bluescsi, a BlueSCSI in initiator mode over USB serial working
Linux SG_IO on /dev/sgN, /dev/sdX, /dev/srX, /dev/bsg/* working
Raspberry Pi 1-4 --gpio, bit-banged initiator working

NOTE: SG_IO needs root or CAP_SYS_RAWIO, or a udev rule granting access to the sg nodes.

Help

scsiexec 0.1.0 - send raw SCSI CDBs and decode the response

Usage: scsiexec [options] -c <CDB>

Target:
-i, --target ID[:LUN]      SCSI target id on the host bus
--device PATH              device node directly (/dev/sgN, /dev/sdX, ...)
--list-devices             print id/path/type/blocksize and exit
--bluescsi PORT            send through a BlueSCSI in initiator mode
                           over its USB serial console (--serial is an alias)
--gpio                     drive the bus from this Raspberry Pi's GPIO
                           (PiSCSI / SCSI2Pi board, needs root)
--initiator-id N           this initiator's own SCSI id (default 7)
--no-irq-mask              do not mask interrupts during a --gpio transfer
--gpio-selftest[=O:I]      check the GPIO register access and exit, without
                           putting anything on the SCSI bus. O:I adds a
                           jumper loopback between two BCM pins
--hba DRIVER               only consider targets on this controller

Command:
-c, --cdb HEX              CDB, colon/space separated or bare hex
-f, --binary-input FILE    DATA OUT payload from a binary file
-d, --data HEX             DATA OUT payload as hex (@FILE reads hex from a file)
-n, --length BYTES         force the transfer length
-b, --buffer-size N        cap an inferred DATA IN length (default 65536)
--block-size N             block size for block-counted commands
-t, --timeout SECS         command timeout (default 20)
-r, --reset-bus            reset the bus and exit
-R, --request-sense        send REQUEST SENSE after an error

Output:
-F, --binary-output FILE   write DATA IN to a binary file
-T, --hex-output FILE      write DATA IN as hex text
-x, --hex-only             drop the offset and ASCII columns
--format FMT               s2pexec (default), plain or json
--decode-sense HEX         decode sense bytes and exit, no device needed
--dump-hex HEX             render hex bytes as a dump and exit

Metadata:
--cdb-meta FILE            load a vendor opcode profile (repeatable)
--chunk                    allow splitting an oversized transfer
--dry-run                  show the resolved command without opening a device

-L, -l                     accepted for s2pexec compatibility, ignored
-h, --help                 this help
-v, --version              version

Install

With nix, straight from the repo - no clone, no build steps of your own:

$ nix profile add github:BlueSCSI/scsiexec           # nix < 2.25: profile install
$ nix run github:BlueSCSI/scsiexec -- --version      # or just run it once
$ nix profile add github:BlueSCSI/scsiexec/v0.1.0    # pinned to a release tag

Since this is a developer tool intended to build simply on modern Linux systems we don't provide a precompiled binary other than Nix.

Build

CMake

cmake -B build && cmake --build build -j
ctest --test-dir build

Requires a C++17 compiler. Tests use doctest, found on the system or fetched.

Nix

There is a nix flake; nix build and nix develop work as usual.

Building for the Pi without a compiler on the Pi

./cross.sh builds a static aarch64 binary; give it a host and it installs it too.

$ ./cross.sh                       # build only, into build-arm64/
$ ./cross.sh eric@piscsi           # build, scp, install to /usr/local/bin

License

BSD 3-Clause. Derived from SCSI2Pi by Uwe Seimet, also BSD 3-Clause; see NOTICE for what came from where.

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages