/************************************************************************** Copyright (c) 2007-2008, Chelsio Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the Chelsio Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define NMTUS 16 #define TCB_SIZE 128 #define TCB_WORDS (TCB_SIZE / 4) #define PROTO_SRAM_LINES 128 #define PROTO_SRAM_LINE_BITS 132 #define PROTO_SRAM_LINE_NIBBLES (132 / 4) #define PROTO_SRAM_SIZE (PROTO_SRAM_LINE_NIBBLES * PROTO_SRAM_LINES / 2) #define PROTO_SRAM_EEPROM_ADDR 4096 #include #include #include "version.h" struct reg_info { const char *name; uint16_t addr; uint16_t len; }; #include "reg_defs.c" #if defined(CONFIG_T3_REGS) # include "reg_defs_t3.c" # include "reg_defs_t3b.c" # include "reg_defs_t3c.c" #endif static const char *progname; static void __attribute__((noreturn)) usage(FILE *fp) { fprintf(fp, "Usage: %s [operation]\n", progname); fprintf(fp, "\tclearstats clear MAC statistics\n" "\tcontext show an SGE context\n" "\tdesc [] dump SGE descriptors\n" "\tloadboot download boot image\n" "\tloadfw download firmware\n" "\tmdio \n" "\t [] read/write MDIO register\n" "\tmemdump cm|tx|rx dump a mem range\n" "\tmeminfo show memory info\n" "\tmtus [...] read/write MTU table\n" "\tpktsched port set TX port scheduler params\n" "\tpktsched tunnelq \n" "\t set TX tunnelq scheduler params\n" "\tpktsched tx \n" "\t [ ] ... set Tx HW scheduler\n" "\tpm [ ] read/write PM config\n" "\tproto read proto SRAM\n" "\tqset read qset parameters\n" "\tqsets read # of qsets\n" "\treg
[=] read/write register\n" "\tregdump [] dump registers\n" "\ttcamdump
show TCAM contents\n" "\ttcb read TCB\n" "\ttrace tx|rx|all on|off [not]\n" "\t [ [:]] ... write trace parameters\n" ); exit(fp == stderr ? 1 : 0); } static int doit(const char *iff_name, unsigned long cmd, void *data) { static int fd = 0; if (fd == 0) { char buf[64]; snprintf(buf, 64, "/dev/%s", iff_name); if ((fd = open(buf, O_RDWR)) < 0) return -1; } return ioctl(fd, cmd, data) < 0 ? -1 : 0; } static int get_int_arg(const char *s, uint32_t *valp) { char *p; *valp = strtoul(s, &p, 0); if (*p) { warnx("bad parameter \"%s\"", s); return -1; } return 0; } static uint32_t read_reg(const char *iff_name, uint32_t addr) { struct ch_reg reg; reg.addr = addr; if (doit(iff_name, CHELSIO_GETREG, ®) < 0) err(1, "register read"); return reg.val; } static void write_reg(const char *iff_name, uint32_t addr, uint32_t val) { struct ch_reg ch_reg; ch_reg.addr = addr; ch_reg.val = val; if (doit(iff_name, CHELSIO_SETREG, &ch_reg) < 0) err(1, "register write"); } static int register_io(int argc, char *argv[], int start_arg, const char *iff_name) { char *p; uint32_t addr, val = 0, write = 0; if (argc != start_arg + 1) return -1; addr = strtoul(argv[start_arg], &p, 0); if (p == argv[start_arg]) return -1; if (*p == '=' && p[1]) { val = strtoul(p + 1, &p, 0); write = 1; } if (*p) { warnx("bad parameter \"%s\"", argv[start_arg]); return -1; } if (write) write_reg(iff_name, addr, val); else { val = read_reg(iff_name, addr); printf("%#x [%u]\n", val, val); } return 0; } static int mdio_io(int argc, char *argv[], int start_arg, const char *iff_name) { struct ifreq ifr; struct ch_mii_data p; unsigned int cmd, phy_addr, reg, mmd, val; if (argc == start_arg + 3) cmd = CHELSIO_GET_MIIREG; else if (argc == start_arg + 4) cmd = CHELSIO_SET_MIIREG; else return -1; if (get_int_arg(argv[start_arg], &phy_addr) || get_int_arg(argv[start_arg + 1], &mmd) || get_int_arg(argv[start_arg + 2], ®) || (cmd == CHELSIO_SET_MIIREG && get_int_arg(argv[start_arg + 3], &val))) return -1; p.phy_id = phy_addr | (mmd << 8); p.reg_num = reg; p.val_in = val; if (doit(iff_name, cmd, &p) < 0) err(1, "MDIO %s", cmd == CHELSIO_GET_MIIREG ? "read" : "write"); if (cmd == CHELSIO_GET_MIIREG) printf("%#x [%u]\n", p.val_out, p.val_out); return 0; } static inline uint32_t xtract(uint32_t val, int shift, int len) { return (val >> shift) & ((1 << len) - 1); } static int dump_block_regs(const struct reg_info *reg_array, uint32_t *regs) { uint32_t reg_val = 0; // silence compiler warning for ( ; reg_array->name; ++reg_array) if (!reg_array->len) { reg_val = regs[reg_array->addr / 4]; printf("[%#5x] %-40s %#-10x [%u]\n", reg_array->addr, reg_array->name, reg_val, reg_val); } else { uint32_t v = xtract(reg_val, reg_array->addr, reg_array->len); printf(" %-40s %#-10x [%u]\n", reg_array->name, v, v); } return 1; } static int dump_regs_t2(int argc, char *argv[], int start_arg, uint32_t *regs) { int match = 0; char *block_name = NULL; if (argc == start_arg + 1) block_name = argv[start_arg]; else if (argc != start_arg) return -1; if (!block_name || !strcmp(block_name, "sge")) match += dump_block_regs(sge_regs, regs); if (!block_name || !strcmp(block_name, "mc3")) match += dump_block_regs(mc3_regs, regs); if (!block_name || !strcmp(block_name, "mc4")) match += dump_block_regs(mc4_regs, regs); if (!block_name || !strcmp(block_name, "tpi")) match += dump_block_regs(tpi_regs, regs); if (!block_name || !strcmp(block_name, "tp")) match += dump_block_regs(tp_regs, regs); if (!block_name || !strcmp(block_name, "rat")) match += dump_block_regs(rat_regs, regs); if (!block_name || !strcmp(block_name, "cspi")) match += dump_block_regs(cspi_regs, regs); if (!block_name || !strcmp(block_name, "espi")) match += dump_block_regs(espi_regs, regs); if (!block_name || !strcmp(block_name, "ulp")) match += dump_block_regs(ulp_regs, regs); if (!block_name || !strcmp(block_name, "pl")) match += dump_block_regs(pl_regs, regs); if (!block_name || !strcmp(block_name, "mc5")) match += dump_block_regs(mc5_regs, regs); if (!match) errx(1, "unknown block \"%s\"", block_name); return 0; } #if defined(CONFIG_T3_REGS) static int dump_regs_t3(int argc, char *argv[], int start_arg, uint32_t *regs, int is_pcie) { int match = 0; char *block_name = NULL; if (argc == start_arg + 1) block_name = argv[start_arg]; else if (argc != start_arg) return -1; if (!block_name || !strcmp(block_name, "sge")) match += dump_block_regs(sge3_regs, regs); if (!block_name || !strcmp(block_name, "pci")) match += dump_block_regs(is_pcie ? pcie0_regs : pcix1_regs, regs); if (!block_name || !strcmp(block_name, "t3dbg")) match += dump_block_regs(t3dbg_regs, regs); if (!block_name || !strcmp(block_name, "pmrx")) match += dump_block_regs(mc7_pmrx_regs, regs); if (!block_name || !strcmp(block_name, "pmtx")) match += dump_block_regs(mc7_pmtx_regs, regs); if (!block_name || !strcmp(block_name, "cm")) match += dump_block_regs(mc7_cm_regs, regs); if (!block_name || !strcmp(block_name, "cim")) match += dump_block_regs(cim_regs, regs); if (!block_name || !strcmp(block_name, "tp")) match += dump_block_regs(tp1_regs, regs); if (!block_name || !strcmp(block_name, "ulp_rx")) match += dump_block_regs(ulp2_rx_regs, regs); if (!block_name || !strcmp(block_name, "ulp_tx")) match += dump_block_regs(ulp2_tx_regs, regs); if (!block_name || !strcmp(block_name, "pmrx")) match += dump_block_regs(pm1_rx_regs, regs); if (!block_name || !strcmp(block_name, "pmtx")) match += dump_block_regs(pm1_tx_regs, regs); if (!block_name || !strcmp(block_name, "mps")) match += dump_block_regs(mps0_regs, regs); if (!block_name || !strcmp(block_name, "cplsw")) match += dump_block_regs(cpl_switch_regs, regs); if (!block_name || !strcmp(block_name, "smb")) match += dump_block_regs(smb0_regs, regs); if (!block_name || !strcmp(block_name, "i2c")) match += dump_block_regs(i2cm0_regs, regs); if (!block_name || !strcmp(block_name, "mi1")) match += dump_block_regs(mi1_regs, regs); if (!block_name || !strcmp(block_name, "sf")) match += dump_block_regs(sf1_regs, regs); if (!block_name || !strcmp(block_name, "pl")) match += dump_block_regs(pl3_regs, regs); if (!block_name || !strcmp(block_name, "mc5")) match += dump_block_regs(mc5a_regs, regs); if (!block_name || !strcmp(block_name, "xgmac0")) match += dump_block_regs(xgmac0_0_regs, regs); if (!block_name || !strcmp(block_name, "xgmac1")) match += dump_block_regs(xgmac0_1_regs, regs); if (!match) errx(1, "unknown block \"%s\"", block_name); return 0; } static int dump_regs_t3b(int argc, char *argv[], int start_arg, uint32_t *regs, int is_pcie) { int match = 0; char *block_name = NULL; if (argc == start_arg + 1) block_name = argv[start_arg]; else if (argc != start_arg) return -1; if (!block_name || !strcmp(block_name, "sge")) match += dump_block_regs(t3b_sge3_regs, regs); if (!block_name || !strcmp(block_name, "pci")) match += dump_block_regs(is_pcie ? t3b_pcie0_regs : t3b_pcix1_regs, regs); if (!block_name || !strcmp(block_name, "t3dbg")) match += dump_block_regs(t3b_t3dbg_regs, regs); if (!block_name || !strcmp(block_name, "pmrx")) match += dump_block_regs(t3b_mc7_pmrx_regs, regs); if (!block_name || !strcmp(block_name, "pmtx")) match += dump_block_regs(t3b_mc7_pmtx_regs, regs); if (!block_name || !strcmp(block_name, "cm")) match += dump_block_regs(t3b_mc7_cm_regs, regs); if (!block_name || !strcmp(block_name, "cim")) match += dump_block_regs(t3b_cim_regs, regs); if (!block_name || !strcmp(block_name, "tp")) match += dump_block_regs(t3b_tp1_regs, regs); if (!block_name || !strcmp(block_name, "ulp_rx")) match += dump_block_regs(t3b_ulp2_rx_regs, regs); if (!block_name || !strcmp(block_name, "ulp_tx")) match += dump_block_regs(t3b_ulp2_tx_regs, regs); if (!block_name || !strcmp(block_name, "pmrx")) match += dump_block_regs(t3b_pm1_rx_regs, regs); if (!block_name || !strcmp(block_name, "pmtx")) match += dump_block_regs(t3b_pm1_tx_regs, regs); if (!block_name || !strcmp(block_name, "mps")) match += dump_block_regs(t3b_mps0_regs, regs); if (!block_name || !strcmp(block_name, "cplsw")) match += dump_block_regs(t3b_cpl_switch_regs, regs); if (!block_name || !strcmp(block_name, "smb")) match += dump_block_regs(t3b_smb0_regs, regs); if (!block_name || !strcmp(block_name, "i2c")) match += dump_block_regs(t3b_i2cm0_regs, regs); if (!block_name || !strcmp(block_name, "mi1")) match += dump_block_regs(t3b_mi1_regs, regs); if (!block_name || !strcmp(block_name, "sf")) match += dump_block_regs(t3b_sf1_regs, regs); if (!block_name || !strcmp(block_name, "pl")) match += dump_block_regs(t3b_pl3_regs, regs); if (!block_name || !strcmp(block_name, "mc5")) match += dump_block_regs(t3b_mc5a_regs, regs); if (!block_name || !strcmp(block_name, "xgmac0")) match += dump_block_regs(t3b_xgmac0_0_regs, regs); if (!block_name || !strcmp(block_name, "xgmac1")) match += dump_block_regs(t3b_xgmac0_1_regs, regs); if (!match) errx(1, "unknown block \"%s\"", block_name); return 0; } static int dump_regs_t3c(int argc, char *argv[], int start_arg, uint32_t *regs, int is_pcie) { int match = 0; char *block_name = NULL; if (argc == start_arg + 1) block_name = argv[start_arg]; else if (argc != start_arg) return -1; if (!block_name || !strcmp(block_name, "sge")) match += dump_block_regs(t3c_sge3_regs, regs); if (!block_name || !strcmp(block_name, "pci")) match += dump_block_regs(is_pcie ? t3c_pcie0_regs : t3c_pcix1_regs, regs); if (!block_name || !strcmp(block_name, "t3dbg")) match += dump_block_regs(t3c_t3dbg_regs, regs); if (!block_name || !strcmp(block_name, "pmrx")) match += dump_block_regs(t3c_mc7_pmrx_regs, regs); if (!block_name || !strcmp(block_name, "pmtx")) match += dump_block_regs(t3c_mc7_pmtx_regs, regs); if (!block_name || !strcmp(block_name, "cm")) match += dump_block_regs(t3c_mc7_cm_regs, regs); if (!block_name || !strcmp(block_name, "cim")) match += dump_block_regs(t3c_cim_regs, regs); if (!block_name || !strcmp(block_name, "tp")) match += dump_block_regs(t3c_tp1_regs, regs); if (!block_name || !strcmp(block_name, "ulp_rx")) match += dump_block_regs(t3c_ulp2_rx_regs, regs); if (!block_name || !strcmp(block_name, "ulp_tx")) match += dump_block_regs(t3c_ulp2_tx_regs, regs); if (!block_name || !strcmp(block_name, "pmrx")) match += dump_block_regs(t3c_pm1_rx_regs, regs); if (!block_name || !strcmp(block_name, "pmtx")) match += dump_block_regs(t3c_pm1_tx_regs, regs); if (!block_name || !strcmp(block_name, "mps")) match += dump_block_regs(t3c_mps0_regs, regs); if (!block_name || !strcmp(block_name, "cplsw")) match += dump_block_regs(t3c_cpl_switch_regs, regs); if (!block_name || !strcmp(block_name, "smb")) match += dump_block_regs(t3c_smb0_regs, regs); if (!block_name || !strcmp(block_name, "i2c")) match += dump_block_regs(t3c_i2cm0_regs, regs); if (!block_name || !strcmp(block_name, "mi1")) match += dump_block_regs(t3c_mi1_regs, regs); if (!block_name || !strcmp(block_name, "sf")) match += dump_block_regs(t3c_sf1_regs, regs); if (!block_name || !strcmp(block_name, "pl")) match += dump_block_regs(t3c_pl3_regs, regs); if (!block_name || !strcmp(block_name, "mc5")) match += dump_block_regs(t3c_mc5a_regs, regs); if (!block_name || !strcmp(block_name, "xgmac0")) match += dump_block_regs(t3c_xgmac0_0_regs, regs); if (!block_name || !strcmp(block_name, "xgmac1")) match += dump_block_regs(t3c_xgmac0_1_regs, regs); if (!match) errx(1, "unknown block \"%s\"", block_name); return 0; } #endif static int dump_regs(int argc, char *argv[], int start_arg, const char *iff_name) { int i, vers, revision, is_pcie; struct ch_ifconf_regs regs; regs.len = REGDUMP_SIZE; /* XXX: This is never freed. Looks like we don't care. */ if ((regs.data = malloc(regs.len)) == NULL) err(1, "can't malloc"); if (doit(iff_name, CHELSIO_IFCONF_GETREGS, ®s)) err(1, "can't read registers"); vers = regs.version & 0x3ff; revision = (regs.version >> 10) & 0x3f; is_pcie = (regs.version & 0x80000000) != 0; if (vers <= 2) return dump_regs_t2(argc, argv, start_arg, (uint32_t *)regs.data); #if defined(CONFIG_T3_REGS) if (vers == 3) { if (revision == 0) return dump_regs_t3(argc, argv, start_arg, (uint32_t *)regs.data, is_pcie); if (revision == 2 || revision == 3) return dump_regs_t3b(argc, argv, start_arg, (uint32_t *)regs.data, is_pcie); if (revision == 4) return dump_regs_t3c(argc, argv, start_arg, (uint32_t *)regs.data, is_pcie); } #endif errx(1, "unknown card type %d.%d", vers, revision); return 0; } static int t3_meminfo(const uint32_t *regs) { enum { SG_EGR_CNTX_BADDR = 0x58, SG_CQ_CONTEXT_BADDR = 0x6c, CIM_SDRAM_BASE_ADDR = 0x28c, CIM_SDRAM_ADDR_SIZE = 0x290, TP_CMM_MM_BASE = 0x314, TP_CMM_TIMER_BASE = 0x318, TP_CMM_MM_RX_FLST_BASE = 0x460, TP_CMM_MM_TX_FLST_BASE = 0x464, TP_CMM_MM_PS_FLST_BASE = 0x468, ULPRX_ISCSI_LLIMIT = 0x50c, ULPRX_ISCSI_ULIMIT = 0x510, ULPRX_TDDP_LLIMIT = 0x51c, ULPRX_TDDP_ULIMIT = 0x520, ULPRX_STAG_LLIMIT = 0x52c, ULPRX_STAG_ULIMIT = 0x530, ULPRX_RQ_LLIMIT = 0x534, ULPRX_RQ_ULIMIT = 0x538, ULPRX_PBL_LLIMIT = 0x53c, ULPRX_PBL_ULIMIT = 0x540, }; unsigned int egr_cntxt = regs[SG_EGR_CNTX_BADDR / 4], cq_cntxt = regs[SG_CQ_CONTEXT_BADDR / 4], timers = regs[TP_CMM_TIMER_BASE / 4] & 0xfffffff, pstructs = regs[TP_CMM_MM_BASE / 4], pstruct_fl = regs[TP_CMM_MM_PS_FLST_BASE / 4], rx_fl = regs[TP_CMM_MM_RX_FLST_BASE / 4], tx_fl = regs[TP_CMM_MM_TX_FLST_BASE / 4], cim_base = regs[CIM_SDRAM_BASE_ADDR / 4], cim_size = regs[CIM_SDRAM_ADDR_SIZE / 4]; unsigned int iscsi_ll = regs[ULPRX_ISCSI_LLIMIT / 4], iscsi_ul = regs[ULPRX_ISCSI_ULIMIT / 4], tddp_ll = regs[ULPRX_TDDP_LLIMIT / 4], tddp_ul = regs[ULPRX_TDDP_ULIMIT / 4], stag_ll = regs[ULPRX_STAG_LLIMIT / 4], stag_ul = regs[ULPRX_STAG_ULIMIT / 4], rq_ll = regs[ULPRX_RQ_LLIMIT / 4], rq_ul = regs[ULPRX_RQ_ULIMIT / 4], pbl_ll = regs[ULPRX_PBL_LLIMIT / 4], pbl_ul = regs[ULPRX_PBL_ULIMIT / 4]; printf("CM memory map:\n"); printf(" TCB region: 0x%08x - 0x%08x [%u]\n", 0, egr_cntxt - 1, egr_cntxt); printf(" Egress contexts: 0x%08x - 0x%08x [%u]\n", egr_cntxt, cq_cntxt - 1, cq_cntxt - egr_cntxt); printf(" CQ contexts: 0x%08x - 0x%08x [%u]\n", cq_cntxt, timers - 1, timers - cq_cntxt); printf(" Timers: 0x%08x - 0x%08x [%u]\n", timers, pstructs - 1, pstructs - timers); printf(" Pstructs: 0x%08x - 0x%08x [%u]\n", pstructs, pstruct_fl - 1, pstruct_fl - pstructs); printf(" Pstruct FL: 0x%08x - 0x%08x [%u]\n", pstruct_fl, rx_fl - 1, rx_fl - pstruct_fl); printf(" Rx FL: 0x%08x - 0x%08x [%u]\n", rx_fl, tx_fl - 1, tx_fl - rx_fl); printf(" Tx FL: 0x%08x - 0x%08x [%u]\n", tx_fl, cim_base - 1, cim_base - tx_fl); printf(" uP RAM: 0x%08x - 0x%08x [%u]\n", cim_base, cim_base + cim_size - 1, cim_size); printf("\nPMRX memory map:\n"); printf(" iSCSI region: 0x%08x - 0x%08x [%u]\n", iscsi_ll, iscsi_ul, iscsi_ul - iscsi_ll + 1); printf(" TCP DDP region: 0x%08x - 0x%08x [%u]\n", tddp_ll, tddp_ul, tddp_ul - tddp_ll + 1); printf(" TPT region: 0x%08x - 0x%08x [%u]\n", stag_ll, stag_ul, stag_ul - stag_ll + 1); printf(" RQ region: 0x%08x - 0x%08x [%u]\n", rq_ll, rq_ul, rq_ul - rq_ll + 1); printf(" PBL region: 0x%08x - 0x%08x [%u]\n", pbl_ll, pbl_ul, pbl_ul - pbl_ll + 1); return 0; } static int meminfo(int argc, char *argv[], int start_arg, const char *iff_name) { int vers; struct ch_ifconf_regs regs; regs.len = REGDUMP_SIZE; if ((regs.data = malloc(regs.len)) == NULL) err(1, "can't malloc"); if (doit(iff_name, CHELSIO_IFCONF_GETREGS, ®s)) err(1, "can't read registers"); vers = regs.version & 0x3ff; if (vers == 3) return t3_meminfo((uint32_t *)regs.data); errx(1, "unknown card type %d", vers); return 0; } static int mtu_tab_op(int argc, char *argv[], int start_arg, const char *iff_name) { struct ch_mtus m; int i; if (argc == start_arg) { if (doit(iff_name, CHELSIO_GETMTUTAB, &m) < 0) err(1, "get MTU table"); for (i = 0; i < m.nmtus; ++i) printf("%u ", m.mtus[i]); printf("\n"); } else if (argc <= start_arg + NMTUS) { m.nmtus = argc - start_arg; for (i = 0; i < m.nmtus; ++i) { char *p; unsigned long mt = strtoul(argv[start_arg + i], &p, 0); if (*p || mt > 9600) { warnx("bad parameter \"%s\"", argv[start_arg + i]); return -1; } if (i && mt < m.mtus[i - 1]) errx(1, "MTUs must be in ascending order"); m.mtus[i] = mt; } if (doit(iff_name, CHELSIO_SETMTUTAB, &m) < 0) err(1, "set MTU table"); } else return -1; return 0; } #ifdef CHELSIO_INTERNAL static void show_egress_cntxt(uint32_t data[]) { printf("credits: %u\n", data[0] & 0x7fff); printf("GTS: %u\n", (data[0] >> 15) & 1); printf("index: %u\n", data[0] >> 16); printf("queue size: %u\n", data[1] & 0xffff); printf("base address: 0x%llx\n", ((data[1] >> 16) | ((uint64_t)data[2] << 16) | (((uint64_t)data[3] & 0xf) << 48)) << 12); printf("rsp queue #: %u\n", (data[3] >> 4) & 7); printf("cmd queue #: %u\n", (data[3] >> 7) & 1); printf("TUN: %u\n", (data[3] >> 8) & 1); printf("TOE: %u\n", (data[3] >> 9) & 1); printf("generation: %u\n", (data[3] >> 10) & 1); printf("uP token: %u\n", (data[3] >> 11) & 0xfffff); printf("valid: %u\n", (data[3] >> 31) & 1); } static void show_fl_cntxt(uint32_t data[]) { printf("base address: 0x%llx\n", ((uint64_t)data[0] | ((uint64_t)data[1] & 0xfffff) << 32) << 12); printf("index: %u\n", (data[1] >> 20) | ((data[2] & 0xf) << 12)); printf("queue size: %u\n", (data[2] >> 4) & 0xffff); printf("generation: %u\n", (data[2] >> 20) & 1); printf("entry size: %u\n", ((data[2] >> 21) & 0x7ff) | (data[3] & 0x1fffff)); printf("congest thr: %u\n", (data[3] >> 21) & 0x3ff); printf("GTS: %u\n", (data[3] >> 31) & 1); } static void show_response_cntxt(uint32_t data[]) { printf("index: %u\n", data[0] & 0xffff); printf("size: %u\n", data[0] >> 16); printf("base address: 0x%llx\n", ((uint64_t)data[1] | ((uint64_t)data[2] & 0xfffff) << 32) << 12); printf("MSI-X/RspQ: %u\n", (data[2] >> 20) & 0x3f); printf("intr enable: %u\n", (data[2] >> 26) & 1); printf("intr armed: %u\n", (data[2] >> 27) & 1); printf("generation: %u\n", (data[2] >> 28) & 1); printf("CQ mode: %u\n", (data[2] >> 31) & 1); printf("FL threshold: %u\n", data[3]); } static void show_cq_cntxt(uint32_t data[]) { printf("index: %u\n", data[0] & 0xffff); printf("size: %u\n", data[0] >> 16); printf("base address: 0x%llx\n", ((uint64_t)data[1] | ((uint64_t)data[2] & 0xfffff) << 32) << 12); printf("rsp queue #: %u\n", (data[2] >> 20) & 0x3f); printf("AN: %u\n", (data[2] >> 26) & 1); printf("armed: %u\n", (data[2] >> 27) & 1); printf("ANS: %u\n", (data[2] >> 28) & 1); printf("generation: %u\n", (data[2] >> 29) & 1); printf("overflow mode: %u\n", (data[2] >> 31) & 1); printf("credits: %u\n", data[3] & 0xffff); printf("credit threshold: %u\n", data[3] >> 16); } static int get_sge_context(int argc, char *argv[], int start_arg, const char *iff_name) { struct ch_cntxt ctx; if (argc != start_arg + 2) return -1; if (!strcmp(argv[start_arg], "egress")) ctx.cntxt_type = CNTXT_TYPE_EGRESS; else if (!strcmp(argv[start_arg], "fl")) ctx.cntxt_type = CNTXT_TYPE_FL; else if (!strcmp(argv[start_arg], "response")) ctx.cntxt_type = CNTXT_TYPE_RSP; else if (!strcmp(argv[start_arg], "cq")) ctx.cntxt_type = CNTXT_TYPE_CQ; else { warnx("unknown context type \"%s\"; known types are egress, " "fl, cq, and response", argv[start_arg]); return -1; } if (get_int_arg(argv[start_arg + 1], &ctx.cntxt_id)) return -1; if (doit(iff_name, CHELSIO_GET_SGE_CONTEXT, &ctx) < 0) err(1, "get SGE context"); if (!strcmp(argv[start_arg], "egress")) show_egress_cntxt(ctx.data); else if (!strcmp(argv[start_arg], "fl")) show_fl_cntxt(ctx.data); else if (!strcmp(argv[start_arg], "response")) show_response_cntxt(ctx.data); else if (!strcmp(argv[start_arg], "cq")) show_cq_cntxt(ctx.data); return 0; } #define ntohll(x) be64toh((x)) static int get_sge_desc(int argc, char *argv[], int start_arg, const char *iff_name) { uint64_t *p, wr_hdr; unsigned int n = 1, qset, qnum; struct ch_desc desc; if (argc != start_arg + 3 && argc != start_arg + 4) return -1; if (get_int_arg(argv[start_arg], &qset) || get_int_arg(argv[start_arg + 1], &qnum) || get_int_arg(argv[start_arg + 2], &desc.idx)) return -1; if (argc == start_arg + 4 && get_int_arg(argv[start_arg + 3], &n)) return -1; if (qnum > 5) errx(1, "invalid queue number %d, range is 0..5", qnum); desc.queue_num = qset * 6 + qnum; for (; n--; desc.idx++) { if (doit(iff_name, CHELSIO_GET_SGE_DESC, &desc) < 0) err(1, "get SGE descriptor"); p = (uint64_t *)desc.data; wr_hdr = ntohll(*p); printf("Descriptor %u: cmd %u, TID %u, %s%s%s%s%u flits\n", desc.idx, (unsigned int)(wr_hdr >> 56), ((unsigned int)wr_hdr >> 8) & 0xfffff, ((wr_hdr >> 55) & 1) ? "SOP, " : "", ((wr_hdr >> 54) & 1) ? "EOP, " : "", ((wr_hdr >> 53) & 1) ? "COMPL, " : "", ((wr_hdr >> 52) & 1) ? "SGL, " : "", (unsigned int)wr_hdr & 0xff); for (; desc.size; p++, desc.size -= sizeof(uint64_t)) printf("%016" PRIx64 "%c", ntohll(*p), desc.size % 32 == 8 ? '\n' : ' '); } return 0; } #endif static int get_tcb2(int argc, char *argv[], int start_arg, const char *iff_name) { uint64_t *d; unsigned int i; unsigned int tcb_idx; struct ch_mem_range mr; if (argc != start_arg + 1) return -1; if (get_int_arg(argv[start_arg], &tcb_idx)) return -1; mr.buf = calloc(1, TCB_SIZE); if (!mr.buf) err(1, "get TCB"); mr.mem_id = MEM_CM; mr.addr = tcb_idx * TCB_SIZE; mr.len = TCB_SIZE; if (doit(iff_name, CHELSIO_GET_MEM, &mr) < 0) err(1, "get TCB"); for (d = (uint64_t *)mr.buf, i = 0; i < TCB_SIZE / 32; i++) { printf("%2u:", i); printf(" %08x %08x %08x %08x", (uint32_t)d[1], (uint32_t)(d[1] >> 32), (uint32_t)d[0], (uint32_t)(d[0] >> 32)); d += 2; printf(" %08x %08x %08x %08x\n", (uint32_t)d[1], (uint32_t)(d[1] >> 32), (uint32_t)d[0], (uint32_t)(d[0] >> 32)); d += 2; } free(mr.buf); return 0; } static int get_pm_page_spec(const char *s, unsigned int *page_size, unsigned int *num_pages) { char *p; unsigned long val; val = strtoul(s, &p, 0); if (p == s) return -1; if (*p == 'x' && p[1]) { *num_pages = val; *page_size = strtoul(p + 1, &p, 0); } else { *num_pages = -1; *page_size = val; } *page_size <<= 10; // KB -> bytes return *p; } static int conf_pm(int argc, char *argv[], int start_arg, const char *iff_name) { struct ch_pm pm; if (argc == start_arg) { if (doit(iff_name, CHELSIO_GET_PM, &pm) < 0) err(1, "read pm config"); printf("%ux%uKB TX pages, %ux%uKB RX pages, %uKB total memory\n", pm.tx_num_pg, pm.tx_pg_sz >> 10, pm.rx_num_pg, pm.rx_pg_sz >> 10, pm.pm_total >> 10); return 0; } if (argc != start_arg + 2) return -1; if (get_pm_page_spec(argv[start_arg], &pm.tx_pg_sz, &pm.tx_num_pg)) { warnx("bad parameter \"%s\"", argv[start_arg]); return -1; } if (get_pm_page_spec(argv[start_arg + 1], &pm.rx_pg_sz, &pm.rx_num_pg)) { warnx("bad parameter \"%s\"", argv[start_arg + 1]); return -1; } if (doit(iff_name, CHELSIO_SET_PM, &pm) < 0) err(1, "pm config"); return 0; } #ifdef CHELSIO_INTERNAL static int dump_tcam(int argc, char *argv[], int start_arg, const char *iff_name) { unsigned int nwords; struct ch_tcam_word op; if (argc != start_arg + 2) return -1; if (get_int_arg(argv[start_arg], &op.addr) || get_int_arg(argv[start_arg + 1], &nwords)) return -1; while (nwords--) { if (doit(iff_name, CHELSIO_READ_TCAM_WORD, &op) < 0) err(1, "tcam dump"); printf("0x%08x: 0x%02x 0x%08x 0x%08x\n", op.addr, op.buf[0] & 0xff, op.buf[1], op.buf[2]); op.addr++; } return 0; } static void hexdump_8b(unsigned int start, uint64_t *data, unsigned int len) { int i; while (len) { printf("0x%08x:", start); for (i = 0; i < 4 && len; ++i, --len) printf(" %016llx", (unsigned long long)*data++); printf("\n"); start += 32; } } static int dump_mc7(int argc, char *argv[], int start_arg, const char *iff_name) { struct ch_mem_range mem; unsigned int mem_id, addr, len; if (argc != start_arg + 3) return -1; if (!strcmp(argv[start_arg], "cm")) mem_id = MEM_CM; else if (!strcmp(argv[start_arg], "rx")) mem_id = MEM_PMRX; else if (!strcmp(argv[start_arg], "tx")) mem_id = MEM_PMTX; else errx(1, "unknown memory \"%s\"; must be one of \"cm\", \"tx\"," " or \"rx\"", argv[start_arg]); if (get_int_arg(argv[start_arg + 1], &addr) || get_int_arg(argv[start_arg + 2], &len)) return -1; mem.buf = malloc(len); if (!mem.buf) err(1, "memory dump"); mem.mem_id = mem_id; mem.addr = addr; mem.len = len; if (doit(iff_name, CHELSIO_GET_MEM, &mem) < 0) err(1, "memory dump"); hexdump_8b(mem.addr, (uint64_t *)mem.buf, mem.len >> 3); free(mem.buf); return 0; } #endif /* Max FW size is 32K including version, +4 bytes for the checksum. */ #define MAX_FW_IMAGE_SIZE (32768 + 4) static int load_fw(int argc, char *argv[], int start_arg, const char *iff_name) { int fd, len; struct ch_mem_range op; const char *fname = argv[start_arg]; if (argc != start_arg + 1) return -1; fd = open(fname, O_RDONLY); if (fd < 0) err(1, "load firmware"); bzero(&op, sizeof(op)); op.buf = malloc(MAX_FW_IMAGE_SIZE + 1); if (!op.buf) err(1, "load firmware"); op.len = read(fd, op.buf, MAX_FW_IMAGE_SIZE + 1); if (op.len < 0) err(1, "load firmware"); if (op.len > MAX_FW_IMAGE_SIZE) errx(1, "FW image too large"); if (doit(iff_name, CHELSIO_LOAD_FW, &op) < 0) err(1, "load firmware"); return 0; } /* Max BOOT size is 255*512 bytes including the BIOS boot ROM basic header */ #define MAX_BOOT_IMAGE_SIZE (0xff * 512) static int load_boot(int argc, char *argv[], int start_arg, const char *iff_name) { int fd, len; struct ch_mem_range op; const char *fname = argv[start_arg]; if (argc != start_arg + 1) return -1; fd = open(fname, O_RDONLY); if (fd < 0) err(1, "load boot image"); op.buf = malloc(MAX_BOOT_IMAGE_SIZE + 1); if (!op.buf) err(1, "load boot image"); len = read(fd, op.buf, MAX_BOOT_IMAGE_SIZE + 1); if (len < 0) err(1, "load boot image"); if (len > MAX_BOOT_IMAGE_SIZE) errx(1, "boot image too large"); op.len = len; if (doit(iff_name, CHELSIO_LOAD_BOOT, &op) < 0) err(1, "load boot image"); return 0; } static int dump_proto_sram(const char *iff_name) { int i, j; uint8_t buf[PROTO_SRAM_SIZE]; struct ch_eeprom ee; uint8_t *p = buf; bzero(buf, sizeof(buf)); ee.offset = PROTO_SRAM_EEPROM_ADDR; ee.data = p; ee.len = sizeof(buf); if (doit(iff_name, CHELSIO_GET_EEPROM, &ee)) err(1, "show protocol sram"); for (i = 0; i < PROTO_SRAM_LINES; i++) { for (j = PROTO_SRAM_LINE_NIBBLES - 1; j >= 0; j--) { int nibble_idx = i * PROTO_SRAM_LINE_NIBBLES + j; uint8_t nibble = p[nibble_idx / 2]; if (nibble_idx & 1) nibble >>= 4; else nibble &= 0xf; printf("%x", nibble); } putchar('\n'); } return 0; } static int proto_sram_op(int argc, char *argv[], int start_arg, const char *iff_name) { if (argc == start_arg) return dump_proto_sram(iff_name); return -1; } static int dump_qset_params(const char *iff_name) { struct ch_qset_params qp; qp.qset_idx = 0; while (doit(iff_name, CHELSIO_GET_QSET_PARAMS, &qp) == 0) { if (!qp.qset_idx) printf("Qset TxQ0 TxQ1 TxQ2 RspQ RxQ0 RxQ1" " Cong Lat IRQ\n"); printf("%4u %6u %6u %6u %6u %6u %6u %5u %4u %5d\n", qp.qnum, qp.txq_size[0], qp.txq_size[1], qp.txq_size[2], qp.rspq_size, qp.fl_size[0], qp.fl_size[1], qp.cong_thres, qp.intr_lat, qp.vector); qp.qset_idx++; } if (!qp.qset_idx || (errno && errno != EINVAL)) err(1, "get qset parameters"); return 0; } static int qset_config(int argc, char *argv[], int start_arg, const char *iff_name) { struct ch_qset_params qp; if (argc == start_arg) return dump_qset_params(iff_name); return -1; } static int qset_num_config(int argc, char *argv[], int start_arg, const char *iff_name) { struct ch_reg reg; if (argc == start_arg) { if (doit(iff_name, CHELSIO_GET_QSET_NUM, ®) < 0) err(1, "get qsets"); printf("%u\n", reg.val); return 0; } return -1; } /* * Parse a string containing an IP address with an optional network prefix. */ static int parse_ipaddr(const char *s, uint32_t *addr, uint32_t *mask) { char *p, *slash; struct in_addr ia; *mask = 0xffffffffU; slash = strchr(s, '/'); if (slash) *slash = 0; if (!inet_aton(s, &ia)) { if (slash) *slash = '/'; *addr = 0; return -1; } *addr = ntohl(ia.s_addr); if (slash) { unsigned int prefix = strtoul(slash + 1, &p, 10); *slash = '/'; if (p == slash + 1 || *p || prefix > 32) return -1; *mask <<= (32 - prefix); } return 0; } /* * Parse a string containing a value and an optional colon separated mask. */ static int parse_val_mask_param(const char *s, uint32_t *val, uint32_t *mask) { char *p; *mask = 0xffffffffU; *val = strtoul(s, &p, 0); if (p == s) return -1; if (*p == ':' && p[1]) *mask = strtoul(p + 1, &p, 0); return *p ? -1 : 0; } static int parse_trace_param(const char *s, uint32_t *val, uint32_t *mask) { return strchr(s, '.') ? parse_ipaddr(s, val, mask) : parse_val_mask_param(s, val, mask); } static int trace_config(int argc, char *argv[], int start_arg, const char *iff_name) { uint32_t val, mask; struct ch_trace trace; if (argc == start_arg) return -1; memset(&trace, 0, sizeof(trace)); if (!strcmp(argv[start_arg], "tx")) trace.config_tx = 1; else if (!strcmp(argv[start_arg], "rx")) trace.config_rx = 1; else if (!strcmp(argv[start_arg], "all")) trace.config_tx = trace.config_rx = 1; else errx(1, "bad trace filter \"%s\"; must be one of \"rx\", " "\"tx\" or \"all\"", argv[start_arg]); if (argc == ++start_arg) return -1; if (!strcmp(argv[start_arg], "on")) { trace.trace_tx = trace.config_tx; trace.trace_rx = trace.config_rx; } else if (strcmp(argv[start_arg], "off")) errx(1, "bad argument \"%s\"; must be \"on\" or \"off\"", argv[start_arg]); start_arg++; if (start_arg < argc && !strcmp(argv[start_arg], "not")) { trace.invert_match = 1; start_arg++; } while (start_arg + 2 <= argc) { int ret = parse_trace_param(argv[start_arg + 1], &val, &mask); if (!strcmp(argv[start_arg], "interface")) { trace.intf = val; trace.intf_mask = mask; } else if (!strcmp(argv[start_arg], "sip")) { trace.sip = val; trace.sip_mask = mask; } else if (!strcmp(argv[start_arg], "dip")) { trace.dip = val; trace.dip_mask = mask; } else if (!strcmp(argv[start_arg], "sport")) { trace.sport = val; trace.sport_mask = mask; } else if (!strcmp(argv[start_arg], "dport")) { trace.dport = val; trace.dport_mask = mask; } else if (!strcmp(argv[start_arg], "vlan")) { trace.vlan = val; trace.vlan_mask = mask; } else if (!strcmp(argv[start_arg], "proto")) { trace.proto = val; trace.proto_mask = mask; } else errx(1, "unknown trace parameter \"%s\"\n" "known parameters are \"interface\", \"sip\", " "\"dip\", \"sport\", \"dport\", \"vlan\", " "\"proto\"", argv[start_arg]); if (ret < 0) errx(1, "bad parameter \"%s\"", argv[start_arg + 1]); start_arg += 2; } if (start_arg != argc) errx(1, "unknown parameter \"%s\"", argv[start_arg]); if (doit(iff_name, CHELSIO_SET_TRACE_FILTER, &trace) < 0) err(1, "trace"); return 0; } static int get_sched_param(int argc, char *argv[], int pos, unsigned int *valp) { if (pos + 1 >= argc) errx(1, "missing value for %s", argv[pos]); if (get_int_arg(argv[pos + 1], valp)) exit(1); return 0; } static int tx_sched(int argc, char *argv[], int start_arg, const char *iff_name) { struct ch_hw_sched op; unsigned int idx, val; if (argc < 5 || get_int_arg(argv[start_arg++], &idx)) return -1; op.sched = idx; op.mode = op.channel = -1; op.kbps = op.class_ipg = op.flow_ipg = -1; while (argc > start_arg) { if (!strcmp(argv[start_arg], "mode")) { if (start_arg + 1 >= argc) errx(1, "missing value for mode"); if (!strcmp(argv[start_arg + 1], "class")) op.mode = 0; else if (!strcmp(argv[start_arg + 1], "flow")) op.mode = 1; else errx(1, "bad mode \"%s\"", argv[start_arg + 1]); } else if (!strcmp(argv[start_arg], "channel") && !get_sched_param(argc, argv, start_arg, &val)) op.channel = val; else if (!strcmp(argv[start_arg], "rate") && !get_sched_param(argc, argv, start_arg, &val)) op.kbps = val; else if (!strcmp(argv[start_arg], "ipg") && !get_sched_param(argc, argv, start_arg, &val)) op.class_ipg = val; else if (!strcmp(argv[start_arg], "flowipg") && !get_sched_param(argc, argv, start_arg, &val)) op.flow_ipg = val; else errx(1, "unknown scheduler parameter \"%s\"", argv[start_arg]); start_arg += 2; } if (doit(iff_name, CHELSIO_SET_HW_SCHED, &op) < 0) err(1, "pktsched"); return 0; } static int pktsched(int argc, char *argv[], int start_arg, const char *iff_name) { struct ch_pktsched_params op; unsigned int idx, min = -1, max, binding = -1; if (argc < 4) errx(1, "no scheduler specified"); if (!strcmp(argv[start_arg], "port")) { if (argc != start_arg + 4) return -1; if (get_int_arg(argv[start_arg + 1], &idx) || get_int_arg(argv[start_arg + 2], &min) || get_int_arg(argv[start_arg + 3], &max)) return -1; op.sched = 0; } else if (!strcmp(argv[start_arg], "tunnelq")) { if (argc != start_arg + 4) return -1; if (get_int_arg(argv[start_arg + 1], &idx) || get_int_arg(argv[start_arg + 2], &max) || get_int_arg(argv[start_arg + 3], &binding)) return -1; op.sched = 1; } else if (!strcmp(argv[start_arg], "tx")) return tx_sched(argc, argv, start_arg + 1, iff_name); else errx(1, "unknown scheduler \"%s\"; must be one of \"port\", " "\"tunnelq\" or \"tx\"", argv[start_arg]); op.idx = idx; op.min = min; op.max = max; op.binding = binding; if (doit(iff_name, CHELSIO_SET_PKTSCHED, &op) < 0) err(1, "pktsched"); return 0; } static int clear_stats(int argc, char *argv[], int start_arg, const char *iff_name) { if (doit(iff_name, CHELSIO_CLEAR_STATS, NULL) < 0) err(1, "clearstats"); return 0; } int main(int argc, char *argv[]) { int r = -1; const char *iff_name; progname = argv[0]; if (argc == 2) { if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) usage(stdout); if (!strcmp(argv[1], "-v") || !strcmp(argv[1], "--version")) { printf("%s version %s\n", PROGNAME, VERSION); printf("%s\n", COPYRIGHT); exit(0); } } if (argc < 3) usage(stderr); iff_name = argv[1]; if (!strcmp(argv[2], "reg")) r = register_io(argc, argv, 3, iff_name); else if (!strcmp(argv[2], "mdio")) r = mdio_io(argc, argv, 3, iff_name); else if (!strcmp(argv[2], "mtus")) r = mtu_tab_op(argc, argv, 3, iff_name); else if (!strcmp(argv[2], "pm")) r = conf_pm(argc, argv, 3, iff_name); else if (!strcmp(argv[2], "regdump")) r = dump_regs(argc, argv, 3, iff_name); else if (!strcmp(argv[2], "tcamdump")) r = dump_tcam(argc, argv, 3, iff_name); else if (!strcmp(argv[2], "memdump")) r = dump_mc7(argc, argv, 3, iff_name); else if (!strcmp(argv[2], "meminfo")) r = meminfo(argc, argv, 3, iff_name); else if (!strcmp(argv[2], "context")) r = get_sge_context(argc, argv, 3, iff_name); else if (!strcmp(argv[2], "desc")) r = get_sge_desc(argc, argv, 3, iff_name); else if (!strcmp(argv[2], "loadfw")) r = load_fw(argc, argv, 3, iff_name); else if (!strcmp(argv[2], "loadboot")) r = load_boot(argc, argv, 3, iff_name); else if (!strcmp(argv[2], "proto")) r = proto_sram_op(argc, argv, 3, iff_name); else if (!strcmp(argv[2], "qset")) r = qset_config(argc, argv, 3, iff_name); else if (!strcmp(argv[2], "qsets")) r = qset_num_config(argc, argv, 3, iff_name); else if (!strcmp(argv[2], "trace")) r = trace_config(argc, argv, 3, iff_name); else if (!strcmp(argv[2], "pktsched")) r = pktsched(argc, argv, 3, iff_name); else if (!strcmp(argv[2], "tcb")) r = get_tcb2(argc, argv, 3, iff_name); else if (!strcmp(argv[2], "clearstats")) r = clear_stats(argc, argv, 3, iff_name); if (r == -1) usage(stderr); return 0; }