/* SPDX-License-Identifier: Apache-2.0 */ /** * @file fmapi_psc_handler.c * * @brief Code file for methods to respond to FM API commands * * @copyright Copyright (C) 2024 Jackrabbit Founders LLC. All rights reserved. * * @date Jan 2024 * @author Barrett Edwards * */ /* INCLUDES ==================================================================*/ /* gettid() */ #define _GNU_SOURCE #include /* printf() */ #include /* memset() */ #include /* struct timespec * timespec_get() * */ #include /* autl_prnt_buf() */ #include /* mctp_init() * mctp_set_mh() * mctp_run() */ #include #include #include #include #include "signals.h" #include "options.h" #include "state.h" #include #include "fmapi_handler.h" /* MACROS ====================================================================*/ #ifdef CSE_VERBOSE #define INIT unsigned step = 0; #define ENTER if (opts[CLOP_VERBOSITY].u64 & CLVB_CALLSTACK) printf("%d:%s Enter\n", gettid(), __FUNCTION__); #define STEP step++; if (opts[CLOP_VERBOSITY].u64 & CLVB_STEPS) printf("%d:%s STEP: %u\n", gettid(), __FUNCTION__, step); #define HEX32(m, i) if (opts[CLOP_VERBOSITY].u64 & CLVB_STEPS) printf("%d:%s STEP: %u %s: 0x%x\n", gettid(), __FUNCTION__, step, m, i); #define INT32(m, i) if (opts[CLOP_VERBOSITY].u64 & CLVB_STEPS) printf("%d:%s STEP: %u %s: %d\n", gettid(), __FUNCTION__, step, m, i); #define EXIT(rc) if (opts[CLOP_VERBOSITY].u64 & CLVB_CALLSTACK) printf("%d:%s Exit: %d\n", gettid(), __FUNCTION__,rc); #else #define ENTER #define EXIT(rc) #define STEP #define HEX32(m, i) #define INT32(m, i) #define INIT #endif // CSE_VERBOSE #define IFV(u) if (opts[CLOP_VERBOSITY].u64 & u) #define ISO_TIME_BUF_LEN 32 /* ENUMERATIONS ==============================================================*/ /* STRUCTS ===================================================================*/ /* PROTOTYPES ================================================================*/ /* GLOBAL VARIABLES ==========================================================*/ /* FUNCTIONS =================================================================*/ /** * Handler for FM API PSC CXL.io Configuration Opcode * * @param m struct mctp* * @param mm struct mctp_msg* * @return 0 upon success, 1 otherwise * * STEPS * 1: Initialize variables * 2: Checkout Response mctp_msg buffer * 3: Fill Response MCTP Header * 4: Set buffer pointers * 5: Deserialize Request Header * 6: Deserialize Request Object * 7: Extract parameters * 8: Obtain lock on switch state * 9: Validate Inputs * 10: Perform Action * 11: Prepare Response Object * 12: Serialize Response Object * 13: Set return code * 14: Release lock on switch state * 15: Fill Response Header * 16: Serialize Header * 17: Push Response mctp_msg onto Transmit Message Queue * 18: Checkin mctp_msgs */ int fmop_psc_cfg(struct mctp *m, struct mctp_action *ma) { INIT char now[ISO_TIME_BUF_LEN]; struct fmapi_msg req, rsp; unsigned rc; int rv, len; struct cxl_port *p; __u16 reg; ENTER STEP // 1: Initialize variables rv = 1; len = 0; rc = FMRC_INVALID_INPUT; isotime(now, ISO_TIME_BUF_LEN); STEP // 2: Get response mctp_msg buffer ma->rsp = pq_pop(m->msgs, 1); if (ma->rsp == NULL) goto end; STEP // 3: Fill Response MCTP Header: dst, src, owner, tag, and type mctp_fill_msg_hdr(ma->rsp, ma->req->src, m->state.eid, 0, ma->req->tag); ma->rsp->type = ma->req->type; // 4: Set buffer pointers req.buf = (struct fmapi_buf*) ma->req->payload; rsp.buf = (struct fmapi_buf*) ma->rsp->payload; STEP // 5: Deserialize Request Header if ( fmapi_deserialize(&req.hdr, req.buf->hdr, FMOB_HDR, NULL) <= 0 ) goto end; STEP // 6: Deserialize Request Object if ( fmapi_deserialize(&req.obj, req.buf->payload, fmapi_fmob_req(req.hdr.opcode), NULL) < 0 ) goto end; STEP // 7: Extract parameters IFV(CLVB_COMMANDS) printf("%s CMD: FM API PSC CXL.io Config. PPID: %d\n", now, req.obj.psc_cfg_req.ppid); STEP // 8: Obtain lock on switch state pthread_mutex_lock(&cxls->mtx); STEP // 9: Validate Inputs if (req.obj.psc_cfg_req.ppid >= cxls->num_ports) { IFV(CLVB_ERRORS) printf("%s ERR: Requested PPDI exceeds number of ports present. Requested PPID: %d Present: %d\n", now, req.obj.psc_cfg_req.ppid, cxls->num_ports); goto send; } p = &cxls->ports[req.obj.psc_cfg_req.ppid]; // Validate port is not bound or is an MLD port //if ( !(p->state == FMPS_DISABLED || p->ld > 0) ) //{ // IFV(CLVB_ERRORS) printf("%s Port is not unbound or is not an MLD Port. PPID: %d Port State: %s Num LD: %d\n", now, req.obj.psc_cfg_req.ppid, fmps(p->state), p->ld); // goto send; //} STEP // 10: Perform Action switch (req.obj.psc_cfg_req.type) { case FMCT_READ: // 0x00 { IFV(CLVB_ACTIONS) printf("%s ACT: Performing CXL.io Read on PPID: %d\n", now, req.obj.psc_cfg_req.ppid); reg = (req.obj.psc_cfg_req.ext << 8) | req.obj.psc_cfg_req.reg; rsp.obj.psc_cfg_rsp.data[0] = 0; rsp.obj.psc_cfg_rsp.data[1] = 0; rsp.obj.psc_cfg_rsp.data[2] = 0; rsp.obj.psc_cfg_rsp.data[3] = 0; if (req.obj.psc_cfg_req.fdbe & 0x01) rsp.obj.psc_cfg_rsp.data[0] = p->cfgspace[reg+0]; if (req.obj.psc_cfg_req.fdbe & 0x02) rsp.obj.psc_cfg_rsp.data[1] = p->cfgspace[reg+1]; if (req.obj.psc_cfg_req.fdbe & 0x04) rsp.obj.psc_cfg_rsp.data[2] = p->cfgspace[reg+2]; if (req.obj.psc_cfg_req.fdbe & 0x08) rsp.obj.psc_cfg_rsp.data[3] = p->cfgspace[reg+3]; } break; case FMCT_WRITE: // 0x01 { HEX32("Write Data", *((int*)req.obj.psc_cfg_req.data)); IFV(CLVB_ACTIONS) printf("%s ACT: Performing CXL.io Write on PPID: %d\n", now, req.obj.psc_cfg_req.ppid); reg = (req.obj.psc_cfg_req.ext << 8) | req.obj.psc_cfg_req.reg; if (req.obj.psc_cfg_req.fdbe & 0x01) p->cfgspace[reg+0] = req.obj.psc_cfg_req.data[0]; if (req.obj.psc_cfg_req.fdbe & 0x02) p->cfgspace[reg+1] = req.obj.psc_cfg_req.data[1]; if (req.obj.psc_cfg_req.fdbe & 0x04) p->cfgspace[reg+2] = req.obj.psc_cfg_req.data[2]; if (req.obj.psc_cfg_req.fdbe & 0x08) p->cfgspace[reg+3] = req.obj.psc_cfg_req.data[3]; } break; } STEP // 11: Prepare Response Object STEP // 12: Serialize Response Object len = fmapi_serialize(rsp.buf->payload, &rsp.obj, fmapi_fmob_rsp(req.hdr.opcode)); STEP // 13: Set return code rc = FMRC_SUCCESS; send: STEP // 14: Release lock on switch state pthread_mutex_unlock(&cxls->mtx); if (len < 0) goto end; STEP // 15: Fill Response Header ma->rsp->len = fmapi_fill_hdr(&rsp.hdr, FMMT_RESP, req.hdr.tag, req.hdr.opcode, 0, len, rc, 0); STEP // 16: Serialize Header fmapi_serialize(rsp.buf->hdr, &rsp.hdr, FMOB_HDR); STEP // 17: Push mctp_action onto queue pq_push(m->tmq, ma); rv = 0; end: EXIT(rc) return rv; } /** * Handler for FM API PSC Identify Switch Device Opcode * * @param m struct mctp* * @param mm struct mctp_msg* * @return 0 upon success, 1 otherwise * * STEPS * 1: Initialize variables * 2: Checkout Response mctp_msg buffer * 3: Fill Response MCTP Header * 4: Set buffer pointers * 5: Deserialize Request Header * 6: Deserialize Request Object * 7: Extract parameters * 8: Obtain lock on switch state * 9: Validate Inputs * 10: Perform Action * 11: Prepare Response Object * 12: Serialize Response Object * 13: Set return code * 14: Release lock on switch state * 15: Fill Response Header * 16: Serialize Header * 17: Push Response mctp_msg onto Transmit Message Queue * 18: Checkin mctp_msgs */ int fmop_psc_id(struct mctp *m, struct mctp_action *ma) { INIT char now[ISO_TIME_BUF_LEN]; struct fmapi_msg req, rsp; unsigned rc; int rv, len; ENTER STEP // 1: Initialize variables rv = 1; len = 0; rc = FMRC_INVALID_INPUT; isotime(now, ISO_TIME_BUF_LEN); STEP // 2: Get response mctp_msg buffer ma->rsp = pq_pop(m->msgs, 1); if (ma->rsp == NULL) goto end; STEP // 3: Fill Response MCTP Header: dst, src, owner, tag, and type mctp_fill_msg_hdr(ma->rsp, ma->req->src, m->state.eid, 0, ma->req->tag); ma->rsp->type = ma->req->type; // 4: Set buffer pointers req.buf = (struct fmapi_buf*) ma->req->payload; rsp.buf = (struct fmapi_buf*) ma->rsp->payload; STEP // 5: Deserialize Request Header if ( fmapi_deserialize(&req.hdr, req.buf->hdr, FMOB_HDR, NULL) <= 0 ) goto end; STEP // 6: Deserialize Request Object if ( fmapi_deserialize(&req.obj, req.buf->payload, fmapi_fmob_req(req.hdr.opcode), NULL) < 0 ) goto end; STEP // 7: Extract parameters IFV(CLVB_COMMANDS) printf("%s CMD: FM API PSC Identify Switch Device\n", now); STEP // 8: Obtain lock on switch state pthread_mutex_lock(&cxls->mtx); STEP // 9: Validate Inputs STEP // 10: Perform Action STEP // 11: Prepare Response Object { struct cxl_switch *cs = cxls; struct fmapi_psc_id_rsp *fi = &rsp.obj.psc_id_rsp; // Zero out destination memset(fi, 0, sizeof(*fi)); // Copy static information fi->ingress_port = cs->ingress_port; //!< Ingress Port ID fi->num_ports = cs->num_ports; //!< Total number of physical ports fi->num_vcss = cs->num_vcss; //!< Max number of VCSs fi->num_vppbs = cs->num_vppbs; //!< Max number of vPPBs fi->num_decoders = cs->num_decoders; //!< Number of HDM decoders available per USP // Compute dynamic information for ( int i = 0 ; i < cs->num_ports ; i++ ) { if ( cs->ports[i].state != FMPS_DISABLED ) fi->active_ports[i/8] |= (0x01 << (i % 8)); } for ( int i = 0 ; i < cs->num_vcss ; i++ ) { if ( cs->vcss[i].state == FMVS_ENABLED) fi->active_vcss[i/8] |= (0x01 << (i % 8)); } for ( int i = 0 ; i < cs->num_vcss ; i++ ) { for ( int j = 0 ; j < MAX_VPPBS_PER_VCS ; j++ ) { if ( cs->vcss[i].vppbs[j].bind_status != FMBS_UNBOUND ) fi->active_vppbs++; } } } STEP // 12: Serialize Response Object len = fmapi_serialize(rsp.buf->payload, &rsp.obj, fmapi_fmob_rsp(req.hdr.opcode)); STEP // 13: Set return code rc = FMRC_SUCCESS; //send: STEP // 14: Release lock on switch state pthread_mutex_unlock(&cxls->mtx); if (len < 0) goto end; STEP // 15: Fill Response Header ma->rsp->len = fmapi_fill_hdr(&rsp.hdr, FMMT_RESP, req.hdr.tag, req.hdr.opcode, 0, len, rc, 0); STEP // 16: Serialize Header fmapi_serialize(rsp.buf->hdr, &rsp.hdr, FMOB_HDR); STEP // 17: Push mctp_action onto queue pq_push(m->tmq, ma); rv = 0; end: EXIT(rc) return rv; } /** * Handler for FM API PSC Get Physical Port State Opcode * * @param m struct mctp* * @param mm struct mctp_msg* * @return 0 upon success, 1 otherwise * * STEPS * 1: Initialize variables * 2: Checkout Response mctp_msg buffer * 3: Fill Response MCTP Header * 4: Set buffer pointers * 5: Deserialize Request Header * 6: Deserialize Request Object * 7: Extract parameters * 8: Obtain lock on switch state * 9: Validate Inputs * 10: Perform Action * 11: Prepare Response Object * 12: Serialize Response Object * 13: Set return code * 14: Release lock on switch state * 15: Fill Response Header * 16: Serialize Header * 17: Push Response mctp_msg onto Transmit Message Queue * 18: Checkin mctp_msgs */ int fmop_psc_port(struct mctp *m, struct mctp_action *ma) { INIT char now[ISO_TIME_BUF_LEN]; struct fmapi_msg req, rsp; unsigned rc; int rv, len; int i; __u8 id; ENTER STEP // 1: Initialize variables rv = 1; len = 0; rc = FMRC_INVALID_INPUT; isotime(now, ISO_TIME_BUF_LEN); STEP // 2: Get response mctp_msg buffer ma->rsp = pq_pop(m->msgs, 1); if (ma->rsp == NULL) goto end; STEP // 3: Fill Response MCTP Header: dst, src, owner, tag, and type mctp_fill_msg_hdr(ma->rsp, ma->req->src, m->state.eid, 0, ma->req->tag); ma->rsp->type = ma->req->type; // 4: Set buffer pointers req.buf = (struct fmapi_buf*) ma->req->payload; rsp.buf = (struct fmapi_buf*) ma->rsp->payload; STEP // 5: Deserialize Request Header if ( fmapi_deserialize(&req.hdr, req.buf->hdr, FMOB_HDR, NULL) <= 0 ) goto end; STEP // 6: Deserialize Request Object if ( fmapi_deserialize(&req.obj, req.buf->payload, fmapi_fmob_req(req.hdr.opcode), NULL) < 0 ) goto end; STEP // 7: Extract parameters IFV(CLVB_COMMANDS) printf("%s CMD: FM API PSC Get Physical Port Status. Num: %d\n", now, req.obj.psc_port_req.num); STEP // 8: Obtain lock on switch state pthread_mutex_lock(&cxls->mtx); STEP // 9: Validate Inputs STEP // 10: Perform Action STEP // 11: Prepare Response Object for ( i = 0, rsp.obj.psc_port_rsp.num = 0 ; i < req.obj.psc_port_req.num ; i++ ) { id = req.obj.psc_port_req.ports[i]; // Validate portid if (id >= cxls->num_ports) continue; // Copy the data struct cxl_port *src = &cxls->ports[id]; struct fmapi_psc_port_info *dst = &rsp.obj.psc_port_rsp.list[i]; // Zero out destination memset(dst, 0, sizeof(*dst)); // Copy static information dst->ppid = src->ppid; //!< Physical Port ID dst->state = src->state; //!< Current Port Configuration State [FMPS] dst->dv = src->dv; //!< Connected Device CXL Version [FMDV] dst->dt = src->dt; //!< Connected Device Type [FMDT] dst->cv = src->cv; //!< Connected device CXL Version [FMCV] dst->mlw = src->mlw; //!< Max link width dst->nlw = src->nlw; //!< Negotiated link width [FMNW] dst->speeds = src->speeds; //!< Supported Link speeds vector [FMSS] dst->mls = src->mls; //!< Max Link Speed [FMMS] dst->cls = src->cls; //!< Current Link Speed [FMMS] dst->ltssm = src->ltssm; //!< LTSSM State [FMLS] dst->lane = src->lane; //!< First negotiated lane number dst->lane_rev = src->lane_rev;//!< Link State Flags [FMLF] and [FMLO] dst->perst = src->perst; //!< Link State Flags [FMLF] and [FMLO] dst->prsnt = src->prsnt; //!< Link State Flags [FMLF] and [FMLO] dst->pwrctrl = src->pwrctrl; //!< Link State Flags [FMLF] and [FMLO] dst->num_ld = src->ld; //!< Supported Logical Device (LDs) count rsp.obj.psc_port_rsp.num++; } STEP // 12: Serialize Response Object len = fmapi_serialize(rsp.buf->payload, &rsp.obj, fmapi_fmob_rsp(req.hdr.opcode)); STEP // 13: Set return code rc = FMRC_SUCCESS; //send: STEP // 14: Release lock on switch state pthread_mutex_unlock(&cxls->mtx); if (len < 0) goto end; STEP // 15: Fill Response Header ma->rsp->len = fmapi_fill_hdr(&rsp.hdr, FMMT_RESP, req.hdr.tag, req.hdr.opcode, 0, len, rc, 0); STEP // 16: Serialize Header fmapi_serialize(rsp.buf->hdr, &rsp.hdr, FMOB_HDR); STEP // 17: Push mctp_action onto queue pq_push(m->tmq, ma); rv = 0; end: EXIT(rc) return rv; } /** * Handler for FM API PSC Physical Port Control Opcode * * @param m struct mctp* * @param mm struct mctp_msg* * @return 0 upon success, 1 otherwise * * STEPS * 1: Initialize variables * 2: Checkout Response mctp_msg buffer * 3: Fill Response MCTP Header * 4: Set buffer pointers * 5: Deserialize Request Header * 6: Deserialize Request Object * 7: Extract parameters * 8: Obtain lock on switch state * 9: Validate Inputs * 10: Perform Action * 11: Prepare Response Object * 12: Serialize Response Object * 13: Set return code * 14: Release lock on switch state * 15: Fill Response Header * 16: Serialize Header * 17: Push Response mctp_msg onto Transmit Message Queue * 18: Checkin mctp_msgs */ int fmop_psc_port_ctrl(struct mctp *m, struct mctp_action *ma) { INIT char now[ISO_TIME_BUF_LEN]; struct fmapi_msg req, rsp; unsigned rc; int rv, len; struct cxl_port *p; ENTER STEP // 1: Initialize variables rv = 1; len = 0; rc = FMRC_INVALID_INPUT; isotime(now, ISO_TIME_BUF_LEN); STEP // 2: Get response mctp_msg buffer ma->rsp = pq_pop(m->msgs, 1); if (ma->rsp == NULL) goto end; STEP // 3: Fill Response MCTP Header: dst, src, owner, tag, and type mctp_fill_msg_hdr(ma->rsp, ma->req->src, m->state.eid, 0, ma->req->tag); ma->rsp->type = ma->req->type; // 4: Set buffer pointers req.buf = (struct fmapi_buf*) ma->req->payload; rsp.buf = (struct fmapi_buf*) ma->rsp->payload; STEP // 5: Deserialize Request Header if ( fmapi_deserialize(&req.hdr, req.buf->hdr, FMOB_HDR, NULL) <= 0 ) goto end; STEP // 6: Deserialize Request Object if ( fmapi_deserialize(&req.obj, req.buf->payload, fmapi_fmob_req(req.hdr.opcode), NULL) < 0 ) goto end; STEP // 7: Extract parameters IFV(CLVB_COMMANDS) printf("%s CMD: FM API PSC Physical Port Control. PPID: %d Opcode: %d\n", now, req.obj.psc_port_ctrl_req.ppid, req.obj.psc_port_ctrl_req.opcode); STEP // 8: Obtain lock on switch state pthread_mutex_lock(&cxls->mtx); STEP // 9: Validate Inputs if (req.obj.psc_port_ctrl_req.ppid >= cxls->num_ports) { IFV(CLVB_ERRORS) printf("%s ERR: Requested PPID exceeds number of ports present. Requested PPID: %d Present: %d\n", now, req.obj.psc_port_ctrl_req.ppid, cxls->num_ports); goto send; } p = &cxls->ports[req.obj.psc_port_ctrl_req.ppid]; STEP // 10: Perform Action switch (req.obj.psc_port_ctrl_req.opcode) { case FMPO_ASSERT_PERST: // 0x00 IFV(CLVB_ACTIONS) printf("%s ACT: Asserting PERST on PPID: %d\n", now, req.obj.psc_port_ctrl_req.ppid); p->perst = 0x1; break; case FMPO_DEASSERT_PERST: // 0x01 IFV(CLVB_ACTIONS) printf("%s ACT: Deasserting PERST on PPID: %d\n", now, req.obj.psc_port_ctrl_req.ppid); p->perst = 0x0; break; case FMPO_RESET_PPB: // 0x02 IFV(CLVB_ACTIONS) printf("%s ACT: Resetting PPID: %d\n", now, req.obj.psc_port_ctrl_req.ppid); break; default: IFV(CLVB_ERRORS) printf("%s ERR: Invalid port control action Opcode. Opcode: 0x%04x\n", now, req.obj.psc_port_ctrl_req.opcode); goto end; } STEP // 11: Prepare Response Object STEP // 12: Serialize Response Object len = fmapi_serialize(rsp.buf->payload, &rsp.obj, fmapi_fmob_rsp(req.hdr.opcode)); STEP // 13: Set return code rc = FMRC_SUCCESS; send: STEP // 14: Release lock on switch state pthread_mutex_unlock(&cxls->mtx); if (len < 0) goto end; STEP // 15: Fill Response Header ma->rsp->len = fmapi_fill_hdr(&rsp.hdr, FMMT_RESP, req.hdr.tag, req.hdr.opcode, 0, len, rc, 0); STEP // 16: Serialize Header fmapi_serialize(rsp.buf->hdr, &rsp.hdr, FMOB_HDR); STEP // 17: Push mctp_action onto queue pq_push(m->tmq, ma); rv = 0; end: EXIT(rc) return rv; }