1 extern "C" 2 { 3 #include <libpdbg.h> 4 } 5 6 #include "attributes_info.H" 7 8 #include "extensions/phal/common_utils.hpp" 9 #include "extensions/phal/create_pel.hpp" 10 #include "extensions/phal/phal_error.hpp" 11 #include "util.hpp" 12 13 #include <libekb.H> 14 15 #include <ext_interface.hpp> 16 #include <nlohmann/json.hpp> 17 #include <phosphor-logging/log.hpp> 18 #include <registration.hpp> 19 20 #include <format> 21 22 namespace openpower 23 { 24 namespace phal 25 { 26 27 using namespace phosphor::logging; 28 29 /** 30 * @brief Select BOOT SEEPROM and Measurement SEEPROM(PRIMARY/BACKUP) on POWER 31 * processor position 0/1 depending on boot count before kicking off 32 * the boot. 33 * 34 * @return void 35 */ 36 void selectBootSeeprom() 37 { 38 struct pdbg_target* procTarget; 39 ATTR_BACKUP_SEEPROM_SELECT_Enum bkpSeePromSelect; 40 ATTR_BACKUP_MEASUREMENT_SEEPROM_SELECT_Enum bkpMeaSeePromSelect; 41 42 pdbg_for_each_class_target("proc", procTarget) 43 { 44 if (!isPrimaryProc(procTarget)) 45 { 46 continue; 47 } 48 49 // Choose seeprom side to boot from based on boot count 50 if (getBootCount() > 0) 51 { 52 log<level::INFO>("Setting SBE seeprom side to 0", 53 entry("SBE_SIDE_SELECT=%d", 54 ENUM_ATTR_BACKUP_SEEPROM_SELECT_PRIMARY)); 55 56 bkpSeePromSelect = ENUM_ATTR_BACKUP_SEEPROM_SELECT_PRIMARY; 57 bkpMeaSeePromSelect = 58 ENUM_ATTR_BACKUP_MEASUREMENT_SEEPROM_SELECT_PRIMARY; 59 } 60 else 61 { 62 log<level::INFO>("Setting SBE seeprom side to 1", 63 entry("SBE_SIDE_SELECT=%d", 64 ENUM_ATTR_BACKUP_SEEPROM_SELECT_SECONDARY)); 65 bkpSeePromSelect = ENUM_ATTR_BACKUP_SEEPROM_SELECT_SECONDARY; 66 bkpMeaSeePromSelect = 67 ENUM_ATTR_BACKUP_MEASUREMENT_SEEPROM_SELECT_SECONDARY; 68 } 69 70 // Set the Attribute as per bootcount policy for boot seeprom 71 if (DT_SET_PROP(ATTR_BACKUP_SEEPROM_SELECT, procTarget, 72 bkpSeePromSelect)) 73 { 74 log<level::ERR>( 75 "Attribute [ATTR_BACKUP_SEEPROM_SELECT] set failed"); 76 throw std::runtime_error( 77 "Attribute [ATTR_BACKUP_SEEPROM_SELECT] set failed"); 78 } 79 80 // Set the Attribute as per bootcount policy for measurement seeprom 81 if (DT_SET_PROP(ATTR_BACKUP_MEASUREMENT_SEEPROM_SELECT, procTarget, 82 bkpMeaSeePromSelect)) 83 { 84 log<level::ERR>( 85 "Attribute [ATTR_BACKUP_MEASUREMENT_SEEPROM_SELECT] set " 86 "failed"); 87 throw std::runtime_error( 88 "Attribute [ATTR_BACKUP_MEASUREMENT_SEEPROM_SELECT] set " 89 "failed"); 90 } 91 } 92 } 93 94 /** 95 * @brief Read the HW Level from VPD and set CLK NE termination site 96 * Note any failure in this function will result startHost failure. 97 */ 98 void setClkNETerminationSite() 99 { 100 // Get Motherborad VINI Recored "HW" keyword 101 constexpr auto objPath = 102 "/xyz/openbmc_project/inventory/system/chassis/motherboard"; 103 constexpr auto kwdVpdInf = "com.ibm.ipzvpd.VINI"; 104 constexpr auto hwKwd = "HW"; 105 106 auto bus = sdbusplus::bus::new_default(); 107 108 std::string service = util::getService(bus, objPath, kwdVpdInf); 109 110 auto properties = bus.new_method_call( 111 service.c_str(), objPath, "org.freedesktop.DBus.Properties", "Get"); 112 properties.append(kwdVpdInf); 113 properties.append(hwKwd); 114 115 // Store "HW" Keyword data. 116 std::variant<std::vector<uint8_t>> val; 117 try 118 { 119 auto result = bus.call(properties); 120 result.read(val); 121 } 122 catch (const sdbusplus::exception_t& e) 123 { 124 log<level::ERR>("Get HW Keyword read from VINI Failed"); 125 throw std::runtime_error("Get HW Keyword read from VINI Failed"); 126 } 127 128 auto hwData = std::get<std::vector<uint8_t>>(val); 129 130 //"HW" Keyword size is 2 as per VPD spec. 131 constexpr auto hwKwdSize = 2; 132 if (hwKwdSize != hwData.size()) 133 { 134 log<level::ERR>( 135 std::format("Incorrect VINI records HW Keyword data size({})", 136 hwData.size()) 137 .c_str()); 138 throw std::runtime_error("Incorrect VINI records HW Keyword data size"); 139 } 140 141 log<level::DEBUG>(std::format("VINI Records HW[0]:{} HW[1]:{}", 142 hwData.at(0), hwData.at(1)) 143 .c_str()); 144 145 // VINI Record "HW" keyword's Byte 0's MSB bit indicates 146 // proc or planar type need to choose. 147 constexpr uint8_t SYS_CLK_NE_TERMINATION_ON_MASK = 0x80; 148 149 ATTR_SYS_CLK_NE_TERMINATION_SITE_Type clockTerm = 150 ENUM_ATTR_SYS_CLK_NE_TERMINATION_SITE_PLANAR; 151 152 if (SYS_CLK_NE_TERMINATION_ON_MASK & hwData.at(0)) 153 { 154 clockTerm = ENUM_ATTR_SYS_CLK_NE_TERMINATION_SITE_PROC; 155 } 156 157 // update all the processor attributes 158 struct pdbg_target* procTarget; 159 pdbg_for_each_class_target("proc", procTarget) 160 { 161 if (DT_SET_PROP(ATTR_SYS_CLK_NE_TERMINATION_SITE, procTarget, 162 clockTerm)) 163 { 164 log<level::ERR>( 165 "Attribute ATTR_SYS_CLK_NE_TERMINATION_SITE set failed"); 166 throw std::runtime_error( 167 "Attribute ATTR_SYS_CLK_NE_TERMINATION_SITE set failed"); 168 } 169 } 170 } 171 172 /** 173 * @brief Helper function to create error log (aka PEL) with 174 * procedure callout for the hardware isolation policy 175 * settings failures. 176 * 177 * @param[in] procedureCode - The procedure code to include in the callout 178 * @param[in] priority - The priority for the procedure callout 179 * @param[in] additionalData - The additional data to include in the error log 180 * 181 * @return void 182 */ 183 static void 184 createPELForHwIsolationSettingsErr(const std::string& procedureCode, 185 const std::string& priority, 186 const pel::FFDCData& additionalData) 187 { 188 try 189 { 190 using json = nlohmann::json; 191 using Severity = 192 sdbusplus::xyz::openbmc_project::Logging::server::Entry::Level; 193 194 json jsonCalloutDataList; 195 jsonCalloutDataList = json::array(); 196 json jsonCalloutData; 197 jsonCalloutData["Procedure"] = procedureCode; 198 jsonCalloutData["Priority"] = priority; 199 jsonCalloutDataList.emplace_back(jsonCalloutData); 200 201 openpower::pel::createErrorPEL("org.open_power.PHAL.Error.Boot", 202 jsonCalloutDataList, additionalData, 203 Severity::Error); 204 } 205 catch (const std::exception& e) 206 { 207 // Don't throw exception since the caller might call in the error path 208 // and even we should allow the hardware isolation by default. 209 log<level::ERR>( 210 std::format("Exception [{}], failed to create the error log " 211 "for the hardware isolation policy settings failures.", 212 e.what()) 213 .c_str()); 214 } 215 } 216 217 /** 218 * @brief Helper function to decide the hardware isolation (aka guard) 219 * 220 * @return xyz.openbmc_project.Object.Enable::Enabled value on success 221 * true on failure since hardware isolation feature should be 222 * enabled by default. 223 */ 224 static bool allowHwIsolation() 225 { 226 bool allowHwIsolation{true}; 227 228 constexpr auto hwIsolationPolicyObjPath = 229 "/xyz/openbmc_project/hardware_isolation/allow_hw_isolation"; 230 constexpr auto hwIsolationPolicyIface = "xyz.openbmc_project.Object.Enable"; 231 232 try 233 { 234 auto bus = sdbusplus::bus::new_default(); 235 236 std::string service = util::getService(bus, hwIsolationPolicyObjPath, 237 hwIsolationPolicyIface); 238 239 auto method = 240 bus.new_method_call(service.c_str(), hwIsolationPolicyObjPath, 241 "org.freedesktop.DBus.Properties", "Get"); 242 method.append(hwIsolationPolicyIface, "Enabled"); 243 244 auto reply = bus.call(method); 245 246 std::variant<bool> resp; 247 248 reply.read(resp); 249 250 if (const bool* enabledPropVal = std::get_if<bool>(&resp)) 251 { 252 allowHwIsolation = *enabledPropVal; 253 } 254 else 255 { 256 const auto trace{std::format( 257 "Failed to read the HardwareIsolation policy " 258 "from the path [{}] interface [{}]. Continuing with " 259 "default mode(allow_hw_isolation)", 260 hwIsolationPolicyObjPath, hwIsolationPolicyIface)}; 261 262 log<level::ERR>(trace.c_str()); 263 createPELForHwIsolationSettingsErr("BMC0001", "M", 264 {{"REASON_FOR_PEL", trace}}); 265 } 266 } 267 catch (const sdbusplus::exception_t& e) 268 { 269 const auto trace{std::format( 270 "Exception [{}] to get the HardwareIsolation policy " 271 "from the path [{}] interface [{}]. Continuing with " 272 "default mode (allow_hw_isolation)", 273 e.what(), hwIsolationPolicyObjPath, hwIsolationPolicyIface)}; 274 275 log<level::ERR>(trace.c_str()); 276 createPELForHwIsolationSettingsErr("BMC0001", "M", 277 {{"REASON_FOR_PEL", trace}}); 278 } 279 280 return allowHwIsolation; 281 } 282 283 /** 284 * @brief Starts the self boot engine on POWER processor position 0 285 * to kick off a boot. 286 * @return void 287 */ 288 void startHost(enum ipl_type iplType = IPL_TYPE_NORMAL) 289 { 290 try 291 { 292 phal_init(); 293 ipl_set_type(iplType); 294 295 /** 296 * Don't apply guard records if the HardwareIsolation (aka guard) 297 * the policy is disabled (false). By default, libipl will apply 298 * guard records. 299 */ 300 if (!allowHwIsolation()) 301 { 302 ipl_disable_guard(); 303 } 304 305 if (iplType == IPL_TYPE_NORMAL) 306 { 307 // Update SEEPROM side only for NORMAL boot 308 selectBootSeeprom(); 309 } 310 setClkNETerminationSite(); 311 } 312 catch (const std::exception& ex) 313 { 314 log<level::ERR>("Exception raised during ipl initialisation", 315 entry("EXCEPTION=%s", ex.what())); 316 openpower::pel::createPEL("org.open_power.PHAL.Error.Boot"); 317 openpower::pel::detail::processBootError(false); 318 throw std::runtime_error("IPL initialization failed"); 319 } 320 321 // To clear trace if success 322 openpower::pel::detail::processBootError(true); 323 324 // callback method will be called upon failure which will create the PEL 325 int rc = ipl_run_major(0); 326 if (rc > 0) 327 { 328 log<level::ERR>("step 0 failed to start the host"); 329 throw std::runtime_error("Failed to execute host start boot step"); 330 } 331 } 332 333 /** 334 * @brief Starts the reboot with type memory preserving reboot. 335 * @return void 336 */ 337 void startHostMpReboot() 338 { 339 // set ipl type as mpipl 340 startHost(IPL_TYPE_MPIPL); 341 } 342 343 /** 344 * @brief Starts the normal boot type. 345 * @return void 346 */ 347 void startHostNormal() 348 { 349 startHost(IPL_TYPE_NORMAL); 350 } 351 352 REGISTER_PROCEDURE("startHost", startHostNormal) 353 REGISTER_PROCEDURE("startHostMpReboot", startHostMpReboot) 354 355 } // namespace phal 356 } // namespace openpower 357