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