/* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * This file is part of the Chelsio T4 support code. * * Copyright (C) 2011-2013 Chelsio Communications. All rights reserved. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the LICENSE file included in this * release for licensing terms and conditions. */ #include #include #include #include "t4nex.h" #include "common/common.h" #include "common/t4_regs.h" /* helpers */ static int pci_rw(struct adapter *sc, void *data, int flags, int write); static int reg_rw(struct adapter *sc, void *data, int flags, int write); static void reg_block_dump(struct adapter *sc, uint8_t *buf, unsigned int start, unsigned int end); static int regdump(struct adapter *sc, void *data, int flags); static int get_sge_context(struct adapter *sc, void *data, int flags); static int get_devlog(struct adapter *sc, void *data, int flags); static int read_card_mem(struct adapter *sc, void *data, int flags); static int read_tid_tab(struct adapter *sc, void *data, int flags); static int read_mbox(struct adapter *sc, void *data, int flags); static int read_cim_la(struct adapter *sc, void *data, int flags); static int read_cim_qcfg(struct adapter *sc, void *data, int flags); static int read_cim_ibq(struct adapter *sc, void *data, int flags); static int read_edc(struct adapter *sc, void *data, int flags); int t4_ioctl(struct adapter *sc, int cmd, void *data, int mode) { int rc = ENOTSUP; switch (cmd) { case T4_IOCTL_PCIGET32: case T4_IOCTL_PCIPUT32: rc = pci_rw(sc, data, mode, cmd == T4_IOCTL_PCIPUT32); break; case T4_IOCTL_GET32: case T4_IOCTL_PUT32: rc = reg_rw(sc, data, mode, cmd == T4_IOCTL_PUT32); break; case T4_IOCTL_REGDUMP: rc = regdump(sc, data, mode); break; case T4_IOCTL_SGE_CONTEXT: rc = get_sge_context(sc, data, mode); break; case T4_IOCTL_DEVLOG: rc = get_devlog(sc, data, mode); break; case T4_IOCTL_GET_MEM: rc = read_card_mem(sc, data, mode); break; case T4_IOCTL_GET_TID_TAB: rc = read_tid_tab(sc, data, mode); break; case T4_IOCTL_GET_MBOX: rc = read_mbox(sc, data, mode); break; case T4_IOCTL_GET_CIM_LA: rc = read_cim_la(sc, data, mode); break; case T4_IOCTL_GET_CIM_QCFG: rc = read_cim_qcfg(sc, data, mode); break; case T4_IOCTL_GET_CIM_IBQ: rc = read_cim_ibq(sc, data, mode); break; case T4_IOCTL_GET_EDC: rc = read_edc(sc, data, mode); break; default: return (EINVAL); } return (rc); } static int pci_rw(struct adapter *sc, void *data, int flags, int write) { struct t4_reg32_cmd r; if (ddi_copyin(data, &r, sizeof (r), flags) < 0) return (EFAULT); /* address must be 32 bit aligned */ r.reg &= ~0x3; if (write != 0) t4_os_pci_write_cfg4(sc, r.reg, r.value); else { t4_os_pci_read_cfg4(sc, r.reg, &r.value); if (ddi_copyout(&r, data, sizeof (r), flags) < 0) return (EFAULT); } return (0); } static int reg_rw(struct adapter *sc, void *data, int flags, int write) { struct t4_reg32_cmd r; if (ddi_copyin(data, &r, sizeof (r), flags) < 0) return (EFAULT); /* Register address must be 32 bit aligned */ r.reg &= ~0x3; if (write != 0) t4_write_reg(sc, r.reg, r.value); else { r.value = t4_read_reg(sc, r.reg); if (ddi_copyout(&r, data, sizeof (r), flags) < 0) return (EFAULT); } return (0); } static void reg_block_dump(struct adapter *sc, uint8_t *buf, unsigned int start, unsigned int end) { /* LINTED: E_BAD_PTR_CAST_ALIGN */ uint32_t *p = (uint32_t *)(buf + start); for (/* */; start <= end; start += sizeof (uint32_t)) *p++ = t4_read_reg(sc, start); } static int regdump(struct adapter *sc, void *data, int flags) { struct t4_regdump r; uint8_t *buf; int rc = 0, i; static const unsigned int reg_ranges[] = { 0x1008, 0x1108, 0x1180, 0x11b4, 0x11fc, 0x123c, 0x1300, 0x173c, 0x1800, 0x18fc, 0x3000, 0x30d8, 0x30e0, 0x5924, 0x5960, 0x59d4, 0x5a00, 0x5af8, 0x6000, 0x6098, 0x6100, 0x6150, 0x6200, 0x6208, 0x6240, 0x6248, 0x6280, 0x6338, 0x6370, 0x638c, 0x6400, 0x643c, 0x6500, 0x6524, 0x6a00, 0x6a38, 0x6a60, 0x6a78, 0x6b00, 0x6b84, 0x6bf0, 0x6c84, 0x6cf0, 0x6d84, 0x6df0, 0x6e84, 0x6ef0, 0x6f84, 0x6ff0, 0x7084, 0x70f0, 0x7184, 0x71f0, 0x7284, 0x72f0, 0x7384, 0x73f0, 0x7450, 0x7500, 0x7530, 0x7600, 0x761c, 0x7680, 0x76cc, 0x7700, 0x7798, 0x77c0, 0x77fc, 0x7900, 0x79fc, 0x7b00, 0x7c38, 0x7d00, 0x7efc, 0x8dc0, 0x8e1c, 0x8e30, 0x8e78, 0x8ea0, 0x8f6c, 0x8fc0, 0x9074, 0x90fc, 0x90fc, 0x9400, 0x9458, 0x9600, 0x96bc, 0x9800, 0x9808, 0x9820, 0x983c, 0x9850, 0x9864, 0x9c00, 0x9c6c, 0x9c80, 0x9cec, 0x9d00, 0x9d6c, 0x9d80, 0x9dec, 0x9e00, 0x9e6c, 0x9e80, 0x9eec, 0x9f00, 0x9f6c, 0x9f80, 0x9fec, 0xd004, 0xd03c, 0xdfc0, 0xdfe0, 0xe000, 0xea7c, 0xf000, 0x11190, 0x19040, 0x19124, 0x19150, 0x191b0, 0x191d0, 0x191e8, 0x19238, 0x1924c, 0x193f8, 0x19474, 0x19490, 0x194f8, 0x19800, 0x19f30, 0x1a000, 0x1a06c, 0x1a0b0, 0x1a120, 0x1a128, 0x1a138, 0x1a190, 0x1a1c4, 0x1a1fc, 0x1a1fc, 0x1e040, 0x1e04c, 0x1e240, 0x1e28c, 0x1e2c0, 0x1e2c0, 0x1e2e0, 0x1e2e0, 0x1e300, 0x1e384, 0x1e3c0, 0x1e3c8, 0x1e440, 0x1e44c, 0x1e640, 0x1e68c, 0x1e6c0, 0x1e6c0, 0x1e6e0, 0x1e6e0, 0x1e700, 0x1e784, 0x1e7c0, 0x1e7c8, 0x1e840, 0x1e84c, 0x1ea40, 0x1ea8c, 0x1eac0, 0x1eac0, 0x1eae0, 0x1eae0, 0x1eb00, 0x1eb84, 0x1ebc0, 0x1ebc8, 0x1ec40, 0x1ec4c, 0x1ee40, 0x1ee8c, 0x1eec0, 0x1eec0, 0x1eee0, 0x1eee0, 0x1ef00, 0x1ef84, 0x1efc0, 0x1efc8, 0x1f040, 0x1f04c, 0x1f240, 0x1f28c, 0x1f2c0, 0x1f2c0, 0x1f2e0, 0x1f2e0, 0x1f300, 0x1f384, 0x1f3c0, 0x1f3c8, 0x1f440, 0x1f44c, 0x1f640, 0x1f68c, 0x1f6c0, 0x1f6c0, 0x1f6e0, 0x1f6e0, 0x1f700, 0x1f784, 0x1f7c0, 0x1f7c8, 0x1f840, 0x1f84c, 0x1fa40, 0x1fa8c, 0x1fac0, 0x1fac0, 0x1fae0, 0x1fae0, 0x1fb00, 0x1fb84, 0x1fbc0, 0x1fbc8, 0x1fc40, 0x1fc4c, 0x1fe40, 0x1fe8c, 0x1fec0, 0x1fec0, 0x1fee0, 0x1fee0, 0x1ff00, 0x1ff84, 0x1ffc0, 0x1ffc8, 0x20000, 0x2002c, 0x20100, 0x2013c, 0x20190, 0x201c8, 0x20200, 0x20318, 0x20400, 0x20528, 0x20540, 0x20614, 0x21000, 0x21040, 0x2104c, 0x21060, 0x210c0, 0x210ec, 0x21200, 0x21268, 0x21270, 0x21284, 0x212fc, 0x21388, 0x21400, 0x21404, 0x21500, 0x21518, 0x2152c, 0x2153c, 0x21550, 0x21554, 0x21600, 0x21600, 0x21608, 0x21628, 0x21630, 0x2163c, 0x21700, 0x2171c, 0x21780, 0x2178c, 0x21800, 0x21c38, 0x21c80, 0x21d7c, 0x21e00, 0x21e04, 0x22000, 0x2202c, 0x22100, 0x2213c, 0x22190, 0x221c8, 0x22200, 0x22318, 0x22400, 0x22528, 0x22540, 0x22614, 0x23000, 0x23040, 0x2304c, 0x23060, 0x230c0, 0x230ec, 0x23200, 0x23268, 0x23270, 0x23284, 0x232fc, 0x23388, 0x23400, 0x23404, 0x23500, 0x23518, 0x2352c, 0x2353c, 0x23550, 0x23554, 0x23600, 0x23600, 0x23608, 0x23628, 0x23630, 0x2363c, 0x23700, 0x2371c, 0x23780, 0x2378c, 0x23800, 0x23c38, 0x23c80, 0x23d7c, 0x23e00, 0x23e04, 0x24000, 0x2402c, 0x24100, 0x2413c, 0x24190, 0x241c8, 0x24200, 0x24318, 0x24400, 0x24528, 0x24540, 0x24614, 0x25000, 0x25040, 0x2504c, 0x25060, 0x250c0, 0x250ec, 0x25200, 0x25268, 0x25270, 0x25284, 0x252fc, 0x25388, 0x25400, 0x25404, 0x25500, 0x25518, 0x2552c, 0x2553c, 0x25550, 0x25554, 0x25600, 0x25600, 0x25608, 0x25628, 0x25630, 0x2563c, 0x25700, 0x2571c, 0x25780, 0x2578c, 0x25800, 0x25c38, 0x25c80, 0x25d7c, 0x25e00, 0x25e04, 0x26000, 0x2602c, 0x26100, 0x2613c, 0x26190, 0x261c8, 0x26200, 0x26318, 0x26400, 0x26528, 0x26540, 0x26614, 0x27000, 0x27040, 0x2704c, 0x27060, 0x270c0, 0x270ec, 0x27200, 0x27268, 0x27270, 0x27284, 0x272fc, 0x27388, 0x27400, 0x27404, 0x27500, 0x27518, 0x2752c, 0x2753c, 0x27550, 0x27554, 0x27600, 0x27600, 0x27608, 0x27628, 0x27630, 0x2763c, 0x27700, 0x2771c, 0x27780, 0x2778c, 0x27800, 0x27c38, 0x27c80, 0x27d7c, 0x27e00, 0x27e04 }; if (ddi_copyin(data, &r, sizeof (r), flags) < 0) return (EFAULT); if (r.len > T4_REGDUMP_SIZE) r.len = T4_REGDUMP_SIZE; else if (r.len < T4_REGDUMP_SIZE) return (E2BIG); buf = kmem_zalloc(T4_REGDUMP_SIZE, KM_SLEEP); r.version = 4 | (sc->params.rev << 10); for (i = 0; i < ARRAY_SIZE(reg_ranges); i += 2) reg_block_dump(sc, buf, reg_ranges[i], reg_ranges[i + 1]); if (ddi_copyout(buf, r.data, r.len, flags) < 0) rc = EFAULT; if (rc == 0 && ddi_copyout(&r, data, sizeof (r), flags) < 0) rc = EFAULT; kmem_free(buf, T4_REGDUMP_SIZE); return (rc); } static int get_sge_context(struct adapter *sc, void *data, int flags) { struct t4_sge_context sgec; uint32_t buff[SGE_CTXT_SIZE / 4]; int rc = 0; if (ddi_copyin(data, &sgec, sizeof (sgec), flags) < 0) { rc = EFAULT; goto _exit; } if (sgec.len < SGE_CTXT_SIZE || sgec.addr > M_CTXTQID) { rc = EINVAL; goto _exit; } if ((sgec.mem_id != T4_CTXT_EGRESS) && (sgec.mem_id != T4_CTXT_FLM) && (sgec.mem_id != T4_CTXT_INGRESS)) { rc = EINVAL; goto _exit; } rc = (sc->flags & FW_OK) ? -t4_sge_ctxt_rd(sc, sc->mbox, sgec.addr, sgec.mem_id, buff) : -t4_sge_ctxt_rd_bd(sc, sgec.addr, sgec.mem_id, buff); if (rc != 0) goto _exit; sgec.version = 4 | (sc->params.rev << 10); /* copyout data and then t4_sge_context */ rc = ddi_copyout(buff, sgec.data, sgec.len, flags); if (rc == 0) rc = ddi_copyout(&sgec, data, sizeof (sgec), flags); /* if ddi_copyout fails, return EFAULT - for either of the two */ if (rc != 0) rc = EFAULT; _exit: return (rc); } static int read_tid_tab(struct adapter *sc, void *data, int flags) { struct t4_tid_info t4tid; uint32_t *buf, *b; struct tid_info *t = &sc->tids; int rc = 0; if (ddi_copyin(data, &t4tid, sizeof (t4tid), flags) < 0) { rc = EFAULT; goto _exit; } buf = b = kmem_zalloc(t4tid.len, KM_NOSLEEP); if (buf == NULL) { rc = ENOMEM; goto _exit; } *b++ = t->tids_in_use; *b++ = t->atids_in_use; *b = t->stids_in_use; if (ddi_copyout(buf, t4tid.data, t4tid.len, flags) < 0) rc = EFAULT; kmem_free(buf, t4tid.len); _exit: return (rc); } static int read_card_mem(struct adapter *sc, void *data, int flags) { struct t4_mem_range mr; uint32_t base, size, lo, hi, win, off, remaining, i, n; uint32_t *buf, *b; int rc = 0; if (ddi_copyin(data, &mr, sizeof (mr), flags) < 0) { rc = EFAULT; goto _exit; } /* reads are in multiples of 32 bits */ if (mr.addr & 3 || mr.len & 3 || mr.len == 0) { rc = EINVAL; goto _exit; } /* * We don't want to deal with potential holes so we mandate that the * requested region must lie entirely within one of the 3 memories. */ lo = t4_read_reg(sc, A_MA_TARGET_MEM_ENABLE); if (lo & F_EDRAM0_ENABLE) { hi = t4_read_reg(sc, A_MA_EDRAM0_BAR); base = G_EDRAM0_BASE(hi) << 20; size = G_EDRAM0_SIZE(hi) << 20; if (size > 0 && mr.addr >= base && mr.addr < base + size && mr.addr + mr.len <= base + size) goto proceed; } if (lo & F_EDRAM1_ENABLE) { hi = t4_read_reg(sc, A_MA_EDRAM1_BAR); base = G_EDRAM1_BASE(hi) << 20; size = G_EDRAM1_SIZE(hi) << 20; if (size > 0 && mr.addr >= base && mr.addr < base + size && mr.addr + mr.len <= base + size) goto proceed; } if (lo & F_EXT_MEM_ENABLE) { hi = t4_read_reg(sc, A_MA_EXT_MEMORY_BAR); base = G_EXT_MEM_BASE(hi) << 20; size = G_EXT_MEM_SIZE(hi) << 20; if (size > 0 && mr.addr >= base && mr.addr < base + size && mr.addr + mr.len <= base + size) goto proceed; } return (ENXIO); proceed: buf = b = kmem_zalloc(mr.len, KM_NOSLEEP); if (buf == NULL) { rc = ENOMEM; goto _exit; } /* * Position the PCIe window (we use memwin2) to the 16B aligned area * just at/before the requested region. */ win = mr.addr & ~0xf; off = mr.addr - win; /* offset of the requested region in the win */ remaining = mr.len; while (remaining) { t4_write_reg(sc, PCIE_MEM_ACCESS_REG(A_PCIE_MEM_ACCESS_OFFSET, 2), win); (void) t4_read_reg(sc, PCIE_MEM_ACCESS_REG(A_PCIE_MEM_ACCESS_OFFSET, 2)); /* number of bytes that we'll copy in the inner loop */ n = min(remaining, MEMWIN2_APERTURE - off); for (i = 0; i < n; i += 4, remaining -= 4) *b++ = t4_read_reg(sc, MEMWIN2_BASE + off + i); win += MEMWIN2_APERTURE; off = 0; } if (ddi_copyout(buf, mr.data, mr.len, flags) < 0) rc = EFAULT; kmem_free(buf, mr.len); _exit: return (rc); } static int get_devlog(struct adapter *sc, void *data, int flags) { struct devlog_params *dparams = &sc->params.devlog; struct fw_devlog_e *buf; struct t4_devlog dl; int rc = 0; if (ddi_copyin(data, &dl, sizeof (dl), flags) < 0) { rc = EFAULT; goto done; } if (dparams->start == 0) { rc = ENXIO; goto done; } if (dl.len < dparams->size) { dl.len = dparams->size; rc = ddi_copyout(&dl, data, sizeof (dl), flags); /* * rc = 0 indicates copyout was successful, then return ENOBUFS * to indicate that the buffer size was not enough. Return of * EFAULT indicates that the copyout was not successful. */ rc = (rc == 0) ? ENOBUFS : EFAULT; goto done; } buf = kmem_zalloc(dparams->size, KM_NOSLEEP); if (buf == NULL) { rc = ENOMEM; goto done; } rc = -t4_mem_read(sc, dparams->memtype, dparams->start, dparams->size, (void *)buf); if (rc != 0) goto done1; /* Copyout device log buffer and then carrier buffer */ if (ddi_copyout(buf, dl.data, dl.len, flags) < 0) rc = EFAULT; else if (ddi_copyout(&dl, data, sizeof (dl), flags) < 0) rc = EFAULT; done1: kmem_free(buf, dparams->size); done: return (rc); } static int read_cim_qcfg(struct adapter *sc, void *data, int flags) { struct t4_cim_qcfg t4cimqcfg; int rc = 0; if (ddi_copyin(data, &t4cimqcfg, sizeof (t4cimqcfg), flags) < 0) { rc = EFAULT; goto _exit; } rc = t4_cim_read(sc, A_UP_IBQ_0_RDADDR, ARRAY_SIZE(t4cimqcfg.stat), t4cimqcfg.stat); if (rc != 0) return (rc); t4_read_cimq_cfg(sc, t4cimqcfg.base, t4cimqcfg.size, t4cimqcfg.thres); if (ddi_copyout(&t4cimqcfg, data, sizeof (t4cimqcfg), flags) < 0) rc = EFAULT; _exit: return (rc); } static int read_edc(struct adapter *sc, void *data, int flags) { struct t4_edc t4edc; int rc = 0; u32 count, pos = 0; u32 memoffset; __be32 *edc = NULL; if (ddi_copyin(data, &t4edc, sizeof (t4edc), flags) < 0) { rc = EFAULT; goto _exit; } if (t4edc.mem > 2) goto _exit; edc = kmem_zalloc(t4edc.len, KM_NOSLEEP); if (edc == NULL) { rc = ENOMEM; goto _exit; } /* * Offset into the region of memory which is being accessed * MEM_EDC0 = 0 * MEM_EDC1 = 1 * MEM_MC = 2 */ memoffset = (t4edc.mem * (5 * 1024 * 1024)); count = t4edc.len; pos = t4edc.pos; while (count) { u32 len; rc = t4_mem_win_read(sc, (pos + memoffset), edc); if (rc != 0) { kmem_free(edc, t4edc.len); goto _exit; } len = MEMWIN0_APERTURE; pos += len; count -= len; } if (ddi_copyout(edc, t4edc.data, t4edc.len, flags) < 0) rc = EFAULT; kmem_free(edc, t4edc.len); _exit: return (rc); } static int read_cim_ibq(struct adapter *sc, void *data, int flags) { struct t4_ibq t4ibq; int rc = 0; __be64 *buf; if (ddi_copyin(data, &t4ibq, sizeof (t4ibq), flags) < 0) { rc = EFAULT; goto _exit; } buf = kmem_zalloc(t4ibq.len, KM_NOSLEEP); if (buf == NULL) { rc = ENOMEM; goto _exit; } rc = t4_read_cim_ibq(sc, 3, (u32 *)buf, CIM_IBQ_SIZE * 4); if (rc < 0) { kmem_free(buf, t4ibq.len); return (rc); } else rc = 0; if (ddi_copyout(buf, t4ibq.data, t4ibq.len, flags) < 0) rc = EFAULT; kmem_free(buf, t4ibq.len); _exit: return (rc); } static int read_cim_la(struct adapter *sc, void *data, int flags) { struct t4_cim_la t4cimla; int rc = 0; unsigned int cfg; __be64 *buf; rc = t4_cim_read(sc, A_UP_UP_DBG_LA_CFG, 1, &cfg); if (rc != 0) return (rc); if (ddi_copyin(data, &t4cimla, sizeof (t4cimla), flags) < 0) { rc = EFAULT; goto _exit; } buf = kmem_zalloc(t4cimla.len, KM_NOSLEEP); if (buf == NULL) { rc = ENOMEM; goto _exit; } rc = t4_cim_read_la(sc, (u32 *)buf, NULL); if (rc != 0) { kmem_free(buf, t4cimla.len); return (rc); } if (ddi_copyout(buf, t4cimla.data, t4cimla.len, flags) < 0) rc = EFAULT; kmem_free(buf, t4cimla.len); _exit: return (rc); } static int read_mbox(struct adapter *sc, void *data, int flags) { struct t4_mbox t4mbox; int rc = 0, i; __be64 *p, *buf; u32 data_reg = PF_REG(4, A_CIM_PF_MAILBOX_DATA); if (ddi_copyin(data, &t4mbox, sizeof (t4mbox), flags) < 0) { rc = EFAULT; goto _exit; } buf = p = kmem_zalloc(t4mbox.len, KM_NOSLEEP); if (buf == NULL) { rc = ENOMEM; goto _exit; } for (i = 0; i < t4mbox.len; i += 8, p++) *p = t4_read_reg64(sc, data_reg + i); if (ddi_copyout(buf, t4mbox.data, t4mbox.len, flags) < 0) rc = EFAULT; kmem_free(buf, t4mbox.len); _exit: return (rc); }