bios_setting: Add a write handler
Tested:
```
// Test that it writes a new file if it doesn't exist
~# rm /run/oem_bios_setting
// Invalid command (no size / payload)
~# ipmitool raw 0x2e 0x32 0x79 0x2b 0x00 0x19
Unable to send RAW command (channel=0x0 netfn=0x2e lun=0x0 cmd=0x32 rsp=0xc7): Request data length invalid
// Invalid command (size doesn't match)
~# ipmitool raw 0x2e 0x32 0x79 0x2b 0x00 0x19 0x01 0x30 0x31
Unable to send RAW command (channel=0x0 netfn=0x2e lun=0x0 cmd=0x32 rsp=0xc7): Request data length invalid
// Command success
~# ipmitool raw 0x2e 0x32 0x79 0x2b 0x00 0x19 0x02 0x30 0x31
79 2b 00 19 02
// Read back
~# ipmitool raw 0x2e 0x32 0x79 0x2b 0x00 0x18
79 2b 00 18 02 30 31
~# cat /run/oem_bios_setting
01~#
// Verify overwrite works (truncates the previous bytes)
~# ipmitool raw 0x2e 0x32 0x79 0x2b 0x00 0x19 0x01 0x33
79 2b 00 19 01
~# cat /run/oem_bios_setting
3~#
~# ipmitool raw 0x2e 0x32 0x79 0x2b 0x00 0x18
79 2b 00 18 01 33
```
Signed-off-by: Brandon Kim <brandonkim@google.com>
Change-Id: I84db782da9b2f121c0a81a855692b5ca25ffda54
diff --git a/bios_setting.cpp b/bios_setting.cpp
index d92f786..864b178 100644
--- a/bios_setting.cpp
+++ b/bios_setting.cpp
@@ -22,7 +22,9 @@
#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>
@@ -72,5 +74,54 @@
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