| Patrick Venture | 5e6ac71 | 2017-10-25 12:16:19 -0700 | [diff] [blame] | 1 | #include "watchdog.hpp" | 
 | 2 |  | 
| William A. Kennington III | 5257525 | 2018-02-09 15:54:56 -0800 | [diff] [blame^] | 3 | #include <cstdint> | 
 | 4 | #include <endian.h> | 
 | 5 | #include <phosphor-logging/log.hpp> | 
 | 6 | #include <string> | 
| Patrick Venture | 894571d | 2017-11-09 14:46:54 -0800 | [diff] [blame] | 7 |  | 
| William A. Kennington III | 5257525 | 2018-02-09 15:54:56 -0800 | [diff] [blame^] | 8 | #include "watchdog_service.hpp" | 
 | 9 | #include "host-ipmid/ipmid-api.h" | 
 | 10 | #include "ipmid.hpp" | 
| Patrick Venture | 5e6ac71 | 2017-10-25 12:16:19 -0700 | [diff] [blame] | 11 |  | 
| William A. Kennington III | 5257525 | 2018-02-09 15:54:56 -0800 | [diff] [blame^] | 12 | using phosphor::logging::level; | 
 | 13 | using phosphor::logging::log; | 
| Patrick Venture | 5e6ac71 | 2017-10-25 12:16:19 -0700 | [diff] [blame] | 14 |  | 
| William A. Kennington III | 61d5f7b | 2018-02-09 15:23:53 -0800 | [diff] [blame] | 15 | ipmi_ret_t ipmi_app_watchdog_reset( | 
| Patrick Venture | 894571d | 2017-11-09 14:46:54 -0800 | [diff] [blame] | 16 |         ipmi_netfn_t netfn, | 
 | 17 |         ipmi_cmd_t cmd, | 
 | 18 |         ipmi_request_t request, | 
 | 19 |         ipmi_response_t response, | 
 | 20 |         ipmi_data_len_t data_len, | 
 | 21 |         ipmi_context_t context) | 
| Patrick Venture | 5e6ac71 | 2017-10-25 12:16:19 -0700 | [diff] [blame] | 22 | { | 
| William A. Kennington III | 5257525 | 2018-02-09 15:54:56 -0800 | [diff] [blame^] | 23 |     // We never return data with this command so immediately get rid of it | 
| Patrick Venture | 5e6ac71 | 2017-10-25 12:16:19 -0700 | [diff] [blame] | 24 |     *data_len = 0; | 
 | 25 |  | 
| William A. Kennington III | 5257525 | 2018-02-09 15:54:56 -0800 | [diff] [blame^] | 26 |     try | 
 | 27 |     { | 
 | 28 |         WatchdogService wd_service; | 
 | 29 |         WatchdogService::Properties wd_prop = wd_service.getProperties(); | 
 | 30 |  | 
 | 31 |         // Reset the countdown to make sure we don't expire our timer | 
 | 32 |         wd_service.setTimeRemaining(wd_prop.interval); | 
 | 33 |  | 
 | 34 |         // The spec states that the timer is activated by reset | 
 | 35 |         wd_service.setEnabled(true); | 
 | 36 |  | 
 | 37 |         return IPMI_CC_OK; | 
| Patrick Venture | 5e6ac71 | 2017-10-25 12:16:19 -0700 | [diff] [blame] | 38 |     } | 
| William A. Kennington III | 5257525 | 2018-02-09 15:54:56 -0800 | [diff] [blame^] | 39 |     catch (const std::exception& e) | 
 | 40 |     { | 
 | 41 |         const std::string e_str = std::string("wd_reset: ") + e.what(); | 
 | 42 |         log<level::ERR>(e_str.c_str()); | 
 | 43 |         return IPMI_CC_UNSPECIFIED_ERROR; | 
| William A. Kennington III | 5325f2c | 2018-01-08 15:17:09 -0800 | [diff] [blame] | 44 |     } | 
| William A. Kennington III | 5257525 | 2018-02-09 15:54:56 -0800 | [diff] [blame^] | 45 |     catch (...) | 
 | 46 |     { | 
 | 47 |         log<level::ERR>("wd_reset: Unknown Error"); | 
 | 48 |         return IPMI_CC_UNSPECIFIED_ERROR; | 
| William A. Kennington III | 5325f2c | 2018-01-08 15:17:09 -0800 | [diff] [blame] | 49 |     } | 
| Patrick Venture | 5e6ac71 | 2017-10-25 12:16:19 -0700 | [diff] [blame] | 50 | } | 
| William A. Kennington III | 61d5f7b | 2018-02-09 15:23:53 -0800 | [diff] [blame] | 51 |  | 
| William A. Kennington III | 5257525 | 2018-02-09 15:54:56 -0800 | [diff] [blame^] | 52 | static constexpr uint8_t wd_dont_stop = 0x1 << 6; | 
 | 53 | static constexpr uint8_t wd_timeout_action_mask = 0x3; | 
 | 54 |  | 
 | 55 | enum class IpmiAction : uint8_t { | 
 | 56 |     None = 0x0, | 
 | 57 |     HardReset = 0x1, | 
 | 58 |     PowerOff = 0x2, | 
 | 59 |     PowerCycle = 0x3, | 
 | 60 | }; | 
 | 61 |  | 
 | 62 | struct wd_set_req { | 
 | 63 |     uint8_t timer_use; | 
 | 64 |     uint8_t timer_action; | 
 | 65 |     uint8_t pretimeout;  // (seconds) | 
 | 66 |     uint8_t expire_flags; | 
 | 67 |     uint16_t initial_countdown;  // Little Endian (deciseconds) | 
 | 68 | }  __attribute__ ((packed)); | 
 | 69 | static_assert(sizeof(wd_set_req) == 6, "wd_set_req has invalid size."); | 
 | 70 | static_assert(sizeof(wd_set_req) <= MAX_IPMI_BUFFER, | 
 | 71 |         "wd_get_res can't fit in request buffer."); | 
 | 72 |  | 
