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_t& 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         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             fmt::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{fmt::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{fmt::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