| // Copyright 2024 Google LLC |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "bios_setting.hpp" |
| |
| #include "commands.hpp" |
| #include "errors.hpp" |
| #include "handler.hpp" |
| |
| #include <ipmid/api-types.hpp> |
| #include <stdplus/fd/create.hpp> |
| #include <stdplus/fd/managed.hpp> |
| #include <stdplus/fd/ops.hpp> |
| #include <stdplus/numeric/endian.hpp> |
| #include <stdplus/print.hpp> |
| #include <stdplus/raw.hpp> |
| |
| #include <filesystem> |
| #include <fstream> |
| #include <span> |
| #include <vector> |
| |
| namespace google |
| { |
| namespace ipmi |
| { |
| |
| std::vector<uint8_t> readBiosSettingFromFile(const std::string& biosSettingPath) |
| { |
| std::vector<uint8_t> biosSettings; |
| try |
| { |
| stdplus::ManagedFd managedFd = stdplus::fd::open( |
| biosSettingPath, |
| stdplus::fd::OpenFlags(stdplus::fd::OpenAccess::ReadOnly)); |
| biosSettings = stdplus::fd::readAll<std::vector<uint8_t>>(managedFd); |
| } |
| catch (const std::exception& e) |
| { |
| stdplus::print(stderr, "Read unsuccessful: {}\n", e.what()); |
| return {}; |
| } |
| return biosSettings; |
| } |
| |
| Resp readBiosSetting(std::span<const uint8_t>, HandlerInterface*, |
| const std::string& biosSettingPath) |
| { |
| std::vector<uint8_t> biosSettings = |
| readBiosSettingFromFile(biosSettingPath); |
| size_t settingsLength = biosSettings.size(); |
| if (settingsLength == 0) |
| { |
| return ::ipmi::responseRetBytesUnavailable(); |
| } |
| |
| // Reply format is: Length of the payload (1 byte) + payload |
| std::vector<std::uint8_t> reply; |
| reply.reserve(1 + settingsLength); |
| reply.emplace_back(static_cast<uint8_t>(settingsLength)); |
| reply.insert(reply.end(), biosSettings.begin(), biosSettings.end()); |
| |
| return ::ipmi::responseSuccess(SysOEMCommands::SysReadBiosSetting, reply); |
| } |
| |
| Resp writeBiosSetting(std::span<const uint8_t> data, HandlerInterface*, |
| const std::string& biosSettingPath) |
| { |
| std::uint8_t payloadSize; |
| try |
| { |
| // This subspans the data automatically |
| payloadSize = stdplus::raw::extract< |
| stdplus::EndianPacked<decltype(payloadSize), std::endian::little>>( |
| data); |
| } |
| catch (const std::exception& e) |
| { |
| stdplus::print(stderr, "Extracting payload failed: {}\n", e.what()); |
| return ::ipmi::responseReqDataLenInvalid(); |
| } |
| |
| if (data.size() != payloadSize) |
| { |
| stdplus::print(stderr, "Invalid command length {} vs. payloadSize {}\n", |
| static_cast<uint32_t>(data.size()), |
| static_cast<uint32_t>(payloadSize)); |
| return ::ipmi::responseReqDataLenInvalid(); |
| } |
| |
| // Write the setting |
| try |
| { |
| stdplus::ManagedFd managedFd = stdplus::fd::open( |
| biosSettingPath, |
| stdplus::fd::OpenFlags(stdplus::fd::OpenAccess::WriteOnly) |
| .set(stdplus::fd::OpenFlag::Trunc) |
| .set(stdplus::fd::OpenFlag::Create)); |
| stdplus::fd::writeExact(managedFd, data); |
| } |
| catch (const std::exception& e) |
| { |
| stdplus::print(stderr, "Write unsuccessful: {}\n", e.what()); |
| return ::ipmi::responseRetBytesUnavailable(); |
| } |
| |
| // Reply format is: Length of the payload written |
| std::vector<std::uint8_t> reply; |
| reply.reserve(1); |
| reply.emplace_back(static_cast<uint8_t>(payloadSize)); |
| |
| return ::ipmi::responseSuccess(SysOEMCommands::SysWriteBiosSetting, reply); |
| } |
| |
| } // namespace ipmi |
| } // namespace google |