Skip to content

Commit

Permalink
Add scsiexec
Browse files Browse the repository at this point in the history
  • Loading branch information
uweseimet committed Nov 24, 2023
1 parent d4ff468 commit bb3b373
Show file tree
Hide file tree
Showing 8 changed files with 932 additions and 7 deletions.
24 changes: 17 additions & 7 deletions cpp/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ endif
PISCSI = s2p
SCSICTL = s2pctl
SCSIDUMP = s2pdump
SCSIEXEC = s2pexec
PISCSI_TEST = s2p_test

INSTALL_BIN = $(TARGET)/bin
Expand All @@ -65,6 +66,7 @@ BIN_ALL = \
# scsidump requires initiator support
ifeq ($(CONNECT_TYPE), FULLSPEC)
BIN_ALL += $(BINDIR)/$(SCSIDUMP)
BIN_ALL += $(BINDIR)/$(SCSIEXEC)
endif

SRC_PROTOC = piscsi_interface.proto
Expand Down Expand Up @@ -94,13 +96,15 @@ SRC_SCSIDUMP = scsidump/scsidump.cpp
SRC_SCSIDUMP += $(shell find ./scsidump -name '*.cpp' | grep -v scsidump.cpp)
SRC_SCSIDUMP += $(shell find ./hal -name '*.cpp')

SRC_SCSIEXEC = scsiexec/scsiexec.cpp
SRC_SCSIEXEC += $(shell find ./scsiexec -name '*.cpp' | grep -v scsiexec.cpp)
SRC_SCSIEXEC += $(shell find ./hal -name '*.cpp')

SRC_PISCSI_TEST = $(shell find ./test -name '*.cpp')
SRC_PISCSI_TEST += $(shell find ./scsidump -name '*.cpp' | grep -v scsidump.cpp)

vpath %.h ./shared ./controllers ./devices ./hal \
./piscsi ./scsictl ./scsidump
vpath %.cpp ./shared ./controllers ./devices ./hal \
./piscsi ./scsictl ./scsidump ./test
vpath %.h ./shared ./controllers ./devices ./hal ./piscsi ./scsictl ./scsidump ./scsiexec
vpath %.cpp ./shared ./controllers ./devices ./hal ./piscsi ./scsictl ./scsidump ./scsiexec ./test
vpath %.o ./$(OBJDIR)
vpath ./$(BINDIR)

Expand All @@ -110,6 +114,7 @@ OBJ_PISCSI := $(addprefix $(OBJDIR)/,$(notdir $(SRC_PISCSI:%.cpp=%.o)))
OBJ_SCSICTL_CORE := $(addprefix $(OBJDIR)/,$(notdir $(SRC_SCSICTL_CORE:%.cpp=%.o)))
OBJ_SCSICTL := $(addprefix $(OBJDIR)/,$(notdir $(SRC_SCSICTL:%.cpp=%.o)))
OBJ_SCSIDUMP := $(addprefix $(OBJDIR)/,$(notdir $(SRC_SCSIDUMP:%.cpp=%.o)))
OBJ_SCSIEXEC := $(addprefix $(OBJDIR)/,$(notdir $(SRC_SCSIEXEC:%.cpp=%.o)))
OBJ_PISCSI_TEST := $(addprefix $(OBJDIR)/,$(notdir $(SRC_PISCSI_TEST:%.cpp=%.o)))
OBJ_SHARED := $(addprefix $(OBJDIR)/,$(notdir $(SRC_SHARED:%.cpp=%.o)))
OBJ_PROTOBUF := $(addprefix $(OBJDIR)/,$(notdir $(SRC_PROTOBUF:%.cpp=%.o)))
Expand All @@ -119,6 +124,7 @@ BINARIES = $(INSTALL_BIN)/$(SCSICTL) \
$(INSTALL_BIN)/$(PISCSI)
ifeq ($(CONNECT_TYPE), FULLSPEC)
BINARIES += $(INSTALL_BIN)/$(SCSIDUMP)
BINARIES += $(INSTALL_BIN)/$(SCSIEXEC)
endif

MAN_PAGES = $(MAN_PAGE_DIR)/piscsi.1 \
Expand All @@ -136,7 +142,8 @@ TEST_WRAPS = -Wl,--wrap=fopen64

# The following will include all of the auto-generated dependency files (*.d)
# if they exist. This will trigger a rebuild of a source file if a header changes
ALL_DEPS := $(patsubst %.o,%.d,$(OBJ_PISCSI_CORE) $(OBJ_SCSICTL_CORE) $(OBJ_PISCSI) $(OBJ_SCSICTL) $(OBJ_SCSIDUMP) $(OBJ_SHARED) $(OBJ_PROTOBUF) $(OBJ_PISCSI_TEST))
ALL_DEPS := $(patsubst %.o,%.d,$(OBJ_PISCSI_CORE) $(OBJ_SCSICTL_CORE) $(OBJ_PISCSI) $(OBJ_SCSICTL) $(OBJ_SCSIDUMP) \
$(OBJ_SCSIEXEC) $(OBJ_SHARED) $(OBJ_PROTOBUF) $(OBJ_PISCSI_TEST))
-include $(ALL_DEPS)

