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 23 #include <cstdint> 24 #include <cstring> 25 #include <ipmiblob/blob_errors.hpp> 26 #include <memory> 27 #include <string> 28 29 namespace host_tool 30 { 31 32 bool P2aDataHandler::sendContents(const std::string& input, 33 std::uint16_t session) 34 { 35 PciDevice result; 36 PciFilter filter; 37 bool found = false; 38 pciaddr_t bar1; 39 40 filter.vid = aspeedVendorId; 41 filter.did = aspeedDeviceId; 42 43 /* Find the ASPEED PCI device entry we want. */ 44 auto output = pci->getPciDevices(filter); 45 for (const auto& d : output) 46 { 47 std::fprintf(stderr, "[0x%x 0x%x] ", d.vid, d.did); 48 49 /* Verify it's a memory-based bar -- we want bar1. */ 50 bar1 = d.bars[1]; 51 if ((bar1 & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) 52 { 53 /* We want it to not be IO-based access. */ 54 continue; 55 } 56 57 /* For now capture the entire device even if we're only using BAR1 */ 58 result = d; 59 found = true; 60 break; 61 } 62 63 if (!found) 64 { 65 return false; 66 } 67 68 std::fprintf(stderr, "\n"); 69 70 /* We sent the open command before this, so the window should be open and 71 * the bridge enabled. 72 */ 73 std::uint32_t value; 74 if (!io->read(bar1 | aspeedP2aConfig, sizeof(value), &value)) 75 { 76 std::fprintf(stderr, "PCI config read failed\n"); 77 return false; 78 } 79 80 if (0 == (value & p2ABridgeEnabled)) 81 { 82 std::fprintf(stderr, "Bridge not enabled.\n"); 83 return false; 84 } 85 86 std::fprintf(stderr, "The bridge is enabled!\n"); 87 88 /* Read the configuration via blobs metadata (stat). */ 89 ipmiblob::StatResponse stat = blob->getStat(session); 90 if (stat.metadata.size() != sizeof(ipmi_flash::PciConfigResponse)) 91 { 92 std::fprintf(stderr, "Didn't receive expected size of metadata for " 93 "PCI Configuration response\n"); 94 return false; 95 } 96 97 ipmi_flash::PciConfigResponse pciResp; 98 std::memcpy(&pciResp, stat.metadata.data(), sizeof(pciResp)); 99 std::fprintf(stderr, "Received address: 0x%x\n", pciResp.address); 100 101 /* Configure the mmio to point there. */ 102 if (!io->write(bar1 | aspeedP2aBridge, sizeof(pciResp.address), 103 &pciResp.address)) 104 { 105 // Failed to set it up, so fall back. 106 std::fprintf(stderr, "Failed to update the bridge address\n"); 107 return false; 108 } 109 110 /* For data blocks in 64kb, stage data, and send blob write command. */ 111 int inputFd = sys->open(input.c_str(), 0); 112 if (inputFd < 0) 113 { 114 return false; 115 } 116 117 std::int64_t fileSize = sys->getSize(input.c_str()); 118 if (fileSize == 0) 119 { 120 std::fprintf(stderr, "Zero-length file, or other file access error\n"); 121 return false; 122 } 123 124 progress->start(fileSize); 125 126 const std::uint32_t p2aLength = aspeedP2aOffset; 127 128 auto readBuffer = std::make_unique<std::uint8_t[]>(p2aLength); 129 if (nullptr == readBuffer) 130 { 131 std::fprintf(stderr, "Unable to allocate memory for read buffer.\n"); 132 return false; 133 } 134 135 try 136 { 137 int bytesRead = 0; 138 std::uint32_t offset = 0; 139 140 do 141 { 142 bytesRead = sys->read(inputFd, readBuffer.get(), p2aLength); 143 if (bytesRead > 0) 144 { 145 /* TODO: Will likely need to store an rv somewhere to know when 146 * we're exiting from failure. 147 */ 148 if (!io->write(bar1 | aspeedP2aOffset, bytesRead, 149 readBuffer.get())) 150 { 151 std::fprintf(stderr, 152 "Failed to write to region in memory!\n"); 153 break; 154 } 155 156 /* Ok, so the data is staged, now send the blob write with the 157 * details. 158 */ 159 struct ipmi_flash::ExtChunkHdr chunk; 160 chunk.length = bytesRead; 161 std::vector<std::uint8_t> chunkBytes(sizeof(chunk)); 162 std::memcpy(chunkBytes.data(), &chunk, sizeof(chunk)); 163 164 /* This doesn't return anything on success. */ 165 blob->writeBytes(session, offset, chunkBytes); 166 offset += bytesRead; 167 progress->updateProgress(bytesRead); 168 } 169 } while (bytesRead > 0); 170 } 171 catch (const ipmiblob::BlobException& b) 172 { 173 sys->close(inputFd); 174 return false; 175 } 176 177 sys->close(inputFd); 178 return true; 179 } 180 181 } // namespace host_tool 182