xref: /openbmc/phosphor-ipmi-flash/tools/p2a.cpp (revision e3feacfa)
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