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 <ipmiblob/blob_errors.hpp> 24 25 #include <cstdint> 26 #include <cstring> 27 #include <memory> 28 #include <string> 29 30 namespace host_tool 31 { 32 33 namespace 34 { 35 void disablePciBridge(HostIoInterface* io, std::size_t address) 36 { 37 /* Read current value, and just blindly unset the bit. */ 38 std::uint32_t value; 39 if (!io->read(address + aspeedP2aConfig, sizeof(value), &value)) 40 { 41 return; 42 } 43 44 value &= ~p2ABridgeEnabled; 45 io->write(address + aspeedP2aConfig, sizeof(value), &value); 46 } 47 48 } // namespace 49 50 bool P2aDataHandler::sendContents(const std::string& input, 51 std::uint16_t session) 52 { 53 PciDevice result; 54 PciFilter filter; 55 bool found = false; 56 pciaddr_t bar; 57 bool returnValue = false; 58 int inputFd = -1; 59 ipmi_flash::PciConfigResponse pciResp; 60 std::int64_t fileSize; 61 std::unique_ptr<std::uint8_t[]> readBuffer; 62 63 std::uint16_t pciDeviceVID; 64 std::uint16_t pciDeviceDID; 65 std::uint32_t p2aOffset; 66 std::uint32_t p2aLength; 67 68 for (auto device : PCIDeviceList) 69 { 70 filter.vid = device.VID; 71 filter.did = device.DID; 72 73 /* Find the PCI device entry we want. */ 74 auto output = pci->getPciDevices(filter); 75 for (const auto& d : output) 76 { 77 std::fprintf(stderr, "[0x%x 0x%x] \n", d.vid, d.did); 78 79 /* Verify it's a memory-based bar. */ 80 bar = d.bars[device.bar]; 81 82 if ((bar & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) 83 { 84 /* We want it to not be IO-based access. */ 85 continue; 86 } 87 88 /* For now capture the entire device even if we're only using BAR0 89 */ 90 result = d; 91 found = true; 92 break; 93 } 94 95 if (found) 96 { 97 std::fprintf(stderr, "Find [0x%x 0x%x] \n", device.VID, device.DID); 98 std::fprintf(stderr, "bar%u[0x%x] \n", device.bar, 99 (unsigned int)bar); 100 pciDeviceVID = device.VID; 101 pciDeviceDID = device.DID; 102 p2aOffset = device.Offset; 103 p2aLength = device.Length; 104 break; 105 } 106 } 107 108 if (!found) 109 { 110 return false; 111 } 112 113 std::fprintf(stderr, "\n"); 114 115 /* We sent the open command before this, so the window should be open and 116 * the bridge enabled on the BMC. 117 */ 118 if (pciDeviceVID == aspeedPciDeviceInfo.VID && 119 pciDeviceDID == aspeedPciDeviceInfo.DID) 120 { 121 std::uint32_t value; 122 if (!io->read(bar + aspeedP2aConfig, sizeof(value), &value)) 123 { 124 std::fprintf(stderr, "PCI config read failed\n"); 125 return false; 126 } 127 128 if (0 == (value & p2ABridgeEnabled)) 129 { 130 std::fprintf(stderr, "Bridge not enabled - Enabling from host\n"); 131 132 value |= p2ABridgeEnabled; 133 if (!io->write(bar + aspeedP2aConfig, sizeof(value), &value)) 134 { 135 std::fprintf(stderr, "PCI config write failed\n"); 136 return false; 137 } 138 } 139 140 /* From this point down we need to disable the bridge. */ 141 std::fprintf(stderr, "The bridge is enabled!\n"); 142 } 143 144 /* Read the configuration via blobs metadata (stat). */ 145 ipmiblob::StatResponse stat = blob->getStat(session); 146 if (stat.metadata.size() != sizeof(ipmi_flash::PciConfigResponse)) 147 { 148 std::fprintf(stderr, "Didn't receive expected size of metadata for " 149 "PCI Configuration response\n"); 150 goto exit; 151 } 152 153 std::memcpy(&pciResp, stat.metadata.data(), sizeof(pciResp)); 154 std::fprintf(stderr, "Received address: 0x%x\n", pciResp.address); 155 156 if (pciDeviceVID == aspeedPciDeviceInfo.VID && 157 pciDeviceDID == aspeedPciDeviceInfo.DID) 158 { 159 /* Configure the mmio to point there. */ 160 if (!io->write(bar + aspeedP2aBridge, sizeof(pciResp.address), 161 &pciResp.address)) 162 { 163 // Failed to set it up, so fall back. 164 std::fprintf(stderr, "Failed to update the bridge address\n"); 165 goto exit; 166 } 167 } 168 169 /* For data blocks in 64kb, stage data, and send blob write command. */ 170 inputFd = sys->open(input.c_str(), 0); 171 if (inputFd < 0) 172 { 173 std::fprintf(stderr, "Unable to open file: '%s'\n", input.c_str()); 174 goto exit; 175 } 176 177 fileSize = sys->getSize(input.c_str()); 178 if (fileSize == 0) 179 { 180 std::fprintf(stderr, "Zero-length file, or other file access error\n"); 181 goto exit; 182 } 183 184 progress->start(fileSize); 185 186 readBuffer = std::make_unique<std::uint8_t[]>(p2aLength); 187 if (nullptr == readBuffer) 188 { 189 std::fprintf(stderr, "Unable to allocate memory for read buffer.\n"); 190 goto exit; 191 } 192 193 try 194 { 195 int bytesRead = 0; 196 std::uint32_t offset = 0; 197 198 do 199 { 200 bytesRead = sys->read(inputFd, readBuffer.get(), p2aLength); 201 if (bytesRead > 0) 202 { 203 /* TODO: Will likely need to store an rv somewhere to know when 204 * we're exiting from failure. 205 */ 206 if (!io->write(bar + p2aOffset, bytesRead, readBuffer.get())) 207 { 208 std::fprintf(stderr, 209 "Failed to write to region in memory!\n"); 210 goto exit; 211 } 212 213 /* Ok, so the data is staged, now send the blob write with the 214 * details. 215 */ 216 struct ipmi_flash::ExtChunkHdr chunk; 217 chunk.length = bytesRead; 218 std::vector<std::uint8_t> chunkBytes(sizeof(chunk)); 219 std::memcpy(chunkBytes.data(), &chunk, sizeof(chunk)); 220 221 /* This doesn't return anything on success. */ 222 blob->writeBytes(session, offset, chunkBytes); 223 offset += bytesRead; 224 progress->updateProgress(bytesRead); 225 } 226 } while (bytesRead > 0); 227 } 228 catch (const ipmiblob::BlobException& b) 229 { 230 goto exit; 231 } 232 233 /* defaults to failure. */ 234 returnValue = true; 235 236 exit: 237 /* disable ASPEED PCI bridge. */ 238 if (pciDeviceVID == aspeedPciDeviceInfo.VID && 239 pciDeviceDID == aspeedPciDeviceInfo.DID) 240 { 241 disablePciBridge(io, bar); 242 } 243 244 /* close input file. */ 245 if (inputFd != -1) 246 { 247 sys->close(inputFd); 248 } 249 return returnValue; 250 } 251 252 } // namespace host_tool 253