1348 lines
48 KiB
C++
1348 lines
48 KiB
C++
// Cray-XMP I/O Processor simulator class
|
|
|
|
// !!!!!!!!!!!! IMPORTANT !!!!!!!!!!!!!!
|
|
// !!!!!!!!!!!! IMPORTANT !!!!!!!!!!!!!!
|
|
//
|
|
// To be true to the documentation, octal numbers are extensively used in this file.
|
|
// Watch out for the leading 0-s!
|
|
//
|
|
// !!!!!!!!!!!! IMPORTANT !!!!!!!!!!!!!!
|
|
// !!!!!!!!!!!! IMPORTANT !!!!!!!!!!!!!!
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <math.h>
|
|
#include <memory.h>
|
|
#include <string.h>
|
|
#include <algorithm>
|
|
#include "utils.h"
|
|
#include "sim_iop.h"
|
|
#include "cray_mainframe.h"
|
|
|
|
#if defined(PARTIAL_DEBUG) && defined(_MSC_VER)
|
|
#pragma optimize ("", off)
|
|
#endif
|
|
|
|
bool IopEPacket_c::IsValid() const {
|
|
if (mPacket[0] != mPacket[mPacket.size() - 1]) {
|
|
std::cout << "mPacket[0] != mPacket[mPacket.size() - 1]" << std::endl;
|
|
return false;
|
|
}
|
|
if (GetMagic() != cMagic) {
|
|
std::cout << "GetMagic() != cMagic" << std::endl;
|
|
return false;
|
|
}
|
|
if (GetLength() != mPacket.size() - 1) {
|
|
std::cout << "GetLength() != mPacket.size() - 1" << std::endl;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
std::vector<CInt_t> &IopEPacket_c::Finalize() {
|
|
SetMagic(cMagic);
|
|
// if (mPacket.size() != 1) {
|
|
SetLength(uint16_t(mPacket.size()));
|
|
SetFlags(cFlagAck);
|
|
mPacket.push_back(mPacket[0]);
|
|
// } else {
|
|
// SetLength(0);
|
|
// }
|
|
return mPacket;
|
|
}
|
|
|
|
std::unique_ptr<SimIopCluster_c::ChannelIToC_c> SimIopCluster_c::CreateIToCChannel(SimIopCluster_c *aThis, const Configuration_c &aConfig) {
|
|
return std::make_unique<ChannelIToC_c>(aConfig.get_child_safe("I2C"), aConfig.get_child_safe("I2C").get<size_t>("Channel"), aThis->GetMainframe(), *aThis);
|
|
}
|
|
|
|
std::unique_ptr<SimIopCluster_c::ChannelCToI_c> SimIopCluster_c::CreateCToIChannel(SimIopCluster_c *aThis, const Configuration_c &aConfig) {
|
|
return std::make_unique<ChannelCToI_c>(aConfig.get_child_safe("C2I"), aConfig.get_child_safe("C2I").get<size_t>("Channel"), aThis->GetMainframe(), *aThis);
|
|
}
|
|
|
|
SimIopCluster_c::SimIopCluster_c(const Configuration_c &aConfig, size_t aIopId, class Mainframe_c &aMainframe, IToCChannelCreator_t aIToCChannelCreator, CToIChannelCreator_t aCToIChannelCreator):
|
|
mMainframe(&aMainframe),
|
|
mLogger(aConfig,"IOP",aIopId),
|
|
mIopId(aIopId),
|
|
mState(States_e::Reset),
|
|
mMasterCluster(aConfig.get<bool>("MasterCluster", true)),
|
|
mDontTouchExchangePacket(aConfig.get<bool>("DontTouchExchangePacket", false)),
|
|
mPacketsReceived(0),
|
|
mPacketsSent(0)
|
|
{
|
|
mLogger.SetParent(aMainframe.GetLogger());
|
|
mIToCChannel = aIToCChannelCreator(this, aConfig);
|
|
mCToIChannel = aCToIChannelCreator(this, aConfig);
|
|
|
|
try {
|
|
std::string IopRevision = aConfig.get<std::string>("IopRevision", "IOSE");
|
|
if (IopRevision == "IOSD") {
|
|
mIopRevision = IopRevision_e::IOSD;
|
|
}
|
|
else if (IopRevision == "IOSE") {
|
|
mIopRevision = IopRevision_e::IOSE;
|
|
}
|
|
else {
|
|
throw Generic_x() << "Unknown IOP revision: " << IopRevision;
|
|
}
|
|
|
|
for (const auto &DiskConfig : aConfig.get_child_safe("Disks")) {
|
|
std::string DiskId = DiskConfig.first;
|
|
std::unique_ptr<SimIopDisk_c> Disk = std::make_unique<SimIopDisk_c>(DiskConfig.second, DiskId, *this);
|
|
mDisks.push_back(std::move(Disk));
|
|
}
|
|
for (const auto &ConsoleConfig : aConfig.get_child_safe("Consoles")) {
|
|
if (ConsoleConfig.first == "Default") {
|
|
mDefaultConsoleConfig = ConsoleConfig.second;
|
|
} else {
|
|
size_t ConsoleId = FromString<size_t>(ConsoleConfig.first);
|
|
if (mIopRevision == IopRevision_e::IOSE) {
|
|
std::unique_ptr<SimIopConsole_c> Console = std::make_unique<SimIopEConsole_c>(
|
|
ConsoleConfig.second,
|
|
ConsoleId,
|
|
*this,
|
|
uint8_t(mConsoles.size()),
|
|
aMainframe.DoDisableAutoTerminal()
|
|
);
|
|
mConsoles.push_back(std::move(Console));
|
|
} else {
|
|
std::unique_ptr<SimIopConsole_c> Console = std::make_unique<SimIopDConsole_c>(
|
|
ConsoleConfig.second,
|
|
ConsoleId,
|
|
*this,
|
|
uint8_t(mConsoles.size()),
|
|
aMainframe.DoDisableAutoTerminal()
|
|
);
|
|
mConsoles.push_back(std::move(Console));
|
|
}
|
|
}
|
|
}
|
|
for (const auto &EthConfig : aConfig.get_child_safe("EthInterfaces")) {
|
|
std::string Name = EthConfig.first;
|
|
std::unique_ptr<SimIopEth_c> Eth = std::make_unique<SimIopEth_c>(EthConfig.second, *this, Name);
|
|
mEthInterfaces.push_back(std::move(Eth));
|
|
}
|
|
mMainframe->SetChannel(mIToCChannel->GetChannelId() - 8, *mIToCChannel);
|
|
mMainframe->SetChannel(mCToIChannel->GetChannelId() - 8, *mCToIChannel);
|
|
|
|
mKernelFileName = aConfig.get_optional<std::string>("KernelFileName");
|
|
mKernelPreamble = aConfig.get<size_t>("KernelPreamble", 8 * 8);
|
|
mParameterFileName = aConfig.get_optional<std::string>("ParameterFileName");
|
|
mRamFsFileName = aConfig.get_optional<std::string>("RamFsFileName");
|
|
mBootCluster = aConfig.get<uint32_t>("BootCluster", 0);
|
|
mBootChannel = aConfig.get<uint32_t>("BootChannel", 0);
|
|
mLogicalMainframeNumber = aConfig.get<uint8_t>("LogicalMainframeNumber", 0);
|
|
mOWSProtocolLevel = aConfig.get<uint8_t>("OWSProtocolLevel", 0);
|
|
mRunLevel = aConfig.get_optional<std::string>("RunLevel");
|
|
if (mRunLevel.is_initialized()) {
|
|
if (mRunLevel.get().length() != 1) throw Generic_x() << "RunLevel must be a single character";
|
|
}
|
|
if (aConfig.get_optional<std::string>("Peripherals").is_initialized() || (mIopRevision == IopRevision_e::IOSD)) {
|
|
mIopPeripheral = std::make_unique<SimIopPeripheral_c>(aConfig.get_child_safe("Peripherals"), *this);
|
|
}
|
|
}
|
|
catch (...) {
|
|
throw;
|
|
}
|
|
}
|
|
|
|
void SimIopCluster_c::GetPeripheralStatus(StatusReport_c &aStatus, PeripheralType_e aFilter, boost::timer::nanosecond_type aElapsedTime, bool aLongFormat) const {
|
|
switch (aFilter) {
|
|
case PeripheralType_e::Disk:
|
|
for (auto &Disk : mDisks) {
|
|
StatusReport_c Status;
|
|
Disk->GetStatus(Status, aElapsedTime, aLongFormat);
|
|
if (!Status.empty()) {
|
|
aStatus.put_child(Disk->GetName(), Status);
|
|
}
|
|
}
|
|
break;
|
|
case PeripheralType_e::Console:
|
|
for (auto &Console : mConsoles) {
|
|
StatusReport_c Status;
|
|
Console->GetStatus(Status, aElapsedTime, aLongFormat);
|
|
if (!Status.empty()) {
|
|
aStatus.put_child(Console->GetName(), Status);
|
|
}
|
|
}
|
|
break;
|
|
case PeripheralType_e::Network:
|
|
for (auto &EthInterface : mEthInterfaces) {
|
|
StatusReport_c Status;
|
|
EthInterface->GetStatus(Status, aElapsedTime, aLongFormat);
|
|
if (!Status.empty()) {
|
|
aStatus.put_child(EthInterface->GetName(), Status);
|
|
}
|
|
}
|
|
aStatus.put(aLongFormat ? "MF Packets Received" : "MFR", mPacketsReceived);
|
|
aStatus.put(aLongFormat ? "MF Packets Sent" : "MFS", mPacketsSent);
|
|
aStatus.put(aLongFormat ? "MF Queue Lenngth" : "QL", mIToCChannel->GetQueueLength());
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void SimIopCluster_c::RegisterCommands(CommandHooks_t &aHooks) {
|
|
for (auto &Disk : mDisks) {
|
|
Disk->RegisterCommands(aHooks);
|
|
}
|
|
for (auto &Console : mConsoles) {
|
|
Console->RegisterCommands(aHooks);
|
|
}
|
|
for (auto &Eth : mEthInterfaces) {
|
|
Eth->RegisterCommands(aHooks);
|
|
}
|
|
}
|
|
|
|
std::string SimIopCluster_c::GetName() const {
|
|
std::stringstream Name;
|
|
Name << "ICL" << DecPrinter(mIopId);
|
|
return Name.str();
|
|
}
|
|
|
|
std::string SimIopCluster_c::GetLongName() const {
|
|
std::stringstream Name;
|
|
Name << "IOP Cluster " << DecPrinter(mIopId);
|
|
return Name.str();
|
|
}
|
|
|
|
void SimIopCluster_c::MasterClear() {
|
|
for (auto &Disk : mDisks) {
|
|
Disk->MasterClear();
|
|
}
|
|
for (auto &Console : mConsoles) {
|
|
Console->Reset();
|
|
}
|
|
mState = States_e::Reset;
|
|
}
|
|
|
|
IopEPacket_c SimIopCluster_c::GenerateResponse(IopEPacket_c &aRequest) const {
|
|
IopEPacket_c Response(aRequest);
|
|
Response.SetSize(0);
|
|
Response.SetAckSeq(mSeqReceived);
|
|
Response.SetSeq(mSeqSent);
|
|
Response.SetFlags(IopEPacket_c::cFlagAck);
|
|
return Response;
|
|
}
|
|
|
|
IopEPacket_c SimIopCluster_c::GenerateResponse() const {
|
|
IopEPacket_c Response;
|
|
Response.SetSize(0);
|
|
Response.SetAckSeq(mSeqReceived);
|
|
Response.SetSeq(mSeqSent);
|
|
Response.SetFlags(IopEPacket_c::cFlagAck); // TODO: do we set this here?
|
|
return Response;
|
|
}
|
|
|
|
void SimIopCluster_c::SendPacket(IopEPacket_c &aPacket) {
|
|
// CRAY_ASSERT(!aReplace);
|
|
aPacket.SetAckSeq(mSeqReceived);
|
|
aPacket.SetSeq(mSeqSent);
|
|
mIToCChannel->QueuePacket(aPacket.Finalize());
|
|
++mSeqSent;
|
|
}
|
|
|
|
void SimIopCluster_c::SendPacket(std::vector<CInt_t> &aPacket) {
|
|
mIToCChannel->QueuePacket(aPacket);
|
|
}
|
|
|
|
MachineTypes_e SimIopCluster_c::GetMachineType() const
|
|
{
|
|
return mMainframe->GetMachineType();
|
|
}
|
|
|
|
void SimIopCluster_c::HandleIosEOPacket(IopEPacket_c &aPacket) {
|
|
const BitRange_s TypeRange(56, 63);
|
|
const BitRange_s RunLevelRange(48, 55);
|
|
const BitRange_s RequestRange(40, 47);
|
|
const BitRange_s ResponseRange(32, 39);
|
|
const BitRange_s ClusterRange(24, 31);
|
|
const BitRange_s IopRange(16, 23);
|
|
|
|
enum class RequestCodes_e {
|
|
OP_BOOT = 1, // boot the specified IOP
|
|
OP_DOWN = 2, // mark the specified IOP as down
|
|
OP_UP = 3, // mark the specified IOP as up
|
|
OP_KILL = 4, // master clear and down the specified IOP
|
|
OP_OBIT = 5, // obituary notice for the specified IOP
|
|
OP_ALIVE = 6, // existence notice for the specified IOP
|
|
OP_CLOCK = 7, // send current clock
|
|
OP_PANIC = 8, // CPU panic message
|
|
OP_DUMP = 9, // dump given IOP
|
|
OP_ISTOP = 10, // inform CPU of IOP stop
|
|
OP_IRESTART = 11, // inform CPU of IOP restart
|
|
OP_CHGLVL = 12, // Unicos run level changed notice
|
|
OP_RUNLVL = 13, // change the Unicos run level
|
|
OP_SYSHALT = 14, // halt the Unicos system
|
|
OP_IABORT = 15, // inform CPU of IOP abort
|
|
OP_VITAL = 16, // check health of UNICOS
|
|
OP_FPANIC = 17, // force a panic
|
|
OP_PREPANIC = 18, // CPU started panic, may flush
|
|
OP_ERRLOG = 19 // make errlog entry
|
|
};
|
|
|
|
const uint8_t cLogicalPath = 7;
|
|
|
|
CRAY_ASSERT(aPacket.GetLogicalPath() == cLogicalPath);
|
|
RequestCodes_e Request = RequestCodes_e(GetBits(aPacket.Data(0), RequestRange));
|
|
|
|
IopEPacket_c Response = GenerateResponse(aPacket);
|
|
|
|
switch (Request) {
|
|
case RequestCodes_e::OP_CLOCK: {
|
|
Response.SetSize(8);
|
|
SetBitsInPlace(Response.Data(0), TypeRange, 'o'); // Mark packet as response
|
|
SetBitsInPlace(Response.Data(0), RequestRange, uint64_t(RequestCodes_e::OP_CLOCK));
|
|
time_t NowTime = time(0);
|
|
struct tm *Now = localtime(&NowTime);
|
|
Response.Data(1) = CInt_t(0);
|
|
Response.Data(2) = CInt_t(Now->tm_mon+1); // Month
|
|
Response.Data(3) = CInt_t(Now->tm_mday); // Day
|
|
Response.Data(4) = CInt_t(Now->tm_year + 1900); // Year
|
|
Response.Data(5) = CInt_t(Now->tm_hour); // Hour
|
|
Response.Data(6) = CInt_t(Now->tm_min); // Minute
|
|
Response.Data(7) = CInt_t(Now->tm_sec); // Second
|
|
SendPacket(Response);
|
|
}
|
|
break;
|
|
case RequestCodes_e::OP_PANIC: {
|
|
MakeConsoles(0);
|
|
mConsoles[0]->SendString("================== KERNEL PANIC ================\r\n");
|
|
size_t CharCount = (aPacket.GetLength() - 2) * 8;
|
|
for (size_t i = 0; i < (CharCount + 7) / 8; ++i) {
|
|
aPacket.Data(i + 2) = SwapBytes(aPacket.Data(i + 2));
|
|
}
|
|
char *Str = (char*)(&aPacket.Data(2));
|
|
mConsoles[0]->SendString(Str, std::min(size_t(CharCount), strlen(Str)));
|
|
mConsoles[0]->SendString("\r\n================================================\r\n");
|
|
}
|
|
break;
|
|
default:
|
|
CRAY_ASSERT(false);
|
|
}
|
|
}
|
|
|
|
void SimIopCluster_c::HandleIosEPPacket(IopEPacket_c &aPacket) {
|
|
// Word 0
|
|
const BitRange_s TypeRange(56, 63);
|
|
const BitRange_s ClusterRange(48, 55);
|
|
const BitRange_s IopRange(40, 47);
|
|
const BitRange_s ChannelRange(32, 39);
|
|
const BitRange_s UnitRange(24, 31);
|
|
const BitRange_s RequestRange(16, 23);
|
|
const BitRange_s ResponseRange(8, 15);
|
|
// OPEN
|
|
///////////////////////////////////
|
|
// Word 1
|
|
const BitRange_s Open_HwTypeRange(32, 63);
|
|
const BitRange_s Open_PacketAddrRange(0, 31);
|
|
// Word 2
|
|
const BitRange_s Open_InReqQueueRange(32, 63);
|
|
const BitRange_s Open_InRspQueueRange(0, 31);
|
|
// Word 3
|
|
const BitRange_s Open_OutReqQueueRange(32, 63);
|
|
const BitRange_s Open_OutRspQueueRange(0, 31);
|
|
// CLOSE
|
|
///////////////////////////////////
|
|
// Word 1
|
|
const BitRange_s Close_PacketAddrRange(0, 31);
|
|
// I/O
|
|
///////////////////////////////////
|
|
// Word 1
|
|
const BitRange_s IO_AalRange(60, 63);
|
|
const BitRange_s IO_FlagsRange(52, 59);
|
|
const BitRange_s IO_LengthMtuRange(32, 51);
|
|
const BitRange_s IO_VpiVciRange(0, 31);
|
|
// Word 2
|
|
const BitRange_s IO_PacketAddrRange(32, 63);
|
|
const BitRange_s IO_SeqAddrRange(0, 31);
|
|
|
|
enum class Flags_e {
|
|
PP_TX_VPCI = 0x01, // vpi/vci is used for transmitting data
|
|
PP_RX_VPCI = 0x02, // vpi/vci is used for receiving data
|
|
};
|
|
enum class Request_e {
|
|
PP_OPEN = 0x01, // open function
|
|
PP_CLOSE = 0x02, // close function
|
|
PP_INPUT = 0x03, // Input function
|
|
PP_OUTPUT = 0x04, // Output function
|
|
PP_ACTIVATE = 0x05, // Activate a VCI/VPI pair on the FORE card
|
|
PP_DEACTIVATE = 0x06, // Deactivate a VCI/VPI pair on the FORE card
|
|
PP_FSTATS = 0x07, // Dump FORE statistics to addr <see ioctl>
|
|
PP_DSTATS = 0x08, // Dump driver statistics to addr <see ioctl>
|
|
PP_TRACE = 0x09, // Dump standard trace buffer
|
|
PP_ATRACE = 0x0a, // Dump auxiliary trace buffer
|
|
PP_GQUEUES = 0x0b, // Get a copy of all the IOS queue structures
|
|
PP_GOC3CONF = 0x0c, // Get OC3 configuration from SUNI chip
|
|
PP_SOC3CONF = 0x0d, // Set OC3 configuration on SUNI chip
|
|
PP_DEBUG = 0x20, // Toggle IOS debug mode (prints traces, etc)
|
|
PP_CLRTRACE = 0x21 // Clear the IOS trace and aux trace
|
|
};
|
|
enum class Response_e {
|
|
PPST_OK = 0x00, // ok status
|
|
PPST_HDW = 0x01, // hardware error
|
|
PPST_PROER = 0x02, // protocol error
|
|
PPST_ILCHN = 0x03, // illegal channel
|
|
PPST_ILFNC = 0x04, // illegal function
|
|
PPST_RTO = 0x05, // read timeout
|
|
PPST_WTO = 0x06, // write timeout
|
|
PPST_ILDRV = 0x07, // illegal driver
|
|
PPST_INVAL = 0x08, // invalid argument (010)
|
|
PPST_FAILED = 0x09, // request failed (011)
|
|
PPST_TIMER = 0x0a, // Funcion timeout (012) (0x0a)
|
|
PPST_OVRFLW = 0x0b, // Queue overflow error (013) (0x0b)
|
|
PPST_CANCEL = 0x0c, // I/O request cancelled (014) (0x0c)
|
|
PPST_BUSY = 0x0d, // Packet request already in progress (busy)
|
|
PPST_OPENOK = 0xf0 // Open completed succesfully
|
|
};
|
|
|
|
Request_e Request = Request_e(GetBits(aPacket.Data(0), RequestRange));
|
|
switch (Request) {
|
|
case Request_e::PP_OPEN:
|
|
{
|
|
IopEPacket_c Response = GenerateResponse(aPacket);
|
|
Response.SetSize(4);
|
|
for (size_t Idx = 0; Idx < size_t(aPacket.GetLength()-1); ++Idx) {
|
|
Response.Data(Idx) = aPacket.Data(Idx);
|
|
}
|
|
mLogger << setloglevel(LogLevel_IoActivity) << "ATM open" << std::endl;
|
|
//DumpPacket(mLogger, (CAddr_t(GetBits(aPacket.Data(2), IO_PacketAddrRange))), *mMainframe, "packet");
|
|
|
|
SetBitsInPlace(Response.Data(0), TypeRange, 'p'); // Mark packet as response
|
|
SetBitsInPlace(Response.Data(0), ResponseRange, uint64_t(Response_e::PPST_OPENOK));
|
|
SendPacket(Response);
|
|
}
|
|
break;
|
|
case Request_e::PP_INPUT:
|
|
{
|
|
IopEPacket_c Response = GenerateResponse(aPacket);
|
|
Response.SetSize(4);
|
|
for (size_t Idx = 0; Idx < size_t(aPacket.GetLength()-1); ++Idx) {
|
|
Response.Data(Idx) = aPacket.Data(Idx);
|
|
}
|
|
mLogger << setloglevel(LogLevel_IoActivity) << "ATM input " <<
|
|
"Length: " << DecPrinter((GetBits(aPacket.Data(1), IO_LengthMtuRange))) << " " <<
|
|
"Vpi/Vci: " << HexPrinter((CAddr_t(GetBits(aPacket.Data(1), IO_VpiVciRange)))) << " " <<
|
|
"Seq: " << HexPrinter((CAddr_t(GetBits(aPacket.Data(2), IO_SeqAddrRange)))) << " " << std::endl;
|
|
//DumpPacket(mLogger, (CAddr_t(GetBits(aPacket.Data(1), Open_PacketAddrRange))), *mMainframe, "packet");
|
|
|
|
SetBitsInPlace(Response.Data(0), TypeRange, 'p'); // Mark packet as response
|
|
SetBitsInPlace(Response.Data(0), ResponseRange, uint64_t(Response_e::PPST_OK));
|
|
SendPacket(Response);
|
|
}
|
|
break;
|
|
case Request_e::PP_OUTPUT:
|
|
{
|
|
IopEPacket_c Response = GenerateResponse(aPacket);
|
|
Response.SetSize(4);
|
|
for (size_t Idx = 0; Idx < size_t(aPacket.GetLength()-1); ++Idx) {
|
|
Response.Data(Idx) = aPacket.Data(Idx);
|
|
}
|
|
mLogger << setloglevel(LogLevel_IoActivity) << "ATM output " <<
|
|
"Length: " << DecPrinter((GetBits(aPacket.Data(1), IO_LengthMtuRange))) << " " <<
|
|
"Vpi/Vci: " << HexPrinter((CAddr_t(GetBits(aPacket.Data(1), IO_VpiVciRange)))) << " " <<
|
|
"Seq: " << HexPrinter((CAddr_t(GetBits(aPacket.Data(2), IO_SeqAddrRange)))) << " " << std::endl;
|
|
//DumpPacket(mLogger, (CAddr_t(GetBits(aPacket.Data(1), Open_PacketAddrRange))), *mMainframe, "packet");
|
|
|
|
SetBitsInPlace(Response.Data(0), TypeRange, 'p'); // Mark packet as response
|
|
SetBitsInPlace(Response.Data(0), ResponseRange, uint64_t(Response_e::PPST_OK));
|
|
SendPacket(Response);
|
|
}
|
|
break;
|
|
case Request_e::PP_CLOSE:
|
|
{
|
|
IopEPacket_c Response = GenerateResponse(aPacket);
|
|
Response.SetSize(4);
|
|
for (size_t Idx = 0; Idx < size_t(aPacket.GetLength()-1); ++Idx) {
|
|
Response.Data(Idx) = aPacket.Data(Idx);
|
|
}
|
|
mLogger << setloglevel(LogLevel_IoActivity) << "ATM close" << std::endl;
|
|
//DumpPacket(mLogger, (CAddr_t(GetBits(aPacket.Data(2), IO_PacketAddrRange))), *mMainframe, "packet");
|
|
|
|
SetBitsInPlace(Response.Data(0), TypeRange, 'p'); // Mark packet as response
|
|
SetBitsInPlace(Response.Data(0), ResponseRange, uint64_t(Response_e::PPST_OK));
|
|
SendPacket(Response);
|
|
}
|
|
break;
|
|
default:
|
|
CRAY_ASSERT(false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void SimIopCluster_c::HandleIosEDPacket(IopEPacket_c &aPacket) {
|
|
// Word -1
|
|
// EIOP number is stuffed into 'process' GetProcess
|
|
// Word 0
|
|
const BitRange_s TypeRange(56, 63);
|
|
const BitRange_s RequestCodeRange(40, 47);
|
|
const BitRange_s ResponseCodeRange(32, 39);
|
|
const BitRange_s DeviceTypeRange(25, 31);
|
|
const BitRange_s UnitRange(21, 24);
|
|
const BitRange_s ChannelRange(16, 20);
|
|
const BitRange_s RawFunctionParameterRange(0, 15);
|
|
// Word 1
|
|
const BitRange_s IndexRange(32, 63);
|
|
const BitRange_s CurrentBpRange(0, 31);
|
|
// Word 2
|
|
const BitRange_s CylinderRange(32, 63);
|
|
const BitRange_s HeadRange(31, 31);
|
|
const BitRange_s SectorRange(30, 30);
|
|
const BitRange_s SectorCntRange(0, 15);
|
|
// Word 3
|
|
const BitRange_s ErrorRecoveryInProgressRange(59, 59);
|
|
const BitRange_s NextValidPacketRange(58, 58);
|
|
const BitRange_s EnableSpiralRange(57, 57);
|
|
const BitRange_s DisableZeroLatencyRange(56, 56);
|
|
const BitRange_s LogicalSectorSizeRange(32, 47);
|
|
const BitRange_s ChannelBufferDesciptorRange(16, 31);
|
|
const BitRange_s AddressIncrementRange(0, 15);
|
|
// Word 4
|
|
const BitRange_s TargetMemoryTypeRange(48, 50);
|
|
const BitRange_s TargetMemoryAddrRange(0, 47);
|
|
// Word 5
|
|
const BitRange_s ChanneBufferOffsetRange(32, 47);
|
|
const BitRange_s StreamMasksRange(0, 15);
|
|
|
|
// Response only fields
|
|
// Word 6
|
|
const BitRange_s GeneralStatusRange(48, 63);
|
|
const BitRange_s M1StatusRange(32, 47);
|
|
const BitRange_s DriveStatusRange(16, 31);
|
|
const BitRange_s ControllerStatus(0, 15);
|
|
// Word 7
|
|
const BitRange_s CylinderInErrorRange(32, 63);
|
|
const BitRange_s HeadInErrorRange(31, 31);
|
|
const BitRange_s SectorInErrorRange(30, 30);
|
|
const BitRange_s SiralOffsetRange(0, 15);
|
|
// Word 8...24
|
|
const BitRange_s WriteBehindCylinderRange(48, 63);
|
|
const BitRange_s WriteBehindHeadRange(32, 47);
|
|
const BitRange_s WriteBehindSectorRange(16, 31);
|
|
const BitRange_s WriteBehindSegmentNumberRange(9, 15);
|
|
const BitRange_s WriteBehindRecoveredFlagRange(8, 8);
|
|
const BitRange_s WriteBehindSectorsInErrorRange(0, 7);
|
|
// 25..70
|
|
// There is more status here up to 70
|
|
|
|
enum class RequestCodes_e {
|
|
// Note: there's quite a bit of repetition here in the list. From what I can tell, this is mostly about more reable names, but same meaning.
|
|
DRQ_ECHO = 001, // echo
|
|
DRQ_READ = 002, // read data
|
|
DRQ_WRITE = 003, // write data
|
|
DRQ_WRITEB = 004, // write behind
|
|
DRQ_RID = 033, // read ID
|
|
DRQ_RDAECC = 041, // read data and ecc - dd6x
|
|
DRQ_READ_TARGET = 042,
|
|
DRQ_WDAECCTP = 046, // write data, ecc, toggle parity - dd6x
|
|
DRQ_WDAECC = 047, // write data and ecc - dd6x
|
|
DRQ_WID = 052, // write ID
|
|
DRQ_WDID = 053, // write defective ID
|
|
DRQ_WZECC = 055, // write with zero ecc
|
|
|
|
DRQ_WRITE_BEHIND = 004,
|
|
DRQ_CONFIGURE_UP = 011,
|
|
DRQ_CONFIGURE_DOWN = 012,
|
|
DRQ_RETURN_STATS = 021,
|
|
DRQ_READ_SECTOR = 030,
|
|
DRQ_READ_SECTOR_FROM_BUFFER = 031,
|
|
DRQ_READ_SECTOR_NO_HW_RA = 032,
|
|
DRQ_READ_ID = 033,
|
|
DRQ_READ_ABSOLUTE = 034,
|
|
DRQ_READ_BUFFER = 035,
|
|
DRQ_READ_ECC = 036,
|
|
DRQ_READ_VECTORS = 037,
|
|
DRQ_READ_TRACK_HEADER = 040,
|
|
DRQ_WRITE_DATA = 050,
|
|
DRQ_WRITE_THRU = 051,
|
|
DRQ_WRITE_ID = 052,
|
|
DRQ_WRITE_DEFECTIVE_ID = 053,
|
|
DRQ_WRITE_BUFFER = 054,
|
|
DRQ_WRITE_DATA_ZERO_ECC = 055,
|
|
DRQ_WRITE_TRACK_HEADER = 056,
|
|
DRQ_DIAGNOSTIC = 060,
|
|
DRQ_RESET = 061,
|
|
DRQ_CLEAR_FAULTS = 062,
|
|
DRQ_RTZ = 063,
|
|
DRQ_SELECT = 064,
|
|
DRQ_DESELECT = 065,
|
|
DRQ_DESELECT_OPP_SELECT = 066,
|
|
DRQ_SEEK = 067,
|
|
DRQ_SELECT_OFFSET = 070,
|
|
DRQ_SELECT_STROBE = 071,
|
|
DRQ_SELECT_HEAD = 072,
|
|
DRQ_WRITE_STREAMS = 076,
|
|
DRQ_LOAD_FORMAT = 077,
|
|
DRQ_RETURN_GENERAL_STATUS = 0174,
|
|
DRQ_RETURN_SELECT_STATUS = 0175,
|
|
DRQ_ACCUMULATE_STATUS = 0176,
|
|
DRQ_RETURN_ACCUMULATED_STATUS = 0177
|
|
};
|
|
enum class ResponseCodes_e {
|
|
DRS_OK = 0000, // ok
|
|
DRS_ERR_MIN = 0010,
|
|
DRS_ERR = 0010, // error
|
|
DRS_DERR = 0011, // data error
|
|
DRS_ERR_MAX = 0077,
|
|
DRS_RERR = 0301, // request error
|
|
DRS_PERR = 0303, // parameter error
|
|
DRS_NVPERR = 0322, // next valid packet error
|
|
|
|
DRS_RECOVERED = 0001,
|
|
DRS_CORRECTED = 0002,
|
|
DRS_SINGLE_BIT = 0003,
|
|
DRS_IO_WAIT = 0004,
|
|
DRS_FIRST_FATAL_ERROR_CODE = 0010,
|
|
DRS_IO_ERROR = 0010,
|
|
DRS_TRANSFER_ERROR = 0011,
|
|
DRS_POS_ERROR = 0012,
|
|
DRS_HEAD_ERROR = 0013,
|
|
DRS_SELECT_ERROR = 0014,
|
|
DRS_DESELECT_ERROR = 0015,
|
|
DRS_RESERVE_ERROR = 0016,
|
|
DRS_RELEASE_ERROR = 0017,
|
|
DRS_DOUBLE_BIT = 0020,
|
|
DRS_STATUS_ERROR = 0021,
|
|
DRS_TRANSFER_PARAM_ERROR = 0022,
|
|
DRS_TIMEOUT = 0023,
|
|
DRS_CHANNEL_ERROR = 0024,
|
|
DRS_NOT_READY = 0025,
|
|
DRS_LOAD_POS_ERROR = 0026,
|
|
DRS_RCP_ERROR = 0027,
|
|
DRS_RESET_ERROR = 0030,
|
|
DRS_STROBE_ERROR = 0031,
|
|
DRS_DIAGNOSTIC_ERROR = 0032,
|
|
DRS_CLEAR_FAULTS_ERROR = 0033,
|
|
DRS_OFFSET_ERROR = 0034,
|
|
DRS_ECHO_ERROR = 0035,
|
|
DRS_IO_TIMEOUT = 0040,
|
|
DRS_POS_TIMEOUT = 0041,
|
|
DRS_HEAD_TIMEOUT = 0042,
|
|
DRS_SELECT_TIMEOUT = 0043,
|
|
DRS_DESELECT_TIMEOUT = 0044,
|
|
DRS_RESERVE_TIMEOUT = 0045,
|
|
DRS_RELEASE_TIMEOUT = 0046,
|
|
DRS_STATUS_TIMEOUT = 0047,
|
|
DRS_LOADPOS_TIMEOUT = 0050,
|
|
DRS_RCP_TIMEOUT = 0051,
|
|
DRS_RESET_TIMEOUT = 0052,
|
|
DRS_STROBE_TIMEOUT = 0053,
|
|
DRS_DIAG_TIMEOUT = 0054,
|
|
DRS_CLRFLTS_TIMEOUT = 0055,
|
|
DRS_OFFSET_TIMEOUT = 0056,
|
|
DRS_ECHO_TIMEOUT = 0057,
|
|
DRS_BAD_OPTION = 0076,
|
|
DRS_OTHER_FATAL_ERROR = 0077,
|
|
DRS_LAST_FATAL_ERROR_CODE = 0077,
|
|
DRS_NO_LOCAL_MEMORY = 0100,
|
|
DRS_NO_CACHE_MEMORY = 0101,
|
|
DRS_SEGMENT_MEMORY = 0102,
|
|
DRS_NO_STATISTICS = 0200,
|
|
DRS_INVALID_REQUEST_CODE = 0301,
|
|
DRS_UNKNOWN_PACKET_TYPE = 0302,
|
|
DRS_INVALID_PARAMETER = 0303,
|
|
DRS_NO_DRIVER = 0320,
|
|
DRS_ALREADY_CONFIGURED = 0321,
|
|
DRS_NVP_SEQUENCE_ERROR = 0322,
|
|
DRS_UNSUPPORTED = 0323,
|
|
DRS_DEVICE_NOT_CONFIGURED = 0324,
|
|
DRS_CONFIGURATION_FAILURE = 0325,
|
|
DRS_UCODE_CHKSUM_FAILURE = 0326,
|
|
DRS_INCOMPAT_UNICOS = 0327,
|
|
DRS_NOT_EXECUTED = 0377
|
|
};
|
|
enum class MemoryTypes_e {
|
|
DTM_CM = 0, // central memory
|
|
DTM_SSD = 2, // SSD
|
|
DTM_T3D = 4 // T3D
|
|
};
|
|
|
|
uint8_t Channel = uint8_t(GetBits(aPacket.Data(0), ChannelRange));
|
|
uint8_t Unit = uint8_t(GetBits(aPacket.Data(0), UnitRange));
|
|
RequestCodes_e RequestCode = RequestCodes_e(GetBits(aPacket.Data(0), RequestCodeRange));
|
|
size_t Cylinder = size_t(GetBits(aPacket.Data(2), CylinderRange));
|
|
size_t Head = size_t(GetBits(aPacket.Data(2), HeadRange));
|
|
size_t Sector = size_t(GetBits(aPacket.Data(2), SectorRange));
|
|
size_t SectorCnt = size_t(GetBits(aPacket.Data(2), SectorCntRange));
|
|
MemoryTypes_e TargetMemoryType = MemoryTypes_e(GetBits(aPacket.Data(4), TargetMemoryTypeRange));
|
|
uint64_t TargetMemoryAddr = GetBits(aPacket.Data(4), TargetMemoryAddrRange);
|
|
uint8_t IopNumber = aPacket.GetProcess();
|
|
switch (RequestCode) {
|
|
case RequestCodes_e::DRQ_READ:
|
|
{
|
|
bool Found = false;
|
|
for (auto &Disk : mDisks) {
|
|
if (Disk->GetPhysicalDeviceId() == Channel && Disk->GetIopNumber() == IopNumber && Disk->GetUnit() == Unit) {
|
|
Found = true;
|
|
CRAY_ASSERT(Head == 0);
|
|
CRAY_ASSERT(Sector == 0);
|
|
CRAY_ASSERT(TargetMemoryType == MemoryTypes_e::DTM_CM);
|
|
Disk->Read(Cylinder, uint32_t(SectorCnt), CAddr_t(TargetMemoryAddr));
|
|
|
|
IopEPacket_c Response = GenerateResponse(aPacket);
|
|
Response.SetSize(68);
|
|
for (size_t Idx = 0; Idx < size_t(aPacket.GetLength()-1); ++Idx) {
|
|
Response.Data(Idx) = aPacket.Data(Idx);
|
|
}
|
|
SetBitsInPlace(Response.Data(0), TypeRange, 'd'); // Mark packet as response
|
|
SetBitsInPlace(Response.Data(0), RequestCodeRange, uint64_t(RequestCodes_e::DRQ_READ));
|
|
SetBitsInPlace(Response.Data(0), ResponseCodeRange, uint64_t(ResponseCodes_e::DRS_OK));
|
|
SendPacket(Response);
|
|
}
|
|
}
|
|
if (!Found) { CRAY_ASSERT(false); }
|
|
}
|
|
break;
|
|
case RequestCodes_e::DRQ_WRITE:
|
|
case RequestCodes_e::DRQ_WRITE_BEHIND:
|
|
{
|
|
bool Found = false;
|
|
for (auto &Disk : mDisks) {
|
|
if (Disk->GetPhysicalDeviceId() == Channel) {
|
|
Found = true;
|
|
CRAY_ASSERT(Head == 0);
|
|
CRAY_ASSERT(Sector == 0);
|
|
CRAY_ASSERT(TargetMemoryType == MemoryTypes_e::DTM_CM);
|
|
Disk->Write(Cylinder, uint32_t(SectorCnt), CAddr_t(TargetMemoryAddr));
|
|
|
|
IopEPacket_c Response = GenerateResponse(aPacket);
|
|
Response.SetSize(68);
|
|
for (size_t Idx = 0; Idx < aPacket.GetLength(); ++Idx) {
|
|
Response.Data(Idx) = aPacket.Data(Idx);
|
|
}
|
|
SetBitsInPlace(Response.Data(0), TypeRange, 'd'); // Mark packet as response
|
|
SetBitsInPlace(Response.Data(0), RequestCodeRange, uint64_t(RequestCodes_e::DRQ_READ));
|
|
SetBitsInPlace(Response.Data(0), ResponseCodeRange, uint64_t(ResponseCodes_e::DRS_OK));
|
|
SendPacket(Response);
|
|
}
|
|
}
|
|
if (!Found) { CRAY_ASSERT(false); }
|
|
}
|
|
break;
|
|
case RequestCodes_e::DRQ_CONFIGURE_DOWN:
|
|
case RequestCodes_e::DRQ_CONFIGURE_UP:
|
|
case RequestCodes_e::DRQ_RETURN_ACCUMULATED_STATUS:
|
|
case RequestCodes_e::DRQ_RETURN_GENERAL_STATUS:
|
|
case RequestCodes_e::DRQ_DIAGNOSTIC:
|
|
case RequestCodes_e::DRQ_RESET:
|
|
case RequestCodes_e::DRQ_DESELECT:
|
|
case RequestCodes_e::DRQ_ECHO:
|
|
{
|
|
IopEPacket_c Response = GenerateResponse(aPacket);
|
|
Response.SetSize(68);
|
|
for (size_t Idx = 0; Idx < aPacket.GetLength(); ++Idx) {
|
|
Response.Data(Idx) = aPacket.Data(Idx);
|
|
}
|
|
SetBitsInPlace(Response.Data(0), TypeRange, 'd'); // Mark packet as response
|
|
SetBitsInPlace(Response.Data(0), RequestCodeRange, uint64_t(RequestCode));
|
|
SetBitsInPlace(Response.Data(0), ResponseCodeRange, uint64_t(ResponseCodes_e::DRS_OK));
|
|
SendPacket(Response);
|
|
}
|
|
break;
|
|
default:
|
|
CRAY_ASSERT(false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void SimIopCluster_c::HandleAPacket(std::vector<CInt_t> &aPacket) {
|
|
enum class FunctionCodes_e {
|
|
Read = 1,
|
|
Write = 2,
|
|
WriteBehind = 12,
|
|
SelectDrive = 040,
|
|
ReleaseDrive = 041,
|
|
ReleaseOpposite = 042
|
|
};
|
|
enum class StatusCodes_e {
|
|
OK = 0,
|
|
RecoveredDataError = 1,
|
|
CorrectedDataError = 11,
|
|
UnrecoveredDataError = 12,
|
|
UnrecoveredHardwareError = 13,
|
|
BadParameterInRequest = 16,
|
|
NoBackdoorConfigured = 17
|
|
};
|
|
|
|
uint8_t Diag = uint8_t(GetBitsReverse(aPacket[1], 16, 16));
|
|
uint16_t DiagRequest = uint16_t(GetBitsReverse(aPacket[1], 17, 21));
|
|
|
|
// The second word would also contain more info for diagnostics (see apacket.h) but we don't support that for now...
|
|
uint8_t StripeFactor = uint8_t(GetBitsReverse(aPacket[2], 0, 3));
|
|
uint8_t UpperCylinder = uint8_t(GetBitsReverse(aPacket[2], 31, 31));
|
|
uint8_t SequenceNumber = uint8_t(GetBitsReverse(aPacket[2], 40, 47));
|
|
uint16_t MinorDev = uint16_t(GetBitsReverse(aPacket[2], 48, 63));
|
|
|
|
uint32_t TargetMem = uint32_t(GetBitsReverse(aPacket[3], 0, 1));
|
|
uint32_t DataAddr = uint32_t(GetBitsReverse(aPacket[3], 2, 31));
|
|
FunctionCodes_e FunctionCode = FunctionCodes_e(GetBitsReverse(aPacket[3], 32, 39));
|
|
StatusCodes_e ReturnStatus = StatusCodes_e(GetBitsReverse(aPacket[3], 40, 47));
|
|
DeviceTypes_e DeviceType = DeviceTypes_e(GetBitsReverse(aPacket[3], 48, 52));
|
|
uint8_t IopNumber = uint8_t(GetBitsReverse(aPacket[3], 53, 54));
|
|
uint8_t UnitNumber = uint8_t(GetBitsReverse(aPacket[3], 55, 56));
|
|
uint8_t PhysicalChannel = uint8_t(GetBitsReverse(aPacket[3], 57, 63));
|
|
|
|
uint32_t Cylinder;
|
|
uint32_t Head;
|
|
uint32_t Sector;
|
|
// Device address for HDDs
|
|
if (GetMachineType() == MachineTypes_e::YEL || GetMachineType() == MachineTypes_e::YMP) {
|
|
Cylinder = uint32_t(GetBitsReverse(aPacket[4], 2, 31));
|
|
Head = uint32_t(GetBitsReverse(aPacket[4], 0, 0));
|
|
Sector = uint32_t(GetBitsReverse(aPacket[4], 1, 1));
|
|
} else {
|
|
Cylinder = uint32_t(GetBitsReverse(aPacket[4], 0, 10)) | (UpperCylinder << 11);
|
|
Head = uint32_t(GetBitsReverse(aPacket[4], 11, 15));
|
|
Sector = uint32_t(GetBitsReverse(aPacket[4], 16, 22));
|
|
}
|
|
// Device address for RAM disks
|
|
uint16_t RamAddr = uint16_t(GetBitsReverse(aPacket[4], 0, 31));
|
|
|
|
uint32_t WordCount = uint32_t(GetBitsReverse(aPacket[4], 32, 63));
|
|
|
|
uint16_t ReadAhead = uint16_t(GetBitsReverse(aPacket[5], 32, 47));
|
|
|
|
CRAY_ASSERT(Diag == 0);
|
|
|
|
std::stringstream EventStr;
|
|
EventStr << "C->I PACKET A request -" <<
|
|
" StripeFactor: " << HexPrinter(StripeFactor) <<
|
|
" SeqNo: " << HexPrinter(SequenceNumber) <<
|
|
" MinorDev: " << HexPrinter(MinorDev) <<
|
|
" TargetMem: " << HexPrinter(TargetMem) <<
|
|
" DataAddr: " << HexPrinter(DataAddr) <<
|
|
" FunctionCode: " << HexPrinter(uint8_t(FunctionCode)) <<
|
|
" RetStatus: " << HexPrinter(uint8_t(ReturnStatus)) <<
|
|
" DeviceType: " << DecPrinter(uint8_t(DeviceType)) <<
|
|
" Iop: " << OctPrinter(IopNumber) <<
|
|
" Unit: " << DecPrinter(UnitNumber) <<
|
|
" PhysicalChannel: " << OctPrinter(PhysicalChannel) <<
|
|
" CHS: " << DecPrinter(Cylinder) << "-" << DecPrinter(Head) << "-" << DecPrinter(Sector) <<
|
|
" WordCount: " << HexPrinter(WordCount) <<
|
|
" ReadAhead: " << DecPrinter(ReadAhead) <<
|
|
std::endl;
|
|
mLogger << setloglevel(LogLevel_EventFire) << EventStr.str();
|
|
GetMainframe().GetEventDispatcher().Fire(EventStr.str());
|
|
|
|
// CRAY_ASSERT(StripeFactor == 0);
|
|
CRAY_ASSERT(TargetMem == 0);
|
|
CRAY_ASSERT(FunctionCode == FunctionCodes_e::Read || FunctionCode == FunctionCodes_e::Write || FunctionCode == FunctionCodes_e::WriteBehind);
|
|
// CRAY_ASSERT(DeviceType == DeviceTypes_e::DD29);
|
|
|
|
// Look up the drive
|
|
bool Found = false;
|
|
for (auto &Disk : mDisks) {
|
|
if (Disk->GetPhysicalDeviceId() == PhysicalChannel && Disk->GetIopNumber() == IopNumber && Disk->GetUnit() == UnitNumber) {
|
|
Found = true;
|
|
switch(FunctionCode) {
|
|
case FunctionCodes_e::Read:
|
|
Disk->Read(Cylinder, Head, Sector, WordCount * sizeof(CInt_t) / Disk->GetSectorSize(), CAddr_t(DataAddr));
|
|
break;
|
|
case FunctionCodes_e::Write:
|
|
case FunctionCodes_e::WriteBehind:
|
|
Disk->Write(Cylinder, Head, Sector, WordCount * sizeof(CInt_t) / Disk->GetSectorSize(), CAddr_t(DataAddr));
|
|
break;
|
|
default:
|
|
CRAY_ASSERT(false);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (!Found) throw Generic_x() << "Can't find drive for physical channel: " << OctPrinter(PhysicalChannel) << " iop: " << OctPrinter(IopNumber) << " and unit: " << DecPrinter(UnitNumber);
|
|
|
|
// Compose a reply
|
|
ReversePacket(aPacket);
|
|
aPacket[3] = SetBitsReverse(aPacket[3], 40, 47, uint64_t(StatusCodes_e::OK));
|
|
mIToCChannel->QueuePacket(aPacket);
|
|
}
|
|
|
|
void SimIopCluster_c::HandleKPacket(std::vector<CInt_t> &aPacket) {
|
|
// Kernel crash: wait for the next packet, print it, and exit
|
|
mState = States_e::KernelPanic;
|
|
}
|
|
|
|
void SimIopCluster_c::Tick() {
|
|
for (auto &Console : mConsoles) Console->Tick();
|
|
for (auto &EthInterfaces : mEthInterfaces) EthInterfaces->Tick();
|
|
|
|
switch (mState) {
|
|
case States_e::Reset:
|
|
/* Boot config:
|
|
*
|
|
* On Model-E boot, boot info is in registers.
|
|
*
|
|
$ELSE
|
|
owslink,0 A5 Boot cluster number
|
|
owschan,0 A6 Boot cluster channel number
|
|
rfsleng,0 S1 Length of root file system
|
|
scfleng,0 S2 Length of spare chip configuration file
|
|
sysmem,0 S4 LWA+1 of UNICOS binary
|
|
csim,0 S6 CSIM flag from deadstart
|
|
end,0 S7 length of binary + spare chip + par file
|
|
S3 <D'8
|
|
S1 A7
|
|
S1 S1&S3
|
|
logicalmf,0 S1 logical mainframe number
|
|
S1 A7
|
|
S1 S1>D'8
|
|
S1 S1&S3
|
|
owsplvl,0 S1 OWS protocol level
|
|
S1 A7
|
|
S1 S1>D'16
|
|
S1 S1&S3
|
|
S1 S1<D'56
|
|
irunlvl,0 S1 Run level for init
|
|
$ENDIF
|
|
|
|
*/
|
|
if (mMasterCluster) {
|
|
mSeqSent = 0;
|
|
mSeqReceived = 0;
|
|
for (auto &Console : mConsoles) Console->Reset();
|
|
if (mKernelFileName.is_initialized()) {
|
|
CAddr_t KernelStart = CAddr_t(0);
|
|
mMainframe->LoadImageFile(mKernelFileName.get().c_str(), KernelStart, mKernelPreamble);
|
|
uint64_t KernelSize = boost::filesystem::file_size(mKernelFileName.get());
|
|
CAddr_t DataEnd = KernelStart + CAddr_t((KernelSize + sizeof(CInt_t) - 1) / sizeof(CInt_t));
|
|
if (mParameterFileName.is_initialized()) {
|
|
CAddr_t ParamStart = DataEnd;
|
|
mMainframe->LoadImageFile(mParameterFileName.get().c_str(), ParamStart);
|
|
uint64_t ParamSize = boost::filesystem::file_size(mParameterFileName.get());
|
|
CAddr_t ParamEnd = ParamStart + CAddr_t((ParamSize + sizeof(CInt_t) - 1) / sizeof(CInt_t));
|
|
if (!mDontTouchExchangePacket) {
|
|
mMainframe->MemWrite(CAddr_t(0x00000c), SwapBytes(CInt_t(ParamStart))); // Start address of PARAM file patched into S4 on boot
|
|
mMainframe->MemWrite(CAddr_t(0x00000f), SwapBytes(CInt_t(ParamEnd))); // End address of PARAM file patched into S7 on boot
|
|
}
|
|
DataEnd = ParamEnd;
|
|
if (mRamFsFileName.is_initialized()) {
|
|
CAddr_t RamFsStart = DataEnd;
|
|
mMainframe->LoadImageFile(mRamFsFileName.get().c_str(), RamFsStart);
|
|
uint64_t RamFsSize = boost::filesystem::file_size(mRamFsFileName.get());
|
|
RamFsSize = CAddr_t((RamFsSize + sizeof(CInt_t) - 1) / sizeof(CInt_t));
|
|
CAddr_t RamFsEnd = CAddr_t(RamFsStart + RamFsSize);
|
|
if (!mDontTouchExchangePacket) {
|
|
mMainframe->MemWrite(CAddr_t(0x000009), SwapBytes(CInt_t(RamFsSize))); // End address of RAM FS file patched into S1 on boot
|
|
}
|
|
DataEnd = RamFsEnd;
|
|
}
|
|
}
|
|
if (!mDontTouchExchangePacket) {
|
|
mMainframe->MemWriteNoWatchpointByType<CAddr_t>(CAddr_t(0x000005) * 2 + 1, SwapBytes(CAddr_t(mBootCluster))); // Boot cluster in A5
|
|
mMainframe->MemWriteNoWatchpointByType<CAddr_t>(CAddr_t(0x000006) * 2 + 1, SwapBytes(CAddr_t(mBootChannel))); // Boot channel in A6
|
|
}
|
|
uint8_t RunLevel = 0;
|
|
if (mRunLevel.is_initialized()) {
|
|
RunLevel = mRunLevel.get()[0];
|
|
}
|
|
if (!mDontTouchExchangePacket) {
|
|
CAddr_t A7 = (RunLevel << 16) | (mOWSProtocolLevel << 8) | (mLogicalMainframeNumber); // RunLevel needs to be specified as a 'char' not as an 'int'
|
|
mMainframe->MemWriteNoWatchpointByType<CAddr_t>(CAddr_t(0x000007) * 2 + 1, SwapBytes(A7)); // RunLevel and stuff in A7
|
|
}
|
|
}
|
|
mMainframe->CpuMasterClear(true);
|
|
mMainframe->CpuMasterClear(false);
|
|
}
|
|
if (GetIopRevision() == IopRevision_e::IOSD) {
|
|
time_t NowTime = time(0);
|
|
struct tm *Now = localtime(&NowTime);
|
|
std::vector<CInt_t> Packet(6);
|
|
Packet[0] = CInt_t(Now->tm_mon + 1); // Month
|
|
Packet[1] = CInt_t(Now->tm_mday); // Day
|
|
Packet[2] = 1999; // CInt_t(Now->tm_year + 1900); // Year
|
|
Packet[3] = CInt_t(Now->tm_hour); // Hour
|
|
Packet[4] = CInt_t(Now->tm_min); // Minute
|
|
Packet[5] = CInt_t(Now->tm_sec); // Second (upper 32 bits is TZ offset)
|
|
// TZ offset indeed is in the upper 32 bits, but
|
|
// 1. UNICOS doesn't actually read it
|
|
// 2. Windows doesn't seem to have a tm_gmtoff field
|
|
//Packet[5] = (CInt_t(Now->tm_gmtoff) << 32) | (CInt_t(Now->tm_sec)); // Second (upper 32 bits is TZ offset)
|
|
mIToCChannel->QueuePacket(Packet);
|
|
if (mMasterCluster) {
|
|
mState = States_e::WaitTimeSent;
|
|
} else {
|
|
mState = States_e::WaitTimeSent;
|
|
}
|
|
} else {
|
|
mState = States_e::RunningJ90;
|
|
}
|
|
break;
|
|
case States_e::WaitTimeSent:
|
|
if (mIToCChannel->ReadyToSend()) {
|
|
mLogger << setloglevel(LogLevel_IoTrace) << "###################################################################################" << std::endl;
|
|
mLogger << setloglevel(LogLevel_IoTrace) << "###################################################################################" << std::endl;
|
|
mLogger << setloglevel(LogLevel_IoTrace) << "sending in States_e::WaitTimeSent" << std::endl;
|
|
mLogger << setloglevel(LogLevel_IoTrace) << "###################################################################################" << std::endl;
|
|
mLogger << setloglevel(LogLevel_IoTrace) << "###################################################################################" << std::endl;
|
|
std::vector<CInt_t> Packet(6);
|
|
Packet[0] = 0x4331004900000000; // - C1.I....
|
|
Packet[1] = 0x0000000000000000; // - ........
|
|
Packet[2] = 0x0000000000000000; // - ........
|
|
Packet[3] = 0x0000000000000000; // - ........
|
|
Packet[4] = 0x0000000000000000; // - ........
|
|
Packet[5] = 0x0000000000000000; // - ........
|
|
mIToCChannel->QueuePacket(Packet);
|
|
mState = States_e::WaitISent;
|
|
}
|
|
break;
|
|
case States_e::WaitISent:
|
|
if (mCToIChannel->HasPacket()) {
|
|
mLogger << setloglevel(LogLevel_IoTrace) << "###################################################################################" << std::endl;
|
|
mLogger << setloglevel(LogLevel_IoTrace) << "###################################################################################" << std::endl;
|
|
mLogger << setloglevel(LogLevel_IoTrace) << "receiving in States_e::WaitISent" << std::endl;
|
|
mLogger << setloglevel(LogLevel_IoTrace) << "###################################################################################" << std::endl;
|
|
mLogger << setloglevel(LogLevel_IoTrace) << "###################################################################################" << std::endl;
|
|
std::vector<CInt_t> Packet = mCToIChannel->GetPacket();
|
|
CRAY_ASSERT(Packet.size() == 6);
|
|
CRAY_ASSERT(Packet[0] == 0x0049433100000000); // - .IC1....
|
|
CRAY_ASSERT(Packet[1] == 0x0000000000000000); // - ........
|
|
CRAY_ASSERT(Packet[2] == 0x0000000000000000); // - ........
|
|
CRAY_ASSERT(Packet[3] == 0x0000000000000000); // - ........
|
|
CRAY_ASSERT(Packet[4] == 0x0000000000000000); // - ........
|
|
CRAY_ASSERT(Packet[5] == 0x0000000000000000); // - ........
|
|
|
|
Packet[0] = 0x4331004AFFFFFFFF; // - C1.J....
|
|
Packet[1] = 0xFFFFFFFFFFFFFFFF; // - ........
|
|
Packet[2] = 0xFFFFFFFFFFFFFFFF; // - ........
|
|
Packet[3] = 0xFFFFFFFFFFFFFFFF; // - ........
|
|
Packet[4] = 0xFFFFFFFFFFFFFFFF; // - ........
|
|
Packet[5] = 0xFFFFFFFFFFFFFFFF; // - ........
|
|
mIToCChannel->QueuePacket(Packet);
|
|
|
|
mState = States_e::WaitJSent;
|
|
}
|
|
break;
|
|
case States_e::WaitJSent:
|
|
if (mCToIChannel->HasPacket()) {
|
|
std::vector<CInt_t> Packet = mCToIChannel->GetPacket();
|
|
CRAY_ASSERT(Packet.size() == 6);
|
|
CRAY_ASSERT(Packet[0] == 0x004A4331FFFFFFFF); // - .JC1....
|
|
CRAY_ASSERT(Packet[1] == 0xFFFFFFFFFFFFFFFF); // - ........
|
|
CRAY_ASSERT(Packet[2] == 0xFFFFFFFFFFFFFFFF); // - ........
|
|
CRAY_ASSERT(Packet[3] == 0xFFFFFFFFFFFFFFFF); // - ........
|
|
CRAY_ASSERT(Packet[4] == 0xFFFFFFFFFFFFFFFF); // - ........
|
|
CRAY_ASSERT(Packet[5] == 0xFFFFFFFFFFFFFFFF); // - ........
|
|
// Send an error-report (C) packet.
|
|
Packet[0] = 0x4331004300000004; // - C1.C....
|
|
Packet[1] = 0x0000000000000000; // - ........
|
|
Packet[2] = 0x0000000000000000; // - ........
|
|
Packet[3] = 0x0000000000000000; // - ........
|
|
Packet[4] = 0x0000000000000000; // - ........
|
|
Packet[5] = 0x0000000000000000; // - ........
|
|
mIToCChannel->QueuePacket(Packet);
|
|
|
|
mState = States_e::RunningYEL;
|
|
}
|
|
break;
|
|
case States_e::RunningYEL:
|
|
if (mCToIChannel->HasPacket()) {
|
|
// TODO: do incoming packet processing
|
|
std::vector<CInt_t> Packet = mCToIChannel->GetPacket();
|
|
CRAY_ASSERT(Packet.size() == 6);
|
|
char PacketType = char(GetBitsReverse(Packet[0], 8, 15));
|
|
switch (PacketType) {
|
|
case 'T': // terminal packet
|
|
HandleTPacket(Packet);
|
|
break;
|
|
case 'A':
|
|
HandleAPacket(Packet);
|
|
break;
|
|
case 'K':
|
|
HandleKPacket(Packet);
|
|
break;
|
|
case 'M': { // Ethernet packet
|
|
bool Found = false;
|
|
for (auto &EthInterface : mEthInterfaces) {
|
|
if (EthInterface->HandleIosDPacket(Packet)) {
|
|
Found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!Found && !mEthInterfaces.empty()) mEthInterfaces.front()->SendErrorResponse(Packet, SimIopEth_n::Status_e::MPST_ILCHN);
|
|
break;
|
|
}
|
|
case 'F': { // IOP local peripheral packet
|
|
mIopPeripheral->HandleIosDPacket(Packet);
|
|
break;
|
|
}
|
|
default:
|
|
CRAY_ASSERT(false);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case States_e::RunningJ90:
|
|
if (mCToIChannel->HasPacket()) {
|
|
// We have to break the buffer up to multiple messages...
|
|
std::vector<CInt_t> RawPacket = mCToIChannel->GetPacket();
|
|
std::vector<CInt_t>::const_iterator PacketBegin = RawPacket.begin();
|
|
std::vector<CInt_t>::const_iterator PacketEnd = std::find(PacketBegin + 1, RawPacket.cend(), *PacketBegin);
|
|
while (PacketEnd != RawPacket.cend()) {
|
|
IopEPacket_c Packet(PacketBegin,PacketEnd+1);
|
|
CRAY_ASSERT(Packet.IsValid());
|
|
mSeqReceived = Packet.GetSeq();
|
|
mPacketsReceived++;
|
|
if (Packet.GetFlags() & IopEPacket_c::cFlagIni) mSeqSent = 1;
|
|
if (Packet.GetLength() > 1) {
|
|
char PacketType = char(GetBits(Packet.Data(0), 56, 63));
|
|
switch (PacketType) {
|
|
case 'O': // OWS packet
|
|
HandleIosEOPacket(Packet);
|
|
break;
|
|
case 'Z': // Terminal packet
|
|
HandleIosEZPacket(Packet);
|
|
break;
|
|
case 'D': // Disk packet
|
|
HandleIosEDPacket(Packet);
|
|
break;
|
|
case 'P': // ATM packet
|
|
HandleIosEPPacket(Packet);
|
|
break;
|
|
case 'M': { // Ethernet packet
|
|
bool Found = false;
|
|
for (auto &EthInterface : mEthInterfaces) {
|
|
if (EthInterface->HandleIosEPacket(Packet)) {
|
|
Found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!Found && !mEthInterfaces.empty()) mEthInterfaces.front()->SendErrorResponse(Packet, SimIopEth_n::Status_e::MPST_ILCHN);
|
|
break;
|
|
}
|
|
case 'F': { // Expander packet (????) It seems that it is an IOS-D-style F packet embedded in an IOS-E packet. However the IOS-E style 'f' packet is FTDI. For now, going with an F packet
|
|
if (mIopPeripheral == nullptr) throw Generic_x() << "Can't handle shell driver (F) packet without Peripheral section in IopCluster configuration";
|
|
mIopPeripheral->HandleIosEPacket(Packet);
|
|
break;
|
|
}
|
|
default:
|
|
CRAY_ASSERT(false);
|
|
break;
|
|
}
|
|
}
|
|
PacketBegin = PacketEnd + 1;
|
|
if (PacketBegin == RawPacket.cend()) break;
|
|
PacketEnd = std::find(PacketBegin + 1, RawPacket.cend(), *PacketBegin);
|
|
}
|
|
}
|
|
break;
|
|
case States_e::KernelPanic:
|
|
if (mCToIChannel->HasPacket()) {
|
|
if (mConsoles.size() > 0) {
|
|
SimIopConsole_c &Console = *mConsoles[0];
|
|
std::vector<CInt_t> Packet = mCToIChannel->GetPacket();
|
|
size_t ByteCnt = Packet.size() * sizeof(CInt_t);
|
|
// Simply dump all incoming data to the first console...
|
|
Console.SendString("\r\n");
|
|
bool NewLinePrinted = false;
|
|
for (size_t i = 0; i < ByteCnt; ++i) {
|
|
char Char = ((char*)(&Packet[0]))[(i / 8 * 8) + (7 - (i % 8))];
|
|
switch (Char) {
|
|
case 0:
|
|
if (!NewLinePrinted) {
|
|
Console.SendString("\r\n");
|
|
NewLinePrinted = true;
|
|
}
|
|
break;
|
|
case 0x0a:
|
|
case 0x0d:
|
|
Console.SendString("\r\n");
|
|
break;
|
|
default:
|
|
Console.SendChar(Char);
|
|
NewLinePrinted = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
CRAY_ASSERT(false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// SimIopCluster_c::ChannelIToC_c
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
SimIopCluster_c::ChannelIToC_c::ChannelIToC_c(const Configuration_c &aConfig, size_t aChannelId, Mainframe_c &aMainframe, SimIopCluster_c &aParent):
|
|
Channel_i(aChannelId, aMainframe),
|
|
mLogger(aConfig, "I->C_", aChannelId),
|
|
mParent(aParent),
|
|
mPacketValid(false)
|
|
{
|
|
mLogger.SetParent(aParent.GetLogger());
|
|
}
|
|
|
|
void SimIopCluster_c::ChannelIToC_c::ChannelTick() {
|
|
if (!mPacketValid) {
|
|
if (mPacketQueue.empty()) return;
|
|
SendPacket(mPacketQueue.front());
|
|
mPacketQueue.pop();
|
|
return;
|
|
}
|
|
|
|
if (IsActive()) {
|
|
while (IsActive()) {
|
|
CInt_t Data = mPacket[mPacketIdx++];
|
|
SetData(Data, mPacketIdx == mPacket.size());
|
|
if (mPacketIdx == mPacket.size()) {
|
|
mPacketValid = false;
|
|
return;
|
|
}
|
|
}
|
|
// mLogger << setloglevel(LogLevel_Error) << "Packet is too large for buffer of Start:" << HexPrinter(mStart) << " Limit: " << HexPrinter(mLimit) << " Size: " << DecPrinter(mLimit - mStart) << std::endl;
|
|
mLogger << setloglevel(LogLevel_Error) << "Packet is too large for buffer" << std::endl;
|
|
mLogger << setloglevel(LogLevel_Error) << "Packet size:" << mPacket.size() << " words" << std::endl;
|
|
for (size_t Idx = 0; Idx < mPacket.size(); ++Idx) {
|
|
mLogger << setloglevel(LogLevel_Error) << HexPrinter(Idx, 4) << " : " << HexPrinter(mPacket[Idx]) << " - " << AsciiDumpPrinter(mPacket[Idx]) << std::endl;
|
|
}
|
|
CRAY_ASSERT(false);
|
|
//Disconnect();
|
|
}
|
|
}
|
|
|
|
bool SimIopCluster_c::ChannelIToC_c::ReadyToSend() const {
|
|
return !mPacketValid;
|
|
}
|
|
|
|
void SimIopCluster_c::ChannelIToC_c::SendPacket(const std::vector<CInt_t> &aPacket, bool aReplace) {
|
|
CRAY_ASSERT(!mPacketValid || aReplace);
|
|
mPacket = aPacket;
|
|
mPacketIdx = 0;
|
|
mPacketValid = true;
|
|
mLogger << setloglevel(LogLevel_IoTrace) << "Priming packet to mainframe of " << mPacket.size() << " words" << std::endl;
|
|
}
|
|
|
|
void SimIopCluster_c::ChannelIToC_c::QueuePacket(const std::vector<CInt_t> &aPacket) {
|
|
++mParent.mPacketsSent;
|
|
if (!mPacketValid) {
|
|
SendPacket(aPacket);
|
|
}
|
|
else {
|
|
mPacketQueue.push(aPacket);
|
|
}
|
|
}
|
|
|
|
// SimIopCluster_c::ChannelCToI_c
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
SimIopCluster_c::ChannelCToI_c::ChannelCToI_c(const Configuration_c &aConfig, size_t aChannelId, Mainframe_c &aMainframe, SimIopCluster_c &aParent) :
|
|
Channel_i(aChannelId, aMainframe),
|
|
mLogger(aConfig, "C->I_", aChannelId),
|
|
mParent(aParent),
|
|
mPacketValid(false),
|
|
mPacketIdx(0)
|
|
{
|
|
mLogger.SetParent(aParent.GetLogger());
|
|
}
|
|
|
|
void SimIopCluster_c::ChannelCToI_c::ChannelTick() {
|
|
while (IsActive() && !mPacketValid) {
|
|
CInt_t Data = GetData();
|
|
mPacket.emplace_back(Data);
|
|
if (!IsActive()) { // Channel just got done with transmission due to this word --> mark the packet valid
|
|
mPacketValid = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool SimIopCluster_c::ChannelCToI_c::HasPacket() {
|
|
return mPacketValid;
|
|
}
|
|
|
|
std::vector<CInt_t> SimIopCluster_c::ChannelCToI_c::GetPacket() {
|
|
CRAY_ASSERT(mPacketValid);
|
|
mPacketValid = false;
|
|
std::vector<CInt_t> RetVal(std::move(mPacket));
|
|
mPacket.clear();
|
|
return RetVal;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Console handling
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void SimIopCluster_c::MakeConsoles(size_t aMaxConsoleId) {
|
|
while (mConsoles.size() <= aMaxConsoleId) {
|
|
if (mIopRevision == IopRevision_e::IOSE) {
|
|
std::unique_ptr<SimIopConsole_c> Console = std::make_unique<SimIopEConsole_c>(
|
|
mDefaultConsoleConfig,
|
|
mConsoles.size(),
|
|
*this,
|
|
uint8_t(mConsoles.size()),
|
|
mMainframe->DoDisableAutoTerminal()
|
|
);
|
|
mConsoles.push_back(std::move(Console));
|
|
}
|
|
else {
|
|
std::unique_ptr<SimIopConsole_c> Console = std::make_unique<SimIopDConsole_c>(
|
|
mDefaultConsoleConfig,
|
|
mConsoles.size(),
|
|
*this,
|
|
uint8_t(mConsoles.size()),
|
|
mMainframe->DoDisableAutoTerminal()
|
|
);
|
|
mConsoles.push_back(std::move(Console));
|
|
}
|
|
}
|
|
}
|
|
|
|
void SimIopCluster_c::HandleIosEZPacket(IopEPacket_c &aPacket) {
|
|
using namespace IosEZPacket_n;
|
|
|
|
uint8_t Dev = uint8_t(GetBits(aPacket.Data(0), DevRange));
|
|
|
|
MakeConsoles(Dev);
|
|
SimIopConsole_c &Console = *mConsoles[Dev];
|
|
Console.HandlePacket(aPacket.GetRawData());
|
|
}
|
|
|
|
void SimIopCluster_c::HandleTPacket(std::vector<CInt_t> &aPacket) {
|
|
using namespace IosTPacket_n;
|
|
|
|
uint8_t IopNumber = uint8_t(GetBits(aPacket[0], IopNumberRange));
|
|
uint8_t TTyNumber = uint8_t(GetBits(aPacket[0], TTyNumberRange));
|
|
|
|
CRAY_ASSERT(IopNumber == mIopId);
|
|
|
|
MakeConsoles(TTyNumber);
|
|
SimIopConsole_c &Console = *mConsoles[TTyNumber];
|
|
Console.HandlePacket(aPacket);
|
|
}
|
|
|
|
|
|
|
|
#if defined(PARTIAL_DEBUG) && defined(_MSC_VER)
|
|
#pragma optimize ("", on)
|
|
#endif
|