blob: a95ad8591773601a0c78025ef7cd0e5f1343fc30 [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 "firmware_updater_interface.hpp"
#include "host_command.hpp"
#include <boost/endian/arithmetic.hpp>
#include <array>
#include <cstdint>
namespace google
{
namespace hoth
{
namespace internal
{
using boost::endian::little_uint16_t;
using boost::endian::little_uint32_t;
/** @brief Equivalent of `hoth_update_region_response` in
* google3/host_commands.h
*/
struct HothUpdateRegionResponse
{
little_uint32_t offset;
little_uint32_t len;
uint8_t flags;
};
/** @brief Equivalent of `ec_response_sps_passthrough_status` in
* google3/host_commands.h
*/
struct SpsPassthroughStatusResponse
{
uint8_t enabled;
std::array<uint8_t, 3> padding;
};
/** @brief Equivalent of `ec_spi_operation_request` in
* google3/host_commands.h
*/
struct EcSpiOperationHeader
{
little_uint16_t mosiLen;
little_uint16_t misoLen;
};
enum class ResetMode : uint8_t
{
needed,
never,
ignore,
needed_active,
};
/** @class FirmwareSpiUpdater class
* @brief Hoth firmware updater using Hoth SPI command
*/
class FirmwareSpiUpdater : public FirmwareUpdater
{
public:
using Base = FirmwareUpdater;
/** @brief A RAII object to prepare and cleanup the spiWrite op(s).
*/
class SpiWritePreparer
{
public:
SpiWritePreparer(FirmwareSpiUpdater* updater, ResetMode mode);
SpiWritePreparer(SpiWritePreparer&& preparer) noexcept;
~SpiWritePreparer();
SpiWritePreparer(const SpiWritePreparer&) = delete;
SpiWritePreparer& operator=(const SpiWritePreparer&) = delete;
SpiWritePreparer& operator=(SpiWritePreparer&&) = delete;
private:
FirmwareSpiUpdater* fwUpdater{nullptr};
bool setSpsPassThoughDisabled{false};
bool didResetTarget{false};
ResetMode resetMode{ResetMode::needed};
};
/** @brief Constructor
*
* @param[in] hostCmd - Reference to the host command interface
* @param[in] addressSize - SPI operation address size
* @param[in] targetReset - Whether to reset target
* @param[in] ignoreAddressMode - Ignore address mode change failure
*/
explicit FirmwareSpiUpdater(HostCommand* hostCmd, uint8_t addressSize = 4,
ResetMode targetReset = ResetMode::never,
bool ignoreAddressMode = false);
[[nodiscard]] SpiWritePreparer prepareSpiWrite();
/** @brief Updates firmware.
*
* @param[in] firmwareData - Hoth firmware image.
*/
void update(std::vector<uint8_t> firmwareData) override;
/** @brief SPI write to arbitrary address
*
* @param[in] address - SPI offset to start writing from
* @param[in] data - Data to write to the SPI EEPROM
*/
void spiWrite(uint32_t address, std::vector<uint8_t> data) override;
private:
/** @brief Check if update firmware with Hoth is supported
* Check firmware update mode supported using Hoth
* EC_PRV_CMD_HOTH_GET_REGION_FROM_IMG_DESC command.
*
* @param[in] firmwareSize - Hoth firmware image length.
*
* @return absolute address from which new firmware data can be written
*/
uint32_t supportUpdateWithHothSpi(uint32_t firmwareSize);
/** @brief Set/release target from reset
* Calls EC_PRV_CMD_HOTH_RESET_TARGET command to reset target
*
* We only need to call this funciton when the build option is enabled.
*
* @param[in] option - one of ec_target_reset_option enums
*/
void resetTarget(uint8_t option);
/** @brief Static helper function for RAII target release
*/
static void releaseTarget(FirmwareSpiUpdater*&& updater) noexcept;
/** @brief Check whether SPS passthrough is enabled
*
* If SPS passthrough is disabled, then we can send SPI commands without
* having to reset the target.
*
* @return true if passthrough is enabled, false if it's disabled
*/
bool getSpsPassthrough();
/** @brief Set SPS passthrough mode
*
* If SPS passthrough is disabled, then we can send SPI commands without
* having to reset the target.
*
* @param[in] enable - The desired SPS passthough mode.
* @return indicates if the passthrough mode is successfully set.
*/
bool setSpsPassthrough(bool enable);
/** @brief SPI requires erase enough space before write
*
* @param[in] firmwareSize - Hoth firmware image length.
* @param[in] address - address to erase
*/
void erase(size_t firmwareSize, uint32_t address);
/** @brief Writes firmwara data
*
* @param[in] firmwareData - Hoth firmware image.
* @param[in] address - address to write
*/
void writeData(const std::vector<uint8_t>& firmwareData, uint32_t address);
/** @brief Reads back data and verifies it matches input firmware
*
* @param[in] firmwareData - Hoth firmware image.
* @param[in] address - address to read
*/
void verifyData(const std::vector<uint8_t>& firmwareData, uint32_t address);
/** @brief Appends 3 or 4 byte address to `array` depending on `addressSize`
*
* @param[in] array - byte array
* @param[in] address - address
*/
void appendAddress(std::vector<uint8_t> &array, uint32_t address) const;
/** @brief The actual firmware update operation.
*
* @param[in] firmwareData - data
* @param[in] address - address
*/
void doUpdate(const std::vector<uint8_t>& data, uint32_t address);
/** @brief Connection to Hoth for sending and receiving host commands */
HostCommand* hostCmd;
/** @brief SPI operation address size, it can be 3 or 4 bytes */
uint8_t addressSize;
/** @brief Whether set target to reset before SPI */
ResetMode targetReset;
/** @brief Whether to ignore address mode change failure */
bool ignoreAddressMode;
};
} // namespace internal
} // namespace hoth
} // namespace google