1 /** 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 "services.hpp" 18 19 #include "types.hpp" 20 #include "utility.hpp" 21 22 #include <sys/types.h> // for getpid() 23 #include <unistd.h> // for getpid() 24 25 #include <gpiod.hpp> 26 27 #include <exception> 28 29 namespace phosphor::power::sequencer 30 { 31 32 void BMCServices::logError(const std::string& message, Entry::Level severity, 33 std::map<std::string, std::string>& additionalData) 34 { 35 try 36 { 37 // Add PID to AdditionalData 38 additionalData.emplace("_PID", std::to_string(getpid())); 39 40 // If severity is critical, set error as system terminating 41 if (severity == Entry::Level::Critical) 42 { 43 additionalData.emplace("SEVERITY_DETAIL", "SYSTEM_TERM"); 44 } 45 46 auto method = bus.new_method_call( 47 "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging", 48 "xyz.openbmc_project.Logging.Create", "Create"); 49 method.append(message, severity, additionalData); 50 bus.call_noreply(method); 51 } 52 catch (const std::exception& e) 53 { 54 lg2::error("Unable to log error {ERROR}: {EXCEPTION}", "ERROR", message, 55 "EXCEPTION", e); 56 } 57 } 58 59 bool BMCServices::isPresent(const std::string& inventoryPath) 60 { 61 // Initially assume hardware is not present 62 bool present{false}; 63 64 // Try to find cached presence value 65 auto it = presenceCache.find(inventoryPath); 66 if (it != presenceCache.end()) 67 { 68 present = it->second; 69 } 70 else 71 { 72 // Get presence from D-Bus interface/property 73 try 74 { 75 util::getProperty(INVENTORY_IFACE, PRESENT_PROP, inventoryPath, 76 INVENTORY_MGR_IFACE, bus, present); 77 } 78 catch (const sdbusplus::exception_t& e) 79 { 80 // If exception type is expected and indicates hardware not present 81 if (isExpectedException(e)) 82 { 83 present = false; 84 } 85 else 86 { 87 // Re-throw unexpected exception 88 throw; 89 } 90 } 91 92 // Cache presence value 93 presenceCache[inventoryPath] = present; 94 } 95 96 return present; 97 } 98 99 std::vector<int> BMCServices::getGPIOValues(const std::string& chipLabel) 100 { 101 // Set up the chip object 102 gpiod::chip chip{chipLabel, gpiod::chip::OPEN_BY_LABEL}; 103 unsigned int numLines = chip.num_lines(); 104 lg2::info( 105 "Reading GPIO values from chip {NAME} with label {LABEL} and {NUM_LINES} lines", 106 "NAME", chip.name(), "LABEL", chipLabel, "NUM_LINES", numLines); 107 108 // Read GPIO values. Work around libgpiod bulk line maximum by getting 109 // values from individual lines. 110 std::vector<int> values; 111 for (unsigned int offset = 0; offset < numLines; ++offset) 112 { 113 gpiod::line line = chip.get_line(offset); 114 line.request({"phosphor-power-control", 115 gpiod::line_request::DIRECTION_INPUT, 0}); 116 values.push_back(line.get_value()); 117 line.release(); 118 } 119 return values; 120 } 121 122 bool BMCServices::isExpectedException(const sdbusplus::exception_t& e) 123 { 124 // Initially assume exception is not one of the expected types 125 bool isExpected{false}; 126 127 // If the D-Bus error name is set within the exception 128 if (e.name() != nullptr) 129 { 130 // Check if the error name is one of the expected values when hardware 131 // is not present. 132 // 133 // Sometimes the object path does not exist. Sometimes the object path 134 // exists, but it does not implement the D-Bus interface that contains 135 // the present property. Both of these cases result in exceptions. 136 // 137 // In the case where the interface is not implemented, the systemd 138 // documentation seems to indicate that the error name should be 139 // SD_BUS_ERROR_UNKNOWN_INTERFACE. However, in OpenBMC the 140 // SD_BUS_ERROR_UNKNOWN_PROPERTY error name can occur. 141 std::string name = e.name(); 142 if ((name == SD_BUS_ERROR_UNKNOWN_OBJECT) || 143 (name == SD_BUS_ERROR_UNKNOWN_INTERFACE) || 144 (name == SD_BUS_ERROR_UNKNOWN_PROPERTY)) 145 { 146 isExpected = true; 147 } 148 } 149 150 return isExpected; 151 } 152 153 } // namespace phosphor::power::sequencer 154