xref: /openbmc/phosphor-net-ipmid/sol/sol_context.cpp (revision 8425624a9046f5a853e8596cc74441e622028494)
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 
Context(std::shared_ptr<boost::asio::io_context> io,uint8_t maxRetryCount,uint8_t sendThreshold,uint8_t instance,session::SessionID sessionID)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) :
20*8425624aSPatrick Williams     accumulateTimer(*io), retryTimer(*io), maxRetryCount(maxRetryCount),
21*8425624aSPatrick Williams     retryCounter(maxRetryCount), sendThreshold(sendThreshold),
22*8425624aSPatrick Williams     payloadInstance(instance), sessionID(sessionID)
236f353e86SVernon Mauery {
242085ae07SVernon Mauery     session = session::Manager::get().getSession(sessionID);
25a6ad5e16SVernon Mauery }
26a6ad5e16SVernon Mauery 
makeContext(std::shared_ptr<boost::asio::io_context> io,uint8_t maxRetryCount,uint8_t sendThreshold,uint8_t instance,session::SessionID sessionID)27*8425624aSPatrick Williams std::shared_ptr<Context> Context::makeContext(
28*8425624aSPatrick Williams     std::shared_ptr<boost::asio::io_context> io, uint8_t maxRetryCount,
29*8425624aSPatrick Williams     uint8_t sendThreshold, uint8_t instance, session::SessionID sessionID)
30a6ad5e16SVernon Mauery {
31a6ad5e16SVernon Mauery     auto ctx = std::make_shared<Context>(io, maxRetryCount, sendThreshold,
32a6ad5e16SVernon Mauery                                          instance, sessionID);
33a6ad5e16SVernon Mauery     ctx->enableAccumulateTimer(true);
34a6ad5e16SVernon Mauery     return ctx;
356f353e86SVernon Mauery }
366f353e86SVernon Mauery 
enableAccumulateTimer(bool enable)376f353e86SVernon Mauery void Context::enableAccumulateTimer(bool enable)
386f353e86SVernon Mauery {
396f353e86SVernon Mauery     // fetch the timeout from the SOL manager
402085ae07SVernon Mauery     std::chrono::microseconds interval = sol::Manager::get().accumulateInterval;
4115535143STang Yiwei 
426f353e86SVernon Mauery     if (enable)
436f353e86SVernon Mauery     {
4415535143STang Yiwei         auto bufferSize = sol::Manager::get().dataBuffer.size();
4515535143STang Yiwei         if (bufferSize > sendThreshold)
4615535143STang Yiwei         {
47426fcab8SKonstantin Aladyshev             try
485499bf86SJian Zhang             {
49426fcab8SKonstantin Aladyshev                 int rc = sendOutboundPayload();
50426fcab8SKonstantin Aladyshev                 if (rc == 0)
51426fcab8SKonstantin Aladyshev                 {
5215535143STang Yiwei                     return;
5315535143STang Yiwei                 }
54426fcab8SKonstantin Aladyshev             }
55426fcab8SKonstantin Aladyshev             catch (const std::exception& e)
56426fcab8SKonstantin Aladyshev             {
57426fcab8SKonstantin Aladyshev                 lg2::error(
58426fcab8SKonstantin Aladyshev                     "Failed to call the sendOutboundPayload method: {ERROR}",
59426fcab8SKonstantin Aladyshev                     "ERROR", e);
60426fcab8SKonstantin Aladyshev                 return;
61426fcab8SKonstantin Aladyshev             }
62426fcab8SKonstantin Aladyshev         }
636f353e86SVernon Mauery         accumulateTimer.expires_after(interval);
64426fcab8SKonstantin Aladyshev         std::weak_ptr<Context> weakRef = weak_from_this();
65a6ad5e16SVernon Mauery         accumulateTimer.async_wait(
66a6ad5e16SVernon Mauery             [weakRef](const boost::system::error_code& ec) {
67a6ad5e16SVernon Mauery                 std::shared_ptr<Context> self = weakRef.lock();
68a6ad5e16SVernon Mauery                 if (!ec && self)
696f353e86SVernon Mauery                 {
70a6ad5e16SVernon Mauery                     self->charAccTimerHandler();
716f353e86SVernon Mauery                 }
726f353e86SVernon Mauery             });
736f353e86SVernon Mauery     }
746f353e86SVernon Mauery     else
756f353e86SVernon Mauery     {
766f353e86SVernon Mauery         accumulateTimer.cancel();
776f353e86SVernon Mauery     }
786f353e86SVernon Mauery }
796f353e86SVernon Mauery 
enableRetryTimer(bool enable)806f353e86SVernon Mauery void Context::enableRetryTimer(bool enable)
816f353e86SVernon Mauery {
826f353e86SVernon Mauery     if (enable)
836f353e86SVernon Mauery     {
846f353e86SVernon Mauery         // fetch the timeout from the SOL manager
852085ae07SVernon Mauery         std::chrono::microseconds interval = sol::Manager::get().retryInterval;
866f353e86SVernon Mauery         retryTimer.expires_after(interval);
87a6ad5e16SVernon Mauery         std::weak_ptr<Context> weakRef = weak_from_this();
88a6ad5e16SVernon Mauery         retryTimer.async_wait([weakRef](const boost::system::error_code& ec) {
89a6ad5e16SVernon Mauery             std::shared_ptr<Context> self = weakRef.lock();
90a6ad5e16SVernon Mauery             if (!ec && self)
916f353e86SVernon Mauery             {
92a6ad5e16SVernon Mauery                 self->retryTimerHandler();
936f353e86SVernon Mauery             }
946f353e86SVernon Mauery         });
956f353e86SVernon Mauery     }
966f353e86SVernon Mauery     else
976f353e86SVernon Mauery     {
986f353e86SVernon Mauery         retryTimer.cancel();
996f353e86SVernon Mauery     }
1006f353e86SVernon Mauery }
1016f353e86SVernon Mauery 
processInboundPayload(uint8_t seqNum,uint8_t ackSeqNum,uint8_t count,bool status,bool isBreak,const std::vector<uint8_t> & input)1029e801a2bSVernon Mauery void Context::processInboundPayload(uint8_t seqNum, uint8_t ackSeqNum,
103ec437414STingting Chen                                     uint8_t count, bool status, bool isBreak,
10470fd29cfSVernon Mauery                                     const std::vector<uint8_t>& input)
105fbcac2e7STom Joseph {
106fbcac2e7STom Joseph     uint8_t respAckSeqNum = 0;
107fbcac2e7STom Joseph     uint8_t acceptedCount = 0;
108fbcac2e7STom Joseph     auto ack = false;
109fbcac2e7STom Joseph 
110fbcac2e7STom Joseph     /*
111fbcac2e7STom Joseph      * Check if the Inbound sequence number is same as the expected one.
112fbcac2e7STom Joseph      * If the Packet Sequence Number is 0, it is an ACK-Only packet. Multiple
113fbcac2e7STom Joseph      * outstanding sequence numbers are not supported in this version of the SOL
114fbcac2e7STom Joseph      * specification. Retried packets use the same sequence number as the first
115fbcac2e7STom Joseph      * packet.
116fbcac2e7STom Joseph      */
117fbcac2e7STom Joseph     if (seqNum && (seqNum != seqNums.get(true)))
118fbcac2e7STom Joseph     {
1197b7f25f7SGeorge Liu         lg2::info("Out of sequence SOL packet - packet is dropped");
120fbcac2e7STom Joseph         return;
121fbcac2e7STom Joseph     }
122fbcac2e7STom Joseph 
123fbcac2e7STom Joseph     /*
124fbcac2e7STom Joseph      * Check if the expected ACK/NACK sequence number is same as the
125fbcac2e7STom Joseph      * ACK/NACK sequence number in the packet. If packet ACK/NACK sequence
126fbcac2e7STom Joseph      * number is 0, then it is an informational packet. No request packet being
127fbcac2e7STom Joseph      * ACK'd or NACK'd.
128fbcac2e7STom Joseph      */
129fbcac2e7STom Joseph     if (ackSeqNum && (ackSeqNum != seqNums.get(false)))
130fbcac2e7STom Joseph     {
1317b7f25f7SGeorge Liu         lg2::info("Out of sequence ack number - SOL packet is dropped");
132fbcac2e7STom Joseph         return;
133fbcac2e7STom Joseph     }
134fbcac2e7STom Joseph 
135fbcac2e7STom Joseph     /*
136fbcac2e7STom Joseph      * Retry the SOL payload packet in the following conditions:
137fbcac2e7STom Joseph      *
138fbcac2e7STom Joseph      * a) NACK in Operation/Status
139fbcac2e7STom Joseph      * b) Accepted Character Count does not match with the sent out SOL payload
140fbcac2e7STom Joseph      * c) Non-zero Packet ACK/NACK Sequence Number
141fbcac2e7STom Joseph      */
142fbcac2e7STom Joseph     if (status || ((count != expectedCharCount) && ackSeqNum))
143fbcac2e7STom Joseph     {
144fbcac2e7STom Joseph         resendPayload(noClear);
1456f353e86SVernon Mauery         enableRetryTimer(false);
1466f353e86SVernon Mauery         enableRetryTimer(true);
147fbcac2e7STom Joseph         return;
148fbcac2e7STom Joseph     }
149fbcac2e7STom Joseph     /*
150fbcac2e7STom Joseph      * Clear the sent data once the acknowledgment sequence number matches
151fbcac2e7STom Joseph      * and the expected character count matches.
152fbcac2e7STom Joseph      */
153fbcac2e7STom Joseph     else if ((count == expectedCharCount) && ackSeqNum)
154fbcac2e7STom Joseph     {
155fbcac2e7STom Joseph         // Clear the Host Console Buffer
1562085ae07SVernon Mauery         sol::Manager::get().dataBuffer.erase(count);
157fbcac2e7STom Joseph 
158fbcac2e7STom Joseph         // Once it is acknowledged stop the retry interval timer
1596f353e86SVernon Mauery         enableRetryTimer(false);
160fbcac2e7STom Joseph 
161fbcac2e7STom Joseph         retryCounter = maxRetryCount;
162fbcac2e7STom Joseph         expectedCharCount = 0;
163fbcac2e7STom Joseph         payloadCache.clear();
164fbcac2e7STom Joseph     }
165fbcac2e7STom Joseph 
166ec437414STingting Chen     if (isBreak && seqNum)
167ec437414STingting Chen     {
168ec437414STingting Chen         lg2::info("Writing break to console socket descriptor");
169ec437414STingting Chen         constexpr uint8_t sysrqValue = 72; // use this to notify sol server
170ec437414STingting Chen         const std::vector<uint8_t> test{sysrqValue};
171ec437414STingting Chen         auto ret = sol::Manager::get().writeConsoleSocket(test, isBreak);
172ec437414STingting Chen         if (ret)
173ec437414STingting Chen         {
174ec437414STingting Chen             lg2::error("Writing to console socket descriptor failed: {ERROR}",
175ec437414STingting Chen                        "ERROR", strerror(errno));
176ec437414STingting Chen         }
177ec437414STingting Chen     }
178ec437414STingting Chen 
179ec437414STingting Chen     isBreak = false;
180fbcac2e7STom Joseph     // Write character data to the Host Console
181fbcac2e7STom Joseph     if (!input.empty() && seqNum)
182fbcac2e7STom Joseph     {
183ec437414STingting Chen         auto rc = sol::Manager::get().writeConsoleSocket(input, isBreak);
184fbcac2e7STom Joseph         if (rc)
185fbcac2e7STom Joseph         {
1867b7f25f7SGeorge Liu             lg2::error("Writing to console socket descriptor failed: {ERROR}",
1877b7f25f7SGeorge Liu                        "ERROR", strerror(errno));
188fbcac2e7STom Joseph             ack = true;
189fbcac2e7STom Joseph         }
190fbcac2e7STom Joseph         else
191fbcac2e7STom Joseph         {
192fbcac2e7STom Joseph             respAckSeqNum = seqNum;
193fbcac2e7STom Joseph             ack = false;
194fbcac2e7STom Joseph             acceptedCount = input.size();
195fbcac2e7STom Joseph         }
196fbcac2e7STom Joseph     }
197694fc0cfSTom Joseph     /*
198694fc0cfSTom Joseph      * SOL payload with no character data and valid sequence number can be used
199694fc0cfSTom Joseph      * as method to keep the SOL session active.
200694fc0cfSTom Joseph      */
201694fc0cfSTom Joseph     else if (input.empty() && seqNum)
202694fc0cfSTom Joseph     {
203694fc0cfSTom Joseph         respAckSeqNum = seqNum;
204694fc0cfSTom Joseph     }
205fbcac2e7STom Joseph 
206fbcac2e7STom Joseph     if (seqNum != 0)
207fbcac2e7STom Joseph     {
208fbcac2e7STom Joseph         seqNums.incInboundSeqNum();
209fbcac2e7STom Joseph         prepareResponse(respAckSeqNum, acceptedCount, ack);
210fbcac2e7STom Joseph     }
211fbcac2e7STom Joseph     else
212fbcac2e7STom Joseph     {
2136f353e86SVernon Mauery         enableAccumulateTimer(true);
214fbcac2e7STom Joseph     }
215fbcac2e7STom Joseph }
216fbcac2e7STom Joseph 
prepareResponse(uint8_t ackSeqNum,uint8_t count,bool ack)21775e15db4STom Joseph void Context::prepareResponse(uint8_t ackSeqNum, uint8_t count, bool ack)
21875e15db4STom Joseph {
2192085ae07SVernon Mauery     auto bufferSize = sol::Manager::get().dataBuffer.size();
22075e15db4STom Joseph 
22175e15db4STom Joseph     /* Sent a ACK only response */
22275e15db4STom Joseph     if (payloadCache.size() != 0 || (bufferSize < sendThreshold))
22375e15db4STom Joseph     {
2246f353e86SVernon Mauery         enableAccumulateTimer(true);
22575e15db4STom Joseph 
22670fd29cfSVernon Mauery         std::vector<uint8_t> outPayload(sizeof(Payload));
22775e15db4STom Joseph         auto response = reinterpret_cast<Payload*>(outPayload.data());
22875e15db4STom Joseph         response->packetSeqNum = 0;
22975e15db4STom Joseph         response->packetAckSeqNum = ackSeqNum;
23075e15db4STom Joseph         response->acceptedCharCount = count;
23175e15db4STom Joseph         response->outOperation.ack = ack;
23275e15db4STom Joseph         sendPayload(outPayload);
23375e15db4STom Joseph         return;
23475e15db4STom Joseph     }
23575e15db4STom Joseph 
23675e15db4STom Joseph     auto readSize = std::min(bufferSize, MAX_PAYLOAD_SIZE);
23775e15db4STom Joseph     payloadCache.resize(sizeof(Payload) + readSize);
23875e15db4STom Joseph     auto response = reinterpret_cast<Payload*>(payloadCache.data());
23975e15db4STom Joseph     response->packetAckSeqNum = ackSeqNum;
24075e15db4STom Joseph     response->acceptedCharCount = count;
24175e15db4STom Joseph     response->outOperation.ack = ack;
24275e15db4STom Joseph     response->packetSeqNum = seqNums.incOutboundSeqNum();
24375e15db4STom Joseph 
2442085ae07SVernon Mauery     auto handle = sol::Manager::get().dataBuffer.read();
24575e15db4STom Joseph     std::copy_n(handle, readSize, payloadCache.data() + sizeof(Payload));
24675e15db4STom Joseph     expectedCharCount = readSize;
24775e15db4STom Joseph 
2486f353e86SVernon Mauery     enableRetryTimer(true);
2496f353e86SVernon Mauery     enableAccumulateTimer(false);
25075e15db4STom Joseph 
25175e15db4STom Joseph     sendPayload(payloadCache);
25275e15db4STom Joseph }
25375e15db4STom Joseph 
sendOutboundPayload()2547306314fSTom Joseph int Context::sendOutboundPayload()
2557306314fSTom Joseph {
2567306314fSTom Joseph     if (payloadCache.size() != 0)
2577306314fSTom Joseph     {
2587306314fSTom Joseph         return -1;
2597306314fSTom Joseph     }
2607306314fSTom Joseph 
2612085ae07SVernon Mauery     auto bufferSize = sol::Manager::get().dataBuffer.size();
2627306314fSTom Joseph     auto readSize = std::min(bufferSize, MAX_PAYLOAD_SIZE);
2637306314fSTom Joseph 
2647306314fSTom Joseph     payloadCache.resize(sizeof(Payload) + readSize);
2657306314fSTom Joseph     auto response = reinterpret_cast<Payload*>(payloadCache.data());
2667306314fSTom Joseph     response->packetAckSeqNum = 0;
2677306314fSTom Joseph     response->acceptedCharCount = 0;
2687306314fSTom Joseph     response->outOperation.ack = false;
2697306314fSTom Joseph     response->packetSeqNum = seqNums.incOutboundSeqNum();
2707306314fSTom Joseph 
2712085ae07SVernon Mauery     auto handle = sol::Manager::get().dataBuffer.read();
2727306314fSTom Joseph     std::copy_n(handle, readSize, payloadCache.data() + sizeof(Payload));
2737306314fSTom Joseph     expectedCharCount = readSize;
2747306314fSTom Joseph 
2756f353e86SVernon Mauery     enableRetryTimer(true);
2766f353e86SVernon Mauery     enableAccumulateTimer(false);
2777306314fSTom Joseph 
2787306314fSTom Joseph     sendPayload(payloadCache);
2797306314fSTom Joseph 
2807306314fSTom Joseph     return 0;
2817306314fSTom Joseph }
2827306314fSTom Joseph 
resendPayload(bool clear)28375e15db4STom Joseph void Context::resendPayload(bool clear)
284fbcac2e7STom Joseph {
2852fd466f4STom Joseph     sendPayload(payloadCache);
286fbcac2e7STom Joseph 
2872fd466f4STom Joseph     if (clear)
2882fd466f4STom Joseph     {
2892fd466f4STom Joseph         payloadCache.clear();
2902fd466f4STom Joseph         expectedCharCount = 0;
2912085ae07SVernon Mauery         sol::Manager::get().dataBuffer.erase(expectedCharCount);
2922fd466f4STom Joseph     }
293fbcac2e7STom Joseph }
294fbcac2e7STom Joseph 
sendPayload(const std::vector<uint8_t> & out) const29570fd29cfSVernon Mauery void Context::sendPayload(const std::vector<uint8_t>& out) const
296fbcac2e7STom Joseph {
29722596f21STom Joseph     message::Handler msgHandler(session->channelPtr, sessionID);
29822596f21STom Joseph 
29922596f21STom Joseph     msgHandler.sendSOLPayload(out);
300fbcac2e7STom Joseph }
301fbcac2e7STom Joseph 
charAccTimerHandler()3026f353e86SVernon Mauery void Context::charAccTimerHandler()
3036f353e86SVernon Mauery {
3042085ae07SVernon Mauery     auto bufferSize = sol::Manager::get().dataBuffer.size();
3056f353e86SVernon Mauery 
3066f353e86SVernon Mauery     try
3076f353e86SVernon Mauery     {
3086f353e86SVernon Mauery         if (bufferSize > 0)
3096f353e86SVernon Mauery         {
3106f353e86SVernon Mauery             int rc = sendOutboundPayload();
3116f353e86SVernon Mauery             if (rc == 0)
3126f353e86SVernon Mauery             {
3136f353e86SVernon Mauery                 return;
3146f353e86SVernon Mauery             }
3156f353e86SVernon Mauery         }
3166f353e86SVernon Mauery         enableAccumulateTimer(true);
3176f353e86SVernon Mauery     }
31812d199b2SPatrick Williams     catch (const std::exception& e)
3196f353e86SVernon Mauery     {
3207b7f25f7SGeorge Liu         lg2::error("Failed to call the sendOutboundPayload method: {ERROR}",
3217b7f25f7SGeorge Liu                    "ERROR", e);
3226f353e86SVernon Mauery     }
3236f353e86SVernon Mauery }
3246f353e86SVernon Mauery 
retryTimerHandler()3256f353e86SVernon Mauery void Context::retryTimerHandler()
3266f353e86SVernon Mauery {
3276f353e86SVernon Mauery     try
3286f353e86SVernon Mauery     {
3296f353e86SVernon Mauery         if (retryCounter)
3306f353e86SVernon Mauery         {
3316f353e86SVernon Mauery             --retryCounter;
3326f353e86SVernon Mauery             enableRetryTimer(true);
3336f353e86SVernon Mauery             resendPayload(sol::Context::noClear);
3346f353e86SVernon Mauery         }
3356f353e86SVernon Mauery         else
3366f353e86SVernon Mauery         {
3376f353e86SVernon Mauery             retryCounter = maxRetryCount;
3386f353e86SVernon Mauery             resendPayload(sol::Context::clear);
3396f353e86SVernon Mauery             enableRetryTimer(false);
3406f353e86SVernon Mauery             enableAccumulateTimer(true);
3416f353e86SVernon Mauery         }
3426f353e86SVernon Mauery     }
34312d199b2SPatrick Williams     catch (const std::exception& e)
3446f353e86SVernon Mauery     {
3457b7f25f7SGeorge Liu         lg2::error("Failed to retry timer: {ERROR}", "ERROR", e);
3466f353e86SVernon Mauery     }
3476f353e86SVernon Mauery }
348fbcac2e7STom Joseph } // namespace sol
349