pci: implement sending firmware image over p2a
Implement sending firmware image over p2a via the aspeed-p2a-ctrl
driver.
Test configuration:
# Image is static, uses the PCI bridge, and requires the ASPEED
# PCI-to-AHB hardware implementation.
EXTRA_OECONF_append_quanta-q71l = " --enable-static-layout"
EXTRA_OECONF_append_quanta-q71l = " --enable-pci-bridge"
EXTRA_OECONF_append_quanta-q71l = " --enable-aspeed-p2a"
EXTRA_OECONF_append_quanta-q71l = " MAPPED_ADDRESS=0x47FF0000"
Tested: Verified via md5sum the image-bmc file sent from the host via
this tool matches the hash of /run/initramfs/bmc-image. This code can
be used to send any file down, but was only tested with a "static"
layout build.
Signed-off-by: Patrick Venture <venture@google.com>
Change-Id: I430bc7444f06f4f93a63e3bf2646bd195eaa2e52
diff --git a/tools/p2a.cpp b/tools/p2a.cpp
index 5dcf6c9..dd05ccd 100644
--- a/tools/p2a.cpp
+++ b/tools/p2a.cpp
@@ -16,10 +16,12 @@
#include "p2a.hpp"
+#include "firmware_handler.hpp"
#include "pci.hpp"
#include "pci_handler.hpp"
#include <cstring>
+#include <ipmiblob/blob_errors.hpp>
namespace host_tool
{
@@ -31,6 +33,7 @@
PciUtilImpl pci;
PciFilter filter;
bool found = false;
+ pciaddr_t bar1;
filter.vid = aspeedVendorId;
filter.did = aspeedDeviceId;
@@ -42,13 +45,14 @@
std::fprintf(stderr, "[0x%x 0x%x] ", d.vid, d.did);
/* Verify it's a memory-based bar -- we want bar1. */
- pciaddr_t bar1 = d.bars[1];
+ 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;
@@ -65,7 +69,7 @@
* the bridge enabled.
*/
std::uint32_t value;
- if (!io->read(result.bars[1] | aspeedP2aConfig, sizeof(value), &value))
+ if (!io->read(bar1 | aspeedP2aConfig, sizeof(value), &value))
{
if (0 == (value & p2ABridgeEnabled))
{
@@ -89,16 +93,73 @@
std::memcpy(&pciResp, stat.metadata.data(), sizeof(pciResp));
std::fprintf(stderr, "Received address: 0x%x\n", pciResp.address);
-#if 0
/* Configure the mmio to point there. */
- if (!io->IoWrite(bar | kAspeedP2aBridge, sizeof(phys), &phys)) {
+ 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;
}
-#endif
- 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;
+ }
+
+ int bytesRead = 0;
+ std::uint32_t offset = 0;
+ 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
+ {
+ 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 blobs::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