diff --git a/include/zephyr/pmci/pldm/service.h b/include/zephyr/pmci/pldm/service.h new file mode 100644 index 000000000000..7af408c08699 --- /dev/null +++ b/include/zephyr/pmci/pldm/service.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2025 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_PLDM_SERVICE_H_ +#define ZEPHYR_PLDM_SERVICE_H_ + +#include +#include + +const char *MESSAGE_TYPE_TO_STRING[] = {"Response", "Request", "Reserved", "Async Request Notify"}; + +const char *COMMAND_TO_STRING[] = {"UNDEFINED", "SetTID", "GetTID", + "GetPLDMVersion", "GetPLDMCommands", "SelectPLDMVersion"}; + +/** + * @brief Set of functions that are expected to be implemented by PLDM base specification + + * Some of these functions are *mandatory* to comply with the PLDM specification, and common + implementations + * of these can use the common pldm_context struct to provide them. + */ +struct pldm_base_api { + void (*get_tid_request)(uint8_t mctp_src_eid, struct pldm_header_info *hdr); + void (*get_tid_response)(uint8_t mctp_src_eid, struct pldm_header_info *hdr, + uint8_t completion_code, uint8_t tid); + void (*get_version_request)(uint8_t mctp_src_eid, struct pldm_header_info *hdr, + uint32_t data_transfer_handle, uint8_t flags, + uint8_t pldm_type); + void (*get_version_response)(uint8_t mctp_src_eid, struct pldm_header_info *hdr, + uint8_t completion_code, uint32_t version); + void (*get_types)(uint8_t mctp_src_eid, struct pldm_header_info *hdr); + void (*get_commands)(uint8_t mctp_src_eid, struct pldm_header_info *hdr); +}; + +/** + * @brief PLDM handler handles PLDM requests/response messages + * + * Struct should be embedded into a containing struct which holds all the needed contextual + * information. + */ +struct pldm_handler { + const struct pldm_base_api *api; +}; + +/** + * @brief Handle a pldm message from mctp by routing to the appropriate function pointer + */ +void pldm_service_mctp_receive(struct pldm_handler *hndlr, uint8_t mctp_src_eid, uint8_t *msg, size_t msg_len); + +/** + * @brief Common base API implementations + */ +extern const struct pldm_base_api COMMON_BASE_API; + +struct pldm_common_handler { + struct pldm_handler handler; + uint8_t tid; + uint32_t base_version; + uint8_t *commands; + size_t num_commands; + uint8_t *types; + size_t num_types; +}; + +#define PLDM_COMMON_HANDLER_INIT(_tid, _base_version, _commands, _types) \ + { \ + .handler = \ + { \ + .api = COMMON_BASE_API, \ + }, \ + .tid = _tid, \ + .version = _base_version, \ + .commands = _commands, \ + .num_commands = ARRAY_SIZE(_commands).types = _types, \ + .num_types = ARRAY_SIZE(_types), \ + } + +#endif /* ZEPHYR_PLDM_SERVICE_H_ */ diff --git a/samples/modules/pmci/pldm/endpoint/CMakeLists.txt b/samples/modules/pmci/pldm/endpoint/CMakeLists.txt new file mode 100644 index 000000000000..e3f4e04676ad --- /dev/null +++ b/samples/modules/pmci/pldm/endpoint/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) + +project(mctp_endpoint) + +target_sources(app PRIVATE src/main.c) diff --git a/samples/modules/pmci/pldm/endpoint/README.rst b/samples/modules/pmci/pldm/endpoint/README.rst new file mode 100644 index 000000000000..6f7d7b98a5f2 --- /dev/null +++ b/samples/modules/pmci/pldm/endpoint/README.rst @@ -0,0 +1,41 @@ +.. zephyr:code-sample:: mctp_endpoint_sample + :name: MCTP Endpoint Sample + + Create an MCTP endpoint over UART. + +Overview +******** +Sets up an MCTP node that listens on a UART for messages targeting a particular +MCTP endpoint id with the message "hello". Responds to this "hello" message with +"world". + +Requirements +************ +A board and SoC that provide access to a UART and a driver that implements the +UART async API. + +Wiring +****** +The listening UART pins should be wired to a board which will run the MCTP host +sample such that this board's UART tx pin connects to the host board's rx pin, +and this board's UART rx pin connects to the host board's tx pin. The boards' +grounds should also be wired together. + +Optionally a logic analyzer can be wired up and listening to the UART to inspect +the data flowing. + +Building and Running +******************** + + +.. zephyr-app-commands:: + :zephyr-app: samples/modules/mctp/mctp_endpoint + :host-os: unix + :board: nrf52840_nrf52840dk + :goals: run + :compact: + +References +********** + +`MCTP Base Specification 2019 `_ diff --git a/samples/modules/pmci/pldm/endpoint/boards/nrf52840dk_nrf52840.overlay b/samples/modules/pmci/pldm/endpoint/boards/nrf52840dk_nrf52840.overlay new file mode 100644 index 000000000000..48c7f840dcab --- /dev/null +++ b/samples/modules/pmci/pldm/endpoint/boards/nrf52840dk_nrf52840.overlay @@ -0,0 +1,3 @@ +&arduino_serial{ + status = "okay"; +}; diff --git a/samples/modules/pmci/pldm/endpoint/prj.conf b/samples/modules/pmci/pldm/endpoint/prj.conf new file mode 100644 index 000000000000..5b15347d3694 --- /dev/null +++ b/samples/modules/pmci/pldm/endpoint/prj.conf @@ -0,0 +1,9 @@ +CONFIG_SERIAL=y +CONFIG_UART_ASYNC_API=y +CONFIG_MCTP=y +CONFIG_PLDM=y +CONFIG_MCTP_UART=y +CONFIG_LOG=y +CONFIG_LOG_BUFFER_SIZE=4096 +CONFIG_MCTP_LOG_LEVEL_INF=y +CONFIG_ISR_STACK_SIZE=4096 diff --git a/samples/modules/pmci/pldm/endpoint/src/main.c b/samples/modules/pmci/pldm/endpoint/src/main.c new file mode 100644 index 000000000000..40fdc6c9353d --- /dev/null +++ b/samples/modules/pmci/pldm/endpoint/src/main.c @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2025 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +LOG_MODULE_REGISTER(pldm_endpoint); + +/* PLDM MCTP Message Type */ +#define PLDM_MCTP_MESSAGE_TYPE 1 + +/* Local MCTP Endpoint ID that responds to requests */ +#define LOCAL_EID 10 +/* Local PLDM Terminus ID that responds to requests */ +#define LOCAL_TID 2 + +/* Remote MCTP Endpoint ID that we request from to */ +#define REMOTE_EID 20 + +#define MCTP_INTEGRITY_CHECK 0x80 +#define MCTP_MESSAGE_TYPE_MASK 0x7F + +const char *MESSAGE_TYPE_TO_STRING[] = {"Response", "Request", "Reserved", "Async Request Notify"}; + +const char *COMMAND_TO_STRING[] = {"UNDEFINED", "SetTID", "GetTID", + "GetPLDMVersion", "GetPLDMCommands", "SelectPLDMVersion"}; + +static struct mctp *mctp_ctx; + +static void pldm_rx_handler(uint8_t src_eid, void *data, struct pldm_msg_hdr *msg_hdr, void *msg, + size_t msg_len) +{ + struct pldm_header_info hdr_info; + const char *message_type_str = ""; + const char *command_str = ""; + int rc; + + rc = unpack_pldm_header(msg_hdr, &hdr_info); + if (rc != 0) { + LOG_ERR("Failed unpacking pldm header"); + } + + if (hdr_info.msg_type < ARRAY_SIZE(MESSAGE_TYPE_TO_STRING)) { + message_type_str = MESSAGE_TYPE_TO_STRING[hdr_info.msg_type]; + } + + if (hdr_info.command < ARRAY_SIZE(COMMAND_TO_STRING)) { + command_str = COMMAND_TO_STRING[hdr_info.command]; + } + + LOG_INF("received pldm message from mctp endpoint %d len %zu message type %d (%s) command " + "%d (%s)", + src_eid, msg_len, hdr_info.msg_type, message_type_str, hdr_info.command, + command_str); + + /* Handle the GetTID command */ + if (hdr_info.command == PLDM_GET_TID && hdr_info.msg_type == PLDM_REQUEST) { + /* Response buffer for the GetTID command needs + * pldm response header (4 bytes) + 1 bytes (the tid is 1 byte) + 1 byte (mctp + * message type byte) + */ + uint8_t resp_msg_buf[PLDM_MSG_SIZE(sizeof(struct pldm_get_tid_resp)) + 1]; + + resp_msg_buf[0] = PLDM_MCTP_MESSAGE_TYPE; + + rc = encode_get_tid_resp(hdr_info.instance, PLDM_SUCCESS, LOCAL_TID, + (struct pldm_msg *)&resp_msg_buf[1]); + __ASSERT(rc == PLDM_SUCCESS, "Encoding pldm response should succeed"); + + rc = mctp_message_tx(mctp_ctx, src_eid, false, 0, resp_msg_buf, + sizeof(resp_msg_buf)); + __ASSERT(rc == 0, "Sending response to GetTID should succeed"); + } else if (hdr_info.command == PLDM_GET_PLDM_TYPES && hdr_info.msg_type == PLDM_REQUEST) { + /* Response buffer for the GetTID command needs + * pldm response header (4 bytes) + 1 bytes (the tid is 1 byte) + 1 byte (mctp + * message type byte) + */ + uint8_t resp_msg_buf[PLDM_MSG_SIZE(PLDM_GET_TYPES_RESP_BYTES) + 1]; + uint8_t types[8]; + + resp_msg_buf[0] = PLDM_MCTP_MESSAGE_TYPE; + + types[0] = PLDM_BASE; + rc = encode_get_types_resp(hdr_info.instance, PLDM_SUCCESS, (const bitfield8_t *)types, + (struct pldm_msg *)&resp_msg_buf[1]); + __ASSERT(rc == PLDM_SUCCESS, "Encoding pldm response should succeed"); + + rc = mctp_message_tx(mctp_ctx, src_eid, false, 0, resp_msg_buf, + sizeof(resp_msg_buf)); + __ASSERT(rc == 0, "Sending response to GetTypes should succeed"); } else if (hdr_info.command == PLDM_GET_PLDM_COMMANDS && hdr_info.msg_type == PLDM_REQUEST) { + + } else if (hdr_info.command == PLDM_GET_PLDM_VERSION && hdr_info.msg_type == PLDM_REQUEST) { + uint32_t transfer_handle; + uint8_t transfer_opflag; + uint8_t type; + + rc = decode_get_version_req(msg, PLDM_GET_VERSION_REQ_BYTES, &transfer_handle, &transfer_opflag, &type); + __ASSERT(rc == PLDM_SUCCESS, "Decoding GetVersion request should succeed"); + + + /* Response buffer for the GetTID command needs + * pldm response header (4 bytes) + 1 bytes (the tid is 1 byte) + 1 byte (mctp + * message type byte) + */ + uint8_t resp_msg_buf[PLDM_MSG_SIZE(PLDM_GET_VERSION_RESP_BYTES) + 1]; + ver32_t version = { .major = 1 }; + + resp_msg_buf[0] = PLDM_MCTP_MESSAGE_TYPE; + + rc = encode_get_version_resp(hdr_info.instance, PLDM_SUCCESS, 0, 0, &version, sizeof(ver32_t), + (struct pldm_msg *)&resp_msg_buf[1]); + __ASSERT(rc == PLDM_SUCCESS, "Encoding pldm response should succeed"); + + rc = mctp_message_tx(mctp_ctx, src_eid, false, 0, resp_msg_buf, + sizeof(resp_msg_buf)); + __ASSERT(rc == 0, "Sending response to GetVersion should succeed"); } else if (hdr_info.command == PLDM_GET_PLDM_COMMANDS && hdr_info.msg_type == PLDM_REQUEST) { + } else if (hdr_info.command == PLDM_GET_PLDM_COMMANDS && hdr_info.msg_type == PLDM_REQUEST) { + uint8_t type; + ver32_t vers; + + rc = decode_get_commands_req(msg, PLDM_GET_COMMANDS_REQ_BYTES, &type, &vers); + + __ASSERT(rc == PLDM_SUCCESS, "Decoding GetCommands request should succeed"); + + uint8_t resp_msg_buf[PLDM_MSG_SIZE(PLDM_GET_COMMANDS_RESP_BYTES) + 1]; + uint8_t commands[32]; + + resp_msg_buf[0] = PLDM_MCTP_MESSAGE_TYPE; + commands[0] = PLDM_GET_TID | PLDM_GET_PLDM_VERSION | PLDM_GET_PLDM_TYPES | PLDM_GET_PLDM_COMMANDS; + rc = encode_get_commands_resp(hdr_info.instance, PLDM_SUCCESS, (const bitfield8_t *)commands, + (struct pldm_msg *)&resp_msg_buf[1]); + __ASSERT(rc == PLDM_SUCCESS, "Encoding pldm response should succeed"); + + rc = mctp_message_tx(mctp_ctx, src_eid, false, 0, resp_msg_buf, + sizeof(resp_msg_buf)); + __ASSERT(rc == 0, "Sending response to GetCommands should succeed"); } else if (hdr_info.command == PLDM_GET_PLDM_COMMANDS && hdr_info.msg_type == PLDM_REQUEST) { + } else { + LOG_WRN("Unhandled pldm message, command %d, type %d", hdr_info.command, hdr_info.msg_type); + } +} + +static void rx_message(uint8_t src_eid, bool tag_owner, uint8_t msg_tag, void *data, void *msg, + size_t len) +{ + LOG_INF("received message from mctp endpoint %d, msg_tag %d, len %zu", src_eid, msg_tag, + len); + LOG_HEXDUMP_INF(msg, len, "mctp rx message"); + if (len < 1) { + LOG_ERR("MCTP Message should contain a message type and integrity check byte!"); + return; + } + + /* Treat data as a buffer for byte wise access */ + uint8_t *msg_buf = msg; + + /* If the message endpoint ID matches our local endpoint ID, and its a pldm message, call + * the pldm_rx_message call + */ + if ((msg_buf[0] & MCTP_MESSAGE_TYPE_MASK) == PLDM_MCTP_MESSAGE_TYPE) { + /* HAZARD This is potentially error prone but libpldm provides little help here */ + struct pldm_msg_hdr *pldm_hdr = (struct pldm_msg_hdr *)&(msg_buf[1]); + size_t pldm_msg_body_len = len - (1 + sizeof(struct pldm_msg_hdr)); + void *pldm_msg_body = &msg_buf[1 + sizeof(struct pldm_msg_hdr)]; + + pldm_rx_handler(src_eid, msg, pldm_hdr, pldm_msg_body, pldm_msg_body_len); + } +} + +MCTP_UART_DT_DEFINE(mctp_host, DEVICE_DT_GET(DT_NODELABEL(arduino_serial))); + +int main(void) +{ + LOG_INF("PLDM Endpoint EID:%d TID:%d on %s\n", LOCAL_EID, LOCAL_TID, CONFIG_BOARD_TARGET); + + mctp_set_alloc_ops(malloc, free, realloc); + mctp_ctx = mctp_init(); + __ASSERT_NO_MSG(mctp_ctx != NULL); + mctp_register_bus(mctp_ctx, &mctp_host.binding, LOCAL_EID); + mctp_set_rx_all(mctp_ctx, rx_message, NULL); + mctp_uart_start_rx(&mctp_host); + + return 0; +} diff --git a/samples/modules/pmci/pldm/host/CMakeLists.txt b/samples/modules/pmci/pldm/host/CMakeLists.txt new file mode 100644 index 000000000000..4b19d992eb2b --- /dev/null +++ b/samples/modules/pmci/pldm/host/CMakeLists.txt @@ -0,0 +1,6 @@ +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(mctp_host) + +target_sources(app PRIVATE src/main.c) diff --git a/samples/modules/pmci/pldm/host/README.rst b/samples/modules/pmci/pldm/host/README.rst new file mode 100644 index 000000000000..d80d02d2137e --- /dev/null +++ b/samples/modules/pmci/pldm/host/README.rst @@ -0,0 +1,40 @@ +.. zephyr:code-sample:: mctp_host_sample + :name: MCTP Host Sample + + Create an MCTP host over UART. + +Overview +******** +Sets up an MCTP node that sends a request on a UART targeting a particular MCTP +endpoint id with the message "hello". Expects and waits for a response to this +"hello" message containing "world". + +Requirements +************ +A board and SoC that provide access to a UART and a driver that implements the +UART async API. + +Wiring +****** +The UART pins should be wired to a board which will run the MCTP endpoint +sample such that this board's UART tx pin connects to the endpoint board's rx +pin, and this board's UART rx pin connects to the endpoint board's tx pin. The +boards' grounds should also be wired together. + +Optionally a logic analyzer can be wired up and listening to the UART to inspect +the data flowing. + +Building and Running +******************** + +.. zephyr-app-commands:: + :zephyr-app: samples/modules/mctp/mctp_host + :host-os: unix + :board: nrf52840_nrf52840dk + :goals: run + :compact: + +References +********** + +`MCTP Base Specification 2019 `_ diff --git a/samples/modules/pmci/pldm/host/boards/nrf52840dk_nrf52840.overlay b/samples/modules/pmci/pldm/host/boards/nrf52840dk_nrf52840.overlay new file mode 100644 index 000000000000..48c7f840dcab --- /dev/null +++ b/samples/modules/pmci/pldm/host/boards/nrf52840dk_nrf52840.overlay @@ -0,0 +1,3 @@ +&arduino_serial{ + status = "okay"; +}; diff --git a/samples/modules/pmci/pldm/host/prj.conf b/samples/modules/pmci/pldm/host/prj.conf new file mode 100644 index 000000000000..edaf15dc56f1 --- /dev/null +++ b/samples/modules/pmci/pldm/host/prj.conf @@ -0,0 +1,9 @@ +# nothing here +CONFIG_SERIAL=y +CONFIG_UART_ASYNC_API=y +CONFIG_MCTP=y +CONFIG_MCTP_UART=y +CONFIG_MCTP_LOG_LEVEL_INF=y +CONFIG_PLDM=y +CONFIG_LOG=y +CONFIG_LOG_BUFFER_SIZE=4096 diff --git a/samples/modules/pmci/pldm/host/src/main.c b/samples/modules/pmci/pldm/host/src/main.c new file mode 100644 index 000000000000..265e0b13e4e6 --- /dev/null +++ b/samples/modules/pmci/pldm/host/src/main.c @@ -0,0 +1,252 @@ +/* + * Copyright (c) 2025 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +LOG_MODULE_REGISTER(pldm_host); + +/* PLDM MCTP Message Type */ +#define PLDM_MCTP_MESSAGE_TYPE 1 + +/* Local MCTP Endpoint ID that responds to requests */ +#define LOCAL_EID 20 +/* Local PLDM Terminus ID that responds to requests */ +#define LOCAL_TID 1 + +/* Remote MCTP Endpoint ID that we respond to */ +#define REMOTE_EID 10 + +#define MCTP_INTEGRITY_CHECK 0x80 +#define MCTP_MESSAGE_TYPE_MASK 0x7F + +K_SEM_DEFINE(mctp_rx, 0, 1); + +const char *MESSAGE_TYPE_TO_STRING[] = {"Response", "Request", "Reserved", "Async Request Notify"}; + +const char *COMMAND_TO_STRING[] = {"UNDEFINED", "SetTID", "GetTID", + "GetPLDMVersion", "GetPLDMCommands", "SelectPLDMVersion"}; + +static struct mctp *mctp_ctx; + +struct pldm_type_info { + ver32_t version; + uint8_t commands[32]; +}; + +struct pldm_tid_info { + uint8_t tid; + uint8_t types[8]; /* PLDM Types supported, up to 256 types are representable */ + struct pldm_type_info + *type_infos; /* An array to be allocated once the types are known... */ +}; + +/* Response message buffer */ +uint8_t mctp_msg[256]; + +/** + * response data + */ +static uint8_t comp_code; +static uint8_t tid; +static uint8_t types[8]; +static uint8_t commands[32]; +static ver32_t version; + +/* Discovery if and what a MCTP endpoint can do */ +/* TODO pldm_discovery(struct mctp *mctp_ctx, uint8_t eid, struct pldm_tid_info *tid_info); */ + +static void pldm_rx_handler(uint8_t src_eid, void *data, void *msg, size_t msg_len) +{ + struct pldm_header_info hdr_info; + const char *msg_type_str = ""; + const char *command_str = ""; + int rc; + + rc = unpack_pldm_header(msg, &hdr_info); + if (rc != 0) { + LOG_ERR("Failed unpacking pldm header"); + } + + if (hdr_info.msg_type < ARRAY_SIZE(MESSAGE_TYPE_TO_STRING)) { + msg_type_str = MESSAGE_TYPE_TO_STRING[hdr_info.msg_type]; + } + + if (hdr_info.command < ARRAY_SIZE(COMMAND_TO_STRING)) { + command_str = COMMAND_TO_STRING[hdr_info.command]; + } + + LOG_INF("received pldm message from mctp endpoint %d len %zu message type %d (%s) command " + "%d (%s)", + src_eid, msg_len, hdr_info.msg_type, msg_type_str, hdr_info.command, command_str); + + /* Handle the GetTID Response */ + if (hdr_info.command == PLDM_GET_TID && hdr_info.msg_type == PLDM_RESPONSE) { + decode_get_tid_resp(msg, msg_len - sizeof(struct pldm_msg_hdr), &comp_code, &tid); + LOG_INF("get tid response, completion code %d, tid %d", comp_code, tid); + } else if (hdr_info.command == PLDM_GET_PLDM_TYPES && hdr_info.msg_type == PLDM_RESPONSE) { + decode_get_types_resp(msg, msg_len - sizeof(struct pldm_msg_hdr), &comp_code, + (bitfield8_t *)types); + LOG_INF("get types response, completion code %d", comp_code); + } else if (hdr_info.command == PLDM_GET_PLDM_COMMANDS && + hdr_info.msg_type == PLDM_RESPONSE) { + decode_get_commands_resp(msg, msg_len - sizeof(struct pldm_msg_hdr), &comp_code, + (bitfield8_t *)types); + LOG_INF("get commands response, completion code %d", comp_code); + } else if (hdr_info.command == PLDM_GET_PLDM_VERSION && + hdr_info.msg_type == PLDM_RESPONSE) { + /* ignored, we only accept the first version response... */ + uint32_t next_transfer_handle; + /* ignored */ + uint8_t transfer_flag; + + decode_get_version_resp(msg, msg_len - sizeof(struct pldm_msg_hdr), &comp_code, + &next_transfer_handle, &transfer_flag, &version); + + LOG_INF("get version response, completion code %d, version %d.%d", comp_code, + version.major, version.minor); + } else { + LOG_WRN("unhandled message command %d and type %d", hdr_info.command, + hdr_info.msg_type); + } +} + +static void rx_message(uint8_t src_eid, bool tag_owner, uint8_t msg_tag, void *data, void *msg, + size_t len) +{ + LOG_INF("received message from mctp endpoint %d, msg_tag %d, len %zu", src_eid, msg_tag, + len); + LOG_HEXDUMP_INF(msg, len, "mctp rx message"); + + if (len < 1) { + LOG_ERR("MCTP Message should contain a message type and integrity check" + " byte!"); + return; + } + + /* Treat data as a buffer for byte wise access */ + uint8_t *msg_buf = msg; + + /* if the message endpoint id matches our local endpoint id, and its a pldm message, call + * the pldm_rx_message call + */ + if ((msg_buf[0] & MCTP_MESSAGE_TYPE_MASK) == PLDM_MCTP_MESSAGE_TYPE) { + pldm_rx_handler(src_eid, data, &msg_buf[1], len - 1); + } + + k_sem_give(&mctp_rx); +} + +MCTP_UART_DT_DEFINE(mctp_host, DEVICE_DT_GET(DT_NODELABEL(arduino_serial))); + +int main(void) +{ + int rc; + + LOG_INF("PLDM Host EID:%d on %s\n", LOCAL_EID, CONFIG_BOARD_TARGET); + + mctp_set_alloc_ops(malloc, free, realloc); + mctp_ctx = mctp_init(); + __ASSERT_NO_MSG(mctp_ctx != NULL); + mctp_register_bus(mctp_ctx, &mctp_host.binding, LOCAL_EID); + mctp_set_rx_all(mctp_ctx, rx_message, NULL); + mctp_uart_start_rx(&mctp_host); + + /* We can set this one time here */ + mctp_msg[0] = PLDM_MCTP_MESSAGE_TYPE; + + /* PLDM message is after the MCTP message type byte */ + struct pldm_msg *msg = &mctp_msg[1]; + uint32_t instance = 0; + + /* PLDM poll (discovery) loop, send a sequence of commands and wait on responses */ + while (true) { + k_msleep(1000); + rc = 0; + + /* GetTID request/response */ + uint8_t get_tid_request_size = PLDM_MSG_SIZE(0) + 1; + + encode_get_tid_req(instance, msg); + instance++; + + LOG_HEXDUMP_INF(mctp_msg, get_tid_request_size, "pldm get_tid_request"); + rc = mctp_message_tx(mctp_ctx, REMOTE_EID, false, 0, mctp_msg, + get_tid_request_size); + if (rc != 0) { + LOG_WRN("Failed to send message, errno %d\n", rc); + continue; + } else { + k_sem_take(&mctp_rx, K_MSEC(1000)); + } + uint8_t get_types_request_size = PLDM_MSG_SIZE(0) + 1; + + /* GetPLDMTypes request/response */ + encode_get_types_req(instance, msg); + instance++; + + LOG_HEXDUMP_INF(mctp_msg, get_types_request_size, "pldm get_types_request"); + rc = mctp_message_tx(mctp_ctx, REMOTE_EID, false, 0, mctp_msg, + get_types_request_size); + if (rc != 0) { + LOG_WRN("Failed to send message, errno %d\n", rc); + continue; + } else { + k_sem_take(&mctp_rx, K_MSEC(1000)); + } + + /* TODO in the discovery case we'd iterate types to gather information on versions + * and available commands for each version (or a selected version) + */ + + uint8_t pldm_type = PLDM_BASE; + + /* GetPLDMVersion request/response */ + uint32_t transfer_handle = 0; + uint8_t transfer_opflag = PLDM_GET_FIRSTPART; + uint8_t get_version_request_size = PLDM_MSG_SIZE(PLDM_GET_VERSION_REQ_BYTES) + 1; + + encode_get_version_req(instance, transfer_handle, transfer_opflag, pldm_type, msg); + instance++; + + LOG_HEXDUMP_INF(mctp_msg, get_version_request_size, "pldm get_version_request"); + rc = mctp_message_tx(mctp_ctx, REMOTE_EID, false, 0, mctp_msg, + get_version_request_size); + if (rc != 0) { + LOG_WRN("Failed to send message, errno %d\n", rc); + continue; + } else { + k_sem_take(&mctp_rx, K_MSEC(1000)); + } + + /* GetPLDMCommands request/response */ + uint8_t get_commands_request_size = PLDM_MSG_SIZE(PLDM_GET_COMMANDS_REQ_BYTES) + 1; + + encode_get_commands_req(instance, pldm_type, version, msg); + instance++; + + LOG_HEXDUMP_INF(mctp_msg, get_version_request_size, "pldm get_commands_request"); + rc = mctp_message_tx(mctp_ctx, REMOTE_EID, false, 0, mctp_msg, + get_commands_request_size); + if (rc != 0) { + LOG_WRN("Failed to send message, errno %d\n", rc); + continue; + } else { + k_sem_take(&mctp_rx, K_MSEC(1000)); + } + } + + return 0; +} diff --git a/samples/modules/pmci/pldm/pldm.rst b/samples/modules/pmci/pldm/pldm.rst new file mode 100644 index 000000000000..ff7d119ff78a --- /dev/null +++ b/samples/modules/pmci/pldm/pldm.rst @@ -0,0 +1,5 @@ +.. zephyr:code-sample-category:: pldm + :name: PLDM + :show-listing: + + These samples demonstrate how to build communicating firmwares using PLDM in Zephyr. diff --git a/samples/modules/pmci/pmci.rst b/samples/modules/pmci/pmci.rst new file mode 100644 index 000000000000..580f0671f40b --- /dev/null +++ b/samples/modules/pmci/pmci.rst @@ -0,0 +1,6 @@ +.. zephyr:code-sample-category:: PMCI + :name: PMCI + :show-listing: + + These samples demonstrate how to build communicating firmwares using various PMCI specifications + in Zephyr. diff --git a/subsys/pmci/Kconfig b/subsys/pmci/Kconfig index 917a084a52ff..50cb1782cf75 100644 --- a/subsys/pmci/Kconfig +++ b/subsys/pmci/Kconfig @@ -8,6 +8,7 @@ menu "Platform Management Communication Infrastruction (PMCI)" # zephyr-keep-sorted-start source "subsys/pmci/mctp/Kconfig" +source "subsys/pmci/pldm/Kconfig" # zephyr-keep-sorted-stop diff --git a/subsys/pmci/pldm/Kconfig b/subsys/pmci/pldm/Kconfig new file mode 100644 index 000000000000..8b8ccf7a8f35 --- /dev/null +++ b/subsys/pmci/pldm/Kconfig @@ -0,0 +1,8 @@ +# Copyright (c) 2025 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +menuconfig PLDM + bool "Platform Level Data Model [EXPERIMENTAL]" + select EXPERIMENTAL + help + Enable the PLDM Subsystem and Module Usage diff --git a/west.yml b/west.yml index e079b568d77b..ae50ad2a1c4c 100644 --- a/west.yml +++ b/west.yml @@ -293,6 +293,10 @@ manifest: path: modules/hal/libmetal groups: - hal + - name: libpldm + revision: zephyr + url: git@github.com:teburd/libpldm.git + path: modules/lib/libpldm - name: littlefs path: modules/fs/littlefs groups: