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