19e801a2bSVernon Mauery #include "sol_context.hpp" 29e801a2bSVernon Mauery 3fbcac2e7STom Joseph #include "main.hpp" 42085ae07SVernon Mauery #include "message_handler.hpp" 5fbcac2e7STom Joseph #include "sd_event_loop.hpp" 62085ae07SVernon Mauery #include "sessions_manager.hpp" 7fbcac2e7STom Joseph #include "sol_manager.hpp" 8fbcac2e7STom Joseph 97b7f25f7SGeorge Liu #include <errno.h> 107b7f25f7SGeorge Liu 117b7f25f7SGeorge Liu #include <phosphor-logging/lg2.hpp> 129e801a2bSVernon Mauery 13fbcac2e7STom Joseph namespace sol 14fbcac2e7STom Joseph { 15fbcac2e7STom Joseph using namespace phosphor::logging; 16fbcac2e7STom Joseph 176f353e86SVernon Mauery Context::Context(std::shared_ptr<boost::asio::io_context> io, 186f353e86SVernon Mauery uint8_t maxRetryCount, uint8_t sendThreshold, uint8_t instance, 196f353e86SVernon Mauery session::SessionID sessionID) : 206f353e86SVernon Mauery accumulateTimer(*io), 216f353e86SVernon Mauery retryTimer(*io), maxRetryCount(maxRetryCount), retryCounter(maxRetryCount), 226f353e86SVernon Mauery sendThreshold(sendThreshold), payloadInstance(instance), 236f353e86SVernon Mauery sessionID(sessionID) 246f353e86SVernon Mauery { 252085ae07SVernon Mauery session = session::Manager::get().getSession(sessionID); 26a6ad5e16SVernon Mauery } 27a6ad5e16SVernon Mauery 28a6ad5e16SVernon Mauery std::shared_ptr<Context> 29a6ad5e16SVernon Mauery Context::makeContext(std::shared_ptr<boost::asio::io_context> io, 30a6ad5e16SVernon Mauery uint8_t maxRetryCount, uint8_t sendThreshold, 31a6ad5e16SVernon Mauery uint8_t instance, session::SessionID sessionID) 32a6ad5e16SVernon Mauery { 33a6ad5e16SVernon Mauery auto ctx = std::make_shared<Context>(io, maxRetryCount, sendThreshold, 34a6ad5e16SVernon Mauery instance, sessionID); 35a6ad5e16SVernon Mauery ctx->enableAccumulateTimer(true); 36a6ad5e16SVernon Mauery return ctx; 376f353e86SVernon Mauery } 386f353e86SVernon Mauery 396f353e86SVernon Mauery void Context::enableAccumulateTimer(bool enable) 406f353e86SVernon Mauery { 416f353e86SVernon Mauery // fetch the timeout from the SOL manager 422085ae07SVernon Mauery std::chrono::microseconds interval = sol::Manager::get().accumulateInterval; 436f353e86SVernon Mauery if (enable) 446f353e86SVernon Mauery { 456f353e86SVernon Mauery accumulateTimer.expires_after(interval); 46a6ad5e16SVernon Mauery std::weak_ptr<Context> weakRef = weak_from_this(); 47a6ad5e16SVernon Mauery accumulateTimer.async_wait( 48a6ad5e16SVernon Mauery [weakRef](const boost::system::error_code& ec) { 49a6ad5e16SVernon Mauery std::shared_ptr<Context> self = weakRef.lock(); 50a6ad5e16SVernon Mauery if (!ec && self) 516f353e86SVernon Mauery { 52a6ad5e16SVernon Mauery self->charAccTimerHandler(); 536f353e86SVernon Mauery } 546f353e86SVernon Mauery }); 556f353e86SVernon Mauery } 566f353e86SVernon Mauery else 576f353e86SVernon Mauery { 586f353e86SVernon Mauery accumulateTimer.cancel(); 596f353e86SVernon Mauery } 606f353e86SVernon Mauery } 616f353e86SVernon Mauery 626f353e86SVernon Mauery void Context::enableRetryTimer(bool enable) 636f353e86SVernon Mauery { 646f353e86SVernon Mauery if (enable) 656f353e86SVernon Mauery { 666f353e86SVernon Mauery // fetch the timeout from the SOL manager 672085ae07SVernon Mauery std::chrono::microseconds interval = sol::Manager::get().retryInterval; 686f353e86SVernon Mauery retryTimer.expires_after(interval); 69a6ad5e16SVernon Mauery std::weak_ptr<Context> weakRef = weak_from_this(); 70a6ad5e16SVernon Mauery retryTimer.async_wait([weakRef](const boost::system::error_code& ec) { 71a6ad5e16SVernon Mauery std::shared_ptr<Context> self = weakRef.lock(); 72a6ad5e16SVernon Mauery if (!ec && self) 736f353e86SVernon Mauery { 74a6ad5e16SVernon Mauery self->retryTimerHandler(); 756f353e86SVernon Mauery } 766f353e86SVernon Mauery }); 776f353e86SVernon Mauery } 786f353e86SVernon Mauery else 796f353e86SVernon Mauery { 806f353e86SVernon Mauery retryTimer.cancel(); 816f353e86SVernon Mauery } 826f353e86SVernon Mauery } 836f353e86SVernon Mauery 849e801a2bSVernon Mauery void Context::processInboundPayload(uint8_t seqNum, uint8_t ackSeqNum, 85*ec437414STingting Chen uint8_t count, bool status, bool isBreak, 8670fd29cfSVernon Mauery const std::vector<uint8_t>& input) 87fbcac2e7STom Joseph { 88fbcac2e7STom Joseph uint8_t respAckSeqNum = 0; 89fbcac2e7STom Joseph uint8_t acceptedCount = 0; 90fbcac2e7STom Joseph auto ack = false; 91fbcac2e7STom Joseph 92fbcac2e7STom Joseph /* 93fbcac2e7STom Joseph * Check if the Inbound sequence number is same as the expected one. 94fbcac2e7STom Joseph * If the Packet Sequence Number is 0, it is an ACK-Only packet. Multiple 95fbcac2e7STom Joseph * outstanding sequence numbers are not supported in this version of the SOL 96fbcac2e7STom Joseph * specification. Retried packets use the same sequence number as the first 97fbcac2e7STom Joseph * packet. 98fbcac2e7STom Joseph */ 99fbcac2e7STom Joseph if (seqNum && (seqNum != seqNums.get(true))) 100fbcac2e7STom Joseph { 1017b7f25f7SGeorge Liu lg2::info("Out of sequence SOL packet - packet is dropped"); 102fbcac2e7STom Joseph return; 103fbcac2e7STom Joseph } 104fbcac2e7STom Joseph 105fbcac2e7STom Joseph /* 106fbcac2e7STom Joseph * Check if the expected ACK/NACK sequence number is same as the 107fbcac2e7STom Joseph * ACK/NACK sequence number in the packet. If packet ACK/NACK sequence 108fbcac2e7STom Joseph * number is 0, then it is an informational packet. No request packet being 109fbcac2e7STom Joseph * ACK'd or NACK'd. 110fbcac2e7STom Joseph */ 111fbcac2e7STom Joseph if (ackSeqNum && (ackSeqNum != seqNums.get(false))) 112fbcac2e7STom Joseph { 1137b7f25f7SGeorge Liu lg2::info("Out of sequence ack number - SOL packet is dropped"); 114fbcac2e7STom Joseph return; 115fbcac2e7STom Joseph } 116fbcac2e7STom Joseph 117fbcac2e7STom Joseph /* 118fbcac2e7STom Joseph * Retry the SOL payload packet in the following conditions: 119fbcac2e7STom Joseph * 120fbcac2e7STom Joseph * a) NACK in Operation/Status 121fbcac2e7STom Joseph * b) Accepted Character Count does not match with the sent out SOL payload 122fbcac2e7STom Joseph * c) Non-zero Packet ACK/NACK Sequence Number 123fbcac2e7STom Joseph */ 124fbcac2e7STom Joseph if (status || ((count != expectedCharCount) && ackSeqNum)) 125fbcac2e7STom Joseph { 126fbcac2e7STom Joseph resendPayload(noClear); 1276f353e86SVernon Mauery enableRetryTimer(false); 1286f353e86SVernon Mauery enableRetryTimer(true); 129fbcac2e7STom Joseph return; 130fbcac2e7STom Joseph } 131fbcac2e7STom Joseph /* 132fbcac2e7STom Joseph * Clear the sent data once the acknowledgment sequence number matches 133fbcac2e7STom Joseph * and the expected character count matches. 134fbcac2e7STom Joseph */ 135fbcac2e7STom Joseph else if ((count == expectedCharCount) && ackSeqNum) 136fbcac2e7STom Joseph { 137fbcac2e7STom Joseph // Clear the Host Console Buffer 1382085ae07SVernon Mauery sol::Manager::get().dataBuffer.erase(count); 139fbcac2e7STom Joseph 140fbcac2e7STom Joseph // Once it is acknowledged stop the retry interval timer 1416f353e86SVernon Mauery enableRetryTimer(false); 142fbcac2e7STom Joseph 143fbcac2e7STom Joseph retryCounter = maxRetryCount; 144fbcac2e7STom Joseph expectedCharCount = 0; 145fbcac2e7STom Joseph payloadCache.clear(); 146fbcac2e7STom Joseph } 147fbcac2e7STom Joseph 148*ec437414STingting Chen if (isBreak && seqNum) 149*ec437414STingting Chen { 150*ec437414STingting Chen lg2::info("Writing break to console socket descriptor"); 151*ec437414STingting Chen constexpr uint8_t sysrqValue = 72; // use this to notify sol server 152*ec437414STingting Chen const std::vector<uint8_t> test{sysrqValue}; 153*ec437414STingting Chen auto ret = sol::Manager::get().writeConsoleSocket(test, isBreak); 154*ec437414STingting Chen if (ret) 155*ec437414STingting Chen { 156*ec437414STingting Chen lg2::error("Writing to console socket descriptor failed: {ERROR}", 157*ec437414STingting Chen "ERROR", strerror(errno)); 158*ec437414STingting Chen } 159*ec437414STingting Chen } 160*ec437414STingting Chen 161*ec437414STingting Chen isBreak = false; 162fbcac2e7STom Joseph // Write character data to the Host Console 163fbcac2e7STom Joseph if (!input.empty() && seqNum) 164fbcac2e7STom Joseph { 165*ec437414STingting Chen auto rc = sol::Manager::get().writeConsoleSocket(input, isBreak); 166fbcac2e7STom Joseph if (rc) 167fbcac2e7STom Joseph { 1687b7f25f7SGeorge Liu lg2::error("Writing to console socket descriptor failed: {ERROR}", 1697b7f25f7SGeorge Liu "ERROR", strerror(errno)); 170fbcac2e7STom Joseph ack = true; 171fbcac2e7STom Joseph } 172fbcac2e7STom Joseph else 173fbcac2e7STom Joseph { 174fbcac2e7STom Joseph respAckSeqNum = seqNum; 175fbcac2e7STom Joseph ack = false; 176fbcac2e7STom Joseph acceptedCount = input.size(); 177fbcac2e7STom Joseph } 178fbcac2e7STom Joseph } 179694fc0cfSTom Joseph /* 180694fc0cfSTom Joseph * SOL payload with no character data and valid sequence number can be used 181694fc0cfSTom Joseph * as method to keep the SOL session active. 182694fc0cfSTom Joseph */ 183694fc0cfSTom Joseph else if (input.empty() && seqNum) 184694fc0cfSTom Joseph { 185694fc0cfSTom Joseph respAckSeqNum = seqNum; 186694fc0cfSTom Joseph } 187fbcac2e7STom Joseph 188fbcac2e7STom Joseph if (seqNum != 0) 189fbcac2e7STom Joseph { 190fbcac2e7STom Joseph seqNums.incInboundSeqNum(); 191fbcac2e7STom Joseph prepareResponse(respAckSeqNum, acceptedCount, ack); 192fbcac2e7STom Joseph } 193fbcac2e7STom Joseph else 194fbcac2e7STom Joseph { 1956f353e86SVernon Mauery enableAccumulateTimer(true); 196fbcac2e7STom Joseph } 197fbcac2e7STom Joseph } 198fbcac2e7STom Joseph 19975e15db4STom Joseph void Context::prepareResponse(uint8_t ackSeqNum, uint8_t count, bool ack) 20075e15db4STom Joseph { 2012085ae07SVernon Mauery auto bufferSize = sol::Manager::get().dataBuffer.size(); 20275e15db4STom Joseph 20375e15db4STom Joseph /* Sent a ACK only response */ 20475e15db4STom Joseph if (payloadCache.size() != 0 || (bufferSize < sendThreshold)) 20575e15db4STom Joseph { 2066f353e86SVernon Mauery enableAccumulateTimer(true); 20775e15db4STom Joseph 20870fd29cfSVernon Mauery std::vector<uint8_t> outPayload(sizeof(Payload)); 20975e15db4STom Joseph auto response = reinterpret_cast<Payload*>(outPayload.data()); 21075e15db4STom Joseph response->packetSeqNum = 0; 21175e15db4STom Joseph response->packetAckSeqNum = ackSeqNum; 21275e15db4STom Joseph response->acceptedCharCount = count; 21375e15db4STom Joseph response->outOperation.ack = ack; 21475e15db4STom Joseph sendPayload(outPayload); 21575e15db4STom Joseph return; 21675e15db4STom Joseph } 21775e15db4STom Joseph 21875e15db4STom Joseph auto readSize = std::min(bufferSize, MAX_PAYLOAD_SIZE); 21975e15db4STom Joseph payloadCache.resize(sizeof(Payload) + readSize); 22075e15db4STom Joseph auto response = reinterpret_cast<Payload*>(payloadCache.data()); 22175e15db4STom Joseph response->packetAckSeqNum = ackSeqNum; 22275e15db4STom Joseph response->acceptedCharCount = count; 22375e15db4STom Joseph response->outOperation.ack = ack; 22475e15db4STom Joseph response->packetSeqNum = seqNums.incOutboundSeqNum(); 22575e15db4STom Joseph 2262085ae07SVernon Mauery auto handle = sol::Manager::get().dataBuffer.read(); 22775e15db4STom Joseph std::copy_n(handle, readSize, payloadCache.data() + sizeof(Payload)); 22875e15db4STom Joseph expectedCharCount = readSize; 22975e15db4STom Joseph 2306f353e86SVernon Mauery enableRetryTimer(true); 2316f353e86SVernon Mauery enableAccumulateTimer(false); 23275e15db4STom Joseph 23375e15db4STom Joseph sendPayload(payloadCache); 23475e15db4STom Joseph } 23575e15db4STom Joseph 2367306314fSTom Joseph int Context::sendOutboundPayload() 2377306314fSTom Joseph { 2387306314fSTom Joseph if (payloadCache.size() != 0) 2397306314fSTom Joseph { 2406f353e86SVernon Mauery enableAccumulateTimer(true); 2417306314fSTom Joseph return -1; 2427306314fSTom Joseph } 2437306314fSTom Joseph 2442085ae07SVernon Mauery auto bufferSize = sol::Manager::get().dataBuffer.size(); 2457306314fSTom Joseph auto readSize = std::min(bufferSize, MAX_PAYLOAD_SIZE); 2467306314fSTom Joseph 2477306314fSTom Joseph payloadCache.resize(sizeof(Payload) + readSize); 2487306314fSTom Joseph auto response = reinterpret_cast<Payload*>(payloadCache.data()); 2497306314fSTom Joseph response->packetAckSeqNum = 0; 2507306314fSTom Joseph response->acceptedCharCount = 0; 2517306314fSTom Joseph response->outOperation.ack = false; 2527306314fSTom Joseph response->packetSeqNum = seqNums.incOutboundSeqNum(); 2537306314fSTom Joseph 2542085ae07SVernon Mauery auto handle = sol::Manager::get().dataBuffer.read(); 2557306314fSTom Joseph std::copy_n(handle, readSize, payloadCache.data() + sizeof(Payload)); 2567306314fSTom Joseph expectedCharCount = readSize; 2577306314fSTom Joseph 2586f353e86SVernon Mauery enableRetryTimer(true); 2596f353e86SVernon Mauery enableAccumulateTimer(false); 2607306314fSTom Joseph 2617306314fSTom Joseph sendPayload(payloadCache); 2627306314fSTom Joseph 2637306314fSTom Joseph return 0; 2647306314fSTom Joseph } 2657306314fSTom Joseph 26675e15db4STom Joseph void Context::resendPayload(bool clear) 267fbcac2e7STom Joseph { 2682fd466f4STom Joseph sendPayload(payloadCache); 269fbcac2e7STom Joseph 2702fd466f4STom Joseph if (clear) 2712fd466f4STom Joseph { 2722fd466f4STom Joseph payloadCache.clear(); 2732fd466f4STom Joseph expectedCharCount = 0; 2742085ae07SVernon Mauery sol::Manager::get().dataBuffer.erase(expectedCharCount); 2752fd466f4STom Joseph } 276fbcac2e7STom Joseph } 277fbcac2e7STom Joseph 27870fd29cfSVernon Mauery void Context::sendPayload(const std::vector<uint8_t>& out) const 279fbcac2e7STom Joseph { 28022596f21STom Joseph message::Handler msgHandler(session->channelPtr, sessionID); 28122596f21STom Joseph 28222596f21STom Joseph msgHandler.sendSOLPayload(out); 283fbcac2e7STom Joseph } 284fbcac2e7STom Joseph 2856f353e86SVernon Mauery void Context::charAccTimerHandler() 2866f353e86SVernon Mauery { 2872085ae07SVernon Mauery auto bufferSize = sol::Manager::get().dataBuffer.size(); 2886f353e86SVernon Mauery 2896f353e86SVernon Mauery try 2906f353e86SVernon Mauery { 2916f353e86SVernon Mauery if (bufferSize > 0) 2926f353e86SVernon Mauery { 2936f353e86SVernon Mauery int rc = sendOutboundPayload(); 2946f353e86SVernon Mauery if (rc == 0) 2956f353e86SVernon Mauery { 2966f353e86SVernon Mauery return; 2976f353e86SVernon Mauery } 2986f353e86SVernon Mauery } 2996f353e86SVernon Mauery enableAccumulateTimer(true); 3006f353e86SVernon Mauery } 30112d199b2SPatrick Williams catch (const std::exception& e) 3026f353e86SVernon Mauery { 3037b7f25f7SGeorge Liu lg2::error("Failed to call the sendOutboundPayload method: {ERROR}", 3047b7f25f7SGeorge Liu "ERROR", e); 3056f353e86SVernon Mauery } 3066f353e86SVernon Mauery } 3076f353e86SVernon Mauery 3086f353e86SVernon Mauery void Context::retryTimerHandler() 3096f353e86SVernon Mauery { 3106f353e86SVernon Mauery try 3116f353e86SVernon Mauery { 3126f353e86SVernon Mauery if (retryCounter) 3136f353e86SVernon Mauery { 3146f353e86SVernon Mauery --retryCounter; 3156f353e86SVernon Mauery enableRetryTimer(true); 3166f353e86SVernon Mauery resendPayload(sol::Context::noClear); 3176f353e86SVernon Mauery } 3186f353e86SVernon Mauery else 3196f353e86SVernon Mauery { 3206f353e86SVernon Mauery retryCounter = maxRetryCount; 3216f353e86SVernon Mauery resendPayload(sol::Context::clear); 3226f353e86SVernon Mauery enableRetryTimer(false); 3236f353e86SVernon Mauery enableAccumulateTimer(true); 3246f353e86SVernon Mauery } 3256f353e86SVernon Mauery } 32612d199b2SPatrick Williams catch (const std::exception& e) 3276f353e86SVernon Mauery { 3287b7f25f7SGeorge Liu lg2::error("Failed to retry timer: {ERROR}", "ERROR", e); 3296f353e86SVernon Mauery } 3306f353e86SVernon Mauery } 331fbcac2e7STom Joseph } // namespace sol 332