From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from lists.ozlabs.org (lists.ozlabs.org [112.213.38.117]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 02335CD6E4A for ; Tue, 2 Jun 2026 10:06:01 +0000 (UTC) Received: from boromir.ozlabs.org (localhost [127.0.0.1]) by lists.ozlabs.org (Postfix) with ESMTP id 4gV60m4jyHz2xmX; Tue, 02 Jun 2026 20:06:00 +1000 (AEST) Authentication-Results: lists.ozlabs.org; arc=none smtp.remote-ip=148.163.156.1 ARC-Seal: i=1; a=rsa-sha256; d=lists.ozlabs.org; s=201707; t=1780382955; cv=none; b=LalmhbbNzR+Pzd9ygaN99NgdNsI4g16sZVbhbaNdbJ3P71ERFDEGeEf3f3i2SUAahF9Fkie8SfbZS6yVN4RPuH/5dpmfhkL2g3CRL56RiuA4abQFq9KrVzuA1ha12Gh4/PIgblYKI5rL88EL0PQECzwFERe6r6fZG+PqgMAcmK4UUh81z/oKu5uNEWsz0dzov9HxHLE1GC/OzEJIQranzvweem9cEAjDd/Rn+ZuYa30W7kNg7jamNRsaIBwQj808ZktbKje0zhpnKMoMe5h6LLZAz+RZZptIY64+JLI4rtXdi+YuXiZjmhpgFeW9iP64GUg+J6ykCXJPKNYGqBGKGg== ARC-Message-Signature: i=1; a=rsa-sha256; d=lists.ozlabs.org; s=201707; t=1780382955; c=relaxed/relaxed; bh=VJ0o9BWZuiVhLFqHcXPNQfgzeljcIMxrMvOXk5DL/fU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=N93HR/VWx4l6xPOGjytdMLs9UdrMpNpDPBxKCVl4Zzha7Z2DcQXmlxgfsFlrZFCX839zJPg8zFbyzyLTHs3P463RyoHFkNBnCXAf4w84yz+Ye9ceOmJQmZgf9cWzGikq9IgFNMylB8vxLO4twOrqOr9AgKtKs74mV49ggE+xRDcDDKiNhsME8f1mjLwSIHwRQOIjgmWzEUYeVafahU/4+YKIwsDedPOXeavix5U6SoT9Le8X0dio20k+vLIVeUBZCqZ0BuGpLydrHU3MQwfPo+WnH4FkdHrgf/Of+oXy7q0QYqh5foiagn0ltBqulZWroGZOeCx3GejiYrVjc6NR9g== ARC-Authentication-Results: i=1; lists.ozlabs.org; dmarc=pass (p=none dis=none) header.from=linux.ibm.com; dkim=pass (2048-bit key; unprotected) header.d=ibm.com header.i=@ibm.com header.a=rsa-sha256 header.s=pp1 header.b=UWbYW2pG; dkim-atps=neutral; spf=pass (client-ip=148.163.156.1; helo=mx0a-001b2d01.pphosted.com; envelope-from=rathc@linux.ibm.com; receiver=lists.ozlabs.org) smtp.mailfrom=linux.ibm.com Authentication-Results: lists.ozlabs.org; dmarc=pass (p=none dis=none) header.from=linux.ibm.com Authentication-Results: lists.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=ibm.com header.i=@ibm.com header.a=rsa-sha256 header.s=pp1 header.b=UWbYW2pG; dkim-atps=neutral Authentication-Results: lists.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=linux.ibm.com (client-ip=148.163.156.1; helo=mx0a-001b2d01.pphosted.com; envelope-from=rathc@linux.ibm.com; receiver=lists.ozlabs.org) Received: from mx0a-001b2d01.pphosted.com (mx0a-001b2d01.pphosted.com [148.163.156.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 4gV1dk0bG4z2xmX for ; Tue, 02 Jun 2026 16:49:13 +1000 (AEST) Received: from pps.filterd (m0360083.ppops.net [127.0.0.1]) by mx0a-001b2d01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 6526UAsd1648854; Tue, 2 Jun 2026 06:48:57 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ibm.com; h=cc :content-transfer-encoding:date:from:in-reply-to:message-id :mime-version:references:subject:to; s=pp1; bh=VJ0o9BWZuiVhLFqHc XPNQfgzeljcIMxrMvOXk5DL/fU=; b=UWbYW2pGySkRiywxZDo0EBcbh3USwaPmC lvp04lHpBPbjEwpkhv3FMgITnlGvPZmOQet5FGpO+DL4/k6dMV/Fptx9KfPS39lH vFbeYE4FBAxK5z6OUnzNZ1bQb+aTpF5kxihhLaPQxohPQOObNHppvn0k+nu1o6ky X0XxbsjTVvqDaajRzSXQX6+GuJq0/IqK4L8g470o2CX/1Shc5kDXFWpy797dJUvP VOda8eYT6I3eFphcppln2Upg4vmtdOeZ2gEOm/AEVc6B3xzFfrVI5m2mj4mQGOKO TeIhkjU/aSoj5bLwWjniUPDvpCJKISqUnDIBwtMnVwIyU/vBOgrzw== Received: from ppma23.wdc07v.mail.ibm.com (5d.69.3da9.ip4.static.sl-reverse.com [169.61.105.93]) by mx0a-001b2d01.pphosted.com (PPS) with ESMTPS id 4efqd44n9g-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Tue, 02 Jun 2026 06:48:57 +0000 (GMT) Received: from pps.filterd (ppma23.wdc07v.mail.ibm.com [127.0.0.1]) by ppma23.wdc07v.mail.ibm.com (8.18.1.7/8.18.1.7) with ESMTP id 6526dEeP030045; Tue, 2 Jun 2026 06:48:55 GMT Received: from smtprelay07.fra02v.mail.ibm.com ([9.218.2.229]) by ppma23.wdc07v.mail.ibm.com (PPS) with ESMTPS id 4egbqh9v87-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Tue, 02 Jun 2026 06:48:55 +0000 (GMT) Received: from smtpav07.fra02v.mail.ibm.com (smtpav07.fra02v.mail.ibm.com [10.20.54.106]) by smtprelay07.fra02v.mail.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id 6526mqmf47120728 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Tue, 2 Jun 2026 06:48:52 GMT Received: from smtpav07.fra02v.mail.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 00F2F20043; Tue, 2 Jun 2026 06:48:52 +0000 (GMT) Received: from smtpav07.fra02v.mail.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 6C83B20040; Tue, 2 Jun 2026 06:48:49 +0000 (GMT) Received: from li-18a0a34c-33fc-11b2-a85c-d9f1631c5692.bl1-in.ibm.com (unknown [9.123.8.229]) by smtpav07.fra02v.mail.ibm.com (Postfix) with ESMTP; Tue, 2 Jun 2026 06:48:49 +0000 (GMT) From: Chinmay Rath To: thuth@redhat.com Cc: npiggin@gmail.com, harshpb@linux.ibm.com, lvivier@redhat.com, linuxppc-dev@lists.ozlabs.org, kvm@vger.kernel.org, andrew.jones@linux.dev, sbhat@linux.ibm.com, Chinmay Rath Subject: [kvm-unit-tests RFC PATCH 1/6] powerpc: add pmu tests Date: Tue, 2 Jun 2026 12:18:01 +0530 Message-ID: <20260602064806.3101025-2-rathc@linux.ibm.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260602064806.3101025-1-rathc@linux.ibm.com> References: <20260602064806.3101025-1-rathc@linux.ibm.com> X-Mailing-List: linuxppc-dev@lists.ozlabs.org List-Id: List-Help: List-Owner: List-Post: List-Archive: , List-Subscribe: , , List-Unsubscribe: Precedence: list MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-TM-AS-GCONF: 00 X-Proofpoint-Reinject: loops=2 maxloops=12 X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwNjAyMDA1OSBTYWx0ZWRfX3TW+6db5Hjg2 RRwSinF2wmVDwXzj+E49HgawlTRZG4lRcxO2/l/tycUcByzbWIvvqUM8LcoP0CbmMZ1O6a4Xudo mFOvcn+aFTBorY2I5LfDJxEfgnZ65SnSyH8qXZzDOlP/52Ux8Kw0AmakpVlO6AJYnfvvr9pAWl5 NZJhRapOfPJYSBefwmSemaREktGpMOsgb95mhDURPCaP47wDtgnVaVAsJfkFQx3u4GVWbXAez6d sViGHT54PrKDpDe6rNVNSFZxU1hEmMv91pl/vZph2Nn8Z+Mkf25Owh3LhrDVS0p0xc8T8PlsAT1 e855DFEz6Y1W8QNnfUGZ19Y9HQGsOGd1qwAjlgBEbwe3iuLJtS/tRj5MVh8KfsHnO0m2b0d+PgO ui37xh4aLExlsAYLbpnQFsJ1rnN1/vNaHswaTlZHz8ZZ6h7L4Q0GsyDHbwlTKid/Yq3Yqrf9ZAV Uw6H3C8Mm1A9c4r9EKw== X-Proofpoint-GUID: BziqPL4XyabErD3BmAC-VX0gxL-UuusP X-Proofpoint-ORIG-GUID: 0Ot1Bsd7GUrUaxToFadRsaFCMAbeD50z X-Authority-Analysis: v=2.4 cv=DZknbPtW c=1 sm=1 tr=0 ts=6a1e7cd9 cx=c_pps a=3Bg1Hr4SwmMryq2xdFQyZA==:117 a=3Bg1Hr4SwmMryq2xdFQyZA==:17 a=FelO9ux0wxsA:10 a=VkNPw1HP01LnGYTKEx00:22 a=RnoormkPH1_aCDwRdu11:22 a=iQ6ETzBq9ecOQQE5vZCe:22 a=pGLkceISAAAA:8 a=VnNF1IyMAAAA:8 a=Sis57A-BupVIILBV42sA:9 X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1143,Hydra:6.1.125,FMLib:17.12.100.49 definitions=2026-06-01_07,2026-05-28_03,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 spamscore=0 clxscore=1015 suspectscore=0 impostorscore=0 lowpriorityscore=0 phishscore=0 malwarescore=0 priorityscore=1501 bulkscore=0 adultscore=0 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2605210000 definitions=main-2606020059 From: Nicholas Piggin Add some initial PMU testing. - PMC5/6 tests - PMAE / PMI test - BHRB basic tests Signed-off-by: Nicholas Piggin Signed-off-by: Chinmay Rath --- lib/powerpc/asm/processor.h | 2 + lib/powerpc/asm/reg.h | 9 + lib/powerpc/asm/setup.h | 1 + lib/powerpc/setup.c | 20 ++ powerpc/Makefile.common | 3 +- powerpc/pmu.c | 567 ++++++++++++++++++++++++++++++++++++ powerpc/unittests.cfg | 3 + 7 files changed, 604 insertions(+), 1 deletion(-) create mode 100644 powerpc/pmu.c diff --git a/lib/powerpc/asm/processor.h b/lib/powerpc/asm/processor.h index 153126fe..08506438 100644 --- a/lib/powerpc/asm/processor.h +++ b/lib/powerpc/asm/processor.h @@ -17,6 +17,8 @@ extern bool cpu_has_hv; extern bool cpu_has_power_mce; extern bool cpu_has_siar; extern bool cpu_has_heai; +extern bool cpu_has_bhrb; +extern bool cpu_has_p10_bhrb; extern bool cpu_has_radix; extern bool cpu_has_prefix; extern bool cpu_has_sc_lev; diff --git a/lib/powerpc/asm/reg.h b/lib/powerpc/asm/reg.h index 69ef21ad..602fba1b 100644 --- a/lib/powerpc/asm/reg.h +++ b/lib/powerpc/asm/reg.h @@ -40,10 +40,19 @@ #define SPR_LPIDR 0x13f #define SPR_HEIR 0x153 #define SPR_PTCR 0x1d0 +#define SPR_MMCRA 0x312 +#define MMCRA_BHRBRD UL(0x0000002000000000) +#define MMCRA_IFM_MASK UL(0x00000000c0000000) +#define SPR_PMC5 0x317 +#define SPR_PMC6 0x318 #define SPR_MMCR0 0x31b #define MMCR0_FC UL(0x80000000) +#define MMCR0_FCP UL(0x20000000) #define MMCR0_PMAE UL(0x04000000) +#define MMCR0_BHRBA UL(0x00200000) +#define MMCR0_FCPC UL(0x00001000) #define MMCR0_PMAO UL(0x00000080) +#define MMCR0_FC56 UL(0x00000010) #define SPR_SIAR 0x31c /* Machine State Register definitions: */ diff --git a/lib/powerpc/asm/setup.h b/lib/powerpc/asm/setup.h index 9ca318ce..8f0b58ed 100644 --- a/lib/powerpc/asm/setup.h +++ b/lib/powerpc/asm/setup.h @@ -10,6 +10,7 @@ #define NR_CPUS 8 /* arbitrarily set for now */ extern uint64_t tb_hz; +extern uint64_t cpu_hz; #define NR_MEM_REGIONS 8 #define MR_F_PRIMARY (1U << 0) diff --git a/lib/powerpc/setup.c b/lib/powerpc/setup.c index c1f0f9ad..ef4ebdbc 100644 --- a/lib/powerpc/setup.c +++ b/lib/powerpc/setup.c @@ -33,6 +33,7 @@ u32 initrd_size; u32 cpu_to_hwid[NR_CPUS] = { [0 ... NR_CPUS-1] = (~0U) }; int nr_cpus_present; uint64_t tb_hz; +uint64_t cpu_hz; struct mem_region mem_regions[NR_MEM_REGIONS]; phys_addr_t __physical_start, __physical_end; @@ -42,6 +43,7 @@ struct cpu_set_params { unsigned icache_bytes; unsigned dcache_bytes; uint64_t tb_hz; + uint64_t cpu_hz; }; static void cpu_set(int fdtnode, u64 regval, void *info) @@ -95,6 +97,19 @@ static void cpu_set(int fdtnode, u64 regval, void *info) data = (u32 *)prop->data; params->tb_hz = fdt32_to_cpu(*data); + prop = fdt_get_property(dt_fdt(), fdtnode, + "ibm,extended-clock-frequency", NULL); + if (prop) { + u64 *data64 = (u64 *)prop->data; + params->cpu_hz = fdt64_to_cpu(*data64); + } else { + prop = fdt_get_property(dt_fdt(), fdtnode, + "clock-frequency", NULL); + assert(prop != NULL); + data = (u32 *)prop->data; + params->cpu_hz = fdt32_to_cpu(*data); + } + read_common_info = true; } } @@ -103,6 +118,8 @@ bool cpu_has_hv; bool cpu_has_power_mce; /* POWER CPU machine checks */ bool cpu_has_siar; bool cpu_has_heai; +bool cpu_has_bhrb; +bool cpu_has_p10_bhrb; bool cpu_has_radix; bool cpu_has_prefix; bool cpu_has_sc_lev; /* sc interrupt has LEV field in SRR1 */ @@ -119,12 +136,14 @@ static void cpu_init_params(void) __icache_bytes = params.icache_bytes; __dcache_bytes = params.dcache_bytes; tb_hz = params.tb_hz; + cpu_hz = params.cpu_hz; switch (mfspr(SPR_PVR) & PVR_VERSION_MASK) { case PVR_VER_POWER10: cpu_has_prefix = true; cpu_has_sc_lev = true; cpu_has_pause_short = true; + cpu_has_p10_bhrb = true; case PVR_VER_POWER9: cpu_has_radix = true; case PVR_VER_POWER8E: @@ -133,6 +152,7 @@ static void cpu_init_params(void) cpu_has_power_mce = true; cpu_has_heai = true; cpu_has_siar = true; + cpu_has_bhrb = true; break; default: break; diff --git a/powerpc/Makefile.common b/powerpc/Makefile.common index db4a34f2..3b357982 100644 --- a/powerpc/Makefile.common +++ b/powerpc/Makefile.common @@ -18,7 +18,8 @@ tests-common = \ $(TEST_DIR)/sprs.elf \ $(TEST_DIR)/timebase.elf \ $(TEST_DIR)/interrupts.elf \ - $(TEST_DIR)/mmu.elf + $(TEST_DIR)/mmu.elf \ + $(TEST_DIR)/pmu.elf tests-all = $(tests-common) $(tests) all: directories $(TEST_DIR)/boot_rom.bin $(tests-all) diff --git a/powerpc/pmu.c b/powerpc/pmu.c new file mode 100644 index 00000000..402ce569 --- /dev/null +++ b/powerpc/pmu.c @@ -0,0 +1,567 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Test PMU + * + * Copyright 2024 Nicholas Piggin, IBM Corp. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "alloc_phys.h" +#include "vmalloc.h" + +static volatile bool got_interrupt; +static volatile struct pt_regs recorded_regs; +static volatile unsigned long recorded_mmcr0; + +static void illegal_handler(struct pt_regs *regs, void *data) +{ + got_interrupt = true; + regs_advance_insn(regs); +} + +static void fault_handler(struct pt_regs *regs, void *data) +{ + got_interrupt = true; + regs_advance_insn(regs); +} + +static void sc_handler(struct pt_regs *regs, void *data) +{ + got_interrupt = true; +} + +static void reset_mmcr0(void) +{ + mtspr(SPR_MMCR0, mfspr(SPR_MMCR0) | (MMCR0_FC | MMCR0_FC56)); + mtspr(SPR_MMCR0, mfspr(SPR_MMCR0) & ~(MMCR0_PMAE | MMCR0_PMAO)); +} + +static __attribute__((__noinline__)) unsigned long pmc5_count_nr_insns(unsigned long nr) +{ + reset_mmcr0(); + mtspr(SPR_PMC5, 0); + mtspr(SPR_MMCR0, mfspr(SPR_MMCR0) & ~(MMCR0_FC | MMCR0_FC56)); + asm volatile("mtctr %0 ; 1: bdnz 1b" :: "r"(nr) : "ctr"); + mtspr(SPR_MMCR0, mfspr(SPR_MMCR0) | (MMCR0_FC | MMCR0_FC56)); + + return mfspr(SPR_PMC5); +} + +static void test_pmc5(void) +{ + unsigned long pmc5; + unsigned long mmcr; + + reset_mmcr0(); + mmcr = mfspr(SPR_MMCR0); + mtspr(SPR_PMC5, 0); + mtspr(SPR_MMCR0, mmcr & ~(MMCR0_FC | MMCR0_FC56)); + asm volatile(".rep 20 ; nop ; .endr" ::: "memory"); + mtspr(SPR_MMCR0, mmcr); + pmc5 = mfspr(SPR_PMC5); + + report_kfail(true, pmc5 == 21, "PMC5 counts instructions exactly %ld", pmc5); +} + +static void test_pmc5_with_branch(void) +{ + unsigned long pmc5; + unsigned long mmcr; + + reset_mmcr0(); + mmcr = mfspr(SPR_MMCR0); + mtspr(SPR_PMC5, 0); + mtspr(SPR_MMCR0, mmcr & ~(MMCR0_FC | MMCR0_FC56)); + asm volatile(".rep 20 ; b $+4 ; .endr" ::: "memory"); + mtspr(SPR_MMCR0, mmcr); + pmc5 = mfspr(SPR_PMC5); + + /* TCG and POWER9 do not count instructions around faults correctly */ + report_kfail(true, pmc5 == 21, "PMC5 counts instructions with branch %ld", pmc5); +} + +static void test_pmc5_with_cond_branch(void) +{ + unsigned long pmc5; + unsigned long mmcr; + + reset_mmcr0(); + mmcr = mfspr(SPR_MMCR0); + mtspr(SPR_PMC5, 0); + mtspr(SPR_MMCR0, mmcr & ~(MMCR0_FC | MMCR0_FC56)); + asm volatile(".rep 10 ; nop ; .endr ; cmpdi %0,1 ; beq 1f ; .rep 10 ; nop ; .endr ; 1:" : : "r"(0) : "memory", "cr0"); + mtspr(SPR_MMCR0, mmcr); + pmc5 = mfspr(SPR_PMC5); + + /* TCG and POWER9 do not count instructions around faults correctly */ + report_kfail(true, pmc5 == 24, + "PMC5 counts instructions wth conditional branch %ld", pmc5); +} + +static void test_pmc5_with_ill(void) +{ + unsigned long pmc5_1, pmc5_2; + + handle_exception(0x700, &illegal_handler, NULL); + handle_exception(0xe40, &illegal_handler, NULL); + + reset_mmcr0(); + mtspr(SPR_PMC5, 0); + mtspr(SPR_MMCR0, mfspr(SPR_MMCR0) & ~(MMCR0_FC | MMCR0_FC56)); + asm volatile(".long 0x0" ::: "memory"); + mtspr(SPR_MMCR0, mfspr(SPR_MMCR0) | (MMCR0_FC | MMCR0_FC56)); + assert(got_interrupt); + got_interrupt = false; + pmc5_1 = mfspr(SPR_PMC5); + + reset_mmcr0(); + mtspr(SPR_PMC5, 0); + mtspr(SPR_MMCR0, mfspr(SPR_MMCR0) & ~(MMCR0_FC | MMCR0_FC56)); + asm volatile(".rep 10 ; nop ; .endr ; .long 0x0 ; .rep 10 ; nop ; .endr " ::: "memory"); + mtspr(SPR_MMCR0, mfspr(SPR_MMCR0) | (MMCR0_FC | MMCR0_FC56)); + assert(got_interrupt); + got_interrupt = false; + pmc5_2 = mfspr(SPR_PMC5); + + /* TCG and POWER9 do not count instructions around faults correctly */ + report_kfail(true, pmc5_1 + 20 == pmc5_2, + "PMC5 counts instructions with illegal instruction"); + + handle_exception(0x700, NULL, NULL); + handle_exception(0xe40, NULL, NULL); +} + +static void test_pmc5_with_fault(void) +{ + unsigned long pmc5_1, pmc5_2; + unsigned long tmp; + + setup_vm(); + + handle_exception(0x300, &fault_handler, NULL); + handle_exception(0x380, &fault_handler, NULL); + + reset_mmcr0(); + mtspr(SPR_PMC5, 0); + mtspr(SPR_MMCR0, mfspr(SPR_MMCR0) & ~(MMCR0_FC | MMCR0_FC56)); + asm volatile("ld %0,0(%1)" : "=r"(tmp) : "r"(NULL) : "memory"); + mtspr(SPR_MMCR0, mfspr(SPR_MMCR0) | (MMCR0_FC | MMCR0_FC56)); + assert(got_interrupt); + got_interrupt = false; + pmc5_1 = mfspr(SPR_PMC5); + + reset_mmcr0(); + mtspr(SPR_PMC5, 0); + mtspr(SPR_MMCR0, mfspr(SPR_MMCR0) & ~(MMCR0_FC | MMCR0_FC56)); + asm volatile(".rep 10 ; nop ; .endr ; ld %0,0(%1) ; .rep 10 ; nop ; .endr " : "=r"(tmp) : "r"(NULL) : "memory"); + mtspr(SPR_MMCR0, mfspr(SPR_MMCR0) | (MMCR0_FC | MMCR0_FC56)); + assert(got_interrupt); + got_interrupt = false; + pmc5_2 = mfspr(SPR_PMC5); + + /* TCG and POWER9 do not count instructions around faults correctly */ + report_kfail(true, pmc5_1 + 20 == pmc5_2, "PMC5 counts instructions with fault"); + + handle_exception(0x300, NULL, NULL); + handle_exception(0x380, NULL, NULL); +} + +static void test_pmc5_with_sc(void) +{ + unsigned long pmc5_1, pmc5_2; + + handle_exception(0xc00, &sc_handler, NULL); + + reset_mmcr0(); + mtspr(SPR_PMC5, 0); + mtspr(SPR_MMCR0, mfspr(SPR_MMCR0) & ~(MMCR0_FC | MMCR0_FC56)); + asm volatile("sc 0" ::: "memory"); + mtspr(SPR_MMCR0, mfspr(SPR_MMCR0) | (MMCR0_FC | MMCR0_FC56)); + assert(got_interrupt); + got_interrupt = false; + pmc5_1 = mfspr(SPR_PMC5); + + reset_mmcr0(); + mtspr(SPR_PMC5, 0); + mtspr(SPR_MMCR0, mfspr(SPR_MMCR0) & ~(MMCR0_FC | MMCR0_FC56)); + asm volatile(".rep 10 ; nop ; .endr ; sc 0 ; .rep 10 ; nop ; .endr" ::: "memory"); + mtspr(SPR_MMCR0, mfspr(SPR_MMCR0) | (MMCR0_FC | MMCR0_FC56)); + assert(got_interrupt); + got_interrupt = false; + pmc5_2 = mfspr(SPR_PMC5); + + /* TCG does not count instructions around syscalls correctly */ + report_kfail(host_is_tcg, pmc5_1 + 20 == pmc5_2, + "PMC5 counts instructions with syscall"); + + handle_exception(0xc00, NULL, NULL); +} + +extern char next_insn[]; + +static void test_pmc5_with_rfid(void) +{ + unsigned long pmc5; + unsigned long mmcr; + + mtspr(SPR_SRR0, (unsigned long)next_insn); + mtspr(SPR_SRR1, mfmsr()); + reset_mmcr0(); + mmcr = mfspr(SPR_MMCR0); + mtspr(SPR_PMC5, 0); + mtspr(SPR_MMCR0, mmcr & ~(MMCR0_FC | MMCR0_FC56)); + asm volatile("rfid ; trap ; .global next_insn ; next_insn: " ::: "memory"); + mtspr(SPR_MMCR0, mmcr); + pmc5 = mfspr(SPR_PMC5); + + /* TCG does not count instructions around syscalls correctly */ + report_kfail(host_is_tcg, pmc5 == 2, + "PMC5 counts instructions with rfid %ld", pmc5); +} + +static void test_pmc5_with_ldat(void) +{ + unsigned long pmc5_1, pmc5_2; + register unsigned long r4 asm("r4"); + register unsigned long r5 asm("r5"); + register unsigned long r6 asm("r6"); + uint64_t val; + + reset_mmcr0(); + mtspr(SPR_PMC5, 0); + mtspr(SPR_MMCR0, mfspr(SPR_MMCR0) & ~(MMCR0_FC | MMCR0_FC56)); + asm volatile(".rep 20 ; nop ; .endr" ::: "memory"); + mtspr(SPR_MMCR0, mfspr(SPR_MMCR0) | (MMCR0_FC | MMCR0_FC56)); + pmc5_1 = mfspr(SPR_PMC5); + + val = 0xdeadbeef; + r4 = 0; + r5 = 0xdeadbeef; + r6 = 100; + reset_mmcr0(); + mtspr(SPR_PMC5, 0); + mtspr(SPR_MMCR0, mfspr(SPR_MMCR0) & ~(MMCR0_FC | MMCR0_FC56)); + asm volatile(".rep 10 ; nop ; .endr ; " + "ldat %0,%3,0x10 ; " + ".rep 10 ; nop ; .endr" + : "=r"(r4), "+r"(r5), "+r"(r6) + : "r"(&val) + : "memory"); + mtspr(SPR_MMCR0, mfspr(SPR_MMCR0) | (MMCR0_FC | MMCR0_FC56)); + pmc5_2 = mfspr(SPR_PMC5); + assert(r4 == 0xdeadbeef); + assert(val == 0xdeadbeef); + + /* TCG does not count instructions around syscalls correctly */ + report_kfail(host_is_tcg, pmc5_1 != pmc5_2 + 1, + "PMC5 counts instructions with ldat"); +} + +static void test_pmc56(void) +{ + unsigned long tmp; + + report_prefix_push("pmc56"); + + reset_mmcr0(); + mtspr(SPR_PMC5, 0); + mtspr(SPR_PMC6, 0); + report(mfspr(SPR_PMC5) == 0, "PMC5 zeroed"); + report(mfspr(SPR_PMC6) == 0, "PMC6 zeroed"); + mtspr(SPR_MMCR0, mfspr(SPR_MMCR0) & ~MMCR0_FC); + msleep(100); + report(mfspr(SPR_PMC5) == 0, "PMC5 frozen"); + report(mfspr(SPR_PMC6) == 0, "PMC6 frozen"); + mtspr(SPR_MMCR0, mfspr(SPR_MMCR0) & ~MMCR0_FC56); + mdelay(100); + mtspr(SPR_MMCR0, mfspr(SPR_MMCR0) | (MMCR0_FC | MMCR0_FC56)); + report(mfspr(SPR_PMC5) != 0, "PMC5 counting"); + report(mfspr(SPR_PMC6) != 0, "PMC6 counting"); + + /* Dynamic frequency scaling could cause to be out, so don't fail. */ + tmp = mfspr(SPR_PMC6); + report(true, "PMC6 ratio to reported clock frequency is %ld%%", + tmp * 1000 / cpu_hz); + + tmp = pmc5_count_nr_insns(100); + tmp = pmc5_count_nr_insns(1000) - tmp; + report(tmp == 900, "PMC5 counts instructions precisely %ld", tmp); + + test_pmc5(); + test_pmc5_with_branch(); + test_pmc5_with_cond_branch(); + test_pmc5_with_ill(); + test_pmc5_with_fault(); + test_pmc5_with_sc(); + test_pmc5_with_rfid(); + test_pmc5_with_ldat(); + + report_prefix_pop(); +} + +static void dec_ignore_handler(struct pt_regs *regs, void *data) +{ + mtspr(SPR_DEC, 0x7fffffff); +} + +static void pmi_handler(struct pt_regs *regs, void *data) +{ + got_interrupt = true; + memcpy((void *)&recorded_regs, regs, sizeof(struct pt_regs)); + recorded_mmcr0 = mfspr(SPR_MMCR0); + if (mfspr(SPR_MMCR0) & MMCR0_PMAO) { + /* This may cause infinite interrupts, so clear it. */ + mtspr(SPR_MMCR0, mfspr(SPR_MMCR0) & ~MMCR0_PMAO); + } +} + +static void test_pmi(void) +{ + report_prefix_push("pmi"); + handle_exception(0x900, &dec_ignore_handler, NULL); + handle_exception(0xf00, &pmi_handler, NULL); + reset_mmcr0(); + mtspr(SPR_MMCR0, mfspr(SPR_MMCR0) | MMCR0_PMAO); + mtmsr(mfmsr() | MSR_EE); + mtmsr(mfmsr() & ~MSR_EE); + report(got_interrupt, "PMAO caused interrupt"); + got_interrupt = false; + handle_exception(0xf00, NULL, NULL); + handle_exception(0x900, NULL, NULL); + report_prefix_pop(); +} + +static void clrbhrb(void) +{ + asm volatile("clrbhrb" ::: "memory"); +} + +static inline unsigned long mfbhrbe(int nr) +{ + unsigned long e; + + asm volatile("mfbhrbe %0,%1" : "=r"(e) : "i"(nr) : "memory"); + + return e; +} + +extern unsigned char dummy_branch_1[]; +extern unsigned char dummy_branch_2[]; + +static __attribute__((__noinline__)) void bhrb_dummy(int i) +{ + asm volatile( + " cmpdi %0,1 \n\t" + " beq 1f \n\t" + ".global dummy_branch_1 \n\t" + "dummy_branch_1: \n\t" + " b 2f \n\t" + "1: trap \n\t" + ".global dummy_branch_2 \n\t" + "dummy_branch_2: \n\t" + "2: bne 3f \n\t" + " trap \n\t" + "3: nop \n\t" + : : "r"(i)); +} + +#define NR_BHRBE 16 +static unsigned long bhrbe[NR_BHRBE]; +static int nr_bhrbe; + +static void run_and_load_bhrb(void) +{ + int i; + + mtspr(SPR_MMCR0, mfspr(SPR_MMCR0) & ~MMCR0_PMAE); + clrbhrb(); + mtspr(SPR_MMCR0, mfspr(SPR_MMCR0) | MMCR0_BHRBA); + mtspr(SPR_MMCR0, mfspr(SPR_MMCR0) & ~(MMCR0_FC | MMCR0_FCP | MMCR0_FCPC)); + mtspr(SPR_MMCRA, mfspr(SPR_MMCRA) & ~(MMCRA_BHRBRD | MMCRA_IFM_MASK)); + + if (cpu_has_p10_bhrb) { + mtspr(SPR_MMCR0, mfspr(SPR_MMCR0) | MMCR0_PMAE); + asm volatile("isync" ::: "memory"); + enter_usermode(); + bhrb_dummy(0); + exit_usermode(); + mtspr(SPR_MMCR0, mfspr(SPR_MMCR0) & ~MMCR0_PMAE); + asm volatile("isync" ::: "memory"); + } else { + mtspr(SPR_MMCR0, mfspr(SPR_MMCR0) | MMCR0_PMAE); + asm volatile("isync" ::: "memory"); + mtmsr(mfmsr()); + asm volatile(".rept 100 ; nop ; .endr"); + bhrb_dummy(0); + mtspr(SPR_MMCR0, mfspr(SPR_MMCR0) & ~MMCR0_PMAE); + asm volatile("isync" ::: "memory"); + } + + bhrbe[0] = mfbhrbe(0); + bhrbe[1] = mfbhrbe(1); + bhrbe[2] = mfbhrbe(2); + bhrbe[3] = mfbhrbe(3); + bhrbe[4] = mfbhrbe(4); + bhrbe[5] = mfbhrbe(5); + bhrbe[6] = mfbhrbe(6); + bhrbe[7] = mfbhrbe(7); + bhrbe[8] = mfbhrbe(8); + bhrbe[9] = mfbhrbe(9); + bhrbe[10] = mfbhrbe(10); + bhrbe[11] = mfbhrbe(11); + bhrbe[12] = mfbhrbe(12); + bhrbe[13] = mfbhrbe(13); + bhrbe[14] = mfbhrbe(14); + bhrbe[15] = mfbhrbe(15); + + for (i = 0; i < NR_BHRBE; i++) { + bhrbe[i] &= ~0x1UL; /* remove prediction bit */ + if (!bhrbe[i]) + break; + } + nr_bhrbe = i; +} + +static void test_bhrb(void) +{ + int i; + + if (cpu_has_p10_bhrb && !vm_available()) + return; + + report_prefix_push("bhrb"); + + /* TCG doesn't impelment BHRB yet */ + handle_exception(0x700, &illegal_handler, NULL); + handle_exception(0xe40, &illegal_handler, NULL); + clrbhrb(); + handle_exception(0x700, NULL, NULL); + handle_exception(0xe40, NULL, NULL); + if (got_interrupt) { + got_interrupt = false; + report_skip("BHRB support missing"); + report_prefix_pop(); + return; + } + + if (vm_available()) { + handle_exception(0x900, &dec_ignore_handler, NULL); + setup_vm(); + } + reset_mmcr0(); + clrbhrb(); + if (cpu_has_p10_bhrb) { + enter_usermode(); + bhrb_dummy(0); + exit_usermode(); + } else { + bhrb_dummy(0); + } + report(mfbhrbe(0) == 0, "BHRB is frozen"); + + /* + * BHRB may be cleared at any time (e.g., by OS or hypervisor) + * so this test could be occasionally incorrect. Try several + * times before giving up... + */ + + if (cpu_has_p10_bhrb) { + /* + * BHRB should have 8 entries: + * 1. enter_usermode blr + * 2. enter_usermode blr target + * 3. bl dummy + * 4. dummy unconditional + * 5. dummy conditional + * 6. dummy blr + * 7. dummy blr target + * 8. exit_usermode bl + * + * POWER10 often gives 4 entries, if other threads are + * running on the core, it seems to struggle. + */ + for (i = 0; i < 200; i++) { + run_and_load_bhrb(); + if (nr_bhrbe == 8) + break; + if (i > 100 && nr_bhrbe == 4) + break; + } + report(nr_bhrbe, "BHRB has been written"); + report_kfail(!host_is_tcg, nr_bhrbe == 8, + "BHRB has written 8 entries"); + if (nr_bhrbe == 8) { + report(bhrbe[4] == (unsigned long)dummy_branch_1, + "correct unconditional branch address"); + report(bhrbe[3] == (unsigned long)dummy_branch_2, + "correct conditional branch address"); + } else if (nr_bhrbe == 4) { + /* POWER10 workaround */ + report(nr_bhrbe == 4, "BHRB has written 4 entries"); + report(bhrbe[3] == (unsigned long)dummy_branch_2, + "correct conditional branch address"); + } + } else { + /* + * BHRB should have 6 entries: + * 1. bl dummy + * 2. dummy unconditional + * 3. dummy conditional + * 4. dummy blr + * 5. dummy blr target + * 6. Final b loop before disabled. + * + * POWER9 often gives 4 entries, if other threads are + * running on the core, it seems to struggle. + */ + for (i = 0; i < 200; i++) { + run_and_load_bhrb(); + if (nr_bhrbe == 6) + break; + if (i > 100 && nr_bhrbe == 4) + break; + } + report(nr_bhrbe, "BHRB has been written"); + report_kfail(!host_is_tcg, nr_bhrbe == 6, + "BHRB has written 6 entries"); + if (nr_bhrbe == 6) { + report(bhrbe[4] == (unsigned long)dummy_branch_1, + "correct unconditional branch address"); + report(bhrbe[3] == (unsigned long)dummy_branch_2, + "correct conditional branch address"); + } else if (nr_bhrbe == 4) { + /* POWER9 workaround */ + report(nr_bhrbe == 4, "BHRB has written 4 entries"); + report(bhrbe[3] == (unsigned long)dummy_branch_2, + "correct conditional branch address"); + } + } + + handle_exception(0x900, NULL, NULL); + + report_prefix_pop(); +} + +int main(int argc, char **argv) +{ + report_prefix_push("pmu"); + + test_pmc56(); + test_pmi(); + if (cpu_has_bhrb) + test_bhrb(); + + report_prefix_pop(); + + return report_summary(); +} diff --git a/powerpc/unittests.cfg b/powerpc/unittests.cfg index 2dd32edf..60c73086 100644 --- a/powerpc/unittests.cfg +++ b/powerpc/unittests.cfg @@ -75,6 +75,9 @@ file = interrupts.elf file = mmu.elf smp = 2 +[pmu] +file = pmu.elf + [smp] file = smp.elf smp = 2 -- 2.53.0