From patchwork Sat Sep 21 10:08:23 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: 1988132 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=t3s4pDNv; 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=VI78LHD9; 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 4X9lgl0Qpxz1y36 for ; Sat, 21 Sep 2024 20:22:43 +1000 (AEST) 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=oLl+lPp9Gcxmx/W8kMqAH+sNbPsFlItNpZx7eoxdscU=; b=t3s4pDNvdL77t9 PJm9juD/HgQrBts0Wq6ibJ59837hZ2z0ijm6fKplGsnzAFv3eUYtQ6FVpto4trJ2gfygQm6rUPywK /wE4+iJNs5W8nI1Ky+23T2hvuI+iRLe2EVh8bItuJCQmJ1Clg1aaJdFHe+JrLx7SNnIPPwFMaSeOI XWE0z0I08PTE/RczXsaWooFndaOBlzY6RvABREtbJtvBwK9e2HLTZxyPokbeD30cYBQp4iyUV0fiR 54Lm4sHZBNSWOLBdSJJMZukBMrr1KoQHQ/m5QlyQLkdZPmxEQju8z24666YOJUniO6dvz8rvMVYQv W9ZI3au8jcrOjpiN673Q==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.98 #2 (Red Hat Linux)) id 1srxGD-0000000Dbq3-3YE5; Sat, 21 Sep 2024 10:22:41 +0000 Received: from mail-pj1-x1031.google.com ([2607:f8b0:4864:20::1031]) by bombadil.infradead.org with esmtps (Exim 4.98 #2 (Red Hat Linux)) id 1srx2k-0000000DaXF-45AY for kvm-riscv@lists.infradead.org; Sat, 21 Sep 2024 10:08:49 +0000 Received: by mail-pj1-x1031.google.com with SMTP id 98e67ed59e1d1-2d8a744aa9bso2003821a91.3 for ; Sat, 21 Sep 2024 03:08:46 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1726913326; x=1727518126; 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=Gh0oI/atX+NM3kjZRs6CAv7WW1PdJeWlMXibH7CAlQM=; b=VI78LHD9pwNxNLOIowvsr2XbYvrhOGa1YKg5X7QVEBdFTIxTx9+R9nxy1YWDIzU0n0 dRZlsIJzwKzwJd5lmm/OhCPUM+ASwvFvqeNZ80ZHLu+rXyUioashE0eXn1cQs7+ifp/Y XsF5Nd162pxGoF7A564J7wLRH4D/M213h94zQzKndS9vdBpRDqdKu1L30bsj3Py5BGnh 06U+NJtaGr4raJ/JYVn7MUZEn/XFeFIUUQnaO3DT+Y8h3MHpPkyvJcRwSXHwSe56hjE3 dX2mKdzekiNSwbSMsuHspPPLg0JQ0kk7fWq/vZUbasOlN6uG6ylCFBkoWrV7gUmIh2CM xPow== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1726913326; x=1727518126; 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=Gh0oI/atX+NM3kjZRs6CAv7WW1PdJeWlMXibH7CAlQM=; b=qc8LCzOYZ48ioTOO6BWjMJVYfDe70Vg5tqQ9AFXg9vmhHiH4QF6GuIhMfEY9tsojLo I/sc1QlNhZ1bBWQc0kreX5gN1rXoxJ7a3dJ07Ydliy384jyHyVkX+h/dd1Qku+As7PxJ zvg92szh9wpkqbw1GAl1j5SpPhb7LJoptN7l8TaGxuNWBrrbS9i5zkPdlpK2aC+ILz94 lJ56uMXgo6a+9vV0/TBdjdFpBFD26oMzng/s787xxqlxaE7rhE7V/Jpk8loDcfIHzEpu aEXjlz4L2SmLpA5rj6JBUB9EK7Inbs+npAJJLspM2eHcsGIEL2qgNONUZzdXE3/EkHGT fHsw== X-Forwarded-Encrypted: i=1; AJvYcCWATm0xN9GUut0zRHhW6V/XiizxZSE0gDAUZTOvWe+ugm9NAopViLtBIaE3s7XQn/R8+GSK5zCKxgE=@lists.infradead.org X-Gm-Message-State: AOJu0Yy9kCMUGgOb0L2NKzJI61Z+LyJeelH45+1oCFWlLOGxSR3HCkQA GbLXhEnto+TrjzxuPN92Y/0JUTqLT+/GCuzo3Uufl06R/CL+oQDd X-Google-Smtp-Source: AGHT+IFmQ1WIJjK7hdvJekNzrqonXR7dlnFDzaYWcvcdWQRkVPk13MzEkqtY8CWNy3VmJUEnD7d8CQ== X-Received: by 2002:a17:90b:184f:b0:2d3:db53:5577 with SMTP id 98e67ed59e1d1-2dd7f71a85emr7402814a91.36.1726913325935; Sat, 21 Sep 2024 03:08:45 -0700 (PDT) Received: from JRT-PC.. ([203.116.176.98]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-2dd6ee7c03fsm5680024a91.11.2024.09.21.03.08.43 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 21 Sep 2024 03:08:45 -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 v5 5/5] riscv: sbi: Add tests for HSM extension Date: Sat, 21 Sep 2024 18:08:23 +0800 Message-ID: <20240921100824.151761-6-jamestiotio@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240921100824.151761-1-jamestiotio@gmail.com> References: <20240921100824.151761-1-jamestiotio@gmail.com> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20240921_030847_060832_2BBF7157 X-CRM114-Status: GOOD ( 18.52 ) 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 | 648 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 648 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:1031 listed in] [list.dnswl.org] -0.0 SPF_PASS SPF: sender matches SPF record 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.1 DKIM_VALID_EF Message has a valid DKIM or DK signature from envelope-from domain -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature -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 | 648 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 648 insertions(+) diff --git a/riscv/sbi.c b/riscv/sbi.c index b5147dee..cd1ed95b 100644 --- a/riscv/sbi.c +++ b/riscv/sbi.c @@ -6,6 +6,8 @@ */ #include #include +#include +#include #include #include #include @@ -16,12 +18,15 @@ #include #include #include +#include #include #include #include #include #include +#include "sbi-tests.h" + #define HIGH_ADDR_BOUNDARY ((phys_addr_t)1 << 32) static void help(void) @@ -47,6 +52,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 void split_phys_addr(phys_addr_t paddr, unsigned long *hi, unsigned long *lo) { *lo = (unsigned long)paddr; @@ -433,6 +443,643 @@ static void check_dbcn(void) 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_start"); + + cpumask_copy(&secondary_cpus_mask, &cpu_present_mask); + cpumask_clear_cpu(me, &secondary_cpus_mask); + hsm_timer_setup(); + + 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"); + + /* Reset the state of the secondary cpus since they did not have a chance to clean up after stopping */ + for_each_cpu(cpu, &secondary_cpus_mask) { + on_cpu_clear_func(cpu); + set_cpu_online(cpu, false); + set_cpu_started(cpu, false); + } + + 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"); + + /* Reset the state of the secondary cpu since it did not have a chance to clean up after stopping */ + on_cpu_clear_func(cpu); + set_cpu_online(cpu, false); + set_cpu_started(cpu, false); + + hsm_timer_teardown(); + report_prefix_popn(2); +} int main(int argc, char **argv) { @@ -444,6 +1091,7 @@ int main(int argc, char **argv) report_prefix_push("sbi"); check_base(); check_time(); + check_hsm(); check_dbcn(); return report_summary();