1 /* 2 * Copyright 2018 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 "lpc.hpp" 18 19 #include "data.hpp" 20 21 #include <ipmiblob/blob_errors.hpp> 22 23 #include <cerrno> 24 #include <cstdint> 25 #include <cstring> 26 #include <memory> 27 #include <string> 28 29 namespace host_tool 30 { 31 32 bool LpcDataHandler::sendContents(const std::string& input, 33 std::uint16_t session) 34 { 35 LpcRegion host_lpc_buf; 36 host_lpc_buf.address = address; 37 host_lpc_buf.length = length; 38 39 std::vector<std::uint8_t> payload(sizeof(host_lpc_buf)); 40 41 while (true) 42 { 43 /* If the writeMeta() is rejected we need to call sessionStat on it. */ 44 try 45 { 46 std::fprintf(stderr, "sending writeMeta\n"); 47 48 std::memcpy(payload.data(), &host_lpc_buf, sizeof(host_lpc_buf)); 49 blob->writeMeta(session, 0x00, payload); 50 51 std::fprintf(stderr, "writemeta sent\n"); 52 53 break; 54 } 55 catch (...) 56 { 57 std::fprintf(stderr, "caught exception\n"); 58 59 ipmiblob::StatResponse resp = blob->getStat(session); 60 if (resp.metadata.empty()) 61 { 62 std::fprintf(stderr, "Received no metadata bytes back!"); 63 return false; 64 } 65 66 struct MemoryMapResultDetails 67 { 68 std::uint8_t code; 69 std::uint32_t offset; 70 std::uint32_t length; 71 } __attribute__((packed)); 72 73 struct MemoryMapResultDetails bytes; 74 75 if (resp.metadata.size() != sizeof(bytes)) 76 { 77 std::fprintf( 78 stderr, 79 "Received insufficient bytes back on expected return!\n"); 80 return false; 81 } 82 83 std::memcpy(&bytes, resp.metadata.data(), sizeof(bytes)); 84 85 if (bytes.code == EFBIG) 86 { 87 std::fprintf(stderr, "EFBIG returned!\n"); 88 89 host_lpc_buf.length = bytes.length; 90 host_lpc_buf.address += bytes.offset; 91 } 92 else if (bytes.code == 0) 93 { 94 /* We're good, continue! */ 95 break; 96 } 97 } 98 } 99 100 /* For data blockss, stage data, and send blob write command. */ 101 int inputFd = sys->open(input.c_str(), 0); 102 if (inputFd < 0) 103 { 104 return false; 105 } 106 107 std::int64_t fileSize = sys->getSize(input.c_str()); 108 if (fileSize == 0) 109 { 110 std::fprintf(stderr, "Zero-length file, or other file access error\n"); 111 return false; 112 } 113 114 progress->start(fileSize); 115 116 /* For Nuvoton the maximum is 4K */ 117 auto readBuffer = std::make_unique<std::uint8_t[]>(host_lpc_buf.length); 118 if (nullptr == readBuffer) 119 { 120 sys->close(inputFd); 121 std::fprintf(stderr, "Unable to allocate memory for read buffer.\n"); 122 return false; 123 } 124 125 /* TODO: This is similar to PCI insomuch as how it sends data, so combine. 126 */ 127 try 128 { 129 int bytesRead = 0; 130 std::uint32_t offset = 0; 131 132 do 133 { 134 bytesRead = 135 sys->read(inputFd, readBuffer.get(), host_lpc_buf.length); 136 if (bytesRead > 0) 137 { 138 if (!io->write(host_lpc_buf.address, bytesRead, 139 readBuffer.get())) 140 { 141 std::fprintf(stderr, 142 "Failed to write to region in memory!\n"); 143 } 144 145 struct ipmi_flash::ExtChunkHdr chunk; 146 chunk.length = bytesRead; 147 std::vector<std::uint8_t> chunkBytes(sizeof(chunk)); 148 std::memcpy(chunkBytes.data(), &chunk, sizeof(chunk)); 149 150 /* This doesn't return anything on success. */ 151 blob->writeBytes(session, offset, chunkBytes); 152 offset += bytesRead; 153 progress->updateProgress(bytesRead); 154 } 155 } while (bytesRead > 0); 156 } 157 catch (const ipmiblob::BlobException& b) 158 { 159 sys->close(inputFd); 160 return false; 161 } 162 163 sys->close(inputFd); 164 return true; 165 } 166 167 } // namespace host_tool 168