From 2950eda4bd5e698ff18b0eecaf360aaf3649f444 Mon Sep 17 00:00:00 2001 From: Nidhin MS Date: Tue, 22 Jul 2025 11:50:16 +0530 Subject: [PATCH] Add RegisterTypeSupport dbus API GetMsgType response is hardcoded and always return only control message support. Add a dbus method RegisterTypeSupport which accept msg type and versions array as input and reply accordingly in GetMsgType and GetVersin control commands Tested by sending GetMsgType and GetVersion from another EP Default responses for GetMsgType and GetVersion for type 0 5, 0, 1, 0 and 4, 0, 4, 241, 240, 255, 0, 241, 241, 255, 0, 241, 242, 255, 0, 241, 243, 241, 0 After calling au.com.codeconstruct.MCTP1 /au/com/codeconstruct/mctp1 au.com.codeconstruct.MCTP1 RegisterTypeSupport yau 5 1 0xF1F2F3F4 with a tool which stays alive after method call Response for GetMsgType and GetVersion for type 5 5, 0, 2, 0, 5 and 4, 0, 1, 244, 243, 242, 241 Signed-off-by: Nidhin MS --- docs/mctpd.md | 35 ++++- src/mctpd.c | 273 ++++++++++++++++++++++++++++++++++----- tests/mctp_test_utils.py | 6 + tests/test_mctpd.py | 25 +++- 4 files changed, 307 insertions(+), 32 deletions(-) diff --git a/docs/mctpd.md b/docs/mctpd.md index f60f568..eee8e81 100644 --- a/docs/mctpd.md +++ b/docs/mctpd.md @@ -25,8 +25,39 @@ Service au.com.codeconstruct.MCTP1: ## Top-level object: `/au/com/codeconstruct/mctp1` -This object serves as the global MCTP daemon namespace; it doesn't contain -much at present, but hosts two trees of MCTP objects: +This object serves as the global MCTP daemon namespace. +It hosts `au.com.codeconstruct.MCTP1` dbus interface to modify mctp properties like +supported message types. +``` +NAME TYPE SIGNATURE RESULT/VALUE FLAGS +au.com.codeconstruct.MCTP1 interface - - - +.RegisterTypeSupport method yau - - +``` + +#### `.RegisterTypeSupport`: `yau` + +This method is used to add support for mctp message types other than control +messages. Once called successfully subsequent response for get message type +control command will include this new message type also. Also the versions +passed to this method will be used to respond to get version control command. + +`RegisterTypeSupport ` + +If message type is already registered then dbus call will fail + +`` Message type defined in DSP0239 + +`` Versions supported for this message type formatted as uint32 integers as +specified in DSP0236 + +Example for PLDM type with two versions: + +```shell +busctl call au.com.codeconstruct.MCTP1 /au/com/codeconstruct/mctp1 \ + au.com.codeconstruct.MCTP1 RegisterTypeSupport yau 1 2 0xF1F2F3F4 0xF0F0F0F1 +``` + +Also it hosts two trees of MCTP objects: * Interfaces: Local hardware transport bindings that connect us to a MCTP bus * Endpoints: MCTP endpoints that `mctpd` knows about, both remote and local diff --git a/src/mctpd.c b/src/mctpd.c index 7ab68f4..91bcd4f 100644 --- a/src/mctpd.c +++ b/src/mctpd.c @@ -201,6 +201,13 @@ struct peer { uint8_t pool_start; }; +struct msg_type_support { + uint8_t msg_type; + uint32_t *versions; + size_t num_versions; + sd_bus_track *source_peer; +}; + struct ctx { sd_event *event; sd_bus *bus; @@ -232,6 +239,10 @@ struct ctx { uint8_t uuid[16]; + // Supported message types and their versions + struct msg_type_support *supported_msg_types; + size_t num_supported_msg_types; + // Verbose logging bool verbose; @@ -838,9 +849,10 @@ handle_control_get_version_support(struct ctx *ctx, int sd, struct mctp_ctrl_cmd_get_mctp_ver_support *req = NULL; struct mctp_ctrl_resp_get_mctp_ver_support *resp = NULL; uint32_t *versions = NULL; - // space for 4 versions - uint8_t respbuf[sizeof(*resp) + 4 * sizeof(*versions)] = { 0 }; - size_t resp_len; + uint8_t *respbuf = NULL; + size_t resp_len, i, ver_count = 0, ver_bytes_count; + ssize_t ver_idx = -1; + int status; if (buf_size < sizeof(struct mctp_ctrl_cmd_get_mctp_ver_support)) { warnx("short Get Version Support message"); @@ -848,29 +860,51 @@ handle_control_get_version_support(struct ctx *ctx, int sd, } req = (void *)buf; - resp = (void *)respbuf; - mctp_ctrl_msg_hdr_init_resp(&resp->ctrl_hdr, req->ctrl_hdr); - versions = (void *)(resp + 1); - switch (req->msg_type_number) { - case 0xff: // Base Protocol - case 0x00: // Control protocol - // from DSP0236 1.3.1 section 12.6.2. Big endian. - versions[0] = htonl(0xF1F0FF00); - versions[1] = htonl(0xF1F1FF00); - versions[2] = htonl(0xF1F2FF00); - versions[3] = htonl(0xF1F3F100); - resp->number_of_entries = 4; - resp->completion_code = MCTP_CTRL_CC_SUCCESS; - resp_len = sizeof(*resp) + 4 * sizeof(*versions); - break; - default: - // Unsupported message type + if (req->msg_type_number == 0xFF) { + // use same version for base spec and control protocol + req->msg_type_number = 0; + } + for (i = 0; i < ctx->num_supported_msg_types; i++) { + if (ctx->supported_msg_types[i].msg_type == + req->msg_type_number) { + ver_idx = i; + break; + } + } + + if (ver_idx < 0) { + respbuf = malloc(sizeof(struct mctp_ctrl_resp)); + if (!respbuf) { + warnx("Failed to allocate response buffer"); + return -ENOMEM; + } + resp = (void *)respbuf; + // Nobody registered yet as responder for this type resp->completion_code = MCTP_CTRL_CC_GET_MCTP_VER_SUPPORT_UNSUPPORTED_TYPE; - resp_len = sizeof(*resp); + resp_len = sizeof(struct mctp_ctrl_resp); + } else { + ver_count = ctx->supported_msg_types[ver_idx].num_versions; + ver_bytes_count = ver_count * sizeof(uint32_t); + respbuf = malloc(sizeof(*resp) + ver_bytes_count); + if (!respbuf) { + warnx("Failed to allocate response buffer for versions"); + return -ENOMEM; + } + resp = (void *)respbuf; + resp->number_of_entries = ver_count; + versions = (void *)(resp + 1); + memcpy(versions, ctx->supported_msg_types[ver_idx].versions, + ver_bytes_count); + resp->completion_code = MCTP_CTRL_CC_SUCCESS; + resp_len = sizeof(*resp) + ver_bytes_count; } - return reply_message(ctx, sd, resp, resp_len, addr); + mctp_ctrl_msg_hdr_init_resp(&resp->ctrl_hdr, req->ctrl_hdr); + + status = reply_message(ctx, sd, resp, resp_len, addr); + free(respbuf); + return status; } static int handle_control_get_endpoint_id(struct ctx *ctx, int sd, @@ -927,8 +961,9 @@ static int handle_control_get_message_type_support( { struct mctp_ctrl_cmd_get_msg_type_support *req = NULL; struct mctp_ctrl_resp_get_msg_type_support *resp = NULL; - uint8_t resp_buf[sizeof(*resp) + 1] = { 0 }; - size_t resp_len; + uint8_t *resp_buf, *msg_types; + size_t resp_len, type_count; + size_t i; if (buf_size < sizeof(*req)) { warnx("short Get Message Type Support message"); @@ -936,15 +971,29 @@ static int handle_control_get_message_type_support( } req = (void *)buf; + type_count = ctx->num_supported_msg_types; + // Allocate extra space for the message types + resp_len = sizeof(*resp) + type_count; + resp_buf = malloc(resp_len); + if (!resp_buf) { + warnx("Failed to allocate response buffer"); + return -ENOMEM; + } + resp = (void *)resp_buf; mctp_ctrl_msg_hdr_init_resp(&resp->ctrl_hdr, req->ctrl_hdr); + resp->completion_code = MCTP_CTRL_CC_SUCCESS; - // Only control messages supported - resp->msg_type_count = 1; - *((uint8_t *)(resp + 1)) = MCTP_CTRL_HDR_MSG_TYPE; - resp_len = sizeof(*resp) + resp->msg_type_count; + resp->msg_type_count = type_count; + // Append message types after msg_type_count + msg_types = (uint8_t *)(resp + 1); + for (i = 0; i < type_count; i++) { + msg_types[i] = ctx->supported_msg_types[i].msg_type; + } - return reply_message(ctx, sd, resp, resp_len, addr); + int result = reply_message(ctx, sd, resp, resp_len, addr); + free(resp_buf); + return result; } static int @@ -3319,6 +3368,113 @@ static int method_net_learn_endpoint(sd_bus_message *call, void *data, return rc; } +int on_dbus_peer_removed(sd_bus_track *track, void *userdata) +{ + struct ctx *ctx = userdata; + size_t i, msg_types = ctx->num_supported_msg_types; + + for (i = 0; i < msg_types; i++) { + if (ctx->supported_msg_types[i].source_peer == track) { + free(ctx->supported_msg_types[i].versions); + if (i < (msg_types - 1)) { + ctx->supported_msg_types[i] = + ctx->supported_msg_types[msg_types - 1]; + } + ctx->num_supported_msg_types--; + break; + } + } + sd_bus_track_unref(track); + return 0; +} + +static int method_register_responder(sd_bus_message *call, void *data, + sd_bus_error *berr) +{ + struct ctx *ctx = data; + uint8_t msg_type; + const uint32_t *versions = NULL; + size_t i, versions_len; + int rc; + + rc = sd_bus_message_read(call, "y", &msg_type); + if (rc < 0) + goto err; + rc = sd_bus_message_read_array(call, 'u', (const void **)&versions, + &versions_len); + if (rc < 0) + goto err; + + if (versions_len == 0) { + warnx("No versions provided for message type %d", msg_type); + return sd_bus_error_setf( + berr, SD_BUS_ERROR_INVALID_ARGS, + "No versions provided for message type %d", msg_type); + } + + for (i = 0; i < ctx->num_supported_msg_types; i++) { + if (ctx->supported_msg_types[i].msg_type == msg_type) { + warnx("Message type %d already registered", msg_type); + return sd_bus_error_setf( + berr, SD_BUS_ERROR_INVALID_ARGS, + "Message type %d already registered", msg_type); + } + } + + struct msg_type_support *msg_types = + realloc(ctx->supported_msg_types, + (ctx->num_supported_msg_types + 1) * + sizeof(struct msg_type_support)); + if (!msg_types) { + return sd_bus_error_setf( + berr, SD_BUS_ERROR_NO_MEMORY, + "Failed to allocate memory for message types"); + } + ctx->supported_msg_types = msg_types; + struct msg_type_support *cur_msg_type = + &ctx->supported_msg_types[ctx->num_supported_msg_types]; + + cur_msg_type->source_peer = NULL; + cur_msg_type->versions = NULL; + rc = sd_bus_track_new(ctx->bus, &cur_msg_type->source_peer, + on_dbus_peer_removed, ctx); + if (rc < 0) { + warnx("Failed to create dbus track for message type %d: %s", + msg_type, strerror(-rc)); + goto track_err; + } + + rc = sd_bus_track_add_sender(cur_msg_type->source_peer, call); + if (rc < 0) { + warnx("Failed to add dbus track for message type %d: %s", + msg_type, strerror(-rc)); + goto track_err; + } + + cur_msg_type->msg_type = msg_type; + cur_msg_type->num_versions = versions_len / sizeof(uint32_t); + cur_msg_type->versions = malloc(versions_len); + if (!cur_msg_type->versions) { + goto track_err; + } + // Assume callers's responsibility to provide version in uint32 format from spec + memcpy(cur_msg_type->versions, versions, versions_len); + + ctx->num_supported_msg_types++; + + return sd_bus_reply_method_return(call, ""); + +track_err: + // Extra memory for last msg type will remain allocated but tracked + sd_bus_track_unref(cur_msg_type->source_peer); + set_berr(ctx, rc, berr); + return rc; + +err: + set_berr(ctx, rc, berr); + return rc; +} + // clang-format off static const sd_bus_vtable bus_link_owner_vtable[] = { SD_BUS_VTABLE_START(0), @@ -3672,6 +3828,17 @@ static const sd_bus_vtable bus_network_vtable[] = { SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_VTABLE_END }; + +static const sd_bus_vtable mctp_base_vtable[] = { + SD_BUS_VTABLE_START(0), + SD_BUS_METHOD_WITH_ARGS("RegisterTypeSupport", + SD_BUS_ARGS("y", msg_type, + "au", versions), + SD_BUS_NO_RESULT, + method_register_responder, + 0), + SD_BUS_VTABLE_END, +}; // clang-format on static int emit_endpoint_added(const struct peer *peer) @@ -3822,6 +3989,13 @@ static int setup_bus(struct ctx *ctx) goto out; } + rc = sd_bus_add_object_vtable(ctx->bus, NULL, MCTP_DBUS_PATH, + MCTP_DBUS_NAME, mctp_base_vtable, ctx); + if (rc < 0) { + warnx("Adding MCTP base vtable failed: %s", strerror(-rc)); + goto out; + } + rc = 0; out: return rc; @@ -4613,6 +4787,34 @@ static int parse_config(struct ctx *ctx) return rc; } +static void setup_ctrl_cmd_defaults(struct ctx *ctx) +{ + ctx->supported_msg_types = NULL; + ctx->num_supported_msg_types = 0; + + // Default to supporting only control messages + ctx->supported_msg_types = malloc(sizeof(struct msg_type_support)); + if (!ctx->supported_msg_types) { + warnx("Out of memory for supported message types"); + return; + } + ctx->num_supported_msg_types = 1; + ctx->supported_msg_types[0].msg_type = MCTP_CTRL_HDR_MSG_TYPE; + + ctx->supported_msg_types[0].versions = malloc(sizeof(uint32_t) * 4); + if (!ctx->supported_msg_types[0].versions) { + warnx("Out of memory for versions"); + free(ctx->supported_msg_types); + ctx->num_supported_msg_types = 0; + return; + } + ctx->supported_msg_types[0].num_versions = 4; + ctx->supported_msg_types[0].versions[0] = htonl(0xF1F0FF00); + ctx->supported_msg_types[0].versions[1] = htonl(0xF1F1FF00); + ctx->supported_msg_types[0].versions[2] = htonl(0xF1F2FF00); + ctx->supported_msg_types[0].versions[3] = htonl(0xF1F3F100); +} + static void setup_config_defaults(struct ctx *ctx) { ctx->mctp_timeout = 250000; // 250ms @@ -4627,6 +4829,16 @@ static void free_config(struct ctx *ctx) free(ctx->config_filename); } +static void free_ctrl_cmd_defaults(struct ctx *ctx) +{ + size_t i; + + for (i = 0; i < ctx->num_supported_msg_types; i++) { + free(ctx->supported_msg_types[i].versions); + } + free(ctx->supported_msg_types); +} + static int endpoint_send_allocate_endpoint_ids( struct peer *peer, mctp_eid_t eid_start, uint8_t eid_pool_size, mctp_ctrl_cmd_allocate_eids_op op, uint8_t *allocated_pool_size, @@ -4778,6 +4990,8 @@ int main(int argc, char **argv) setlinebuf(stdout); setup_config_defaults(ctx); + setup_ctrl_cmd_defaults(ctx); + mctp_ops_init(); rc = parse_args(ctx, argc, argv); @@ -4842,6 +5056,7 @@ int main(int argc, char **argv) free_peers(ctx); free_nets(ctx); free_config(ctx); + free_ctrl_cmd_defaults(ctx); mctp_nl_close(ctx->nl); diff --git a/tests/mctp_test_utils.py b/tests/mctp_test_utils.py index b0de2fe..7370536 100644 --- a/tests/mctp_test_utils.py +++ b/tests/mctp_test_utils.py @@ -1,3 +1,9 @@ +async def mctpd_mctp_base_iface_obj(dbus): + obj = await dbus.get_proxy_object( + 'au.com.codeconstruct.MCTP1', + '/au/com/codeconstruct/mctp1' + ) + return await obj.get_interface('au.com.codeconstruct.MCTP1') async def mctpd_mctp_iface_obj(dbus, iface): obj = await dbus.get_proxy_object( diff --git a/tests/test_mctpd.py b/tests/test_mctpd.py index 4a353ab..767a44c 100644 --- a/tests/test_mctpd.py +++ b/tests/test_mctpd.py @@ -7,7 +7,8 @@ mctpd_mctp_iface_obj, mctpd_mctp_network_obj, mctpd_mctp_endpoint_common_obj, - mctpd_mctp_endpoint_control_obj + mctpd_mctp_endpoint_control_obj, + mctpd_mctp_base_iface_obj ) from mctpenv import Endpoint, MCTPSockAddr, MCTPControlCommand, MctpdWrapper @@ -1252,3 +1253,25 @@ async def test_bridge_pool_range_limited(dbus, sysnet, nursery): res = await mctpd.stop_mctpd() assert res == 0 + +async def test_get_message_types(dbus, mctpd): + ep = mctpd.network.endpoints[0] + ep.eid = 12 + iface = mctpd.system.interfaces[0] + await mctpd.system.add_route(mctpd.system.Route(ep.eid, 1, iface = iface)) + await mctpd.system.add_neighbour(mctpd.system.Neighbour(iface, ep.lladdr, ep.eid)) + + # Check default response when no responder registered + rsp = await ep.send_control(mctpd.network.mctp_socket, MCTPControlCommand(True, 0, 0x05, bytes([0x00]))) + assert rsp.hex(' ') == '00 05 00 01 00' + + # Register spdm responder with a random version + mctp = await mctpd_mctp_base_iface_obj(dbus) + await mctp.call_register_type_support(5, [0xF1F2F3F4]) + + # Verify get message type response includes spdm + rsp = await ep.send_control(mctpd.network.mctp_socket, MCTPControlCommand(True, 0, 0x05, bytes([0x00]))) + assert rsp.hex(' ') == '00 05 00 02 00 05' + # Verify version passed in dbus call is responded back + rsp = await ep.send_control(mctpd.network.mctp_socket, MCTPControlCommand(True, 0, 0x04, bytes([0x05]))) + assert rsp.hex(' ') == '00 04 00 01 f4 f3 f2 f1' \ No newline at end of file