xref: /openbmc/google-ipmi-sys/cpld.cpp (revision fff98617)
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 "cpld.hpp"
18 
19 #include "main.hpp"
20 
21 #include <cstring>
22 #include <experimental/filesystem>
23 #include <fstream>
24 #include <sstream>
25 
26 namespace google
27 {
28 namespace ipmi
29 {
30 namespace fs = std::experimental::filesystem;
31 
32 struct CpldRequest
33 {
34     uint8_t subcommand;
35     uint8_t id;
36 } __attribute__((packed));
37 
38 struct CpldReply
39 {
40     uint8_t subcommand;
41     uint8_t major;
42     uint8_t minor;
43     uint8_t point;
44     uint8_t subpoint;
45 } __attribute__((packed));
46 
47 //
48 // Handle reading the cpld version from the tmpfs.
49 //
50 ipmi_ret_t CpldVersion(const uint8_t* reqBuf, uint8_t* replyBuf,
51                        size_t* dataLen)
52 {
53     struct CpldRequest request;
54 
55     if ((*dataLen) < sizeof(request))
56     {
57         std::fprintf(stderr, "Invalid command length: %u\n",
58                      static_cast<uint32_t>(*dataLen));
59         return IPMI_CC_REQ_DATA_LEN_INVALID;
60     }
61 
62     // reqBuf[0] is the subcommand.
63     // reqBuf[1] is the CPLD id. "/run/cpld{id}.version" is what we read.
64     // Verified that this cast actually returns the value 255 and not something
65     // negative in the case where reqBuf[1] is 0xff.  However, it looks weird
66     // since I would expect int(uint8(0xff)) to be -1.  So, just cast it
67     // unsigned. we're casting to an int width to avoid it thinking it's a
68     // letter, because it does that.
69     std::memcpy(&request, &reqBuf[0], sizeof(request));
70 
71     std::ostringstream opath;
72     opath << "/run/cpld" << static_cast<unsigned int>(request.id) << ".version";
73     // Check for file
74 
75     std::error_code ec;
76     if (!fs::exists(opath.str(), ec))
77     {
78         std::fprintf(stderr, "Path: '%s' doesn't exist.\n",
79                      opath.str().c_str());
80         return IPMI_CC_INVALID_FIELD_REQUEST;
81     }
82     // We're uninterested in the state of ec.
83 
84     // If file exists, read.
85     std::ifstream ifs;
86     ifs.exceptions(std::ifstream::failbit);
87     std::string value;
88     try
89     {
90         ifs.open(opath.str());
91         ifs >> value;
92     }
93     catch (std::ios_base::failure& fail)
94     {
95         return IPMI_CC_UNSPECIFIED_ERROR;
96     }
97 
98     // If value parses as expected, return version.
99     int major = 0;
100     int minor = 0;
101     int point = 0;
102     int subpoint = 0;
103 
104     int num_fields =
105         sscanf(value.c_str(), "%d.%d.%d.%d", &major, &minor, &point, &subpoint);
106     if (num_fields == 0)
107     {
108         std::fprintf(stderr, "Invalid version.\n");
109         return IPMI_CC_UNSPECIFIED_ERROR;
110     }
111 
112     // Truncate if the version is too high (documented).
113     struct CpldReply reply;
114     reply.subcommand = SysCpldVersion;
115     reply.major = static_cast<uint8_t>(major);
116     reply.minor = static_cast<uint8_t>(minor);
117     reply.point = static_cast<uint8_t>(point);
118     reply.subpoint = static_cast<uint8_t>(subpoint);
119 
120     std::memcpy(&replyBuf[0], &reply, sizeof(reply));
121     (*dataLen) = sizeof(reply);
122 
123     return IPMI_CC_OK;
124 }
125 
126 } // namespace ipmi
127 } // namespace google
128