blob: 9e4a18dc35ea219268651a847238342cfe05b769 [file] [log] [blame]
Shawn McCarney6a957f62024-01-10 16:15:19 -06001/**
2 * Copyright © 2024 IBM 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
17#include "config_file_parser.hpp"
18
19#include "config_file_parser_error.hpp"
Shawn McCarney38f85002025-10-31 17:59:36 -050020#include "json_parser_utils.hpp"
Shawn McCarney4840b252025-11-06 16:06:40 -060021#include "ucd90160_device.hpp"
22#include "ucd90320_device.hpp"
Shawn McCarney6a957f62024-01-10 16:15:19 -060023
Shawn McCarney38f85002025-10-31 17:59:36 -050024#include <cstdint>
Shawn McCarney6a957f62024-01-10 16:15:19 -060025#include <exception>
26#include <fstream>
27#include <optional>
Shawn McCarney38f85002025-10-31 17:59:36 -050028#include <stdexcept>
Shawn McCarney6a957f62024-01-10 16:15:19 -060029
Shawn McCarney38f85002025-10-31 17:59:36 -050030using namespace phosphor::power::json_parser_utils;
31using ConfigFileParserError = phosphor::power::util::ConfigFileParserError;
Shawn McCarneye9144ab2024-05-22 12:34:18 -050032namespace fs = std::filesystem;
Shawn McCarney6a957f62024-01-10 16:15:19 -060033
34namespace phosphor::power::sequencer::config_file_parser
35{
36
Shawn McCarneye9144ab2024-05-22 12:34:18 -050037const std::filesystem::path standardConfigFileDirectory{
38 "/usr/share/phosphor-power-sequencer"};
39
Patrick Williams92261f82025-02-01 08:22:34 -050040std::filesystem::path find(
41 const std::vector<std::string>& compatibleSystemTypes,
42 const std::filesystem::path& configFileDir)
Shawn McCarneye9144ab2024-05-22 12:34:18 -050043{
44 fs::path pathName, possiblePath;
45 std::string fileName;
46
47 for (const std::string& systemType : compatibleSystemTypes)
48 {
49 // Look for file name that is entire system type + ".json"
50 // Example: com.acme.Hardware.Chassis.Model.MegaServer.json
51 fileName = systemType + ".json";
52 possiblePath = configFileDir / fileName;
53 if (fs::is_regular_file(possiblePath))
54 {
55 pathName = possiblePath;
56 break;
57 }
58
59 // Look for file name that is last node of system type + ".json"
60 // Example: MegaServer.json
61 std::string::size_type pos = systemType.rfind('.');
62 if ((pos != std::string::npos) && ((systemType.size() - pos) > 1))
63 {
64 fileName = systemType.substr(pos + 1) + ".json";
65 possiblePath = configFileDir / fileName;
66 if (fs::is_regular_file(possiblePath))
67 {
68 pathName = possiblePath;
69 break;
70 }
71 }
72 }
73
74 return pathName;
75}
76
Shawn McCarney6a957f62024-01-10 16:15:19 -060077std::vector<std::unique_ptr<Rail>> parse(const std::filesystem::path& pathName)
78{
79 try
80 {
81 // Use standard JSON parser to create tree of JSON elements
82 std::ifstream file{pathName};
83 json rootElement = json::parse(file);
84
85 // Parse tree of JSON elements and return corresponding C++ objects
86 return internal::parseRoot(rootElement);
87 }
88 catch (const std::exception& e)
89 {
90 throw ConfigFileParserError{pathName, e.what()};
91 }
92}
93
94namespace internal
95{
96
Shawn McCarneye02aa3c2025-11-10 12:25:54 -060097std::unique_ptr<Chassis> parseChassis(
Shawn McCarney1cd07b82025-11-12 13:29:26 -060098 const json& element,
99 const std::map<std::string, JSONRefWrapper>& chassisTemplates,
Shawn McCarneye02aa3c2025-11-10 12:25:54 -0600100 Services& services)
101{
102 verifyIsObject(element);
103
104 // If chassis object is not using a template, parse properties normally
105 if (!element.contains("template_id"))
106 {
107 bool isChassisTemplate{false};
108 return parseChassisProperties(element, isChassisTemplate, NO_VARIABLES,
109 services);
110 }
111
112 // Parse chassis object that is using a template
113 unsigned int propertyCount{0};
114
115 // Optional comments property; value not stored
116 if (element.contains("comments"))
117 {
118 ++propertyCount;
119 }
120
121 // Required template_id property
122 const json& templateIDElement = getRequiredProperty(element, "template_id");
123 std::string templateID = parseString(templateIDElement);
124 ++propertyCount;
125
126 // Required template_variable_values property
127 const json& variablesElement =
128 getRequiredProperty(element, "template_variable_values");
129 std::map<std::string, std::string> variables =
130 parseVariables(variablesElement);
131 ++propertyCount;
132
133 // Verify no invalid properties exist
134 verifyPropertyCount(element, propertyCount);
135
136 // Get reference to chassis template JSON
137 auto it = chassisTemplates.find(templateID);
138 if (it == chassisTemplates.end())
139 {
140 throw std::invalid_argument{
141 "Invalid chassis template id: " + templateID};
142 }
143 const json& templateElement = it->second.get();
144
145 // Parse properties in template using variable values for this chassis
146 bool isChassisTemplate{true};
147 return parseChassisProperties(templateElement, isChassisTemplate, variables,
148 services);
149}
150
151std::vector<std::unique_ptr<Chassis>> parseChassisArray(
Shawn McCarney1cd07b82025-11-12 13:29:26 -0600152 const json& element,
153 const std::map<std::string, JSONRefWrapper>& chassisTemplates,
Shawn McCarneye02aa3c2025-11-10 12:25:54 -0600154 Services& services)
155{
156 verifyIsArray(element);
157 std::vector<std::unique_ptr<Chassis>> chassis;
158 for (auto& chassisElement : element)
159 {
160 chassis.emplace_back(
161 parseChassis(chassisElement, chassisTemplates, services));
162 }
163 return chassis;
164}
165
166std::unique_ptr<Chassis> parseChassisProperties(
167 const json& element, bool isChassisTemplate,
168 const std::map<std::string, std::string>& variables, Services& services)
169
170{
171 verifyIsObject(element);
172 unsigned int propertyCount{0};
173
174 // Optional comments property; value not stored
175 if (element.contains("comments"))
176 {
177 ++propertyCount;
178 }
179
180 // Required id property if this is a chassis template
181 // Don't parse again; this was already parsed by parseChassisTemplate()
182 if (isChassisTemplate)
183 {
184 getRequiredProperty(element, "id");
185 ++propertyCount;
186 }
187
188 // Required number property
189 const json& numberElement = getRequiredProperty(element, "number");
190 unsigned int number = parseUnsignedInteger(numberElement, variables);
191 if (number < 1)
192 {
193 throw std::invalid_argument{"Invalid chassis number: Must be > 0"};
194 }
195 ++propertyCount;
196
197 // Required inventory_path property
198 const json& inventoryPathElement =
199 getRequiredProperty(element, "inventory_path");
200 std::string inventoryPath =
201 parseString(inventoryPathElement, false, variables);
202 ++propertyCount;
203
204 // Required power_sequencers property
205 const json& powerSequencersElement =
206 getRequiredProperty(element, "power_sequencers");
207 std::vector<std::unique_ptr<PowerSequencerDevice>> powerSequencers =
208 parsePowerSequencerArray(powerSequencersElement, variables, services);
209 ++propertyCount;
210
211 // Verify no invalid properties exist
212 verifyPropertyCount(element, propertyCount);
213
214 return std::make_unique<Chassis>(number, inventoryPath,
215 std::move(powerSequencers));
216}
217
Shawn McCarneyfb7e0932025-11-10 10:23:05 -0600218std::tuple<std::string, JSONRefWrapper> parseChassisTemplate(
219 const json& element)
220{
221 verifyIsObject(element);
222 unsigned int propertyCount{0};
223
224 // Optional comments property; value not stored
225 if (element.contains("comments"))
226 {
227 ++propertyCount;
228 }
229
230 // Required id property
231 const json& idElement = getRequiredProperty(element, "id");
232 std::string id = parseString(idElement);
233 ++propertyCount;
234
235 // Required number property
236 // Just verify it exists; cannot be parsed without variable values
237 getRequiredProperty(element, "number");
238 ++propertyCount;
239
240 // Required inventory_path property
241 // Just verify it exists; cannot be parsed without variable values
242 getRequiredProperty(element, "inventory_path");
243 ++propertyCount;
244
245 // Required power_sequencers property
246 // Just verify it exists; cannot be parsed without variable values
247 getRequiredProperty(element, "power_sequencers");
248 ++propertyCount;
249
250 // Verify no invalid properties exist
251 verifyPropertyCount(element, propertyCount);
252
253 return {id, JSONRefWrapper{element}};
254}
255
Shawn McCarney1cd07b82025-11-12 13:29:26 -0600256std::map<std::string, JSONRefWrapper> parseChassisTemplateArray(
257 const json& element)
258{
259 verifyIsArray(element);
260 std::map<std::string, JSONRefWrapper> chassisTemplates;
261 for (auto& chassisTemplateElement : element)
262 {
263 chassisTemplates.emplace(parseChassisTemplate(chassisTemplateElement));
264 }
265 return chassisTemplates;
266}
267
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600268GPIO parseGPIO(const json& element,
269 const std::map<std::string, std::string>& variables)
Shawn McCarney6a957f62024-01-10 16:15:19 -0600270{
271 verifyIsObject(element);
272 unsigned int propertyCount{0};
273
274 // Required line property
275 const json& lineElement = getRequiredProperty(element, "line");
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600276 unsigned int line = parseUnsignedInteger(lineElement, variables);
Shawn McCarney6a957f62024-01-10 16:15:19 -0600277 ++propertyCount;
278
279 // Optional active_low property
280 bool activeLow{false};
281 auto activeLowIt = element.find("active_low");
282 if (activeLowIt != element.end())
283 {
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600284 activeLow = parseBoolean(*activeLowIt, variables);
Shawn McCarney6a957f62024-01-10 16:15:19 -0600285 ++propertyCount;
286 }
287
288 // Verify no invalid properties exist
289 verifyPropertyCount(element, propertyCount);
290
291 return GPIO(line, activeLow);
292}
293
Shawn McCarneye140ed92025-11-06 11:20:38 -0600294std::tuple<uint8_t, uint16_t> parseI2CInterface(
295 const nlohmann::json& element,
296 const std::map<std::string, std::string>& variables)
297{
298 verifyIsObject(element);
299 unsigned int propertyCount{0};
300
301 // Required bus property
302 const json& busElement = getRequiredProperty(element, "bus");
303 uint8_t bus = parseUint8(busElement, variables);
304 ++propertyCount;
305
306 // Required address property
307 const json& addressElement = getRequiredProperty(element, "address");
308 uint16_t address = parseHexByte(addressElement, variables);
309 ++propertyCount;
310
311 // Verify no invalid properties exist
312 verifyPropertyCount(element, propertyCount);
313
314 return {bus, address};
315}
316
Shawn McCarney4840b252025-11-06 16:06:40 -0600317std::unique_ptr<PowerSequencerDevice> parsePowerSequencer(
318 const nlohmann::json& element,
319 const std::map<std::string, std::string>& variables, Services& services)
320{
321 verifyIsObject(element);
322 unsigned int propertyCount{0};
323
324 // Optional comments property; value not stored
325 if (element.contains("comments"))
326 {
327 ++propertyCount;
328 }
329
330 // Required type property
331 const json& typeElement = getRequiredProperty(element, "type");
332 std::string type = parseString(typeElement, false, variables);
333 ++propertyCount;
334
335 // Required i2c_interface property
336 const json& i2cInterfaceElement =
337 getRequiredProperty(element, "i2c_interface");
338 auto [bus, address] = parseI2CInterface(i2cInterfaceElement, variables);
339 ++propertyCount;
340
341 // Required power_control_gpio_name property
342 const json& powerControlGPIONameElement =
343 getRequiredProperty(element, "power_control_gpio_name");
344 std::string powerControlGPIOName =
345 parseString(powerControlGPIONameElement, false, variables);
346 ++propertyCount;
347
348 // Required power_good_gpio_name property
349 const json& powerGoodGPIONameElement =
350 getRequiredProperty(element, "power_good_gpio_name");
351 std::string powerGoodGPIOName =
352 parseString(powerGoodGPIONameElement, false, variables);
353 ++propertyCount;
354
355 // Required rails property
356 const json& railsElement = getRequiredProperty(element, "rails");
357 std::vector<std::unique_ptr<Rail>> rails =
358 parseRailArray(railsElement, variables);
359 ++propertyCount;
360
361 // Verify no invalid properties exist
362 verifyPropertyCount(element, propertyCount);
363
364 if (type == UCD90160Device::deviceName)
365 {
366 return std::make_unique<UCD90160Device>(
367 bus, address, powerControlGPIOName, powerGoodGPIOName,
368 std::move(rails), services);
369 }
370 else if (type == UCD90320Device::deviceName)
371 {
372 return std::make_unique<UCD90320Device>(
373 bus, address, powerControlGPIOName, powerGoodGPIOName,
374 std::move(rails), services);
375 }
376 throw std::invalid_argument{"Invalid power sequencer type: " + type};
377}
378
379std::vector<std::unique_ptr<PowerSequencerDevice>> parsePowerSequencerArray(
380 const nlohmann::json& element,
381 const std::map<std::string, std::string>& variables, Services& services)
382{
383 verifyIsArray(element);
384 std::vector<std::unique_ptr<PowerSequencerDevice>> powerSequencers;
385 for (auto& powerSequencerElement : element)
386 {
387 powerSequencers.emplace_back(
388 parsePowerSequencer(powerSequencerElement, variables, services));
389 }
390 return powerSequencers;
391}
392
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600393std::unique_ptr<Rail> parseRail(
394 const json& element, const std::map<std::string, std::string>& variables)
Shawn McCarney6a957f62024-01-10 16:15:19 -0600395{
396 verifyIsObject(element);
397 unsigned int propertyCount{0};
398
399 // Required name property
400 const json& nameElement = getRequiredProperty(element, "name");
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600401 std::string name = parseString(nameElement, false, variables);
Shawn McCarney6a957f62024-01-10 16:15:19 -0600402 ++propertyCount;
403
404 // Optional presence property
405 std::optional<std::string> presence{};
406 auto presenceIt = element.find("presence");
407 if (presenceIt != element.end())
408 {
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600409 presence = parseString(*presenceIt, false, variables);
Shawn McCarney6a957f62024-01-10 16:15:19 -0600410 ++propertyCount;
411 }
412
413 // Optional page property
414 std::optional<uint8_t> page{};
415 auto pageIt = element.find("page");
416 if (pageIt != element.end())
417 {
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600418 page = parseUint8(*pageIt, variables);
Shawn McCarney6a957f62024-01-10 16:15:19 -0600419 ++propertyCount;
420 }
421
Shawn McCarney16e493a2024-01-29 14:20:32 -0600422 // Optional is_power_supply_rail property
423 bool isPowerSupplyRail{false};
424 auto isPowerSupplyRailIt = element.find("is_power_supply_rail");
425 if (isPowerSupplyRailIt != element.end())
426 {
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600427 isPowerSupplyRail = parseBoolean(*isPowerSupplyRailIt, variables);
Shawn McCarney16e493a2024-01-29 14:20:32 -0600428 ++propertyCount;
429 }
430
Shawn McCarney6a957f62024-01-10 16:15:19 -0600431 // Optional check_status_vout property
432 bool checkStatusVout{false};
433 auto checkStatusVoutIt = element.find("check_status_vout");
434 if (checkStatusVoutIt != element.end())
435 {
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600436 checkStatusVout = parseBoolean(*checkStatusVoutIt, variables);
Shawn McCarney6a957f62024-01-10 16:15:19 -0600437 ++propertyCount;
438 }
439
Shawn McCarney9ec0d432024-02-09 18:26:00 -0600440 // Optional compare_voltage_to_limit property
441 bool compareVoltageToLimit{false};
442 auto compareVoltageToLimitIt = element.find("compare_voltage_to_limit");
443 if (compareVoltageToLimitIt != element.end())
Shawn McCarney6a957f62024-01-10 16:15:19 -0600444 {
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600445 compareVoltageToLimit =
446 parseBoolean(*compareVoltageToLimitIt, variables);
Shawn McCarney6a957f62024-01-10 16:15:19 -0600447 ++propertyCount;
448 }
449
450 // Optional gpio property
451 std::optional<GPIO> gpio{};
452 auto gpioIt = element.find("gpio");
453 if (gpioIt != element.end())
454 {
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600455 gpio = parseGPIO(*gpioIt, variables);
Shawn McCarney6a957f62024-01-10 16:15:19 -0600456 ++propertyCount;
457 }
458
Shawn McCarney9ec0d432024-02-09 18:26:00 -0600459 // If check_status_vout or compare_voltage_to_limit property is true, the
460 // page property is required; verify page was specified
461 if ((checkStatusVout || compareVoltageToLimit) && !page.has_value())
Shawn McCarney6a957f62024-01-10 16:15:19 -0600462 {
463 throw std::invalid_argument{"Required property missing: page"};
464 }
465
466 // Verify no invalid properties exist
467 verifyPropertyCount(element, propertyCount);
468
Shawn McCarney16e493a2024-01-29 14:20:32 -0600469 return std::make_unique<Rail>(name, presence, page, isPowerSupplyRail,
Shawn McCarney9ec0d432024-02-09 18:26:00 -0600470 checkStatusVout, compareVoltageToLimit, gpio);
Shawn McCarney6a957f62024-01-10 16:15:19 -0600471}
472
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600473std::vector<std::unique_ptr<Rail>> parseRailArray(
474 const json& element, const std::map<std::string, std::string>& variables)
Shawn McCarney6a957f62024-01-10 16:15:19 -0600475{
476 verifyIsArray(element);
477 std::vector<std::unique_ptr<Rail>> rails;
478 for (auto& railElement : element)
479 {
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600480 rails.emplace_back(parseRail(railElement, variables));
Shawn McCarney6a957f62024-01-10 16:15:19 -0600481 }
482 return rails;
483}
484
485std::vector<std::unique_ptr<Rail>> parseRoot(const json& element)
486{
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600487 std::map<std::string, std::string> variables{};
488
Shawn McCarney6a957f62024-01-10 16:15:19 -0600489 verifyIsObject(element);
490 unsigned int propertyCount{0};
491
492 // Required rails property
493 const json& railsElement = getRequiredProperty(element, "rails");
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600494 std::vector<std::unique_ptr<Rail>> rails =
495 parseRailArray(railsElement, variables);
Shawn McCarney6a957f62024-01-10 16:15:19 -0600496 ++propertyCount;
497
498 // Verify no invalid properties exist
499 verifyPropertyCount(element, propertyCount);
500
501 return rails;
502}
503
Shawn McCarneye02aa3c2025-11-10 12:25:54 -0600504std::map<std::string, std::string> parseVariables(const json& element)
505{
506 verifyIsObject(element);
507
508 std::map<std::string, std::string> variables;
509 std::string name, value;
510 for (const auto& [nameElement, valueElement] : element.items())
511 {
512 name = parseString(nameElement);
513 value = parseString(valueElement);
514 variables.emplace(name, value);
515 }
516 return variables;
517}
518
Shawn McCarney6a957f62024-01-10 16:15:19 -0600519} // namespace internal
520
521} // namespace phosphor::power::sequencer::config_file_parser