blob: 50e891da4cccbf265d8ff4bd67802765d91879e5 [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 "p2a.hpp"
#include "data.hpp"
#include "flags.hpp"
#include "pci.hpp"
#include "tool_errors.hpp"
#include <ipmiblob/blob_errors.hpp>
#include <stdplus/handle/managed.hpp>
#include <cstdint>
#include <cstring>
#include <format>
#include <memory>
#include <span>
#include <string>
namespace host_tool
{
namespace
{
/** @brief RAII wrapper and its destructor for opening a file descriptor */
static void closeFd(int&& fd, const internal::Sys* const& sys)
{
sys->close(fd);
}
using Fd = stdplus::Managed<int, const internal::Sys* const>::Handle<closeFd>;
} // namespace
bool P2aDataHandler::sendContents(const std::string& input,
std::uint16_t session)
{
std::unique_ptr<PciBridgeIntf> bridge;
ipmi_flash::PciConfigResponse pciResp;
std::int64_t fileSize;
try
{
bridge = std::make_unique<NuvotonPciBridge>(pci, skipBridgeDisable);
}
catch (const NotFoundException& e)
{}
try
{
bridge = std::make_unique<AspeedPciBridge>(pci, skipBridgeDisable);
}
catch (const NotFoundException& e)
{}
if (!bridge)
{
throw NotFoundException("supported PCI device");
}
/* Read the configuration via blobs metadata (stat). */
ipmiblob::StatResponse stat = blob->getStat(session);
if (stat.metadata.size() != sizeof(ipmi_flash::PciConfigResponse))
{
throw ToolException("Didn't receive expected size of metadata for "
"PCI Configuration response");
}
std::memcpy(&pciResp, stat.metadata.data(), sizeof(pciResp));
bridge->configure(pciResp);
/* For data blocks in 64kb, stage data, and send blob write command. */
Fd inputFd(sys->open(input.c_str(), 0), sys);
if (*inputFd < 0)
{
(void)inputFd.release();
throw internal::errnoException(
std::format("Error opening file '{}'", input));
}
fileSize = sys->getSize(input.c_str());
progress->start(fileSize);
std::vector<std::uint8_t> readBuffer(bridge->getDataLength());
int bytesRead = 0;
std::uint32_t offset = 0;
do
{
bytesRead = sys->read(*inputFd, readBuffer.data(), readBuffer.size());
if (bytesRead > 0)
{
bridge->write(
std::span<const std::uint8_t>(readBuffer.data(), bytesRead));
/* Ok, so the data is staged, now send the blob write with the
* details.
*/
struct ipmi_flash::ExtChunkHdr chunk;
chunk.length = bytesRead;
std::vector<std::uint8_t> chunkBytes(sizeof(chunk));
std::memcpy(chunkBytes.data(), &chunk, sizeof(chunk));
/* This doesn't return anything on success. */
blob->writeBytes(session, offset, chunkBytes);
offset += bytesRead;
progress->updateProgress(bytesRead);
}
} while (bytesRead > 0);
progress->finish();
return true;
}
} // namespace host_tool