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 /* For Nuvoton the maximum is 4K */ 109 auto readBuffer = std::make_unique<std::uint8_t[]>(host_lpc_buf.length); 110 if (nullptr == readBuffer) 111 { 112 sys->close(inputFd); 113 std::fprintf(stderr, "Unable to allocate memory for read buffer.\n"); 114 return false; 115 } 116 117 progress->start(fileSize); 118 119 /* TODO: This is similar to PCI insomuch as how it sends data, so combine. 120 */ 121 try 122 { 123 int bytesRead = 0; 124 std::uint32_t offset = 0; 125 126 do 127 { 128 bytesRead = 129 sys->read(inputFd, readBuffer.get(), host_lpc_buf.length); 130 if (bytesRead > 0) 131 { 132 if (!io->write(host_lpc_buf.address, bytesRead, 133 readBuffer.get())) 134 { 135 std::fprintf(stderr, 136 "Failed to write to region in memory!\n"); 137 } 138 139 struct ipmi_flash::ExtChunkHdr chunk; 140 chunk.length = bytesRead; 141 std::vector<std::uint8_t> chunkBytes(sizeof(chunk)); 142 std::memcpy(chunkBytes.data(), &chunk, sizeof(chunk)); 143 144 /* This doesn't return anything on success. */ 145 blob->writeBytes(session, offset, chunkBytes); 146 offset += bytesRead; 147 progress->updateProgress(bytesRead); 148 } 149 } while (bytesRead > 0); 150 } 151 catch (const ipmiblob::BlobException& b) 152 { 153 progress->abort(); 154 sys->close(inputFd); 155 return false; 156 } 157 158 progress->finish(); 159 sys->close(inputFd); 160 return true; 161 } 162 163 } // namespace host_tool 164