| /* |
| * Copyright 2019 Google Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "p2a.hpp" |
| |
| #include "firmware_handler.hpp" |
| #include "pci.hpp" |
| #include "pci_handler.hpp" |
| |
| #include <cstring> |
| #include <ipmiblob/blob_errors.hpp> |
| |
| namespace host_tool |
| { |
| |
| bool P2aDataHandler::sendContents(const std::string& input, |
| std::uint16_t session) |
| { |
| PciDevice result; |
| PciFilter filter; |
| bool found = false; |
| pciaddr_t bar1; |
| |
| filter.vid = aspeedVendorId; |
| filter.did = aspeedDeviceId; |
| |
| /* Find the ASPEED PCI device entry we want. */ |
| auto output = pci->getPciDevices(filter); |
| for (const auto& d : output) |
| { |
| std::fprintf(stderr, "[0x%x 0x%x] ", d.vid, d.did); |
| |
| /* Verify it's a memory-based bar -- we want bar1. */ |
| bar1 = d.bars[1]; |
| if ((bar1 & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) |
| { |
| /* We want it to not be IO-based access. */ |
| continue; |
| } |
| |
| /* For now capture the entire device even if we're only using BAR1 */ |
| result = d; |
| found = true; |
| break; |
| } |
| |
| if (!found) |
| { |
| return false; |
| } |
| |
| std::fprintf(stderr, "\n"); |
| |
| /* We sent the open command before this, so the window should be open and |
| * the bridge enabled. |
| */ |
| std::uint32_t value; |
| if (!io->read(bar1 | aspeedP2aConfig, sizeof(value), &value)) |
| { |
| if (0 == (value & p2ABridgeEnabled)) |
| { |
| std::fprintf(stderr, "Bridge not enabled.\n"); |
| return false; |
| } |
| } |
| |
| std::fprintf(stderr, "The bridge is enabled!\n"); |
| |
| /* Read the configuration via blobs metadata (stat). */ |
| ipmiblob::StatResponse stat = blob->getStat(session); |
| if (stat.metadata.size() != sizeof(ipmi_flash::PciConfigResponse)) |
| { |
| std::fprintf(stderr, "Didn't receive expected size of metadata for " |
| "PCI Configuration response\n"); |
| return false; |
| } |
| |
| ipmi_flash::PciConfigResponse pciResp; |
| std::memcpy(&pciResp, stat.metadata.data(), sizeof(pciResp)); |
| std::fprintf(stderr, "Received address: 0x%x\n", pciResp.address); |
| |
| /* Configure the mmio to point there. */ |
| if (!io->write(bar1 | aspeedP2aBridge, sizeof(pciResp.address), |
| &pciResp.address)) |
| { |
| // Failed to set it up, so fall back. |
| std::fprintf(stderr, "Failed to update the bridge address\n"); |
| return false; |
| } |
| |
| /* For data blocks in 64kb, stage data, and send blob write command. */ |
| int inputFd = sys->open(input.c_str(), 0); |
| if (inputFd < 0) |
| { |
| return false; |
| } |
| |
| const std::uint32_t p2aLength = aspeedP2aOffset; |
| |
| auto readBuffer = std::make_unique<std::uint8_t[]>(p2aLength); |
| if (nullptr == readBuffer) |
| { |
| std::fprintf(stderr, "Unable to allocate memory for read buffer.\n"); |
| return false; |
| } |
| |
| try |
| { |
| int bytesRead = 0; |
| std::uint32_t offset = 0; |
| |
| do |
| { |
| bytesRead = sys->read(inputFd, readBuffer.get(), p2aLength); |
| if (bytesRead > 0) |
| { |
| /* TODO: Will likely need to store an rv somewhere to know when |
| * we're exiting from failure. |
| */ |
| if (!io->write(bar1 | aspeedP2aOffset, bytesRead, |
| readBuffer.get())) |
| { |
| std::fprintf(stderr, |
| "Failed to write to region in memory!\n"); |
| break; |
| } |
| |
| /* Ok, so the data is staged, now send the blob write with the |
| * details. |
| */ |
| struct ipmi_flash::ExtChunkHdr chunk; |
| chunk.length = bytesRead; |
| std::vector<std::uint8_t> chunkBytes(sizeof(chunk)); |
| std::memcpy(chunkBytes.data(), &chunk, sizeof(chunk)); |
| |
| /* This doesn't return anything on success. */ |
| blob->writeBytes(session, offset, chunkBytes); |
| offset += bytesRead; |
| } |
| } while (bytesRead > 0); |
| } |
| catch (const ipmiblob::BlobException& b) |
| { |
| sys->close(inputFd); |
| return false; |
| } |
| |
| sys->close(inputFd); |
| return true; |
| } |
| |
| } // namespace host_tool |