blob: 1095526ac9a0fdd7e6cdca1766c3949bead5e1e5 [file] [log] [blame]
// 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