1 /* 2 // Copyright (c) 2019 Intel Corporation 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 */ 16 #include "post_code.hpp" 17 18 #include "iomanip" 19 20 PostCodeDataHolder* PostCodeDataHolder::instance = 0; 21 22 void PostCode::deleteAll() 23 { 24 auto dir = fs::path(postcodeDataHolderObj->PostCodeListPathPrefix + 25 std::to_string(postcodeDataHolderObj->node)); 26 std::uintmax_t n = fs::remove_all(dir); 27 std::cerr << "clearPostCodes deleted " << n << " files in " 28 << postcodeDataHolderObj->PostCodeListPathPrefix + 29 std::to_string(postcodeDataHolderObj->node) 30 << std::endl; 31 fs::create_directories(dir); 32 postCodes.clear(); 33 currentBootCycleIndex = 0; 34 currentBootCycleCount(0); 35 } 36 37 std::vector<postcode_t> PostCode::getPostCodes(uint16_t index) 38 { 39 std::vector<postcode_t> codesVec; 40 if (1 == index && !postCodes.empty()) 41 { 42 for (auto& code : postCodes) 43 codesVec.push_back(code.second); 44 } 45 else 46 { 47 uint16_t bootNum = getBootNum(index); 48 49 decltype(postCodes) codes; 50 deserializePostCodes( 51 fs::path(strPostCodeListPath + std::to_string(bootNum)), codes); 52 for (std::pair<uint64_t, postcode_t> code : codes) 53 codesVec.push_back(code.second); 54 } 55 return codesVec; 56 } 57 58 std::map<uint64_t, postcode_t> 59 PostCode::getPostCodesWithTimeStamp(uint16_t index) 60 { 61 if (1 == index && !postCodes.empty()) 62 { 63 return postCodes; 64 } 65 66 uint16_t bootNum = getBootNum(index); 67 decltype(postCodes) codes; 68 deserializePostCodes( 69 fs::path(strPostCodeListPath + std::to_string(bootNum)), codes); 70 return codes; 71 } 72 73 void PostCode::savePostCodes(postcode_t code) 74 { 75 uint64_t usTimeOffset = 0; 76 // steady_clock is a monotonic clock that is guaranteed to never be adjusted 77 auto postCodeTimeSteady = std::chrono::steady_clock::now(); 78 uint64_t tsUS = std::chrono::duration_cast<std::chrono::microseconds>( 79 std::chrono::system_clock::now().time_since_epoch()) 80 .count(); 81 82 if (postCodes.empty()) 83 { 84 firstPostCodeTimeSteady = postCodeTimeSteady; 85 firstPostCodeUsSinceEpoch = tsUS; // uS since epoch for 1st post code 86 incrBootCycle(); 87 } 88 else 89 { 90 // calculating tsUS so it is monotonic within the same boot 91 usTimeOffset = std::chrono::duration_cast<std::chrono::microseconds>( 92 postCodeTimeSteady - firstPostCodeTimeSteady) 93 .count(); 94 tsUS = usTimeOffset + firstPostCodeUsSinceEpoch; 95 } 96 97 postCodes.insert(std::make_pair(tsUS, code)); 98 serialize(fs::path(strPostCodeListPath)); 99 100 #ifdef ENABLE_BIOS_POST_CODE_LOG 101 std::ostringstream hexCode; 102 hexCode << "0x" << std::setfill('0') << std::setw(2) << std::hex 103 << std::get<0>(code); 104 105 std::ostringstream timeOffsetStr; 106 // Set Fixed-Point Notation 107 timeOffsetStr << std::fixed; 108 // Set precision to 4 digits 109 timeOffsetStr << std::setprecision(4); 110 // Add double to stream 111 timeOffsetStr << static_cast<double>(usTimeOffset) / 1000 / 1000; 112 113 phosphor::logging::log<phosphor::logging::level::INFO>( 114 "BIOS POST Code", 115 phosphor::logging::entry("REDFISH_MESSAGE_ID=%s", 116 "OpenBMC.0.1.BIOSPOSTCode"), 117 phosphor::logging::entry( 118 "REDFISH_MESSAGE_ARGS=%d,%s,%s", currentBootCycleIndex, 119 timeOffsetStr.str().c_str(), hexCode.str().c_str())); 120 #endif 121 122 return; 123 } 124 125 fs::path PostCode::serialize(const std::string& path) 126 { 127 try 128 { 129 fs::path idxPath(path + strCurrentBootCycleIndexName); 130 std::ofstream osIdx(idxPath.c_str(), std::ios::binary); 131 cereal::JSONOutputArchive idxArchive(osIdx); 132 idxArchive(currentBootCycleIndex); 133 134 uint16_t count = currentBootCycleCount(); 135 fs::path cntPath(path + strCurrentBootCycleCountName); 136 std::ofstream osCnt(cntPath.c_str(), std::ios::binary); 137 cereal::JSONOutputArchive cntArchive(osCnt); 138 cntArchive(count); 139 140 std::ofstream osPostCodes( 141 (path + std::to_string(currentBootCycleIndex))); 142 cereal::JSONOutputArchive oarchivePostCodes(osPostCodes); 143 oarchivePostCodes(postCodes); 144 } 145 catch (cereal::Exception& e) 146 { 147 phosphor::logging::log<phosphor::logging::level::ERR>(e.what()); 148 return ""; 149 } 150 catch (const fs::filesystem_error& e) 151 { 152 phosphor::logging::log<phosphor::logging::level::ERR>(e.what()); 153 return ""; 154 } 155 return path; 156 } 157 158 bool PostCode::deserialize(const fs::path& path, uint16_t& index) 159 { 160 try 161 { 162 if (fs::exists(path)) 163 { 164 std::ifstream is(path.c_str(), std::ios::in | std::ios::binary); 165 cereal::JSONInputArchive iarchive(is); 166 iarchive(index); 167 return true; 168 } 169 return false; 170 } 171 catch (cereal::Exception& e) 172 { 173 phosphor::logging::log<phosphor::logging::level::ERR>(e.what()); 174 return false; 175 } 176 catch (const fs::filesystem_error& e) 177 { 178 return false; 179 } 180 181 return false; 182 } 183 184 bool PostCode::deserializePostCodes(const fs::path& path, 185 std::map<uint64_t, postcode_t>& codes) 186 { 187 try 188 { 189 if (fs::exists(path)) 190 { 191 std::ifstream is(path.c_str(), std::ios::in | std::ios::binary); 192 cereal::JSONInputArchive iarchive(is); 193 iarchive(codes); 194 return true; 195 } 196 return false; 197 } 198 catch (cereal::Exception& e) 199 { 200 phosphor::logging::log<phosphor::logging::level::ERR>(e.what()); 201 return false; 202 } 203 catch (const fs::filesystem_error& e) 204 { 205 return false; 206 } 207 return false; 208 } 209 210 void PostCode::incrBootCycle() 211 { 212 if (currentBootCycleIndex >= maxBootCycleNum()) 213 { 214 currentBootCycleIndex = 1; 215 } 216 else 217 { 218 currentBootCycleIndex++; 219 } 220 currentBootCycleCount(std::min( 221 maxBootCycleNum(), static_cast<uint16_t>(currentBootCycleCount() + 1))); 222 } 223 224 uint16_t PostCode::getBootNum(const uint16_t index) const 225 { 226 // bootNum assumes the oldest archive is boot number 1 227 // and the current boot number equals bootCycleCount 228 // map bootNum back to bootIndex that was used to archive postcode 229 uint16_t bootNum = currentBootCycleIndex; 230 if (index > bootNum) // need to wrap around 231 { 232 bootNum = (maxBootCycleNum() + currentBootCycleIndex) - index + 1; 233 } 234 else 235 { 236 bootNum = currentBootCycleIndex - index + 1; 237 } 238 return bootNum; 239 } 240