14faea7e0STomasz Jeznach /* 24faea7e0STomasz Jeznach * RISC-V IOMMU - Hardware Performance Monitor (HPM) helpers 34faea7e0STomasz Jeznach * 44faea7e0STomasz Jeznach * Copyright (C) 2022-2023 Rivos Inc. 54faea7e0STomasz Jeznach * 64faea7e0STomasz Jeznach * This program is free software; you can redistribute it and/or modify it 74faea7e0STomasz Jeznach * under the terms and conditions of the GNU General Public License, 84faea7e0STomasz Jeznach * version 2 or later, as published by the Free Software Foundation. 94faea7e0STomasz Jeznach * 104faea7e0STomasz Jeznach * This program is distributed in the hope that it will be useful, 114faea7e0STomasz Jeznach * but WITHOUT ANY WARRANTY; without even the implied warranty of 124faea7e0STomasz Jeznach * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 134faea7e0STomasz Jeznach * GNU General Public License for more details. 144faea7e0STomasz Jeznach * 154faea7e0STomasz Jeznach * You should have received a copy of the GNU General Public License along 164faea7e0STomasz Jeznach * with this program; if not, see <http://www.gnu.org/licenses/>. 174faea7e0STomasz Jeznach */ 184faea7e0STomasz Jeznach 194faea7e0STomasz Jeznach #include "qemu/osdep.h" 204faea7e0STomasz Jeznach #include "qemu/timer.h" 214faea7e0STomasz Jeznach #include "cpu_bits.h" 224faea7e0STomasz Jeznach #include "riscv-iommu-hpm.h" 234faea7e0STomasz Jeznach #include "riscv-iommu.h" 244faea7e0STomasz Jeznach #include "riscv-iommu-bits.h" 254faea7e0STomasz Jeznach #include "trace.h" 264faea7e0STomasz Jeznach 274faea7e0STomasz Jeznach /* For now we assume IOMMU HPM frequency to be 1GHz so 1-cycle is of 1-ns. */ 284faea7e0STomasz Jeznach static inline uint64_t get_cycles(void) 294faea7e0STomasz Jeznach { 304faea7e0STomasz Jeznach return qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 314faea7e0STomasz Jeznach } 324faea7e0STomasz Jeznach 334faea7e0STomasz Jeznach uint64_t riscv_iommu_hpmcycle_read(RISCVIOMMUState *s) 344faea7e0STomasz Jeznach { 354faea7e0STomasz Jeznach const uint64_t cycle = riscv_iommu_reg_get64( 364faea7e0STomasz Jeznach s, RISCV_IOMMU_REG_IOHPMCYCLES); 374faea7e0STomasz Jeznach const uint32_t inhibit = riscv_iommu_reg_get32( 384faea7e0STomasz Jeznach s, RISCV_IOMMU_REG_IOCOUNTINH); 394faea7e0STomasz Jeznach const uint64_t ctr_prev = s->hpmcycle_prev; 404faea7e0STomasz Jeznach const uint64_t ctr_val = s->hpmcycle_val; 414faea7e0STomasz Jeznach 424faea7e0STomasz Jeznach if (get_field(inhibit, RISCV_IOMMU_IOCOUNTINH_CY)) { 434faea7e0STomasz Jeznach /* 444faea7e0STomasz Jeznach * Counter should not increment if inhibit bit is set. We can't really 454faea7e0STomasz Jeznach * stop the QEMU_CLOCK_VIRTUAL, so we just return the last updated 464faea7e0STomasz Jeznach * counter value to indicate that counter was not incremented. 474faea7e0STomasz Jeznach */ 484faea7e0STomasz Jeznach return (ctr_val & RISCV_IOMMU_IOHPMCYCLES_COUNTER) | 494faea7e0STomasz Jeznach (cycle & RISCV_IOMMU_IOHPMCYCLES_OVF); 504faea7e0STomasz Jeznach } 514faea7e0STomasz Jeznach 524faea7e0STomasz Jeznach return (ctr_val + get_cycles() - ctr_prev) | 534faea7e0STomasz Jeznach (cycle & RISCV_IOMMU_IOHPMCYCLES_OVF); 544faea7e0STomasz Jeznach } 55*11ecf24cSTomasz Jeznach 56*11ecf24cSTomasz Jeznach static void hpm_incr_ctr(RISCVIOMMUState *s, uint32_t ctr_idx) 57*11ecf24cSTomasz Jeznach { 58*11ecf24cSTomasz Jeznach const uint32_t off = ctr_idx << 3; 59*11ecf24cSTomasz Jeznach uint64_t cntr_val; 60*11ecf24cSTomasz Jeznach 61*11ecf24cSTomasz Jeznach cntr_val = ldq_le_p(&s->regs_rw[RISCV_IOMMU_REG_IOHPMCTR_BASE + off]); 62*11ecf24cSTomasz Jeznach stq_le_p(&s->regs_rw[RISCV_IOMMU_REG_IOHPMCTR_BASE + off], cntr_val + 1); 63*11ecf24cSTomasz Jeznach 64*11ecf24cSTomasz Jeznach /* Handle the overflow scenario. */ 65*11ecf24cSTomasz Jeznach if (cntr_val == UINT64_MAX) { 66*11ecf24cSTomasz Jeznach /* 67*11ecf24cSTomasz Jeznach * Generate interrupt only if OF bit is clear. +1 to offset the cycle 68*11ecf24cSTomasz Jeznach * register OF bit. 69*11ecf24cSTomasz Jeznach */ 70*11ecf24cSTomasz Jeznach const uint32_t ovf = 71*11ecf24cSTomasz Jeznach riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_IOCOUNTOVF, 72*11ecf24cSTomasz Jeznach BIT(ctr_idx + 1), 0); 73*11ecf24cSTomasz Jeznach if (!get_field(ovf, BIT(ctr_idx + 1))) { 74*11ecf24cSTomasz Jeznach riscv_iommu_reg_mod64(s, 75*11ecf24cSTomasz Jeznach RISCV_IOMMU_REG_IOHPMEVT_BASE + off, 76*11ecf24cSTomasz Jeznach RISCV_IOMMU_IOHPMEVT_OF, 77*11ecf24cSTomasz Jeznach 0); 78*11ecf24cSTomasz Jeznach riscv_iommu_notify(s, RISCV_IOMMU_INTR_PM); 79*11ecf24cSTomasz Jeznach } 80*11ecf24cSTomasz Jeznach } 81*11ecf24cSTomasz Jeznach } 82*11ecf24cSTomasz Jeznach 83*11ecf24cSTomasz Jeznach void riscv_iommu_hpm_incr_ctr(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, 84*11ecf24cSTomasz Jeznach unsigned event_id) 85*11ecf24cSTomasz Jeznach { 86*11ecf24cSTomasz Jeznach const uint32_t inhibit = riscv_iommu_reg_get32( 87*11ecf24cSTomasz Jeznach s, RISCV_IOMMU_REG_IOCOUNTINH); 88*11ecf24cSTomasz Jeznach uint32_t did_gscid; 89*11ecf24cSTomasz Jeznach uint32_t pid_pscid; 90*11ecf24cSTomasz Jeznach uint32_t ctr_idx; 91*11ecf24cSTomasz Jeznach gpointer value; 92*11ecf24cSTomasz Jeznach uint32_t ctrs; 93*11ecf24cSTomasz Jeznach uint64_t evt; 94*11ecf24cSTomasz Jeznach 95*11ecf24cSTomasz Jeznach if (!(s->cap & RISCV_IOMMU_CAP_HPM)) { 96*11ecf24cSTomasz Jeznach return; 97*11ecf24cSTomasz Jeznach } 98*11ecf24cSTomasz Jeznach 99*11ecf24cSTomasz Jeznach value = g_hash_table_lookup(s->hpm_event_ctr_map, 100*11ecf24cSTomasz Jeznach GUINT_TO_POINTER(event_id)); 101*11ecf24cSTomasz Jeznach if (value == NULL) { 102*11ecf24cSTomasz Jeznach return; 103*11ecf24cSTomasz Jeznach } 104*11ecf24cSTomasz Jeznach 105*11ecf24cSTomasz Jeznach for (ctrs = GPOINTER_TO_UINT(value); ctrs != 0; ctrs &= ctrs - 1) { 106*11ecf24cSTomasz Jeznach ctr_idx = ctz32(ctrs); 107*11ecf24cSTomasz Jeznach if (get_field(inhibit, BIT(ctr_idx + 1))) { 108*11ecf24cSTomasz Jeznach continue; 109*11ecf24cSTomasz Jeznach } 110*11ecf24cSTomasz Jeznach 111*11ecf24cSTomasz Jeznach evt = riscv_iommu_reg_get64(s, 112*11ecf24cSTomasz Jeznach RISCV_IOMMU_REG_IOHPMEVT_BASE + (ctr_idx << 3)); 113*11ecf24cSTomasz Jeznach 114*11ecf24cSTomasz Jeznach /* 115*11ecf24cSTomasz Jeznach * It's quite possible that event ID has been changed in counter 116*11ecf24cSTomasz Jeznach * but hashtable hasn't been updated yet. We don't want to increment 117*11ecf24cSTomasz Jeznach * counter for the old event ID. 118*11ecf24cSTomasz Jeznach */ 119*11ecf24cSTomasz Jeznach if (event_id != get_field(evt, RISCV_IOMMU_IOHPMEVT_EVENT_ID)) { 120*11ecf24cSTomasz Jeznach continue; 121*11ecf24cSTomasz Jeznach } 122*11ecf24cSTomasz Jeznach 123*11ecf24cSTomasz Jeznach if (get_field(evt, RISCV_IOMMU_IOHPMEVT_IDT)) { 124*11ecf24cSTomasz Jeznach did_gscid = get_field(ctx->gatp, RISCV_IOMMU_DC_IOHGATP_GSCID); 125*11ecf24cSTomasz Jeznach pid_pscid = get_field(ctx->ta, RISCV_IOMMU_DC_TA_PSCID); 126*11ecf24cSTomasz Jeznach } else { 127*11ecf24cSTomasz Jeznach did_gscid = ctx->devid; 128*11ecf24cSTomasz Jeznach pid_pscid = ctx->process_id; 129*11ecf24cSTomasz Jeznach } 130*11ecf24cSTomasz Jeznach 131*11ecf24cSTomasz Jeznach if (get_field(evt, RISCV_IOMMU_IOHPMEVT_PV_PSCV)) { 132*11ecf24cSTomasz Jeznach /* 133*11ecf24cSTomasz Jeznach * If the transaction does not have a valid process_id, counter 134*11ecf24cSTomasz Jeznach * increments if device_id matches DID_GSCID. If the transaction 135*11ecf24cSTomasz Jeznach * has a valid process_id, counter increments if device_id 136*11ecf24cSTomasz Jeznach * matches DID_GSCID and process_id matches PID_PSCID. See 137*11ecf24cSTomasz Jeznach * IOMMU Specification, Chapter 5.23. Performance-monitoring 138*11ecf24cSTomasz Jeznach * event selector. 139*11ecf24cSTomasz Jeznach */ 140*11ecf24cSTomasz Jeznach if (ctx->process_id && 141*11ecf24cSTomasz Jeznach get_field(evt, RISCV_IOMMU_IOHPMEVT_PID_PSCID) != pid_pscid) { 142*11ecf24cSTomasz Jeznach continue; 143*11ecf24cSTomasz Jeznach } 144*11ecf24cSTomasz Jeznach } 145*11ecf24cSTomasz Jeznach 146*11ecf24cSTomasz Jeznach if (get_field(evt, RISCV_IOMMU_IOHPMEVT_DV_GSCV)) { 147*11ecf24cSTomasz Jeznach uint32_t mask = ~0; 148*11ecf24cSTomasz Jeznach 149*11ecf24cSTomasz Jeznach if (get_field(evt, RISCV_IOMMU_IOHPMEVT_DMASK)) { 150*11ecf24cSTomasz Jeznach /* 151*11ecf24cSTomasz Jeznach * 1001 1011 mask = GSCID 152*11ecf24cSTomasz Jeznach * 0000 0111 mask = mask ^ (mask + 1) 153*11ecf24cSTomasz Jeznach * 1111 1000 mask = ~mask; 154*11ecf24cSTomasz Jeznach */ 155*11ecf24cSTomasz Jeznach mask = get_field(evt, RISCV_IOMMU_IOHPMEVT_DID_GSCID); 156*11ecf24cSTomasz Jeznach mask = mask ^ (mask + 1); 157*11ecf24cSTomasz Jeznach mask = ~mask; 158*11ecf24cSTomasz Jeznach } 159*11ecf24cSTomasz Jeznach 160*11ecf24cSTomasz Jeznach if ((get_field(evt, RISCV_IOMMU_IOHPMEVT_DID_GSCID) & mask) != 161*11ecf24cSTomasz Jeznach (did_gscid & mask)) { 162*11ecf24cSTomasz Jeznach continue; 163*11ecf24cSTomasz Jeznach } 164*11ecf24cSTomasz Jeznach } 165*11ecf24cSTomasz Jeznach 166*11ecf24cSTomasz Jeznach hpm_incr_ctr(s, ctr_idx); 167*11ecf24cSTomasz Jeznach } 168*11ecf24cSTomasz Jeznach } 169