Release candidate

This commit is contained in:
Grant Mackey 2024-04-02 04:55:14 +00:00
parent a1db8a3a1b
commit 7d9fa84cb5
21 changed files with 11635 additions and 2 deletions

2659
Doxyfile Normal file

File diff suppressed because it is too large Load Diff

76
Makefile Normal file
View File

@ -0,0 +1,76 @@
# SPDX-License-Identifier: Apache-2.0
# ******************************************************************************
#
# @file Makefile
#
# @brief Makefile for CXL Switch Endpoint Application
#
# @copyright Copyright (C) 2024 Jackrabbit Founders LLC. All rights reserved.
#
# @date Mar 2024
# @author Barrett Edwards <code@jrlabs.io>
#
# ******************************************************************************
CC=gcc
CFLAGS= -g3 -O0 -Wall -Wextra
MACROS=-D CSE_VERBOSE
INCLUDE_DIR=/usr/local/include
LIB_DIR=/usr/local/lib
INCLUDE_PATH=-I $(INCLUDE_DIR) -I /usr/include/glib-2.0 -I /usr/lib/`uname -m`-linux-gnu/glib-2.0/include/ -I /usr/lib64/glib-2.0/include
LIB_PATH=-L $(LIB_DIR)
LIBS=-l yamlloader -l yaml -l glib-2.0 -l mctp -l uuid -l ptrqueue -l fmapi -l emapi -l arrayutils -l pciutils -l timeutils -l pci
TARGET=cse
all: $(TARGET)
$(TARGET): main.c options.o state.o signals.o emapi_handler.o fmapi_handler.o fmapi_isc_handler.o fmapi_psc_handler.o fmapi_vsc_handler.o fmapi_mpc_handler.o fmapi_mcc_handler.o
$(CC) $^ $(CFLAGS) $(MACROS) $(INCLUDE_PATH) $(LIB_PATH) $(LIBS) -o $@
emapi_handler.o: emapi_handler.c emapi_handler.h
$(CC) -c $< $(CFLAGS) $(MACROS) $(INCLUDE_PATH) -o $@
fmapi_mcc_handler.o: fmapi_mcc_handler.c
$(CC) -c $< $(CFLAGS) $(MACROS) $(INCLUDE_PATH) -o $@
fmapi_mpc_handler.o: fmapi_mpc_handler.c
$(CC) -c $< $(CFLAGS) $(MACROS) $(INCLUDE_PATH) -o $@
fmapi_vsc_handler.o: fmapi_vsc_handler.c
$(CC) -c $< $(CFLAGS) $(MACROS) $(INCLUDE_PATH) -o $@
fmapi_psc_handler.o: fmapi_psc_handler.c
$(CC) -c $< $(CFLAGS) $(MACROS) $(INCLUDE_PATH) -o $@
fmapi_isc_handler.o: fmapi_isc_handler.c
$(CC) -c $< $(CFLAGS) $(MACROS) $(INCLUDE_PATH) -o $@
fmapi_handler.o: fmapi_handler.c fmapi_handler.h
$(CC) -c $< $(CFLAGS) $(MACROS) $(INCLUDE_PATH) -o $@
signals.o: signals.c signals.h
$(CC) -c $< $(CFLAGS) $(MACROS) $(INCLUDE_PATH) -o $@
options.o: options.c options.h
$(CC) -c $< $(CFLAGS) $(MACROS) $(INCLUDE_PATH) -o $@
state.o: state.c state.h
$(CC) -c $< $(CFLAGS) $(MACROS) $(INCLUDE_PATH) -o $@
clean:
rm -rf ./*.o ./*.a $(TARGET)
doc:
doxygen
install: $(TARGET)
sudo cp $(TARGET) /usr/local/bin/
# List all non file name targets as PHONY
.PHONY: all clean doc install
# Variables
# $^ Will expand to be all the sensitivity list
# $< Will expand to be the frist file in sensitivity list
# $@ Will expand to be the target name (the left side of the ":" )
# -c gcc will compile but not try and link

View File

@ -1,3 +1,22 @@
# CSE-release # Install
CXL Switch Emulator Install the following Linux Packages:
- libyaml libyaml-dev doxygen uuid-dev pciutils-dev
Clone the following repositories from code.jrlabs.io
- JackrabbitLabs/array_utils.git
- JackrabbitLabs/ptr_queue.git
- JackrabbitLabs/duplex_queue.git
- JackrabbitLabs/yaml_loader.git
- JackrabbitLabs/mctp.git
- JackrabbitLabs/fmapi.git
- JackrabbitLabs/cse.git
- JackrabbitLabs/jack.git
On each repository perform the following commands (in order listed above)
- make
- make install

584
config.yaml Normal file
View File

@ -0,0 +1,584 @@
---
devices:
cpu_5x16_1.1:
did: 0
port:
dv: 1 # 0=NC, 1=CXL1.1, 2=CXL2.0
dt: 2 # 0=NC, 1=PCIe, 2=CXLT1, 3=CXLT2, 4=CXLT3, 5=CXLT3 Pooled, 6=switch
cv: 0x1 # bit0=CXL1.1, bit1=CXL 2.0
mlw: 16
mls: 5
rootport: 1
pcicfg:
vendor: 0x1aed
device: 0x1001
command: 0x0506
status: 0x0010
revid: 0
pi: 0x00
subclass: 0x02
baseclass: 0x05
cacheline: 0x10
type: 0x0
subvendor: 0x1aed
subsystem: 0x0000
cpu_5x16_2.0:
did: 1
port:
dv: 2 # 0=NC, 1=CXL1.1, 2=CXL2.0
dt: 2 # 0=NC, 1=PCIe, 2=CXLT1, 3=CXLT2, 4=CXLT3, 5=CXLT3 Pooled, 6=switch
cv: 0x3 # bit0=CXL1.1, bit1=CXL 2.0
mlw: 16
mls: 5
rootport: 1
pcicfg:
vendor: 0x1aed
device: 0x1002
command: 0x0506
status: 0x0010
revid: 0
pi: 0x00
subclass: 0x02
baseclass: 0x05
cacheline: 0x10
type: 0x0
subvendor: 0x1aed
subsystem: 0x0001
mld_5x8_1.1_4G:
did: 2
port:
dv: 1 # 0=NC, 1=CXL1.1, 2=CXL2.0
dt: 5 # 0=NC, 1=PCIe, 2=CXLT1, 3=CXLT2, 4=CXLT3, 5=CXLT3 Pooled, 6=switch
cv: 0x1 # bit0=CXL1.1, bit1=CXL 2.0
mlw: 8
mls: 5
pcicfg:
vendor: 0x1aed
device: 0x2001
command: 0x0506
status: 0x0010
revid: 0
pi: 0x10 # CXL 2.0 Compliant Device
subclass: 0x02 # 0x02=CXL Device
baseclass: 0x05 # 0x05=Memory Controller
cacheline: 0x10 # number of 4B values. 0x10=64B
type: 0x0
subvendor: 0x1aed
subsystem: 0x0002
intline: 0
intpin: 0
mingnt: 0
maxlat: 0
cap:
0x01: "03,00,00,00,00,00"
0x05: "8A,01,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00"
ecap:
0x00030: "02,00,00,00,00,00,00,00"
mld:
mmap: 1
memory_size: 0x100000000 # 4GB
num: 4
epc: 1
ttr: 1
granularity: 0 # 0=256MB, 1=512MB, 2=1GB
rng1: "0,4,8,12"
rng2: "3,7,11,15"
epc_en: 1
ttr_en: 1
egress_mod_pcnt: 10
egress_sev_pcnt: 25
sample_interval: 8
req_cmp_basis: 0
comp_interval: 64
bp_avg_pcnt: 42
alloc_bw: "64,64,64,64"
bw_limit: "ff,ff,ff,ff"
mld_5x8_1.1_64G:
did: 3
port:
dv: 1 # 0=NC, 1=CXL1.1, 2=CXL2.0
dt: 5 # 0=NC, 1=PCIe, 2=CXLT1, 3=CXLT2, 4=CXLT3, 5=CXLT3 Pooled, 6=switch
cv: 0x1 # bit0=CXL1.1, bit1=CXL 2.0
mlw: 8
mls: 5
pcicfg:
vendor: 0x1aed
device: 0x3001
command: 0x0506
status: 0x0010
revid: 0
pi: 0x10
subclass: 0x02
baseclass: 0x05
cacheline: 0x10
type: 0x0
subvendor: 0x1aed
subsystem: 0x0006
cap:
0x01: "03,00,00,00,00,00"
0x05: "8A,01,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00"
ecap:
0x00030: "06,00,00,00,00,00,00,00"
mld:
memory_size: 0x1000000000 # 64GB
num: 16
epc: 1
ttr: 1
granularity: 2 # 0=256MB, 1=512MB, 2=1GB
rng1: "0,4,8,12,16,20,24,28,32,36,40,44,48,52,56,60"
rng2: "4,8,12,16,20,24,28,32,36,40,44,48,52,56,60,64"
epc_en: 1
ttr_en: 1
egress_mod_pcnt: 10
egress_sev_pcnt: 25
sample_interval: 8
req_cmp_basis: 0
comp_interval: 64
bp_avg_pcnt: 42
alloc_bw: "16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16"
bw_limit: "ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff"
mld_5x8_2.0_4G:
did: 4
port:
dv: 2 # 0=NC, 1=CXL1.1, 2=CXL2.0
dt: 5 # 0=NC, 1=PCIe, 2=CXLT1, 3=CXLT2, 4=CXLT3, 5=CXLT3 Pooled, 6=switch
cv: 0x3 # bit0=CXL1.1, bit1=CXL 2.0
mlw: 8
mls: 5
pcicfg:
vendor: 0x1aed
device: 0x2003
revid: 0
command: 0x0506
status: 0x0010
pi: 0x10
subclass: 0x02
baseclass: 0x05
cacheline: 0x10
type: 0x0
subvendor: 0x1aed
subsystem: 0x0004
cap:
0x01: "03,00,00,00,00,00"
0x05: "8A,01,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00"
ecap:
0x00030: "04,00,00,00,00,00,00,00"
mld:
mmap: 1
memory_size: 0x100000000 # 4GB
num: 4
epc: 1
ttr: 1
granularity: 0 # 0=256MB, 1=512MB, 2=1GB
rng1: "0,4,8,12"
rng2: "3,7,11,15"
epc_en: 1
ttr_en: 1
egress_mod_pcnt: 10
egress_sev_pcnt: 25
sample_interval: 8
req_cmp_basis: 0
comp_interval: 64
bp_avg_pcnt: 42
alloc_bw: "64,64,64,64"
bw_limit: "ff,ff,ff,ff"
mld_5x8_2.0_64G:
did: 5
port:
dv: 2 # 0=NC, 1=CXL1.1, 2=CXL2.0
dt: 5 # 0=NC, 1=PCIe, 2=CXLT1, 3=CXLT2, 4=CXLT3, 5=CXLT3 Pooled, 6=switch
cv: 0x3 # bit0=CXL1.1, bit1=CXL 2.0
mlw: 8
mls: 5
pcicfg:
vendor: 0x1aed
device: 0x3003
command: 0x0506
status: 0x0010
revid: 0
pi: 0x10
subclass: 0x02
baseclass: 0x05
cacheline: 0x10
type: 0x0
subvendor: 0x1aed
subsystem: 0x0008
cap:
0x01: "03,00,00,00,00,00"
0x05: "8A,01,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00"
ecap:
0x00030: "08,00,00,00,00,00,00,00"
mld:
memory_size: 0x1000000000 # 64GB
num: 16
epc: 1
ttr: 1
granularity: 2 # 0=256MB, 1=512MB, 2=1GB
rng1: "0,4,8,12,16,20,24,28,32,36,40,44,48,52,56,60"
rng2: "4,8,12,16,20,24,28,32,36,40,44,48,52,56,60,64"
epc_en: 1
ttr_en: 1
egress_mod_pcnt: 10
egress_sev_pcnt: 25
sample_interval: 8
req_cmp_basis: 0
comp_interval: 64
bp_avg_pcnt: 42
alloc_bw: "16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16"
bw_limit: "ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff"
mld_5x16_1.1_4G:
did: 6
port:
dv: 1 # 0=NC, 1=CXL1.1, 2=CXL2.0
dt: 5 # 0=NC, 1=PCIe, 2=CXLT1, 3=CXLT2, 4=CXLT3, 5=CXLT3 Pooled, 6=switch
cv: 0x1 # bit0=CXL1.1, bit1=CXL 2.0
mlw: 16
mls: 5
pcicfg:
vendor: 0x1aed
device: 0x2002
command: 0x0506
status: 0x0010
revid: 0
pi: 0x10
subclass: 0x02
baseclass: 0x05
cacheline: 0x10
type: 0x0
subvendor: 0x1aed
subsystem: 0x0003
intline: 0
intpin: 0
mingnt: 0
maxlat: 0
cap:
0x01: "03,00,00,00,00,00"
0x05: "8A,01,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00"
ecap:
0x00030: "03,00,00,00,00,00,00,00"
mld:
mmap: 1
memory_size: 0x100000000 # 4GB
num: 4
epc: 1
ttr: 1
granularity: 0 # 0=256MB, 1=512MB, 2=1GB
rng1: "0,4,8,12"
rng2: "3,7,11,15"
epc_en: 1
ttr_en: 1
egress_mod_pcnt: 10
egress_sev_pcnt: 25
sample_interval: 8
req_cmp_basis: 0
comp_interval: 64
bp_avg_pcnt: 42
alloc_bw: "64,64,64,64"
bw_limit: "ff,ff,ff,ff"
mld_5x16_1.1_64G:
did: 7
port:
dv: 1 # 0=NC, 1=CXL1.1, 2=CXL2.0
dt: 5 # 0=NC, 1=PCIe, 2=CXLT1, 3=CXLT2, 4=CXLT3, 5=CXLT3 Pooled, 6=switch
cv: 0x1 # bit0=CXL1.1, bit1=CXL 2.0
mlw: 16
mls: 5
pcicfg:
vendor: 0x1aed
device: 0x3002
command: 0x0506
status: 0x0010
revid: 0
pi: 0x10
subclass: 0x02
baseclass: 0x05
cacheline: 0x10
type: 0x0
subvendor: 0x1aed
subsystem: 0x0007
cap:
0x01: "03,00,00,00,00,00"
0x05: "8A,01,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00"
ecap:
0x00030: "07,00,00,00,00,00,00,00"
mld:
memory_size: 0x1000000000 # 64GB
num: 16
epc: 1
ttr: 1
granularity: 2 # 0=256MB, 1=512MB, 2=1GB
rng1: "0,4,8,12,16,20,24,28,32,36,40,44,48,52,56,60"
rng2: "4,8,12,16,20,24,28,32,36,40,44,48,52,56,60,64"
epc_en: 1
ttr_en: 1
egress_mod_pcnt: 10
egress_sev_pcnt: 25
sample_interval: 8
req_cmp_basis: 0
comp_interval: 64
bp_avg_pcnt: 42
alloc_bw: "16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16"
bw_limit: "ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff"
mld_5x16_2.0_4G:
did: 8
port:
dv: 2 # 0=NC, 1=CXL1.1, 2=CXL2.0
dt: 5 # 0=NC, 1=PCIe, 2=CXLT1, 3=CXLT2, 4=CXLT3, 5=CXLT3 Pooled, 6=switch
cv: 0x3 # bit0=CXL1.1, bit1=CXL 2.0
mlw: 16
mls: 5
pcicfg:
vendor: 0x1aed
device: 0x2004
revid: 0
command: 0x0506
status: 0x0010
pi: 0x10
subclass: 0x02
baseclass: 0x05
cacheline: 0x10
type: 0x0
subvendor: 0x1aed
subsystem: 0x0005
cap:
0x01: "03,00,00,00,00,00"
0x05: "8A,01,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00"
ecap:
0x00030: "05,00,00,00,00,00,00,00"
mld:
mmap: 1
memory_size: 0x100000000 # 4GB
num: 4
epc: 1
ttr: 1
granularity: 0 # 0=256MB, 1=512MB, 2=1GB
rng1: "0,4,8,12"
rng2: "3,7,11,15"
epc_en: 1
ttr_en: 1
egress_mod_pcnt: 10
egress_sev_pcnt: 25
sample_interval: 8
req_cmp_basis: 0
comp_interval: 64
bp_avg_pcnt: 42
alloc_bw: "64,64,64,64"
bw_limit: "ff,ff,ff,ff"
mld_5x16_2.0_64G:
did: 9
port:
dv: 2 # 0=NC, 1=CXL1.1, 2=CXL2.0
dt: 5 # 0=NC, 1=PCIe, 2=CXLT1, 3=CXLT2, 4=CXLT3, 5=CXLT3 Pooled, 6=switch
cv: 0x3 # bit0=CXL1.1, bit1=CXL 2.0
mlw: 16
mls: 5
pcicfg:
vendor: 0x1aed
device: 0x3004
command: 0x0506
status: 0x0010
revid: 0
pi: 0x10
subclass: 0x02
baseclass: 0x05
cacheline: 0x10
type: 0x0
subvendor: 0x1aed
subsystem: 0x0009
cap:
0x01: "03,00,00,00,00,00"
0x05: "8A,01,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00"
ecap:
0x00030: "09,00,00,00,00,00,00,00"
mld:
memory_size: 0x1000000000 # 64GB
num: 16
epc: 1
ttr: 1
granularity: 2 # 0=256MB, 1=512MB, 2=1GB
rng1: "0,4,8,12,16,20,24,28,32,36,40,44,48,52,56,60"
rng2: "4,8,12,16,20,24,28,32,36,40,44,48,52,56,60,64"
epc_en: 1
ttr_en: 1
egress_mod_pcnt: 10
egress_sev_pcnt: 25
sample_interval: 8
req_cmp_basis: 0
comp_interval: 64
bp_avg_pcnt: 42
alloc_bw: "16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16"
bw_limit: "ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff"
sld_5x8_1.1_4G:
did: 10
port:
dv: 1 # 0=NC, 1=CXL1.1, 2=CXL2.0
dt: 4 # 0=NC, 1=PCIe, 2=CXLT1, 3=CXLT2, 4=CXLT3, 5=CXLT3 Pooled, 6=switch
cv: 0x1 # bit0=CXL1.1, bit1=CXL 2.0
mlw: 8
mls: 5
pcicfg:
vendor: 0x1aed
device: 0x4001
command: 0x0506
status: 0x0010
revid: 0
pi: 0x00 # 0=Vendor Specific 0x10=CXL 2.0 Compliant Device
subclass: 0x02 # 0x02=CXL Device
baseclass: 0x05 # 0x05=Memory Controller
cacheline: 0x10 # number of 4B values. 0x10=64B
type: 0x0
subvendor: 0x1aed
subsystem: 0x000A
intline: 0
intpin: 0
mingnt: 0
maxlat: 0
cap:
0x01: "03,00,00,00,00,00"
0x05: "8A,01,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00"
ecap:
0x00030: "02,00,00,00,00,00,00,00"
---
emulator:
# verbosity-hex: 0x70
# verbosity-mctp: 0x00
tcp-port: 2508
dir: "/cxl" # mount -t tmpfs -o size=32G,mode=1777 cxl /cxl
---
switch:
vid: 0xa1a2
did: 0xb1b2
svid: 0xc1c2
ssid: 0xd1d2
sn: 0xa1a2a3a4a5a6a7a8
max_msg_size_n: 13
msg_rsp_limit_n: 13
bos_running: 0
bos_pcnt: 0
bos_opcode: 0
bos_rc: 0
bos_ext: 0
ingress_port: 1
num_ports: 32
num_vcss: 4
num_vppbs: 32
num_decoders: 32
mlw: 16
speeds: 0x30
mls: 5
---
ports:
0:
device: "cpu_5x16_2.0"
1:
device: "mld_5x8_2.0_4G"
2:
device: "mld_5x8_2.0_4G"
3:
device: "mld_5x16_2.0_64G"
4:
device: "mld_5x16_2.0_64G"
8:
state: 4
# device: "cpu_5x16_2.0"
9:
# device: "mld_5x8_1.1_4G"
10:
# device: "mld_5x16_1.1_4G"
11:
# device: "mld_5x8_2.0_4G"
12:
# device: "mld_5x16_2.0_4G"
16:
mlw: 8
state: 4
# device: "cpu_5x16_2.0"
17:
mlw: 8
# device: "mld_5x8_1.1_4G"
18:
mlw: 8
# device: "mld_5x16_1.1_4G"
19:
mlw: 8
# device: "mld_5x8_2.0_4G"
20:
mlw: 8
# device: "mld_5x16_2.0_4G"
21:
mlw: 8
22:
mlw: 8
23:
mlw: 8
24:
mlw: 8
mls: 6
state: 4
25:
mlw: 8
mls: 6
speeds: 0x70
26:
mlw: 8
mls: 6
speeds: 0x70
27:
mlw: 8
mls: 6
speeds: 0x70
28:
mlw: 8
mls: 6
speeds: 0x70
29:
mlw: 8
mls: 6
speeds: 0x70
30:
mlw: 8
mls: 6
speeds: 0x70
31:
mlw: 8
mls: 6
speeds: 0x70
---
vcss:
0:
state: 1
uspid: 0
num_vppb: 8
vppbs:
0:
bind_status: 2 # 0=Unbound, 1=Inprogress, 2=Bound Physical, 3=Bound LD
ppid: 0
ldid: 0
1:
bind_status: 3 # 0=Unbound, 1=Inprogress, 2=Bound Physical, 3=Bound LD
ppid: 1
ldid: 0
2:
bind_status: 3 # 0=Unbound, 1=Inprogress, 2=Bound Physical, 3=Bound LD
ppid: 2
ldid: 0
3:
bind_status: 3 # 0=Unbound, 1=Inprogress, 2=Bound Physical, 3=Bound LD
ppid: 3
ldid: 0
4:
bind_status: 3 # 0=Unbound, 1=Inprogress, 2=Bound Physical, 3=Bound LD
ppid: 4
ldid: 0
1:
state: 1
uspid: 8
num_vppb: 8
2:
state: 1
uspid: 16
num_vppb: 8
3:
state: 1
uspid: 24
num_vppb: 8

207
cse.c Normal file
View File

@ -0,0 +1,207 @@
/**
* @file cse.c
*
* @brief Code file for entry point CXL Switch Emulator
*
* @copyright Copyright (C) 2024 Jackrabbit Founders LLC. All rights reserved.
*
* @date Jan 2024
* @author Barrett Edwards <barrett@jrlabs.io>
*
*/
/* INCLUDES ==================================================================*/
/* gettid()
*/
#define _GNU_SOURCE
#include <unistd.h>
/* printf()
*/
#include <stdio.h>
/* memset()
*/
#include <string.h>
/* autl_prnt_buf()
*/
#include <arrayutils.h>
/* mctp_init()
* mctp_set_mh()
* mctp_run()
*/
#include <mctp.h>
#include "signals.h"
#include "options.h"
#include "state.h"
#include "fmapi_handler.h"
#include "emapi_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)
/* ENUMERATIONS ==============================================================*/
/* STRUCTS ===================================================================*/
/* PROTOTYPES ================================================================*/
/* GLOBAL VARIABLES ==========================================================*/
/* FUNCTIONS =================================================================*/
/**
* cse main
*
* STEPS
* 0: Parse CLI options
* 1: Register Signal Handlers
* 2: Initialize global state array
* 3: Load state file
* 4: Print the state
* 5: MCTP Init
* 6: Run MCTP
* 7: While loop
* 8: Stop MCTP
* 9: Free memory
*/
int main(int argc, char* argv[])
{
INIT
int rv;
struct mctp *m;
// Initialize varaibles
stop_requested = 0;
rv = 1;
// 0: Parse CLI options
rv = options_parse(argc,argv);
if (rv != 0)
{
printf("Error: Parse options failed: %d\n", rv);
goto end;
}
STEP // 1: Register Signal Handlers
signals_register();
STEP // 2: Initialize global state array
cxl_state = state_init(32, 32, 256);
if (cxl_state == NULL)
{
printf("Error: state init failed \n");
goto end_options;
}
STEP // 3: Load state file
if (opts[CLOP_CONFIG_FILE].set)
{
rv = state_load(cxl_state, opts[CLOP_CONFIG_FILE].str);
if (rv < 0)
{
printf("Error: state load config file failed \n");
goto end_state;
}
}
STEP // 5: Print the state
if (opts[CLOP_PRINT_STATE].set)
state_print(cxl_state);
STEP // 6: MCTP Init
m = mctp_init();
if (m == NULL)
goto end_state;
// Set supported MCTP Message Versions
mctp_set_version(m, MCMT_CXLFMAPI, 0xF2,0xF1,0xFF,0x00);
mctp_set_version(m, MCMT_CXLCCI, 0xF2,0xF1,0xFF,0x00);
// Set Message handler functions
mctp_set_handler(m, MCMT_CXLFMAPI, fmapi_handler);
mctp_set_handler(m, MCMT_CSE, emapi_handler);
// Set MCTP verbosity levels
mctp_set_verbosity(m, opts[CLOP_MCTP_VERBOSITY].u64);
STEP // 7: Run MCTP
rv = mctp_run(m, opts[CLOP_TCP_PORT].u16, MCRM_SERVER, 1, 1);
if (rv != 0)
{
switch (rv)
{
case -1:
printf("Socket create failed\n");
break;
case -2:
printf("Socket bind failed\n");
break;
case -3:
printf("Socket connect failed");
break;
case 1:
printf("Could not create Connection Handler Thread\n");
break;
case 2:
printf("MCTP threads failed to start\n");
break;
}
goto end_mctp;
}
STEP // 8: While loop
while ( stop_requested == 0 )
{
sleep(1);
}
STEP // 9: Stop MCTP
mctp_stop(m);
end_mctp:
mctp_free(m);
rv = 0;
end_state:
state_free(cxl_state);
end_options:
options_free(opts);
end:
EXIT(rv)
return rv;
};

