xref: /openbmc/bios-settings-mgr/src/manager.cpp (revision 5e2cb7206e695662f09c7cb4e0c73ba11686acaa)
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         // BIOS attribute is read only
128         if (std::get<static_cast<uint8_t>(Index::readOnly)>(iter->second))
129         {
130             phosphor::logging::log<phosphor::logging::level::ERR>(
131                 "BIOS attribute is read only");
132             throw AttributeReadOnly();
133         }
134 
135         auto attributeType =
136             std::get<static_cast<uint8_t>(Index::attributeType)>(iter->second);
137         if (attributeType != std::get<0>(pair.second))
138         {
139             phosphor::logging::log<phosphor::logging::level::ERR>(
140                 "attributeType is not same with bios base table");
141             throw InvalidArgument();
142         }
143 
144         // Validate enumeration BIOS attributes
145         if (attributeType == AttributeType::Enumeration)
146         {
147             // For enumeration the expected variant types is Enumeration
148             if (std::get<1>(pair.second).index() == 0)
149             {
150                 phosphor::logging::log<phosphor::logging::level::ERR>(
151                     "Enumeration property value is not enum");
152                 throw InvalidArgument();
153             }
154 
155             const auto& attrValue =
156                 std::get<std::string>(std::get<1>(pair.second));
157             const auto& options =
158                 std::get<static_cast<uint8_t>(Index::options)>(iter->second);
159 
160             bool found = false;
161             for (const auto& enumOptions : options)
162             {
163                 if ((BoundType::OneOf == std::get<0>(enumOptions)) &&
164                     (attrValue ==
165                      std::get<std::string>(std::get<1>(enumOptions))))
166                 {
167                     found = true;
168                     break;
169                 }
170             }
171 
172             if (!found)
173             {
174                 phosphor::logging::log<phosphor::logging::level::ERR>(
175                     "No valid attribute");
176                 throw InvalidArgument();
177             }
178         }
179 
180         if (attributeType == AttributeType::String)
181         {
182             // For enumeration the expected variant types is std::string
183             if (std::get<1>(pair.second).index() == 0)
184             {
185                 phosphor::logging::log<phosphor::logging::level::ERR>(
186                     "String property value is not string");
187                 throw InvalidArgument();
188             }
189 
190             const auto& attrValue =
191                 std::get<std::string>(std::get<1>(pair.second));
192             const auto& options =
193                 std::get<static_cast<uint8_t>(Index::options)>(iter->second);
194             int64_t minStringLength = 0;
195             int64_t maxStringLength = 0;
196 
197             for (const auto& stringOptions : options)
198             {
199                 if (BoundType::MinStringLength == std::get<0>(stringOptions))
200                 {
201                     minStringLength =
202                         std::get<int64_t>(std::get<1>(stringOptions));
203                 }
204                 else if (BoundType::MaxStringLength ==
205                          std::get<0>(stringOptions))
206                 {
207                     maxStringLength =
208                         std::get<int64_t>(std::get<1>(stringOptions));
209                 }
210             }
211 
212             if ((attrValue.length() < static_cast<size_t>(minStringLength)) ||
213                 (attrValue.length() > static_cast<size_t>(maxStringLength)))
214             {
215                 phosphor::logging::log<phosphor::logging::level::ERR>(
216                     "std::string, length is invalid");
217                 throw InvalidArgument();
218             }
219         }
220 
221         if (attributeType == AttributeType::Integer)
222         {
223             // For enumeration the expected variant types is Integer
224             if (std::get<1>(pair.second).index() == 1)
225             {
226                 phosphor::logging::log<phosphor::logging::level::ERR>(
227                     "Enumeration property value is not int");
228                 throw InvalidArgument();
229             }
230 
231             const auto& attrValue = std::get<int64_t>(std::get<1>(pair.second));
232             const auto& options =
233                 std::get<static_cast<uint8_t>(Index::options)>(iter->second);
234             int64_t lowerBound = 0;
235             int64_t upperBound = 0;
236             int64_t scalarIncrement = 0;
237 
238             for (const auto& integerOptions : options)
239             {
240                 if (BoundType::LowerBound == std::get<0>(integerOptions))
241                 {
242                     lowerBound = std::get<int64_t>(std::get<1>(integerOptions));
243                 }
244                 else if (BoundType::UpperBound == std::get<0>(integerOptions))
245                 {
246                     upperBound = std::get<int64_t>(std::get<1>(integerOptions));
247                 }
248                 else if (BoundType::ScalarIncrement ==
249                          std::get<0>(integerOptions))
250                 {
251                     scalarIncrement =
252                         std::get<int64_t>(std::get<1>(integerOptions));
253                 }
254             }
255 
256             if ((attrValue < lowerBound) || (attrValue > upperBound))
257             {
258                 phosphor::logging::log<phosphor::logging::level::ERR>(
259                     "Integer, bound is invalid");
260                 throw InvalidArgument();
261             }
262 
263             if (((std::abs(attrValue - lowerBound)) % scalarIncrement) != 0)
264             {
265                 phosphor::logging::log<phosphor::logging::level::ERR>(
266                     "((std::abs(attrValue - lowerBound)) % scalarIncrement) != "
267                     "0");
268                 throw InvalidArgument();
269             }
270         }
271     }
272 
273     PendingAttributes pendingAttribute = Base::pendingAttributes();
274 
275     for (const auto& pair : value)
276     {
277         auto iter = pendingAttribute.find(pair.first);
278         if (iter != pendingAttribute.end())
279         {
280             iter = pendingAttribute.erase(iter);
281         }
282 
283         pendingAttribute.emplace(std::make_pair(pair.first, pair.second));
284     }
285 
286     auto pendingAttrs = Base::pendingAttributes(pendingAttribute, false);
287     serialize(*this, biosFile);
288 
289     return pendingAttrs;
290 }
291 
292 Manager::Manager(sdbusplus::asio::object_server& objectServer,
293                  std::shared_ptr<sdbusplus::asio::connection>& systemBus) :
294     sdbusplus::xyz::openbmc_project::BIOSConfig::server::Manager(*systemBus,
295                                                                  objectPath),
296     objServer(objectServer), systemBus(systemBus)
297 {
298     fs::path biosDir(BIOS_PERSIST_PATH);
299     fs::create_directories(biosDir);
300     biosFile = biosDir / biosPersistFile;
301     deserialize(biosFile, *this);
302 }
303 
304 } // namespace bios_config
305 
306 int main()
307 {
308     boost::asio::io_service io;
309     auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
310 
311     systemBus->request_name(bios_config::service);
312     sdbusplus::asio::object_server objectServer(systemBus);
313     bios_config::Manager manager(objectServer, systemBus);
314 
315     io.run();
316     return 0;
317 }
318