| // Copyright 2024 Google LLC |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #pragma once |
| |
| #include "host_command_interface.hpp" |
| #include "log_collector_util.hpp" |
| #include "message_intf.hpp" |
| |
| #include <boost/asio.hpp> |
| #include <boost/asio/io_context.hpp> |
| |
| #include <chrono> |
| #include <future> |
| #include <mutex> |
| #include <span> |
| #include <unordered_map> |
| #include <vector> |
| |
| namespace google |
| { |
| namespace hoth |
| { |
| namespace internal |
| { |
| |
| /** @class HostCommandImpl |
| * @brief HostCommandImpl method implementation |
| * @details Methods to send host commands to Hoth |
| */ |
| class HostCommandImpl : public HostCommand |
| { |
| public: |
| // 1024 erase blocks or 4MB takes ~12 seconds for most SPI devices |
| static constexpr size_t kMaxErase = 1024 * 4096; |
| // 16 MB on a 20MHz SPI takes ~9 seconds for most SPI devices |
| static constexpr size_t kMaxVerify = 16 * 1024 * 1024; |
| |
| static constexpr int kMaxLogCollectorRetries = 3; |
| |
| explicit HostCommandImpl(MessageIntf* msg, boost::asio::io_context* ioc, |
| LogCollectorUtil* logCollectorUtil, |
| const std::string& name, size_t max_retries = 0, |
| bool allowLegacyVerify = true, |
| uint32_t uartChannelId = 0); |
| |
| std::vector<uint8_t> |
| sendCommand(const std::vector<uint8_t>& command) override; |
| std::vector<uint8_t> |
| sendCommand(const std::vector<uint8_t>& command, |
| std::chrono::milliseconds timeout) override; |
| std::vector<uint8_t> sendCommand(uint16_t command, uint8_t commandVersion, |
| const void* request, |
| size_t requestSize) override; |
| std::vector<uint8_t> |
| sendCommand(uint16_t command, uint8_t commandVersion, |
| const void* request, size_t requestSize, |
| std::chrono::milliseconds timeout) override; |
| uint64_t sendCommandAsync(const std::vector<uint8_t>& command) override; |
| std::vector<uint8_t> getResponse(uint64_t callToken) override; |
| bool communicationFailure() const override; |
| |
| /** |
| * @brief Triggers collect hoth logs asynchronously. Does not wait for log |
| * collection completion. If the requester wants to wait for log collection |
| * completion, should run waitOnLogCollectorPromise() to wait |
| * |
| * @param[in] cleanupPromiseAfterExecution- if true cleans up the future |
| * promise when request completes asynchronously. Will not cleanup the |
| * request, if set false, in case requester wants to wait for the promise |
| * |
| * @return requestId: Every request would have a request id that the |
| * requester can use to wait on and cleanup later |
| */ |
| int collectHothLogsAsync(bool cleanupPromiseAfterExecution) override; |
| |
| /** |
| * @brief Triggers collect uart logs scheduler asynchronously. If the |
| * scheduler is already working (since it gets triggered on its own in |
| * constructor) this function does nothing. |
| */ |
| void collectUartLogsAsync() override; |
| |
| /** |
| * @brief Stops the uart collector scheduler. |
| */ |
| void stopUartLogs() override; |
| |
| /** |
| * @brief When the requester wants to wait on the log collector request to |
| * finish. Cleans up the request after promise fulfills |
| */ |
| void waitOnLogCollectorPromise(int requestId); |
| |
| /** @brief Determines if a command will take too long to execute based |
| * on well known parameters. This is useful to prevent the SPI |
| * flash from remaining busy for too long. Allowing VERIFY is |
| * optional since some older platforms depend on it to exist. |
| * We limit this to <~15s to prevent IPMI and SPI channels from |
| * blocking too long. |
| * |
| * @param[in] cmd - The command request bytes |
| * @param[in] allowLegacyVerify - If PAYLOAD_UPDATE_VERIFY is allowed |
| * @return An ec_status error for banning, EC_RES_SUCCESS otherwise |
| */ |
| static uint8_t isCommandLongRunning(std::span<const uint8_t> cmd, |
| bool allowLegacyVerify); |
| |
| private: |
| /** @brief The same operation as sendHostCommand without holding the lock |
| * @note Requires the caller to hold the cmdLock |
| * |
| * @param[in] req - Host command |
| * @return Response from the hoth |
| */ |
| std::vector<uint8_t> sendCommandNoLock(const std::vector<uint8_t>& command); |
| |
| /** @brief Validate the given command and send it to Hoth |
| * |
| * @param[in] req - Host command |
| */ |
| void validateAndSend(const std::vector<uint8_t>& req); |
| |
| /** @brief Receive a response from Hoth and validate it |
| * |
| * @param[out] rsp - Response from Hoth |
| */ |
| void recvAndValidate(std::vector<uint8_t>& rsp); |
| |
| /** |
| * @brief Collects hoth logs |
| */ |
| void collectHothLogs(); |
| |
| /** |
| * @brief Collect Uart logs |
| * |
| * @param[in] offset - offset for the current read request |
| * |
| * @return offset for the next read request |
| */ |
| uint32_t collectUartLogs(uint32_t offset); |
| |
| /** |
| * @brief Gets the write offset from ec channel status command. This is |
| * required for the first log collector command |
| * |
| * @return Optional channel write offset. In case of sendHostCommand failure |
| * or invalid response received, we will return a nullopt that would stop |
| * the scheduler as we need this to get the correct read input point |
| */ |
| std::optional<little_uint32_t> getChannelWriteOffset(); |
| |
| /** |
| * @brief Sets promise value while collecting hoth logs for a unique request |
| * id |
| * |
| * @param[in] requestId - Unique id for the log collector request |
| */ |
| void setPromiseValue(int requestId); |
| |
| /** |
| * @brief Sets promise exception while collecting hoth logs for a unique |
| * request id |
| * |
| * @param[in] requestId - Unique id for the log collector request |
| */ |
| void setPromiseException(int requestId); |
| |
| /** |
| * @brief Cleans up the requests/promise from the map after execution or |
| * wait is completed |
| * |
| * @param[in] requestId - Unique id for the log collector request |
| */ |
| void cleanupLogCollectorRequest(int requestId); |
| |
| /** |
| * @brief Schedules the log collector by waiting for kSleepUartTimeInSeconds |
| * seconds between each request |
| * |
| * @param[in] offset - Offset for the current read request |
| */ |
| void scheduleUartLogCollector(little_uint32_t offset = 0); |
| |
| /** @brief Connection to Hoth for sending and receiving messages */ |
| MessageIntf* msg; |
| |
| boost::asio::steady_timer timer; // Scheduler timer |
| |
| boost::asio::io_context* io; |
| |
| LogCollectorUtil* logCollectorUtil; |
| |
| std::string name; // Name for the uart endpoint if exists |
| |
| /** @brief Maximum number of times to retry communication */ |
| size_t max_retries; |
| |
| /** @brief Ensure only a single command is outstanding at a time |
| * since we don't know how to associate interleaved responses. |
| */ |
| std::timed_mutex cmdLock; |
| |
| /** @brief Tracks failure to communicate with hoth */ |
| bool communicationFailed = false; |
| |
| /** @brief Do we allow for legacy verification */ |
| bool allowLegacyVerify; |
| |
| /** @brief Specifies the UART channel ID if hothd should collect and stream |
| * the device's UART logs*/ |
| uint32_t uartChannelId; |
| |
| bool runUartCollector = false; // Scheduler status |
| |
| int logCollectorRetryCounter = 0; |
| |
| /** |
| * @brief Stores log collector request id to promise object until the |
| * execution or wait request completes |
| * */ |
| std::unordered_map<int, std::promise<void>> idToLogCollectorPromiseMap; |
| }; |
| |
| } // namespace internal |
| |
| } // namespace hoth |
| |
| } // namespace google |