control: Add basic MCTP Control Protocol handler
This will respond to the 4 mandatory MCTP Control Protocol commands.
Applications can register supported types using mctp_control_add_type().
Change-Id: Ia904bcbe118626adf9254ffa71dd8e17fbdfc9b7
Signed-off-by: Matt Johnston <matt@codeconstruct.com.au>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6bfb3c1..7b2506f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -22,7 +22,7 @@
add_definitions (-DMCTP_HAVE_STDIO)
add_definitions (-DMCTP_DEFAULT_ALLOC)
-add_library (mctp STATIC alloc.c astlpc.c crc32.c core.c log.c libmctp.h serial.c crc-16-ccitt.c)
+add_library (mctp STATIC alloc.c astlpc.c crc32.c core.c log.c libmctp.h serial.c crc-16-ccitt.c control.c)
target_include_directories (mctp PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
diff --git a/Makefile.am b/Makefile.am
index 984a5d4..bd7db2b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -3,7 +3,7 @@
lib_LTLIBRARIES = libmctp.la
libmctp_la_SOURCES = core.c alloc.c log.c \
libmctp-alloc.h libmctp-log.h \
- libmctp-cmds.h
+ libmctp-cmds.h control.c
include_HEADERS = libmctp.h
if LIBMCTP_BINDING_serial
diff --git a/control.c b/control.c
new file mode 100644
index 0000000..fedfe56
--- /dev/null
+++ b/control.c
@@ -0,0 +1,309 @@
+#include <string.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <errno.h>
+
+#include "libmctp-cmds.h"
+#include "libmctp-alloc.h"
+#include "libmctp-log.h"
+#include "core-internal.h"
+
+#include "control.h"
+
+static void fill_resp(const void *req, struct mctp_ctrl_msg_hdr *hdr)
+{
+ const struct mctp_ctrl_msg_hdr *req_hdr = req;
+ hdr->ic_msg_type = MCTP_CTRL_HDR_MSG_TYPE;
+ hdr->rq_dgram_inst = req_hdr->rq_dgram_inst &
+ MCTP_CTRL_HDR_INSTANCE_ID_MASK;
+ hdr->command_code = req_hdr->command_code;
+}
+
+static uint8_t mctp_ctrl_set_endpoint_id(struct mctp_bus *bus, uint8_t src_eid,
+ uint8_t msg_tag, const void *data,
+ size_t len)
+{
+ if (len != sizeof(struct mctp_ctrl_cmd_set_endpoint_id_req)) {
+ return MCTP_CTRL_CC_ERROR_INVALID_LENGTH;
+ }
+ const struct mctp_ctrl_cmd_set_endpoint_id_req *req = data;
+
+ uint8_t op = req->operation & MCTP_CTRL_SET_EID_OP_MASK;
+ if (!(op == MCTP_CTRL_SET_EID_OP_SET ||
+ op == MCTP_CTRL_SET_EID_OP_FORCE)) {
+ return MCTP_CTRL_CC_ERROR_INVALID_DATA;
+ }
+
+ if (mctp_bus_set_eid(bus->binding, req->eid)) {
+ return MCTP_CTRL_CC_ERROR_INVALID_DATA;
+ }
+
+ struct mctp_ctrl_cmd_set_endpoint_id_resp *resp =
+ __mctp_msg_alloc(sizeof(*resp), bus->mctp);
+ if (!resp) {
+ mctp_prdebug("no response buffer");
+ return MCTP_CTRL_CC_ERROR;
+ }
+ memset(resp, 0x00, sizeof(*resp));
+ fill_resp(data, &resp->hdr);
+ resp->completion_code = MCTP_CTRL_CC_SUCCESS;
+ resp->status = MCTP_CTRL_SET_EID_STATUS_ACCEPTED;
+ resp->eid = req->eid;
+ resp->pool_size = 0;
+
+ int rc = mctp_message_tx_alloced(bus->mctp, src_eid, false, msg_tag,
+ resp, sizeof(*resp));
+ if (!rc) {
+ mctp_prdebug("set_endpoint_id response send failed: %d", rc);
+ }
+ return MCTP_CTRL_CC_SUCCESS;
+}
+
+static uint8_t mctp_ctrl_get_endpoint_id(struct mctp_bus *bus, uint8_t src_eid,
+ uint8_t msg_tag, const void *data,
+ size_t len)
+{
+ if (len != sizeof(struct mctp_ctrl_msg_hdr)) {
+ /* Expect empty request */
+ return MCTP_CTRL_CC_ERROR_INVALID_LENGTH;
+ }
+ (void)data;
+
+ struct mctp_ctrl_cmd_get_endpoint_id_resp *resp =
+ __mctp_msg_alloc(sizeof(*resp), bus->mctp);
+ if (!resp) {
+ mctp_prdebug("no response buffer");
+ return MCTP_CTRL_CC_ERROR;
+ }
+ memset(resp, 0x00, sizeof(*resp));
+ fill_resp(data, &resp->hdr);
+ resp->completion_code = MCTP_CTRL_CC_SUCCESS;
+ resp->endpoint_id = bus->eid;
+ resp->endpoint_type = MCTP_CTRL_ENDPOINT_TYPE_SIMPLE |
+ MCTP_CTRL_ENDPOINT_ID_TYPE_STATIC;
+ resp->medium_specific = 0x00;
+
+ int rc = mctp_message_tx_alloced(bus->mctp, src_eid, false, msg_tag,
+ resp, sizeof(*resp));
+ if (!rc) {
+ mctp_prdebug("get_endpoint_id response send failed: %d", rc);
+ }
+ return MCTP_CTRL_CC_SUCCESS;
+}
+
+#define MCTP_PROTOCOL_COUNT 4
+/* Big endian */
+const uint8_t MCTP_PROTOCOL_VERSIONS[MCTP_PROTOCOL_COUNT * 4] = {
+ // 1.0
+ 0xf1,
+ 0xf0,
+ 0xff,
+ 0x00,
+ // 1.1
+ 0xf1,
+ 0xf1,
+ 0xff,
+ 0x00,
+ // 1.2
+ 0xf1,
+ 0xf2,
+ 0xff,
+ 0x00,
+ // 1.3.3
+ 0xf1,
+ 0xf3,
+ 0xf3,
+ 0x00,
+};
+
+static uint8_t mctp_ctrl_get_version(struct mctp_bus *bus, uint8_t src_eid,
+ uint8_t msg_tag, const void *data,
+ size_t len)
+{
+ if (len != sizeof(struct mctp_ctrl_cmd_get_version_req)) {
+ return MCTP_CTRL_CC_ERROR_INVALID_LENGTH;
+ }
+ const struct mctp_ctrl_cmd_get_version_req *req = data;
+
+ switch (req->msg_type) {
+ case 0x00:
+ case 0xff:
+ /* Only have versions for MCTP base or control */
+ break;
+ default:
+ return MCTP_CTRL_VERSIONS_NOT_SUPPORTED;
+ }
+
+ /* Return only the versions for MCTP */
+ size_t total_sz = sizeof(struct mctp_ctrl_cmd_get_version_resp) +
+ sizeof(MCTP_PROTOCOL_VERSIONS);
+
+ struct mctp_ctrl_cmd_get_version_resp *resp =
+ __mctp_msg_alloc(total_sz, bus->mctp);
+ if (!resp) {
+ mctp_prdebug("no response buffer");
+ return MCTP_CTRL_CC_ERROR;
+ }
+ memset(resp, 0x00, total_sz);
+ fill_resp(data, &resp->hdr);
+ resp->completion_code = MCTP_CTRL_CC_SUCCESS;
+ resp->version_count = MCTP_PROTOCOL_COUNT;
+ memcpy(resp->versions, MCTP_PROTOCOL_VERSIONS,
+ sizeof(MCTP_PROTOCOL_VERSIONS));
+
+ int rc = mctp_message_tx_alloced(bus->mctp, src_eid, false, msg_tag,
+ resp, total_sz);
+ if (!rc) {
+ mctp_prdebug("mctp get_version response send failed: %d", rc);
+ }
+ return MCTP_CTRL_CC_SUCCESS;
+}
+
+static uint8_t mctp_ctrl_get_types(struct mctp_bus *bus, uint8_t src_eid,
+ uint8_t msg_tag, const void *data,
+ size_t len)
+{
+ if (len != sizeof(struct mctp_ctrl_msg_hdr)) {
+ return MCTP_CTRL_CC_ERROR_INVALID_LENGTH;
+ }
+ (void)data;
+
+ size_t total_sz = sizeof(struct mctp_ctrl_cmd_get_types_resp) +
+ bus->mctp->control.num_msg_types;
+
+ struct mctp_ctrl_cmd_get_types_resp *resp =
+ __mctp_msg_alloc(total_sz, bus->mctp);
+ if (!resp) {
+ mctp_prdebug("no response buffer");
+ return MCTP_CTRL_CC_ERROR;
+ }
+ memset(resp, 0x00, total_sz);
+ fill_resp(data, &resp->hdr);
+ resp->completion_code = MCTP_CTRL_CC_SUCCESS;
+ resp->type_count = bus->mctp->control.num_msg_types;
+ memcpy(resp->types, bus->mctp->control.msg_types,
+ bus->mctp->control.num_msg_types);
+
+ int rc = mctp_message_tx_alloced(bus->mctp, src_eid, false, msg_tag,
+ resp, total_sz);
+ if (!rc) {
+ mctp_prdebug("mctp get_types response send failed: %d", rc);
+ }
+ return MCTP_CTRL_CC_SUCCESS;
+}
+
+static void reply_error(struct mctp *mctp, uint8_t src_eid, uint8_t msg_tag,
+ const struct mctp_ctrl_msg_hdr *ctrl_hdr, uint8_t ccode)
+{
+ struct mctp_ctrl_cmd_empty_resp *resp =
+ __mctp_msg_alloc(sizeof(*resp), mctp);
+ if (!resp) {
+ mctp_prdebug("no response buffer");
+ return;
+ }
+ memset(resp, 0x00, sizeof(*resp));
+ fill_resp(ctrl_hdr, &resp->hdr);
+ resp->completion_code = ccode;
+
+ int rc = mctp_message_tx_alloced(mctp, src_eid, false, msg_tag, resp,
+ sizeof(*resp));
+ if (!rc) {
+ mctp_prdebug("error response send failed: %d", rc);
+ }
+}
+
+/* Control message request handler. This will respond to the mandatory MCTP control
+ * commands */
+bool mctp_control_handler(struct mctp_bus *bus, mctp_eid_t src_eid,
+ bool tag_owner, uint8_t msg_tag, const void *data,
+ size_t len)
+{
+ if (!tag_owner) {
+ // Not a request
+ return false;
+ }
+
+ if (len < 1) {
+ // No type byte
+ return false;
+ }
+
+ const struct mctp_ctrl_msg_hdr *ctrl_hdr = data;
+ if (ctrl_hdr->ic_msg_type != MCTP_CTRL_HDR_MSG_TYPE) {
+ // Not Control type
+ return false;
+ }
+
+ if (len < sizeof(struct mctp_ctrl_msg_hdr)) {
+ // Drop short messages, but treat as handled
+ return true;
+ }
+
+ if ((ctrl_hdr->rq_dgram_inst &
+ (MCTP_CTRL_HDR_FLAG_REQUEST | MCTP_CTRL_HDR_FLAG_DGRAM)) !=
+ MCTP_CTRL_HDR_FLAG_REQUEST) {
+ // Drop message, isn't a request.
+ // Treat as handled since TO bit was set.
+ return true;
+ }
+
+ // A valid MCTP Control request has been received, process it
+
+ uint8_t cc = MCTP_CTRL_CC_ERROR_UNSUPPORTED_CMD;
+ switch (ctrl_hdr->command_code) {
+ case MCTP_CTRL_CMD_SET_ENDPOINT_ID:
+ cc = mctp_ctrl_set_endpoint_id(bus, src_eid, msg_tag, data,
+ len);
+ break;
+ case MCTP_CTRL_CMD_GET_ENDPOINT_ID:
+ cc = mctp_ctrl_get_endpoint_id(bus, src_eid, msg_tag, data,
+ len);
+ break;
+ case MCTP_CTRL_CMD_GET_VERSION_SUPPORT:
+ cc = mctp_ctrl_get_version(bus, src_eid, msg_tag, data, len);
+ break;
+ case MCTP_CTRL_CMD_GET_MESSAGE_TYPE_SUPPORT:
+ cc = mctp_ctrl_get_types(bus, src_eid, msg_tag, data, len);
+ break;
+ default:
+ cc = MCTP_CTRL_CC_ERROR_UNSUPPORTED_CMD;
+ break;
+ }
+
+ if (cc) {
+ reply_error(bus->mctp, src_eid, msg_tag, ctrl_hdr, cc);
+ }
+
+ // No further handling required.
+ return true;
+}
+
+int mctp_control_add_type(struct mctp *mctp, uint8_t msg_type)
+{
+ /* Check for existing */
+ for (size_t i = 0; i < mctp->control.num_msg_types; i++) {
+ if (mctp->control.msg_types[i] == msg_type) {
+ return 0;
+ }
+ }
+
+ if (mctp->control.num_msg_types == MCTP_CONTROL_MAX_TYPES) {
+ return -ENOSPC;
+ }
+
+ mctp->control.msg_types[mctp->control.num_msg_types] = msg_type;
+ mctp->control.num_msg_types++;
+ return 0;
+}
+
+void mctp_control_remove_type(struct mctp *mctp, uint8_t msg_type)
+{
+ for (size_t i = 0; i < mctp->control.num_msg_types; i++) {
+ if (mctp->control.msg_types[i] == msg_type) {
+ memmove(&mctp->control.msg_types[i],
+ &mctp->control.msg_types[i + 1],
+ mctp->control.num_msg_types - (i + 1));
+ mctp->control.num_msg_types--;
+ }
+ }
+}
diff --git a/control.h b/control.h
new file mode 100644
index 0000000..579ea55
--- /dev/null
+++ b/control.h
@@ -0,0 +1,11 @@
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "libmctp.h"
+
+/* Handle a MCTP control message. Returns true for control requests,
+ * false otherwise */
+bool mctp_control_handler(struct mctp_bus *bus, uint8_t src_eid, bool tag_owner,
+ uint8_t msg_tag, const void *data, size_t len);
diff --git a/core-internal.h b/core-internal.h
index c8e7b25..ef0fc83 100644
--- a/core-internal.h
+++ b/core-internal.h
@@ -28,6 +28,10 @@
#define MCTP_DEFAULT_CLOCK_GETTIME 1
#endif
+#ifndef MCTP_CONTROL_HANDLER
+#define MCTP_CONTROL_HANDLER 1
+#endif
+
/* Tag expiry timeout, in milliseconds */
static const uint64_t MCTP_TAG_TIMEOUT = 6000;
@@ -84,6 +88,14 @@
uint64_t expiry;
};
+#define MCTP_CONTROL_MAX_TYPES 10
+
+struct mctp_control {
+ /* Types to report from Get MCTP Version Support */
+ uint8_t msg_types[MCTP_CONTROL_MAX_TYPES];
+ size_t num_msg_types;
+};
+
struct mctp {
int n_busses;
struct mctp_bus busses[MCTP_MAX_BUSSES];
@@ -110,6 +122,10 @@
} route_policy;
size_t max_message_size;
+#if MCTP_CONTROL_HANDLER
+ struct mctp_control control;
+#endif
+
void *alloc_ctx;
uint64_t (*platform_now)(void *);
diff --git a/core.c b/core.c
index e28f6bc..8a0dc13 100644
--- a/core.c
+++ b/core.c
@@ -19,6 +19,7 @@
#include "range.h"
#include "compiler.h"
#include "core-internal.h"
+#include "control.h"
#if MCTP_DEFAULT_CLOCK_GETTIME
#include <time.h>
@@ -266,6 +267,9 @@
#if MCTP_DEFAULT_CLOCK_GETTIME
mctp->platform_now = mctp_default_now;
#endif
+#if MCTP_CONTROL_HANDLER
+ mctp_control_add_type(mctp, MCTP_CTRL_HDR_MSG_TYPE);
+#endif
return 0;
}
@@ -360,6 +364,16 @@
return rc;
}
+int mctp_bus_set_eid(struct mctp_binding *binding, mctp_eid_t eid)
+{
+ if (eid < 8 || eid == 0xff) {
+ return -EINVAL;
+ }
+
+ binding->bus->eid = eid;
+ return 0;
+}
+
void mctp_unregister_bus(struct mctp *mctp, struct mctp_binding *binding)
{
/*
@@ -444,6 +458,12 @@
buffer, length);
return true;
}
+ } else {
+#if MCTP_CONTROL_HANDLER
+ /* libmctp will handle control requests */
+ return mctp_control_handler(bus, src, tag_owner, msg_tag,
+ buffer, length);
+#endif
}
/*
diff --git a/libmctp-cmds.h b/libmctp-cmds.h
index 80f3513..406a096 100644
--- a/libmctp-cmds.h
+++ b/libmctp-cmds.h
@@ -66,6 +66,75 @@
#define MCTP_CTRL_CC_ERROR_UNSUPPORTED_CMD 0x05
/* 0x80 - 0xFF are command specific */
+struct mctp_ctrl_cmd_empty_resp {
+ struct mctp_ctrl_msg_hdr hdr;
+ uint8_t completion_code;
+} __attribute__((packed));
+
+/* Set Endpoint ID request, Operation. Bits [1:0] */
+#define MCTP_CTRL_SET_EID_OP_MASK 0x03
+#define MCTP_CTRL_SET_EID_OP_SET 0x00
+#define MCTP_CTRL_SET_EID_OP_FORCE 0x01
+#define MCTP_CTRL_SET_EID_OP_RESET 0x02
+#define MCTP_CTRL_SET_EID_OP_SET_DISCOVERED 0x03
+
+struct mctp_ctrl_cmd_set_endpoint_id_req {
+ struct mctp_ctrl_msg_hdr hdr;
+ uint8_t operation;
+ uint8_t eid;
+} __attribute__((packed));
+
+/* Set Endpoint ID response, assignment status. Bits [1:0] */
+#define MCTP_CTRL_SET_EID_STATUS_ACCEPTED 0x00
+#define MCTP_CTRL_SET_EID_STATUS_REJECTED 0x01
+
+struct mctp_ctrl_cmd_set_endpoint_id_resp {
+ struct mctp_ctrl_msg_hdr hdr;
+ uint8_t completion_code;
+ uint8_t status;
+ uint8_t eid;
+ uint8_t pool_size;
+} __attribute__((packed));
+
+/* Get Endpoint ID, Endpoint Type. Bits [5:4] */
+#define MCTP_CTRL_ENDPOINT_TYPE_SIMPLE 0x00
+#define MCTP_CTRL_ENDPOINT_TYPE_BUSOWNER_BRIDGE 0x10
+
+/* Get Endpoint ID, Endpoint ID Type. Bits [1:0] */
+#define MCTP_CTRL_ENDPOINT_ID_TYPE_DYNAMIC_ONLY 0x00
+#define MCTP_CTRL_ENDPOINT_ID_TYPE_STATIC 0x01
+#define MCTP_CTRL_ENDPOINT_ID_TYPE_STATIC_SAME 0x02
+#define MCTP_CTRL_ENDPOINT_ID_TYPE_STATIC_DIFFERENT 0x03
+
+struct mctp_ctrl_cmd_get_endpoint_id_resp {
+ struct mctp_ctrl_msg_hdr hdr;
+ uint8_t completion_code;
+ uint8_t endpoint_id;
+ uint8_t endpoint_type;
+ uint8_t medium_specific;
+} __attribute__((packed));
+
+#define MCTP_CTRL_VERSIONS_NOT_SUPPORTED 0x80
+
+struct mctp_ctrl_cmd_get_version_req {
+ struct mctp_ctrl_msg_hdr hdr;
+ uint8_t msg_type;
+} __attribute__((packed));
+
+struct mctp_ctrl_cmd_get_version_resp {
+ struct mctp_ctrl_msg_hdr hdr;
+ uint8_t completion_code;
+ uint8_t version_count;
+ uint32_t versions[];
+} __attribute__((packed));
+
+struct mctp_ctrl_cmd_get_types_resp {
+ struct mctp_ctrl_msg_hdr hdr;
+ uint8_t completion_code;
+ uint8_t type_count;
+ uint8_t types[];
+} __attribute__((packed));
+
#ifdef __cplusplus
}
#endif
diff --git a/libmctp.h b/libmctp.h
index f78132b..2fdd812 100644
--- a/libmctp.h
+++ b/libmctp.h
@@ -110,6 +110,8 @@
void mctp_unregister_bus(struct mctp *mctp, struct mctp_binding *binding);
+int mctp_bus_set_eid(struct mctp_binding *binding, mctp_eid_t eid);
+
/* Create a simple bidirectional bridge between busses.
*
* In this mode, the MCTP stack is initialised as a bridge. There is no EID
@@ -233,6 +235,14 @@
/* Returns a timestamp in milliseconds */
uint64_t mctp_now(struct mctp *mctp);
+int mctp_control_handler_enable(struct mctp *mctp);
+void mctp_control_handler_disable(struct mctp *mctp);
+
+/* Add/remove message types to be reported by Get MCTP Version Support.
+ * Control type is added automatically for the control handler */
+int mctp_control_add_type(struct mctp *mctp, uint8_t msg_type);
+void mctp_control_remove_type(struct mctp *mctp, uint8_t msg_type);
+
#ifdef __cplusplus
}
#endif
diff --git a/meson.build b/meson.build
index fa6ff81..28c0126 100644
--- a/meson.build
+++ b/meson.build
@@ -14,6 +14,7 @@
sources = [
'core.c',
'alloc.c',
+ 'control.c',
]
headers = [
@@ -45,6 +46,9 @@
i2c_headers = [
'libmctp-i2c.h',
]
+control_sources = [
+ 'control.c',
+]
libmctp_sources = sources
libmctp_headers = headers
@@ -61,6 +65,9 @@
libmctp_sources += i2c_sources
libmctp_headers += i2c_headers
endif
+if get_option('control')
+ libmctp_sources += control_sources
+endif
compiler = meson.get_compiler('c')
diff --git a/meson.options b/meson.options
index e3d9f9c..18b594e 100644
--- a/meson.options
+++ b/meson.options
@@ -43,3 +43,9 @@
value: false,
description: 'Don\'t include any logging functionality',
)
+option(
+ 'control',
+ type: 'boolean',
+ value: true,
+ description: 'Include MCTP control protocol handler',
+)