xref: /openbmc/bios-settings-mgr/src/manager.cpp (revision 2f7ba73a)
1 /*
2 // Copyright (c) 2020 Intel Corporation
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 #include "manager.hpp"
17 
18 #include "manager_serialize.hpp"
19 #include "xyz/openbmc_project/BIOSConfig/Common/error.hpp"
20 #include "xyz/openbmc_project/Common/error.hpp"
21 
22 #include <boost/asio.hpp>
23 #include <phosphor-logging/elog-errors.hpp>
24 #include <sdbusplus/asio/connection.hpp>
25 #include <sdbusplus/asio/object_server.hpp>
26 
27 namespace bios_config
28 {
29 
30 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
31 using namespace sdbusplus::xyz::openbmc_project::BIOSConfig::Common::Error;
32 
33 void Manager::setAttribute(AttributeName attribute, AttributeValue value)
34 {
35     auto pendingAttrs = Base::pendingAttributes();
36     auto iter = pendingAttrs.find(attribute);
37 
38     if (iter != pendingAttrs.end())
39     {
40         std::get<1>(iter->second) = value;
41     }
42     else
43     {
44         Manager::PendingAttribute attributeValue;
45 
46         if (std::get_if<int64_t>(&value))
47         {
48             std::get<0>(attributeValue) = AttributeType::Integer;
49         }
50         else
51         {
52             std::get<0>(attributeValue) = AttributeType::String;
53         }
54 
55         std::get<1>(attributeValue) = value;
56         pendingAttrs.emplace(attribute, attributeValue);
57     }
58 
59     pendingAttributes(pendingAttrs);
60 }
61 
62 Manager::AttributeDetails Manager::getAttribute(AttributeName attribute)
63 {
64     Manager::AttributeDetails value;
65 
66     auto table = Base::baseBIOSTable();
67     auto iter = table.find(attribute);
68 
69     if (iter != table.end())
70     {
71         std::get<0>(value) =
72             std::get<static_cast<uint8_t>(Index::attributeType)>(iter->second);
73         std::get<1>(value) =
74             std::get<static_cast<uint8_t>(Index::currentValue)>(iter->second);
75 
76         auto pending = Base::pendingAttributes();
77         auto pendingIter = pending.find(attribute);
78         if (pendingIter != pending.end())
79         {
80             std::get<2>(value) = std::get<1>(pendingIter->second);
81         }
82         else if (std::get_if<std::string>(&std::get<1>(value)))
83         {
84             std::get<2>(value) = std::string();
85         }
86     }
87     else
88     {
89         throw AttributeNotFound();
90     }
91 
92     return value;
93 }
94 
95 Manager::BaseTable Manager::baseBIOSTable(BaseTable value)
96 {
97     pendingAttributes({});
98     auto baseTable = Base::baseBIOSTable(value, false);
99     serialize(*this, biosFile);
100     Base::resetBIOSSettings(Base::ResetFlag::NoAction);
101     return baseTable;
102 }
103 
104 Manager::PendingAttributes Manager::pendingAttributes(PendingAttributes value)
105 {
106     // Clear the pending attributes
107     if (value.empty())
108     {
109         auto pendingAttrs = Base::pendingAttributes({}, false);
110         serialize(*this, biosFile);
111         return pendingAttrs;
112     }
113 
114     // Validate all the BIOS attributes before setting PendingAttributes
115     BaseTable biosTable = Base::baseBIOSTable();
116     for (const auto& pair : value)
117     {
118         auto iter = biosTable.find(pair.first);
119         // BIOS attribute not found in the BaseBIOSTable
120         if (iter == biosTable.end())
121         {
122             phosphor::logging::log<phosphor::logging::level::ERR>(
123                 "BIOS attribute not found in the BaseBIOSTable");
124             throw AttributeNotFound();
125         }
126 
127         auto attributeType =
128             std::get<static_cast<uint8_t>(Index::attributeType)>(iter->second);
129         if (attributeType != std::get<0>(pair.second))
130         {
131             phosphor::logging::log<phosphor::logging::level::ERR>(
132                 "attributeType is not same with bios base table");
133             throw InvalidArgument();
134         }
135 
136         // Validate enumeration BIOS attributes
137         if (attributeType == AttributeType::Enumeration)
138         {
139             // For enumeration the expected variant types is Enumeration
140             if (std::get<1>(pair.second).index() == 0)
141             {
142                 phosphor::logging::log<phosphor::logging::level::ERR>(
143                     "Enumeration property value is not enum");
144                 throw InvalidArgument();
145             }
146 
147             const auto& attrValue =
148                 std::get<std::string>(std::get<1>(pair.second));
149             const auto& options =
150                 std::get<static_cast<uint8_t>(Index::options)>(iter->second);
151 
152             bool found = false;
153             for (const auto& enumOptions : options)
154             {
155                 if ((BoundType::OneOf == std::get<0>(enumOptions)) &&
156                     (attrValue ==
157                      std::get<std::string>(std::get<1>(enumOptions))))
158                 {
159                     found = true;
160                     break;
161                 }
162             }
163 
164             if (!found)
165             {
166                 phosphor::logging::log<phosphor::logging::level::ERR>(
167                     "No valid attribute");
168                 throw InvalidArgument();
169             }
170         }
171 
172         if (attributeType == AttributeType::String)
173         {
174             // For enumeration the expected variant types is std::string
175             if (std::get<1>(pair.second).index() == 0)
176             {
177                 phosphor::logging::log<phosphor::logging::level::ERR>(
178                     "String property value is not string");
179                 throw InvalidArgument();
180             }
181 
182             const auto& attrValue =
183                 std::get<std::string>(std::get<1>(pair.second));
184             const auto& options =
185                 std::get<static_cast<uint8_t>(Index::options)>(iter->second);
186 
187             auto optionsIterator = options.begin();
188 
189             for (; optionsIterator != options.end(); ++optionsIterator)
190             {
191                 if (std::get<1>(std::get<1>(*optionsIterator)) == attrValue)
192                 {
193                     break;
194                 }
195             }
196 
197             if (optionsIterator == options.end())
198             {
199                 std::string error =
200                     attrValue + " is not a valid value for " + pair.first;
201                 phosphor::logging::log<phosphor::logging::level::ERR>(
202                     error.c_str());
203                 throw InvalidArgument();
204             }
205         }
206 
207         if (attributeType == AttributeType::Integer)
208         {
209             // For enumeration the expected variant types is Integer
210             if (std::get<1>(pair.second).index() == 1)
211             {
212                 phosphor::logging::log<phosphor::logging::level::ERR>(
213                     "Enumeration property value is not int");
214                 throw InvalidArgument();
215             }
216 
217             const auto& attrValue = std::get<int64_t>(std::get<1>(pair.second));
218             const auto& options =
219                 std::get<static_cast<uint8_t>(Index::options)>(iter->second);
220             int64_t lowerBound = 0;
221             int64_t upperBound = 0;
222             int64_t scalarIncrement = 0;
223 
224             for (const auto& integerOptions : options)
225             {
226                 if (BoundType::LowerBound == std::get<0>(integerOptions))
227                 {
228                     lowerBound = std::get<int64_t>(std::get<1>(integerOptions));
229                 }
230                 else if (BoundType::UpperBound == std::get<0>(integerOptions))
231                 {
232                     upperBound = std::get<int64_t>(std::get<1>(integerOptions));
233                 }
234                 else if (BoundType::ScalarIncrement ==
235                          std::get<0>(integerOptions))
236                 {
237                     scalarIncrement =
238                         std::get<int64_t>(std::get<1>(integerOptions));
239                 }
240             }
241 
242             if ((attrValue < lowerBound) || (attrValue > upperBound))
243             {
244                 phosphor::logging::log<phosphor::logging::level::ERR>(
245                     "Integer, bound is invalid");
246                 throw InvalidArgument();
247             }
248 
249             if (((std::abs(attrValue - lowerBound)) % scalarIncrement) != 0)
250             {
251                 phosphor::logging::log<phosphor::logging::level::ERR>(
252                     "((std::abs(attrValue - lowerBound)) % scalarIncrement) != "
253                     "0");
254                 throw InvalidArgument();
255             }
256         }
257     }
258 
259     PendingAttributes pendingAttribute = Base::pendingAttributes();
260 
261     for (const auto& pair : value)
262     {
263         auto iter = pendingAttribute.find(pair.first);
264         if (iter != pendingAttribute.end())
265         {
266             iter = pendingAttribute.erase(iter);
267         }
268 
269         pendingAttribute.emplace(std::make_pair(pair.first, pair.second));
270     }
271 
272     auto pendingAttrs = Base::pendingAttributes(pendingAttribute, false);
273     serialize(*this, biosFile);
274 
275     return pendingAttrs;
276 }
277 
278 Manager::Manager(sdbusplus::asio::object_server& objectServer,
279                  std::shared_ptr<sdbusplus::asio::connection>& systemBus) :
280     sdbusplus::xyz::openbmc_project::BIOSConfig::server::Manager(*systemBus,
281                                                                  objectPath),
282     objServer(objectServer), systemBus(systemBus)
283 {
284     fs::path biosDir(BIOS_PERSIST_PATH);
285     fs::create_directories(biosDir);
286     biosFile = biosDir / biosPersistFile;
287     deserialize(biosFile, *this);
288 }
289 
290 } // namespace bios_config
291 
292 int main()
293 {
294     boost::asio::io_service io;
295     auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
296 
297     systemBus->request_name(bios_config::service);
298     sdbusplus::asio::object_server objectServer(systemBus);
299     bios_config::Manager manager(objectServer, systemBus);
300 
301     io.run();
302     return 0;
303 }
304