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