696
emapi_handler.c Normal file
View File

@ -0,0 +1,696 @@
/* SPDX-License-Identifier: Apache-2.0 */
/**
* @file emapi_handler.c
*
* @brief Code file for methods to respond to CXL Emulator API commands
*
* @copyright Copyright (C) 2024 Jackrabbit Founders LLC. All rights reserved.
*
* @date Feb 2024
* @author Barrett Edwards <code@jrlabs.io>
*
*/
/* INCLUDES ==================================================================*/
/* gettid()
*/
#define _GNU_SOURCE
#include <unistd.h>
/* printf()
*/
#include <stdio.h>
/* memset()
*/
#include <string.h>
/* struct timespec
* timespec_get()
*
*/
#include <time.h>
/* autl_prnt_buf()
*/
#include <arrayutils.h>
/* mctp_init()
* mctp_set_mh()
* mctp_run()
*/
#include <mctp.h>
#include <ptrqueue.h>
#include <timeutils.h>
#include <emapi.h>
#include "signals.h"
#include "options.h"
#include "state.h"
#include "emapi_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 ================================================================*/
static int emop_conn_dev (struct mctp *m, struct mctp_action *ma);
static int emop_disconn_dev(struct mctp *m, struct mctp_action *ma);
static int emop_list_dev (struct mctp *m, struct mctp_action *ma);
static int emop_unsupported(struct mctp *m, struct mctp_action *ma);
/* GLOBAL VARIABLES ==========================================================*/
/* FUNCTIONS =================================================================*/
/**
* Handler for all CXL Emulator API Opcodes
*
* @return 0 upon success, 1 otherwise
*
* STEPS
* 1: Deserialize Header
* 2: Verify EM API Message Type
* 3: Handle Opcode
*/
int emapi_handler(struct mctp *m, struct mctp_action *ma)
{
INIT
struct emapi_hdr hdr;
int rv;
ENTER
// Initialize variables
rv = 1;
STEP // 1: Deserialize Header
if ( emapi_deserialize(&hdr, ma->req->payload, EMOB_HDR, NULL) == 0 )
goto fail;
STEP // 2: Verify EM API Message Type
if (hdr.type != EMMT_REQ)
goto fail;
STEP // 3: Handle Opcode
HEX32("Opcode", hdr.opcode);
switch(hdr.opcode)
{
case EMOP_EVENT: // 0x00
break;
case EMOP_LIST_DEV: // 0x01
rv = emop_list_dev(m, ma);
break;
case EMOP_CONN_DEV: // 0x02
rv = emop_conn_dev(m, ma);
break;
case EMOP_DISCON_DEV: // 0x03
rv = emop_disconn_dev(m, ma);
break;
default:
rv = emop_unsupported(m, ma);
break;
}
rv = 0;
goto end;
fail:
ma->completion_code = 1;
pq_push(m->acq, ma);
end:
EXIT(rv)
return rv;
}
/**
* Handler for EM API Connect Device Command
*
* @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
*/
static int emop_conn_dev(struct mctp *m, struct mctp_action *ma)
{
INIT
char now[ISO_TIME_BUF_LEN];
struct emapi_msg reqm, rspm;
struct emapi_buf *reqb, *rspb;
unsigned rc;
int rv, len;
__u8 ppid, dev;
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 fail;
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
reqb = (struct emapi_buf*) ma->req->payload;
rspb = (struct emapi_buf*) ma->rsp->payload;
STEP // 5: Deserialize Request Header
if ( emapi_deserialize(&reqm.hdr, reqb->hdr, EMOB_HDR, NULL) <= 0 )
goto fail;
STEP // 6: Deserialize Request Object
if ( emapi_deserialize(&reqm.obj, reqb->payload, emapi_emob_req(reqm.hdr.opcode), NULL) < 0 )
goto fail;
STEP // 7: Extract parameters
ppid = reqm.hdr.a;
dev = reqm.hdr.b;
IFV(CLVB_COMMANDS) printf("%s CMD: EM API Connect Device. PPID: %d Device: %d\n", now, ppid, dev);
STEP // 8: Obtain lock on switch state
pthread_mutex_lock(&cxl_state->mtx);
STEP // 9: Validate Inputs
if (ppid >= cxl_state->num_ports)
{
IFV(CLVB_ERRORS) printf("%s ERR: PPID out of range. PPID: %d Total: %d\n", now, ppid, cxl_state->num_ports);
goto send;
}
if (dev >= cxl_state->num_devices)
{
IFV(CLVB_ERRORS) printf("%s ERR: Device ID out of range. Device ID: %d Total: %d\n", now, dev, cxl_state->num_devices);
goto send;
}
if (cxl_state->devices[dev].name == NULL)
{
IFV(CLVB_ERRORS) printf("%s ERR: Device is NULL. Device ID: %d\n", now, dev);
goto send;
}
IFV(CLVB_ACTIONS) printf("%s ACT: Connecting Device %d to PPID %d\n", now, dev, ppid);
STEP // 10: Perform Action
state_connect_device(&cxl_state->ports[ppid], &cxl_state->devices[dev]);
STEP // 11: Prepare Response Object
STEP // 12: Serialize Response Object
len = emapi_serialize(rspb->payload, &rspm.obj, emapi_emob_rsp(reqm.hdr.opcode), NULL);
STEP // 13: Set return code
rc = EMRC_SUCCESS;
send:
STEP // 14: Release lock on switch state
pthread_mutex_unlock(&cxl_state->mtx);
if(len < 0)
goto fail;
STEP // 15: Fill Response Header
ma->rsp->len = emapi_fill_hdr(&rspm.hdr, EMMT_RSP, reqm.hdr.tag, rc, reqm.hdr.opcode, len, 0, 0);
STEP // 16: Serialize Header
emapi_serialize(rspb->hdr, &rspm.hdr, EMOB_HDR, NULL);
STEP // 17: Push mctp_action onto queue
pq_push(m->tmq, ma);
rv = 0;
goto end;
fail:
ma->completion_code = 1;
pq_push(m->acq, ma);
end:
EXIT(rc)
return rv;
}
/**
* Handler for EM API Disconnect Device Command
*
* @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
*/
static int emop_disconn_dev(struct mctp *m, struct mctp_action *ma)
{
INIT
char now[ISO_TIME_BUF_LEN];
struct emapi_msg reqm, rspm;
struct emapi_buf *reqb, *rspb;
unsigned rc;
int rv, len;
__u8 ppid, all, start, end, i;
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 fail;
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;
STEP // 4: Set buffer pointers
reqb = (struct emapi_buf*) ma->req->payload;
rspb = (struct emapi_buf*) ma->rsp->payload;
STEP // 5: Deserialize Request Header
if ( emapi_deserialize(&reqm.hdr, reqb->hdr, EMOB_HDR, NULL) <= 0 )
goto fail;
STEP // 6: Deserialize Request Object
if ( emapi_deserialize(&reqm.obj, reqb->payload, emapi_emob_req(reqm.hdr.opcode), NULL) < 0 )
goto fail;
STEP // 7: Extract parameters
ppid = reqm.hdr.a;
all = reqm.hdr.b;
IFV(CLVB_COMMANDS) printf("%s CMD: EM API Disconnect Device. PPID: %d All: %d\n", now, ppid, all);
STEP // 8: Obtain lock on switch state
pthread_mutex_lock(&cxl_state->mtx);
STEP // 9: Validate Inputs
if (all) {
start = 0;
end = cxl_state->num_ports;
}
else {
start = ppid;
end = ppid+1;
}
if (start >= cxl_state->num_ports)
{
IFV(CLVB_ERRORS) printf("%s ERR: PPID out of range. PPID: %d Total: %d\n", now, ppid, cxl_state->num_ports);
goto send;
}
STEP // 10: Perform Action
for ( i = start ; i < end ; i++ )
{
// Validate if port is connected
if (cxl_state->ports[i].prsnt == 1)
{
IFV(CLVB_ACTIONS) printf("%s ACT: Disconnecting PPID %d\n", now, i);
// Perform disconnect
state_disconnect_device(&cxl_state->ports[i]);
}
}
STEP // 11: Prepare Response Object
STEP // 12: Serialize Response Object
len = emapi_serialize(rspb->payload, &rspm.obj, emapi_emob_rsp(reqm.hdr.opcode), NULL);
STEP // 13: Set return code
rc = EMRC_SUCCESS;
send:
STEP // 14: Release lock on switch state
pthread_mutex_unlock(&cxl_state->mtx);
if (len < 0)
goto fail;
STEP // 15: Fill Response Header
ma->rsp->len = emapi_fill_hdr(&rspm.hdr, EMMT_RSP, reqm.hdr.tag, rc, reqm.hdr.opcode, len, 0, 0);
STEP // 16: Serialize Header
emapi_serialize(rspb->hdr, &rspm.hdr, EMOB_HDR, NULL);
STEP // 17: Push mctp_action onto queue
pq_push(m->tmq, ma);
rv = 0;
goto end;
fail:
ma->completion_code = 1;
pq_push(m->acq, ma);
end:
EXIT(rc)
return rv;
}
/**
* Handler for EM API List Devices 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
*/
static int emop_list_dev(struct mctp *m, struct mctp_action *ma)
{
INIT
char now[ISO_TIME_BUF_LEN];
struct emapi_msg reqm, rspm;
struct emapi_buf *reqb, *rspb;
unsigned rc;
int rv, len;
unsigned i, count;
__u8 num_requested, start_num;
struct cse_device *d;
ENTER
STEP // 1: Initialize variables
rv = 1;
len = 0;
rc = FMRC_INVALID_INPUT;
isotime(now, ISO_TIME_BUF_LEN);
count = 0;
STEP // 2: Get response mctp_msg buffer
ma->rsp = pq_pop(m->msgs, 1);
if (ma->rsp == NULL)
goto fail;
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;
STEP // 4: Set buffer pointers
reqb = (struct emapi_buf*) ma->req->payload;
rspb = (struct emapi_buf*) ma->rsp->payload;
STEP // 5: Deserialize Request Header
if ( emapi_deserialize(&reqm.hdr, reqb->hdr, EMOB_HDR, NULL) <= 0 )
goto fail;
STEP // 6: Deserialize Request Object
if ( emapi_deserialize(&reqm.obj, reqb->payload, emapi_emob_req(reqm.hdr.opcode), NULL) < 0 )
goto fail;
STEP // 7: Extract parameters
num_requested = reqm.hdr.a;
start_num = reqm.hdr.b;
IFV(CLVB_COMMANDS) printf("%s CMD: EM API list Devices. Start: %d Num: %d\n", now, start_num, num_requested);
STEP // 8: Obtain lock on switch state
pthread_mutex_lock(&cxl_state->mtx);
STEP // 9: Validate Inputs
if (num_requested == 0)
num_requested = (cxl_state->num_devices - start_num);
if (start_num >= cxl_state->num_devices)
{
IFV(CLVB_ERRORS) printf("%s ERR: Start num out of range. Start: %d Total: %d\n", now, start_num, num_requested);
goto send;
}
if ( (start_num + num_requested) >= cxl_state->num_devices)
num_requested = (cxl_state->num_devices - start_num);
STEP // 10: Perform Action
IFV(CLVB_ACTIONS) printf("%s ACT: Responding with %d devices\n", now, num_requested);
STEP // 11: Prepare Response Object
for ( i = 0 ; i < num_requested ; i++ )
{
d = &cxl_state->devices[start_num + i];
// Serialize the id number
rspb->payload[len+0] = start_num + i;
// Serialize the name string
if (d->name != NULL )
{
rspb->payload[len+1] = strlen(d->name) + 1;
memcpy(&rspb->payload[len+2], d->name, rspb->payload[len+1]);
}
else
rspb->payload[len+1] = 0;
len += (2 + rspb->payload[len+1]);
count++;
}
STEP // 12: Serialize Response Object
STEP // 13: Set return code
rc = EMRC_SUCCESS;
send:
STEP // 14: Release lock on switch state
pthread_mutex_unlock(&cxl_state->mtx);
STEP // 15: Fill Response Header
ma->rsp->len = emapi_fill_hdr(&rspm.hdr, EMMT_RSP, reqm.hdr.tag, rc, reqm.hdr.opcode, len, count, 0);
STEP // 16: Serialize Header
emapi_serialize(rspb->hdr, &rspm.hdr, EMOB_HDR, NULL);
STEP // 17: Push response mctp_msg onto queue
pq_push(m->tmq, ma);
rv = 0;
goto end;
fail:
ma->completion_code = 1;
pq_push(m->acq, ma);
end:
EXIT(rc)
return rv;
}
/**
* Handler for EM API List Devices 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
*/
static int emop_unsupported(struct mctp *m, struct mctp_action *ma)
{
INIT
char now[ISO_TIME_BUF_LEN];
struct emapi_msg reqm, rspm;
struct emapi_buf *reqb, *rspb;
unsigned rc;
int rv;
ENTER
STEP // 1: Initialize variables
rv = 1;
rc = EMRC_UNSUPPORTED;
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 fail;
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;
STEP // 4: Set buffer pointers
reqb = (struct emapi_buf*) ma->req->payload;
rspb = (struct emapi_buf*) ma->rsp->payload;
STEP // 5: Deserialize Request Header
if ( emapi_deserialize(&reqm.hdr, reqb->hdr, EMOB_HDR, NULL) <= 0 )
goto fail;
STEP // 6: Deserialize Request Object
STEP // 7: Extract parameters
IFV(CLVB_COMMANDS) printf("%s ERR: Unsupported Opcode: 0x%04x\n", now, reqm.hdr.opcode);
STEP // 8: Obtain lock on switch state
STEP // 9: Validate Inputs
STEP // 10: Perform Action
STEP // 11: Prepare Response Object
STEP // 12: Serialize Response Object
STEP // 13: Set return code
STEP // 14: Release lock on switch state
STEP // 15: Fill Response Header
ma->rsp->len = emapi_fill_hdr(&rspm.hdr, EMMT_RSP, reqm.hdr.tag, rc, reqm.hdr.opcode, 0, 0, 0);
STEP // 16: Serialize Header
emapi_serialize(rspb->hdr, &rspm.hdr, EMOB_HDR, NULL);
STEP // 17: Push response mctp_msg onto queue
pq_push(m->tmq, ma);
rv = 0;
goto end;
fail:
ma->completion_code = 1;
pq_push(m->acq, ma);
end:
EXIT(rc)
return rv;
}

