diff --git a/include/sbi/sbi_tlb.h b/include/sbi/sbi_tlb.h
index 48f1962d7dcf..680fcc758072 100644
--- a/include/sbi/sbi_tlb.h
+++ b/include/sbi/sbi_tlb.h
@@ -33,6 +33,7 @@ struct sbi_tlb_info {
 	struct sbi_hartmask smask;
 };
 
+void sbi_tlb_pmu_incr_fw_ctr(struct sbi_tlb_info *data);
 void sbi_tlb_local_hfence_vvma(struct sbi_tlb_info *tinfo);
 void sbi_tlb_local_hfence_gvma(struct sbi_tlb_info *tinfo);
 void sbi_tlb_local_sfence_vma(struct sbi_tlb_info *tinfo);
diff --git a/lib/sbi/sbi_ecall_replace.c b/lib/sbi/sbi_ecall_replace.c
index a7935d97300d..83544bb6e05f 100644
--- a/lib/sbi/sbi_ecall_replace.c
+++ b/lib/sbi/sbi_ecall_replace.c
@@ -14,6 +14,7 @@
 #include <sbi/sbi_error.h>
 #include <sbi/sbi_hart.h>
 #include <sbi/sbi_ipi.h>
+#include <sbi/sbi_pmu.h>
 #include <sbi/sbi_system.h>
 #include <sbi/sbi_timer.h>
 #include <sbi/sbi_tlb.h>
