2050 lines
51 KiB
C
2050 lines
51 KiB
C
/* SPDX-License-Identifier: Apache-2.0 */
|
|
/**
|
|
* @file state.c
|
|
*
|
|
* @brief Code file to manage the CXL switch state
|
|
*
|
|
* @copyright Copyright (C) 2024 Jackrabbit Founders LLC. All rights reserved.
|
|
*
|
|
* @date Jan 2024
|
|
* @author Barrett Edwards <code@jrlabs.io>
|
|
*
|
|
*/
|
|
/* INCLUDES ==================================================================*/
|
|
|
|
/* gettid()
|
|
*/
|
|
#define _GNU_SOURCE
|
|
|
|
#include <unistd.h>
|
|
|
|
/* memset()
|
|
*/
|
|
#include <string.h>
|
|
|
|
/* printf()
|
|
*/
|
|
#include <stdio.h>
|
|
|
|
/* malloc()
|
|
* free()
|
|
*/
|
|
#include <stdlib.h>
|
|
|
|
/* errno
|
|
*/
|
|
#include <errno.h>
|
|
|
|
/* mmap()
|
|
*/
|
|
#include <sys/mman.h>
|
|
|
|
/** GHashTable
|
|
* g_hash_table_foreach()
|
|
/ */
|
|
#include <glib-2.0/glib.h>
|
|
|
|
/* autl_prnt_buf()
|
|
*/
|
|
#include <arrayutils.h>
|
|
|
|
/* yl_obj_t
|
|
* ly_load()
|
|
* yl_free()
|
|
*
|
|
*/
|
|
#include <yamlloader.h>
|
|
|
|
#include <fmapi.h>
|
|
|
|
#include <pciutils.h>
|
|
|
|
#include "options.h"
|
|
|
|
#include "state.h"
|
|
|
|
/* MACROS ====================================================================*/
|
|
|
|
#define MAX_STR 256
|
|
|
|
#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)
|
|
|
|
/* ENUMERATIONS ==============================================================*/
|
|
|
|
/* STRUCTS ===================================================================*/
|
|
|
|
/* PROTOTYPES ================================================================*/
|
|
|
|
int state_load_devices(struct cxl_switch_state *state, GHashTable *ht);
|
|
int state_load_emulator(struct cxl_switch_state *state, GHashTable *ht);
|
|
int state_load_ports(struct cxl_switch_state *state, GHashTable *ht);
|
|
int state_load_switch(struct cxl_switch_state *state, GHashTable *ht);
|
|
int state_load_vcss(struct cxl_switch_state *state, GHashTable *ht);
|
|
|
|
void _parse_devices(gpointer key, gpointer value, gpointer user_data);
|
|
void _parse_device(gpointer key, gpointer value, gpointer user_data);
|
|
void _parse_device_mld(gpointer key, gpointer value, gpointer user_data);
|
|
void _parse_device_pciecfg(gpointer key, gpointer value, gpointer user_data);
|
|
void _parse_device_pcicap(gpointer key, gpointer value, gpointer user_data);
|
|
void _parse_device_pciecap(gpointer key, gpointer value, gpointer user_data);
|
|
void _parse_device_port(gpointer key, gpointer value, gpointer user_data);
|
|
void _parse_emulator(gpointer key, gpointer value, gpointer user_data);
|
|
void _parse_ports(gpointer key, gpointer value, gpointer user_data);
|
|
void _parse_port(gpointer key, gpointer value, gpointer user_data);
|
|
void _parse_switch(gpointer key, gpointer value, gpointer user_data);
|
|
void _parse_vcss(gpointer key, gpointer value, gpointer user_data);
|
|
void _parse_vcs(gpointer key, gpointer value, gpointer user_data);
|
|
void _parse_vppbs(gpointer key, gpointer value, gpointer user_data);
|
|
void _parse_vppb(gpointer key, gpointer value, gpointer user_data);
|
|
|
|
void state_print_pcie_cfg_space(__u8 *cfgspace, unsigned indent);
|
|
|
|
/* GLOBAL VARIABLES ==========================================================*/
|
|
|
|
/**
|
|
* Global pointer to CXL Switch State
|
|
*/
|
|
struct cxl_switch_state *cxl_state;
|
|
|
|
/* FUNCTIONS =================================================================*/
|
|
|
|
/**
|
|
* Convert state representation to fmapi representation for FM API Identify Switch Device
|
|
*
|
|
* @param[in] cs struct cxl_switch_state* to pull info from
|
|
* @param[out] fi struct fmapi_psc_ident* to put info into
|
|
*/
|
|
void state_conv_identity(struct cxl_switch_state *cs, struct fmapi_psc_id_rsp *fi)
|
|
{
|
|
// 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++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Convert state representation to fmapi representation for FM API Phy Port State Response
|
|
*
|
|
* @param[in] src struct cxl_switch_state* to pull info from
|
|
* @param[out] dst struct fmapi_psc_port_info* to put info into
|
|
*/
|
|
void state_conv_port_info(struct port *src, struct fmapi_psc_port_info *dst)
|
|
{
|
|
// 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
|
|
}
|
|
|
|
/**
|
|
* Convert state representation to fmapi representation for FM API Get Virtual CXL Switch info
|
|
*
|
|
* @param[in] src struct cxl_switch_state* to pull info from
|
|
* @param[out] dst struct fmapi_psc_port_info* to put info into
|
|
*/
|
|
void state_conv_vcs_info(struct vcs *src, struct fmapi_vsc_info_blk *dst)
|
|
{
|
|
// Zero out destination
|
|
memset(dst, 0, sizeof(*dst));
|
|
|
|
// Copy static information
|
|
dst->vcsid = src->vcsid; //!< Virtual CXL Switch ID
|
|
dst->state = src->state; //!< VCS State [FMVS]
|
|
dst->uspid = src->uspid; //!< USP ID. Upstream physical port ID
|
|
dst->num = src->num; //!< Number of vPPBs
|
|
|
|
//!< Variable array of PPB Status Blocksa
|
|
for (int i = 0 ; i < dst->num ; i++) {
|
|
dst->list[i].status = src->vppbs[i].bind_status;
|
|
dst->list[i].ppid = src->vppbs[i].ppid;
|
|
dst->list[i].ldid = src->vppbs[i].ldid;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Free memory allocated by the CXL Switch State
|
|
*
|
|
* STEPS:
|
|
* 1: Destroy Mutex
|
|
* 2: Free pci config space memory
|
|
* 3: Free Port MLD config space
|
|
* 4: unmap memory space if present
|
|
* 5: Free Port MLD
|
|
* 6: Free VCSs
|
|
* 7: Free ports
|
|
* 8: Free devices
|
|
* 9: Free Switch State
|
|
*/
|
|
void state_free(struct cxl_switch_state *state)
|
|
{
|
|
INIT
|
|
unsigned i, k;
|
|
struct port *p;
|
|
struct cse_device *d;
|
|
|
|
ENTER
|
|
|
|
if (state == NULL)
|
|
return;
|
|
|
|
STEP // 1: Destroy mutex
|
|
pthread_mutex_destroy(&state->mtx);
|
|
|
|
STEP // 2: Free pci config space memory
|
|
for ( i = 0 ; i < state->num_ports ; i++ )
|
|
{
|
|
p = &state->ports[i];
|
|
if ( p->cfgspace != NULL )
|
|
{
|
|
free(p->cfgspace);
|
|
p->cfgspace = NULL;
|
|
}
|
|
}
|
|
|
|
STEP // 3: Free Port MLD config space
|
|
for ( i = 0 ; i < state->num_ports ; i++ )
|
|
{
|
|
p = &state->ports[i];
|
|
if (p->mld != NULL)
|
|
{
|
|
for ( k = 0 ; k < MAX_LD ; k++ )
|
|
{
|
|
if ( p->mld->cfgspace[k] != NULL )
|
|
{
|
|
free(p->mld->cfgspace[k]);
|
|
p->mld->cfgspace[k] = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
STEP // 4: unmap memory space if present
|
|
for ( i = 0 ; i < state->num_ports ; i++ )
|
|
{
|
|
p = &state->ports[i];
|
|
if (p->mld != NULL)
|
|
{
|
|
if (p->mld->memspace != NULL)
|
|
{
|
|
munmap(p->mld->memspace, p->mld->memory_size);
|
|
p->mld->memspace = NULL;
|
|
}
|
|
|
|
if (p->mld->file != NULL)
|
|
{
|
|
free(p->mld->file);
|
|
p->mld->file = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
STEP // 5: Free Port MLD
|
|
for ( i = 0 ; i < state->num_ports ; i++ )
|
|
{
|
|
p = &state->ports[i];
|
|
if (p->mld != NULL)
|
|
{
|
|
free(p->mld);
|
|
p->mld = NULL;
|
|
}
|
|
}
|
|
|
|
STEP // 6: Free VCSs
|
|
if (state->vcss != NULL)
|
|
{
|
|
free(state->vcss);
|
|
state ->vcss = NULL;
|
|
}
|
|
|
|
STEP // 7: Free Ports
|
|
if (state->ports != NULL)
|
|
{
|
|
free(state->ports);
|
|
state->ports = NULL;
|
|
}
|
|
|
|
STEP // 8: Free devices
|
|
if (state->devices != NULL )
|
|
{
|
|
for ( i = 0 ; i < state->len_devices ; i++ )
|
|
{
|
|
d = &state->devices[i];
|
|
|
|
// Free device name string if present
|
|
if (d->name != NULL)
|
|
{
|
|
free(d->name);
|
|
d->name = NULL;
|
|
}
|
|
|
|
// Free device pcie config space if present
|
|
if (d->cfgspace != NULL)
|
|
{
|
|
free(d->cfgspace);
|
|
d->cfgspace = NULL;
|
|
}
|
|
|
|
// Free device MLD if present
|
|
if (d->mld != NULL)
|
|
{
|
|
free(d->mld);
|
|
d->mld = NULL;
|
|
}
|
|
}
|
|
|
|
free(state->devices);
|
|
state->devices = NULL;
|
|
}
|
|
state->len_devices = 0;
|
|
state->num_devices = 0;
|
|
|
|
STEP // 9: Free Switch State
|
|
if (state->dir != NULL)
|
|
{
|
|
free(state->dir);
|
|
state->dir = NULL;
|
|
}
|
|
|
|
free(state);
|
|
state = NULL;
|
|
|
|
EXIT(0)
|
|
}
|
|
|
|
/**
|
|
* Initialize state object with default values
|
|
*
|
|
* @return struct state. Returns 0 upon error and sets errno
|
|
*
|
|
* STEPS
|
|
* 1: Validate inputs
|
|
* 2: Initalize State Identity
|
|
* 3: Initalize Ports
|
|
* 4: Initalize VCSs
|
|
* 5: Initalize PCIe config space register
|
|
*/
|
|
struct cxl_switch_state *state_init(unsigned ports, unsigned vcss, unsigned vppbs)
|
|
{
|
|
INIT
|
|
unsigned i;
|
|
struct port *p;
|
|
struct vcs *v;
|
|
struct cxl_switch_state *state;
|
|
|
|
ENTER
|
|
|
|
STEP // 1: Validate inputs
|
|
if (ports > MAX_PORTS)
|
|
ports = MAX_PORTS;
|
|
if (vcss > MAX_VCSS)
|
|
vcss = MAX_VCSS;
|
|
if (vppbs > MAX_VPPBS)
|
|
vppbs = MAX_VPPBS;
|
|
|
|
STEP // 2: Initalize State Identity
|
|
state = calloc(1, sizeof(struct cxl_switch_state));
|
|
if(state == NULL) {
|
|
errno = ENOMEM;
|
|
goto end;
|
|
}
|
|
|
|
// Initialize Identity information
|
|
state->version = 1;
|
|
state->vid = 0xb1b2;
|
|
state->did = 0xc1c2;
|
|
state->svid = 0xd1d2;
|
|
state->ssid = 0xe1e2;
|
|
state->sn = 0xa1a2a3a4a5a6a7a8;
|
|
state->ingress_port = 1;
|
|
state->num_ports = ports;
|
|
state->num_vcss = vcss;
|
|
state->num_vppbs = vppbs;
|
|
state->num_decoders = 42;
|
|
|
|
// Initialize Mutex
|
|
pthread_mutex_init(&state->mtx, NULL);
|
|
|
|
STEP // 3: Initalize Ports
|
|
state->ports = calloc(ports, sizeof(struct port));
|
|
if(state->ports == NULL) {
|
|
errno = ENOMEM;
|
|
goto end_state;
|
|
}
|
|
|
|
// Set default port values
|
|
for ( i = 0 ; i < ports ; i++ ) {
|
|
p = &state->ports[i];
|
|
p->ppid = i;
|
|
p->state = FMPS_DISABLED;
|
|
p->dv = FMDV_NOT_CXL;
|
|
p->dt = FMDT_NONE;
|
|
p->cv = 0;
|
|
p->mlw = 16;
|
|
p->nlw = 0;
|
|
p->speeds = FMSS_PCIE5 | FMSS_PCIE4 | FMSS_PCIE3 | FMSS_PCIE2 | FMSS_PCIE1;
|
|
p->mls = FMMS_PCIE5;
|
|
p->cls = 0;
|
|
p->ltssm = FMLS_DISABLED;
|
|
p->lane = 0;
|
|
p->lane_rev = 0;
|
|
p->perst = 0;
|
|
p->prsnt = 0;
|
|
p->pwrctrl = 0;
|
|
p->ld = 0;
|
|
}
|
|
|
|
STEP // 4: Initalize VCSs
|
|
state->vcss = calloc(vcss, sizeof(struct vcs));
|
|
if(state->vcss == NULL) {
|
|
errno = ENOMEM;
|
|
goto end_ports;
|
|
}
|
|
|
|
// Set default vcs values
|
|
for ( i = 0 ; i < vcss ; i++) {
|
|
v = &state->vcss[i];
|
|
v->vcsid = i;
|
|
v->state = FMVS_DISABLED;
|
|
v->uspid = 0;
|
|
v->num = 0;
|
|
|
|
// Set the vcs->vppb[] array to zero
|
|
memset(v->vppbs, 0, MAX_VPPBS_PER_VCS * sizeof(struct vppb));
|
|
}
|
|
|
|
STEP // 5: Initalize PCIe config space register
|
|
for ( i = 0 ; i < ports ; i++ )
|
|
{
|
|
state->ports[i].cfgspace = calloc(1, CFG_SPACE_SIZE);
|
|
if(state->vcss == NULL) {
|
|
errno = ENOMEM;
|
|
goto end_cfgspace;
|
|
}
|
|
}
|
|
|
|
goto end;
|
|
|
|
end_cfgspace:
|
|
|
|
for ( i = 0 ; i < ports ; i++ ) {
|
|
if( cxl_state->ports[i].cfgspace != NULL ) {
|
|
free(cxl_state->ports[i].cfgspace);
|
|
cxl_state->ports[i].cfgspace = NULL;
|
|
}
|
|
}
|
|
|
|
free(state->vcss);
|
|
state->vcss = NULL;
|
|
|
|
end_ports:
|
|
|
|
free(state->ports);
|
|
state->ports = NULL;
|
|
|
|
end_state:
|
|
|
|
free(state);
|
|
state = NULL;
|
|
|
|
end:
|
|
|
|
EXIT(0)
|
|
|
|
return state;
|
|
}
|
|
|
|
/**
|
|
* Load config file and update state
|
|
*
|
|
* @param state struct cxl_switch_state to fill
|
|
* @param filename char * to yaml config file to load
|
|
* @return Returns 0 on success, error code otherwise
|
|
*
|
|
* STEPS:
|
|
* 1: Validate inputs
|
|
* 2: Parse config file into hash table
|
|
* 3: Parse Emulator configuration
|
|
* 4: Parse Devices
|
|
* 5: Parse Switch
|
|
* 6: Parse Ports
|
|
* 7: Parse VCSs
|
|
* 8: Free memory allocated for hash table
|
|
*/
|
|
int state_load(struct cxl_switch_state *state, char *filename)
|
|
{
|
|
INIT
|
|
int rv;
|
|
GHashTable *ht;
|
|
char *default_file = "config.yaml";
|
|
|
|
ENTER
|
|
|
|
// Initialize varialbes
|
|
rv = 1;
|
|
|
|
STEP // 1: Validate inputs
|
|
if( state == NULL ) {
|
|
rv = EINVAL;
|
|
goto end;
|
|
}
|
|
|
|
if( filename == NULL )
|
|
filename = default_file;
|
|
|
|
STEP // 2: Parse config file into hash table
|
|
ht = yl_load(filename);
|
|
if ( ht == NULL ) {
|
|
rv = errno;
|
|
goto end;
|
|
}
|
|
|
|
STEP // 3: Parse Emulator configuration
|
|
rv = state_load_emulator(state, ht);
|
|
if (rv != 0)
|
|
goto end;
|
|
|
|
STEP // 4: Parse Devices
|
|
rv = state_load_devices(state, ht);
|
|
if (rv != 0)
|
|
goto end;
|
|
|
|
STEP // 5: Parse Switch
|
|
rv = state_load_switch(state, ht);
|
|
if (rv != 0)
|
|
goto end;
|
|
|
|
STEP // 6: Parse Ports
|
|
rv = state_load_ports(state, ht);
|
|
if (rv != 0)
|
|
goto end;
|
|
|
|
STEP // 7: Parse VCSs
|
|
rv = state_load_vcss(state, ht);
|
|
if (rv != 0)
|
|
goto end;
|
|
|
|
STEP // 8: Free memory allocated for hash table
|
|
yl_free(ht);
|
|
|
|
rv = 0;
|
|
|
|
end:
|
|
|
|
EXIT(rv)
|
|
|
|
return rv;
|
|
}
|
|
|
|
/**
|
|
* Load device definitions from hash table into memory
|
|
*
|
|
* @param ht GHashTable holding contents of config.yaml file
|
|
* @return Returns 0 upon success. Non zero otherwise
|
|
*
|
|
* STEPS
|
|
* 1: Obtain hash table
|
|
* 2: Allocate memory for devices in state
|
|
* 3: Parse each entry in the hash table
|
|
*/
|
|
int state_load_devices(struct cxl_switch_state *state, GHashTable *ht)
|
|
{
|
|
INIT
|
|
int rv;
|
|
yl_obj_t *ylo;
|
|
|
|
ENTER
|
|
|
|
// Initialize variables
|
|
rv = 1;
|
|
|
|
STEP // 1: Obtain devices hash table
|
|
ylo = (yl_obj_t*) g_hash_table_lookup(ht, "devices");
|
|
if (ylo == NULL || ylo->ht == NULL)
|
|
goto end;
|
|
|
|
STEP // 2: Allocate memory for devices in state
|
|
state->devices = calloc(INITIAL_NUM_DEVICES, sizeof(struct cse_device));
|
|
if (state->devices == NULL)
|
|
goto end;
|
|
state->len_devices = INITIAL_NUM_DEVICES;
|
|
|
|
STEP // 3: Parse each entry in the device table
|
|
g_hash_table_foreach(ylo->ht, _parse_devices, state);
|
|
|
|
rv = 0;
|
|
|
|
end:
|
|
|
|
EXIT(rv)
|
|
|
|
return rv;
|
|
}
|
|
|
|
/**
|
|
* Load emulator configration variables from hash table into memory
|
|
*
|
|
* @param ht GHashTable holding contents of config.yaml file
|
|
* @return Returns 0 upon success. Non zero otherwise
|
|
*
|
|
* STEPS
|
|
* 1: Obtain hash table
|
|
* 2: Parse each entry in the hash table
|
|
*/
|
|
int state_load_emulator(struct cxl_switch_state *state, GHashTable *ht)
|
|
{
|
|
INIT
|
|
int rv;
|
|
yl_obj_t *ylo;
|
|
|
|
ENTER
|
|
|
|
// Initialize variables
|
|
rv = 1;
|
|
|
|
STEP // 1: Obtain devices hash table
|
|
ylo = (yl_obj_t*) g_hash_table_lookup(ht, "emulator");
|
|
if (ylo == NULL || ylo->ht == NULL)
|
|
goto end;
|
|
|
|
STEP // 2: Parse each entry in the device table
|
|
g_hash_table_foreach(ylo->ht, _parse_emulator, state);
|
|
|
|
rv = 0;
|
|
|
|
end:
|
|
|
|
EXIT(rv)
|
|
|
|
return rv;
|
|
}
|
|
|
|
/**
|
|
* Load port definitions from hash table into memory
|
|
*
|
|
* @param ht GHashTable holding contents of config.yaml file
|
|
* @return Returns 0 upon success. Non zero otherwise
|
|
*
|
|
* STEPS
|
|
* 1: Initialize port state to defaults
|
|
* 2: Obtain hash table
|
|
* 3: Parse each entry in the hash table
|
|
* 4: Instantiate each port device
|
|
*/
|
|
int state_load_ports(struct cxl_switch_state *state, GHashTable *ht)
|
|
{
|
|
INIT
|
|
int rv;
|
|
unsigned i, k;
|
|
yl_obj_t *ylo;
|
|
struct port *port;
|
|
|
|
ENTER
|
|
|
|
// Initialize variables
|
|
rv = 1;
|
|
|
|
STEP // 1: Initialize port state to defaults
|
|
for ( i = 0 ; i < state->num_ports ; i++ )
|
|
{
|
|
port = &state->ports[i];
|
|
port->state = FMPS_DSP;
|
|
port->mlw = state->mlw;
|
|
port->mls = state->mls;
|
|
port->speeds = state->speeds;
|
|
port->ltssm = FMLS_L0;
|
|
port->lane_rev = 0;
|
|
port->perst = 0;
|
|
port->prsnt = 0;
|
|
port->pwrctrl = 0;
|
|
port->ld = 0;
|
|
}
|
|
|
|
STEP // 2: Obtain hash table
|
|
ylo = (yl_obj_t*) g_hash_table_lookup(ht, "ports");
|
|
if (ylo == NULL || ylo->ht == NULL)
|
|
goto end;
|
|
|
|
STEP // 3: Parse each entry in the hash table
|
|
g_hash_table_foreach(ylo->ht, _parse_ports, state->ports);
|
|
|
|
STEP // 4: Instantiate each port device
|
|
for ( i = 0 ; i < state->num_ports ; i++ )
|
|
{
|
|
port = &state->ports[i];
|
|
|
|
// If the port has a device name, copy values from it
|
|
if ( port->device_name != NULL )
|
|
for ( k = 0 ; k < state->num_devices ; k++ )
|
|
if (state->devices[k].name != NULL)
|
|
if ( !strcmp(state->devices[k].name, port->device_name) )
|
|
state_connect_device(port, &state->devices[k]);
|
|
}
|
|
|
|
rv = 0;
|
|
|
|
end:
|
|
|
|
EXIT(rv)
|
|
|
|
return rv;
|
|
}
|
|
|
|
/**
|
|
* Load switch definitions from hash table into memory
|
|
*
|
|
* @param ht GHashTable holding contents of config.yaml file
|
|
* @return Returns 0 upon success. Non zero otherwise
|
|
*
|
|
* STEPS
|
|
* 1: Obtain hash table
|
|
* 2: Parse each entry in the hash table
|
|
*/
|
|
int state_load_switch(struct cxl_switch_state *state, GHashTable *ht)
|
|
{
|
|
INIT
|
|
int rv;
|
|
yl_obj_t *ylo;
|
|
|
|
ENTER
|
|
|
|
// Initialize variables
|
|
rv = 1;
|
|
|
|
STEP // 1: Obtain hash table
|
|
ylo = (yl_obj_t*) g_hash_table_lookup(ht, "switch");
|
|
if (ylo == NULL || ylo->ht == NULL)
|
|
goto end;
|
|
|
|
STEP // 2: Parse each entry in the hash table
|
|
g_hash_table_foreach(ylo->ht, _parse_switch, state);
|
|
|
|
rv = 0;
|
|
|
|
end:
|
|
|
|
EXIT(rv)
|
|
|
|
return rv;
|
|
}
|
|
|
|
/**
|
|
* Load VCS definitions from hash table into memory
|
|
*
|
|
* @param ht GHashTable holding contents of config.yaml file
|
|
* @return Returns 0 upon success. Non zero otherwise
|
|
*
|
|
* STEPS
|
|
* 1: Obtain hash table
|
|
* 2: Parse each entry in the hash table
|
|
*/
|
|
int state_load_vcss(struct cxl_switch_state *state, GHashTable *ht)
|
|
{
|
|
INIT
|
|
int rv;
|
|
yl_obj_t *ylo;
|
|
|
|
ENTER
|
|
|
|
// Initialize variables
|
|
rv = 1;
|
|
|
|
STEP // 1: Obtain hash table
|
|
ylo = (yl_obj_t*) g_hash_table_lookup(ht, "vcss");
|
|
if (ylo == NULL || ylo->ht == NULL)
|
|
goto end;
|
|
|
|
STEP // 2: Parse each entry in the hash table
|
|
g_hash_table_foreach(ylo->ht, _parse_vcss, state->vcss);
|
|
|
|
rv = 0;
|
|
|
|
end:
|
|
|
|
EXIT(rv)
|
|
|
|
return rv;
|
|
}
|
|
|
|
/**
|
|
* Copy data from a device definition to a port
|
|
*
|
|
* @param p struct port* to fill with data
|
|
* @param d struct cse_device* to pull the data from
|
|
*
|
|
* STEPS:
|
|
* 1: Copy basic parameters
|
|
* 2: Copy PCIe config space to the port
|
|
* 3: Copy MLD information if present
|
|
* 4: Memory Map a file if requested by the device profile
|
|
*/
|
|
int state_connect_device(struct port *p, struct cse_device *d)
|
|
{
|
|
INIT
|
|
int rv;
|
|
unsigned i;
|
|
char filename[MAX_FILE_NAME_LEN];
|
|
FILE *fp;
|
|
|
|
ENTER
|
|
|
|
// Initialize variables
|
|
rv = 1;
|
|
|
|
// Validate Inputs
|
|
if (d->name == NULL)
|
|
goto end;
|
|
|
|
STEP // 1: Copy basic parameters
|
|
p->dv = d->dv;
|
|
p->dt = d->dt;
|
|
p->cv = d->cv;
|
|
p->ltssm = FMLS_L0;
|
|
p->lane = 0;
|
|
p->lane_rev = 0;
|
|
p->perst = 0;
|
|
p->pwrctrl = 0;
|
|
p->ld = 0;
|
|
|
|
// If the device definition says this is a rootport then set as an Upstream Port
|
|
if( d->rootport == 1 )
|
|
p->state = FMPS_USP;
|
|
else
|
|
p->state = FMPS_DSP;
|
|
|
|
// Pick the lower of the two widths
|
|
if (d->mlw < p->mlw)
|
|
p->nlw = d->mlw << 4;
|
|
else
|
|
p->nlw = p->mlw << 4;
|
|
|
|
// Pick the lower of the two speeds
|
|
if (d->mls < p->mls)
|
|
p->cls = d->mls;
|
|
else
|
|
p->cls = p->mls;
|
|
|
|
// Set present bit
|
|
p->prsnt = 1;
|
|
|
|
STEP // 2: Copy PCIe config space to the port
|
|
memcpy(p->cfgspace, d->cfgspace, CFG_SPACE_SIZE);
|
|
|
|
STEP // 3: Copy MLD information if present
|
|
if (d->mld != NULL)
|
|
{
|
|
p->ld = d->mld->num;
|
|
|
|
// Allocate memory for MLD object in the port
|
|
p->mld = malloc(sizeof(struct mld));
|
|
|
|
// Copy MLD from device definition to port
|
|
memcpy(p->mld, d->mld, sizeof(struct mld));
|
|
|
|
for ( i = 0 ; i < d->mld->num ; i++ )
|
|
{
|
|
// Allocate memory for each LD pcie config space
|
|
p->mld->cfgspace[i] = malloc(CFG_SPACE_SIZE);
|
|
|
|
// Copy PCIe config space from device definition to port
|
|
memcpy(p->mld->cfgspace[i], d->cfgspace, CFG_SPACE_SIZE);
|
|
}
|
|
}
|
|
|
|
STEP // 4: Memory Map a file if requested by the device profile
|
|
if (d->mld != NULL && d->mld->mmap == 1)
|
|
{
|
|
// Prepare filename
|
|
sprintf(filename, "%s/port%02d", cxl_state->dir, p->ppid);
|
|
|
|
// Create file
|
|
fp = fopen(filename, "w+");
|
|
if (fp == NULL) {
|
|
printf("Error: Could not open file: %s\n", filename);
|
|
goto end;
|
|
}
|
|
|
|
// Truncate file to desired length
|
|
rv = ftruncate(fileno(fp), p->mld->memory_size);
|
|
if (rv != 0) {
|
|
printf("Error: Could not truncate file. Memory Size: 0x%llx errno: %d\n", p->mld->memory_size, errno);
|
|
goto end;
|
|
}
|
|
|
|
// mmap file
|
|
p->mld->memspace = mmap(NULL, p->mld->memory_size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(fp), 0);
|
|
if (p->mld->memspace == NULL) {
|
|
printf("Error: Could not mmap the file. errno: %d\n", errno);
|
|
rv = 1;
|
|
goto end;
|
|
}
|
|
|
|
// Save the filename to the port mld object
|
|
p->mld->file = strdup(filename);
|
|
|
|
// Close file
|
|
fclose(fp);
|
|
|
|
}
|
|
|
|
rv = 0;
|
|
|
|
end:
|
|
|
|
return rv;
|
|
}
|
|
|
|
/**
|
|
* Clear / Free data from a port device definition
|
|
*
|
|
* This function essemtially makes it appear as if the device has been removed from the slot
|
|
*
|
|
* @param p struct port* The port to clear of values
|
|
*
|
|
* STEPS:
|
|
* 1: Clear basic parameters
|
|
* 2: Clear PCIe config space
|
|
* 3: Free device name
|
|
* 4: Unmemmap MLD if present
|
|
* 5: Free PCIe cfg space for each ld
|
|
* 6: Free MLD if present
|
|
*/
|
|
int state_disconnect_device(struct port *p)
|
|
{
|
|
INIT
|
|
int rv;
|
|
unsigned i;
|
|
|
|
ENTER
|
|
|
|
// Initialize variables
|
|
rv = 1;
|
|
|
|
STEP // 1: Clear basic parameters
|
|
p->dv = 0;
|
|
p->dt = 0;
|
|
p->cv = 0;
|
|
p->nlw = 0;
|
|
p->cls = 0;
|
|
p->ltssm = 0;
|
|
p->lane = 0;
|
|
p->lane_rev = 0;
|
|
p->perst = 0;
|
|
p->prsnt = 0;
|
|
p->pwrctrl = 0;
|
|
p->ld = 0;
|
|
|
|
STEP // 2: Clear PCIe config space
|
|
memset(p->cfgspace, 0, CFG_SPACE_SIZE);
|
|
|
|
STEP // 3: Free device name
|
|
if (p->device_name != NULL)
|
|
{
|
|
free(p->device_name);
|
|
p->device_name = NULL;
|
|
}
|
|
|
|
STEP // 4: Unmemmap MLD if present
|
|
if (p->mld != NULL && p->mld->memspace != NULL)
|
|
{
|
|
msync (p->mld->memspace, p->mld->memory_size, MS_SYNC);
|
|
munmap(p->mld->memspace, p->mld->memory_size);
|
|
p->mld->memspace = NULL;
|
|
}
|
|
|
|
STEP // 5: Free PCIe cfg space for each ld
|
|
if (p->mld != NULL)
|
|
{
|
|
for ( i = 0 ; i < p->mld->num ; i++ ) {
|
|
if ( p->mld->cfgspace[i] != NULL ) {
|
|
free(p->mld->cfgspace[i]);
|
|
p->mld->cfgspace[i] = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
STEP // 6: Free MLD if present
|
|
if (p->mld != NULL)
|
|
{
|
|
free(p->mld);
|
|
p->mld = NULL;
|
|
}
|
|
|
|
rv = 0;
|
|
|
|
return rv;
|
|
}
|
|
|
|
/**
|
|
* Function to parse a device entry in the hash table
|
|
*
|
|
* STEPS
|
|
* 1: Obtain device ID from device entry
|
|
* 2: Check if there is space for a new device in the device table, allocate more if needed
|
|
* 3: Duplicate key string into state object
|
|
* 4: Run parse function for each entry in sub hash table
|
|
*/
|
|
void _parse_devices(gpointer key, gpointer value, gpointer user_data)
|
|
{
|
|
INIT
|
|
struct cxl_switch_state *s;
|
|
yl_obj_t *ylo, *ylo_did;
|
|
unsigned did;
|
|
void * ptr;
|
|
|
|
ENTER
|
|
|
|
// Initialize varialbes
|
|
ylo = (yl_obj_t*) value;
|
|
s = (struct cxl_switch_state*) user_data;
|
|
|
|
IFV(CLVB_PARSE) printf("%d:%s Key: %s\n", gettid(), __FUNCTION__, (char*) key);
|
|
|
|
STEP // 1: Obtain device ID from device entry
|
|
ylo_did = (yl_obj_t*) g_hash_table_lookup(ylo->ht, "did");
|
|
if (ylo_did == NULL || ylo_did->str == NULL)
|
|
goto end;
|
|
|
|
did = strtoul(ylo_did->str, NULL, 0);
|
|
|
|
STEP // 2: Check if there is space for a new device in the device table, allocate more if needed
|
|
if (s->num_devices >= s->len_devices)
|
|
{
|
|
// Allocate more memory
|
|
ptr = calloc(s->len_devices + INITIAL_NUM_DEVICES, sizeof(struct cse_device));
|
|
|
|
// Copy the existing data to new buffer
|
|
memcpy(ptr, s->devices, s->num_devices * sizeof(struct cse_device));
|
|
|
|
// free old buffer
|
|
free(s->devices);
|
|
|
|
// reassign new buffer to state
|
|
s->devices = ptr;
|
|
}
|
|
|
|
STEP // 3: Duplicate key string into state object
|
|
s->devices[did].name = strdup(key);
|
|
|
|
STEP // 4: Run parse function for each entry in sub hash table
|
|
g_hash_table_foreach(ylo->ht, _parse_device, &s->devices[did]);
|
|
|
|
if ( (did + 1 )> s->num_devices)
|
|
s->num_devices = did + 1;
|
|
|
|
end:
|
|
|
|
EXIT(0);
|
|
}
|
|
|
|
/**
|
|
* Function to parse each device hashtable entry in the hashtable
|
|
*
|
|
* STEPS
|
|
* 1: Verify the yaml loader object hash table is not NULL
|
|
* 2: Call parser for each type of ntry
|
|
*/
|
|
void _parse_device(gpointer key, gpointer value, gpointer user_data)
|
|
{
|
|
INIT
|
|
int rv;
|
|
struct cse_device *d;
|
|
yl_obj_t *ylo;
|
|
|
|
ENTER
|
|
|
|
// Initialize varialbes
|
|
rv = 1;
|
|
ylo = (yl_obj_t*) value;
|
|
d = (struct cse_device*) user_data;
|
|
|
|
STEP // 1: Verify the yaml loader object hash table is not NULL
|
|
if (ylo->ht == NULL)
|
|
goto end;
|
|
|
|
STEP // 2: Call parser for each type of ntry
|
|
if (!strcmp(key, "port"))
|
|
{
|
|
g_hash_table_foreach(ylo->ht, _parse_device_port, d);
|
|
}
|
|
else if (!strcmp(key, "pcicfg"))
|
|
{
|
|
// Allocate memory for PCIe config space
|
|
if (d->cfgspace == NULL)
|
|
d->cfgspace = calloc(1, CFG_SPACE_SIZE);
|
|
|
|
g_hash_table_foreach(ylo->ht, _parse_device_pciecfg, d->cfgspace);
|
|
}
|
|
else if (!strcmp(key, "mld"))
|
|
{
|
|
// Allocate memory for MLD struct
|
|
if (d->mld == NULL)
|
|
d->mld = calloc(1, sizeof(struct mld));
|
|
|
|
g_hash_table_foreach(ylo->ht, _parse_device_mld, d->mld);
|
|
}
|
|
|
|
rv = 0;
|
|
|
|
end:
|
|
|
|
EXIT(rv)
|
|
}
|
|
|
|
/**
|
|
* Function to parse each device mld entry in the hashtable
|
|
*
|
|
* STEPS:
|
|
* 1: Verify the yaml loader object string is not NULL
|
|
* 2: Assign KV pairs to state variables
|
|
*/
|
|
void _parse_device_mld(gpointer key, gpointer value, gpointer user_data)
|
|
{
|
|
INIT
|
|
int rv;
|
|
struct mld *mld;
|
|
yl_obj_t *ylo;
|
|
|
|
ENTER
|
|
|
|
// Initialize varialbes
|
|
rv = 1;
|
|
ylo = (yl_obj_t*) value;
|
|
mld = (struct mld*) user_data;
|
|
|
|
STEP // 1: Verify the yaml loader object string is not NULL
|
|
if (ylo->str == NULL)
|
|
goto end;
|
|
|
|
STEP // 2: Assign KV pairs to state variables
|
|
IFV(CLVB_PARSE) printf("%d:%s Parsing Key: %s VAL: %s\n", gettid(), __FUNCTION__, (char*) key, ylo->str);
|
|
|
|
if (!strcmp(key, "memory_size")) mld->memory_size = strtoul(ylo->str, NULL, 16);
|
|
else if (!strcmp(key, "num")) mld->num = strtoul(ylo->str, NULL, 10);
|
|
else if (!strcmp(key, "epc")) mld->epc = strtoul(ylo->str, NULL, 10);
|
|
else if (!strcmp(key, "ttr")) mld->ttr = strtoul(ylo->str, NULL, 10);
|
|
else if (!strcmp(key, "granularity")) mld->granularity = strtoul(ylo->str, NULL, 10);
|
|
else if (!strcmp(key, "epc_en")) mld->epc_en = strtoul(ylo->str, NULL, 10);
|
|
else if (!strcmp(key, "ttr_en")) mld->ttr_en = strtoul(ylo->str, NULL, 10);
|
|
else if (!strcmp(key, "egress_mod_pcnt")) mld->egress_mod_pcnt = strtoul(ylo->str, NULL, 10);
|
|
else if (!strcmp(key, "egress_sev_pcnt")) mld->egress_sev_pcnt = strtoul(ylo->str, NULL, 10);
|
|
else if (!strcmp(key, "sample_interval")) mld->sample_interval = strtoul(ylo->str, NULL, 10);
|
|
else if (!strcmp(key, "rcb")) mld->rcb = strtoul(ylo->str, NULL, 10);
|
|
else if (!strcmp(key, "comp_interval")) mld->comp_interval = strtoul(ylo->str, NULL, 10);
|
|
else if (!strcmp(key, "bp_avg_pcnt")) mld->bp_avg_pcnt = strtoul(ylo->str, NULL, 10);
|
|
else if (!strcmp(key, "rng1")) autl_csv_to_u64(mld->rng1, ylo->str, FM_MAX_NUM_LD, 0);
|
|
else if (!strcmp(key, "rng2")) autl_csv_to_u64(mld->rng2, ylo->str, FM_MAX_NUM_LD, 0);
|
|
else if (!strcmp(key, "alloc_bw")) autl_csv_to_u8(mld->alloc_bw, ylo->str, FM_MAX_NUM_LD, 1);
|
|
else if (!strcmp(key, "bw_limit")) autl_csv_to_u8(mld->bw_limit, ylo->str, FM_MAX_NUM_LD, 1);
|
|
else if (!strcmp(key, "mmap")) mld->mmap = strtoul(ylo->str, NULL, 0);
|
|
|
|
rv = 0;
|
|
|
|
end:
|
|
|
|
EXIT(rv)
|
|
}
|
|
|
|
/**
|
|
* Function to parse each device pci config space entry in the hashtable
|
|
*
|
|
* STEPS:
|
|
* 1: Assign KV pairs to state variables
|
|
* 2: Call parse function for each sub entry
|
|
*/
|
|
void _parse_device_pciecfg(gpointer key, gpointer value, gpointer user_data)
|
|
{
|
|
INIT
|
|
struct pcie_cfg_hdr *ph;
|
|
yl_obj_t *ylo;
|
|
|
|
ENTER
|
|
|
|
// Initialize varialbes
|
|
ylo = (yl_obj_t*) value;
|
|
ph = (struct pcie_cfg_hdr*) user_data;
|
|
|
|
STEP // 1: Assign KV pairs to state variables
|
|
if (ylo->str != NULL)
|
|
{
|
|
IFV(CLVB_PARSE) printf("%d:%s Parsing Key: %s VAL: %s\n", gettid(), __FUNCTION__, (char*) key, ylo->str);
|
|
|
|
if (!strcmp(key, "vendor")) ph->vendor = strtoul(ylo->str, NULL, 0);
|
|
else if (!strcmp(key, "device")) ph->device = strtoul(ylo->str, NULL, 0);
|
|
else if (!strcmp(key, "command")) ph->command = strtoul(ylo->str, NULL, 0);
|
|
else if (!strcmp(key, "status")) ph->status = strtoul(ylo->str, NULL, 0);
|
|
|
|
else if (!strcmp(key, "revid")) ph->rev = strtoul(ylo->str, NULL, 0);
|
|
else if (!strcmp(key, "baseclass")) ph->baseclass = strtoul(ylo->str, NULL, 0);
|
|
else if (!strcmp(key, "subclass")) ph->subclass = strtoul(ylo->str, NULL, 0);
|
|
else if (!strcmp(key, "pi")) ph->pi = strtoul(ylo->str, NULL, 0);
|
|
else if (!strcmp(key, "cacheline")) ph->cls = strtoul(ylo->str, NULL, 0);
|
|
|
|
else if (!strcmp(key, "type")) ph->type = strtoul(ylo->str, NULL, 0);
|
|
else if (!strcmp(key, "subvendor")) ph->subvendor = strtoul(ylo->str, NULL, 0);
|
|
else if (!strcmp(key, "subsystem")) ph->subsystem = strtoul(ylo->str, NULL, 0);
|
|
|
|
|
|
else if (!strcmp(key, "intline")) ph->intline = strtoul(ylo->str, NULL, 0);
|
|
else if (!strcmp(key, "intpin")) ph->intpin = strtoul(ylo->str, NULL, 0);
|
|
else if (!strcmp(key, "mingnt")) ph->mingnt = strtoul(ylo->str, NULL, 0);
|
|
else if (!strcmp(key, "maxlat")) ph->maxlat = strtoul(ylo->str, NULL, 0);
|
|
}
|
|
|
|
STEP // 2: Call parse function for each sub entry
|
|
if (ylo->ht != NULL)
|
|
{
|
|
if (!strcmp(key, "cap")) {
|
|
g_hash_table_foreach(ylo->ht, _parse_device_pcicap, ph);
|
|
|
|
// Clear rsvd2 field now that we are done parsing the capabilities list
|
|
ph->rsvd2 = 0;
|
|
}
|
|
else if (!strcmp(key, "ecap")) {
|
|
g_hash_table_foreach(ylo->ht, _parse_device_pciecap, ph);
|
|
|
|
// Clear rsvd2 field now that we are done parsing the capabilities list
|
|
ph->rsvd2 = 0;
|
|
}
|
|
}
|
|
|
|
EXIT(0)
|
|
}
|
|
|
|
/**
|
|
* Function to parse each device pci config space capabilities entry in the hashtable
|
|
*
|
|
* STEPS:
|
|
* 1: Verify the yaml loader object string is not NULL
|
|
* 2: Find ptr to last cap in the list
|
|
* 3: Fill in the new capability header
|
|
* 4: Convert CSV string to bytes at the location after the new pci capabilities header
|
|
* 5: Store the offset to the next capability entry in the list in reserved field
|
|
*/
|
|
void _parse_device_pcicap(gpointer key, gpointer value, gpointer user_data)
|
|
{
|
|
INIT
|
|
struct pcie_cfg_hdr *ph;
|
|
yl_obj_t *ylo;
|
|
struct pcie_cap *pc;
|
|
__u8 *base, *ptr;
|
|
int rv;
|
|
|
|
ENTER
|
|
|
|
// Initialize varialbes
|
|
rv = 1;
|
|
ylo = (yl_obj_t*) value;
|
|
ph = (struct pcie_cfg_hdr*) user_data;
|
|
|
|
STEP // 1: Verify the yaml loader object string is not NULL
|
|
if (ylo->str == NULL)
|
|
goto end;
|
|
|
|
IFV(CLVB_PARSE) printf("%d:%s Parsing Key: %s VAL: %s\n", gettid(), __FUNCTION__, (char*) key, ylo->str);
|
|
|
|
STEP // 2: Find ptr to last cap in the list
|
|
/* If the cap ptr in the pci_hdr is null, then there are no capabilities in the current list
|
|
* Set the cap pointer to 0x40 which is the next byte after the pci_hdra
|
|
* And set the ptr to that byte in memory
|
|
*/
|
|
base = (__u8*) ph;
|
|
if (ph->cap == 0)
|
|
{
|
|
ph->cap = 0x40;
|
|
ptr = base + ph->cap;
|
|
}
|
|
else {
|
|
// Get PC pointer to first entry in table
|
|
pc = (struct pcie_cap*) (base + ph->cap);
|
|
|
|
// Walk the linked list until a null pointer is found in the next field
|
|
while (pc->next != 0)
|
|
pc = (struct pcie_cap*) (base + pc->next);
|
|
|
|
// Set the pointer of the next entry
|
|
pc->next = ph->rsvd2;
|
|
|
|
// prepare the pointer of the next extry to fill out
|
|
ptr = base + ph->rsvd2;
|
|
}
|
|
|
|
STEP // 3: Fill in the new capability header
|
|
pc = (struct pcie_cap*) ptr;
|
|
pc->id = strtoul(key, NULL, 0);
|
|
pc->next = 0;
|
|
ptr += 2;
|
|
|
|
STEP // 4: Convert CSV string to bytes at the location after the new pci capabilities header
|
|
rv = autl_csv_to_u8(ptr, ylo->str, 128, 1);
|
|
|
|
STEP // 5: Store the offset to the next capability entry in the list in reserved field
|
|
ph->rsvd2 = ptr + rv - base;
|
|
|
|
rv = 0;
|
|
|
|
end:
|
|
|
|
EXIT(rv)
|
|
}
|
|
|
|
/**
|
|
* Function to parse each device pci config space extended capability entry in the hashtable
|
|
*
|
|
* STEPS:
|
|
* 1: Verify the yaml loader object string is not NULL
|
|
* 2: Find ptr to last cap in the list
|
|
*/
|
|
void _parse_device_pciecap(gpointer key, gpointer value, gpointer user_data)
|
|
{
|
|
INIT
|
|
struct pcie_cfg_hdr *ph;
|
|
yl_obj_t *ylo;
|
|
struct pcie_ecap *pc;
|
|
__u8 *base, *ptr;
|
|
__u32 k;
|
|
int rv, num;
|
|
|
|
ENTER
|
|
|
|
// Initialize varialbes
|
|
rv = 1;
|
|
ylo = (yl_obj_t*) value;
|
|
ph = (struct pcie_cfg_hdr*) user_data;
|
|
|
|
STEP // 1: Verify the yaml loader object string is not NULL
|
|
if (ylo->str == NULL)
|
|
goto end;
|
|
|
|
IFV(CLVB_PARSE) printf("%d:%s Parsing Key: %s VAL: %s\n", gettid(), __FUNCTION__, (char*) key, ylo->str);
|
|
|
|
STEP // 2: Find ptr to last cap in the list
|
|
|
|
// ptr to start of pci hdr
|
|
base = (__u8*) ph;
|
|
|
|
// Get ptr to first ecap entry
|
|
pc = (struct pcie_ecap*) (base + 0x100);
|
|
|
|
if (pc->id == 0) {
|
|
ptr = (__u8*) pc;
|
|
}
|
|
else {
|
|
// Walk the linked list until a null pointer is found in the next field
|
|
while (pc->next != 0)
|
|
pc = (struct pcie_ecap*) (base + pc->next);
|
|
|
|
// Set the pointer of the next entryf from saved end ptr
|
|
pc->next = ph->rsvd2;
|
|
|
|
// prepare the pointer of the next extry to fill out
|
|
ptr = base + ph->rsvd2;
|
|
}
|
|
|
|
// Fill in the new capability header
|
|
pc = (struct pcie_ecap*) ptr;
|
|
k = strtoul(key, NULL, 0);
|
|
pc->id = k >> 4;
|
|
pc->ver = k & 0x0F;
|
|
pc->next = 0;
|
|
ptr += 4;
|
|
|
|
// Convert CSV string to bytes at the location after the new pci capabilities header
|
|
num = autl_csv_to_u8(ptr, ylo->str, 128, 1);
|
|
|
|
// Store the offset to the next capability entry in the list in reserved field
|
|
ph->rsvd2 = ptr + num - base;
|
|
|
|
rv = 0;
|
|
|
|
end:
|
|
|
|
EXIT(rv)
|
|
}
|
|
|
|
/**
|
|
* Function to parse each device port entry in the hashtable
|
|
*
|
|
* STEPS:
|
|
* 1: Verify the yaml loader object string is not NULL
|
|
* 2: Assign KV pairs to state variables
|
|
*/
|
|
void _parse_device_port(gpointer key, gpointer value, gpointer user_data)
|
|
{
|
|
INIT
|
|
struct cse_device *d;
|
|
yl_obj_t *ylo;
|
|
int rv;
|
|
|
|
ENTER
|
|
|
|
// Initialize varialbes
|
|
rv = 1;
|
|
ylo = (yl_obj_t*) value;
|
|
d = (struct cse_device*) user_data;
|
|
|
|
STEP // 1: Verify the yaml loader object string is not NULL
|
|
if (ylo->str == NULL)
|
|
goto end;
|
|
|
|
STEP // 2: Assign KV pairs to state variables
|
|
|
|
IFV(CLVB_PARSE) printf("%d:%s Parsing Key: %s VAL: %s\n", gettid(), __FUNCTION__, (char*) key, ylo->str);
|
|
|
|
if (!strcmp(key, "dv")) d->dv = strtoul(ylo->str, NULL,0);
|
|
else if (!strcmp(key, "dt")) d->dt = strtoul(ylo->str, NULL,0);
|
|
else if (!strcmp(key, "cv")) d->cv = strtoul(ylo->str, NULL,0);
|
|
else if (!strcmp(key, "mlw")) d->mlw = strtoul(ylo->str, NULL,0);
|
|
else if (!strcmp(key, "mls")) d->mls = strtoul(ylo->str, NULL,0);
|
|
else if (!strcmp(key, "rootport")) d->rootport = strtoul(ylo->str, NULL,0);
|
|
|
|
rv = 0;
|
|
|
|
end:
|
|
|
|
EXIT(rv)
|
|
}
|
|
|
|
/**
|
|
* Function to parse each emulator configuration entry in the hashtable
|
|
*
|
|
* STEPS:
|
|
* 1: Verify the yaml loader object string is not NULL
|
|
* 2: Assign KV pairs to state variables
|
|
*/
|
|
void _parse_emulator(gpointer key, gpointer value, gpointer user_data)
|
|
{
|
|
INIT
|
|
struct cxl_switch_state *s;
|
|
yl_obj_t *ylo;
|
|
int rv;
|
|
|
|
ENTER
|
|
|
|
// Initialize varialbes
|
|
rv = 1;
|
|
ylo = (yl_obj_t*) value;
|
|
s = (struct cxl_switch_state*) user_data;
|
|
|
|
STEP // 1: Verify the yaml loader object string is not NULL
|
|
if (ylo->str == NULL)
|
|
goto end;
|
|
|
|
STEP // 2: Assign KV pairs to state variables
|
|
|
|
IFV(CLVB_PARSE) printf("%d:%s Parsing Key: %s VAL: %s\n", gettid(), __FUNCTION__, (char*) key, ylo->str);
|
|
|
|
if (!strcmp(key, "verbosity-hex")) {
|
|
opts[CLOP_VERBOSITY].set = 1;
|
|
opts[CLOP_VERBOSITY].u64 = strtoull(ylo->str, NULL, 16);
|
|
}
|
|
else if (!strcmp(key, "verbosity-mctp")) {
|
|
opts[CLOP_MCTP_VERBOSITY].set = 1;
|
|
opts[CLOP_MCTP_VERBOSITY].u64 = strtoull(ylo->str, NULL, 16);
|
|
}
|
|
else if (!strcmp(key, "tcp-port")) {
|
|
opts[CLOP_TCP_PORT].set = 1;
|
|
opts[CLOP_TCP_PORT].u16 = strtoull(ylo->str, NULL, 0);
|
|
}
|
|
else if (!strcmp(key, "dir"))
|
|
s->dir = strdup(ylo->str);
|
|
|
|
rv = 0;
|
|
|
|
end:
|
|
|
|
EXIT(rv)
|
|
}
|
|
|
|
/**
|
|
* Function to parse switch entries in the hash table
|
|
*
|
|
* STEPS:
|
|
* 1: Verify the yaml loader object string is not NULL
|
|
* 2: Assign KV pairs to state variables
|
|
*/
|
|
void _parse_switch(gpointer key, gpointer value, gpointer user_data)
|
|
{
|
|
INIT
|
|
struct cxl_switch_state *s;
|
|
yl_obj_t *ylo;
|
|
int rv;
|
|
|
|
ENTER
|
|
|
|
// Initialize varialbes
|
|
rv = 1;
|
|
ylo = (yl_obj_t*) value;
|
|
s = (struct cxl_switch_state*) user_data;
|
|
|
|
STEP // 1: Verify the yaml loader object string is not NULL
|
|
if (ylo->str == NULL)
|
|
goto end;
|
|
|
|
STEP // 2: Assign KV pairs to state variables
|
|
|
|
IFV(CLVB_PARSE) printf("%d:%s Parsing Key: %s VAL: %s\n", gettid(), __FUNCTION__, (char*) key, ylo->str);
|
|
|
|
if (!strcmp(key, "version")) s->version = atoi(ylo->str);
|
|
else if (!strcmp(key, "vid")) s->vid = strtoul(ylo->str, NULL,16);
|
|
else if (!strcmp(key, "did")) s->did = strtoul(ylo->str, NULL,16);
|
|
else if (!strcmp(key, "svid")) s->svid = strtoul(ylo->str, NULL,16);
|
|
else if (!strcmp(key, "ssid")) s->ssid = strtoul(ylo->str, NULL,16);
|
|
else if (!strcmp(key, "sn")) s->sn = strtoull(ylo->str, NULL, 0);
|
|
else if (!strcmp(key, "max_msg_size_n")) s->max_msg_size_n = atoi(ylo->str);
|
|
else if (!strcmp(key, "bos_running")) s->bos_running = strtoul(ylo->str, NULL,0);
|
|
else if (!strcmp(key, "bos_pcnt")) s->bos_pcnt = strtoul(ylo->str, NULL,0);
|
|
else if (!strcmp(key, "bos_opcode")) s->bos_opcode = strtoul(ylo->str, NULL,0);
|
|
else if (!strcmp(key, "bos_rc")) s->bos_rc = strtoul(ylo->str, NULL,0);
|
|
else if (!strcmp(key, "bos_ext")) s->bos_ext = strtoul(ylo->str, NULL,0);
|
|
else if (!strcmp(key, "msg_rsp_limit_n")) s->msg_rsp_limit_n = atoi(ylo->str);
|
|
else if (!strcmp(key, "ingress_port")) s->ingress_port = atoi(ylo->str);
|
|
else if (!strcmp(key, "num_ports")) s->num_ports = atoi(ylo->str);
|
|
else if (!strcmp(key, "num_vcss")) s->num_vcss = atoi(ylo->str);
|
|
else if (!strcmp(key, "num_vppbs")) s->num_vppbs = atoi(ylo->str);
|
|
else if (!strcmp(key, "num_decoders")) s->num_decoders = atoi(ylo->str);
|
|
else if (!strcmp(key, "mlw")) s->mlw = atoi(ylo->str);
|
|
else if (!strcmp(key, "speeds")) s->speeds = strtoul(ylo->str, NULL, 0);
|
|
else if (!strcmp(key, "mls")) s->mls = atoi(ylo->str);
|
|
|
|
rv = 0;
|
|
|
|
end:
|
|
|
|
EXIT(rv)
|
|
}
|
|
|
|
/**
|
|
* Function to parse each ports entry in the hashtable
|
|
*
|
|
* STEPS:
|
|
* 1: Verify the yaml loader object hash table is not NULL
|
|
* 2: Call parse function for each port
|
|
*/
|
|
void _parse_ports(gpointer key, gpointer value, gpointer user_data)
|
|
{
|
|
INIT
|
|
yl_obj_t *ylo;
|
|
struct port *ports;
|
|
int rv, id;
|
|
|
|
ENTER
|
|
|
|
// Initialize varialbes
|
|
rv = 1;
|
|
ylo = (yl_obj_t*) value;
|
|
ports = (struct port*) user_data;
|
|
|
|
STEP // 1: Verify the yaml loader object hash table is not NULL
|
|
if ( ylo->ht == NULL )
|
|
goto end;
|
|
|
|
STEP // 2: Call parse function for each port
|
|
id = atoi(key);
|
|
|
|
IFV(CLVB_PARSE) printf("%d:%s Parsing Port: %d\n", gettid(), __FUNCTION__, id);
|
|
|
|
g_hash_table_foreach(ylo->ht, _parse_port, &ports[id]);
|
|
|
|
rv = 0;
|
|
|
|
end:
|
|
|
|
EXIT(rv)
|
|
}
|
|
|
|
/**
|
|
* Function to parse each port entry in the hashtable
|
|
*
|
|
* STEPS:
|
|
* 1: Verify the yaml loader object string is not NULL
|
|
* 2: Assign KV pairs to state variables
|
|
*/
|
|
void _parse_port(gpointer key, gpointer value, gpointer user_data)
|
|
{
|
|
INIT
|
|
yl_obj_t *ylo;
|
|
struct port *port;
|
|
int rv;
|
|
|
|
ENTER
|
|
|
|
// Initialize varialbes
|
|
rv = 1;
|
|
ylo = (yl_obj_t*) value;
|
|
port = (struct port*) user_data;
|
|
|
|
STEP // 1: Verify the yaml loader object string is not NULL
|
|
if ( ylo->str == NULL )
|
|
goto end;
|
|
|
|
STEP // 2: Assign KV pairs to state variables
|
|
|
|
IFV(CLVB_PARSE) printf("%d:%s Parsing Key: %s VAL: %s\n", gettid(), __FUNCTION__, (char*) key, ylo->str);
|
|
|
|
if (!strcmp(key, "device")) port->device_name = strdup(ylo->str);
|
|
else if (!strcmp(key, "mlw")) port->mlw = atoi(ylo->str);
|
|
else if (!strcmp(key, "mls")) port->mls = atoi(ylo->str);
|
|
else if (!strcmp(key, "state")) port->state = strtoul(ylo->str, NULL, 0);
|
|
|
|
rv = 0;
|
|
|
|
end:
|
|
|
|
EXIT(rv)
|
|
}
|
|
|
|
/**
|
|
* Function to parse each VCS entry
|
|
*
|
|
* STEPS:
|
|
* 1: Verify the yaml loader object hash table is not NULL
|
|
* 2: Call parse function for each VCS
|
|
*/
|
|
void _parse_vcss(gpointer key, gpointer value, gpointer user_data)
|
|
{
|
|
INIT
|
|
yl_obj_t *ylo;
|
|
struct vcs *vcss;
|
|
int rv, id;
|
|
|
|
ENTER
|
|
|
|
// Initialize varialbes
|
|
rv = 1;
|
|
ylo = (yl_obj_t*) value;
|
|
vcss = (struct vcs*) user_data;
|
|
|
|
STEP // 1: Verify the yaml loader object hash table is not NULL
|
|
if ( ylo->ht == NULL )
|
|
goto end;
|
|
|
|
STEP // 2: Call parse function for each vcs
|
|
id = atoi(key);
|
|
|
|
IFV(CLVB_PARSE) printf("%d:%s Parsing VCS: %d\n", gettid(), __FUNCTION__, id);
|
|
|
|
g_hash_table_foreach(ylo->ht, _parse_vcs, &vcss[id]);
|
|
|
|
rv = 0;
|
|
|
|
end:
|
|
|
|
EXIT(rv)
|
|
}
|
|
|
|
/**
|
|
* Function to parse each VCS block entry
|
|
*
|
|
* STEPS:
|
|
* 1: Assign KV pairs to state variables
|
|
* 2: Call parse function for vPPBs
|
|
*/
|
|
void _parse_vcs(gpointer key, gpointer value, gpointer user_data)
|
|
{
|
|
INIT
|
|
yl_obj_t *ylo;
|
|
struct vcs *vcs;
|
|
|
|
ENTER
|
|
|
|
// Initialize varialbes
|
|
ylo = (yl_obj_t*) value;
|
|
vcs = (struct vcs*) user_data;
|
|
|
|
STEP // 1: Assign KV pairs to state variables
|
|
if ( ylo->str != NULL )
|
|
{
|
|
IFV(CLVB_PARSE) printf("%d:%s Parsing Key: %s VAL: %s\n", gettid(), __FUNCTION__, (char*) key, ylo->str);
|
|
|
|
if (!strcmp(key, "state")) vcs->state = atoi(ylo->str);
|
|
else if (!strcmp(key, "uspid")) vcs->uspid = atoi(ylo->str);
|
|
else if (!strcmp(key, "num_vppb")) vcs->num = atoi(ylo->str);
|
|
}
|
|
|
|
STEP // 2: Call parse function for vPPBs
|
|
if (ylo->ht != NULL)
|
|
{
|
|
g_hash_table_foreach(ylo->ht, _parse_vppbs, vcs->vppbs);
|
|
}
|
|
|
|
EXIT(0)
|
|
}
|
|
|
|
/**
|
|
* Function to parse each vPPB entry
|
|
*
|
|
* STEPS:
|
|
* 1: Verify the yaml loader object hash table is not NULL
|
|
* 2: Call parse function for each vPPB
|
|
*/
|
|
void _parse_vppbs(gpointer key, gpointer value, gpointer user_data)
|
|
{
|
|
INIT
|
|
yl_obj_t *ylo;
|
|
struct vppb *vppbs;
|
|
int rv, id;
|
|
|
|
ENTER
|
|
|
|
// Initialize varialbes
|
|
rv = 1;
|
|
ylo = (yl_obj_t*) value;
|
|
vppbs = (struct vppb*) user_data;
|
|
|
|
STEP // 1: Verify the yaml loader object hash table is not NULL
|
|
if ( ylo->ht == NULL )
|
|
goto end;
|
|
|
|
STEP // 2: Call parse function for each vPPB
|
|
id = atoi(key);
|
|
|
|
IFV(CLVB_PARSE) printf("%d:%s Parsing vPPB: %d", gettid(), __FUNCTION__, id);
|
|
|
|
g_hash_table_foreach(ylo->ht, _parse_vppb, &vppbs[id]);
|
|
|
|
rv = 0;
|
|
|
|
end:
|
|
|
|
EXIT(rv)
|
|
}
|
|
|
|
/**
|
|
* Function to parse each vPPB block entry
|
|
*
|
|
* STEPS:
|
|
* 1: Verify the yaml loader object string is not NULL
|
|
* 2: Assign KV pairs to state variables
|
|
*/
|
|
void _parse_vppb(gpointer key, gpointer value, gpointer user_data)
|
|
{
|
|
INIT
|
|
yl_obj_t *ylo;
|
|
struct vppb *vppb;
|
|
int rv;
|
|
|
|
ENTER
|
|
|
|
// Initialize varialbes
|
|
rv = 1;
|
|
ylo = (yl_obj_t*) value;
|
|
vppb = (struct vppb*) user_data;
|
|
|
|
STEP // 1: Verify the yaml loader object string is not NULL
|
|
if ( ylo->str == NULL )
|
|
goto end;
|
|
|
|
STEP // 2: Assign KV pairs to state variables
|
|
|
|
IFV(CLVB_PARSE) printf("%d:%s Parsing Key: %s VAL: %s\n", gettid(), __FUNCTION__, (char*) key, ylo->str);
|
|
|
|
if (!strcmp(key, "bind_status")) vppb->bind_status = atoi(ylo->str);
|
|
else if (!strcmp(key, "ppid")) vppb->ppid = atoi(ylo->str);
|
|
else if (!strcmp(key, "ldid")) vppb->ldid = atoi(ylo->str);
|
|
|
|
rv = 0;
|
|
|
|
end:
|
|
|
|
EXIT(rv)
|
|
}
|
|
|
|
/**
|
|
* Print the CXL Switch State
|
|
*/
|
|
void state_print(struct cxl_switch_state *state)
|
|
{
|
|
state_print_identity(state, 0);
|
|
state_print_ports(state, 0);
|
|
state_print_vcss(state, 0);
|
|
}
|
|
|
|
/**
|
|
* Print the Device List
|
|
*/
|
|
void state_print_devices(struct cxl_switch_state *s)
|
|
{
|
|
struct cse_device *d;
|
|
|
|
if (s->devices == NULL)
|
|
return;
|
|
|
|
for ( unsigned i = 0 ; i < s->num_devices ; i++ )
|
|
{
|
|
d = &s->devices[i];
|
|
|
|
printf("%s:\n", d->name);
|
|
printf(" Port:\n");
|
|
printf(" dt: %2d - %s\n", d->dt, fmdt(d->dt));
|
|
printf(" dv: %2d - %s\n", d->dv, fmdv(d->dv));
|
|
printf(" cv: %2d - %s\n", d->cv, fmvc(d->cv));
|
|
printf(" mlw: %2d\n", d->mlw);
|
|
|
|
pcie_prnt_cfgspace(d->cfgspace, 2);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Print the CXL Switch Idenfity Information
|
|
*
|
|
* @param struct cxl_switch_state* to print
|
|
* @param indent The number of spaces to indent the printed text
|
|
*/
|
|
void state_print_identity(struct cxl_switch_state *s, unsigned indent)
|
|
{
|
|
char space[MAX_INDENT] = " ";
|
|
|
|
// Handle indent
|
|
if (indent >= MAX_INDENT)
|
|
indent = MAX_INDENT;
|
|
space[indent] = 0;
|
|
|
|
// Print fields
|
|
printf("%singress_port: %u\n", space, s->ingress_port);
|
|
printf("%snum_ports: %u\n", space, s->num_ports);
|
|
printf("%snum_vcss: %u\n", space, s->num_vcss);
|
|
printf("%snum_vppbs: %u\n", space, s->num_vppbs);
|
|
printf("%snum_decoders: %u\n", space, s->num_decoders);
|
|
printf("%sdir: %s\n", space, s->dir);
|
|
|
|
}
|
|
|
|
/**
|
|
* Print CXL MLD Info
|
|
*
|
|
* @param mld struct mld* to use to print
|
|
* @param indent The number of spaces to indent the printed text
|
|
*/
|
|
void state_print_mld(struct mld *mld, unsigned indent)
|
|
{
|
|
char space[MAX_INDENT] = " ";
|
|
|
|
// Handle indent
|
|
if (indent >= MAX_INDENT)
|
|
indent = MAX_INDENT;
|
|
space[indent] = 0;
|
|
|
|
printf("%sMulti-Logical Device:\n", space);
|
|
|
|
space[indent] = ' ';
|
|
space[indent+2] = 0;
|
|
|
|
printf("%sMemory Size 0x%016llx\n", space, mld->memory_size);
|
|
printf("%sNum LD %d\n", space, mld->num);
|
|
printf("%sEgress Port Congestion Supported %d\n", space, mld->epc);
|
|
printf("%sTemporary Throughput Reduction Supported %d\n", space, mld->ttr);
|
|
printf("%sGranularity %d - %s\n", space, mld->granularity, fmmg(mld->granularity));
|
|
printf("%sEgress Port Congestion Enabled %d\n", space, mld->epc_en);
|
|
printf("%sTemporary Throughput Reduction Enabled %d\n", space, mld->ttr_en);
|
|
printf("%sEgress Moderate Percentage %d\n", space, mld->egress_mod_pcnt);
|
|
printf("%sEgress Severe Percentage %d\n", space, mld->egress_sev_pcnt);
|
|
printf("%sBackpressure Sample Interval %d\n", space, mld->sample_interval);
|
|
printf("%sReqCmpBasis %d\n", space, mld->rcb);
|
|
printf("%sCompletion Collection Interval %d\n", space, mld->comp_interval);
|
|
printf("%sBackpressure Average Percentage %d\n", space, mld->bp_avg_pcnt);
|
|
printf("%smmap %d\n", space, mld->mmap);
|
|
printf("%smmap file %s\n", space, mld->file);
|
|
printf("\n");
|
|
printf("%sLDID Range 1 Range 2 Alloc BW BW Limit\n", space);
|
|
printf("%s---- ------------------ ------------------ -------- --------\n", space);
|
|
for ( int i = 0 ; i < mld->num ; i++ )
|
|
printf("%s%4d: 0x%016llx 0x%016llx %8d %8d\n", space, i, mld->rng1[i], mld->rng2[i], mld->alloc_bw[i], mld->bw_limit[i]);
|
|
}
|
|
|
|
/**
|
|
* Print CXL Ports
|
|
*
|
|
* @param struct cxl_switch_stat* to use to print
|
|
* @param indent The number of spaces to indent the printed text
|
|
*/
|
|
void state_print_ports(struct cxl_switch_state *s, unsigned indent)
|
|
{
|
|
char space[MAX_INDENT] = " ";
|
|
|
|
// Handle indent
|
|
if (indent >= MAX_INDENT)
|
|
indent = MAX_INDENT;
|
|
space[indent] = 0;
|
|
|
|
// Print fields
|
|
printf("%sports:\n", space);
|
|
|
|
for (int i = 0 ; i < s->num_ports ; i++) {
|
|
printf("%s %02u:\n", space,i);
|
|
state_print_port(&s->ports[i], indent + 2 + INDENT);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Print the CXL Port Information
|
|
*
|
|
* @param struct port* to print
|
|
* @param indent The number of spaces to indent the printed text
|
|
*/
|
|
void state_print_port(struct port *p, unsigned indent)
|
|
{
|
|
char space[MAX_INDENT] = " ";
|
|
|
|
// Handle indent
|
|
if (indent >= MAX_INDENT)
|
|
indent = MAX_INDENT;
|
|
space[indent] = 0;
|
|
|
|
// Print fields
|
|
printf("%sstate: %u\t\t%s\n", space, p->state, fmps(p->state));
|
|
printf("%sdv: %u\t\t%s\n", space, p->dv, fmdv(p->dv));
|
|
printf("%sdt: %u\t\t%s\n", space, p->dt, fmdt(p->dt));
|
|
printf("%scv: 0x%02x\n", space, p->cv);
|
|
printf("%smax_link_width: %u\n", space, p->mlw);
|
|
printf("%sneg_link_width: %u\n", space, p->nlw);
|
|
printf("%sspeeds: 0x%02x\n", space, p->speeds);
|
|
printf("%smax_link_speed: %u\t\t%s\n", space, p->mls, fmms(p->mls));
|
|
printf("%scur_link_speed: %u\t\t%s\n", space, p->cls, fmms(p->cls));
|
|
printf("%sltssm: %u\t\t%s\n", space, p->ltssm, fmls(p->ltssm));
|
|
printf("%sfirst_lane: %u\n", space, p->lane);
|
|
printf("%sLane Reversal State %d\n", space, p->lane_rev);
|
|
printf("%sPCIe Reset State %d\n", space, p->perst);
|
|
printf("%sPort Presence pin state %d\n", space, p->prsnt);
|
|
printf("%sPower Control State %d\n", space, p->pwrctrl);
|
|
printf("%sld: %u\n", space, p->ld);
|
|
printf("%sDevice Name %s\n", space, p->device_name);
|
|
|
|
if (p->cfgspace != NULL) {
|
|
pcie_prnt_cfgspace(p->cfgspace, indent);
|
|
autl_prnt_buf(p->cfgspace, 1024, 16, 1);
|
|
}
|
|
|
|
if (p->mld != NULL)
|
|
state_print_mld(p->mld, indent);
|
|
}
|
|
|
|
/**
|
|
* Print the CXL VCS List
|
|
*
|
|
* @param struct cxl_switch_state* to print from
|
|
* @param indent The number of spaces to indent the printed text
|
|
*/
|
|
void state_print_vcss(struct cxl_switch_state *s, unsigned indent)
|
|
{
|
|
char space[MAX_INDENT] = " ";
|
|
|
|
// Handle indent
|
|
if (indent >= MAX_INDENT)
|
|
indent = MAX_INDENT;
|
|
space[indent] = 0;
|
|
|
|
// Print fields
|
|
printf("%svcss:\n", space);
|
|
|
|
for (int i = 0 ; i < s->num_vcss ; i++) {
|
|
printf("%s %02u:\n", space, i);
|
|
state_print_vcs(&s->vcss[i], indent + 2 + INDENT);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Print information for a single CXL VCS
|
|
*
|
|
* @param struct vcs* to print
|
|
* @param indent The number of spaces to indent the printed text
|
|
*/
|
|
void state_print_vcs(struct vcs *v, unsigned indent)
|
|
{
|
|
char space[MAX_INDENT] = " ";
|
|
|
|
// Handle indent
|
|
if (indent >= MAX_INDENT)
|
|
indent = MAX_INDENT;
|
|
space[indent] = 0;
|
|
|
|
// Print fields of the VCS
|
|
printf("%sstate: %u\t\t%s\n", space, v->state, fmvs(v->state));
|
|
printf("%suspid: %u\n", space, v->uspid);
|
|
printf("%snum_vppb: %u\n", space, v->num);
|
|
printf("%svppbs:\n", space);
|
|
|
|
// Print the vPPBs of the VCS
|
|
for (int i = 0 ; i < v->num ; i++) {
|
|
printf("%s %u:\n", space, i);
|
|
state_print_vppb(&v->vppbs[i], indent + 2 + INDENT);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Print information for a single CXL vPPB
|
|
*
|
|
* @param struct vppb* to print
|
|
* @param indent The number of spaces to indent the printed text
|
|
*/
|
|
void state_print_vppb(struct vppb *b, unsigned indent)
|
|
{
|
|
char space[MAX_INDENT] = " ";
|
|
|
|
// Handle indent
|
|
if (indent >= MAX_INDENT)
|
|
indent = MAX_INDENT;
|
|
space[indent] = 0;
|
|
|
|
// Print fields of the VCS
|
|
printf("%sldid: %u\n", space, b->ldid);
|
|
printf("%sppid: %u\n", space, b->ppid);
|
|
printf("%sbind_status: %u\t\t%s\n", space, b->bind_status, fmbs(b->bind_status));
|
|
}
|
|
|