1 extern "C"
2 {
3 #include <libpdbg.h>
4 }
5 
6 #include "attributes_info.H"
7 
8 #include "phalerror/phal_error.hpp"
9 #include "procedures/phal/common_utils.hpp"
10 #include "util.hpp"
11 
12 #include <fmt/format.h>
13 #include <libekb.H>
14 
15 #include <ext_interface.hpp>
16 #include <phosphor-logging/log.hpp>
17 #include <registration.hpp>
18 
19 namespace openpower
20 {
21 namespace phal
22 {
23 
24 using namespace phosphor::logging;
25 
26 /**
27  *  @brief  Check if master processor or not
28  *
29  *  @return True/False
30  */
31 bool isMasterProc(struct pdbg_target* procTarget)
32 {
33     ATTR_PROC_MASTER_TYPE_Type type;
34 
35     // Get processor type (Master or Alt-master)
36     if (DT_GET_PROP(ATTR_PROC_MASTER_TYPE, procTarget, type))
37     {
38         log<level::ERR>("Attribute [ATTR_PROC_MASTER_TYPE] get failed");
39         throw std::runtime_error(
40             "Attribute [ATTR_PROC_MASTER_TYPE] get failed");
41     }
42 
43     /* Attribute value 0 corresponds to master processor */
44     if (type == 0)
45     {
46         return true;
47     }
48     else
49     {
50         return false;
51     }
52 }
53 
54 /**
55  *  @brief  Select BOOT SEEPROM and Measurement SEEPROM(PRIMARY/BACKUP) on POWER
56  *          processor position 0/1 depending on boot count before kicking off
57  *          the boot.
58  *
59  *  @return void
60  */
61 void selectBootSeeprom()
62 {
63     struct pdbg_target* procTarget;
64     ATTR_BACKUP_SEEPROM_SELECT_Enum bkpSeePromSelect;
65     ATTR_BACKUP_MEASUREMENT_SEEPROM_SELECT_Enum bkpMeaSeePromSelect;
66 
67     pdbg_for_each_class_target("proc", procTarget)
68     {
69         if (!isMasterProc(procTarget))
70         {
71             continue;
72         }
73 
74         // Choose seeprom side to boot from based on boot count
75         if (getBootCount() > 0)
76         {
77             log<level::INFO>("Setting SBE seeprom side to 0",
78                              entry("SBE_SIDE_SELECT=%d",
79                                    ENUM_ATTR_BACKUP_SEEPROM_SELECT_PRIMARY));
80 
81             bkpSeePromSelect = ENUM_ATTR_BACKUP_SEEPROM_SELECT_PRIMARY;
82             bkpMeaSeePromSelect =
83                 ENUM_ATTR_BACKUP_MEASUREMENT_SEEPROM_SELECT_PRIMARY;
84         }
85         else
86         {
87             log<level::INFO>("Setting SBE seeprom side to 1",
88                              entry("SBE_SIDE_SELECT=%d",
89                                    ENUM_ATTR_BACKUP_SEEPROM_SELECT_SECONDARY));
90             bkpSeePromSelect = ENUM_ATTR_BACKUP_SEEPROM_SELECT_SECONDARY;
91             bkpMeaSeePromSelect =
92                 ENUM_ATTR_BACKUP_MEASUREMENT_SEEPROM_SELECT_SECONDARY;
93         }
94 
95         // Set the Attribute as per bootcount policy for boot seeprom
96         if (DT_SET_PROP(ATTR_BACKUP_SEEPROM_SELECT, procTarget,
97                         bkpSeePromSelect))
98         {
99             log<level::ERR>(
100                 "Attribute [ATTR_BACKUP_SEEPROM_SELECT] set failed");
101             throw std::runtime_error(
102                 "Attribute [ATTR_BACKUP_SEEPROM_SELECT] set failed");
103         }
104 
105         // Set the Attribute as per bootcount policy for measurement seeprom
106         if (DT_SET_PROP(ATTR_BACKUP_MEASUREMENT_SEEPROM_SELECT, procTarget,
107                         bkpMeaSeePromSelect))
108         {
109             log<level::ERR>(
110                 "Attribute [ATTR_BACKUP_MEASUREMENT_SEEPROM_SELECT] set "
111                 "failed");
112             throw std::runtime_error(
113                 "Attribute [ATTR_BACKUP_MEASUREMENT_SEEPROM_SELECT] set "
114                 "failed");
115         }
116     }
117 }
118 
119 /**
120  * @brief Read the HW Level from VPD and set CLK NE termination site
121  * Note any failure in this function will result startHost failure.
122  */
123 void setClkNETerminationSite()
124 {
125     // Get Motherborad VINI Recored "HW" keyword
126     constexpr auto objPath =
127         "/xyz/openbmc_project/inventory/system/chassis/motherboard";
128     constexpr auto kwdVpdInf = "com.ibm.ipzvpd.VINI";
129     constexpr auto hwKwd = "HW";
130 
131     auto bus = sdbusplus::bus::new_default();
132 
133     std::string service = util::getService(bus, objPath, kwdVpdInf);
134 
135     auto properties = bus.new_method_call(
136         service.c_str(), objPath, "org.freedesktop.DBus.Properties", "Get");
137     properties.append(kwdVpdInf);
138     properties.append(hwKwd);
139 
140     // Store "HW" Keyword data.
141     std::variant<std::vector<uint8_t>> val;
142     try
143     {
144         auto result = bus.call(properties);
145         result.read(val);
146     }
147     catch (const sdbusplus::exception::SdBusError& e)
148     {
149         log<level::ERR>("Get HW Keyword read from VINI Failed");
150         throw std::runtime_error("Get HW Keyword read from VINI Failed");
151     }
152 
153     auto hwData = std::get<std::vector<uint8_t>>(val);
154 
155     //"HW" Keyword size is 2 as per VPD spec.
156     constexpr auto hwKwdSize = 2;
157     if (hwKwdSize != hwData.size())
158     {
159         log<level::ERR>(
160             fmt::format("Incorrect VINI records HW Keyword data size({})",
161                         hwData.size())
162                 .c_str());
163         throw std::runtime_error("Incorrect VINI records HW Keyword data size");
164     }
165 
166     log<level::DEBUG>(fmt::format("VINI Records HW[0]:{} HW[1]:{}",
167                                   hwData.at(0), hwData.at(1))
168                           .c_str());
169 
170     // VINI Record "HW" keyword's Byte 0's MSB bit indicates
171     // proc or planar type need to choose.
172     constexpr uint8_t SYS_CLK_NE_TERMINATION_ON_MASK = 0x80;
173 
174     ATTR_SYS_CLK_NE_TERMINATION_SITE_Type clockTerm =
175         ENUM_ATTR_SYS_CLK_NE_TERMINATION_SITE_PLANAR;
176 
177     if (SYS_CLK_NE_TERMINATION_ON_MASK & hwData.at(0))
178     {
179         clockTerm = ENUM_ATTR_SYS_CLK_NE_TERMINATION_SITE_PROC;
180     }
181 
182     // update all the processor attributes
183     struct pdbg_target* procTarget;
184     pdbg_for_each_class_target("proc", procTarget)
185     {
186 
187         if (DT_SET_PROP(ATTR_SYS_CLK_NE_TERMINATION_SITE, procTarget,
188                         clockTerm))
189         {
190             log<level::ERR>(
191                 "Attribute ATTR_SYS_CLK_NE_TERMINATION_SITE set failed");
192             throw std::runtime_error(
193                 "Attribute ATTR_SYS_CLK_NE_TERMINATION_SITE set failed");
194         }
195     }
196 }
197 
198 /**
199  * @brief Starts the self boot engine on POWER processor position 0
200  *        to kick off a boot.
201  * @return void
202  */
203 void startHost(enum ipl_type iplType = IPL_TYPE_NORMAL)
204 {
205     try
206     {
207         phal_init();
208         ipl_set_type(iplType);
209     }
210     catch (std::exception& ex)
211     {
212         log<level::ERR>("Exception raised during init PHAL",
213                         entry("EXCEPTION=%s", ex.what()));
214         openpower::pel::detail::processBootErrorCallback(false);
215         throw std::runtime_error("PHAL initialization failed");
216     }
217 
218     // To clear trace if success
219     openpower::pel::detail::processBootErrorCallback(true);
220 
221     setClkNETerminationSite();
222 
223     // callback method will be called upon failure which will create the PEL
224     int rc = ipl_run_major(0);
225     if (rc > 0)
226     {
227         log<level::ERR>("step 0 failed to start the host");
228         throw std::runtime_error("Failed to execute host start boot step");
229     }
230 }
231 
232 /**
233  * @brief Starts the reboot with type memory preserving reboot.
234  * @return void
235  */
236 void startHostMpReboot()
237 {
238     // set ipl type as mpipl
239     startHost(IPL_TYPE_MPIPL);
240 }
241 
242 /**
243  * @brief Starts the normal boot type.
244  * @return void
245  */
246 void startHostNormal()
247 {
248     // Run select seeprom before poweron
249     try
250     {
251         selectBootSeeprom();
252 
253         // To clear trace as it is success
254         openpower::pel::detail::processBootErrorCallback(true);
255     }
256     catch (const std::exception& ex)
257     {
258         // create PEL in failure
259         openpower::pel::detail::processBootErrorCallback(false);
260         log<level::ERR>("SEEPROM selection failed", entry("ERR=%s", ex.what()));
261         throw ex;
262     }
263 
264     startHost();
265 }
266 
267 REGISTER_PROCEDURE("startHost", startHostNormal)
268 REGISTER_PROCEDURE("startHostMpReboot", startHostMpReboot)
269 
270 } // namespace phal
271 } // namespace openpower
272