xref: /openbmc/phosphor-ipmi-flash/tools/lpc.cpp (revision c04c2c5c)
1 /*
2  * Copyright 2018 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 "lpc.hpp"
18 
19 #include "data.hpp"
20 
21 #include <ipmiblob/blob_errors.hpp>
22 
23 #include <cerrno>
24 #include <cstdint>
25 #include <cstring>
26 #include <memory>
27 #include <string>
28 
29 namespace host_tool
30 {
31 
32 bool LpcDataHandler::sendContents(const std::string& input,
33                                   std::uint16_t session)
34 {
35     LpcRegion host_lpc_buf;
36     host_lpc_buf.address = address;
37     host_lpc_buf.length = length;
38 
39     std::vector<std::uint8_t> payload(sizeof(host_lpc_buf));
40 
41     while (true)
42     {
43         /* If the writeMeta() is rejected we need to call sessionStat on it. */
44         try
45         {
46             std::fprintf(stderr, "sending writeMeta\n");
47 
48             std::memcpy(payload.data(), &host_lpc_buf, sizeof(host_lpc_buf));
49             blob->writeMeta(session, 0x00, payload);
50 
51             std::fprintf(stderr, "writemeta sent\n");
52 
53             break;
54         }
55         catch (...)
56         {
57             std::fprintf(stderr, "caught exception\n");
58 
59             ipmiblob::StatResponse resp = blob->getStat(session);
60             if (resp.metadata.empty())
61             {
62                 std::fprintf(stderr, "Received no metadata bytes back!");
63                 return false;
64             }
65 
66             struct MemoryMapResultDetails
67             {
68                 std::uint8_t code;
69                 std::uint32_t offset;
70                 std::uint32_t length;
71             } __attribute__((packed));
72 
73             struct MemoryMapResultDetails bytes;
74 
75             if (resp.metadata.size() != sizeof(bytes))
76             {
77                 std::fprintf(
78                     stderr,
79                     "Received insufficient bytes back on expected return!\n");
80                 return false;
81             }
82 
83             std::memcpy(&bytes, resp.metadata.data(), sizeof(bytes));
84 
85             if (bytes.code == EFBIG)
86             {
87                 std::fprintf(stderr, "EFBIG returned!\n");
88 
89                 host_lpc_buf.length = bytes.length;
90                 host_lpc_buf.address += bytes.offset;
91             }
92             else if (bytes.code == 0)
93             {
94                 /* We're good, continue! */
95                 break;
96             }
97         }
98     }
99 
100     /* For data blockss, stage data, and send blob write command. */
101     int inputFd = sys->open(input.c_str(), 0);
102     if (inputFd < 0)
103     {
104         return false;
105     }
106 
107     std::int64_t fileSize = sys->getSize(input.c_str());
108     if (fileSize == 0)
109     {
110         std::fprintf(stderr, "Zero-length file, or other file access error\n");
111         return false;
112     }
113 
114     progress->start(fileSize);
115 
116     /* For Nuvoton the maximum is 4K */
117     auto readBuffer = std::make_unique<std::uint8_t[]>(host_lpc_buf.length);
118     if (nullptr == readBuffer)
119     {
120         sys->close(inputFd);
121         std::fprintf(stderr, "Unable to allocate memory for read buffer.\n");
122         return false;
123     }
124 
125     /* TODO: This is similar to PCI insomuch as how it sends data, so combine.
126      */
127     try
128     {
129         int bytesRead = 0;
130         std::uint32_t offset = 0;
131 
132         do
133         {
134             bytesRead =
135                 sys->read(inputFd, readBuffer.get(), host_lpc_buf.length);
136             if (bytesRead > 0)
137             {
138                 if (!io->write(host_lpc_buf.address, bytesRead,
139                                readBuffer.get()))
140                 {
141                     std::fprintf(stderr,
142                                  "Failed to write to region in memory!\n");
143                 }
144 
145                 struct ipmi_flash::ExtChunkHdr chunk;
146                 chunk.length = bytesRead;
147                 std::vector<std::uint8_t> chunkBytes(sizeof(chunk));
148                 std::memcpy(chunkBytes.data(), &chunk, sizeof(chunk));
149 
150                 /* This doesn't return anything on success. */
151                 blob->writeBytes(session, offset, chunkBytes);
152                 offset += bytesRead;
153                 progress->updateProgress(bytesRead);
154             }
155         } while (bytesRead > 0);
156     }
157     catch (const ipmiblob::BlobException& b)
158     {
159         sys->close(inputFd);
160         return false;
161     }
162 
163     sys->close(inputFd);
164     return true;
165 }
166 
167 } // namespace host_tool
168