35
emapi_handler.h Normal file
View File

@ -0,0 +1,35 @@
/* SPDX-License-Identifier: Apache-2.0 */
/**
* @file emapi_handler.h
*
* @brief Header file for methods to respond to CXL Emulator API commands
*
* @copyright Copyright (C) 2024 Jackrabbit Founders LLC. All rights reserved.
*
* @date Feb 2024
* @author Barrett Edwards <code@jrlabs.io>
*
*/
/* INCLUDES ==================================================================*/
#ifndef _EMAPI_HANDLER_H
#define _EMAPI_HANDLER_H
/* mctp_state
* mctp_msg
*/
#include <mctp.h>
/* MACROS ====================================================================*/
/* ENUMERATIONS ==============================================================*/
/* STRUCTS ===================================================================*/
/* PROTOTYPES ================================================================*/
int emapi_handler(struct mctp *m, struct mctp_action *ma);
/* GLOBAL VARIABLES ==========================================================*/
#endif //_EMAPI_HANDLER_H

168
fmapi_handler.c Normal file
View File

@ -0,0 +1,168 @@
/* SPDX-License-Identifier: Apache-2.0 */
/**
* @file fmapi_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 <code@jrlabs.io>
*
*/
/* INCLUDES ==================================================================*/
/* gettid()
*/
#define _GNU_SOURCE
#include <unistd.h>
/* printf()
*/
#include <stdio.h>
/* memset()
*/
#include <string.h>
/* struct timespec
* timespec_get()
*
*/
#include <time.h>
/* autl_prnt_buf()
*/
#include <arrayutils.h>
/* mctp_init()
* mctp_set_mh()
* mctp_run()
*/
#include <mctp.h>
#include <ptrqueue.h>
#include "signals.h"
#include "options.h"
#include "state.h"
#include <fmapi.h>
#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)
/* ENUMERATIONS ==============================================================*/
/* STRUCTS ===================================================================*/
/* PROTOTYPES ================================================================*/
int fmop_isc_bos (struct mctp *m, struct mctp_action *ma);
int fmop_isc_id (struct mctp *m, struct mctp_action *ma);
int fmop_isc_msg_limit_get (struct mctp *m, struct mctp_action *ma);
int fmop_isc_msg_limit_set (struct mctp *m, struct mctp_action *ma);
int fmop_mpc_cfg (struct mctp *m, struct mctp_action *ma);
int fmop_mpc_mem (struct mctp *m, struct mctp_action *ma);
int fmop_mpc_tmc (struct mctp *m, struct mctp_action *ma);
int fmop_psc_cfg (struct mctp *m, struct mctp_action *ma);
int fmop_psc_id (struct mctp *m, struct mctp_action *ma);
int fmop_psc_port (struct mctp *m, struct mctp_action *ma);
int fmop_psc_port_ctrl (struct mctp *m, struct mctp_action *ma);
int fmop_vsc_aer (struct mctp *m, struct mctp_action *ma);
int fmop_vsc_bind (struct mctp *m, struct mctp_action *ma);
int fmop_vsc_info (struct mctp *m, struct mctp_action *ma);
int fmop_vsc_unbind (struct mctp *m, struct mctp_action *ma);
/* GLOBAL VARIABLES ==========================================================*/
/* FUNCTIONS =================================================================*/
/**
* Handler for all FM API Opcodes
*
* @return 0 upon success, 1 otherwise
*
* STEPS
* 1: Deserialize Header
* 2: Verify FM API Message Category
* 3: Handle Opcode
*/
int fmapi_handler(struct mctp *m, struct mctp_action *ma)
{
INIT
struct fmapi_hdr hdr;
int rv;
ENTER
// Initialize variables
rv = 0;
STEP // 1: Deserialize FM API Header
rv = fmapi_deserialize(&hdr, ma->req->payload, FMOB_HDR, NULL);
if (rv <= 0)
goto end;
STEP // 2: Verify FM API Message Category
if (hdr.category != FMMT_REQ)
goto end;
STEP // 3: Handle Opcode
HEX32("Opcode", hdr.opcode);
switch(hdr.opcode)
{
case FMOP_ISC_BOS: rv = fmop_isc_bos(m, ma); break;
case FMOP_ISC_ID: rv = fmop_isc_id(m, ma); break;
case FMOP_ISC_MSG_LIMIT_GET: rv = fmop_isc_msg_limit_get(m, ma); break;
case FMOP_ISC_MSG_LIMIT_SET: rv = fmop_isc_msg_limit_set(m, ma); break;
case FMOP_PSC_ID: rv = fmop_psc_id(m, ma); break;
case FMOP_PSC_PORT: rv = fmop_psc_port(m, ma); break;
case FMOP_PSC_PORT_CTRL: rv = fmop_psc_port_ctrl(m, ma); break;
case FMOP_PSC_CFG: rv = fmop_psc_cfg(m, ma); break;
case FMOP_VSC_INFO: rv = fmop_vsc_info(m, ma); break;
case FMOP_VSC_BIND: rv = fmop_vsc_bind(m, ma); break;
case FMOP_VSC_UNBIND: rv = fmop_vsc_unbind(m, ma); break;
case FMOP_VSC_AER: rv = fmop_vsc_aer(m, ma); break;
case FMOP_MPC_TMC: rv = fmop_mpc_tmc(m, ma); break;
case FMOP_MPC_CFG: rv = fmop_mpc_cfg(m, ma); break;
case FMOP_MPC_MEM: rv = fmop_mpc_mem(m, ma); break;
default: break;
}
end:
// If subhandler fails, check in mctp_action
if (rv != 0)
{
ma->completion_code = 1;
pq_push(m->acq, ma);
}
EXIT(rv)
return rv;
}

