/* * Copyright 2018 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "cpld.hpp" #include "main.hpp" #include #include #include namespace google { namespace ipmi { namespace fs = std::experimental::filesystem; struct CpldRequest { uint8_t subcommand; uint8_t id; } __attribute__((packed)); struct CpldReply { uint8_t subcommand; uint8_t major; uint8_t minor; uint8_t point; uint8_t subpoint; } __attribute__((packed)); // // Handle reading the cpld version from the tmpfs. // ipmi_ret_t CpldVersion(const uint8_t* reqBuf, uint8_t* replyBuf, size_t* dataLen) { if ((*dataLen) < sizeof(struct CpldRequest)) { fprintf(stderr, "Invalid command length: %u\n", static_cast(*dataLen)); return IPMI_CC_INVALID; } // reqBuf[0] is the subcommand. // reqBuf[1] is the CPLD id. "/run/cpld{id}.version" is what we read. // Verified that this cast actually returns the value 255 and not something // negative in the case where reqBuf[1] is 0xff. However, it looks weird // since I would expect int(uint8(0xff)) to be -1. So, just cast it // unsigned. we're casting to an int width to avoid it thinking it's a // letter, because it does that. const auto request = reinterpret_cast(&reqBuf[0]); std::ostringstream opath; opath << "/run/cpld" << static_cast(request->id) << ".version"; // Check for file std::error_code ec; if (!fs::exists(opath.str(), ec)) { fprintf(stderr, "Path: '%s' doesn't exist.\n", opath.str().c_str()); return IPMI_CC_INVALID; } // We're uninterested in the state of ec. // If file exists, read. std::ifstream ifs; ifs.exceptions(std::ifstream::failbit); std::string value; try { ifs.open(opath.str()); ifs >> value; } catch (std::ios_base::failure& fail) { return IPMI_CC_INVALID; } // If value parses as expected, return version. int major = 0; int minor = 0; int point = 0; int subpoint = 0; int num_fields = sscanf(value.c_str(), "%d.%d.%d.%d", &major, &minor, &point, &subpoint); if (num_fields == 0) { fprintf(stderr, "Invalid version.\n"); return IPMI_CC_INVALID; } // Truncate if the version is too high (documented). struct CpldReply reply; reply.subcommand = SysCpldVersion; reply.major = static_cast(major); reply.minor = static_cast(minor); reply.point = static_cast(point); reply.subpoint = static_cast(subpoint); memcpy(&replyBuf[0], &reply, sizeof(struct CpldReply)); (*dataLen) = sizeof(struct CpldReply); return IPMI_CC_OK; } } // namespace ipmi } // namespace google