1 /** 2 * Copyright © 2019 IBM 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 "registry.hpp" 17 18 #include "json_utils.hpp" 19 #include "pel_types.hpp" 20 #include "pel_values.hpp" 21 22 #include <phosphor-logging/lg2.hpp> 23 24 #include <fstream> 25 26 namespace openpower 27 { 28 namespace pels 29 { 30 namespace message 31 { 32 33 namespace pv = pel_values; 34 namespace fs = std::filesystem; 35 36 constexpr auto debugFilePath = "/etc/phosphor-logging/"; 37 38 namespace helper 39 { 40 41 uint8_t getSubsystem(const std::string& subsystemName) 42 { 43 // Get the actual value to use in the PEL for the string name 44 auto ss = pv::findByName(subsystemName, pv::subsystemValues); 45 if (ss == pv::subsystemValues.end()) 46 { 47 // Schema validation should be catching this. 48 lg2::error("Invalid subsystem name used in message registry: {SUBSYS}", 49 "SUBSYS", subsystemName); 50 51 throw std::runtime_error("Invalid subsystem used in message registry"); 52 } 53 54 return std::get<pv::fieldValuePos>(*ss); 55 } 56 57 uint8_t getSeverity(const std::string& severityName) 58 { 59 auto s = pv::findByName(severityName, pv::severityValues); 60 if (s == pv::severityValues.end()) 61 { 62 // Schema validation should be catching this. 63 lg2::error("Invalid severity name used in message registry: {SEV}", 64 "SEV", severityName); 65 66 throw std::runtime_error("Invalid severity used in message registry"); 67 } 68 69 return std::get<pv::fieldValuePos>(*s); 70 } 71 72 std::vector<RegistrySeverity> getSeverities(const nlohmann::json& severity) 73 { 74 std::vector<RegistrySeverity> severities; 75 76 // The plain string value, like "unrecoverable" 77 if (severity.is_string()) 78 { 79 RegistrySeverity s; 80 s.severity = getSeverity(severity.get<std::string>()); 81 severities.push_back(std::move(s)); 82 } 83 else 84 { 85 // An array, with an element like: 86 // { 87 // "SevValue": "unrecoverable", 88 // "System", "systemA" 89 // } 90 for (const auto& sev : severity) 91 { 92 RegistrySeverity s; 93 s.severity = getSeverity(sev["SevValue"].get<std::string>()); 94 95 if (sev.contains("System")) 96 { 97 s.system = sev["System"].get<std::string>(); 98 } 99 100 severities.push_back(std::move(s)); 101 } 102 } 103 104 return severities; 105 } 106 107 uint16_t getActionFlags(const std::vector<std::string>& flags) 108 { 109 uint16_t actionFlags = 0; 110 111 // Make the bitmask based on the array of flag names 112 for (const auto& flag : flags) 113 { 114 auto s = pv::findByName(flag, pv::actionFlagsValues); 115 if (s == pv::actionFlagsValues.end()) 116 { 117 // Schema validation should be catching this. 118 lg2::error( 119 "Invalid action flag name used in message registry: {FLAG}", 120 "FLAG", flag); 121 122 throw std::runtime_error( 123 "Invalid action flag used in message registry"); 124 } 125 126 actionFlags |= std::get<pv::fieldValuePos>(*s); 127 } 128 129 return actionFlags; 130 } 131 132 uint8_t getEventType(const std::string& eventTypeName) 133 { 134 auto t = pv::findByName(eventTypeName, pv::eventTypeValues); 135 if (t == pv::eventTypeValues.end()) 136 { 137 lg2::error("Invalid event type used in message registry: {TYPE}", 138 "TYPE", eventTypeName); 139 140 throw std::runtime_error("Invalid event type used in message registry"); 141 } 142 return std::get<pv::fieldValuePos>(*t); 143 } 144 145 uint8_t getEventScope(const std::string& eventScopeName) 146 { 147 auto s = pv::findByName(eventScopeName, pv::eventScopeValues); 148 if (s == pv::eventScopeValues.end()) 149 { 150 lg2::error("Invalid event scope used in registry: {SCOPE}", "SCOPE", 151 eventScopeName); 152 153 throw std::runtime_error( 154 "Invalid event scope used in message registry"); 155 } 156 return std::get<pv::fieldValuePos>(*s); 157 } 158 159 uint16_t getSRCReasonCode(const nlohmann::json& src, const std::string& name) 160 { 161 std::string rc = src["ReasonCode"]; 162 uint16_t reasonCode = strtoul(rc.c_str(), nullptr, 16); 163 if (reasonCode == 0) 164 { 165 lg2::error( 166 "Invalid reason code {RC} in message registry, error name = {ERROR}", 167 "RC", rc, "ERROR", name); 168 169 throw std::runtime_error("Invalid reason code in message registry"); 170 } 171 return reasonCode; 172 } 173 174 uint8_t getSRCType(const nlohmann::json& src, const std::string& name) 175 { 176 // Looks like: "22" 177 std::string srcType = src["Type"]; 178 size_t type = strtoul(srcType.c_str(), nullptr, 16); 179 if ((type == 0) || (srcType.size() != 2)) // 1 hex byte 180 { 181 lg2::error( 182 "Invalid SRC Type {TYPE} in message registry, error name = {ERROR}", 183 "TYPE", srcType, "ERROR", name); 184 185 throw std::runtime_error("Invalid SRC Type in message registry"); 186 } 187 188 return type; 189 } 190 191 bool getSRCDeconfigFlag(const nlohmann::json& src) 192 { 193 return src["DeconfigFlag"].get<bool>(); 194 } 195 196 bool getSRCCheckstopFlag(const nlohmann::json& src) 197 { 198 return src["CheckstopFlag"].get<bool>(); 199 } 200 201 std::optional<std::map<SRC::WordNum, SRC::AdditionalDataField>> 202 getSRCHexwordFields(const nlohmann::json& src, const std::string& name) 203 { 204 std::map<SRC::WordNum, SRC::AdditionalDataField> hexwordFields; 205 206 // Build the map of which AdditionalData fields to use for which SRC words 207 208 // Like: 209 // { 210 // "8": 211 // { 212 // "AdditionalDataPropSource": "TEST" 213 // } 214 // 215 // } 216 217 for (const auto& word : src["Words6To9"].items()) 218 { 219 std::string num = word.key(); 220 size_t wordNum = std::strtoul(num.c_str(), nullptr, 10); 221 222 if (wordNum == 0) 223 { 224 lg2::error( 225 "Invalid SRC word number {NUM} in message registry, error name = {ERROR}", 226 "NUM", num, "ERROR", name); 227 228 throw std::runtime_error("Invalid SRC word in message registry"); 229 } 230 231 auto attributes = word.value(); 232 233 // Use an empty string for the description if it does not exist. 234 auto itr = attributes.find("Description"); 235 std::string desc = (attributes.end() != itr) ? *itr : ""; 236 237 std::tuple<std::string, std::string> adPropSourceDesc( 238 attributes["AdditionalDataPropSource"], desc); 239 hexwordFields[wordNum] = std::move(adPropSourceDesc); 240 } 241 242 if (!hexwordFields.empty()) 243 { 244 return hexwordFields; 245 } 246 247 return std::nullopt; 248 } 249 std::optional<std::vector<SRC::WordNum>> 250 getSRCSymptomIDFields(const nlohmann::json& src, const std::string& name) 251 { 252 std::vector<SRC::WordNum> symptomIDFields; 253 254 // Looks like: 255 // "SymptomIDFields": ["SRCWord3", "SRCWord6"], 256 257 for (const std::string field : src["SymptomIDFields"]) 258 { 259 // Just need the last digit off the end, e.g. SRCWord6. 260 // The schema enforces the format of these. 261 auto srcWordNum = field.substr(field.size() - 1); 262 size_t num = std::strtoul(srcWordNum.c_str(), nullptr, 10); 263 if (num == 0) 264 { 265 lg2::error( 266 "Invalid symptom ID field {FIELD} in message registry, error name = {ERROR}", 267 "FIELD", field, "ERROR", name); 268 269 throw std::runtime_error("Invalid symptom ID in message registry"); 270 } 271 symptomIDFields.push_back(num); 272 } 273 if (!symptomIDFields.empty()) 274 { 275 return symptomIDFields; 276 } 277 278 return std::nullopt; 279 } 280 281 uint16_t getComponentID(uint8_t srcType, uint16_t reasonCode, 282 const nlohmann::json& pelEntry, const std::string& name) 283 { 284 uint16_t id = 0; 285 286 // If the ComponentID field is there, use that. Otherwise, if it's a 287 // 0xBD BMC error SRC, use the reasoncode. 288 if (pelEntry.contains("ComponentID")) 289 { 290 std::string componentID = pelEntry["ComponentID"]; 291 id = strtoul(componentID.c_str(), nullptr, 16); 292 } 293 else 294 { 295 // On BMC error SRCs (BD), can just get the component ID from 296 // the first byte of the reason code. 297 if (srcType == static_cast<uint8_t>(SRCType::bmcError)) 298 { 299 id = reasonCode & 0xFF00; 300 } 301 else 302 { 303 lg2::error( 304 "Missing component ID field in message registry, error name = {ERROR}", 305 "ERROR", name); 306 307 throw std::runtime_error( 308 "Missing component ID field in message registry"); 309 } 310 } 311 312 return id; 313 } 314 315 /** 316 * @brief Says if the JSON is the format that contains AdditionalData keys 317 * as in index into them. 318 * 319 * @param[in] json - The highest level callout JSON 320 * 321 * @return bool - If it is the AdditionalData format or not 322 */ 323 bool calloutUsesAdditionalData(const nlohmann::json& json) 324 { 325 return (json.contains("ADName") && 326 json.contains("CalloutsWithTheirADValues")); 327 } 328 329 /** 330 * @brief Finds the callouts to use when there is no AdditionalData, 331 * but the system type may be used as a key. 332 * 333 * One entry in the array looks like the following. The System key 334 * is optional and if not present it means that entry applies to 335 * every configuration that doesn't have another entry with a matching 336 * System key. 337 * 338 * { 339 * "System": "system1", 340 * "CalloutList": 341 * [ 342 * { 343 * "Priority": "high", 344 * "LocCode": "P1-C1" 345 * }, 346 * { 347 * "Priority": "low", 348 * "LocCode": "P1" 349 * } 350 * ] 351 * } 352 */ 353 const nlohmann::json& 354 findCalloutList(const nlohmann::json& json, 355 const std::vector<std::string>& systemNames) 356 { 357 const nlohmann::json* callouts = nullptr; 358 359 if (!json.is_array()) 360 { 361 throw std::runtime_error{"findCalloutList was not passed a JSON array"}; 362 } 363 364 // The entry with the system type match will take precedence over the entry 365 // without any "System" field in it at all, which will match all other 366 // cases. 367 for (const auto& calloutList : json) 368 { 369 if (calloutList.contains("System")) 370 { 371 if (std::find(systemNames.begin(), systemNames.end(), 372 calloutList["System"].get<std::string>()) != 373 systemNames.end()) 374 { 375 callouts = &calloutList["CalloutList"]; 376 break; 377 } 378 } 379 else 380 { 381 // Any entry with no System key 382 callouts = &calloutList["CalloutList"]; 383 } 384 } 385 386 if (!callouts) 387 { 388 std::string types; 389 std::for_each(systemNames.begin(), systemNames.end(), 390 [&types](const auto& t) { types += t + '|'; }); 391 lg2::warning( 392 "No matching system name entry or default system name entry " 393 " for PEL callout list, names = {TYPES}", 394 "TYPES", types); 395 396 throw std::runtime_error{ 397 "Could not find a CalloutList JSON for this error and system name"}; 398 } 399 400 return *callouts; 401 } 402 403 /** 404 * @brief Creates a RegistryCallout based on the input JSON. 405 * 406 * The JSON looks like: 407 * { 408 * "Priority": "high", 409 * "LocCode": "E1" 410 * ... 411 * } 412 * 413 * Schema validation enforces what keys are present. 414 * 415 * @param[in] json - The JSON dictionary entry for a callout 416 * 417 * @return RegistryCallout - A filled in RegistryCallout 418 */ 419 RegistryCallout makeRegistryCallout(const nlohmann::json& json) 420 { 421 RegistryCallout callout; 422 423 callout.priority = "high"; 424 callout.useInventoryLocCode = false; 425 426 if (json.contains("Priority")) 427 { 428 callout.priority = json["Priority"].get<std::string>(); 429 } 430 431 if (json.contains("LocCode")) 432 { 433 callout.locCode = json["LocCode"].get<std::string>(); 434 } 435 436 if (json.contains("Procedure")) 437 { 438 callout.procedure = json["Procedure"].get<std::string>(); 439 } 440 else if (json.contains("SymbolicFRU")) 441 { 442 callout.symbolicFRU = json["SymbolicFRU"].get<std::string>(); 443 } 444 else if (json.contains("SymbolicFRUTrusted")) 445 { 446 callout.symbolicFRUTrusted = 447 json["SymbolicFRUTrusted"].get<std::string>(); 448 } 449 450 if (json.contains("UseInventoryLocCode")) 451 { 452 callout.useInventoryLocCode = json["UseInventoryLocCode"].get<bool>(); 453 } 454 455 return callout; 456 } 457 458 /** 459 * @brief Returns the callouts to use when an AdditionalData key is 460 * required to find the correct entries. 461 * 462 * The System property is used to find which CalloutList to use. 463 * If System is missing, then that CalloutList is valid for 464 * everything. 465 * 466 * The JSON looks like: 467 * [ 468 * { 469 * "System": "systemA", 470 * "CalloutList": 471 * [ 472 * { 473 * "Priority": "high", 474 * "LocCode": "P1-C5" 475 * } 476 * ] 477 * } 478 * ] 479 * 480 * @param[in] json - The callout JSON 481 * @param[in] systemNames - List of compatible system type names 482 * 483 * @return std::vector<RegistryCallout> - The callouts to use 484 */ 485 std::vector<RegistryCallout> 486 getCalloutsWithoutAD(const nlohmann::json& json, 487 const std::vector<std::string>& systemNames) 488 { 489 std::vector<RegistryCallout> calloutEntries; 490 491 // Find the CalloutList to use based on the system type 492 const auto& calloutList = findCalloutList(json, systemNames); 493 494 // We finally found the callouts, make the objects. 495 for (const auto& callout : calloutList) 496 { 497 calloutEntries.push_back(std::move(makeRegistryCallout(callout))); 498 } 499 500 return calloutEntries; 501 } 502 503 /** 504 * @brief Returns the callouts to use when an AdditionalData key is 505 * required to find the correct entries. 506 * 507 * The JSON looks like: 508 * { 509 * "ADName": "PROC_NUM", 510 * "CalloutsWithTheirADValues": 511 * [ 512 * { 513 * "ADValue": "0", 514 * "Callouts": 515 * [ 516 * { 517 * "CalloutList": 518 * [ 519 * { 520 * "Priority": "high", 521 * "LocCode": "P1-C5" 522 * } 523 * ] 524 * } 525 * ] 526 * } 527 * ] 528 * } 529 * 530 * Note that the "Callouts" entry above is the same as the top level 531 * entry used when there is no AdditionalData key. 532 * 533 * @param[in] json - The callout JSON 534 * @param[in] systemNames - List of compatible system type names 535 * @param[in] additionalData - The AdditionalData property 536 * 537 * @return std::vector<RegistryCallout> - The callouts to use 538 */ 539 std::vector<RegistryCallout> 540 getCalloutsUsingAD(const nlohmann::json& json, 541 const std::vector<std::string>& systemNames, 542 const AdditionalData& additionalData) 543 { 544 // This indicates which AD field we'll be using 545 auto keyName = json["ADName"].get<std::string>(); 546 547 // Get the actual value from the AD data 548 auto adValue = additionalData.getValue(keyName); 549 550 if (!adValue) 551 { 552 // The AdditionalData did not contain the necessary key 553 lg2::warning("The PEL message registry callouts JSON " 554 "said to use an AdditionalData key that isn't in the " 555 "AdditionalData event log property, key = {KEY}", 556 "KEY", keyName); 557 throw std::runtime_error{ 558 "Missing AdditionalData entry for this callout"}; 559 } 560 561 const auto& callouts = json["CalloutsWithTheirADValues"]; 562 563 // find the entry with that AD value 564 auto it = std::find_if(callouts.begin(), callouts.end(), 565 [adValue](const nlohmann::json& j) { 566 return *adValue == j["ADValue"].get<std::string>(); 567 }); 568 569 if (it == callouts.end()) 570 { 571 // This can happen if not all possible values were in the 572 // message registry and that's fine. There may be a 573 // "CalloutsWhenNoADMatch" section that contains callouts 574 // to use in this case. 575 if (json.contains("CalloutsWhenNoADMatch")) 576 { 577 return getCalloutsWithoutAD(json["CalloutsWhenNoADMatch"], 578 systemNames); 579 } 580 return std::vector<RegistryCallout>{}; 581 } 582 583 // Proceed to find the callouts possibly based on system type. 584 return getCalloutsWithoutAD((*it)["Callouts"], systemNames); 585 } 586 587 /** 588 * @brief Returns the journal capture information 589 * 590 * The JSON looks like: 591 * "JournalCapture": { 592 * "NumLines": 30 593 * } 594 * 595 * "JournalCapture": 596 * { 597 * "Sections": [ 598 * { 599 * "SyslogID": "phosphor-log-manager", 600 * "NumLines": 20 601 * } 602 * ] 603 * } 604 * 605 * @param json - The journal capture JSON 606 * @return JournalCapture - The filled in variant 607 */ 608 JournalCapture getJournalCapture(const nlohmann::json& json) 609 { 610 JournalCapture capt; 611 612 // Primary key is either NumLines or Sections. 613 if (json.contains("NumLines")) 614 { 615 capt = json.at("NumLines").get<size_t>(); 616 } 617 else if (json.contains("Sections")) 618 { 619 AppCaptureList captures; 620 for (const auto& capture : json.at("Sections")) 621 { 622 AppCapture ac; 623 ac.syslogID = capture.at("SyslogID").get<std::string>(); 624 ac.numLines = capture.at("NumLines").get<size_t>(); 625 captures.push_back(std::move(ac)); 626 } 627 628 capt = captures; 629 } 630 else 631 { 632 lg2::error("JournalCapture section not the right format"); 633 throw std::runtime_error{"JournalCapture section not the right format"}; 634 } 635 636 return capt; 637 } 638 639 } // namespace helper 640 641 std::optional<Entry> Registry::lookup(const std::string& name, LookupType type, 642 bool toCache) 643 { 644 std::optional<nlohmann::json> registryTmp; 645 auto& registryOpt = (_registry) ? _registry : registryTmp; 646 if (!registryOpt) 647 { 648 registryOpt = readRegistry(_registryFile); 649 if (!registryOpt) 650 { 651 return std::nullopt; 652 } 653 else if (toCache) 654 { 655 // Save message registry in memory for peltool 656 _registry = std::move(registryTmp); 657 } 658 } 659 auto& reg = (_registry) ? _registry : registryTmp; 660 const auto& registry = reg.value(); 661 // Find an entry with this name in the PEL array. 662 auto e = std::find_if(registry["PELs"].begin(), registry["PELs"].end(), 663 [&name, &type](const nlohmann::json& j) { 664 return ((name == j.at("Name").get<std::string>() && 665 type == LookupType::name) || 666 (name == j.at("SRC").at("ReasonCode").get<std::string>() && 667 type == LookupType::reasonCode)); 668 }); 669 670 if (e != registry["PELs"].end()) 671 { 672 // Fill in the Entry structure from the JSON. Most, but not all, fields 673 // are optional. 674 675 try 676 { 677 Entry entry; 678 entry.name = (*e)["Name"]; 679 680 if (e->contains("Subsystem")) 681 { 682 entry.subsystem = helper::getSubsystem((*e)["Subsystem"]); 683 } 684 685 if (e->contains("ActionFlags")) 686 { 687 entry.actionFlags = helper::getActionFlags((*e)["ActionFlags"]); 688 } 689 690 if (e->contains("MfgActionFlags")) 691 { 692 entry.mfgActionFlags = 693 helper::getActionFlags((*e)["MfgActionFlags"]); 694 } 695 696 if (e->contains("Severity")) 697 { 698 entry.severity = helper::getSeverities((*e)["Severity"]); 699 } 700 701 if (e->contains("MfgSeverity")) 702 { 703 entry.mfgSeverity = helper::getSeverities((*e)["MfgSeverity"]); 704 } 705 706 if (e->contains("EventType")) 707 { 708 entry.eventType = helper::getEventType((*e)["EventType"]); 709 } 710 711 if (e->contains("EventScope")) 712 { 713 entry.eventScope = helper::getEventScope((*e)["EventScope"]); 714 } 715 716 auto& src = (*e)["SRC"]; 717 entry.src.reasonCode = helper::getSRCReasonCode(src, name); 718 719 if (src.contains("Type")) 720 { 721 entry.src.type = helper::getSRCType(src, name); 722 } 723 else 724 { 725 entry.src.type = static_cast<uint8_t>(SRCType::bmcError); 726 } 727 728 // Now that we know the SRC type and reason code, 729 // we can get the component ID. 730 entry.componentID = helper::getComponentID( 731 entry.src.type, entry.src.reasonCode, *e, name); 732 733 if (src.contains("Words6To9")) 734 { 735 entry.src.hexwordADFields = helper::getSRCHexwordFields(src, 736 name); 737 } 738 739 if (src.contains("SymptomIDFields")) 740 { 741 entry.src.symptomID = helper::getSRCSymptomIDFields(src, name); 742 } 743 744 if (src.contains("DeconfigFlag")) 745 { 746 entry.src.deconfigFlag = helper::getSRCDeconfigFlag(src); 747 } 748 749 if (src.contains("CheckstopFlag")) 750 { 751 entry.src.checkstopFlag = helper::getSRCCheckstopFlag(src); 752 } 753 754 auto& doc = (*e)["Documentation"]; 755 entry.doc.message = doc["Message"]; 756 entry.doc.description = doc["Description"]; 757 if (doc.contains("MessageArgSources")) 758 { 759 entry.doc.messageArgSources = doc["MessageArgSources"]; 760 } 761 762 // If there are callouts defined, save the JSON for later 763 if (_loadCallouts) 764 { 765 if (e->contains("Callouts")) 766 { 767 entry.callouts = (*e)["Callouts"]; 768 } 769 else if (e->contains("CalloutsUsingAD")) 770 { 771 entry.callouts = (*e)["CalloutsUsingAD"]; 772 } 773 } 774 775 if (e->contains("JournalCapture")) 776 { 777 entry.journalCapture = 778 helper::getJournalCapture((*e)["JournalCapture"]); 779 } 780 781 return entry; 782 } 783 catch (const std::exception& ex) 784 { 785 lg2::error("Found invalid message registry field. Error: {ERROR}", 786 "ERROR", ex); 787 } 788 } 789 790 return std::nullopt; 791 } 792 793 std::optional<nlohmann::json> 794 Registry::readRegistry(const std::filesystem::path& registryFile) 795 { 796 // Look in /etc first in case someone put a test file there 797 fs::path debugFile{fs::path{debugFilePath} / registryFileName}; 798 nlohmann::json registry; 799 std::ifstream file; 800 801 if (fs::exists(debugFile)) 802 { 803 lg2::info("Using debug PEL message registry"); 804 file.open(debugFile); 805 } 806 else 807 { 808 file.open(registryFile); 809 } 810 811 try 812 { 813 registry = nlohmann::json::parse(file); 814 } 815 catch (const std::exception& e) 816 { 817 lg2::error("Error parsing message registry JSON. Error: {ERROR}", 818 "ERROR", e); 819 return std::nullopt; 820 } 821 return registry; 822 } 823 824 std::vector<RegistryCallout> 825 Registry::getCallouts(const nlohmann::json& calloutJSON, 826 const std::vector<std::string>& systemNames, 827 const AdditionalData& additionalData) 828 { 829 // The JSON may either use an AdditionalData key 830 // as an index, or not. 831 if (helper::calloutUsesAdditionalData(calloutJSON)) 832 { 833 return helper::getCalloutsUsingAD(calloutJSON, systemNames, 834 additionalData); 835 } 836 837 return helper::getCalloutsWithoutAD(calloutJSON, systemNames); 838 } 839 840 } // namespace message 841 } // namespace pels 842 } // namespace openpower 843