blob: f41941030fe7ca493679e400620923b809b9b78f [file] [log] [blame]
/*
* Copyright 2019 Google Inc.
*
* 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.
*/
#include "helper.hpp"
#include "status.hpp"
#include "tool_errors.hpp"
#include <ipmiblob/blob_errors.hpp>
#include <ipmiblob/blob_interface.hpp>
#include <algorithm>
#include <chrono>
#include <optional>
#include <thread>
#include <utility>
namespace host_tool
{
template <typename Check>
static auto pollStat(std::uint16_t session, ipmiblob::BlobInterface* blob,
Check&& check)
{
using namespace std::chrono_literals;
constexpr auto maxSleep = 1s;
constexpr auto printInterval = 30s;
constexpr auto timeout = 30min;
try
{
auto start = std::chrono::steady_clock::now();
auto last_print = start;
auto last_check = start;
auto check_interval = 50ms;
while (true)
{
ipmiblob::StatResponse resp = blob->getStat(session);
auto ret = check(resp);
if (ret.has_value())
{
std::fprintf(stderr, "success\n");
return std::move(*ret);
}
auto cur = std::chrono::steady_clock::now();
if (cur - last_print >= printInterval)
{
std::fprintf(stderr, "running\n");
last_print = cur;
}
auto sleep = check_interval - (cur - last_check);
last_check = cur;
// Check that we don't timeout immediately after sleeping
// to avoid an extra sleep without checking
if (cur - start > timeout - sleep)
{
throw ToolException("Stat check timed out");
}
check_interval = std::min<decltype(check_interval)>(
check_interval * 2, maxSleep);
std::this_thread::sleep_for(sleep);
}
}
catch (const ipmiblob::BlobException& b)
{
throw ToolException("blob exception received: " +
std::string(b.what()));
}
}
/* Poll an open verification session. Handling closing the session is not yet
* owned by this method.
*/
void pollStatus(std::uint16_t session, ipmiblob::BlobInterface* blob)
{
pollStat(session, blob,
[](const ipmiblob::StatResponse& resp) -> std::optional<bool> {
if (resp.metadata.size() != 1)
{
throw ToolException("Invalid stat metadata");
}
auto result = static_cast<ipmi_flash::ActionStatus>(resp.metadata[0]);
switch (result)
{
case ipmi_flash::ActionStatus::failed:
throw ToolException("BMC reported failure");
case ipmi_flash::ActionStatus::unknown:
case ipmi_flash::ActionStatus::running:
return std::nullopt;
case ipmi_flash::ActionStatus::success:
return true;
default:
throw ToolException("Unrecognized action status");
}
});
}
/* Poll an open blob session for reading.
*
* The committing bit indicates that the blob is not available for reading now
* and the reader might come back and check the state later.
*
* Polling finishes under the following conditions:
* - The open_read bit set -> stat successful
* - The open_read and committing bits unset -> stat failed;
* - Blob exception was received;
* - Time ran out.
* Polling continues when the open_read bit unset and committing bit set.
* If the blob is not open_read and not committing, then it is an error to the
* reader.
*/
uint32_t pollReadReady(std::uint16_t session, ipmiblob::BlobInterface* blob)
{
return pollStat(
session, blob,
[](const ipmiblob::StatResponse& resp) -> std::optional<uint32_t> {
if (resp.blob_state & ipmiblob::StateFlags::open_read)
{
return resp.size;
}
if (resp.blob_state & ipmiblob::StateFlags::committing)
{
return std::nullopt;
}
throw ToolException("BMC blob failed to become ready");
});
}
void* memcpyAligned(void* destination, const void* source, std::size_t size)
{
std::size_t i = 0;
std::size_t bytesCopied = 0;
if (!(reinterpret_cast<std::uintptr_t>(destination) %
sizeof(std::uint64_t)) &&
!(reinterpret_cast<std::uintptr_t>(source) % sizeof(std::uint64_t)))
{
auto src64 = reinterpret_cast<const volatile std::uint64_t*>(source);
auto dest64 = reinterpret_cast<volatile std::uint64_t*>(destination);
for (i = 0; i < size / sizeof(std::uint64_t); i++)
{
*dest64++ = *src64++;
bytesCopied += sizeof(std::uint64_t);
}
}
auto srcMem8 = reinterpret_cast<const volatile std::uint8_t*>(source) +
bytesCopied;
auto destMem8 = reinterpret_cast<volatile std::uint8_t*>(destination) +
bytesCopied;
for (i = bytesCopied; i < size; i++)
{
*destMem8++ = *srcMem8++;
}
return destination;
}
} // namespace host_tool