xref: /openbmc/google-ipmi-sys/cpld.cpp (revision ce07ee0a)
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     if ((*dataLen) < sizeof(struct CpldRequest))
54     {
55         std::fprintf(stderr, "Invalid command length: %u\n",
56                      static_cast<uint32_t>(*dataLen));
57         return IPMI_CC_INVALID;
58     }
59 
60     // reqBuf[0] is the subcommand.
61     // reqBuf[1] is the CPLD id. "/run/cpld{id}.version" is what we read.
62     // Verified that this cast actually returns the value 255 and not something
63     // negative in the case where reqBuf[1] is 0xff.  However, it looks weird
64     // since I would expect int(uint8(0xff)) to be -1.  So, just cast it
65     // unsigned. we're casting to an int width to avoid it thinking it's a
66     // letter, because it does that.
67 
68     const auto request =
69         reinterpret_cast<const struct CpldRequest*>(&reqBuf[0]);
70 
71     std::ostringstream opath;
72     opath << "/run/cpld" << static_cast<unsigned int>(request->id)
73           << ".version";
74     // Check for file
75 
76     std::error_code ec;
77     if (!fs::exists(opath.str(), ec))
78     {
79         std::fprintf(stderr, "Path: '%s' doesn't exist.\n",
80                      opath.str().c_str());
81         return IPMI_CC_INVALID;
82     }
83     // We're uninterested in the state of ec.
84 
85     // If file exists, read.
86     std::ifstream ifs;
87     ifs.exceptions(std::ifstream::failbit);
88     std::string value;
89     try
90     {
91         ifs.open(opath.str());
92         ifs >> value;
93     }
94     catch (std::ios_base::failure& fail)
95     {
96         return IPMI_CC_INVALID;
97     }
98 
99     // If value parses as expected, return version.
100     int major = 0;
101     int minor = 0;
102     int point = 0;
103     int subpoint = 0;
104 
105     int num_fields =
106         sscanf(value.c_str(), "%d.%d.%d.%d", &major, &minor, &point, &subpoint);
107     if (num_fields == 0)
108     {
109         std::fprintf(stderr, "Invalid version.\n");
110         return IPMI_CC_INVALID;
111     }
112 
113     // Truncate if the version is too high (documented).
114     struct CpldReply reply;
115     reply.subcommand = SysCpldVersion;
116     reply.major = static_cast<uint8_t>(major);
117     reply.minor = static_cast<uint8_t>(minor);
118     reply.point = static_cast<uint8_t>(point);
119     reply.subpoint = static_cast<uint8_t>(subpoint);
120 
121     std::memcpy(&replyBuf[0], &reply, sizeof(struct CpldReply));
122     (*dataLen) = sizeof(struct CpldReply);
123 
124     return IPMI_CC_OK;
125 }
126 
127 } // namespace ipmi
128 } // namespace google
129