1 /* 2 * Copyright 2019 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include "p2a.hpp" 18 19 #include "data.hpp" 20 #include "flags.hpp" 21 #include "pci.hpp" 22 #include "tool_errors.hpp" 23 24 #include <fmt/format.h> 25 26 #include <ipmiblob/blob_errors.hpp> 27 #include <stdplus/handle/managed.hpp> 28 29 #include <cstdint> 30 #include <cstring> 31 #include <memory> 32 #include <span> 33 #include <string> 34 35 namespace host_tool 36 { 37 38 namespace 39 { 40 41 /** @brief RAII wrapper and its destructor for opening a file descriptor */ 42 static void closeFd(int&& fd, const internal::Sys* const& sys) 43 { 44 sys->close(fd); 45 } 46 using Fd = stdplus::Managed<int, const internal::Sys* const>::Handle<closeFd>; 47 48 } // namespace 49 50 bool P2aDataHandler::sendContents(const std::string& input, 51 std::uint16_t session) 52 { 53 std::unique_ptr<PciBridgeIntf> bridge; 54 ipmi_flash::PciConfigResponse pciResp; 55 std::int64_t fileSize; 56 57 try 58 { 59 bridge = std::make_unique<NuvotonPciBridge>(pci, skipBridgeDisable); 60 } 61 catch (const NotFoundException& e) 62 {} 63 64 try 65 { 66 bridge = std::make_unique<AspeedPciBridge>(pci, skipBridgeDisable); 67 } 68 catch (const NotFoundException& e) 69 {} 70 71 if (!bridge) 72 { 73 throw NotFoundException("supported PCI device"); 74 } 75 76 /* Read the configuration via blobs metadata (stat). */ 77 ipmiblob::StatResponse stat = blob->getStat(session); 78 if (stat.metadata.size() != sizeof(ipmi_flash::PciConfigResponse)) 79 { 80 throw ToolException("Didn't receive expected size of metadata for " 81 "PCI Configuration response"); 82 } 83 84 std::memcpy(&pciResp, stat.metadata.data(), sizeof(pciResp)); 85 bridge->configure(pciResp); 86 87 /* For data blocks in 64kb, stage data, and send blob write command. */ 88 Fd inputFd(sys->open(input.c_str(), 0), sys); 89 if (*inputFd < 0) 90 { 91 (void)inputFd.release(); 92 throw internal::errnoException( 93 fmt::format("Error opening file '{}'", input)); 94 } 95 96 fileSize = sys->getSize(input.c_str()); 97 progress->start(fileSize); 98 99 std::vector<std::uint8_t> readBuffer(bridge->getDataLength()); 100 101 int bytesRead = 0; 102 std::uint32_t offset = 0; 103 104 do 105 { 106 bytesRead = sys->read(*inputFd, readBuffer.data(), readBuffer.size()); 107 if (bytesRead > 0) 108 { 109 bridge->write( 110 std::span<const std::uint8_t>(readBuffer.data(), bytesRead)); 111 112 /* Ok, so the data is staged, now send the blob write with the 113 * details. 114 */ 115 struct ipmi_flash::ExtChunkHdr chunk; 116 chunk.length = bytesRead; 117 std::vector<std::uint8_t> chunkBytes(sizeof(chunk)); 118 std::memcpy(chunkBytes.data(), &chunk, sizeof(chunk)); 119 120 /* This doesn't return anything on success. */ 121 blob->writeBytes(session, offset, chunkBytes); 122 offset += bytesRead; 123 progress->updateProgress(bytesRead); 124 } 125 } while (bytesRead > 0); 126 127 progress->finish(); 128 return true; 129 } 130 131 } // namespace host_tool 132