blob: 76e3e938aeaa6e07f05b6f48b9721b7c5998d9a3 [file] [log] [blame]
Patrick Venturef7186a62018-11-21 14:08:04 -08001/*
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
Patrick Venture38e8c6e2018-10-24 09:23:35 -070017#include "i2c.hpp"
18
19#include <fcntl.h>
20#include <linux/i2c-dev.h>
21#include <linux/i2c.h>
22#include <sys/ioctl.h>
23#include <unistd.h>
24
25#include <array>
26#include <cerrno>
Benjamin Fairb7d43112020-05-20 09:39:11 -070027#include <cstdio>
Patrick Venture38e8c6e2018-10-24 09:23:35 -070028#include <cstring>
Willy Tu87a8b112021-12-13 22:26:56 -080029#include <ipmid/api-types.hpp>
30#include <ipmid/handler.hpp>
William A. Kennington IIId7f368a2019-02-07 14:54:38 -080031#include <ipmid/iana.hpp>
32#include <ipmid/oemopenbmc.hpp>
Patrick Venture38e8c6e2018-10-24 09:23:35 -070033#include <memory>
Willy Tu87a8b112021-12-13 22:26:56 -080034#include <span>
Patrick Venture38e8c6e2018-10-24 09:23:35 -070035
36namespace oem
37{
38namespace i2c
39{
40
41// Instance object.
42std::unique_ptr<I2c> globalOemI2c;
43
44// Block read (I2C_M_RECV_LEN) reads count byte, then that many bytes,
45// then possibly a checksum byte. The specified byte count limit,
46// I2C_SMBUS_BLOCK_MAX, is 32, but it seems intractible to prove
47// every I2C implementation uses that limit. So to prevent overflow,
48// allocate buffers based on the largest possible count byte of 255.
49constexpr size_t maxRecvLenBuf = 1 + 255 + 1;
50typedef std::array<uint8_t, maxRecvLenBuf> BlockBuf;
51
52struct ParsedStep
53{
Willy Tu87a8b112021-12-13 22:26:56 -080054 std::span<const uint8_t> reqData;
Patrick Venture38e8c6e2018-10-24 09:23:35 -070055 size_t length;
56 DevAddr devAddr;
57 bool isRead;
58 // When nonzero, device supplies count for this step, as in SMBUS block
59 // mode. Value will tell driver how many extra bytes to read for entire
60 // block: 1 for count byte w/o PEC, 2 if there is also a PEC byte.
61 uint16_t blockExtra;
62 bool noStart;
63};
64
65struct ParsedReq
66{
67 BusId localbus;
68 bool usePec;
69 size_t numSteps;
70 std::array<ParsedStep, maxSteps> step;
71};
72
Willy Tu87a8b112021-12-13 22:26:56 -080073ipmi::Cc parseReqHdr(std::span<const uint8_t> data, size_t* bytesUsed,
74 i2c::ParsedReq* req)
Patrick Venture38e8c6e2018-10-24 09:23:35 -070075{
76 // Request header selects bus & flags for operation;
77 // additional bytes beyond are to be interpreted as steps.
Willy Tu87a8b112021-12-13 22:26:56 -080078 if (data.size() < *bytesUsed + requestHeaderLen)
Patrick Venture38e8c6e2018-10-24 09:23:35 -070079 {
Willy Tu87a8b112021-12-13 22:26:56 -080080 std::fprintf(stderr, "i2c::parse reqLen=%zu?\n", data.size());
81 return ipmi::ccReqDataLenInvalid;
Patrick Venture38e8c6e2018-10-24 09:23:35 -070082 }
83 // Deserialize request header bytes.
Willy Tu87a8b112021-12-13 22:26:56 -080084 req->localbus = data[requestHeaderBus];
85 auto reqFlags = data[requestHeaderFlags];
Patrick Venture38e8c6e2018-10-24 09:23:35 -070086 *bytesUsed += requestHeaderLen;
87
88 // Decode flags.
89 req->usePec = !!(reqFlags & requestFlagsUsePec);
Willy Tu87a8b112021-12-13 22:26:56 -080090 return ipmi::ccSuccess;
Patrick Venture38e8c6e2018-10-24 09:23:35 -070091}
92
Willy Tu87a8b112021-12-13 22:26:56 -080093ipmi::Cc parseReqStep(std::span<const uint8_t> data, size_t* bytesUsed,
94 i2c::ParsedReq* req)
Patrick Venture38e8c6e2018-10-24 09:23:35 -070095{
Willy Tu87a8b112021-12-13 22:26:56 -080096 size_t bytesLeft = data.size() - *bytesUsed;
Patrick Venture38e8c6e2018-10-24 09:23:35 -070097 if (req->numSteps >= maxSteps || bytesLeft < stepHeaderLen)
98 {
99 std::fprintf(stderr, "i2c::parse[%zu] bytesLeft=%zu?\n", req->numSteps,
100 bytesLeft);
Willy Tu87a8b112021-12-13 22:26:56 -0800101 return ipmi::ccReqDataLenInvalid;
Patrick Venture38e8c6e2018-10-24 09:23:35 -0700102 }
Willy Tu87a8b112021-12-13 22:26:56 -0800103 const std::span<const uint8_t> stepHdr = data.subspan(*bytesUsed);
Patrick Venture38e8c6e2018-10-24 09:23:35 -0700104 auto step = &req->step[req->numSteps++];
105
106 // Deserialize request step header bytes.
Willy Tu87a8b112021-12-13 22:26:56 -0800107 uint8_t devAndDir = data[stepHeaderDevAndDir];
Patrick Venture38e8c6e2018-10-24 09:23:35 -0700108 uint8_t stepFlags = stepHdr[stepHeaderFlags];
109 step->length = stepHdr[stepHeaderParm];
110 bytesLeft -= stepHeaderLen;
111 *bytesUsed += stepHeaderLen;
112
113 // Decode device addr & direction.
114 step->devAddr = devAndDir >> 1;
115 step->isRead = !!(devAndDir & 1);
116
117 // Decode step flags.
118 step->noStart = !!(stepFlags & stepFlagsNoStart);
119
120 if (step->isRead)
121 {
122 // Read could select blockExtra.
123 if (stepFlags & stepFlagsRecvLen)
124 {
125 step->blockExtra = req->usePec ? 2 : 1;
126 }
127 }
128 else
129 {
130 // For write, requested byte count must follow.
131 if (bytesLeft < step->length)
132 {
133 std::fprintf(stderr, "i2c::parse[%zu] bytesLeft=%zu, parm=%zu?\n",
134 req->numSteps, bytesLeft, step->length);
Willy Tu87a8b112021-12-13 22:26:56 -0800135 return ipmi::ccReqDataLenInvalid;
Patrick Venture38e8c6e2018-10-24 09:23:35 -0700136 }
Willy Tu87a8b112021-12-13 22:26:56 -0800137 step->reqData = data.subspan(*bytesUsed);
Patrick Venture38e8c6e2018-10-24 09:23:35 -0700138 *bytesUsed += step->length;
139 }
Willy Tu87a8b112021-12-13 22:26:56 -0800140 return ipmi::ccSuccess;
Patrick Venture38e8c6e2018-10-24 09:23:35 -0700141}
142
143// Parse i2c request.
Willy Tu87a8b112021-12-13 22:26:56 -0800144ipmi::Cc parse(std::span<const uint8_t> data, i2c::ParsedReq* req)
Patrick Venture38e8c6e2018-10-24 09:23:35 -0700145{
146 size_t bytesUsed = 0;
Willy Tu87a8b112021-12-13 22:26:56 -0800147 auto rc = parseReqHdr(data, &bytesUsed, req);
148 if (rc != ipmi::ccSuccess)
Patrick Venture38e8c6e2018-10-24 09:23:35 -0700149 {
150 return rc;
151 }
152 do
153 {
Willy Tu87a8b112021-12-13 22:26:56 -0800154 rc = parseReqStep(data, &bytesUsed, req);
155 if (rc != ipmi::ccSuccess)
Patrick Venture38e8c6e2018-10-24 09:23:35 -0700156 {
157 return rc;
158 }
Willy Tu87a8b112021-12-13 22:26:56 -0800159 } while (bytesUsed < data.size());
160 return ipmi::ccSuccess;
Patrick Venture38e8c6e2018-10-24 09:23:35 -0700161}
162
163// Convert parsed request to I2C messages.
Willy Tu87a8b112021-12-13 22:26:56 -0800164static ipmi::Cc buildI2cMsgs(const i2c::ParsedReq& req,
165 std::unique_ptr<i2c::BlockBuf> rxBuf[],
166 struct i2c_msg msgs[],
167 struct i2c_rdwr_ioctl_data* msgset)
Patrick Venture38e8c6e2018-10-24 09:23:35 -0700168{
169 size_t minReplyLen = 0;
170
171 for (size_t i = 0; i < req.numSteps; ++msgset->nmsgs, ++i)
172 {
173 const auto& step = req.step[i];
174 auto* msg = &msgs[i];
175 msg->addr = step.devAddr;
176
177 if (!step.isRead)
178 {
179 msg->flags = 0;
180 msg->len = step.length;
Willy Tu87a8b112021-12-13 22:26:56 -0800181 msg->buf = const_cast<uint8_t*>(step.reqData.data());
Patrick Venture38e8c6e2018-10-24 09:23:35 -0700182 continue;
183 }
184 rxBuf[i] = std::make_unique<i2c::BlockBuf>();
185 msg->buf = rxBuf[i]->data();
186
187 if (step.blockExtra == 0)
188 {
189 msg->flags = I2C_M_RD;
190 msg->len = step.length;
191 minReplyLen += msg->len;
192 }
193 else
194 {
195 // Special buffer setup needed for block read:
196 // . 1. msg len must allow for maximum possible transfer,
197 // . 2. blockExtra must be preloaded into buf[0]
198 // The internal i2c_transfer API is slightly different;
199 // the rdwr ioctl handler adapts by moving blockExtra
200 // into msg.len where the driver will expect to find it.
201 //
202 // References:
203 // drivers/i2c/i2c-dev.c: i2cdev_ioctl_rdwr()
204 msg->flags = I2C_M_RD | I2C_M_RECV_LEN;
205 msg->len = maxRecvLenBuf;
206 msg->buf[0] = step.blockExtra;
207 minReplyLen += step.blockExtra;
208 }
209 }
210
211 if (minReplyLen > i2c::largestReply)
212 {
213 std::fprintf(stderr, "I2c::transfer minReplyLen=%zu?\n", minReplyLen);
Willy Tu87a8b112021-12-13 22:26:56 -0800214 return ipmi::ccResponseError; // Won't fit in response message
Patrick Venture38e8c6e2018-10-24 09:23:35 -0700215 }
216
217#ifdef __IPMI_DEBUG__
218 for (size_t i = 0; i < req.numSteps; ++i)
219 {
220 auto* msg = &msgs[i];
221 std::fprintf(stderr, "I2c::transfer msg[%zu]: %02x %04x %d\n", i,
222 msg->addr, msg->flags, msg->len);
223 }
224#endif
225
Willy Tu87a8b112021-12-13 22:26:56 -0800226 return ipmi::ccSuccess;
Patrick Venture38e8c6e2018-10-24 09:23:35 -0700227}
228
229static int openBus(BusId localbus)
230{
231 char busCharDev[16];
232 std::snprintf(busCharDev, sizeof(busCharDev) - 1, "/dev/i2c-%d", localbus);
233 int busFd = open(busCharDev, O_RDWR);
234 if (busFd < 0)
235 {
236 std::fprintf(stderr,
237 "NetFn:[0x2E], OEM:[0x002B79], Cmd:[0x02], "
238 "I2C Bus Open(\"%s\"): \"%s\"\n",
239 busCharDev, strerror(-busFd));
240 }
241 return busFd;
242}
243
244} // namespace i2c
245
Willy Tu87a8b112021-12-13 22:26:56 -0800246Resp I2c::transfer(::ipmi::Context::ptr, std::span<const uint8_t> data)
Patrick Venture38e8c6e2018-10-24 09:23:35 -0700247{
248 // Parse message header.
Patrick Venture38e8c6e2018-10-24 09:23:35 -0700249 i2c::ParsedReq req = {};
Willy Tu87a8b112021-12-13 22:26:56 -0800250 auto rc = parse(data, &req);
251 if (rc != ipmi::ccSuccess)
Patrick Venture38e8c6e2018-10-24 09:23:35 -0700252 {
Willy Tu87a8b112021-12-13 22:26:56 -0800253 return ipmi::response(rc);
Patrick Venture38e8c6e2018-10-24 09:23:35 -0700254 }
255
256 // Build full msgset
257 std::unique_ptr<i2c::BlockBuf> rxBuf[i2c::maxSteps];
258 struct i2c_msg msgs[i2c::maxSteps] = {};
259 struct i2c_rdwr_ioctl_data msgset = {
260 .msgs = msgs,
261 .nmsgs = 0,
262 };
263 rc = buildI2cMsgs(req, rxBuf, msgs, &msgset);
Willy Tu87a8b112021-12-13 22:26:56 -0800264 if (rc != ipmi::ccSuccess)
Patrick Venture38e8c6e2018-10-24 09:23:35 -0700265 {
Willy Tu87a8b112021-12-13 22:26:56 -0800266 return ipmi::response(rc);
Patrick Venture38e8c6e2018-10-24 09:23:35 -0700267 }
268
269 // Try to open i2c bus
270 int busFd = i2c::openBus(req.localbus);
271 if (busFd < 0)
272 {
Willy Tu87a8b112021-12-13 22:26:56 -0800273 return ipmi::responseUnspecifiedError();
Patrick Venture38e8c6e2018-10-24 09:23:35 -0700274 }
275 int ioError = ioctl(busFd, I2C_RDWR, &msgset);
276
277 // Done with busFd, so close it immediately to avoid leaking it.
278 (void)close(busFd);
279 if (ioError < 0)
280 {
281 std::fprintf(stderr, "I2c::transfer I2C_RDWR ioError=%d?\n", ioError);
Willy Tu87a8b112021-12-13 22:26:56 -0800282 return ipmi::responseUnspecifiedError(); // I2C_RDWR I/O error
Patrick Venture38e8c6e2018-10-24 09:23:35 -0700283 }
284
285 // If we read any data, append it, in the order we read it.
Willy Tu87a8b112021-12-13 22:26:56 -0800286 std::vector<uint8_t> output;
Patrick Venture38e8c6e2018-10-24 09:23:35 -0700287 size_t replyLen = 0;
288 for (size_t i = 0; i < req.numSteps; ++i)
289 {
290 const auto& step = req.step[i];
291 if (step.isRead)
292 {
293 const auto* msg = &msgs[i];
294 size_t lenRead =
295 step.blockExtra ? *msg->buf + step.blockExtra : msg->len;
296 replyLen += lenRead;
297 if (replyLen > i2c::largestReply)
298 {
299 std::fprintf(stderr, "I2c::transfer[%zu] replyLen=%zu?\n", i,
300 replyLen);
Willy Tu87a8b112021-12-13 22:26:56 -0800301 return ipmi::responseUnspecifiedError(); // Won't fit in
302 // response message
Patrick Venture38e8c6e2018-10-24 09:23:35 -0700303 }
Willy Tu87a8b112021-12-13 22:26:56 -0800304 output.resize(output.size() + lenRead);
305 std::memcpy(output.data() + output.size() - lenRead, msg->buf,
306 lenRead);
Patrick Venture38e8c6e2018-10-24 09:23:35 -0700307 }
308 }
Willy Tu87a8b112021-12-13 22:26:56 -0800309 return ::ipmi::responseSuccess(output);
Patrick Venture38e8c6e2018-10-24 09:23:35 -0700310}
311
Willy Tu87a8b112021-12-13 22:26:56 -0800312void I2c::registerOemRouter()
Patrick Venture38e8c6e2018-10-24 09:23:35 -0700313{
Willy Tu87a8b112021-12-13 22:26:56 -0800314 auto handler = [this](ipmi::Context::ptr ctx, std::vector<uint8_t> data) {
315 return transfer(ctx, data);
Patrick Venture38e8c6e2018-10-24 09:23:35 -0700316 };
317
318 std::fprintf(stderr, "Registering OEM:[%#08X], Cmd:[%#04X] for I2C\n",
319 googOemNumber, Cmd::i2cCmd);
Willy Tu87a8b112021-12-13 22:26:56 -0800320 ipmi::registerOemHandler(::ipmi::prioOemBase, oem::googOemNumber,
321 Cmd::i2cCmd, ::ipmi::Privilege::User, handler);
Patrick Venture38e8c6e2018-10-24 09:23:35 -0700322
323 std::fprintf(stderr, "Registering OEM:[%#08X], Cmd:[%#04X] for I2C\n",
324 obmcOemNumber, Cmd::i2cCmd);
Willy Tu87a8b112021-12-13 22:26:56 -0800325 ipmi::registerOemHandler(::ipmi::prioOemBase, oem::obmcOemNumber,
326 Cmd::i2cCmd, ::ipmi::Privilege::User, handler);
Patrick Venture38e8c6e2018-10-24 09:23:35 -0700327}
328
329namespace i2c
330{
331// Currently ipmid dynamically loads providers such as these;
332// this creates our singleton upon load.
333void setupGlobalOemI2c() __attribute__((constructor));
334
335void setupGlobalOemI2c()
336{
337 globalOemI2c = std::make_unique<I2c>();
Willy Tu87a8b112021-12-13 22:26:56 -0800338 globalOemI2c->registerOemRouter();
Patrick Venture38e8c6e2018-10-24 09:23:35 -0700339}
340} // namespace i2c
341} // namespace oem