blob: 7ae8c76a88d18793a7d43c8c07c54c524f6605a5 [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 <chrono>
#include <thread>
#include <utility>
namespace host_tool
{
/* 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)
{
using namespace std::chrono_literals;
static constexpr auto verificationSleep = 5s;
ipmi_flash::ActionStatus result = ipmi_flash::ActionStatus::unknown;
try
{
/* sleep for 5 seconds and check 360 times, for a timeout of: 1800
* seconds (30 minutes).
* TODO: make this command line configurable and provide smaller
* default value.
*/
static constexpr int commandAttempts = 360;
int attempts = 0;
bool exitLoop = false;
/* Reach back the current status from the verification service output.
*/
while (attempts++ < commandAttempts)
{
ipmiblob::StatResponse resp = blob->getStat(session);
if (resp.metadata.size() != sizeof(std::uint8_t))
{
/* TODO: How do we want to handle the verification failures,
* because closing the session to the verify blob has a special
* as-of-yet not fully defined behavior.
*/
std::fprintf(stderr, "Received invalid metadata response!!!\n");
}
result = static_cast<ipmi_flash::ActionStatus>(resp.metadata[0]);
switch (result)
{
case ipmi_flash::ActionStatus::failed:
std::fprintf(stderr, "failed\n");
exitLoop = true;
break;
case ipmi_flash::ActionStatus::unknown:
std::fprintf(stderr, "other\n");
break;
case ipmi_flash::ActionStatus::running:
std::fprintf(stderr, "running\n");
break;
case ipmi_flash::ActionStatus::success:
std::fprintf(stderr, "success\n");
exitLoop = true;
break;
default:
std::fprintf(stderr, "wat\n");
}
if (exitLoop)
{
break;
}
std::this_thread::sleep_for(verificationSleep);
}
}
catch (const ipmiblob::BlobException& b)
{
throw ToolException("blob exception received: " +
std::string(b.what()));
}
/* TODO: If this is reached and it's not success, it may be worth just
* throwing a ToolException with a timeout message specifying the final
* read's value.
*
* TODO: Given that excepting from certain points leaves the BMC update
* state machine in an inconsistent state, we need to carefully evaluate
* which exceptions from the lower layers allow one to try and delete the
* blobs to rollback the state and progress.
*/
if (result != ipmi_flash::ActionStatus::success)
{
throw ToolException("BMC reported failure");
}
}
/* 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)
{
using namespace std::chrono_literals;
static constexpr auto pollingSleep = 5s;
ipmiblob::StatResponse blobStatResp;
try
{
/* Polling lasts 5 minutes. When opening a version blob, the system
* unit defined in the version handler will extract the running version
* from the image on the flash.
*/
static constexpr int commandAttempts = 60;
int attempts = 0;
while (attempts++ < commandAttempts)
{
blobStatResp = blob->getStat(session);
if (blobStatResp.blob_state & ipmiblob::StateFlags::open_read)
{
std::fprintf(stderr, "success\n");
return blobStatResp.size;
}
else if (blobStatResp.blob_state & ipmiblob::StateFlags::committing)
{
std::fprintf(stderr, "running\n");
}
else
{
std::fprintf(stderr, "failed\n");
throw ToolException("BMC reported failure");
}
std::this_thread::sleep_for(pollingSleep);
}
}
catch (const ipmiblob::BlobException& b)
{
throw ToolException("blob exception received: " +
std::string(b.what()));
}
throw ToolException("Timed out waiting for BMC read 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