1 // Copyright 2024 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include "bios_setting.hpp" 16 17 #include "commands.hpp" 18 #include "errors.hpp" 19 #include "handler.hpp" 20 21 #include <ipmid/api-types.hpp> 22 #include <stdplus/fd/create.hpp> 23 #include <stdplus/fd/managed.hpp> 24 #include <stdplus/fd/ops.hpp> 25 #include <stdplus/numeric/endian.hpp> 26 #include <stdplus/print.hpp> 27 #include <stdplus/raw.hpp> 28 29 #include <filesystem> 30 #include <fstream> 31 #include <span> 32 #include <vector> 33 34 namespace google 35 { 36 namespace ipmi 37 { 38 39 std::vector<uint8_t> readBiosSettingFromFile(const std::string& biosSettingPath) 40 { 41 std::vector<uint8_t> biosSettings; 42 try 43 { 44 stdplus::ManagedFd managedFd = stdplus::fd::open( 45 biosSettingPath, 46 stdplus::fd::OpenFlags(stdplus::fd::OpenAccess::ReadOnly)); 47 biosSettings = stdplus::fd::readAll<std::vector<uint8_t>>(managedFd); 48 } 49 catch (const std::exception& e) 50 { 51 stdplus::print(stderr, "Read unsuccessful: {}\n", e.what()); 52 return {}; 53 } 54 return biosSettings; 55 } 56 57 Resp readBiosSetting(std::span<const uint8_t>, HandlerInterface*, 58 const std::string& biosSettingPath) 59 { 60 std::vector<uint8_t> biosSettings = 61 readBiosSettingFromFile(biosSettingPath); 62 size_t settingsLength = biosSettings.size(); 63 if (settingsLength == 0) 64 { 65 return ::ipmi::responseRetBytesUnavailable(); 66 } 67 68 // Reply format is: Length of the payload (1 byte) + payload 69 std::vector<std::uint8_t> reply; 70 reply.reserve(1 + settingsLength); 71 reply.emplace_back(static_cast<uint8_t>(settingsLength)); 72 reply.insert(reply.end(), biosSettings.begin(), biosSettings.end()); 73 74 return ::ipmi::responseSuccess(SysOEMCommands::SysReadBiosSetting, reply); 75 } 76 77 Resp writeBiosSetting(std::span<const uint8_t> data, HandlerInterface*, 78 const std::string& biosSettingPath) 79 { 80 std::uint8_t payloadSize; 81 try 82 { 83 // This subspans the data automatically 84 payloadSize = stdplus::raw::extract< 85 stdplus::EndianPacked<decltype(payloadSize), std::endian::little>>( 86 data); 87 } 88 catch (const std::exception& e) 89 { 90 stdplus::print(stderr, "Extracting payload failed: {}\n", e.what()); 91 return ::ipmi::responseReqDataLenInvalid(); 92 } 93 94 if (data.size() != payloadSize) 95 { 96 stdplus::print(stderr, "Invalid command length {} vs. payloadSize {}\n", 97 static_cast<uint32_t>(data.size()), 98 static_cast<uint32_t>(payloadSize)); 99 return ::ipmi::responseReqDataLenInvalid(); 100 } 101 102 // Write the setting 103 try 104 { 105 stdplus::ManagedFd managedFd = stdplus::fd::open( 106 biosSettingPath, 107 stdplus::fd::OpenFlags(stdplus::fd::OpenAccess::WriteOnly) 108 .set(stdplus::fd::OpenFlag::Trunc) 109 .set(stdplus::fd::OpenFlag::Create)); 110 stdplus::fd::writeExact(managedFd, data); 111 } 112 catch (const std::exception& e) 113 { 114 stdplus::print(stderr, "Write unsuccessful: {}\n", e.what()); 115 return ::ipmi::responseRetBytesUnavailable(); 116 } 117 118 // Reply format is: Length of the payload written 119 std::vector<std::uint8_t> reply; 120 reply.reserve(1); 121 reply.emplace_back(static_cast<uint8_t>(payloadSize)); 122 123 return ::ipmi::responseSuccess(SysOEMCommands::SysWriteBiosSetting, reply); 124 } 125 126 } // namespace ipmi 127 } // namespace google 128