| William A. Kennington III | 61d5f7b | 2018-02-09 15:23:53 -0800 | [diff] [blame] | 73 | ipmi_ret_t ipmi_app_watchdog_set( | 
 | 74 |         ipmi_netfn_t netfn, | 
 | 75 |         ipmi_cmd_t cmd, | 
 | 76 |         ipmi_request_t request, | 
 | 77 |         ipmi_response_t response, | 
 | 78 |         ipmi_data_len_t data_len, | 
 | 79 |         ipmi_context_t context) | 
 | 80 | { | 
| William A. Kennington III | 5257525 | 2018-02-09 15:54:56 -0800 | [diff] [blame^] | 81 |     // Extract the request data | 
 | 82 |     if (*data_len < sizeof(wd_set_req)) | 
 | 83 |     { | 
 | 84 |         *data_len = 0; | 
 | 85 |         return IPMI_CC_REQ_DATA_LEN_INVALID; | 
 | 86 |     } | 
 | 87 |     wd_set_req req; | 
 | 88 |     memcpy(&req, request, sizeof(req)); | 
 | 89 |     req.initial_countdown = le16toh(req.initial_countdown); | 
| William A. Kennington III | 61d5f7b | 2018-02-09 15:23:53 -0800 | [diff] [blame] | 90 |     *data_len = 0; | 
 | 91 |  | 
| William A. Kennington III | 5257525 | 2018-02-09 15:54:56 -0800 | [diff] [blame^] | 92 |     try | 
| William A. Kennington III | 61d5f7b | 2018-02-09 15:23:53 -0800 | [diff] [blame] | 93 |     { | 
| William A. Kennington III | 5257525 | 2018-02-09 15:54:56 -0800 | [diff] [blame^] | 94 |         WatchdogService wd_service; | 
 | 95 |         // Stop the timer if the don't stop bit is not set | 
 | 96 |         if (!(req.timer_use & wd_dont_stop)) | 
 | 97 |         { | 
 | 98 |             wd_service.setEnabled(false); | 
| William A. Kennington III | 61d5f7b | 2018-02-09 15:23:53 -0800 | [diff] [blame] | 99 |         } | 
 | 100 |  | 
| William A. Kennington III | 5257525 | 2018-02-09 15:54:56 -0800 | [diff] [blame^] | 101 |         // Set the action based on the request | 
 | 102 |         // Unfortunately we only really support enable or disable | 
 | 103 |         // and don't actually support a real action. Until we have proper | 
 | 104 |         // action support just map NONE as a disable action. | 
 | 105 |         const auto ipmi_action = static_cast<IpmiAction>( | 
 | 106 |                 req.timer_action & wd_timeout_action_mask); | 
 | 107 |         if (ipmi_action == IpmiAction::None) | 
 | 108 |         { | 
 | 109 |             wd_service.setEnabled(false); | 
| William A. Kennington III | 61d5f7b | 2018-02-09 15:23:53 -0800 | [diff] [blame] | 110 |         } | 
| William A. Kennington III | 5257525 | 2018-02-09 15:54:56 -0800 | [diff] [blame^] | 111 |  | 
 | 112 |         // Set the new interval and the time remaining deci -> mill seconds | 
 | 113 |         const uint64_t interval = req.initial_countdown * 100; | 
 | 114 |         wd_service.setInterval(interval); | 
 | 115 |         wd_service.setTimeRemaining(interval); | 
 | 116 |  | 
 | 117 |         return IPMI_CC_OK; | 
| William A. Kennington III | 61d5f7b | 2018-02-09 15:23:53 -0800 | [diff] [blame] | 118 |     } | 
| William A. Kennington III | 5257525 | 2018-02-09 15:54:56 -0800 | [diff] [blame^] | 119 |     catch (const std::domain_error &) | 
 | 120 |     { | 
 | 121 |         return IPMI_CC_INVALID_FIELD_REQUEST; | 
 | 122 |     } | 
 | 123 |     catch (const std::exception& e) | 
 | 124 |     { | 
 | 125 |         const std::string e_str = std::string("wd_set: ") + e.what(); | 
 | 126 |         log<level::ERR>(e_str.c_str()); | 
 | 127 |         return IPMI_CC_UNSPECIFIED_ERROR; | 
 | 128 |     } | 
 | 129 |     catch (...) | 
 | 130 |     { | 
 | 131 |         log<level::ERR>("wd_set: Unknown Error"); | 
 | 132 |         return IPMI_CC_UNSPECIFIED_ERROR; | 
 | 133 |     } | 
| William A. Kennington III | 61d5f7b | 2018-02-09 15:23:53 -0800 | [diff] [blame] | 134 | } |