Support IPMI Serial Transport
Support IPMI Serial interface (basic mode) based on Sec 14,
IPMI 2.0 spec. Serial transport tested with meta-evb-arm
platform with socat and ipmitool.
HW transport can be selected by meson configure flasg.
Tested:
1. socat -x pty,raw,echo=0,link=/tmp/ttyHOST2BMC tcp:localhost:5066
2. ipmitool -I serial-basic -D /tmp/ttyHOST2BMC:115200 mc info
Device ID : 0
Device Revision : 0
Firmware Revision : 2.17
IPMI Version : 2.0
Manufacturer ID : 0
Manufacturer Name : Unknown
Product ID : 0 (0x0000)
Product Name : Unknown (0x0)
Device Available : no
Provides Device SDRs : no
Additional Device Support :
Aux Firmware Rev Info :
0x00
0x00
0x00
0x00
Change-Id: I2a56390022fce7a974ed75ed6a72ad3fffed9bb6
Signed-off-by: John Chung <john.chung@arm.com>
diff --git a/transport/serialbridge/test/meson.build b/transport/serialbridge/test/meson.build
new file mode 100644
index 0000000..d1ad4cf
--- /dev/null
+++ b/transport/serialbridge/test/meson.build
@@ -0,0 +1,42 @@
+gtest = dependency('gtest', main: true, disabler: true, required: false)
+gmock = dependency('gmock', disabler: true, required: false)
+if not gtest.found() or not gmock.found()
+ gtest_opts = import('cmake').subproject_options()
+ gtest_opts.add_cmake_defines({'CMAKE_CXX_FLAGS': '-Wno-pedantic'})
+ gtest_proj = import('cmake').subproject(
+ 'googletest',
+ options: gtest_opts,
+ required: false
+ )
+ if gtest_proj.found()
+ gtest = declare_dependency(
+ dependencies: [
+ dependency('threads'),
+ gtest_proj.dependency('gtest'),
+ gtest_proj.dependency('gtest_main'),
+ ])
+ gmock = gtest_proj.dependency('gmock')
+ else
+ assert(not get_option('tests').enabled(), 'Googletest is required')
+ endif
+endif
+
+# Build/add serial_unittest to test suite
+test('transport_serial',
+ executable(
+ 'transport_serial_unittest',
+ 'serial_unittest.cpp',
+ '../serialcmd.cpp',
+ include_directories: root_inc,
+ build_by_default: false,
+ implicit_include_directories: false,
+ dependencies: [
+ sdbusplus_dep,
+ stdplus_dep,
+ phosphor_logging_dep,
+ sdeventplus_dep,
+ gtest,
+ gmock
+ ]
+ )
+)
diff --git a/transport/serialbridge/test/serial_unittest.cpp b/transport/serialbridge/test/serial_unittest.cpp
new file mode 100644
index 0000000..3fb0227
--- /dev/null
+++ b/transport/serialbridge/test/serial_unittest.cpp
@@ -0,0 +1,84 @@
+#include <transport/serialbridge/serialcmd.hpp>
+
+#include <gtest/gtest.h>
+
+namespace serialbridge
+{
+
+/**
+ * @brief Table of special characters
+ */
+std::unordered_map<uint8_t, uint8_t> testsets = {
+ {bmStart, 0xB0}, /* start */
+ {bmStop, 0xB5}, /* stop */
+ {bmHandshake, 0xB6}, /* packet handshake */
+ {bmEscape, 0xBA}, /* data escape */
+ {0x1B, 0x3B} /* escape */
+};
+
+TEST(TestSpecialCharact, getUnescapedCharact)
+{
+ uint8_t c;
+ auto channel = std::make_shared<SerialChannel>(0);
+
+ for (const auto& set : testsets)
+ {
+ c = channel->getUnescapedCharacter(set.second);
+ ASSERT_EQ(c, set.first);
+ }
+}
+
+TEST(TestSpecialCharact, processEscapedCharacter)
+{
+ std::vector<uint8_t> buffer;
+ uint8_t unescaped = 0xd0;
+ auto channel = std::make_shared<SerialChannel>(0);
+
+ channel->processEscapedCharacter(buffer, std::vector<uint8_t>{bmStart});
+
+ ASSERT_EQ(buffer.at(0), bmEscape);
+ ASSERT_EQ(buffer.at(1), testsets.at(bmStart));
+
+ buffer.clear();
+ channel->processEscapedCharacter(buffer, std::vector<uint8_t>{unescaped});
+
+ ASSERT_EQ(buffer.at(0), unescaped);
+}
+
+TEST(TestChecksum, calculateChecksum)
+{
+ std::array<uint8_t, 5> dataBytes{0x01, 0x10, 0x60, 0xf0, 0x50};
+ auto channel = std::make_shared<SerialChannel>(0);
+
+ uint8_t checksum =
+ channel->calculateChecksum(std::span<uint8_t>(dataBytes));
+
+ checksum += (~checksum) + 1;
+ ASSERT_EQ(checksum, 0);
+}
+
+TEST(TestIpmiSerialPacket, consumeIpmiSerialPacket)
+{
+ std::vector<uint8_t> dataBytes{bmStart, 0x20, 0x18, 0xc8, 0x81,
+ 0xc, 0x46, 0x01, 0x2c, bmStop};
+ std::vector<uint8_t> dataBytesSplit1{bmStart, 0x20, 0x18, 0xc8};
+ std::vector<uint8_t> dataBytesSplit2{0x81, 0xc, 0x46, 0x01, 0x2c, bmStop};
+ std::span<uint8_t> input(dataBytes);
+ std::span<uint8_t> input1(dataBytesSplit1);
+ std::span<uint8_t> input2(dataBytesSplit2);
+ std::vector<uint8_t> output;
+
+ auto channel = std::make_shared<SerialChannel>(0);
+
+ auto result = channel->consumeIpmiSerialPacket(input, output);
+
+ ASSERT_EQ(result, true);
+
+ output.clear();
+ result = channel->consumeIpmiSerialPacket(input1, output);
+ ASSERT_EQ(result, false);
+ result = channel->consumeIpmiSerialPacket(input2, output);
+ ASSERT_EQ(result, true);
+}
+
+} // namespace serialbridge