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 if (0 == (value & p2ABridgeEnabled)) 77 { 78 std::fprintf(stderr, "Bridge not enabled.\n"); 79 return false; 80 } 81 } 82 83 std::fprintf(stderr, "The bridge is enabled!\n"); 84 85 /* Read the configuration via blobs metadata (stat). */ 86 ipmiblob::StatResponse stat = blob->getStat(session); 87 if (stat.metadata.size() != sizeof(ipmi_flash::PciConfigResponse)) 88 { 89 std::fprintf(stderr, "Didn't receive expected size of metadata for " 90 "PCI Configuration response\n"); 91 return false; 92 } 93 94 ipmi_flash::PciConfigResponse pciResp; 95 std::memcpy(&pciResp, stat.metadata.data(), sizeof(pciResp)); 96 std::fprintf(stderr, "Received address: 0x%x\n", pciResp.address); 97 98 /* Configure the mmio to point there. */ 99 if (!io->write(bar1 | aspeedP2aBridge, sizeof(pciResp.address), 100 &pciResp.address)) 101 { 102 // Failed to set it up, so fall back. 103 std::fprintf(stderr, "Failed to update the bridge address\n"); 104 return false; 105 } 106 107 /* For data blocks in 64kb, stage data, and send blob write command. */ 108 int inputFd = sys->open(input.c_str(), 0); 109 if (inputFd < 0) 110 { 111 return false; 112 } 113 114 const std::uint32_t p2aLength = aspeedP2aOffset; 115 116 auto readBuffer = std::make_unique<std::uint8_t[]>(p2aLength); 117 if (nullptr == readBuffer) 118 { 119 std::fprintf(stderr, "Unable to allocate memory for read buffer.\n"); 120 return false; 121 } 122 123 try 124 { 125 int bytesRead = 0; 126 std::uint32_t offset = 0; 127 128 do 129 { 130 bytesRead = sys->read(inputFd, readBuffer.get(), p2aLength); 131 if (bytesRead > 0) 132 { 133 /* TODO: Will likely need to store an rv somewhere to know when 134 * we're exiting from failure. 135 */ 136 if (!io->write(bar1 | aspeedP2aOffset, bytesRead, 137 readBuffer.get())) 138 { 139 std::fprintf(stderr, 140 "Failed to write to region in memory!\n"); 141 break; 142 } 143 144 /* Ok, so the data is staged, now send the blob write with the 145 * details. 146 */ 147 struct ipmi_flash::ExtChunkHdr chunk; 148 chunk.length = bytesRead; 149 std::vector<std::uint8_t> chunkBytes(sizeof(chunk)); 150 std::memcpy(chunkBytes.data(), &chunk, sizeof(chunk)); 151 152 /* This doesn't return anything on success. */ 153 blob->writeBytes(session, offset, chunkBytes); 154 offset += bytesRead; 155 } 156 } while (bytesRead > 0); 157 } 158 catch (const ipmiblob::BlobException& b) 159 { 160 sys->close(inputFd); 161 return false; 162 } 163 164 sys->close(inputFd); 165 return true; 166 } 167 168 } // namespace host_tool 169