blob: 08b86f1583ccddcdaca48f7cb5651d03b648cd20 [file] [log] [blame]
/*
* Copyright (C) 2025 The Android Open Source Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#pragma once
#include <sys/auxv.h>
#include <cstdint>
#if defined(__aarch64__)
#include <arm_sme.h>
// Detects whether FEAT_SME is available.
//
// FEAT_SME is optional from Armv9.2.
[[maybe_unused]] static bool sme_is_enabled() {
return ((getauxval(AT_HWCAP2) & HWCAP2_SME) != 0);
}
// Sets PSTATE.SM to 0.
//
// Requires FEAT_SME, which is optional from Armv9.2.
[[maybe_unused]] __attribute__((naked)) static void sme_disable_sm() {
__asm__ __volatile__(".arch_extension sme; bti c; smstop sm; ret;");
}
// Sets PSTATE.ZA to 1.
//
// Requires FEAT_SME, which is optional from Armv9.2.
[[maybe_unused]] __attribute__((naked)) static void sme_enable_za() {
__asm__ __volatile__(".arch_extension sme; bti c; smstart za; ret;");
}
// Sets PSTATE.ZA to 0.
//
// Requires FEAT_SME, which is optional from Armv9.2.
[[maybe_unused]] __attribute__((naked)) static void sme_disable_za() {
__asm__ __volatile__(".arch_extension sme; bti c; smstop za; ret;");
}
// Sets TPIDR2_EL0 to a given value.
//
// Requires FEAT_SME, which is optional from Armv9.2.
[[maybe_unused]] __attribute__((naked)) static void sme_set_tpidr2_el0(uint64_t value) {
__asm__ __volatile__(".arch_extension sme; bti c; msr TPIDR2_EL0, x0; ret;");
}
// Reads TPIDR2_EL0 and returns its value.
//
// Requires FEAT_SME, which is optional from Armv9.2.
[[maybe_unused]] __attribute__((naked)) static uint64_t sme_tpidr2_el0() {
__asm__ __volatile__(".arch_extension sme; bti c; mrs x0, TPIDR2_EL0; ret;");
}
// Reads SVCR special register.
//
// Requires FEAT_SME, which is optional from Armv9.2.
[[maybe_unused]] __attribute__((naked)) static uint64_t sme_read_svcr() {
__asm__ __volatile__(".arch_extension sme; bti c; mrs x0, SVCR; ret;");
}
// Returns true if PSTATE.SM is 1, otherwise false.
//
// Requires FEAT_SME, which is optional from Armv9.2.
[[maybe_unused]] static bool sme_is_sm_on() {
static constexpr uint64_t kSvcrSmMask = 0x01UL;
return ((sme_read_svcr() & kSvcrSmMask) != 0);
}
// Returns true if PSTATE.ZA is 1, otherwise false.
//
// Requires FEAT_SME, which is optional from Armv9.2.
[[maybe_unused]] static bool sme_is_za_on() {
static constexpr uint64_t kSvcrZaMask = 0x02UL;
return ((sme_read_svcr() & kSvcrZaMask) != 0);
}
// Assembly is required to ensure the test does not depend on compiler optimizations.
[[maybe_unused]] __attribute__((naked)) static void sme_dormant_caller(void (*fn_address)()) {
// clang-format off
__asm__ __volatile__(
".arch_extension sme\n\r"
"bti c\n\r"
"stp fp, lr, [sp, #-16]!\n\r"
"mov fp, sp\n\r"
// Set up a lazy-save buffer on the stack.
// It is 16 bytes + size according to VL.
"sub sp, sp, #16\n\r"
"rdsvl x8, #1\n\r"
"mul x9, x8, x8\n\r"
"sub sp, sp, x9\n\r"
"mov x9, sp\n\r"
// Bytes 0-7: za_save_buffer
// Bytes 8-9: num_za_save_slices
// Other bytes are cleared.
"stp x9, x8, [fp, #-16]\n\r"
// Finalize the lazy-save buffer.
"msr TPIDR2_EL0, x9\n\r"
// Call the given function with dormant SME state.
"smstart za\n\r"
"blr x0\n\r"
// Set SME state to off.
"msr TPIDR2_EL0, xzr\n\r"
"smstop za\n\r"
"mov sp, fp\n\r"
"ldp fp, lr, [sp], #16\n\r"
"ret\n\r"
);
// clang-format on
}
// Turns all SME state off.
//
// Requires FEAT_SME, which is optional from Armv9.2.
[[maybe_unused]] static void sme_state_cleanup() {
sme_disable_sm();
sme_set_tpidr2_el0(0UL);
sme_disable_za();
}
#endif // defined(__aarch64__)