xref: /openbmc/google-ipmi-sys/bios_setting.cpp (revision e89296998b00d3e92361fdb37152a18566cf4505)
1  // Copyright 2024 Google LLC
2  //
3  // Licensed under the Apache License, Version 2.0 (the "License");
4  // you may not use this file except in compliance with the License.
5  // You may obtain a copy of the License at
6  //
7  //      http://www.apache.org/licenses/LICENSE-2.0
8  //
9  // Unless required by applicable law or agreed to in writing, software
10  // distributed under the License is distributed on an "AS IS" BASIS,
11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  // See the License for the specific language governing permissions and
13  // limitations under the License.
14  
15  #include "bios_setting.hpp"
16  
17  #include "commands.hpp"
18  #include "errors.hpp"
19  #include "handler.hpp"
20  
21  #include <ipmid/api-types.hpp>
22  #include <stdplus/fd/create.hpp>
23  #include <stdplus/fd/managed.hpp>
24  #include <stdplus/fd/ops.hpp>
25  #include <stdplus/numeric/endian.hpp>
26  #include <stdplus/print.hpp>
27  #include <stdplus/raw.hpp>
28  
29  #include <filesystem>
30  #include <fstream>
31  #include <span>
32  #include <vector>
33  
34  namespace google
35  {
36  namespace ipmi
37  {
38  
readBiosSettingFromFile(const std::string & biosSettingPath)39  std::vector<uint8_t> readBiosSettingFromFile(const std::string& biosSettingPath)
40  {
41      std::vector<uint8_t> biosSettings;
42      try
43      {
44          stdplus::ManagedFd managedFd = stdplus::fd::open(
45              biosSettingPath,
46              stdplus::fd::OpenFlags(stdplus::fd::OpenAccess::ReadOnly));
47          biosSettings = stdplus::fd::readAll<std::vector<uint8_t>>(managedFd);
48      }
49      catch (const std::exception& e)
50      {
51          stdplus::print(stderr, "Read unsuccessful: {}\n", e.what());
52          return {};
53      }
54      return biosSettings;
55  }
56  
readBiosSetting(std::span<const uint8_t>,HandlerInterface *,const std::string & biosSettingPath)57  Resp readBiosSetting(std::span<const uint8_t>, HandlerInterface*,
58                       const std::string& biosSettingPath)
59  {
60      std::vector<uint8_t> biosSettings =
61          readBiosSettingFromFile(biosSettingPath);
62      size_t settingsLength = biosSettings.size();
63      if (settingsLength == 0)
64      {
65          return ::ipmi::responseRetBytesUnavailable();
66      }
67  
68      // Reply format is: Length of the payload (1 byte) + payload
69      std::vector<std::uint8_t> reply;
70      reply.reserve(1 + settingsLength);
71      reply.emplace_back(static_cast<uint8_t>(settingsLength));
72      reply.insert(reply.end(), biosSettings.begin(), biosSettings.end());
73  
74      return ::ipmi::responseSuccess(SysOEMCommands::SysReadBiosSetting, reply);
75  }
76  
writeBiosSetting(std::span<const uint8_t> data,HandlerInterface *,const std::string & biosSettingPath)77  Resp writeBiosSetting(std::span<const uint8_t> data, HandlerInterface*,
78                        const std::string& biosSettingPath)
79  {
80      std::uint8_t payloadSize;
81      try
82      {
83          // This subspans the data automatically
84          payloadSize = stdplus::raw::extract<
85              stdplus::EndianPacked<decltype(payloadSize), std::endian::little>>(
86              data);
87      }
88      catch (const std::exception& e)
89      {
90          stdplus::print(stderr, "Extracting payload failed: {}\n", e.what());
91          return ::ipmi::responseReqDataLenInvalid();
92      }
93  
94      if (data.size() != payloadSize)
95      {
96          stdplus::print(stderr, "Invalid command length {} vs. payloadSize {}\n",
97                         static_cast<uint32_t>(data.size()),
98                         static_cast<uint32_t>(payloadSize));
99          return ::ipmi::responseReqDataLenInvalid();
100      }
101  
102      // Write the setting
103      try
104      {
105          stdplus::ManagedFd managedFd = stdplus::fd::open(
106              biosSettingPath,
107              stdplus::fd::OpenFlags(stdplus::fd::OpenAccess::WriteOnly)
108                  .set(stdplus::fd::OpenFlag::Trunc)
109                  .set(stdplus::fd::OpenFlag::Create));
110          stdplus::fd::writeExact(managedFd, data);
111      }
112      catch (const std::exception& e)
113      {
114          stdplus::print(stderr, "Write unsuccessful: {}\n", e.what());
115          return ::ipmi::responseRetBytesUnavailable();
116      }
117  
118      // Reply format is: Length of the payload written
119      std::vector<std::uint8_t> reply;
120      reply.reserve(1);
121      reply.emplace_back(static_cast<uint8_t>(payloadSize));
122  
123      return ::ipmi::responseSuccess(SysOEMCommands::SysWriteBiosSetting, reply);
124  }
125  
126  } // namespace ipmi
127  } // namespace google
128