From patchwork Tue Mar 22 17:23:16 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ricardo Koller X-Patchwork-Id: 12788836 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 vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 7BBB2C433FE for ; Tue, 22 Mar 2022 17:23:28 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239555AbiCVRYy (ORCPT ); Tue, 22 Mar 2022 13:24:54 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:41224 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239560AbiCVRYy (ORCPT ); Tue, 22 Mar 2022 13:24:54 -0400 Received: from mail-pl1-x649.google.com (mail-pl1-x649.google.com [IPv6:2607:f8b0:4864:20::649]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 1F8BD13FA0 for ; Tue, 22 Mar 2022 10:23:24 -0700 (PDT) Received: by mail-pl1-x649.google.com with SMTP id b5-20020a170902b60500b001532ec9005aso2717848pls.10 for ; Tue, 22 Mar 2022 10:23:24 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=7ggpAtHNiDJ+NnsLo7ScZ5icbm1fKGUxqGcBJCAEY6Y=; b=mqg3pc+6i6V/WC/2nUh0x/0RXtMDSlRE8rCEA2QSpbd1/19ON/Ef3H/Q8YD6UH990U m5SSErCS/i5EZsNXySHNv0yPxAr6sKj6Id5hTc7JjuTIkR7k354RVJ20WCuPNIP8Dsii z3pujgswQD40fhtW31IvyoXmCvfKEP8wlB/HWBP5bWl2cO2YiRaeAVCry5w+6q4qmysy 6wED+XCVQ1UVCxtFf9om1e+mzHzxzR92DpatXmSRRuUDR46jm38sUkojVr9OxBILR2QD 4OclWsiNpSxfBpE2qlcj7w7lHF0etmhBlMBUjYdfD+juEHJbC/PpMSWGjBvg6JX+7e1/ CtoQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=7ggpAtHNiDJ+NnsLo7ScZ5icbm1fKGUxqGcBJCAEY6Y=; b=XPJ4vIDP0XJoEOClmWZMt0bzyo+fry7/EJA6jIK1+utv+7oW2j4CMPUIxdpWy6vifV W/gnp4gOAO9hacEBSRa8msy+yl8wcqiKpPSoxQ16jy4xMg5xZNY1IWSASFpqqhkkO6Ws Jy4d8iYWOouK2IP2YrTsRhVnQ1B3CZCzArCAJe7JwA8ltU16zT/ttT3HlYs0eyIHgEmO m5nn5T3mWPZhanaq1J2z5NZj5aXmDjQn9+q7ndFE9qBx6x9irIYvmIGO/ndm6O8lv7Y8 6leby57T6xYlgm0B78yqzkQUXTR/UTlGqMtkj/irdKlO5EN2ngOf1qCFHfqJro2dscqH Oi2A== X-Gm-Message-State: AOAM5319IFu+dQLfvyzFS5EC7JOQrnU/Vl9/dYRjV+Mdc3hSDdcEW11P w5Tb5gi2ED7IYLQ5KeIef8x/1JzU7T+BOrvDO1qmyFtPY0ydWP06MheNJ/8Dr4trO7KRh8gDyyh T8tZIPvHqu4kBoC22Hx3ijDNCjCLcyMjPtMehZRDbHQgtVDo9XPKrPcndS5dqaVM= X-Google-Smtp-Source: ABdhPJyhU7hmeOiqa0P0D3pB5tvCb/IIOpleA1rmE8VxLD5P5L6UlLlxLsc3gmLGCUOoq3qoZytbYSHsU4UJ7Q== X-Received: from ricarkol2.c.googlers.com ([fda3:e722:ac3:cc00:24:72f4:c0a8:62fe]) (user=ricarkol job=sendgmr) by 2002:a17:903:2288:b0:154:50ba:2e31 with SMTP id b8-20020a170903228800b0015450ba2e31mr12250063plh.132.1647969804148; Tue, 22 Mar 2022 10:23:24 -0700 (PDT) Date: Tue, 22 Mar 2022 10:23:16 -0700 In-Reply-To: <20220322172319.2943101-1-ricarkol@google.com> Message-Id: <20220322172319.2943101-2-ricarkol@google.com> Mime-Version: 1.0 References: <20220322172319.2943101-1-ricarkol@google.com> X-Mailer: git-send-email 2.35.1.894.gb6a874cedc-goog Subject: [PATCH v3 1/4] KVM: arm64: selftests: add timer_get_tval() lib function From: Ricardo Koller To: kvm@vger.kernel.org, kvmarm@lists.cs.columbia.edu, drjones@redhat.com Cc: pbonzini@redhat.com, maz@kernel.org, alexandru.elisei@arm.com, eric.auger@redhat.com, oupton@google.com, reijiw@google.com, rananta@google.com, Ricardo Koller Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org Add timer_get_tval() into the arch timer library functions in selftests/kvm. Bonus: change the set_tval function to get an int32_t (tval is signed). Reviewed-by: Oliver Upton Reviewed-by: Reiji Watanabe Signed-off-by: Ricardo Koller --- .../selftests/kvm/include/aarch64/arch_timer.h | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/kvm/include/aarch64/arch_timer.h b/tools/testing/selftests/kvm/include/aarch64/arch_timer.h index cb7c03de3a21..93f35a4fc1aa 100644 --- a/tools/testing/selftests/kvm/include/aarch64/arch_timer.h +++ b/tools/testing/selftests/kvm/include/aarch64/arch_timer.h @@ -79,7 +79,7 @@ static inline uint64_t timer_get_cval(enum arch_timer timer) return 0; } -static inline void timer_set_tval(enum arch_timer timer, uint32_t tval) +static inline void timer_set_tval(enum arch_timer timer, int32_t tval) { switch (timer) { case VIRTUAL: @@ -95,6 +95,22 @@ static inline void timer_set_tval(enum arch_timer timer, uint32_t tval) isb(); } +static inline int32_t timer_get_tval(enum arch_timer timer) +{ + isb(); + switch (timer) { + case VIRTUAL: + return (int32_t)read_sysreg(cntv_tval_el0); + case PHYSICAL: + return (int32_t)read_sysreg(cntp_tval_el0); + default: + GUEST_ASSERT_1(0, timer); + } + + /* We should not reach here */ + return 0; +} + static inline void timer_set_ctl(enum arch_timer timer, uint32_t ctl) { switch (timer) { From patchwork Tue Mar 22 17:23:17 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ricardo Koller X-Patchwork-Id: 12788837 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 vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id E1443C4332F for ; Tue, 22 Mar 2022 17:23:36 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239573AbiCVRZA (ORCPT ); Tue, 22 Mar 2022 13:25:00 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:41570 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239556AbiCVRY6 (ORCPT ); Tue, 22 Mar 2022 13:24:58 -0400 Received: from mail-pf1-x449.google.com (mail-pf1-x449.google.com [IPv6:2607:f8b0:4864:20::449]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 8AFC114038 for ; Tue, 22 Mar 2022 10:23:27 -0700 (PDT) Received: by mail-pf1-x449.google.com with SMTP id d145-20020a621d97000000b004f7285f67e8so11698748pfd.2 for ; Tue, 22 Mar 2022 10:23:27 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=A2OVfh4jna2fjjys5QzYhnEURgOUCehkINqKA/XnhEE=; b=KK7kUh3po8iC/Gm8apsdZvHGwC1JoIeebfUmsd/3SXEXyatzCijqZAmSwSo6wwadQH JUKCVydqn3ltAAGTyC80LbVyLW1FzvlGwyIlxEf6cCtgoTXMZIWqAvGZEt/PWFG05BDV 6N59RPQ2Q4dFqq6szU3GV7VdmcqvLQP/2TW+f8JXDd6D4U4H2kLMbutw3kgNL6jTyUTf 4UOiHjqC8Qo/CiH3tTGaxh4UcmX6VbQ4gf9SS23L0BRckdawkCrzGuk2Xfy5FTsg9Zz/ v4a5McWHyEyw3XQFNDidhoJ4MG36QM+fzberYvyLcayIyCdHWe51dJ7Iu1XuF3EpPI9I 8Hgg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=A2OVfh4jna2fjjys5QzYhnEURgOUCehkINqKA/XnhEE=; b=oW8Ajw9pmaoUkiWdooFG66m/YD108XHYdZgcDK8MspuSOY8/33cd8IAI7VsaTll2LE OaU9vCOuWjZqTCO+pGRJwFBBIO0VzGRbeJyUobIExoNI3hVAjrF0+3IeSr684gombLgQ qoCQ4lqueF5M7oDttmfPT7rikH6Jnz1A2YW8fQqEBI1SCTdSqLJjcSAGoI55uDkXrWEh VCM3A6AMPBIjchvsQBDht+zJ++qRkKLjX795u0TTovzL4u4kROZcMxb4r1t08UXKRYVH ApNemQeEdTyFotUDxi2Ph/5ofG7/GG67sG+XB57BYlf2gd1LvOKO6J5RUWLUNgUM0V5N /u5w== X-Gm-Message-State: AOAM53283bWZjzxAeXt5TCsZa+zWVqQQRL2xkpLAXDxQlpk3AS33odIf kv2QKm79xNx+sQLALfXVa23pAVVCWgcF9i4oy0MdmYjRPJ2i/1cZNOi/h4wx7b0XFkVGsDr9LPi VF+jIbg13Ux7FVEfUIDIGOq3qPGn1WLpCzknfY2+2j2JqvRKGYeioJsTWlPZbz3k= X-Google-Smtp-Source: ABdhPJxPCzEWdiZmf1k/k9aSVDd11Orxgxmcb/v81+3NychWWnJaU5A7hAIzcW0g2gazih+FM+9T5Lwo3MaotA== X-Received: from ricarkol2.c.googlers.com ([fda3:e722:ac3:cc00:24:72f4:c0a8:62fe]) (user=ricarkol job=sendgmr) by 2002:a17:90b:e81:b0:1c6:5a9c:5afa with SMTP id fv1-20020a17090b0e8100b001c65a9c5afamr102565pjb.1.1647969805974; Tue, 22 Mar 2022 10:23:25 -0700 (PDT) Date: Tue, 22 Mar 2022 10:23:17 -0700 In-Reply-To: <20220322172319.2943101-1-ricarkol@google.com> Message-Id: <20220322172319.2943101-3-ricarkol@google.com> Mime-Version: 1.0 References: <20220322172319.2943101-1-ricarkol@google.com> X-Mailer: git-send-email 2.35.1.894.gb6a874cedc-goog Subject: [PATCH v3 2/4] KVM: selftests: add is_cpu_online() utility function From: Ricardo Koller To: kvm@vger.kernel.org, kvmarm@lists.cs.columbia.edu, drjones@redhat.com Cc: pbonzini@redhat.com, maz@kernel.org, alexandru.elisei@arm.com, eric.auger@redhat.com, oupton@google.com, reijiw@google.com, rananta@google.com, Ricardo Koller Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org Add is_cpu_online() utility function: a wrapper for "/sys/devices/system/cpu/cpu%d/online". Signed-off-by: Ricardo Koller --- tools/testing/selftests/kvm/include/test_util.h | 2 ++ tools/testing/selftests/kvm/lib/test_util.c | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/tools/testing/selftests/kvm/include/test_util.h b/tools/testing/selftests/kvm/include/test_util.h index 99e0dcdc923f..14084dc4e152 100644 --- a/tools/testing/selftests/kvm/include/test_util.h +++ b/tools/testing/selftests/kvm/include/test_util.h @@ -143,4 +143,6 @@ static inline void *align_ptr_up(void *x, size_t size) return (void *)align_up((unsigned long)x, size); } +bool is_cpu_online(int pcpu); + #endif /* SELFTEST_KVM_TEST_UTIL_H */ diff --git a/tools/testing/selftests/kvm/lib/test_util.c b/tools/testing/selftests/kvm/lib/test_util.c index 6d23878bbfe1..81950e6b6d10 100644 --- a/tools/testing/selftests/kvm/lib/test_util.c +++ b/tools/testing/selftests/kvm/lib/test_util.c @@ -334,3 +334,19 @@ long get_run_delay(void) return val[1]; } + +bool is_cpu_online(int pcpu) +{ + char p[128]; + FILE *fp; + int ret; + + snprintf(p, sizeof(p), "/sys/devices/system/cpu/cpu%d/online", pcpu); + fp = fopen(p, "r"); + if (!fp) + return false; + assert(fscanf(fp, "%d ", &ret) == 1); + fclose(fp); + + return !!ret; +} From patchwork Tue Mar 22 17:23:18 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ricardo Koller X-Patchwork-Id: 12788839 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 vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id A8DAAC433EF for ; Tue, 22 Mar 2022 17:23:43 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239549AbiCVRZJ (ORCPT ); Tue, 22 Mar 2022 13:25:09 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:41460 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239551AbiCVRZA (ORCPT ); Tue, 22 Mar 2022 13:25:00 -0400 Received: from mail-pl1-x649.google.com (mail-pl1-x649.google.com [IPv6:2607:f8b0:4864:20::649]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A884913F11 for ; Tue, 22 Mar 2022 10:23:28 -0700 (PDT) Received: by mail-pl1-x649.google.com with SMTP id ik24-20020a170902ab1800b00153ae512603so7426303plb.14 for ; Tue, 22 Mar 2022 10:23:28 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=ypHYPMOZwZTFdf+pqXbcfmnUGcDgIxAKLnv2fhM+6Vs=; b=smXNXsyUEAHvg45PDKPlfeKMBqeKwKEincdno3H6Hg4xD8jd3SNZJadAUAMNQeX47Z 6i5CyxchbjkhBcKK9ofVhvNDH43jWJs6E/nPUI0jm0uYmU3kSL9L1pIFHgIdXKe3mZwQ 6wWEK/yq12sx+OK67pWrkcn9NQdWv4jirEMkvx5AXPqoVbIVTNjjYXLZLeGC0sksl2Bj DNlgPTsEUSCJUO0cltkyK4JQelW9ba4rYlGWtuMWYWq8uC3SPb14YHv/LWmHOv8G9Im/ 97G/EWndX0HoCq7dGYHoo2svtgGysiHX3VN/X3P/CSXc5UMIe0z2PsrV+2R5qoEexwp2 PP8Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=ypHYPMOZwZTFdf+pqXbcfmnUGcDgIxAKLnv2fhM+6Vs=; b=xdpLFj3A7jFgPVAEIPK4g9PnH7N4G7ETI/5UVac9rknlBg3szVsxZVP/qom6ALeE8E wUL2dqcFmPgzNO8Vge5l/nv0esZ6TZ8JBqR61RIJE/A2HsUtiZdzvN91Xk1FJ7PKpDTu T6Enqyesx+fBhdjWozlqQ12ga4s6ontS/T9D00tzx0kfN46b4dkVzLYdgqlWG1vJhDVZ u45ePiKmKDv8XVSwcSwHTfeIXAHM4tRaL3aHAO1ofHP5suhc0pU9HUGv3A2m0gbaH9KU WTJrMo3p700oQFi301adIKs1Jj2lEpDXQuOMXJBKZyHPOjkiDAdT/Pv6iHBg899+Wgaz Rqnw== X-Gm-Message-State: AOAM532NeJwfuNGO9kUk28c39b+JofmGs7Z4W+sq5Xhb63JYRVjeah4x ayKiaYJ/e5j/2BQukwhGp6uQXEnyaA/ssINP5JW9JnR9L7Mt6lwT6co55soZM+EkQ2k7XZXZy0Q xQKER9xRZdLQZUoeQh+BTPbauaAbqRvyuFoSegtlIEu8he+G5HZ251AHwvW0MwS8= X-Google-Smtp-Source: ABdhPJw30Rtx6G7BGO9Il/Fk+BrbThY4XAiUaaBb2WbQIk/AF9R+gX5eoGCPW2LxKC2HKK1GVO/kjHaMpw2S4g== X-Received: from ricarkol2.c.googlers.com ([fda3:e722:ac3:cc00:24:72f4:c0a8:62fe]) (user=ricarkol job=sendgmr) by 2002:aa7:8d54:0:b0:4e0:bd6:cfb9 with SMTP id s20-20020aa78d54000000b004e00bd6cfb9mr30392496pfe.60.1647969807933; Tue, 22 Mar 2022 10:23:27 -0700 (PDT) Date: Tue, 22 Mar 2022 10:23:18 -0700 In-Reply-To: <20220322172319.2943101-1-ricarkol@google.com> Message-Id: <20220322172319.2943101-4-ricarkol@google.com> Mime-Version: 1.0 References: <20220322172319.2943101-1-ricarkol@google.com> X-Mailer: git-send-email 2.35.1.894.gb6a874cedc-goog Subject: [PATCH v3 3/4] KVM: arm64: selftests: add arch_timer_edge_cases From: Ricardo Koller To: kvm@vger.kernel.org, kvmarm@lists.cs.columbia.edu, drjones@redhat.com Cc: pbonzini@redhat.com, maz@kernel.org, alexandru.elisei@arm.com, eric.auger@redhat.com, oupton@google.com, reijiw@google.com, rananta@google.com, Ricardo Koller Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org Add an arch_timer edge-cases selftest. For now, just add some basic sanity checks, and some stress conditions (like waiting for the timers while re-scheduling the vcpu). The next commit will add the actual edge case tests. This test fails without a867e9d0cc1 "KVM: arm64: Don't miss pending interrupts for suspended vCPU". Reviewed-by: Reiji Watanabe Reviewed-by: Raghavendra Rao Ananta Signed-off-by: Ricardo Koller --- tools/testing/selftests/kvm/.gitignore | 1 + tools/testing/selftests/kvm/Makefile | 1 + .../kvm/aarch64/arch_timer_edge_cases.c | 608 ++++++++++++++++++ 3 files changed, 610 insertions(+) create mode 100644 tools/testing/selftests/kvm/aarch64/arch_timer_edge_cases.c diff --git a/tools/testing/selftests/kvm/.gitignore b/tools/testing/selftests/kvm/.gitignore index dce7de7755e6..8f7e0123dd28 100644 --- a/tools/testing/selftests/kvm/.gitignore +++ b/tools/testing/selftests/kvm/.gitignore @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only /aarch64/arch_timer +/aarch64/arch_timer_edge_cases /aarch64/debug-exceptions /aarch64/get-reg-list /aarch64/psci_cpu_on_test diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile index 0e4926bc9a58..17a0f32cfc91 100644 --- a/tools/testing/selftests/kvm/Makefile +++ b/tools/testing/selftests/kvm/Makefile @@ -101,6 +101,7 @@ TEST_GEN_PROGS_x86_64 += kvm_binary_stats_test TEST_GEN_PROGS_x86_64 += system_counter_offset_test TEST_GEN_PROGS_aarch64 += aarch64/arch_timer +TEST_GEN_PROGS_aarch64 += aarch64/arch_timer_edge_cases TEST_GEN_PROGS_aarch64 += aarch64/debug-exceptions TEST_GEN_PROGS_aarch64 += aarch64/get-reg-list TEST_GEN_PROGS_aarch64 += aarch64/psci_cpu_on_test diff --git a/tools/testing/selftests/kvm/aarch64/arch_timer_edge_cases.c b/tools/testing/selftests/kvm/aarch64/arch_timer_edge_cases.c new file mode 100644 index 000000000000..56376eea1a51 --- /dev/null +++ b/tools/testing/selftests/kvm/aarch64/arch_timer_edge_cases.c @@ -0,0 +1,608 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * arch_timer_edge_cases.c - Tests the aarch64 timer IRQ functionality. + * + * Some of these tests program timers and then wait indefinitely for them to + * fire. We rely on having a timeout mechanism in the "runner", like + * tools/testing/selftests/kselftest/runner.sh. + * + * Copyright (c) 2022, Google LLC. + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include + +#include "kvm_util.h" +#include "processor.h" +#include "delay.h" +#include "arch_timer.h" +#include "gic.h" +#include "vgic.h" + +#define VCPUID 0 + +#define CVAL_MAX (~0ULL) +/* tval is a signed 32-bit int. */ +#define TVAL_MAX INT_MAX +#define TVAL_MIN INT_MIN + +#define GICD_BASE_GPA 0x8000000ULL +#define GICR_BASE_GPA 0x80A0000ULL + +/* After how much time we say there is no IRQ. */ +#define TIMEOUT_NO_IRQ_US (50 * 1000LL) + +/* A nice counter value to use as the starting one for most tests. */ +#define DEF_CNT (CVAL_MAX / 2) + +/* Number of runs. */ +#define NR_TEST_ITERS_DEF 5 + +/* Shared with IRQ handler. */ +volatile struct test_vcpu_shared_data { + int handled; +} shared_data; + +struct test_args { + /* Virtual or physical timer and counter tests. */ + enum arch_timer timer; + /* Number of iterations. */ + int iterations; +}; + +struct test_args test_args = { + /* Only testing VIRTUAL timers for now. */ + .timer = VIRTUAL, + .iterations = NR_TEST_ITERS_DEF, +}; + +static int vtimer_irq, ptimer_irq; + +enum sync_cmd { + SET_REG_KVM_REG_ARM_TIMER_CNT, + USERSPACE_SCHED_YIELD, + USERSPACE_MIGRATE_SELF, +}; + +typedef void (*wait_method_t)(void); + +static void wait_for_non_spurious_irq(void); +static void wait_poll_for_irq(void); +static void wait_sched_poll_for_irq(void); +static void wait_migrate_poll_for_irq(void); + +wait_method_t wait_method[] = { + wait_for_non_spurious_irq, + wait_poll_for_irq, + wait_sched_poll_for_irq, + wait_migrate_poll_for_irq, +}; + +enum timer_view { + TIMER_CVAL, + TIMER_TVAL, +}; + +/* Pair of pcpus for the test to alternate between. */ +static int pcpus[2] = {-1, -1}; +static int pcpus_idx; + +static uint32_t next_pcpu(void) +{ + pcpus_idx = 1 - pcpus_idx; + return pcpus[pcpus_idx]; +} + +#define ASSERT_IRQS_HANDLED_2(__nr, arg1, arg2) do { \ + int __h = shared_data.handled; \ + GUEST_ASSERT_4(__h == (__nr), __h, __nr, arg1, arg2); \ +} while (0) + +#define ASSERT_IRQS_HANDLED_1(__nr, arg1) \ + ASSERT_IRQS_HANDLED_2((__nr), arg1, 0) + +#define ASSERT_IRQS_HANDLED(__nr) \ + ASSERT_IRQS_HANDLED_2((__nr), 0, 0) + +#define SET_COUNTER(__ctr, __t) \ + GUEST_SYNC_ARGS(SET_REG_KVM_REG_ARM_TIMER_CNT, (__ctr), (__t), 0, 0) + +#define USERSPACE_CMD(__cmd) \ + GUEST_SYNC_ARGS(__cmd, 0, 0, 0, 0) + +#define USERSPACE_SCHEDULE() \ + USERSPACE_CMD(USERSPACE_SCHED_YIELD) + +#define USERSPACE_MIGRATE_VCPU() \ + USERSPACE_CMD(USERSPACE_MIGRATE_SELF) + +static void guest_irq_handler(struct ex_regs *regs) +{ + unsigned int intid = gic_get_and_ack_irq(); + uint64_t cnt, cval; + uint32_t ctl; + + if (intid == IAR_SPURIOUS) + return; + + GUEST_ASSERT(gic_irq_get_pending(intid)); + + ctl = timer_get_ctl(test_args.timer); + cnt = timer_get_cntct(test_args.timer); + cval = timer_get_cval(test_args.timer); + + GUEST_ASSERT_1(ctl & CTL_ISTATUS, ctl); + + /* Disable and mask the timer. */ + timer_set_ctl(test_args.timer, CTL_IMASK); + GUEST_ASSERT(!gic_irq_get_pending(intid)); + + shared_data.handled++; + + /* The IRQ should not fire before time. */ + GUEST_ASSERT_2(cnt >= cval, cnt, cval); + + gic_set_eoi(intid); +} + +/* + * Sleep for usec microseconds by polling in the guest (userspace=0) or in + * userspace (e.g., userspace=1 and userspace_cmd=USERSPACE_SCHEDULE). + */ +static void guest_poll(enum arch_timer timer, uint64_t usec, + bool userspace, enum sync_cmd userspace_cmd) +{ + uint64_t cycles = usec_to_cycles(usec); + uint64_t start = timer_get_cntct(timer); + + /* + * TODO: Take care of roll-overs. Right now, we are fine as we use the + * virtual timer/counter for all of our roll-over tests, and so we can use + * the physical counter for this function. Assert this (temporarily): + */ + GUEST_ASSERT(test_args.timer == VIRTUAL && timer == PHYSICAL); + + while ((timer_get_cntct(timer) - start) < cycles) { + if (userspace) + USERSPACE_CMD(userspace_cmd); + else + cpu_relax(); + } +} + +static void sleep_poll(uint64_t usec) +{ + if (test_args.timer == VIRTUAL) + guest_poll(PHYSICAL, usec, false, -1); + else + GUEST_ASSERT(0); /* Not implemented. */ +} + +static void program_timer_irq(uint64_t xval, uint32_t ctl, enum timer_view tv) +{ + shared_data.handled = 0; + + switch (tv) { + case TIMER_CVAL: + timer_set_cval(test_args.timer, xval); + timer_set_ctl(test_args.timer, ctl); + break; + case TIMER_TVAL: + timer_set_tval(test_args.timer, xval); + timer_set_ctl(test_args.timer, ctl); + break; + default: + GUEST_ASSERT(0); + } +} + +/* + * Should be called with IRQs masked. + */ +static void wait_for_non_spurious_irq(void) +{ + int h = shared_data.handled; + + /* Wait for the IRQ handler to process an interrupt */ + while (h == shared_data.handled) { + asm volatile("wfi\n" + "msr daifclr, #2\n" + /* handle IRQ */ + "isb\n" + "msr daifset, #2\n" + : : : "memory"); + } +} + +/* + * Wait for an non-spurious IRQ by polling in the guest (userspace=0) or in + * userspace (e.g., userspace=1 and userspace_cmd=USERSPACE_SCHED_YIELD). + * + * Should be called with IRQs masked. Not really needed like the wfi above, but + * it should match the others. + */ +static void poll_for_non_spurious_irq(bool userspace, + enum sync_cmd userspace_cmd) +{ + int h; + + h = shared_data.handled; + + local_irq_enable(); + while (h == shared_data.handled) { + if (userspace) + USERSPACE_CMD(userspace_cmd); + else + cpu_relax(); + } + local_irq_disable(); +} + +static void wait_poll_for_irq(void) +{ + poll_for_non_spurious_irq(false, -1); +} + +static void wait_sched_poll_for_irq(void) +{ + poll_for_non_spurious_irq(true, USERSPACE_SCHED_YIELD); +} + +static void wait_migrate_poll_for_irq(void) +{ + poll_for_non_spurious_irq(true, USERSPACE_MIGRATE_SELF); +} + +/* + * Reset the timer state to some nice values like the counter not being close + * to the edge, and the control register masked and disabled. + */ +static void reset_timer_state(uint64_t cnt) +{ + SET_COUNTER(cnt, test_args.timer); + timer_set_ctl(test_args.timer, CTL_IMASK); +} + +static void test_timer(uint64_t reset_cnt, uint64_t xval, + wait_method_t wm, enum timer_view tv) +{ + local_irq_disable(); + + reset_timer_state(reset_cnt); + + program_timer_irq(xval, CTL_ENABLE, tv); + wm(); + + ASSERT_IRQS_HANDLED_1(1, tv); + local_irq_enable(); +} + +static void test_basic_functionality(void) +{ + int32_t tval = (int32_t)msec_to_cycles(10); + uint64_t cval; + int i; + + for (i = 0; i < ARRAY_SIZE(wait_method); i++) { + wait_method_t wm = wait_method[i]; + + cval = DEF_CNT + msec_to_cycles(10); + + test_timer(DEF_CNT, cval, wm, TIMER_CVAL); + test_timer(DEF_CNT, tval, wm, TIMER_TVAL); + } +} + +/* + * This test checks basic timer behavior without actually firing timers, things + * like: the relationship between cval and tval, tval down-counting. + */ +static void timers_sanity_checks(bool use_sched) +{ + uint64_t cval; + + reset_timer_state(DEF_CNT); + + local_irq_disable(); + + /* cval in the past */ + timer_set_cval(test_args.timer, timer_get_cntct(test_args.timer) - 1); + if (use_sched) + USERSPACE_SCHEDULE(); + GUEST_ASSERT(timer_get_tval(test_args.timer) < 0); + + /* tval in the past */ + timer_set_tval(test_args.timer, -1); + if (use_sched) + USERSPACE_SCHEDULE(); + GUEST_ASSERT(timer_get_cval(test_args.timer) < + timer_get_cntct(test_args.timer)); + + /* tval larger than TVAL_MAX. */ + cval = timer_get_cntct(test_args.timer) + 2ULL * TVAL_MAX - 1; + timer_set_cval(test_args.timer, cval); + if (use_sched) + USERSPACE_SCHEDULE(); + GUEST_ASSERT(timer_get_tval(test_args.timer) <= 0); + GUEST_ASSERT_EQ(cval, timer_get_cval(test_args.timer)); + + /* tval should keep down-counting from 0 to -1. */ + SET_COUNTER(DEF_CNT, test_args.timer); + timer_set_tval(test_args.timer, 0); + if (use_sched) + USERSPACE_SCHEDULE(); + /* We just need 1 counter tick to pass. */ + sleep_poll(1); + GUEST_ASSERT(timer_get_tval(test_args.timer) < 0); + + local_irq_enable(); + + /* Mask and disable any pending timer. */ + timer_set_ctl(test_args.timer, CTL_IMASK); +} + +static void test_timers_sanity_checks(void) +{ + timers_sanity_checks(false); + /* Check how KVM saves/restores these edge-case values. */ + timers_sanity_checks(true); +} + +static void guest_run_iteration(void) +{ + test_timers_sanity_checks(); + test_basic_functionality(); +} + +static void guest_code(void) +{ + int i; + + local_irq_disable(); + + gic_init(GIC_V3, 1, (void *)GICD_BASE_GPA, (void *)GICR_BASE_GPA); + + timer_set_ctl(test_args.timer, CTL_IMASK); + timer_set_ctl(PHYSICAL, CTL_IMASK); + + gic_irq_enable(vtimer_irq); + gic_irq_enable(ptimer_irq); + local_irq_enable(); + + for (i = 0; i < test_args.iterations; i++) { + GUEST_SYNC(i); + guest_run_iteration(); + } + + GUEST_DONE(); +} + +static void migrate_self(uint32_t new_pcpu) +{ + int ret; + cpu_set_t cpuset; + pthread_t thread; + + thread = pthread_self(); + + CPU_ZERO(&cpuset); + CPU_SET(new_pcpu, &cpuset); + + pr_debug("Migrating from %u to %u\n", sched_getcpu(), new_pcpu); + + ret = pthread_setaffinity_np(thread, sizeof(cpuset), &cpuset); + + TEST_ASSERT(ret == 0, "Failed to migrate to pCPU: %u; ret: %d\n", + new_pcpu, ret); +} + +/* + * Set the two pcpus that the test will use to alternate between. Default to + * use the current cpu as pcpus[0] and the one right after in the affinity set + * as pcpus[1]. + */ +static void set_default_pcpus(void) +{ + int curr = sched_getcpu(); + cpu_set_t cpuset; + long i; + + pcpus[0] = curr; + + sched_getaffinity(getpid(), sizeof(cpu_set_t), &cpuset); + for (i = (curr + 1) % CPU_SETSIZE; i != curr; i = (i + 1) % CPU_SETSIZE) { + if (CPU_ISSET(i, &cpuset)) { + pcpus[1] = i; + break; + } + } + + TEST_ASSERT(pcpus[1] != -1, "Couldn't find a second pcpu."); + pr_debug("pcpus: %d %d\n", pcpus[0], pcpus[1]); +} + +static void kvm_set_cntxct(struct kvm_vm *vm, uint64_t cnt, enum arch_timer timer) +{ + TEST_ASSERT(timer == VIRTUAL, + "Only supports setting the virtual counter for now."); + + struct kvm_one_reg reg = { + .id = KVM_REG_ARM_TIMER_CNT, + .addr = (uint64_t)&cnt, + }; + vcpu_set_reg(vm, 0, ®); +} + +static void handle_sync(struct kvm_vm *vm, struct ucall *uc) +{ + enum sync_cmd cmd = uc->args[1]; + uint64_t val = uc->args[2]; + enum arch_timer timer = uc->args[3]; + + switch (cmd) { + case SET_REG_KVM_REG_ARM_TIMER_CNT: + kvm_set_cntxct(vm, val, timer); + break; + case USERSPACE_SCHED_YIELD: + sched_yield(); + break; + case USERSPACE_MIGRATE_SELF: + migrate_self(next_pcpu()); + break; + default: + break; + } +} + +static void test_run(struct kvm_vm *vm) +{ + struct ucall uc; + int stage = 0; + + /* Start on the first pcpu. */ + migrate_self(pcpus[0]); + + sync_global_to_guest(vm, test_args); + + for (stage = 0; ; stage++) { + vcpu_run(vm, VCPUID); + switch (get_ucall(vm, VCPUID, &uc)) { + case UCALL_SYNC: + handle_sync(vm, &uc); + break; + case UCALL_DONE: + goto out; + case UCALL_ABORT: + TEST_FAIL("%s at %s:%ld\n\tvalues: %lu, %lu; %lu", + (const char *)uc.args[0], __FILE__, uc.args[1], + uc.args[2], uc.args[3], uc.args[4]); + goto out; + default: + TEST_FAIL("Unexpected guest exit\n"); + } + } + +out: + return; +} + +static void test_init_timer_irq(struct kvm_vm *vm) +{ + int vcpu_fd = vcpu_get_fd(vm, VCPUID); + + kvm_device_access(vcpu_fd, KVM_ARM_VCPU_TIMER_CTRL, + KVM_ARM_VCPU_TIMER_IRQ_PTIMER, &ptimer_irq, false); + kvm_device_access(vcpu_fd, KVM_ARM_VCPU_TIMER_CTRL, + KVM_ARM_VCPU_TIMER_IRQ_VTIMER, &vtimer_irq, false); + + sync_global_to_guest(vm, ptimer_irq); + sync_global_to_guest(vm, vtimer_irq); + + pr_debug("ptimer_irq: %d; vtimer_irq: %d\n", ptimer_irq, vtimer_irq); +} + +static struct kvm_vm *test_vm_create(void) +{ + struct kvm_vm *vm; + int ret; + + vm = vm_create_default(VCPUID, 0, guest_code); + + vm_init_descriptor_tables(vm); + vm_install_exception_handler(vm, VECTOR_IRQ_CURRENT, guest_irq_handler); + + vcpu_init_descriptor_tables(vm, 0); + + ucall_init(vm, NULL); + test_init_timer_irq(vm); + ret = vgic_v3_setup(vm, 1, 64, GICD_BASE_GPA, GICR_BASE_GPA); + if (ret < 0) { + print_skip("Failed to create vgic-v3"); + exit(KSFT_SKIP); + } + + return vm; +} + +static void test_print_help(char *name) +{ + pr_info("Usage: %s [-h] [-i iterations] [-w] [-p pcpu1,pcpu2]\n", + name); + pr_info("\t-i: Number of iterations (default: %u)\n", + NR_TEST_ITERS_DEF); + pr_info("\t-p: Pair of pcpus for the vcpus to alternate between.\n"); + pr_info("\t-h: Print this help message\n"); +} + +static bool parse_args(int argc, char *argv[]) +{ + int opt, ret; + + while ((opt = getopt(argc, argv, "hi:p:")) != -1) { + switch (opt) { + case 'i': + test_args.iterations = atoi(optarg); + if (test_args.iterations <= 0) { + print_skip("Positive value needed for -i"); + goto err; + } + break; + case 'p': + ret = sscanf(optarg, "%u,%u", &pcpus[0], &pcpus[1]); + if (ret != 2) { + print_skip("Could not parse the pcpus string"); + goto err; + } + if (!is_cpu_online(pcpus[0])) { + print_skip("Invalid pcpu %d", pcpus[0]); + goto err; + } + if (!is_cpu_online(pcpus[1])) { + print_skip("Invalid pcpu %d", pcpus[1]); + goto err; + } + break; + case 'h': + default: + goto err; + } + } + + return true; + +err: + test_print_help(argv[0]); + return false; +} + +int main(int argc, char *argv[]) +{ + struct kvm_vm *vm; + + /* Tell stdout not to buffer its content */ + setbuf(stdout, NULL); + + if (!parse_args(argc, argv)) + exit(KSFT_SKIP); + + if (get_nprocs() < 2) { + print_skip("Need at least 2 online pcpus"); + exit(KSFT_SKIP); + } + + if (pcpus[0] == -1 || pcpus[1] == -1) + set_default_pcpus(); + + vm = test_vm_create(); + test_run(vm); + kvm_vm_free(vm); + + return 0; +} From patchwork Tue Mar 22 17:23:19 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ricardo Koller X-Patchwork-Id: 12788838 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 vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 9215BC433F5 for ; Tue, 22 Mar 2022 17:23:41 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239585AbiCVRZF (ORCPT ); Tue, 22 Mar 2022 13:25:05 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:41644 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239570AbiCVRZA (ORCPT ); Tue, 22 Mar 2022 13:25:00 -0400 Received: from mail-pj1-x1049.google.com (mail-pj1-x1049.google.com [IPv6:2607:f8b0:4864:20::1049]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 4508717050 for ; Tue, 22 Mar 2022 10:23:30 -0700 (PDT) Received: by mail-pj1-x1049.google.com with SMTP id mq8-20020a17090b380800b001c6f8962e95so2499715pjb.1 for ; Tue, 22 Mar 2022 10:23:30 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=jj1C5+9XaTs/lVlBaXMQH0LmOh3QVqhUhKOqof5ZYnY=; b=NMPcY3UqQPGc22qpqNT4ULqIZ5AElIe0iEN+hMskUIG7yNYqFkeNN818aYbgG2/MTn uq1Xp6fZvhn1N+LoWEsDvAtA7pIKZuMyad5IeO7UftSB0jnSAYHPG34ZYjGGX/mV7YyS Uu391aFGWdDxqVWi3aEtufjJPU3decBVZZ+h3S0BtFBfIf/3YK2509o8VDdhyUY6mrGj opeY/GILqj65AcmSNmd1esQ/YAR6Vg8QgcNa+M9Tu3u37rdd53JAApA6twubTH4atlhX u+VQEbmd8DYPDL1/mlk6RBjH1YDeNfB9w1y5QlI09uoSm43QHNf+XrqtM2eqjtKumOub bP5g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=jj1C5+9XaTs/lVlBaXMQH0LmOh3QVqhUhKOqof5ZYnY=; b=uvponCGsRQbfzDfBe5JhutdluFJ5L7hjYuJXWNXjTTylYT9cQvvrAoPXSw7KYUU+G9 fW1DKCXTPpX08H9N2CT5Y4KZ6NR68kSUmyHXLIO4qlLs5uCfVy4COs0GW4nRfNpHKt1+ 9hgrA3eVHSktbo5Dfc5CMU5js1F0mUw4gd55m5X0hgSptk8guYBxRclHcM1Y3stnJGM8 vFyAip06P9rXs7TKdy+l+Z/8Yan07AM0y5UwFFxflyz7jOvGvs4b/pEw4ZuDh7Cx5UJI TlUfu7QpWk9IEYl/zI+03hhGFm+grLHQwbGAcR+VIkD+AgYKwswEQPEV7VKM+1uAADXM j9KQ== X-Gm-Message-State: AOAM530hNZUIi18/aLtTw3wJFOWtH6De+N1Y1vqIv1TeRwrMuK+gf4tf 1IO6g3fYu6+demW4sQxbNjRaTWR3BmvzleGRjFj8yk7qZVVbTgBAUE6Q8FzjAQ8UFmJKrxhHg2I h1jrZEGZY+Tw+Egv5vtWPwmZ7jmImwfwI9ztR7TKwt3WYZTVrJw7wKboO0Yw6BAo= X-Google-Smtp-Source: ABdhPJxOIT4aHZ/IL38KBsIwUNlHPg8CPQx5nZYsC+4JU8k+AwR5VYJF60WMzCwVW/+TY+bajCmDdS8xAMZL/w== X-Received: from ricarkol2.c.googlers.com ([fda3:e722:ac3:cc00:24:72f4:c0a8:62fe]) (user=ricarkol job=sendgmr) by 2002:a17:902:d488:b0:154:6df6:1e81 with SMTP id c8-20020a170902d48800b001546df61e81mr8198200plg.105.1647969809633; Tue, 22 Mar 2022 10:23:29 -0700 (PDT) Date: Tue, 22 Mar 2022 10:23:19 -0700 In-Reply-To: <20220322172319.2943101-1-ricarkol@google.com> Message-Id: <20220322172319.2943101-5-ricarkol@google.com> Mime-Version: 1.0 References: <20220322172319.2943101-1-ricarkol@google.com> X-Mailer: git-send-email 2.35.1.894.gb6a874cedc-goog Subject: [PATCH v3 4/4] KVM: arm64: selftests: add edge cases tests into arch_timer_edge_cases From: Ricardo Koller To: kvm@vger.kernel.org, kvmarm@lists.cs.columbia.edu, drjones@redhat.com Cc: pbonzini@redhat.com, maz@kernel.org, alexandru.elisei@arm.com, eric.auger@redhat.com, oupton@google.com, reijiw@google.com, rananta@google.com, Ricardo Koller Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org Add tests that validates some edge cases related to the virtual arch-timer: - timers in the past, including TVALs that rollover from 0. - timers across counter roll-overs. - moving counters ahead and behind pending timers. - reprograming timers. - the same timer condition firing multiple times. - masking/unmasking using the timer control mask. Reviewed-by: Reiji Watanabe Signed-off-by: Ricardo Koller --- .../kvm/aarch64/arch_timer_edge_cases.c | 300 +++++++++++++++++- 1 file changed, 298 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/kvm/aarch64/arch_timer_edge_cases.c b/tools/testing/selftests/kvm/aarch64/arch_timer_edge_cases.c index 56376eea1a51..2575d8c70f1e 100644 --- a/tools/testing/selftests/kvm/aarch64/arch_timer_edge_cases.c +++ b/tools/testing/selftests/kvm/aarch64/arch_timer_edge_cases.c @@ -2,6 +2,12 @@ /* * arch_timer_edge_cases.c - Tests the aarch64 timer IRQ functionality. * + * The test validates some edge cases related to the virtual arch-timer: + * - timers across counter roll-overs. + * - moving counters ahead and behind pending timers. + * - reprograming timers. + * - the same timer condition firing multiple times. + * * Some of these tests program timers and then wait indefinitely for them to * fire. We rely on having a timeout mechanism in the "runner", like * tools/testing/selftests/kselftest/runner.sh. @@ -45,6 +51,9 @@ /* Number of runs. */ #define NR_TEST_ITERS_DEF 5 +/* Default "long" wait test time in ms. */ +#define LONG_WAIT_TEST_MS 100 + /* Shared with IRQ handler. */ volatile struct test_vcpu_shared_data { int handled; @@ -53,6 +62,8 @@ volatile struct test_vcpu_shared_data { struct test_args { /* Virtual or physical timer and counter tests. */ enum arch_timer timer; + /* Delay used in the test_long_timer_delays test. */ + uint64_t long_wait_ms; /* Number of iterations. */ int iterations; }; @@ -60,6 +71,7 @@ struct test_args { struct test_args test_args = { /* Only testing VIRTUAL timers for now. */ .timer = VIRTUAL, + .long_wait_ms = LONG_WAIT_TEST_MS, .iterations = NR_TEST_ITERS_DEF, }; @@ -67,10 +79,25 @@ static int vtimer_irq, ptimer_irq; enum sync_cmd { SET_REG_KVM_REG_ARM_TIMER_CNT, + USERSPACE_USLEEP, USERSPACE_SCHED_YIELD, USERSPACE_MIGRATE_SELF, }; +typedef void (*sleep_method_t)(uint64_t usec); + +static void sleep_poll(uint64_t usec); +static void sleep_sched_poll(uint64_t usec); +static void sleep_in_userspace(uint64_t usec); +static void sleep_migrate(uint64_t usec); + +sleep_method_t sleep_method[] = { + sleep_poll, + sleep_sched_poll, + sleep_migrate, + sleep_in_userspace, +}; + typedef void (*wait_method_t)(void); static void wait_for_non_spurious_irq(void); @@ -123,6 +150,9 @@ static uint32_t next_pcpu(void) #define USERSPACE_MIGRATE_VCPU() \ USERSPACE_CMD(USERSPACE_MIGRATE_SELF) +#define SLEEP_IN_USERSPACE(__usecs) \ + GUEST_SYNC_ARGS(USERSPACE_USLEEP, (__usecs), 0, 0, 0) + static void guest_irq_handler(struct ex_regs *regs) { unsigned int intid = gic_get_and_ack_irq(); @@ -260,6 +290,33 @@ static void wait_migrate_poll_for_irq(void) poll_for_non_spurious_irq(true, USERSPACE_MIGRATE_SELF); } +/* + * Both sleep_sched_poll and sleep_migrate temporarily check that the timer in + * use (test_args.timer) is the virtual one. That's because the sleep + * implementation polls on the physical one. + */ + +static void sleep_sched_poll(uint64_t usec) +{ + if (test_args.timer == VIRTUAL) + guest_poll(PHYSICAL, usec, true, USERSPACE_SCHED_YIELD); + else + GUEST_ASSERT(0); /* Not implemented. */ +} + +static void sleep_migrate(uint64_t usec) +{ + if (test_args.timer == VIRTUAL) + guest_poll(PHYSICAL, usec, true, USERSPACE_MIGRATE_SELF); + else + GUEST_ASSERT(0); /* Not implemented. */ +} + +static void sleep_in_userspace(uint64_t usec) +{ + SLEEP_IN_USERSPACE(usec); +} + /* * Reset the timer state to some nice values like the counter not being close * to the edge, and the control register masked and disabled. @@ -284,6 +341,156 @@ static void test_timer(uint64_t reset_cnt, uint64_t xval, local_irq_enable(); } +/* + * Set the counter to just below the edge (CVAL_MAX) and set a timer that + * crosses it over. + */ +static void test_timers_across_rollovers(void) +{ + uint64_t edge_minus_5ms = CVAL_MAX - msec_to_cycles(5); + int i; + + for (i = 0; i < ARRAY_SIZE(wait_method); i++) { + wait_method_t wm = wait_method[i]; + + test_timer(edge_minus_5ms, msec_to_cycles(10), wm, TIMER_TVAL); + test_timer(edge_minus_5ms, TVAL_MAX, wm, TIMER_TVAL); + test_timer(edge_minus_5ms, TVAL_MIN, wm, TIMER_TVAL); + } +} + +/* Check that timer control masks actually mask a timer being fired. */ +static void test_timer_control_masked(sleep_method_t guest_sleep) +{ + reset_timer_state(DEF_CNT); + + /* Local IRQs are not masked at this point. */ + + program_timer_irq(-1, CTL_ENABLE | CTL_IMASK, TIMER_TVAL); + + /* Assume no IRQ after waiting TIMEOUT_NO_IRQ_US microseconds */ + guest_sleep(TIMEOUT_NO_IRQ_US); + + ASSERT_IRQS_HANDLED(0); + timer_set_ctl(test_args.timer, CTL_IMASK); +} + +/* Test masking/unmasking a timer using the timer mask (not the IRQ mask). */ +static void test_timer_control_mask_then_unmask(wait_method_t wm) +{ + reset_timer_state(DEF_CNT); + program_timer_irq(-1, CTL_ENABLE | CTL_IMASK, TIMER_TVAL); + + /* No IRQs because the timer is still masked. */ + ASSERT_IRQS_HANDLED(0); + + /* Unmask the timer, and then get an IRQ. */ + local_irq_disable(); + timer_set_ctl(test_args.timer, CTL_ENABLE); + wm(); + + ASSERT_IRQS_HANDLED(1); + local_irq_enable(); +} + +/* + * Set a timer at the edge, and wait with irqs masked for so long that the + * counter rolls over and the "Timer Condition" doesn't apply anymore. We + * should not get an IRQ fired. + */ +static void test_irq_masked_timer_across_rollover(sleep_method_t guest_sleep) +{ + local_irq_disable(); + reset_timer_state(CVAL_MAX - msec_to_cycles(5)); + + program_timer_irq(-1, CTL_ENABLE, TIMER_TVAL); + + GUEST_ASSERT(timer_get_ctl(test_args.timer) & CTL_ISTATUS); + guest_sleep(10 * 1000LL); + GUEST_ASSERT((timer_get_ctl(test_args.timer) & CTL_ISTATUS) == 0); + + local_irq_enable(); + isb(); + + ASSERT_IRQS_HANDLED(0); +} + +static void test_control_masks(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(sleep_method); i++) + test_timer_control_masked(sleep_method[i]); + + for (i = 0; i < ARRAY_SIZE(wait_method); i++) + test_timer_control_mask_then_unmask(wait_method[i]); + + for (i = 0; i < ARRAY_SIZE(sleep_method); i++) + test_irq_masked_timer_across_rollover(sleep_method[i]); +} + +static void test_fire_a_timer_multiple_times(wait_method_t wm, int num) +{ + int i; + + local_irq_disable(); + reset_timer_state(DEF_CNT); + + program_timer_irq(0, CTL_ENABLE, TIMER_TVAL); + + for (i = 1; i <= num; i++) { + wm(); + + /* + * The IRQ handler masked and disabled the timer. + * Enable and unmmask it again. + */ + timer_set_ctl(test_args.timer, CTL_ENABLE); + + ASSERT_IRQS_HANDLED(i); + } + + local_irq_enable(); +} + +static void test_timers_fired_multiple_times(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(wait_method); i++) + test_fire_a_timer_multiple_times(wait_method[i], 1000); +} + +/* Set a timer for cval1 then reprogram it to cval2. */ +static void test_reprogram_timer(wait_method_t wm, bool use_sched, + uint64_t cnt, uint64_t cval1, uint64_t cval2) +{ + local_irq_disable(); + reset_timer_state(cnt); + + program_timer_irq(cval1, CTL_ENABLE, TIMER_CVAL); + + if (use_sched) + USERSPACE_SCHEDULE(); + + timer_set_cval(test_args.timer, cval2); + + wm(); + + local_irq_enable(); + ASSERT_IRQS_HANDLED(1); +}; + +static void test_reprogram_timers(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(wait_method); i++) { + test_reprogram_timer(wait_method[i], true, 0, CVAL_MAX, 0); + test_reprogram_timer(wait_method[i], true, 0, CVAL_MAX, 0); + } +} + static void test_basic_functionality(void) { int32_t tval = (int32_t)msec_to_cycles(10); @@ -355,10 +562,86 @@ static void test_timers_sanity_checks(void) timers_sanity_checks(true); } +/* + * Set the counter to cnt_1, the [c|t]val to xval, the counter to cnt_2, and + * then wait for an IRQ. + */ +static void test_set_counter_after_programming_timer(uint64_t cnt_1, + uint64_t xval, uint64_t cnt_2, wait_method_t wm, + enum timer_view tv) +{ + local_irq_disable(); + + SET_COUNTER(cnt_1, test_args.timer); + timer_set_ctl(test_args.timer, CTL_IMASK); + + program_timer_irq(xval, CTL_ENABLE, tv); + SET_COUNTER(cnt_2, test_args.timer); + wm(); + + ASSERT_IRQS_HANDLED(1); + local_irq_enable(); +} + +/* Set a timer and then move the counter ahead of it. */ +static void test_move_counters_after_timers(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(wait_method); i++) { + wait_method_t wm = wait_method[i]; + + test_set_counter_after_programming_timer(0, DEF_CNT, + DEF_CNT + 1, wm, TIMER_CVAL); + test_set_counter_after_programming_timer(CVAL_MAX, 1, + 2, wm, TIMER_CVAL); + test_set_counter_after_programming_timer(0, TVAL_MAX, + (uint64_t)TVAL_MAX + 1, wm, TIMER_TVAL); + } +} + +static void test_timers_in_the_past(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(wait_method); i++) { + wait_method_t wm = wait_method[i]; + + test_timer(DEF_CNT, DEF_CNT - 1, wm, TIMER_CVAL); + test_timer(DEF_CNT, TVAL_MIN, wm, TIMER_TVAL); + test_timer(CVAL_MAX, 0, wm, TIMER_CVAL); + test_timer(DEF_CNT, 0, wm, TIMER_CVAL); + test_timer(DEF_CNT, 0, wm, TIMER_TVAL); + } +} + +static void test_long_timer_delays(void) +{ + uint64_t wait_ms = test_args.long_wait_ms; + int i; + + for (i = 0; i < ARRAY_SIZE(wait_method); i++) { + wait_method_t wm = wait_method[i]; + + test_timer(0, msec_to_cycles(wait_ms), wm, TIMER_CVAL); + test_timer(0, msec_to_cycles(wait_ms), wm, TIMER_TVAL); + } +} + static void guest_run_iteration(void) { test_timers_sanity_checks(); test_basic_functionality(); + + test_timers_in_the_past(); + test_timers_across_rollovers(); + + test_move_counters_after_timers(); + test_reprogram_timers(); + + test_control_masks(); + + test_timers_fired_multiple_times(); } static void guest_code(void) @@ -381,6 +664,7 @@ static void guest_code(void) guest_run_iteration(); } + test_long_timer_delays(); GUEST_DONE(); } @@ -450,6 +734,9 @@ static void handle_sync(struct kvm_vm *vm, struct ucall *uc) case SET_REG_KVM_REG_ARM_TIMER_CNT: kvm_set_cntxct(vm, val, timer); break; + case USERSPACE_USLEEP: + usleep(val); + break; case USERSPACE_SCHED_YIELD: sched_yield(); break; @@ -533,11 +820,13 @@ static struct kvm_vm *test_vm_create(void) static void test_print_help(char *name) { - pr_info("Usage: %s [-h] [-i iterations] [-w] [-p pcpu1,pcpu2]\n", + pr_info("Usage: %s [-h] [-i iterations] [-p pcpu1,pcpu2] [-l long_wait_ms]\n", name); pr_info("\t-i: Number of iterations (default: %u)\n", NR_TEST_ITERS_DEF); pr_info("\t-p: Pair of pcpus for the vcpus to alternate between.\n"); + pr_info("\t-l: Delta (in ms) used for long wait time test (default: %u)\n", + LONG_WAIT_TEST_MS); pr_info("\t-h: Print this help message\n"); } @@ -545,7 +834,7 @@ static bool parse_args(int argc, char *argv[]) { int opt, ret; - while ((opt = getopt(argc, argv, "hi:p:")) != -1) { + while ((opt = getopt(argc, argv, "hi:p:l:")) != -1) { switch (opt) { case 'i': test_args.iterations = atoi(optarg); @@ -569,6 +858,13 @@ static bool parse_args(int argc, char *argv[]) goto err; } break; + case 'l': + test_args.long_wait_ms = atoi(optarg); + if (test_args.long_wait_ms <= 0) { + print_skip("Positive value needed for -l"); + goto err; + } + break; case 'h': default: goto err;