@@ -27,6 +28,7 @@ static int sbi_ecall_time_handler(unsigned long extid, unsigned long funcid,
 	int ret = 0;
 
 	if (funcid == SBI_EXT_TIME_SET_TIMER) {
+		sbi_pmu_incr_fw_ctr(SBI_PMU_FW_SET_TIMER);
 #if __riscv_xlen == 32
 		sbi_timer_event_start((((u64)regs->a1 << 32) | (u64)regs->a0));
 #else
diff --git a/lib/sbi/sbi_ipi.c b/lib/sbi/sbi_ipi.c
index b50735e4099a..cd998ac7acfa 100644
--- a/lib/sbi/sbi_ipi.c
+++ b/lib/sbi/sbi_ipi.c
@@ -19,6 +19,9 @@
 #include <sbi/sbi_init.h>
 #include <sbi/sbi_ipi.h>
 #include <sbi/sbi_platform.h>
+#include <sbi/sbi_pmu.h>
+#include <sbi/sbi_string.h>
+#include <sbi/sbi_tlb.h>
 
 struct sbi_ipi_data {
 	unsigned long ipi_type;
@@ -61,6 +64,10 @@ static int sbi_ipi_send(struct sbi_scratch *scratch, u32 remote_hartid,
 	 */
 	atomic_raw_set_bit(event, &ipi_data->ipi_type);
 	smp_wmb();
+	sbi_pmu_incr_fw_ctr(SBI_PMU_FW_IPI_SENT);
+	if (sbi_strncmp(ipi_ops->name, "IPI_TLB", 7))
+		sbi_tlb_pmu_incr_fw_ctr(data);
+
 	sbi_platform_ipi_send(plat, remote_hartid);
 
 	if (ipi_ops->sync)
diff --git a/lib/sbi/sbi_tlb.c b/lib/sbi/sbi_tlb.c
index 73f59e8681e2..36b21a74a713 100644
--- a/lib/sbi/sbi_tlb.c
+++ b/lib/sbi/sbi_tlb.c
@@ -21,6 +21,7 @@
 #include <sbi/sbi_string.h>
 #include <sbi/sbi_console.h>
 #include <sbi/sbi_platform.h>
+#include <sbi/sbi_pmu.h>
 
 static unsigned long tlb_sync_off;
 static unsigned long tlb_fifo_off;
@@ -39,6 +40,8 @@ void sbi_tlb_local_hfence_vvma(struct sbi_tlb_info *tinfo)
 	unsigned long vmid  = tinfo->vmid;
 	unsigned long i, hgatp;
 
+	sbi_pmu_incr_fw_ctr(SBI_PMU_FW_HFENCE_VVMA_RCVD);
+
 	hgatp = csr_swap(CSR_HGATP,
 			 (vmid << HGATP_VMID_SHIFT) & HGATP_VMID_MASK);
 
@@ -61,6 +64,8 @@ void sbi_tlb_local_hfence_gvma(struct sbi_tlb_info *tinfo)
 	unsigned long size  = tinfo->size;
 	unsigned long i;
 
+	sbi_pmu_incr_fw_ctr(SBI_PMU_FW_HFENCE_GVMA_RCVD);
+
 	if ((start == 0 && size == 0) || (size == SBI_TLB_FLUSH_ALL)) {
 		__sbi_hfence_gvma_all();
 		return;
@@ -77,6 +82,8 @@ void sbi_tlb_local_sfence_vma(struct sbi_tlb_info *tinfo)
 	unsigned long size  = tinfo->size;
 	unsigned long i;
 
+	sbi_pmu_incr_fw_ctr(SBI_PMU_FW_SFENCE_VMA_RCVD);
+
 	if ((start == 0 && size == 0) || (size == SBI_TLB_FLUSH_ALL)) {
 		sbi_tlb_flush_all();
 		return;
@@ -98,6 +105,8 @@ void sbi_tlb_local_hfence_vvma_asid(struct sbi_tlb_info *tinfo)
 	unsigned long vmid  = tinfo->vmid;
 	unsigned long i, hgatp;
 
+	sbi_pmu_incr_fw_ctr(SBI_PMU_FW_HFENCE_VVMA_ASID_RCVD);
+
 	hgatp = csr_swap(CSR_HGATP,
 			 (vmid << HGATP_VMID_SHIFT) & HGATP_VMID_MASK);
 
@@ -126,6 +135,8 @@ void sbi_tlb_local_hfence_gvma_vmid(struct sbi_tlb_info *tinfo)
 	unsigned long vmid  = tinfo->vmid;
 	unsigned long i;
 
+	sbi_pmu_incr_fw_ctr(SBI_PMU_FW_HFENCE_GVMA_VMID_RCVD);
+
 	if (start == 0 && size == 0) {
 		__sbi_hfence_gvma_all();
 		return;
@@ -148,6 +159,8 @@ void sbi_tlb_local_sfence_vma_asid(struct sbi_tlb_info *tinfo)
 	unsigned long asid  = tinfo->asid;
 	unsigned long i;
 
+	sbi_pmu_incr_fw_ctr(SBI_PMU_FW_SFENCE_VMA_ASID_RCVD);
+
 	if (start == 0 && size == 0) {
 		sbi_tlb_flush_all();
 		return;
@@ -172,9 +185,32 @@ void sbi_tlb_local_sfence_vma_asid(struct sbi_tlb_info *tinfo)
 
 void sbi_tlb_local_fence_i(struct sbi_tlb_info *tinfo)
 {
+	sbi_pmu_incr_fw_ctr(SBI_PMU_FW_FENCE_I_RECVD);
+
 	__asm__ __volatile("fence.i");
 }
 
+void sbi_tlb_pmu_incr_fw_ctr(struct sbi_tlb_info *data)
+{
+	if (unlikely(!data))
+		return;
+
+	if (data->local_fn == sbi_tlb_local_fence_i)
+		sbi_pmu_incr_fw_ctr(SBI_PMU_FW_FENCE_I_SENT);
+	else if (data->local_fn == sbi_tlb_local_sfence_vma)
+		sbi_pmu_incr_fw_ctr(SBI_PMU_FW_SFENCE_VMA_SENT);
+	else if (data->local_fn == sbi_tlb_local_sfence_vma_asid)
+		sbi_pmu_incr_fw_ctr(SBI_PMU_FW_SFENCE_VMA_ASID_SENT);
+	else if (data->local_fn == sbi_tlb_local_hfence_gvma)
+		sbi_pmu_incr_fw_ctr(SBI_PMU_FW_HFENCE_GVMA_SENT);
+	else if (data->local_fn == sbi_tlb_local_hfence_gvma_vmid)
+		sbi_pmu_incr_fw_ctr(SBI_PMU_FW_HFENCE_GVMA_VMID_SENT);
+	else if (data->local_fn == sbi_tlb_local_hfence_vvma)
+		sbi_pmu_incr_fw_ctr(SBI_PMU_FW_HFENCE_VVMA_SENT);
+	else if (data->local_fn == sbi_tlb_local_hfence_vvma_asid)
+		sbi_pmu_incr_fw_ctr(SBI_PMU_FW_HFENCE_VVMA_ASID_SENT);
+}
+
 static void sbi_tlb_entry_process(struct sbi_tlb_info *tinfo)
 {
 	u32 rhartid;
diff --git a/lib/sbi/sbi_trap.c b/lib/sbi/sbi_trap.c
index b7349d2c914e..3b540ea7f76c 100644
--- a/lib/sbi/sbi_trap.c
+++ b/lib/sbi/sbi_trap.c
@@ -16,6 +16,7 @@
 #include <sbi/sbi_illegal_insn.h>
 #include <sbi/sbi_ipi.h>
 #include <sbi/sbi_misaligned_ldst.h>
+#include <sbi/sbi_pmu.h>
 #include <sbi/sbi_scratch.h>
 #include <sbi/sbi_timer.h>
 #include <sbi/sbi_trap.h>
@@ -230,6 +231,7 @@ void sbi_trap_handler(struct sbi_trap_regs *regs)
 			sbi_timer_process();
 			break;
 		case IRQ_M_SOFT:
+			sbi_pmu_incr_fw_ctr(SBI_PMU_FW_IPI_RECVD);
 			sbi_ipi_process();
 			break;
 		default:
@@ -241,14 +243,17 @@ void sbi_trap_handler(struct sbi_trap_regs *regs)
 
 	switch (mcause) {
 	case CAUSE_ILLEGAL_INSTRUCTION:
+		sbi_pmu_incr_fw_ctr(SBI_PMU_FW_ILLEGAL_INSN);
 		rc  = sbi_illegal_insn_handler(mtval, regs);
 		msg = "illegal instruction handler failed";
 		break;
 	case CAUSE_MISALIGNED_LOAD:
+		sbi_pmu_incr_fw_ctr(SBI_PMU_FW_MISALIGNED_LOAD);
 		rc = sbi_misaligned_load_handler(mtval, mtval2, mtinst, regs);
 		msg = "misaligned load handler failed";
 		break;
 	case CAUSE_MISALIGNED_STORE:
+		sbi_pmu_incr_fw_ctr(SBI_PMU_FW_MISALIGNED_STORE);
 		rc  = sbi_misaligned_store_handler(mtval, mtval2, mtinst, regs);
 		msg = "misaligned store handler failed";
 		break;
@@ -257,6 +262,10 @@ void sbi_trap_handler(struct sbi_trap_regs *regs)
 		rc  = sbi_ecall_handler(regs);
 		msg = "ecall handler failed";
 		break;
+	case CAUSE_LOAD_ACCESS:
+		sbi_pmu_incr_fw_ctr(SBI_PMU_FW_ACCESS_LOAD);
+	case CAUSE_STORE_ACCESS:
+		sbi_pmu_incr_fw_ctr(SBI_PMU_FW_ACCESS_STORE);
 	default:
 		/* If the trap came from S or U mode, redirect it there */
 		trap.epc = regs->mepc;
