From patchwork Sat Oct 26 16:18:13 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Raphael Tiovalen X-Patchwork-Id: 2002800 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; secure) header.d=lists.infradead.org header.i=@lists.infradead.org header.a=rsa-sha256 header.s=bombadil.20210309 header.b=sPBuBkFX; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20230601 header.b=RTT8KQeL; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=none (no SPF record) smtp.mailfrom=lists.infradead.org (client-ip=2607:7c80:54:3::133; helo=bombadil.infradead.org; envelope-from=kvm-riscv-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org; receiver=patchwork.ozlabs.org) Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2607:7c80:54:3::133]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4XbPwC28yFz1xwF for ; Sun, 27 Oct 2024 03:18:33 +1100 (AEDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-ID:Date:Subject:Cc:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=TJ7HfW6Hk0ZMU1OhPLyz5hN7GjzlkcgO4cchfQ7L/58=; b=sPBuBkFXGMQTwE vc1BXVy+0Nc52jYKrOF+YngcGIazW50nMM8vYk7J4HIn5HGd2TJl9pV/Lp9syYslZXac60Je4cdCb +iCmUiLYLUYu1cWxA7QEGM/EgkQWucr37xWDn+1F1sg5xLvoyOlAfwV3ZITckNyArW4GvFxAQ4iAS wIzDQ8ewdXktvCpwVptZfq6xg47Dr2UFk43ILjEU8SQdbaMaFmU6npxbbpNk6am+nymjgZDC3PF4T hyOZDwkt5yKAEuIxyBmeZVoFtgMP8VIf3gh1wkA9RYzwGzKiX1uB+ge4kkWAMj8/IHy7NXe/kIZk6 6q+WkQWU6N0oZ33BEi/Q==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.98 #2 (Red Hat Linux)) id 1t4jUl-00000006tEK-0Ojh; Sat, 26 Oct 2024 16:18:31 +0000 Received: from mail-pf1-x42e.google.com ([2607:f8b0:4864:20::42e]) by bombadil.infradead.org with esmtps (Exim 4.98 #2 (Red Hat Linux)) id 1t4jUh-00000006tDb-1E1V for kvm-riscv@lists.infradead.org; Sat, 26 Oct 2024 16:18:29 +0000 Received: by mail-pf1-x42e.google.com with SMTP id d2e1a72fcca58-71e8235f0b6so2214316b3a.3 for ; Sat, 26 Oct 2024 09:18:26 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1729959506; x=1730564306; darn=lists.infradead.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=T0smmCl2Pl9lYuSwhonRnWZqPkBcB1aJnBGothIWogQ=; b=RTT8KQeLPTzeU5NQmENZlY3N1dgWsj+OpZW1WSLXMb+2r1eoa5aqxC1EQjElXNCdUE ltYqJBVPuOs+S9mshetmJtk5NoG5IMdUk7ohSoPJdWGrWid27rKo3mA5711iIXKuIcdP c7oydp5Nmrnoq8r9uSegzoJKZeN/piLyjNkwD4hmI+ZBFRzaNSorMuKLUNsQ4cmm6imw DKdU+S19li139lackjHloiZG4KnD//yQPhnhpOgzj53ZGvWXReDpawoJLfBzFems19yl fQ2RJUpzSGtoHHt7MsFtzr2r7EvVijTji+G/4FwtAThXpTvblQ1yOabrTrmnDHrkj3HK xMjA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1729959506; x=1730564306; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=T0smmCl2Pl9lYuSwhonRnWZqPkBcB1aJnBGothIWogQ=; b=HzNirEBwc/Ac8Bt9VHGbvJbc3HFSVhXWhE5p47DgfBCeF6LBqZovffIV682xPI601k TZ48swqNk4XYJv3fiaBv2X7Ng9A0SWAE40EflKzLb5kIm8XVno70MrIlc1JKPwyUB/2z N2v9UqvTBwpYcwsXHc8mBsru+mAUwTGkQ8K7LtROke4RGzRuvUekq0pi+NZPwEG7sMUy B+IKlNSUMq5mHjeXL4iNVou7+rw7HFN/4czzrXKvKHYX7n6B536DMVp+QXCJ1KzNgOjS WetSkuccZmSOeMDULpChc48kTKW9PhIuwWIh1sR/lV0HrslE2fWvDpw3iiU9AsY3v0nL 9uCw== X-Forwarded-Encrypted: i=1; AJvYcCVkKZPFyR8bmQfLIK2xI5trsR7Nns0CvxA8ZeJ1tn3+suLs7zEe/DxujBOccP+g8hJeiu4H12f/f0I=@lists.infradead.org X-Gm-Message-State: AOJu0Yzs0U5ueIxWB6Kkh15iz3b1XwH41IkL4OsusPE9BlPGTu+aftjg 2xWVjiKpT4s9P2XUqwm1HkqsgAzGoU+s6kfEclwKCrOxfMcxLC2W X-Google-Smtp-Source: AGHT+IF54vghMgTd/7p1j23WzVQMVZLs+gsvV+uemtll42vrtNOCvSF5dH+1jXhArYOssvGbGdz+vg== X-Received: by 2002:a05:6a20:9e06:b0:1d9:6e67:de83 with SMTP id adf61e73a8af0-1d9a83b04f4mr4535871637.1.1729959506080; Sat, 26 Oct 2024 09:18:26 -0700 (PDT) Received: from JRT-PC.. ([202.166.44.78]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-72057a1fe53sm2904317b3a.162.2024.10.26.09.18.24 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 26 Oct 2024 09:18:25 -0700 (PDT) From: James Raphael Tiovalen To: kvm@vger.kernel.org, kvm-riscv@lists.infradead.org Cc: andrew.jones@linux.dev, atishp@rivosinc.com, cade.richard@berkeley.edu, James Raphael Tiovalen Subject: [kvm-unit-tests PATCH v6 1/1] riscv: sbi: Add tests for HSM extension Date: Sun, 27 Oct 2024 00:18:13 +0800 Message-ID: <20241026161813.17189-2-jamestiotio@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20241026161813.17189-1-jamestiotio@gmail.com> References: <20241026161813.17189-1-jamestiotio@gmail.com> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20241026_091827_373091_A719C974 X-CRM114-Status: GOOD ( 18.01 ) X-Spam-Score: -2.1 (--) X-Spam-Report: Spam detection software, running on the system "bombadil.infradead.org", has NOT identified this incoming email as spam. The original message has been attached to this so you can view it or label similar future email. If you have any questions, see the administrator of that system for details. Content preview: Add some tests for all of the HSM extension functions. These tests ensure that the HSM extension functions follow the behavior as described in the SBI specification. Signed-off-by: James Raphael Tiovalen --- riscv/sbi.c | 663 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 663 insertions(+) Content analysis details: (-2.1 points, 5.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at https://www.dnswl.org/, no trust [2607:f8b0:4864:20:0:0:0:42e listed in] [list.dnswl.org] 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record -0.0 SPF_PASS SPF: sender matches SPF record -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature -0.1 DKIM_VALID_EF Message has a valid DKIM or DK signature from envelope-from domain -1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1% [score: 0.0000] 0.0 FREEMAIL_FROM Sender email is commonly abused enduser mail provider [jamestiotio(at)gmail.com] X-BeenThere: kvm-riscv@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "kvm-riscv" Errors-To: kvm-riscv-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org Add some tests for all of the HSM extension functions. These tests ensure that the HSM extension functions follow the behavior as described in the SBI specification. Signed-off-by: James Raphael Tiovalen --- riscv/sbi.c | 663 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 663 insertions(+) diff --git a/riscv/sbi.c b/riscv/sbi.c index 6f2d3e35..b09e2e45 100644 --- a/riscv/sbi.c +++ b/riscv/sbi.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -54,6 +55,11 @@ static struct sbiret sbi_dbcn_write_byte(uint8_t byte) return sbi_ecall(SBI_EXT_DBCN, SBI_EXT_DBCN_CONSOLE_WRITE_BYTE, byte, 0, 0, 0, 0, 0); } +static struct sbiret sbi_hart_suspend(uint32_t suspend_type, unsigned long resume_addr, unsigned long opaque) +{ + return sbi_ecall(SBI_EXT_HSM, SBI_EXT_HSM_HART_SUSPEND, suspend_type, resume_addr, opaque, 0, 0, 0); +} + static struct sbiret sbi_system_suspend(uint32_t sleep_type, unsigned long resume_addr, unsigned long opaque) { return sbi_ecall(SBI_EXT_SUSP, 0, sleep_type, resume_addr, opaque, 0, 0, 0); @@ -833,6 +839,662 @@ static void check_susp(void) report_prefix_pop(); } +unsigned char sbi_hsm_stop_hart[NR_CPUS]; +unsigned char sbi_hsm_hart_start_checks[NR_CPUS]; +unsigned char sbi_hsm_non_retentive_hart_suspend_checks[NR_CPUS]; +cpumask_t sbi_hsm_started_hart_checks; +static bool sbi_hsm_invalid_hartid_check; +static bool sbi_hsm_timer_fired; +extern void sbi_hsm_check_hart_start(void); +extern void sbi_hsm_check_non_retentive_suspend(void); + +static void hsm_timer_irq_handler(struct pt_regs *regs) +{ + sbi_hsm_timer_fired = true; + timer_stop(); +} + +static void hsm_timer_setup(void) +{ + install_irq_handler(IRQ_S_TIMER, hsm_timer_irq_handler); + local_irq_enable(); + timer_irq_enable(); +} + +static void hsm_timer_teardown(void) +{ + timer_irq_disable(); + local_irq_disable(); + install_irq_handler(IRQ_S_TIMER, NULL); +} + +static void hart_empty_fn(void *data) {} + +static void hart_execute(void *data) +{ + struct sbiret ret; + unsigned long hartid = current_thread_info()->hartid; + int me = smp_processor_id(); + + ret = sbi_hart_start(hartid, virt_to_phys(&hart_empty_fn), 0); + + if (ret.error == SBI_ERR_ALREADY_AVAILABLE) + cpumask_set_cpu(me, &sbi_hsm_started_hart_checks); +} + +static void hart_start_invalid_hartid(void *data) +{ + struct sbiret ret; + + ret = sbi_hart_start(ULONG_MAX, virt_to_phys(&hart_empty_fn), 0); + + if (ret.error == SBI_ERR_INVALID_PARAM) + sbi_hsm_invalid_hartid_check = true; +} + +static void hart_stop(void *data) +{ + unsigned long hartid = current_thread_info()->hartid; + struct sbiret ret = sbi_hart_stop(); + + report_fail("failed to stop hart %ld (error=%ld)", hartid, ret.error); +} + +static void hart_retentive_suspend(void *data) +{ + unsigned long hartid = current_thread_info()->hartid; + struct sbiret ret = sbi_hart_suspend(SBI_EXT_HSM_HART_SUSPEND_RETENTIVE, 0, 0); + + if (ret.error) + report_fail("failed to retentive suspend hart %ld (error=%ld)", hartid, ret.error); +} + +static void hart_non_retentive_suspend(void *data) +{ + unsigned long hartid = current_thread_info()->hartid; + + /* Set opaque as hartid so that we can check a0 == a1, ensuring that a0 is hartid and a1 is opaque */ + struct sbiret ret = sbi_hart_suspend(SBI_EXT_HSM_HART_SUSPEND_NON_RETENTIVE, + virt_to_phys(&sbi_hsm_check_non_retentive_suspend), hartid); + + report_fail("failed to non-retentive suspend hart %ld (error=%ld)", hartid, ret.error); +} + +static void hart_retentive_suspend_with_msb_set(void *data) +{ + unsigned long hartid = current_thread_info()->hartid; + unsigned long suspend_type = SBI_EXT_HSM_HART_SUSPEND_RETENTIVE | (_AC(1, UL) << (__riscv_xlen - 1)); + struct sbiret ret = sbi_ecall(SBI_EXT_HSM, SBI_EXT_HSM_HART_SUSPEND, suspend_type, 0, 0, 0, 0, 0); + + if (ret.error) + report_fail("failed to retentive suspend hart %ld with MSB set (error=%ld)", hartid, ret.error); +} + +static void hart_non_retentive_suspend_with_msb_set(void *data) +{ + unsigned long hartid = current_thread_info()->hartid; + unsigned long suspend_type = SBI_EXT_HSM_HART_SUSPEND_NON_RETENTIVE | (_AC(1, UL) << (__riscv_xlen - 1)); + + /* Set opaque as hartid so that we can check a0 == a1, ensuring that a0 is hartid and a1 is opaque */ + struct sbiret ret = sbi_ecall(SBI_EXT_HSM, SBI_EXT_HSM_HART_SUSPEND, suspend_type, + virt_to_phys(&sbi_hsm_check_non_retentive_suspend), hartid, 0, 0, 0); + + report_fail("failed to non-retentive suspend hart %ld with MSB set (error=%ld)", hartid, ret.error); +} + +static bool hart_wait_on_status(unsigned long hartid, enum sbi_ext_hsm_sid status, unsigned long duration) +{ + struct sbiret ret; + + sbi_hsm_timer_fired = false; + timer_start(duration); + + ret = sbi_hart_get_status(hartid); + + while (!ret.error && ret.value == status && !sbi_hsm_timer_fired) { + cpu_relax(); + ret = sbi_hart_get_status(hartid); + } + + timer_stop(); + + if (sbi_hsm_timer_fired) + report_info("timer fired while waiting on status %u for hart %ld", status, hartid); + else if (ret.error) + report_fail("got %ld while waiting on status %u for hart %ld\n", ret.error, status, hartid); + + return sbi_hsm_timer_fired || ret.error; +} + +static void check_hsm(void) +{ + struct sbiret ret; + unsigned long hartid; + cpumask_t secondary_cpus_mask, hsm_start, hsm_stop, hsm_suspend, hsm_resume, hsm_check; + bool ipi_unavailable = false; + bool suspend_with_msb = false, resume_with_msb = false, check_with_msb = false, stop_with_msb = false; + int cpu, me = smp_processor_id(); + int max_cpus = getenv("SBI_MAX_CPUS") ? strtol(getenv("SBI_MAX_CPUS"), NULL, 0) : nr_cpus; + unsigned long hsm_timer_duration = getenv("SBI_HSM_TIMER_DURATION") + ? strtol(getenv("SBI_HSM_TIMER_DURATION"), NULL, 0) : 200000; + + max_cpus = MIN(max_cpus, nr_cpus); + + report_prefix_push("hsm"); + + if (!sbi_probe(SBI_EXT_HSM)) { + report_skip("hsm extension not available"); + report_prefix_pop(); + return; + } + + report_prefix_push("hart_get_status"); + + hartid = current_thread_info()->hartid; + ret = sbi_hart_get_status(hartid); + + if (ret.error) { + report_fail("failed to get status of current hart (error=%ld)", ret.error); + report_prefix_popn(2); + return; + } else if (ret.value != SBI_EXT_HSM_STARTED) { + report_fail("current hart is not started (ret.value=%ld)", ret.value); + report_prefix_popn(2); + return; + } + + report_pass("status of current hart is started"); + + for_each_present_cpu(cpu) { + if (sbi_hart_get_status(cpus[cpu].hartid).error == SBI_ERR_INVALID_PARAM) + set_cpu_present(cpu, false); + } + + report(cpumask_weight(&cpu_present_mask) == nr_cpus, "all present harts have valid hartids"); + + report_prefix_pop(); + + if (max_cpus < 2) { + report_skip("no other cpus to run the remaining hsm tests on"); + report_prefix_pop(); + return; + } + + report_prefix_push("hart_stop"); + + cpumask_copy(&secondary_cpus_mask, &cpu_present_mask); + cpumask_clear_cpu(me, &secondary_cpus_mask); + hsm_timer_setup(); + + /* Assume that previous tests have not cleaned up and stopped the secondary harts */ + on_cpumask_async(&secondary_cpus_mask, hart_stop, NULL); + + cpumask_clear(&hsm_stop); + + for_each_cpu(cpu, &secondary_cpus_mask) { + hartid = cpus[cpu].hartid; + if (hart_wait_on_status(hartid, SBI_EXT_HSM_STARTED, hsm_timer_duration)) + continue; + if (hart_wait_on_status(hartid, SBI_EXT_HSM_STOP_PENDING, hsm_timer_duration)) + continue; + + ret = sbi_hart_get_status(hartid); + if (ret.error) + report_info("hart %ld get status failed (error=%ld)", hartid, ret.error); + else if (ret.value != SBI_EXT_HSM_STOPPED) + report_info("hart %ld status is not 'stopped' (ret.value=%ld)", hartid, ret.value); + else + cpumask_set_cpu(cpu, &hsm_stop); + } + + report(cpumask_weight(&hsm_stop) == max_cpus - 1, "all secondary harts stopped"); + + report_prefix_pop(); + + report_prefix_push("hart_start"); + + cpumask_clear(&hsm_start); + cpumask_clear(&hsm_check); + + for_each_cpu(cpu, &secondary_cpus_mask) { + hartid = cpus[cpu].hartid; + /* Set opaque as hartid so that we can check a0 == a1, ensuring that a0 is hartid and a1 is opaque */ + ret = sbi_hart_start(hartid, virt_to_phys(&sbi_hsm_check_hart_start), hartid); + if (ret.error) { + report_fail("failed to start test hart %ld (error=%ld)", hartid, ret.error); + continue; + } + + if (hart_wait_on_status(hartid, SBI_EXT_HSM_STOPPED, hsm_timer_duration)) + continue; + if (hart_wait_on_status(hartid, SBI_EXT_HSM_START_PENDING, hsm_timer_duration)) + continue; + + ret = sbi_hart_get_status(hartid); + if (ret.error) { + report_info("hart %ld get status failed (error=%ld)", hartid, ret.error); + continue; + } else if (ret.value != SBI_EXT_HSM_STARTED) { + report_info("hart %ld status is not 'started' (ret.value=%ld)", hartid, ret.value); + continue; + } else { + cpumask_set_cpu(cpu, &hsm_start); + } + + sbi_hsm_timer_fired = false; + timer_start(hsm_timer_duration); + + while (!(READ_ONCE(sbi_hsm_hart_start_checks[cpu]) & SBI_HSM_TEST_DONE) && !sbi_hsm_timer_fired) + cpu_relax(); + + timer_stop(); + + if (sbi_hsm_timer_fired) { + report_info("hsm timer fired before hart %ld is done with start checks", hartid); + continue; + } + + if (!(sbi_hsm_hart_start_checks[cpu] & SBI_HSM_TEST_SATP)) + report_info("satp is not zero for test hart %ld", hartid); + else if (!(sbi_hsm_hart_start_checks[cpu] & SBI_HSM_TEST_SIE)) + report_info("sstatus.SIE is not zero for test hart %ld", hartid); + else if (!(sbi_hsm_hart_start_checks[cpu] & SBI_HSM_TEST_HARTID_A1)) + report_info("either a0 or a1 is not hartid for test hart %ld", hartid); + else + cpumask_set_cpu(cpu, &hsm_check); + } + + report(cpumask_weight(&hsm_start) == max_cpus - 1, "all secondary harts started"); + report(cpumask_weight(&hsm_check) == max_cpus - 1, + "all secondary harts have expected register values after hart start"); + + report_prefix_pop(); + + report_prefix_push("hart_stop"); + + memset(sbi_hsm_stop_hart, 1, sizeof(sbi_hsm_stop_hart)); + + cpumask_clear(&hsm_stop); + + for_each_cpu(cpu, &secondary_cpus_mask) { + hartid = cpus[cpu].hartid; + if (hart_wait_on_status(hartid, SBI_EXT_HSM_STARTED, hsm_timer_duration)) + continue; + if (hart_wait_on_status(hartid, SBI_EXT_HSM_STOP_PENDING, hsm_timer_duration)) + continue; + + ret = sbi_hart_get_status(hartid); + if (ret.error) + report_info("hart %ld get status failed (error=%ld)", hartid, ret.error); + else if (ret.value != SBI_EXT_HSM_STOPPED) + report_info("hart %ld status is not 'stopped' (ret.value=%ld)", hartid, ret.value); + else + cpumask_set_cpu(cpu, &hsm_stop); + } + + report(cpumask_weight(&hsm_stop) == max_cpus - 1, "all secondary harts stopped"); + + /* Reset the stop flags so that we can reuse them after suspension tests */ + memset(sbi_hsm_stop_hart, 0, sizeof(sbi_hsm_stop_hart)); + + report_prefix_pop(); + + report_prefix_push("hart_start"); + + /* Select just one secondary cpu to run the invalid hartid test */ + on_cpu(cpumask_next(-1, &secondary_cpus_mask), hart_start_invalid_hartid, NULL); + + report(sbi_hsm_invalid_hartid_check, "secondary hart refuse to start with invalid hartid"); + + on_cpumask_async(&secondary_cpus_mask, hart_execute, NULL); + + cpumask_clear(&hsm_start); + + for_each_cpu(cpu, &secondary_cpus_mask) { + hartid = cpus[cpu].hartid; + if (hart_wait_on_status(hartid, SBI_EXT_HSM_STOPPED, hsm_timer_duration)) + continue; + if (hart_wait_on_status(hartid, SBI_EXT_HSM_START_PENDING, hsm_timer_duration)) + continue; + + ret = sbi_hart_get_status(hartid); + if (ret.error) + report_info("hart %ld get status failed (error=%ld)", hartid, ret.error); + else if (ret.value != SBI_EXT_HSM_STARTED) + report_info("hart %ld status is not 'started' (ret.value=%ld)", hartid, ret.value); + else + cpumask_set_cpu(cpu, &hsm_start); + } + + report(cpumask_weight(&hsm_start) == max_cpus - 1, "all secondary harts started"); + + sbi_hsm_timer_fired = false; + timer_start(hsm_timer_duration); + + while (cpumask_weight(&cpu_idle_mask) != max_cpus - 1 && !sbi_hsm_timer_fired) + cpu_relax(); + + timer_stop(); + + if (sbi_hsm_timer_fired) + report_info("hsm timer fired before all secondary harts started"); + + report(cpumask_weight(&cpu_idle_mask) == max_cpus - 1, + "all secondary harts successfully executed code after start"); + report(cpumask_weight(&cpu_online_mask) == max_cpus, "all secondary harts online"); + report(cpumask_weight(&sbi_hsm_started_hart_checks) == max_cpus - 1, + "all secondary harts are already started"); + + report_prefix_pop(); + + report_prefix_push("hart_suspend"); + + if (!sbi_probe(SBI_EXT_IPI)) { + report_skip("skipping suspension tests since ipi extension is unavailable"); + report_prefix_pop(); + ipi_unavailable = true; + goto sbi_hsm_hart_stop_tests; + } + + on_cpumask_async(&secondary_cpus_mask, hart_retentive_suspend, NULL); + + cpumask_clear(&hsm_suspend); + + for_each_cpu(cpu, &secondary_cpus_mask) { + hartid = cpus[cpu].hartid; + if (hart_wait_on_status(hartid, SBI_EXT_HSM_STARTED, hsm_timer_duration)) + continue; + if (hart_wait_on_status(hartid, SBI_EXT_HSM_SUSPEND_PENDING, hsm_timer_duration)) + continue; + + ret = sbi_hart_get_status(hartid); + if (ret.error) + report_info("hart %ld get status failed (error=%ld)", hartid, ret.error); + else if (ret.value != SBI_EXT_HSM_SUSPENDED) + report_info("hart %ld status is not 'suspended' (ret.value=%ld)", hartid, ret.value); + else + cpumask_set_cpu(cpu, &hsm_suspend); + } + + report(cpumask_weight(&hsm_suspend) == max_cpus - 1, "all secondary harts retentive suspended"); + + /* Ignore the return value since we check the status of each hart anyway */ + sbi_send_ipi_cpumask(&secondary_cpus_mask); + + cpumask_clear(&hsm_resume); + + for_each_cpu(cpu, &secondary_cpus_mask) { + hartid = cpus[cpu].hartid; + if (hart_wait_on_status(hartid, SBI_EXT_HSM_SUSPENDED, hsm_timer_duration)) + continue; + if (hart_wait_on_status(hartid, SBI_EXT_HSM_RESUME_PENDING, hsm_timer_duration)) + continue; + + ret = sbi_hart_get_status(hartid); + if (ret.error) + report_info("hart %ld get status failed (error=%ld)", hartid, ret.error); + else if (ret.value != SBI_EXT_HSM_STARTED) + report_info("hart %ld status is not 'started' (ret.value=%ld)", hartid, ret.value); + else + cpumask_set_cpu(cpu, &hsm_resume); + } + + report(cpumask_weight(&hsm_resume) == max_cpus - 1, "all secondary harts retentive resumed"); + + sbi_hsm_timer_fired = false; + timer_start(hsm_timer_duration); + + while (cpumask_weight(&cpu_idle_mask) != max_cpus - 1 && !sbi_hsm_timer_fired) + cpu_relax(); + + timer_stop(); + + if (sbi_hsm_timer_fired) + report_info("hsm timer fired before all secondary harts retentive resumed"); + + report(cpumask_weight(&cpu_idle_mask) == max_cpus - 1, + "all secondary harts successfully executed code after retentive suspend"); + report(cpumask_weight(&cpu_online_mask) == max_cpus, + "all secondary harts online"); + + on_cpumask_async(&secondary_cpus_mask, hart_non_retentive_suspend, NULL); + + cpumask_clear(&hsm_suspend); + + for_each_cpu(cpu, &secondary_cpus_mask) { + hartid = cpus[cpu].hartid; + if (hart_wait_on_status(hartid, SBI_EXT_HSM_STARTED, hsm_timer_duration)) + continue; + if (hart_wait_on_status(hartid, SBI_EXT_HSM_SUSPEND_PENDING, hsm_timer_duration)) + continue; + + ret = sbi_hart_get_status(hartid); + if (ret.error) + report_info("hart %ld get status failed (error=%ld)", hartid, ret.error); + else if (ret.value != SBI_EXT_HSM_SUSPENDED) + report_info("hart %ld status is not 'suspended' (ret.value=%ld)", hartid, ret.value); + else + cpumask_set_cpu(cpu, &hsm_suspend); + } + + report(cpumask_weight(&hsm_suspend) == max_cpus - 1, "all secondary harts non-retentive suspended"); + + /* Ignore the return value since we check the status of each hart anyway */ + sbi_send_ipi_cpumask(&secondary_cpus_mask); + + cpumask_clear(&hsm_resume); + cpumask_clear(&hsm_check); + + for_each_cpu(cpu, &secondary_cpus_mask) { + hartid = cpus[cpu].hartid; + if (hart_wait_on_status(hartid, SBI_EXT_HSM_SUSPENDED, hsm_timer_duration)) + continue; + if (hart_wait_on_status(hartid, SBI_EXT_HSM_RESUME_PENDING, hsm_timer_duration)) + continue; + + ret = sbi_hart_get_status(hartid); + if (ret.error) { + report_info("hart %ld get status failed (error=%ld)", hartid, ret.error); + continue; + } else if (ret.value != SBI_EXT_HSM_STARTED) { + report_info("hart %ld status is not 'started' (ret.value=%ld)", hartid, ret.value); + continue; + } else { + cpumask_set_cpu(cpu, &hsm_resume); + } + + sbi_hsm_timer_fired = false; + timer_start(hsm_timer_duration); + + while (!((READ_ONCE(sbi_hsm_non_retentive_hart_suspend_checks[cpu])) & SBI_HSM_TEST_DONE) + && !sbi_hsm_timer_fired) + cpu_relax(); + + timer_stop(); + + if (sbi_hsm_timer_fired) { + report_info("hsm timer fired before hart %ld is done with non-retentive resume checks", + hartid); + continue; + } + + if (!(sbi_hsm_non_retentive_hart_suspend_checks[cpu] & SBI_HSM_TEST_SATP)) + report_info("satp is not zero for test hart %ld", hartid); + else if (!(sbi_hsm_non_retentive_hart_suspend_checks[cpu] & SBI_HSM_TEST_SIE)) + report_info("sstatus.SIE is not zero for test hart %ld", hartid); + else if (!(sbi_hsm_non_retentive_hart_suspend_checks[cpu] & SBI_HSM_TEST_HARTID_A1)) + report_info("either a0 or a1 is not hartid for test hart %ld", hartid); + else + cpumask_set_cpu(cpu, &hsm_check); + } + + report(cpumask_weight(&hsm_resume) == max_cpus - 1, "all secondary harts non-retentive resumed"); + report(cpumask_weight(&hsm_check) == max_cpus - 1, + "all secondary harts have expected register values after non-retentive resume"); + + report_prefix_pop(); + +sbi_hsm_hart_stop_tests: + report_prefix_push("hart_stop"); + + if (ipi_unavailable) + on_cpumask_async(&secondary_cpus_mask, hart_stop, NULL); + else + memset(sbi_hsm_stop_hart, 1, sizeof(sbi_hsm_stop_hart)); + + cpumask_clear(&hsm_stop); + + for_each_cpu(cpu, &secondary_cpus_mask) { + hartid = cpus[cpu].hartid; + if (hart_wait_on_status(hartid, SBI_EXT_HSM_STARTED, hsm_timer_duration)) + continue; + if (hart_wait_on_status(hartid, SBI_EXT_HSM_STOP_PENDING, hsm_timer_duration)) + continue; + + ret = sbi_hart_get_status(hartid); + if (ret.error) + report_info("hart %ld get status failed (error=%ld)", hartid, ret.error); + else if (ret.value != SBI_EXT_HSM_STOPPED) + report_info("hart %ld status is not 'stopped' (ret.value=%ld)", hartid, ret.value); + else + cpumask_set_cpu(cpu, &hsm_stop); + } + + report(cpumask_weight(&hsm_stop) == max_cpus - 1, "all secondary harts stopped"); + + if (__riscv_xlen == 32 || ipi_unavailable) { + hsm_timer_teardown(); + report_prefix_popn(2); + return; + } + + report_prefix_pop(); + + report_prefix_push("hart_suspend"); + + /* Select just one secondary cpu to run suspension tests with MSB of suspend type being set */ + cpu = cpumask_next(-1, &secondary_cpus_mask); + hartid = cpus[cpu].hartid; + + /* Boot up the secondary cpu and let it proceed to the idle loop */ + on_cpu(cpu, hart_empty_fn, NULL); + + on_cpu_async(cpu, hart_retentive_suspend_with_msb_set, NULL); + + if (!hart_wait_on_status(hartid, SBI_EXT_HSM_STARTED, hsm_timer_duration) && + !hart_wait_on_status(hartid, SBI_EXT_HSM_SUSPEND_PENDING, hsm_timer_duration)) { + ret = sbi_hart_get_status(hartid); + if (ret.error) + report_info("hart %ld get status failed (error=%ld)", hartid, ret.error); + else if (ret.value != SBI_EXT_HSM_SUSPENDED) + report_info("hart %ld status is not 'suspended' (ret.value=%ld)", hartid, ret.value); + else + suspend_with_msb = true; + } + + report(suspend_with_msb, "secondary hart retentive suspended with MSB set"); + + /* Ignore the return value since we manually validate the status of the hart anyway */ + sbi_send_ipi_cpu(cpu); + + if (!hart_wait_on_status(hartid, SBI_EXT_HSM_SUSPENDED, hsm_timer_duration) && + !hart_wait_on_status(hartid, SBI_EXT_HSM_RESUME_PENDING, hsm_timer_duration)) { + ret = sbi_hart_get_status(hartid); + if (ret.error) + report_info("hart %ld get status failed (error=%ld)", hartid, ret.error); + else if (ret.value != SBI_EXT_HSM_STARTED) + report_info("hart %ld status is not 'started' (ret.value=%ld)", hartid, ret.value); + else + resume_with_msb = true; + } + + report(resume_with_msb, "secondary hart retentive resumed with MSB set"); + + /* Reset these flags so that we can reuse them for the non-retentive suspension test */ + suspend_with_msb = false; + resume_with_msb = false; + sbi_hsm_stop_hart[cpu] = 0; + sbi_hsm_non_retentive_hart_suspend_checks[cpu] = 0; + + on_cpu_async(cpu, hart_non_retentive_suspend_with_msb_set, NULL); + + if (!hart_wait_on_status(hartid, SBI_EXT_HSM_STARTED, hsm_timer_duration) && + !hart_wait_on_status(hartid, SBI_EXT_HSM_SUSPEND_PENDING, hsm_timer_duration)) { + ret = sbi_hart_get_status(hartid); + if (ret.error) + report_info("hart %ld get status failed (error=%ld)", hartid, ret.error); + else if (ret.value != SBI_EXT_HSM_SUSPENDED) + report_info("hart %ld status is not 'suspended' (ret.value=%ld)", hartid, ret.value); + else + suspend_with_msb = true; + } + + report(suspend_with_msb, "secondary hart non-retentive suspended with MSB set"); + + /* Ignore the return value since we manually validate the status of the hart anyway */ + sbi_send_ipi_cpu(cpu); + + if (!hart_wait_on_status(hartid, SBI_EXT_HSM_SUSPENDED, hsm_timer_duration) && + !hart_wait_on_status(hartid, SBI_EXT_HSM_RESUME_PENDING, hsm_timer_duration)) { + ret = sbi_hart_get_status(hartid); + if (ret.error) + report_info("hart %ld get status failed (error=%ld)", hartid, ret.error); + else if (ret.value != SBI_EXT_HSM_STARTED) + report_info("hart %ld status is not 'started' (ret.value=%ld)", hartid, ret.value); + else + resume_with_msb = true; + + sbi_hsm_timer_fired = false; + timer_start(hsm_timer_duration); + + while (!((READ_ONCE(sbi_hsm_non_retentive_hart_suspend_checks[cpu])) & SBI_HSM_TEST_DONE) + && !sbi_hsm_timer_fired) + cpu_relax(); + + timer_stop(); + + if (sbi_hsm_timer_fired) { + report_info("hsm timer fired before hart %ld is done with non-retentive resume checks", + hartid); + } else { + if (!(sbi_hsm_non_retentive_hart_suspend_checks[cpu] & SBI_HSM_TEST_SATP)) + report_info("satp is not zero for test hart %ld", hartid); + else if (!(sbi_hsm_non_retentive_hart_suspend_checks[cpu] & SBI_HSM_TEST_SIE)) + report_info("sstatus.SIE is not zero for test hart %ld", hartid); + else if (!(sbi_hsm_non_retentive_hart_suspend_checks[cpu] & SBI_HSM_TEST_HARTID_A1)) + report_info("either a0 or a1 is not hartid for test hart %ld", hartid); + else + check_with_msb = true; + } + } + + report(resume_with_msb, "secondary hart non-retentive resumed with MSB set"); + report(check_with_msb, + "secondary hart has expected register values after non-retentive resume with MSB set"); + + report_prefix_pop(); + + report_prefix_push("hart_stop"); + + sbi_hsm_stop_hart[cpu] = 1; + + if (!hart_wait_on_status(hartid, SBI_EXT_HSM_STARTED, hsm_timer_duration) && + !hart_wait_on_status(hartid, SBI_EXT_HSM_STOP_PENDING, hsm_timer_duration)) { + ret = sbi_hart_get_status(hartid); + if (ret.error) + report_info("hart %ld get status failed (error=%ld)", hartid, ret.error); + else if (ret.value != SBI_EXT_HSM_STOPPED) + report_info("hart %ld status is not 'stopped' (ret.value=%ld)", hartid, ret.value); + else + stop_with_msb = true; + } + + report(stop_with_msb, "secondary hart stopped after suspension tests with MSB set"); + + hsm_timer_teardown(); + report_prefix_popn(2); +} + int main(int argc, char **argv) { if (argc > 1 && !strcmp(argv[1], "-h")) { @@ -844,6 +1506,7 @@ int main(int argc, char **argv) check_base(); check_time(); check_ipi(); + check_hsm(); check_dbcn(); check_susp();