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