$(OBJDIR) $(BINDIR):
Expand Down Expand Up @@ -180,7 +187,7 @@ lcov: test

docs: $(DOC_DIR)/piscsi_man_page.txt $(DOC_DIR)/scsictl_man_page.txt $(DOC_DIR)/scsidump_man_page.txt

$(SRC_PISCSI_CORE) $(SRC_SCSICTL_CORE) : $(OBJ_GENERATED)
$(SRC_PISCSI_CORE) $(SRC_SCSICTL_CORE) $(SRC_SCSIEXEC): $(OBJ_GENERATED)

$(BINDIR)/$(PISCSI): $(OBJ_GENERATED) $(OBJ_PISCSI_CORE) $(OBJ_PISCSI) $(OBJ_SHARED) $(OBJ_PROTOBUF) $(OBJ_GENERATED) | $(BINDIR)
$(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $(OBJ_PISCSI_CORE) $(OBJ_PISCSI) $(OBJ_SHARED) $(OBJ_PROTOBUF) $(OBJ_GENERATED) -lpthread -lpcap -lprotobuf
Expand All @@ -191,11 +198,14 @@ $(BINDIR)/$(SCSICTL): $(OBJ_GENERATED) $(OBJ_SCSICTL_CORE) $(OBJ_SCSICTL) $(OBJ_
$(BINDIR)/$(SCSIDUMP): $(OBJ_SCSIDUMP) $(OBJ_SHARED) $(OBJ_PROTOBUF) $(OBJ_GENERATED) | $(BINDIR)
$(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $(OBJ_SCSIDUMP) $(OBJ_SHARED) $(OBJ_PROTOBUF) $(OBJ_GENERATED) -lprotobuf

$(BINDIR)/$(SCSIEXEC): $(OBJ_SCSIEXEC) $(OBJ_SHARED) $(OBJ_PROTOBUF) $(OBJ_GENERATED) | $(BINDIR)
$(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $(OBJ_SCSIEXEC) $(OBJ_SHARED) $(OBJ_PROTOBUF) $(OBJ_GENERATED) -lprotobuf

$(BINDIR)/$(PISCSI_TEST): $(OBJ_GENERATED) $(OBJ_PISCSI_CORE) $(OBJ_SCSICTL_CORE) $(OBJ_PISCSI_TEST) $(OBJ_SCSICTL_TEST) $(OBJ_SHARED) $(OBJ_PROTOBUF) $(OBJ_GENERATED) | $(BINDIR)
$(CXX) $(CXXFLAGS) $(LDFLAGS) $(TEST_WRAPS) -o $@ $(OBJ_PISCSI_CORE) $(OBJ_SCSICTL_CORE) $(OBJ_PISCSI_TEST) $(OBJ_SHARED) $(OBJ_PROTOBUF) $(OBJ_GENERATED) -lpthread -lpcap -lprotobuf -lgmock -lgtest

# Phony rules for building individual utilities
.PHONY: $(PISCSI) $(SCSICTL) $(SCSIDUMP) $(PISCSI_TEST)
.PHONY: $(PISCSI) $(SCSICTL) $(SCSIDUMP) $(SCSIEXEC) $(PISCSI_TEST)
$(PISCSI) : $(BINDIR)/$(PISCSI)
$(SCSICTL) : $(BINDIR)/$(SCSICTL)
$(SCSIDUMP) : $(BINDIR)/$(SCSIDUMP)
Expand Down
273 changes: 273 additions & 0 deletions cpp/scsiexec/phase_executor.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,273 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator PiSCSI
// for Raspberry Pi
//
// Copyright (C) 2023 Uwe Seimet
//
//---------------------------------------------------------------------------

#include "hal/bus.h"
#include "phase_executor.h"
#include <spdlog/spdlog.h>
#include <iostream>
#include <array>
#include <string>
#include <chrono>

using namespace std;
using namespace spdlog;

void PhaseExecutor::Reset() const
{
bus.SetDAT(0);
bus.SetBSY(false);
bus.SetSEL(false);
bus.SetATN(false);
}

bool PhaseExecutor::Execute(scsi_command cmd, span<uint8_t> cdb, span<uint8_t> buffer, int length)
{
status = 0;
byte_count = 0;

spdlog::trace(
fmt::format("Executing {0} for target {1}:{2}", command_mapping.find(cmd)->second.second, target_id,
target_lun));

if (!Arbitration()) {
bus.Reset();
return false;
}

if (!Selection()) {
Reset();
return false;
}

// Timeout 3 s
auto now = chrono::steady_clock::now();
while ((chrono::duration_cast<chrono::seconds>(chrono::steady_clock::now() - now).count()) < 3) {
bus.Acquire();

if (bus.GetREQ()) {
try {
if (Dispatch(cmd, cdb, buffer, length)) {
now = chrono::steady_clock::now();
}
else {
bus.Reset();
return !GetStatus();
}
}
catch (const phase_exception &e) {
cerr << "Error: " << e.what() << endl;
bus.Reset();
return false;
}
}
}

return false;
}

bool PhaseExecutor::Dispatch(scsi_command cmd, span<uint8_t> cdb, span<uint8_t> buffer, int length)
{
const phase_t phase = bus.GetPhase();

spdlog::trace(string("Handling ") + BUS::GetPhaseStrRaw(phase) + " phase");

switch (phase) {
case phase_t::command:
Command(cmd, cdb);
break;

case phase_t::status:
Status();
break;

case phase_t::datain:
DataIn(buffer, length);
break;

case phase_t::dataout:
DataOut(buffer, length);
break;

case phase_t::msgin:
MsgIn();
// Done with this command cycle
return false;

case phase_t::msgout:
MsgOut();
break;

default:
throw phase_exception(string("Ignoring ") + BUS::GetPhaseStrRaw(phase) + " phase");
break;
}

return true;
}

bool PhaseExecutor::Arbitration() const
{
if (!WaitForFree()) {
spdlog::trace("Bus is not free");
return false;
}

Sleep(BUS_FREE_DELAY);

bus.SetDAT(static_cast<uint8_t>(1 << initiator_id));

bus.SetBSY(true);

Sleep(ARBITRATION_DELAY);

if (bus.GetDAT() > (1 << initiator_id)) {
spdlog::trace(
fmt::format("Lost ARBITRATION, competing initiator ID is {}", bus.GetDAT() - (1 << initiator_id)));
return false;
}

bus.SetSEL(true);

Sleep(BUS_CLEAR_DELAY);
Sleep(BUS_SETTLE_DELAY);

return true;
}

bool PhaseExecutor::Selection() const
{
bus.SetDAT(static_cast<uint8_t>((1 << initiator_id) + (1 << target_id)));

bus.SetSEL(true);

// Request MESSAGE OUT for IDENTIFY
bus.SetATN(true);

Sleep(DESKEW_DELAY);
Sleep(DESKEW_DELAY);

bus.SetBSY(false);

Sleep(BUS_SETTLE_DELAY);

if (!WaitForBusy()) {
spdlog::trace("SELECTION failed");
return false;
}

Sleep(DESKEW_DELAY);
Sleep(DESKEW_DELAY);

bus.SetSEL(false);

return true;
}

void PhaseExecutor::Command(scsi_command cmd, span<uint8_t> cdb) const
{
cdb[0] = static_cast<uint8_t>(cmd);
if (target_lun < 8) {
// Encode LUN in the CDB for backwards compatibility with SCSI-1-CCS
cdb[1] = static_cast<uint8_t>(cdb[1] + (target_lun << 5));
}

if (static_cast<int>(cdb.size()) !=
bus.SendHandShake(cdb.data(), static_cast<int>(cdb.size()), BUS::SEND_NO_DELAY)) {
throw phase_exception(command_mapping.find(cmd)->second.second + string(" failed"));
}
}

void PhaseExecutor::Status()
{
array<uint8_t, 1> buf;

if (bus.ReceiveHandShake(buf.data(), 1) != 1) {
throw phase_exception("STATUS failed");
}

status = buf[0];
}

void PhaseExecutor::DataIn(span<uint8_t> buffer, int length)
{
byte_count = bus.ReceiveHandShake(buffer.data(), length);
if (!byte_count) {
throw phase_exception("DATA IN failed");
}
}

void PhaseExecutor::DataOut(span<uint8_t> buffer, int length)
{
if (bus.SendHandShake(buffer.data(), length, BUS::SEND_NO_DELAY) != length) {
throw phase_exception("DATA OUT failed");
}
}

void PhaseExecutor::MsgIn() const
{
array<uint8_t, 1> buf;

if (bus.ReceiveHandShake(buf.data(), buf.size()) != buf.size()) {
throw phase_exception("MESSAGE IN failed");
}

if (buf[0]) {
throw phase_exception("MESSAGE IN did not report COMMAND COMPLETE");
}
}

void PhaseExecutor::MsgOut() const
{
array<uint8_t, 1> buf;

// IDENTIFY
buf[0] = static_cast<uint8_t>(target_lun | 0x80);

if (bus.SendHandShake(buf.data(), buf.size(), BUS::SEND_NO_DELAY) != buf.size()) {
throw phase_exception("MESSAGE OUT for IDENTIFY failed");
}
}

bool PhaseExecutor::WaitForFree() const
{
// Wait for up to 2 s
int count = 10'000;
do {
// Wait 20 ms
Sleep( { .tv_sec = 0, .tv_nsec = 20'000 });
bus.Acquire();
if (!bus.GetBSY() && !bus.GetSEL()) {
return true;
}
} while (count--);

return false;
}

bool PhaseExecutor::WaitForBusy() const
{
// Wait for up to 2 s
int count = 10'000;
do {
// Wait 20 ms
Sleep( { .tv_sec = 0, .tv_nsec = 20'000 });
bus.Acquire();
if (bus.GetBSY()) {
return true;
}
} while (count--);

return false;
}

void PhaseExecutor::SetTarget(int id, int lun)
{
target_id = id;
target_lun = lun;
}
Loading

0 comments on commit bb3b373

Please sign in to comment.