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  */
selectBootSeeprom()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  */
setClkNETerminationSite()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  */
createPELForHwIsolationSettingsErr(const std::string & procedureCode,const std::string & priority,const pel::FFDCData & additionalData)183 static void createPELForHwIsolationSettingsErr(
184     const std::string& procedureCode, const std::string& priority,
185     const pel::FFDCData& additionalData)
186 {
187     try
188     {
189         using json = nlohmann::json;
190         using Severity =
191             sdbusplus::xyz::openbmc_project::Logging::server::Entry::Level;
192 
193         json jsonCalloutDataList;
194         jsonCalloutDataList = json::array();
195         json jsonCalloutData;
196         jsonCalloutData["Procedure"] = procedureCode;
197         jsonCalloutData["Priority"] = priority;
198         jsonCalloutDataList.emplace_back(jsonCalloutData);
199 
200         openpower::pel::createErrorPEL("org.open_power.PHAL.Error.Boot",
201                                        jsonCalloutDataList, additionalData,
202                                        Severity::Error);
203     }
204     catch (const std::exception& e)
205     {
206         // Don't throw exception since the caller might call in the error path
207         // and even we should allow the hardware isolation by default.
208         log<level::ERR>(
209             std::format("Exception [{}], failed to create the error log "
210                         "for the hardware isolation policy settings failures.",
211                         e.what())
212                 .c_str());
213     }
214 }
215 
216 /**
217  * @brief Helper function to decide the hardware isolation (aka guard)
218  *
219  * @return xyz.openbmc_project.Object.Enable::Enabled value on success
220  *         true on failure since hardware isolation feature should be
221  *         enabled by default.
222  */
allowHwIsolation()223 static bool allowHwIsolation()
224 {
225     bool allowHwIsolation{true};
226 
227     constexpr auto hwIsolationPolicyObjPath =
228         "/xyz/openbmc_project/hardware_isolation/allow_hw_isolation";
229     constexpr auto hwIsolationPolicyIface = "xyz.openbmc_project.Object.Enable";
230 
231     try
232     {
233         auto bus = sdbusplus::bus::new_default();
234 
235         std::string service = util::getService(bus, hwIsolationPolicyObjPath,
236                                                hwIsolationPolicyIface);
237 
238         auto method =
239             bus.new_method_call(service.c_str(), hwIsolationPolicyObjPath,
240                                 "org.freedesktop.DBus.Properties", "Get");
241         method.append(hwIsolationPolicyIface, "Enabled");
242 
243         auto reply = bus.call(method);
244 
245         std::variant<bool> resp;
246 
247         reply.read(resp);
248 
249         if (const bool* enabledPropVal = std::get_if<bool>(&resp))
250         {
251             allowHwIsolation = *enabledPropVal;
252         }
253         else
254         {
255             const auto trace{std::format(
256                 "Failed to read the HardwareIsolation policy "
257                 "from the path [{}] interface [{}]. Continuing with "
258                 "default mode(allow_hw_isolation)",
259                 hwIsolationPolicyObjPath, hwIsolationPolicyIface)};
260 
261             log<level::ERR>(trace.c_str());
262             createPELForHwIsolationSettingsErr("BMC0001", "M",
263                                                {{"REASON_FOR_PEL", trace}});
264         }
265     }
266     catch (const sdbusplus::exception_t& e)
267     {
268         const auto trace{std::format(
269             "Exception [{}] to get the HardwareIsolation policy "
270             "from the path [{}] interface [{}]. Continuing with "
271             "default mode (allow_hw_isolation)",
272             e.what(), hwIsolationPolicyObjPath, hwIsolationPolicyIface)};
273 
274         log<level::ERR>(trace.c_str());
275         createPELForHwIsolationSettingsErr("BMC0001", "M",
276                                            {{"REASON_FOR_PEL", trace}});
277     }
278 
279     return allowHwIsolation;
280 }
281 
282 /**
283  * @brief Starts the self boot engine on POWER processor position 0
284  *        to kick off a boot.
285  * @return void
286  */
startHost(enum ipl_type iplType=IPL_TYPE_NORMAL)287 void startHost(enum ipl_type iplType = IPL_TYPE_NORMAL)
288 {
289     try
290     {
291         phal_init();
292         ipl_set_type(iplType);
293 
294         /**
295          * Don't apply guard records if the HardwareIsolation (aka guard)
296          * the policy is disabled (false). By default, libipl will apply
297          * guard records.
298          */
299         if (!allowHwIsolation())
300         {
301             ipl_disable_guard();
302         }
303 
304         if (iplType == IPL_TYPE_NORMAL)
305         {
306             // Update SEEPROM side only for NORMAL boot
307             selectBootSeeprom();
308         }
309         setClkNETerminationSite();
310     }
311     catch (const std::exception& ex)
312     {
313         log<level::ERR>("Exception raised during ipl initialisation",
314                         entry("EXCEPTION=%s", ex.what()));
315         openpower::pel::createPEL("org.open_power.PHAL.Error.Boot");
316         openpower::pel::detail::processBootError(false);
317         throw std::runtime_error("IPL initialization failed");
318     }
319 
320     // To clear trace if success
321     openpower::pel::detail::processBootError(true);
322 
323     // callback method will be called upon failure which will create the PEL
324     int rc = ipl_run_major(0);
325     if (rc > 0)
326     {
327         log<level::ERR>("step 0 failed to start the host");
328         throw std::runtime_error("Failed to execute host start boot step");
329     }
330 }
331 
332 /**
333  * @brief Starts the reboot with type memory preserving reboot.
334  * @return void
335  */
startHostMpReboot()336 void startHostMpReboot()
337 {
338     // set ipl type as mpipl
339     startHost(IPL_TYPE_MPIPL);
340 }
341 
342 /**
343  * @brief Starts the normal boot type.
344  * @return void
345  */
startHostNormal()346 void startHostNormal()
347 {
348     startHost(IPL_TYPE_NORMAL);
349 }
350 
351 REGISTER_PROCEDURE("startHost", startHostNormal)
352 REGISTER_PROCEDURE("startHostMpReboot", startHostMpReboot)
353 
354 } // namespace phal
355 } // namespace openpower
356