35
fmapi_handler.h Normal file
View File

@ -0,0 +1,35 @@
/* SPDX-License-Identifier: Apache-2.0 */
/**
* @file fmapi_handler.h
*
* @brief Header 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 <code@jrlabs.io>
*
*/
/* INCLUDES ==================================================================*/
#ifndef _FMAPI_HANDLER_H
#define _FMAPI_HANDLER_H
/* mctp_state
* mctp_msg
*/
#include <mctp.h>
/* MACROS ====================================================================*/
/* ENUMERATIONS ==============================================================*/
/* STRUCTS ===================================================================*/
/* PROTOTYPES ================================================================*/
int fmapi_handler(struct mctp *m, struct mctp_action *ma);
/* GLOBAL VARIABLES ==========================================================*/
#endif //_FMAPI_HANDLER_H

541
fmapi_isc_handler.c Normal file
View File

@ -0,0 +1,541 @@
/* SPDX-License-Identifier: Apache-2.0 */
/**
* @file fmapi_isc_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 <code@jrlabs.io>
*
*/
/* INCLUDES ==================================================================*/
/* gettid()
*/
#define _GNU_SOURCE
#include <unistd.h>
/* printf()
*/
#include <stdio.h>
/* memset()
*/
#include <string.h>
/* struct timespec
* timespec_get()
*
*/
#include <time.h>
/* autl_prnt_buf()
*/
#include <arrayutils.h>
/* mctp_init()
* mctp_set_mh()
* mctp_run()
*/
#include <mctp.h>
#include <ptrqueue.h>
#include <timeutils.h>
#include "signals.h"
#include "options.h"
#include "state.h"
#include <fmapi.h>
#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 ISC Background Operation Status Opcode (0002h)
*
* @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_isc_bos(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 ISC Background Operation Status\n", now);
STEP // 8: Obtain lock on switch state
pthread_mutex_lock(&cxl_state->mtx);
STEP // 9: Validate Inputs
STEP // 10: Perform Action
STEP // 11: Prepare Response Object
rsp.obj.isc_bos.running = cxl_state->bos_running;
rsp.obj.isc_bos.pcnt = cxl_state->bos_pcnt;
rsp.obj.isc_bos.opcode = cxl_state->bos_opcode;
rsp.obj.isc_bos.rc = cxl_state->bos_rc;
rsp.obj.isc_bos.ext = cxl_state->bos_ext;
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(&cxl_state->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 ISC Identify Opcode (0001h)
*
* @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_isc_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;
STEP // 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 ISC Identify\n", now);
STEP // 8: Obtain lock on switch state
pthread_mutex_lock(&cxl_state->mtx);
STEP // 9: Validate Inputs
STEP // 10: Perform Action
STEP // 11: Prepare Response Object
rsp.obj.isc_id_rsp.vid = cxl_state->vid;
rsp.obj.isc_id_rsp.did = cxl_state->did;
rsp.obj.isc_id_rsp.svid = cxl_state->svid;
rsp.obj.isc_id_rsp.ssid = cxl_state->ssid;
rsp.obj.isc_id_rsp.sn = cxl_state->sn;
rsp.obj.isc_id_rsp.size = cxl_state->max_msg_size_n;
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(&cxl_state->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 ISC Get Response Message Limit Opcode (0003h)
*
* @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_isc_msg_limit_get(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 ISC Get Response Message Limit\n", now);
STEP // 8: Obtain lock on switch state
pthread_mutex_lock(&cxl_state->mtx);
STEP // 9: Validate Inputs
STEP // 10: Perform Action
STEP // 11: Prepare Response Object
rsp.obj.isc_msg_limit.limit = cxl_state->msg_rsp_limit_n;
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(&cxl_state->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 ISC Set Response Message Limit Opcode (0004h)
*
* @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_isc_msg_limit_set(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 ISC Set Response Message Limit\n", now);
STEP // 8: Obtain lock on switch state
pthread_mutex_lock(&cxl_state->mtx);
STEP // 9: Validate Inputs
if (req.obj.isc_msg_limit.limit < 8 || req.obj.isc_msg_limit.limit > 20)
{
IFV(CLVB_ERRORS) printf("%s ERR: Requested Message Response Limit outside allowed values. Requested: %d min: 8 max: 20\n", now, req.obj.isc_msg_limit.limit);
goto send;
}
STEP // 10: Perform Action
cxl_state->msg_rsp_limit_n = req.obj.isc_msg_limit.limit;
STEP // 11: Prepare Response Object
rsp.obj.isc_msg_limit.limit = cxl_state->msg_rsp_limit_n;
STEP // 12: Serialize Response Object
len = fmapi_serialize(rsp.buf->payload, &rsp.obj, fmapi_fmob_rsp(req.hdr.opcode));
if (len < 0)
goto end;
STEP // 13: Set return code
rc = FMRC_SUCCESS;
send:
STEP // 14: Release lock on switch state
pthread_mutex_unlock(&cxl_state->mtx);
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;
}

1095
fmapi_mcc_handler.c Normal file

File diff suppressed because it is too large Load Diff

662
fmapi_mpc_handler.c Normal file
View File

@ -0,0 +1,662 @@
/* SPDX-License-Identifier: Apache-2.0 */
/**
* @file fmapi_mpc_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 <code@jrlabs.io>
*
*/
/* INCLUDES ==================================================================*/
/* gettid()
*/
#define _GNU_SOURCE
#include <unistd.h>
/* printf()
*/
#include <stdio.h>
/* memset()
*/
#include <string.h>
/* struct timespec
* timespec_get()
*
*/
#include <time.h>
/* autl_prnt_buf()
*/
#include <arrayutils.h>
/* mctp_init()
* mctp_set_mh()
* mctp_run()
*/
#include <mctp.h>
#include <ptrqueue.h>
#include <timeutils.h>
#include <arrayutils.h>
#include "signals.h"
#include "options.h"
#include "state.h"
#include <fmapi.h>
#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 ================================================================*/
int fmop_mcc_get_ld_alloc (struct port *p, struct fmapi_msg *req, struct fmapi_msg *rsp);
int fmop_mcc_get_qos_alloc (struct port *p, struct fmapi_msg *req, struct fmapi_msg *rsp);
int fmop_mcc_get_qos_ctrl (struct port *p, struct fmapi_msg *req, struct fmapi_msg *rsp);
int fmop_mcc_get_qos_limit (struct port *p, struct fmapi_msg *req, struct fmapi_msg *rsp);
int fmop_mcc_get_qos_stat (struct port *p, struct fmapi_msg *req, struct fmapi_msg *rsp);
int fmop_mcc_info (struct port *p, struct fmapi_msg *req, struct fmapi_msg *rsp);
int fmop_mcc_set_ld_alloc (struct port *p, struct fmapi_msg *req, struct fmapi_msg *rsp);
int fmop_mcc_set_qos_alloc (struct port *p, struct fmapi_msg *req, struct fmapi_msg *rsp);
int fmop_mcc_set_qos_ctrl (struct port *p, struct fmapi_msg *req, struct fmapi_msg *rsp);
int fmop_mcc_set_qos_limit (struct port *p, struct fmapi_msg *req, struct fmapi_msg *rsp);
/* GLOBAL VARIABLES ==========================================================*/
/* FUNCTIONS =================================================================*/
/**
* Handler for FM API MPC LD 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_mpc_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 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 MPC LD CXL.io Config. PPID: %d LDID: %d\n", now, req.obj.mpc_cfg_req.ppid, req.obj.mpc_cfg_req.ldid);
STEP // 8: Obtain lock on switch state
pthread_mutex_lock(&cxl_state->mtx);
STEP // 9: Validate Inputs
// Validate port number
if (req.obj.mpc_cfg_req.ppid >= cxl_state->num_ports)
{
IFV(CLVB_ERRORS) printf("%s ERR: Invalid Port number requested. PPID: %d\n", now, req.obj.mpc_cfg_req.ppid);
goto send;
}
p = &cxl_state->ports[req.obj.mpc_cfg_req.ppid];
// Validate port is not bound
//if ( !(p->state == FMPS_DISABLED) )
//{
// IFV(CLVB_ERRORS) printf("%s ERR: Port is in a bound state. PPID: %d State: %s\n", now, req.obj.mpc_cfg_req.ppid, fmps(p->state));
// goto send;
//}
// Validate device attached to port is an MLD port
if ( !(p->dt == FMDT_CXL_TYPE_3 || p->dt == FMDT_CXL_TYPE_3_POOLED) )
{
IFV(CLVB_ERRORS) printf("%s ERR: Port is not Type 3 device: Type: %s\n", now, fmdt(p->dt));
goto send;
}
// Validate LDID
if (req.obj.mpc_cfg_req.ldid >= p->ld)
{
IFV(CLVB_ERRORS) printf("%s ERR: Requested LD ID exceeds supported LD count of specified port. Requested LDID: %d\n", now, req.obj.mpc_cfg_req.ldid);
goto send;
}
STEP // 10: Perform Action
STEP // 11: Prepare Response Object
switch (req.obj.mpc_cfg_req.type)
{
case FMCT_READ: // 0x00
{
IFV(CLVB_ACTIONS) printf("%s ACT: Performing CXL.io Read on PPID: %d LDID: %d\n", now, req.obj.mpc_cfg_req.ppid, req.obj.mpc_cfg_req.ldid);
reg = (req.obj.mpc_cfg_req.ext << 8) | req.obj.mpc_cfg_req.reg;
rsp.obj.mpc_cfg_rsp.data[0] = 0;
rsp.obj.mpc_cfg_rsp.data[1] = 0;
rsp.obj.mpc_cfg_rsp.data[2] = 0;
rsp.obj.mpc_cfg_rsp.data[3] = 0;
if (req.obj.mpc_cfg_req.fdbe & 0x01) rsp.obj.mpc_cfg_rsp.data[0] = p->mld->cfgspace[req.obj.mpc_cfg_req.ldid][reg+0];
if (req.obj.mpc_cfg_req.fdbe & 0x02) rsp.obj.mpc_cfg_rsp.data[1] = p->mld->cfgspace[req.obj.mpc_cfg_req.ldid][reg+1];
if (req.obj.mpc_cfg_req.fdbe & 0x04) rsp.obj.mpc_cfg_rsp.data[2] = p->mld->cfgspace[req.obj.mpc_cfg_req.ldid][reg+2];
if (req.obj.mpc_cfg_req.fdbe & 0x08) rsp.obj.mpc_cfg_rsp.data[3] = p->mld->cfgspace[req.obj.mpc_cfg_req.ldid][reg+3];
}
break;
case FMCT_WRITE: // 0x01
{
HEX32("Write Data", *((int*)req.obj.mpc_cfg_req.data));
IFV(CLVB_ACTIONS) printf("%s ACT: Performing CXL.io Write on PPID: %d LDID: %d\n", now, req.obj.mpc_cfg_req.ppid, req.obj.mpc_cfg_req.ldid);
reg = (req.obj.mpc_cfg_req.ext << 8) | req.obj.mpc_cfg_req.reg;
if (req.obj.mpc_cfg_req.fdbe & 0x01) p->mld->cfgspace[req.obj.mpc_cfg_req.ldid][reg+0] = req.obj.mpc_cfg_req.data[0];
if (req.obj.mpc_cfg_req.fdbe & 0x02) p->mld->cfgspace[req.obj.mpc_cfg_req.ldid][reg+1] = req.obj.mpc_cfg_req.data[1];
if (req.obj.mpc_cfg_req.fdbe & 0x04) p->mld->cfgspace[req.obj.mpc_cfg_req.ldid][reg+2] = req.obj.mpc_cfg_req.data[2];
if (req.obj.mpc_cfg_req.fdbe & 0x08) p->mld->cfgspace[req.obj.mpc_cfg_req.ldid][reg+3] = req.obj.mpc_cfg_req.data[3];
}
break;
default:
IFV(CLVB_ERRORS) printf("%s ERR: Invalid Action\n", now);
goto end;
}
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(&cxl_state->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 MPC LD CXL.io Memory 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_mpc_mem(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 port *p;
__u64 base, max, ld_size, granularity;
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 MPC LD CXL.io Mem. PPID: %d LDID: %d\n", now, req.obj.mpc_mem_req.ppid, req.obj.mpc_mem_req.ldid);
STEP // 8: Obtain lock on switch state
pthread_mutex_lock(&cxl_state->mtx);
STEP // 9: Validate Inputs
// Validate port number
if (req.obj.mpc_mem_req.ppid >= cxl_state->num_ports)
{
IFV(CLVB_ERRORS) printf("%s ERR: Invalid Port number requested. PPID: %d\n", now, req.obj.mpc_mem_req.ppid);
goto send;
}
p = &cxl_state->ports[req.obj.mpc_mem_req.ppid];
// Validate port is not bound
//if ( !(p->state == FMPS_DISABLED) )
//{
// IFV(CLVB_ERRORS) printf("%s ERR: Port is in a bound state: %s PPID: %d\n", now, fmps(p->state), req.obj.mpc_mem_req.ppid);
// goto send;
//}
// Validate device attached to port is an MLD port
if ( !(p->dt == FMDT_CXL_TYPE_3 || p->dt == FMDT_CXL_TYPE_3_POOLED) )
{
IFV(CLVB_ERRORS) printf("%s ERR: Port is not Type 3 device. Requested Type: %s\n", now, fmdt(p->dt));
goto send;
}
// Validate LDID
if (req.obj.mpc_mem_req.ldid >= p->ld)
{
IFV(CLVB_ERRORS) printf("%s ERR: Requested LD ID exceeds supported LD count of specified port. LDID: %d\n", now, req.obj.mpc_mem_req.ldid);
goto send;
}
// Validate memory backed file is mmaped
if (p->mld == NULL || p->mld->memspace == NULL)
{
IFV(CLVB_ERRORS) printf("%s ERR: Requested port does not have memory space on the specified port. Port: %d\n", now, p->ppid);
rc = FMRC_UNSUPPORTED;
goto send;
}
// Validate offset & length
if (req.obj.mpc_mem_req.len > 4096)
{
IFV(CLVB_ERRORS) printf("%s ERR: Requested length exceeds maximum length supported (4096B). Requested Len: %d\n", now, req.obj.mpc_mem_req.len);
goto send;
}
// Get granularity in bytes
granularity = 1024*1024;
switch (p->mld->granularity)
{
case FMMG_256MB: granularity *= 256; break;
case FMMG_512MB: granularity *= 512; break;
case FMMG_1GB: granularity *= 1024; break;
}
// compute size of requested LD
base = granularity * p->mld->rng1[req.obj.mpc_mem_req.ldid]; // base is the byte offset into the memspace
max = granularity * (p->mld->rng2[req.obj.mpc_mem_req.ldid] + 1); // max is the byte offset start of the next LD in the memspace
ld_size = max - base; // ld size in bytes
// Verify requested offset + len does not exceed the end of the LD
if ( (req.obj.mpc_mem_req.offset + req.obj.mpc_mem_req.len) >= ld_size)
{
IFV(CLVB_ERRORS) printf("%s ERR: Requested offset + length exceeds maximum size of LD. LD Max size (Bytes): %llu. Requested up to Byte: %llu\n", now, ld_size, req.obj.mpc_mem_req.offset + req.obj.mpc_mem_req.len);
goto send;
}
STEP // 10: Perform Action
STEP // 11: Prepare Response Object
switch (req.obj.mpc_mem_req.type)
{
case FMCT_READ: // 0x00
INT32("Request Len", req.obj.mpc_mem_req.len);
IFV(CLVB_ACTIONS) printf("%s ACT: Performing CXL.io MEM Read on PPID: %d LDID: %d\n", now, req.obj.mpc_mem_req.ppid, req.obj.mpc_mem_req.ldid);
rsp.obj.mpc_mem_rsp.len = req.obj.mpc_mem_req.len;
memcpy(rsp.obj.mpc_mem_rsp.data, &p->mld->memspace[base + req.obj.mpc_mem_req.offset], req.obj.mpc_mem_req.len);
break;
case FMCT_WRITE: // 0x01
IFV(CLVB_ACTIONS) printf("%s ACT: Performing CXL.io MEM Write on PPID: %d LDID: %d\n", now, req.obj.mpc_mem_req.ppid, req.obj.mpc_mem_req.ldid);
memcpy(&p->mld->memspace[base + req.obj.mpc_mem_req.offset], req.obj.mpc_mem_req.data, req.obj.mpc_mem_req.len);
autl_prnt_buf(req.obj.mpc_mem_req.data, req.obj.mpc_mem_req.len, 4, 0);
break;
}
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(&cxl_state->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 MPC Tunnel Management Command Opcode
*
* @param hdr fmapi_hdr*
* @param src __u8* to Request FM API Message Payload in serialized form
* @param dst __u8* to Respnse FM API Message Payload in serialized form
* @return 1 to send reponse back to requestor, 0 to not send it
*
* STEPS
* 1: Deserialize FM API Request Payload
* 2: Validate port number
* 3: Validate device attached to port is an MLD port
* 4: Confirm MCTP Message Type
* 5: Extract FM API HDR and switch on mesage opcode
* 6: Verify FM API Message is a request
* 7: Deserialize payload into buffer
* 8: Perform Requested Action
* 9: Serialize FM API Payload
* 10: Fill Response FM API HDR
* 11: Serialize FM API Header
* 12: Set MCTP Type
* 13: Serialize FM API Response Payload
* 14: Set return code
*/
int fmop_mpc_tmc(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 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 MPC Tunneled Management Command. PPID: %d\n", now, req.obj.mpc_tmc_req.ppid);
STEP // 8: Obtain lock on switch state
pthread_mutex_lock(&cxl_state->mtx);
STEP // 9: Validate Inputs
// Validate MCTP Message Type
if (req.obj.mpc_tmc_req.type != MCMT_CXLCCI)
{
IFV(CLVB_ERRORS) printf("%s ERR: Tunneled command did not have a CXL CCI MCTP Type code. Tunneled MCTP Type code: %d\n", now, req.obj.mpc_tmc_req.type);
goto send;
}
// Validate port number
if (req.obj.mpc_tmc_req.ppid >= cxl_state->num_ports)
{
IFV(CLVB_ERRORS) printf("%s Invalid Port number requested. PPID: %d\n", now, req.obj.mpc_tmc_req.ppid);
goto send;
}
p = &cxl_state->ports[req.obj.mpc_tmc_req.ppid];
// Validate device attached to port is an MLD port
if ( !(p->dt == FMDT_CXL_TYPE_3 || p->dt == FMDT_CXL_TYPE_3_POOLED) )
{
IFV(CLVB_ERRORS) printf("%s Port is not Type 3 device. Type: %s\n", now, fmdt(p->dt));
goto send;
}
STEP // 10: Perform Action
STEP // 11: Prepare Response Object
{
struct fmapi_msg src, dst;
// Configure Buffer pointers
src.buf = (struct fmapi_buf*) req.obj.mpc_tmc_req.msg;
dst.buf = (struct fmapi_buf*) rsp.obj.mpc_tmc_rsp.msg;
// Deserialize Sub Header
fmapi_deserialize(&src.hdr, src.buf->hdr, FMOB_HDR, 0);
// Verify sub message is a request
if (src.hdr.category != FMMT_REQ)
{
IFV(CLVB_ERRORS) printf("%s ERR: Tunneled FM API Message Category is not a request. Tunneled FM API Message Category: %d\n", now, src.hdr.category);
// Fill Sub Header
len = fmapi_fill_hdr(&rsp.hdr, FMMT_RESP, src.hdr.tag, src.hdr.opcode, 0, 0, FMRC_INVALID_INPUT, 0);
// Serialize Sub Header
fmapi_serialize(dst.buf->hdr, &dst.hdr, FMOB_HDR);
goto sub;
}
// Handle Opcode
switch (src.hdr.opcode)
{
case FMOP_MCC_INFO: len = fmop_mcc_info (p, &src, &dst); break; // 0x5400
case FMOP_MCC_ALLOC_GET: len = fmop_mcc_get_ld_alloc (p, &src, &dst); break; // 0x5401
case FMOP_MCC_ALLOC_SET: len = fmop_mcc_set_ld_alloc (p, &src, &dst); break; // 0x5402
case FMOP_MCC_QOS_CTRL_GET: len = fmop_mcc_get_qos_ctrl (p, &src, &dst); break; // 0x5403
case FMOP_MCC_QOS_CTRL_SET: len = fmop_mcc_set_qos_ctrl (p, &src, &dst); break; // 0x5404
case FMOP_MCC_QOS_STAT: len = fmop_mcc_get_qos_stat (p, &src, &dst); break; // 0x5405
case FMOP_MCC_QOS_BW_ALLOC_GET: len = fmop_mcc_get_qos_alloc(p, &src, &dst); break; // 0x5406
case FMOP_MCC_QOS_BW_ALLOC_SET: len = fmop_mcc_set_qos_alloc(p, &src, &dst); break; // 0x5407
case FMOP_MCC_QOS_BW_LIMIT_GET: len = fmop_mcc_get_qos_limit(p, &src, &dst); break; // 0x5408
case FMOP_MCC_QOS_BW_LIMIT_SET: len = fmop_mcc_set_qos_limit(p, &src, &dst); break; // 0x5409
default:
IFV(CLVB_ERRORS) printf("%s ERR: Tunneled FM API Mesage has an invalid opcode. Tunneled FM API Message Opcode %d\n", now, src.hdr.opcode);
// Fill Sub Header
len = fmapi_fill_hdr(&rsp.hdr, FMMT_RESP, src.hdr.tag, src.hdr.opcode, 0, 0, FMRC_UNSUPPORTED, 0);
// Serialize Sub Header
fmapi_serialize(dst.buf->hdr, &dst.hdr, FMOB_HDR);
break;
}
sub:
// Fill Response Object
rsp.obj.mpc_tmc_rsp.len = len;
rsp.obj.mpc_tmc_rsp.type = req.obj.mpc_tmc_req.type;
}
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(&cxl_state->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;
}

624
fmapi_psc_handler.c Normal file
View File

@ -0,0 +1,624 @@
/* 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 <code@jrlabs.io>
*
*/
/* INCLUDES ==================================================================*/
/* gettid()
*/
#define _GNU_SOURCE
#include <unistd.h>
/* printf()
*/
#include <stdio.h>
/* memset()
*/
#include <string.h>
/* struct timespec
* timespec_get()
*
*/
#include <time.h>
/* autl_prnt_buf()
*/
#include <arrayutils.h>
/* mctp_init()
* mctp_set_mh()
* mctp_run()
*/
#include <mctp.h>
#include <ptrqueue.h>
#include <timeutils.h>
#include "signals.h"
#include "options.h"
#include "state.h"
#include <fmapi.h>
#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 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(&cxl_state->mtx);
STEP // 9: Validate Inputs
if (req.obj.psc_cfg_req.ppid >= cxl_state->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, cxl_state->num_ports);
goto send;
}
p = &cxl_state->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(&cxl_state->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(&cxl_state->mtx);
STEP // 9: Validate Inputs
STEP // 10: Perform Action
STEP // 11: Prepare Response Object
state_conv_identity(cxl_state, &rsp.obj.psc_id_rsp);
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(&cxl_state->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(&cxl_state->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 >= cxl_state->num_ports)
continue;
// Copy the data
state_conv_port_info(&cxl_state->ports[id], &rsp.obj.psc_port_rsp.list[i]);
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(&cxl_state->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 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(&cxl_state->mtx);
STEP // 9: Validate Inputs
if (req.obj.psc_port_ctrl_req.ppid >= cxl_state->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, cxl_state->num_ports);
goto send;
}
p = &cxl_state->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(&cxl_state->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;
}

739
fmapi_vsc_handler.c Normal file
View File

@ -0,0 +1,739 @@
/* SPDX-License-Identifier: Apache-2.0 */
/**
* @file fmapi_vsc_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 <code@jrlabs.io>
*
*/
/* INCLUDES ==================================================================*/
/* gettid()
*/
#define _GNU_SOURCE
#include <unistd.h>
/* printf()
*/
#include <stdio.h>
/* memset()
*/
#include <string.h>
/* struct timespec
* timespec_get()
*
*/
#include <time.h>
/* autl_prnt_buf()
*/
#include <arrayutils.h>
/* mctp_init()
* mctp_set_mh()
* mctp_run()
*/
#include <mctp.h>
#include <ptrqueue.h>
#include <timeutils.h>
#include "signals.h"
#include "options.h"
#include "state.h"
#include <fmapi.h>
#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 VSC Generate AER 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_vsc_aer(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 vcs *v;
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 VSC Generate AER Event. VCSID: %d vPPBID: %d\n", now, req.obj.vsc_aer_req.vcsid, req.obj.vsc_aer_req.vppbid);
STEP // 8: Obtain lock on switch state
pthread_mutex_lock(&cxl_state->mtx);
STEP // 9: Validate Inputs
if (req.obj.vsc_aer_req.vcsid >= cxl_state->num_vcss)
{
IFV(CLVB_ERRORS) printf("%s ERR: Requested VCSID exceeds number of VCSs present. Requested VCSID: %d Present: %d\n", now, req.obj.vsc_aer_req.vcsid, cxl_state->num_vcss);
goto send;
}
v = &cxl_state->vcss[req.obj.vsc_aer_req.vcsid];
// Validate vppbid
if (req.obj.vsc_aer_req.vppbid >= v->num)
{
IFV(CLVB_ERRORS) printf("%s ERR: Requested vPPBID exceeds number of vPPBs present in requested VCS. Requested vPPBID: %d Present: %d\n", now, req.obj.vsc_aer_req.vppbid, v->num);
goto send;
}
STEP // 10: Perform Action
IFV(CLVB_ACTIONS) printf("%s ACT: Generating AER on VSCID: %d vPPBID: %d Error: 0x%08x\n", now, req.obj.vsc_aer_req.vcsid, req.obj.vsc_aer_req.vppbid, req.obj.vsc_aer_req.error_type);
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(&cxl_state->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 VSC Bind 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_vsc_bind(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 vcs *v;
struct vppb *b;
struct 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 VSC Bind vPPB. VCSID: %d vPPBID: %d PPID: %d LDID: 0x%04x\n", now, req.obj.vsc_bind_req.vcsid, req.obj.vsc_bind_req.vppbid, req.obj.vsc_bind_req.ppid, req.obj.vsc_bind_req.ldid);
STEP // 8: Obtain lock on switch state
pthread_mutex_lock(&cxl_state->mtx);
STEP // 9: Validate Inputs
// Validate vcsid
if (req.obj.vsc_bind_req.vcsid >= cxl_state->num_vcss)
{
IFV(CLVB_ERRORS) printf("%s ERR: VCS ID out of range. VCSID: %d\n", now, req.obj.vsc_bind_req.vcsid);
goto send;
}
v = &cxl_state->vcss[req.obj.vsc_bind_req.vcsid];
// Validate vppbid
if (req.obj.vsc_bind_req.vppbid >= cxl_state->vcss[req.obj.vsc_bind_req.vcsid].num)
{
IFV(CLVB_ERRORS) printf("%s ERR: vPPB ID out of range. vPPBID: %d\n", now, req.obj.vsc_bind_req.vppbid);
goto send;
}
b = &v->vppbs[req.obj.vsc_bind_req.vppbid];
// Validate port id
if (req.obj.vsc_bind_req.ppid >= cxl_state->num_ports)
{
IFV(CLVB_ERRORS) printf("%s ERR: PPID ID out of range. PPID: %d\n", now, req.obj.vsc_bind_req.ppid);
goto send;
}
p = &cxl_state->ports[req.obj.vsc_bind_req.ppid];
// Check bindability to this port
// Check state of port
if (p->state == FMPS_DISABLED)
{
IFV(CLVB_ERRORS) printf("%s ERR: Port is in a disabled state. PPID: %d State: %s\n", now, req.obj.vsc_bind_req.ppid, fmps(p->state));
goto send;
}
// If an LD is specified, check if the port is connected to a Type-3 Devices
if (req.obj.vsc_bind_req.ldid != 0xFFFF && !(p->dt == FMDT_CXL_TYPE_3 || p->dt == FMDT_CXL_TYPE_3_POOLED) )
{
IFV(CLVB_ERRORS) printf("%s ERR: Bind to an MLD LD requested and specified port is not attached to a Type 3 Device\n", now);
goto send;
}
// If port is an MLD port, an LDID must be specified
if (p->ld > 0 && req.obj.vsc_bind_req.ldid == 0xFFFF)
{
IFV(CLVB_ERRORS) printf("%s ERR: Cannot bind to the physical port of an MLD device\n", now);
goto send;
}
// If an LD is specified, check if the port can support multiple LDs
if (req.obj.vsc_bind_req.ldid != 0xFFFF && p->ld == 0)
{
IFV(CLVB_ERRORS) printf("%s ERR: Specified port does not support multiple Logical Devices: \n", now);
goto send;
}
// Check if vPPB is aleady bound
if (b->bind_status != FMBS_UNBOUND)
{
IFV(CLVB_ERRORS) printf("%s ERR: Specified vPPB is not available to be bound. vPPBID: %d STATUS: %s\n", now, req.obj.vsc_bind_req.vppbid, fmbs(b->bind_status));
goto send;
}
STEP // 10: Perform Action
IFV(CLVB_ACTIONS) printf("%s ACT: Binding VCSID: %d vPPBID: %d PPID: %d LDID: 0x%04x\n", now, req.obj.vsc_bind_req.vcsid, req.obj.vsc_bind_req.vppbid, req.obj.vsc_bind_req.ppid, req.obj.vsc_bind_req.ldid);
if (req.obj.vsc_bind_req.ldid != 0xFFFF)
{
b->bind_status = FMBS_BOUND_LD;
b->ppid = req.obj.vsc_bind_req.ppid;
b->ldid = req.obj.vsc_bind_req.ldid;
}
else
{
b->bind_status = FMBS_BOUND_PORT;
b->ppid = req.obj.vsc_bind_req.ppid;
b->ldid = 0;
}
STEP // 6: Set port state to be a downstream port
p->state = FMPS_DSP;
// Update Background Operation Status
cxl_state->bos_running = 0;
cxl_state->bos_pcnt = 100;
cxl_state->bos_opcode = req.hdr.opcode;
cxl_state->bos_rc = FMRC_SUCCESS;
cxl_state->bos_ext = 0;
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_BACKGROUND_OP_STARTED;
send:
STEP // 14: Release lock on switch state
pthread_mutex_unlock(&cxl_state->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 VSC Get Virtual CXL Switch Info 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_vsc_info(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 vcs *v;
unsigned i, k, stop, vppbid_start, vppbid_limit;
struct fmapi_vsc_info_blk *blk;
__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 VSC Get Virtual Switch Info. Num: %d\n", now, req.obj.vsc_info_req.num);
STEP // 8: Obtain lock on switch state
pthread_mutex_lock(&cxl_state->mtx);
STEP // 9: Validate Inputs
STEP // 10: Perform Action
STEP // 11: Prepare Response Object
rsp.obj.vsc_info_rsp.num = 0;
vppbid_start = req.obj.vsc_info_req.vppbid_start;
vppbid_limit = req.obj.vsc_info_req.vppbid_limit;
for ( i = 0 ; i < req.obj.vsc_info_req.num ; i++ )
{
id = req.obj.vsc_info_req.vcss[i];
// Break, if we have reached the maximum number of VCS entities that can be returned
if (i >= FM_MAX_VCS_PER_RSP)
break;
// Skip VCS IDs that exceed current size
if (id >= cxl_state->num_vcss)
continue;
// Get pointers to objects to copy
v = &cxl_state->vcss[id]; // The struct vcs to copy from
blk = &rsp.obj.vsc_info_rsp.list[i]; // The struct fmapi_vcs_info_blk to copy into
// Zero out destination
memset(blk, 0, sizeof(*blk));
// Copy information
blk->vcsid = v->vcsid; // Virtual CXL Switch ID
blk->state = v->state; // VCS State [FMVS]
blk->uspid = v->uspid; // USP ID. Upstream physical port ID
blk->total = v->num; // Total Number of vPPBs in the VCS.
blk->num = 0; // The number vppb blks returned in this object
// Determine number of vPPB entires to return
stop = v->num;
if ( vppbid_limit < (stop - vppbid_start) )
stop = vppbid_start + vppbid_limit;
// Variable array of PPB Status Blocks
for ( k = vppbid_start ; k < stop ; k++ ) {
blk->list[k].status = v->vppbs[k].bind_status;
blk->list[k].ppid = v->vppbs[k].ppid;
blk->list[k].ldid = v->vppbs[k].ldid;
blk->num++;
}
rsp.obj.vsc_info_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(&cxl_state->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 VSC Unbind 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_vsc_unbind(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 vcs *v;
struct vppb *b;
struct 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 VSC Unbind vPPB. VCSID: %d vPPBID: %d\n", now, req.obj.vsc_unbind_req.vcsid, req.obj.vsc_unbind_req.vppbid);
STEP // 8: Obtain lock on switch state
pthread_mutex_lock(&cxl_state->mtx);
STEP // 9: Validate Inputs
// Validate vcsid
if (req.obj.vsc_unbind_req.vcsid >= cxl_state->num_vcss)
{
IFV(CLVB_ERRORS) printf("%s ERR: VCS ID out of range. VCSID: %d\n", now, req.obj.vsc_unbind_req.vcsid);
goto send;
}
v = &cxl_state->vcss[req.obj.vsc_unbind_req.vcsid];
// Validate vppbid
if (req.obj.vsc_unbind_req.vppbid >= cxl_state->vcss[req.obj.vsc_unbind_req.vcsid].num)
{
IFV(CLVB_ERRORS) printf("%s ERR: vPPB ID out of range. vPPBID: %d\n", now, req.obj.vsc_unbind_req.vppbid);
goto send;
}
b = &v->vppbs[req.obj.vsc_unbind_req.vppbid];
// Validate bind status of vppb
if (b->bind_status == FMBS_UNBOUND || b->bind_status == FMBS_INPROGRESS)
{
IFV(CLVB_ERRORS) printf("%s ERR: vPPB was not bound. vPPBID %d\n", now, req.obj.vsc_unbind_req.vppbid);
goto send;
}
// Validate port id that the vppb was bound to
if (b->ppid >= cxl_state->num_ports)
{
IFV(CLVB_ERRORS) printf("%s ERR: PPID of bound port out of range. PPID: %d\n", now, b->ppid);
b->bind_status = FMBS_UNBOUND;
goto send;
}
p = &cxl_state->ports[b->ppid];
// Check bindability to this port
// Check state of port
if ( !(p->state == FMPS_BINDING || p->state == FMPS_UNBINDING || p->state == FMPS_USP || p->state == FMPS_DSP) )
{
IFV(CLVB_ERRORS) printf("%s ERR: Port is not in a bound state. PPID: %d State: %s\n", now, b->ppid, fmps(p->state));
goto send;
}
STEP // 10: Perform Action
IFV(CLVB_ACTIONS) printf("%s ACT: Unbinding VCSID: %d vPPBID: %d\n", now, req.obj.vsc_unbind_req.vcsid, req.obj.vsc_unbind_req.vppbid);
b->bind_status = FMBS_UNBOUND;
b->ppid = 0;
b->ldid = 0;
// Update Background Operation Status
cxl_state->bos_running = 0;
cxl_state->bos_pcnt = 100;
cxl_state->bos_opcode = req.hdr.opcode;
cxl_state->bos_rc = FMRC_SUCCESS;
cxl_state->bos_ext = 0;
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_BACKGROUND_OP_STARTED;
send:
STEP // 14: Release lock on switch state
pthread_mutex_unlock(&cxl_state->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;
}

207
main.c Normal file
View File

@ -0,0 +1,207 @@
/* SPDX-License-Identifier: Apache-2.0 */
/**
* @file cse.c
*
* @brief Code file for entry point CXL Switch Emulator
*
* @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>
/* printf()
*/
#include <stdio.h>
/* memset()
*/
#include <string.h>
/* autl_prnt_buf()
*/
#include <arrayutils.h>
/* mctp_init()
* mctp_set_mh()
* mctp_run()
*/
#include <mctp.h>
#include "signals.h"
#include "options.h"
#include "state.h"
#include "fmapi_handler.h"
#include "emapi_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)
/* ENUMERATIONS ==============================================================*/
/* STRUCTS ===================================================================*/
/* PROTOTYPES ================================================================*/
/* GLOBAL VARIABLES ==========================================================*/
/* FUNCTIONS =================================================================*/
/**
* cse main
*
* STEPS
* 0: Parse CLI options
* 1: Register Signal Handlers
* 2: Initialize global state array
* 3: Load state file
* 4: Print the state
* 5: MCTP Init
* 6: Run MCTP
* 7: While loop
* 8: Stop MCTP
* 9: Free memory
*/
int main(int argc, char* argv[])
{
INIT
int rv;
struct mctp *m;
// Initialize varaibles
stop_requested = 0;
rv = 1;
// 0: Parse CLI options
rv = options_parse(argc,argv);
if (rv != 0)
{
printf("Error: Parse options failed: %d\n", rv);
goto end;
}
STEP // 1: Register Signal Handlers
signals_register();
STEP // 2: Initialize global state array
cxl_state = state_init(32, 32, 256);
if (cxl_state == NULL)
{
printf("Error: state init failed \n");
goto end_options;
}
STEP // 3: Load state file
if (opts[CLOP_CONFIG_FILE].set)
{
rv = state_load(cxl_state, opts[CLOP_CONFIG_FILE].str);
if (rv < 0)
{
printf("Error: state load config file failed \n");
goto end_state;
}
}
STEP // 4: Print the state
if (opts[CLOP_PRINT_STATE].set)
state_print(cxl_state);
STEP // 5: MCTP Init
m = mctp_init();
if (m == NULL)
goto end_state;
// Set supported MCTP Message Versions
mctp_set_version(m, MCMT_CXLFMAPI, 0xF2,0xF1,0xFF,0x00);
mctp_set_version(m, MCMT_CXLCCI, 0xF2,0xF1,0xFF,0x00);
// Set Message handler functions
mctp_set_handler(m, MCMT_CXLFMAPI, fmapi_handler);
mctp_set_handler(m, MCMT_CSE, emapi_handler);
// Set MCTP verbosity levels
mctp_set_verbosity(m, opts[CLOP_MCTP_VERBOSITY].u64);
STEP // 6: Run MCTP
rv = mctp_run(m, opts[CLOP_TCP_PORT].u16, opts[CLOP_TCP_ADDRESS].u32, MCRM_SERVER, 1, 1);
if (rv != 0)
{
switch (rv)
{
case -1:
printf("Socket create failed\n");
break;
case -2:
printf("Socket bind failed\n");
break;
case -3:
printf("Socket connect failed");
break;
case 1:
printf("Could not create Connection Handler Thread\n");
break;
case 2:
printf("MCTP threads failed to start\n");
break;
}
goto end_mctp;
}
STEP // 7: While loop
while ( stop_requested == 0 )
{
sleep(1);
}
STEP // 8: Stop MCTP
mctp_stop(m);
end_mctp:
mctp_free(m);
rv = 0;
end_state:
state_free(cxl_state);
end_options:
options_free(opts);
end:
EXIT(rv)
return rv;
};

717
options.c Normal file
View File

@ -0,0 +1,717 @@
/* SPDX-License-Identifier: Apache-2.0 */
/**
* @file options.c
*
* @brief Code file for cli options parsing using argp/argz library
*
* @copyright Copyright (C) 2024 Jackrabbit Founders LLC. All rights reserved.
*
* @date Jan 2024
* @author Barrett Edwards <code@jrlabs.io>
*
*/
/* INCLUDES ==================================================================*/
/* memset()
*/
#include <string.h>
/* free()
*/
#include <stdlib.h>
/* inet_pton()
*/
#include <arpa/inet.h>
/*
*/
#include <linux/types.h>
/* struct argp
* struct argp_state
* argp_parse()
*/
#include <argp.h>
/* autl_prnt_buf()
*/
#include <arrayutils.h>
#include "options.h"
#include <unistd.h>
/* MACROS ====================================================================*/
/* ENUMERATIONS ==============================================================*/
/* STRUCTS ===================================================================*/
/* PROTOTYPES ================================================================*/
static int pr_main(int key, char *arg, struct argp_state *state);
static void print_help();
static void print_options(struct argp_option *o);
static void print_options_array(struct opt *o);
static void print_usage(struct argp_option *o);
/* GLOBAL VARIABLES ==========================================================*/
/**
* Global char pointer to dynamically store the name of the application
*
* This is allocated and stored when parse_options() is called
*/
static char *app_name;
/**
* String representation of CLOP Enumeration
*/
char *STR_CLOP[] = {
"VERBOSITY",
"MCTP_VERBOSITY",
"PRINT_STATE",
"PRINT_OPTIONS",
"CONFIG_FILE",
"TCP_PORT"
};
/**
* String representation of CLVO Enumeration
*/
char *STR_CLVO[] = {
"General verbose output", // CVSO_GENERAL = 0
"Call Stack", // CVSO_CALLSTACK = 1
"STEPS", // CVSO_STEPS = 2
"Parsing", // CVSO_PARSE = 3
"Actions", // CVSO_ACTIONS = 4
"Commands", // CVSO_COMMANDS = 5
"Errors" // CVSO_ERRORS = 6
};
/**
* Options array that stores all parsed optioons
*/
struct opt *opts = NULL;
/**
* Global string used by argp to print version with --version
*/
const char *argp_program_version = "version 0.1";
/**
* Global string used by argp when printing the help message
*/
const char *argp_program_bug_address = "code@jrlabs.io";
/**
* Aray of argp_option structs to define the CLI options
*
* Non character options
* 701 - verbosity-options
* 702 - verbosity-hex
* 703 - verbosity-mctp
*/
struct argp_option ao_main[] =
{
{0,0,0,0, "File Options",1},
{"config", 'c', "FILE", 0, "File name of CXL switch config file", 0},
{0,0,0,0, "Networking Options",2},
{"tcp-port", 'P', "INT", 0, "Server TCP Port", 0},
{"tcp-address", 'T', "INT", 0, "Server TCP Address", 0}
,
{0,0,0,0, "Verbosity Options",8},
{"print-options", 706, NULL, OPTION_HIDDEN, "Print the initial State", 0},
{"state", 's', NULL, OPTION_HIDDEN, "Print the initial State", 0},
{"log", 'l', NULL, 0, "Emit Log output", 0} ,
{"verbose", 'v', NULL, 0, "Verbose output", 0} ,
{"verbosity", 'V', "INT", 0, "Set Verbosity Option", 0},
{"verbosity-hex", 'X', "HEX", 0, "Set Verbosity Bitfield (in hex)", 0},
{"verbosity-mctp", 'Z', "HEX", OPTION_HIDDEN, "MCTP Verbosity Bitfield (in hex)", 0},
{"options", 707, NULL, 0, "Print list of verbosity flags", 0},
{0,0,0,0, "Help Options", 9},
{"help", 'h', NULL, 0, "Display Help", 0},
{"usage", 701, NULL, 0, "Display Usage", 0},
{"version", 702, NULL, 0, "Display Version", 0},
{0,0,0,0,0,0}
};
/**
* Options that are passed to argp
*/
struct argp argp =
{
ao_main, // An array of argp_option structures
pr_main, // Function to call when parsing each option
NULL, // Help text "ARG1 ARG2" that is displayed after usage eg: Usage: argp.out [OPTION...] ARG1 ARG2
NULL, // User help text at top of --help output
NULL, // A vector of argp_children structures
NULL, // A function to filter the output of help messages
NULL // String domain used to translate strings in argp library
};
/* FUNCTIONS =================================================================*/
/**
* Return a string representation of CLI Option Names [CLOP]
*
* @param u CLI Option Enumeration value [CLOP]
*/
char *clop(int u)
{
if (u >= CLOP_MAX)
return NULL;
return STR_CLOP[u];
}
/**
* Parse function called by parse args for each parameter passed in on the command line
*
* @detail This function is called by parse_argp() for each option encoutered
* @return 0 success, non-zero to indicate a problem
*
* STEPS:
* 1: Get a pointer to thearguments object for argz
* 2: Handle each option based on the key
*/
static int pr_main (
int key, // short name for the option e.g. 'd' or a number
char *arg, // pointer to string entered after the key
struct argp_state *state // State that is available to each call to this function
)
{
struct opt *o;
int rv, i;
// STEP 2: Handle each option based on the key
switch (key)
{
// config
case 'c':
o = &opts[CLOP_CONFIG_FILE];
o->set = 1;
o->str = strndup(arg, CLMR_MAX_ARG_STR_LEN);
break;
// help
case 'h':
print_help();
exit(0);
break;
// log
case 'l':
o = &opts[CLOP_VERBOSITY];
o->set = 1;
o->u64 |= CLVB_ACTIONS;
o->u64 |= CLVB_COMMANDS;
o->u64 |= CLVB_ERRORS;
break;
// tcp-port
case 'P':
o = &opts[CLOP_TCP_PORT];
o->set = 1;
o->u16 = strtoul(arg, NULL, 0);
break;
// TCP Address
case 'T':
o = &opts[CLOP_TCP_ADDRESS];
o->set = 1;
rv = inet_pton(AF_INET, arg, &o->u32);
if (rv != 1)
{
printf("Invalid TCP IP Address\n");
exit(rv);
}
rv = 0;
break;
// state
case 's':
o = &opts[CLOP_PRINT_STATE];
o->set = 1;
break;
// verbose
case 'v':
o = &opts[CLOP_VERBOSITY];
o->set = 1;
// Set General Verbosity bit
o->u64 |= CLVO_GENERAL;
break;
// verbosity
case 'V':
o = &opts[CLOP_VERBOSITY];
o->set = 1;
i = atoi(arg);
// Validate Option
if ( ( i < 0 ) || ( i >= CLVO_MAX ) ) {
printf("Error: Invalid Verbosity option");
exit(1);
}
// Set the verbosity bit
o->u64 |= (0x01 << i);
break;
// verbosity-hex
case 'X':
o = &opts[CLOP_VERBOSITY];
o->set = 1;
o->u64 = strtoul(arg, NULL, 16);
break;
// verbosity-mctp
case 'Z':
o = &opts[CLOP_MCTP_VERBOSITY];
o->set = 1;
o->u64 = strtoul(arg, NULL, 16);
break;
// usage
case 701:
print_usage(ao_main);
exit(0);
break;
// version
case 702:
printf("%s\n", argp_program_version);
exit(0);
break;
// options
case 706:
o = &opts[CLOP_PRINT_OPTS];
o->set = 1;
break;
// options
case 707:
printf("Verbosity options:\n");
for ( i = 0 ; i < CLVO_MAX ; i++)
printf("%2d: %s\n", i, STR_CLVO[i]);
exit(0);
break;
// Called for non option parameter
case ARGP_KEY_ARG:
argp_failure (state, 1, 0, "too many arguments");
break;
// Last call. Verify parameters. Fill in missing values
case ARGP_KEY_END:
// Set default port if not specified
if (!opts[CLOP_TCP_PORT].set) {
opts[CLOP_TCP_PORT].set = 1;
opts[CLOP_TCP_PORT].u16 = CLMR_DEFAULT_SERVER_PORT;
}
// Print options if requested
if (opts[CLOP_PRINT_OPTS].set)
print_options_array(opts);
break;
}
return 0;
}
/**
* Print the Help output
*
* STEPS
* 1: Print the Global Header Statement
* 2: Print usage
* 3: Print level header and flagged options
*/
static void print_help()
{
// STEP 1: Print the Global Header Statement
printf("CXL Switch Emulator\n");
printf("\n\
Usage: %s <options>\n", app_name);
print_options(ao_main);
printf("\n");
}
/**
* Print the command line flag options to the screen as part of help output
*
* @param o the menu level [CLAP] enum
*/
static void print_options(struct argp_option *o)
{
int len;
while (o->doc != NULL)
{
// Break if this is the ending NULL entry
if ( !o->name && !o->key && !o->arg && !o->flags && !o->doc && !o->group )
break;
// Skip Hidden Options
if (o->flags & OPTION_HIDDEN) {
o++;
continue;
}
// Determine if this is a section heading
else if ( !o->name && !o->key && !o->arg && !o->flags && o->doc)
printf("\n %s:\n", o->doc);
// Print normal option entry
else {
// IF this option has a single character key, print the key, else print spaces
if (isalnum(o->key))
printf(" -%c, ", o->key);
else
printf(" ");
len = 6;
// If this option has a long name, print the long name
if (o->name) {
printf("--%s", o->name);
len += strlen(o->name) + 2;
}
// If this option has an arg type, print the type
if (o->arg) {
printf("=%s", o->arg);
len += strlen(o->arg) + 1;
}
// Print remaining spaces up to description column
for ( int i = 0 ; i < CLMR_HELP_COLUMN - len ; i++ )
printf(" ");
// Print description of this option
printf("%s\n", o->doc);
}
o++;
}
}
/**
* Debug function to print out the options array at the end of parsing
*/
static void print_options_array(struct opt *o)
{
int i, len, maxlen;
maxlen = 0;
// Find max length of CLOP String
for (i = 0 ; i < CLOP_MAX ; i++) {
len = strlen(clop(i));
if (len > maxlen)
maxlen = len;
}
// Print Header
printf("##"); // index
printf(" Name"); // OP Name
for (int k = 5 ; k <= maxlen ; k++) // Spaces
printf(" ");
printf(" S"); // Set
printf(" u8"); // u8
printf(" u16"); // u16
printf(" u32"); // u32
printf(" u64"); // u64
printf(" val"); // val
printf(" num"); // num
printf(" len"); // len
printf(" str"); // str
printf("\n");
// Print each entry
for (i = 0 ; i < CLOP_MAX ; i++) {
// index
printf("%02d", i);
// OP Name
printf(" %s", clop(i));
// Spaces
for (int k = strlen(clop(i)) ; k < maxlen ; k++)
printf(" ");
printf(" %d", o[i].set); // Set
printf(" 0x%02x", o[i].u8); // u8
printf(" 0x%04x", o[i].u16); // u16
printf(" 0x%08x", o[i].u32); // u32
printf(" 0x%016llx", o[i].u64); // u64
printf(" 0x%04x", o[i].val); // val
printf(" 0x%016llx", o[i].num); // num
printf(" 0x%016llx", o[i].len); // len
if (o[i].str)
printf(" %s", o[i].str);
printf("\n");
if (o[i].len > 0)
autl_prnt_buf(o[i].buf, o[i].len, 4, 0);
}
}
/**
* Print the usage information for a option level
*
* @param option Menu item from enum [CLAP]
* @param o struct argp_option* to the string data to pull from
* STEPS:
* 1: Initialize variables
* 2: Generate header text
* 3: Count the number of short options with no argument
* 4: If there is at least one short option with no arg, append short options with no argument here
* 5: Append short options with arguments
* 6: Append long options
* 7: Find index of last space before character 80
* 8: Loop through usage buffer and break it up into smaller chunks
*/
static void print_usage(struct argp_option *o)
{
int hdr_len, buf_len, num, i, index;
char buf[4096];
char str[4096];
char *ptr;
struct argp_option *original;
// STEP 1: Initialize variables
num = 0;
index = 0;
hdr_len = 0;
buf_len = 1;
memset(buf, 0, 4096);
memset(str, 0, 4096);
original = o;
ptr = buf;
// STEP 2: Generate header text
sprintf(str, "Usage: %s ", app_name);
hdr_len = strlen(str);
// STEP 3: Count the number of short options with no argument
while ( !( !o->name && !o->key && !o->arg && !o->flags && !o->doc && !o->group ) )
{
if (isalnum(o->key) && !o->arg)
num++;
o++;
}
// Reset pointer
o = original;
// STEP 4: If there is at least one short option with no arg, append short options with no argument here
if ( num > 0 )
{
// Add Leader [-
sprintf(&buf[buf_len], "[-");
buf_len += 2;
// Add each key character
while ( !( !o->name && !o->key && !o->arg && !o->flags && !o->doc && !o->group ) )
{
// If this option has a single character key, print the key, else print spaces
if (isalnum(o->key) && !o->arg) {
sprintf(&buf[buf_len], "%c", o->key);
buf_len += 1;
}
o++;
}
// Add trailing ]
sprintf(&buf[buf_len], "] ");
buf_len += 2;
}
// Reset pointer
o = original;
// STEP 5: Append short options with arguments
while ( !( !o->name && !o->key && !o->arg && !o->flags && !o->doc && !o->group ) )
{
// If this option has a single character key and an arg
if (isalnum(o->key) && o->arg) {
sprintf(&buf[buf_len], "[-%c=%s] ", o->key, o->arg);
buf_len += 6;
buf_len += strlen(o->arg);
}
o++;
}
// Reset pointer
o = original;
// STEP 6: Append long options
while ( !( !o->name && !o->key && !o->arg && !o->flags && !o->doc && !o->group ) )
{
// If this option has a long name, print the long name
if (o->name)
{
sprintf(&buf[buf_len], "[--%s", o->name);
buf_len += strlen(o->name) + 3;
// If this option has an arg type, print the type
if (o->arg)
{
sprintf(&buf[buf_len], "=%s", o->arg);
buf_len += strlen(o->arg) + 1;
}
// Add trailing ]
sprintf(&buf[buf_len], "] ");
buf_len += 2;
}
o++;
}
// STEP 7: Find index of last space before character 80
index = 0;
for ( i = 1 ; i < (CLMR_MAX_HELP_WIDTH-hdr_len) ; i++ ) {
if (ptr[i] == ' ')
index = i;
if (ptr[i]==0)
break;
}
// STEP 8: Loop through usage buffer and break it up into smaller chunks
while (index != 0)
{
// Copy the line of the buffer into str
memcpy(&str[hdr_len], &ptr[1], index-1);
// Set the next char after the string to 0
str[hdr_len+index-1] = 0;
// Print the merged string
printf("%s\n", str);
// Clear the header portion of the print str
memset(str, ' ', hdr_len);
// Advance buffer
ptr = &ptr[index];
// Find index of last space before character 80
index = 0;
for ( i = 1 ; i < (CLMR_MAX_HELP_WIDTH-hdr_len) ; i++ ) {
if (ptr[i] == ' ')
index = i;
if (ptr[i]==0)
break;
}
}
}
/**
* Free allocated memory by option parsing proceedure
*
* @return 0 upon success. Non zero otherwise
*/
int options_free(struct opt *opts)
{
struct opt *o;
int i, rv;
rv = 1;
// Free app_name
if (app_name != NULL)
free(app_name);
app_name = NULL;
// Verify inputs
if (opts == NULL)
goto end;
// For each option in the array, free any allocated memory in the option
for ( i = 0 ; i < CLOP_MAX ; i++ )
{
o = &opts[i];
// Free buf field
if (o->buf)
free(o->buf);
o->buf = NULL;
// Free str field
if (o->str)
free(o->str);
o->str = NULL;
}
// Free options array
free(opts);
opts = NULL;
rv = 0;
end:
return rv;
}
/**
* Parse the command line options
*
* @param argc int representing the number of cli parameters
* @param argv char** of cli parameter strings
* @return Returns 0 on success, error code on error
*
* STEPS
* 1: Zero out global options array
* 2: Zero out arguments array for argz use
* 3: Parse arguments
* 4: argz allocates some memory that needs to be freed after parsing
*/
int options_parse(int argc, char *argv[])
{
int rv, len;
// Initialize variables
rv = 1;
// STEP 1: Store app name in global variable
len = strnlen(argv[0], CLMR_MAX_NAME_LEN);
if (len > 0)
{
app_name = malloc(len+1);
if (app_name == NULL)
goto end;
if (!strncmp("./", argv[0], 2))
memcpy(app_name, &argv[0][2], len-1);
else
memcpy(app_name, argv[0], len+1);
}
// STEP 2: Allocate and clear memory for options array
opts = calloc(CLOP_MAX, sizeof(struct opt));
if (opts == NULL)
goto end_name;
// STEP 3: Parse arguments
rv = argp_parse(&argp, argc, argv, ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
goto end;
end_name:
free(app_name);
end:
return rv;
}

140
options.h Normal file
View File

@ -0,0 +1,140 @@
/* SPDX-License-Identifier: Apache-2.0 */
/**
* @file options.h
*
* @brief Header file for cli options parsing using argp/argz library
*
* @copyright Copyright (C) 2024 Jackrabbit Founders LLC. All rights reserved.
*
* @date Jan 2024
* @author Barrett Edwards <code@jrlabs.io>
*
* Macro / Enumeration Prefixes (CL)
* CLOP - CLI Option (OP)
* CLVB - CLI Verbosity Bit Field (VB)
* CLVO - CLI Verbosity Options (VO)
*
* Standard key mapping
* -h --help Display Help
* -T --tcp-port Server TCP Port
* -V --verbosity Set Verbosity Flag
* -X --verbosity-hex Set all Verbosity Flags with hex value
*
* Non char key mapping
* 701 - usage
* 702 - version
* 703 - data
* 704 - infile
* 705 - outfile
* 706 - print-options
* 707 - options
*/
#ifndef _OPTIONS_H
#define _OPTIONS_H
/* INCLUDES ==================================================================*/
/*
*/
#include <linux/types.h>
/* MACROS ====================================================================*/
#define CLMR_DEFAULT_SERVER_PORT 2508
#define CLMR_MAX_NAME_LEN 64
#define CLMR_HELP_COLUMN 30
#define CLMR_MAX_HELP_WIDTH 100
#define CLMR_MAX_ARG_STR_LEN 256
/* ENUMERATIONS ==============================================================*/
/**
* CLI Verbosity Options (VO)
*/
enum _CLVO
{
CLVO_GENERAL = 0,
CLVO_CALLSTACK = 1,
CLVO_STEPS = 2,
CLVO_PARSE = 3,
CLVO_ACTIONS = 4,
CLVO_COMMANDS = 5,
CLVO_ERRORS = 6,
CLVO_MAX
};
/**
* CLI Verbosity Bit Field (VB)
*/
enum _CLVB
{
CLVB_GENERAL = (0x01 << 0),
CLVB_CALLSTACK = (0x01 << 1),
CLVB_STEPS = (0x01 << 2),
CLVB_PARSE = (0x01 << 3),
CLVB_ACTIONS = (0x01 << 4),
CLVB_COMMANDS = (0x01 << 5),
CLVB_ERRORS = (0x01 << 6),
};
/**
* CLI Option (OP)
*/
enum _CLOP
{
CLOP_VERBOSITY, //!< Verbosity level bitfield <u64>
CLOP_MCTP_VERBOSITY, //!< MCTP Library Verbosity level: Bitfield <u64>
CLOP_PRINT_STATE, //!< Print the state <set>
CLOP_PRINT_OPTS, //!< Print CLI options to console <set>
CLOP_CONFIG_FILE, //!< File to load CXL Switch configuration data <str>
CLOP_TCP_PORT, //!< TCP Port to listen on for connections <u16>
CLOP_TCP_ADDRESS, //!< TCP Address to listen on for connections <u32>
CLOP_MAX
};
/* STRUCTS ===================================================================*/
/**
* CLI Option Struct
*
* Each command line parameter is stored in one of these objects
*/
struct opt
{
int set; //!< Not set (0), set (1)
__u8 u8; //!< Unsigned char value
__u16 u16; //!< Unsigned long value
__u32 u32; //!< Unsigned long value
__u64 u64; //!< Unsigned long long value
__s32 val; //!< Generic signed value
__u64 num; //!< Number of items
__u64 len; //!< Data Buffer Length
char *str; //!< String value
__u8 *buf; //!< Data buffer
};
/* GLOBAL VARIABLES ==========================================================*/
/**
* Array of options from args parse
*/
extern struct opt *opts;
/* PROTOTYPES ================================================================*/
char *clop(int u);
/**
* Free allocated memory by option parsing proceedure
*
* @return 0 upon success. Non zero otherwise
*/
int options_free(struct opt *opts);
/**
* Parse command line options
*/
int options_parse(int argc, char *argv[]);
#endif //ifndef _OPTIONS_H

98
signals.c Normal file
View File

@ -0,0 +1,98 @@
/* SPDX-License-Identifier: Apache-2.0 */
/**
* @file signals.c
*
* @brief Code file for signal handling functions
*
* @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>
/* printf()
*/
#include <stdio.h>
/* signal()
*/
#include <signal.h>
/* strsignal()
*/
#include <string.h>
#include "options.h"
#include "signals.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)
/* ENUMERATIONS ==============================================================*/
/* STRUCTS ===================================================================*/
/* PROTOTYPES ================================================================*/
/* GLOBAL VARIABLES ==========================================================*/
/**
* Global variable used for the signal handlers to tell the main loop to stop
*/
int stop_requested = 0;
/* FUNCTIONS =================================================================*/
/**
* Register signal handlers
*/
void signals_register()
{
ENTER
signal(SIGINT, signals_sigint);
EXIT(0)
}
/**
* Handler for SIGINT (CTRL-C)
*/
void signals_sigint(int sig)
{
ENTER
IFV(CLVB_CALLSTACK) printf("Caught Signal: %d - %s\n", sig, strsignal(sig));
stop_requested = 1;
EXIT(0)
}

40
signals.h Normal file
View File

@ -0,0 +1,40 @@
/* SPDX-License-Identifier: Apache-2.0 */
/**
* @file signals.h
*
* @brief Header file for signal handling functions
*
* @copyright Copyright (C) 2024 Jackrabbit Founders LLC. All rights reserved.
*
* @date Jan 2024
* @author Barrett Edwards <code@jrlabs.io>
*
*/
#ifndef _SIGNALS_H
#define _SIGNALS_H
/* INCLUDES ==================================================================*/
/* MACROS ====================================================================*/
/* ENUMERATIONS ==============================================================*/
/* STRUCTS ===================================================================*/
/* PROTOTYPES ================================================================*/
/**
* Register signal handlers
*/
void signals_register();
/**
* Handler for SIGINT (CTRL-C)
*/
void signals_sigint(int sig);
/* GLOBAL VARIABLES ==========================================================*/
extern int stop_requested;
#endif //ifndef _SIGNALS_H

2049
state.c Normal file

File diff suppressed because it is too large Load Diff

242
state.h Normal file
View File

@ -0,0 +1,242 @@
/* SPDX-License-Identifier: Apache-2.0 */
/**
* @file state.h
*
* @brief Header 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 ==================================================================*/
#ifndef _STATE_H
#define _STATE_H
/* __u8
* __u16
*/
#include <linux/types.h>
/* pthread_mutex_t
*/
#include <pthread.h>
#include <fmapi.h>
/* MACROS ====================================================================*/
#define MAX_LD 16
#define MAX_PORTS 256
#define MAX_VCSS MAX_PORTS
#define MAX_VPPBS_PER_VCS 256
#define MAX_VPPBS MAX_PORTS * MAX_LD
#define MAX_INDENT 32
#define INDENT 2
#define CFG_SPACE_SIZE 4096
#define MAX_FILE_NAME_LEN 256
#define INITIAL_NUM_DEVICES 32
/* ENUMERATIONS ==============================================================*/
/* STRUCTS ===================================================================*/
/**
* Multi Logical Device Object*
*
* This device aggregates all the descriptors for a CXL MLD Logical Device
*
* CXL 2.0 v1.0 Table 111,112,113,116,117,118,119
*/
struct mld {
/* LD Info: Table 111*/
__u64 memory_size; //!< Total device memory capacity
__u16 num; //!< Number of Logical Devices supported
__u8 epc; //!< Egress Port Congestion Supported
__u8 ttr; //!< Temporary Throughput Reduction Supported
/* LD Allocations: Table 112,113 */
__u8 granularity; //!< Memory Granularity [FMMG]
__u64 rng1[FM_MAX_NUM_LD]; //!< Range 1 Allocation Multiplier
__u64 rng2[FM_MAX_NUM_LD]; //!< Range 2 Allocation Multiplier
/* LD QoS Control parameters: Table 116*/
__u8 epc_en; //!< QoS Telem: Egress Port Congestion Enable. Bitfield [FMQT]
__u8 ttr_en; //!< QoS Telem: Temporary Throuhput Reduction Enable. Bitfield [FMQT]
__u8 egress_mod_pcnt; //!< Egress Moderate Percentage: Threshold in percent for Egress Port Congestion mechanism to indicate moderate congestion. Valid range is 1-100. Default is 10.
__u8 egress_sev_pcnt; //!< Egress Severe Percentage: Threshold in percent for Egress Port Congestion mechanism to indicate severe congestion. Valid range is 1-100. Default is 25
__u8 sample_interval; //!< Backpressure Sample Interval: Interval in ns for Egress Port Congestion mechanism to take samples. Valid range is 0-15. Default is 8 (800 ns of history). Value of 0 disables the mechanism.
__u16 rcb; //!< ReqCmpBasis. Estimated maximum sustained sum of requests and recent responses across the entire device, serving as the basis for QoS Limit Fraction. Valid range is 0-65,535. Value of 0 disables the mechanism. Default is 0.
__u8 comp_interval; //!< Completion Collection Interval: Interval in ns for Completion Counting mechanism to collect the number of transmitted responses in a single counter. Valid range is 0-255. Default is 64
/* LD QoS Status: Table 117*/
__u8 bp_avg_pcnt; //!< Backpressure Average Percentage: Current snapshot of the measured Egress Port average congestion. Table 117
/* LD QoS Allocated BW Fractions: Table 118 */
__u8 alloc_bw[FM_MAX_NUM_LD];
/* LD QoS BW Limit Fractions: Table 119 */
__u8 bw_limit[FM_MAX_NUM_LD];
__u8 *cfgspace[FM_MAX_NUM_LD]; //!< Buffers representing PCIe config space for each logical device
__u8 mmap; //!< Direction to mmap a file for the memory space
char *file; //!< Filename for mmaped file
__u8 *memspace; //!< Buffer representing memory space for entire logical device
};
/**
* Virtual PCIe-to-PCIe Bridge Object
*
* CXL 2.0 v1.0 Table 99
*/
struct vppb {
__u16 vppbid; //!< Index of this vPPB in the state->vppbs[] array
__u8 bind_status; //!< PBB Binding Status [FMBS]
__u8 ppid; //!< Physical port number of bound port
__u16 ldid; //!< ID of LD bound to port from MLD on associated physical port
};
/**
* Virtual CXL Switch Object
*
* CXL 2.0 v1.0 Table 99
*/
struct vcs {
__u8 vcsid; //!< VCS ID - Index of this vcs in the state->vcss[] array
__u8 state; //!< Virtual CXL switch State [FMVS]
__u8 uspid; //!< USP Physical Port ID
__u8 num; //!< Number of vPPBs
//!< Array of pointers to vPPB objects
struct vppb vppbs[MAX_VPPBS_PER_VCS];
};
/**
* CXL Switch Port Object
*
* CXL 2.0 v1.0 Table 92
*/
struct port {
__u8 ppid; //!< Port ID - Index of this port in the state->ports[] array
__u8 state; //!< Current Port Configuration State [FMPS]
__u8 dv; //!< Connected Device CXL version [FMDV]
__u8 dt; //!< Connected device type [FMDT]
__u8 cv; //!< Connected CXL version bitmask [FMVC]
__u8 mlw; //!< Max Link Width. Integer number of lanes (1,2,4,8,16)
__u8 nlw; //!< Negotiated Link Width [FMNW]
__u8 speeds; //!< Supported Link Speeds Vector [FMSS]
__u8 mls; //!< Maximum Link Speed [FMMS]
__u8 cls; //!< Current Link Speed [FMMS]
__u8 ltssm; //!< LTSSM State [FMLS]
__u8 lane; //!< First negotiated lane number (Integer lane number)
/** Link State Flags [FMLF] [FMLO] */
__u8 lane_rev; //!< Lane reversal state. 0=standard, 1=rev [FMLO]
__u8 perst; //!< PCIe Reset State PERST#
__u8 prsnt; //!< Port Presence pin state PRSNT#
__u8 pwrctrl; //!< Power Control State (PWR_CTRL)
__u8 ld; //!< Additional supported LD Count (beyond 1)
__u8 *cfgspace; //!< Buffer representing PCIe config space
struct mld *mld; //!< State for MLD
char *device_name; //!< Name of device used to populate this port
};
struct cse_device
{
char *name; //!< Name of device
__u8 rootport; //!< Root Port Device. 1=root, 2=endpoint
__u8 dv; //!< Connected Device CXL version [FMDV]
__u8 dt; //!< Connected device type [FMDT]
__u8 cv; //!< Connected CXL version bitmask [FMVC]
__u8 mlw; //!< Maximum Link Width. Integer number of lanes (1,2,4,8,16)
__u8 mls; //!< Maximum Link Speed [FMMS]
__u8 *cfgspace; //!< Buffer representing PCIe config space
struct mld *mld; //!< MLD info if this is an MLD
};
/**
* CXL Switch State Identify Information
*
* CXL 2.0 v1 Table 89
*/
struct cxl_switch_state {
__u8 version; //!< Device Management Version
__u16 vid; //!< PCIe Vendor ID
__u16 did; //!< PCIe Device ID
__u16 svid; //!< PCIe Subsystem Vendor ID
__u16 ssid; //!< PCIe Subsystem ID
__u64 sn; //!< Device Serial Number
__u8 max_msg_size_n; //!< Max fmapi msg size. 2^n
__u8 msg_rsp_limit_n; //!< Message Response Limit n of 2^n
__u8 bos_running; //!< Background operation status 0=none, 1=running
__u8 bos_pcnt; //!< Background operation percent complete [0-100]
__u16 bos_opcode; //!< Background operation opcode
__u16 bos_rc; //!< Background operation return code
__u16 bos_ext; //!< Background operation Extended Vendor Status
__u8 ingress_port; //!< Ingress Port ID
__u8 num_ports; //!< Total number of physical ports
__u8 num_vcss; //!< Max number of VCSs
__u16 num_vppbs; //!< Max number of vPPBs
__u16 active_vppbs; //!< Number of active vPPBs
__u8 num_decoders; //!< Number of HDM decoders available per USP
struct port *ports; //!< array of Port objects
struct vcs *vcss; //!< array of VCS objects
struct cse_device *devices; //!< array of device definitions
__u16 len_devices; //!< Number of entries supported in devices array
__u16 num_devices; //!< Number of entries in devices array
/* Port defaults */
__u8 mlw; //!< Max Link Width. Integer number of lanes (1,2,4,8,16)
__u8 speeds; //!< Supported Link Speeds Vector [FMSS]
__u8 mls; //!< Maximum Link Speed [FMMS]
char *dir; //!< Filepath to directory for instantiated memory
pthread_mutex_t mtx; //!< Mutex to control access to this object
};
/* PROTOTYPES ================================================================*/
struct cxl_switch_state *state_init(unsigned ports, unsigned vcss, unsigned vppbs);
int state_load(struct cxl_switch_state *s, char *filename);
void state_free(struct cxl_switch_state *s);
int state_connect_device(struct port *p, struct cse_device *d);
int state_disconnect_device(struct port *p);
/* Conversion Functions */
void state_conv_identity(struct cxl_switch_state *src, struct fmapi_psc_id_rsp *dst);
void state_conv_port_info(struct port *src, struct fmapi_psc_port_info *dst);
void state_conv_vcs_info(struct vcs *src, struct fmapi_vsc_info_blk *dst);
/* Print Functions */
void state_print(struct cxl_switch_state *s);
void state_print_identity(struct cxl_switch_state *s, unsigned indent);
void state_print_ports(struct cxl_switch_state *s, unsigned indent);
void state_print_port(struct port *p, unsigned indent);
void state_print_vcss(struct cxl_switch_state *s, unsigned indent);
void state_print_vcs(struct vcs *v, unsigned indent);
void state_print_vppb(struct vppb *b, unsigned indent);
void state_print_mld(struct mld *mld, unsigned indent);
void state_print_devices(struct cxl_switch_state *s);
/* GLOBAL VARIABLES ==========================================================*/
extern struct cxl_switch_state *cxl_state;
#endif //ifndef _STATE_H