Skip to content

Commit 855be25

Browse files
committed
[libunwind] Disable ZA before resuming from unwinding (on Linux)
This patch reimplements the SME ABI `__arm_za_disable` routine within libunwind. This routine must be called before resuming from unwinding on AArch64 platforms with SME support. Before calling the routine, we need to check that SME is available. In this patch, this is implemented for Linux-based platforms by checking HWCAP2. It should be possible to implement this check for other platforms as required. This patch includes a test for this functionality. This test requires SME, so on platforms without it, it will simply pass.
1 parent 54d86df commit 855be25

File tree

2 files changed

+99
-15
lines changed

2 files changed

+99
-15
lines changed

libunwind/src/Registers.hpp

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@
2020
#include "libunwind_ext.h"
2121
#include "shadow_stack_unwind.h"
2222

23+
#if __has_include(<sys/auxv.h>)
24+
#include <sys/auxv.h>
25+
#define HAVE_SYS_AUXV_H
26+
#endif
27+
2328
namespace libunwind {
2429

2530
// For emulating 128-bit registers
@@ -1828,6 +1833,7 @@ inline const char *Registers_ppc64::getRegisterName(int regNum) {
18281833
/// process.
18291834
class _LIBUNWIND_HIDDEN Registers_arm64;
18301835
extern "C" void __libunwind_Registers_arm64_jumpto(Registers_arm64 *);
1836+
extern "C" int64_t __libunwind_Registers_arm64_za_disable();
18311837

18321838
#if defined(_LIBUNWIND_USE_GCS)
18331839
extern "C" void *__libunwind_shstk_get_jump_target() {
@@ -1837,7 +1843,7 @@ extern "C" void *__libunwind_shstk_get_jump_target() {
18371843

18381844
class _LIBUNWIND_HIDDEN Registers_arm64 {
18391845
public:
1840-
Registers_arm64();
1846+
Registers_arm64() = default;
18411847
Registers_arm64(const void *registers);
18421848
Registers_arm64(const Registers_arm64 &);
18431849
Registers_arm64 &operator=(const Registers_arm64 &);
@@ -1855,7 +1861,10 @@ class _LIBUNWIND_HIDDEN Registers_arm64 {
18551861
v128 getVectorRegister(int num) const;
18561862
void setVectorRegister(int num, v128 value);
18571863
static const char *getRegisterName(int num);
1858-
void jumpto() { __libunwind_Registers_arm64_jumpto(this); }
1864+
void jumpto() {
1865+
zaDisable();
1866+
__libunwind_Registers_arm64_jumpto(this);
1867+
}
18591868
static constexpr int lastDwarfRegNum() {
18601869
return _LIBUNWIND_HIGHEST_DWARF_REGISTER_ARM64;
18611870
}
@@ -1908,25 +1917,43 @@ class _LIBUNWIND_HIDDEN Registers_arm64 {
19081917
private:
19091918
uint64_t lazyGetVG() const;
19101919

1920+
void zaDisable() const {
1921+
if (!_misc_registers.__has_sme)
1922+
return;
1923+
if (__libunwind_Registers_arm64_za_disable() != 0)
1924+
_LIBUNWIND_ABORT("SME ZA disable failed");
1925+
}
1926+
1927+
static bool checkHasSME() {
1928+
#if defined(HAVE_SYS_AUXV_H)
1929+
constexpr int hwcap2_sme = (1 << 23);
1930+
unsigned long hwcap2 = getauxval(AT_HWCAP2);
1931+
return (hwcap2 & hwcap2_sme) != 0;
1932+
#endif
1933+
// TODO: Support other platforms.
1934+
return false;
1935+
}
1936+
19111937
struct GPRs {
1912-
uint64_t __x[29]; // x0-x28
1913-
uint64_t __fp; // Frame pointer x29
1914-
uint64_t __lr; // Link register x30
1915-
uint64_t __sp; // Stack pointer x31
1916-
uint64_t __pc; // Program counter
1917-
uint64_t __ra_sign_state; // RA sign state register
1938+
uint64_t __x[29] = {}; // x0-x28
1939+
uint64_t __fp = 0; // Frame pointer x29
1940+
uint64_t __lr = 0; // Link register x30
1941+
uint64_t __sp = 0; // Stack pointer x31
1942+
uint64_t __pc = 0; // Program counter
1943+
uint64_t __ra_sign_state = 0; // RA sign state register
19181944
};
19191945

19201946
struct Misc {
1921-
mutable uint64_t __vg = 0; // Vector Granule
1947+
mutable uint32_t __vg = 0; // Vector Granule
1948+
bool __has_sme = checkHasSME();
19221949
};
19231950

1924-
GPRs _registers;
1951+
GPRs _registers = {};
19251952
// Currently only the lower double in 128-bit vectore registers
19261953
// is perserved during unwinding. We could define new register
19271954
// numbers (> 96) which mean whole vector registers, then this
19281955
// struct would need to change to contain whole vector registers.
1929-
double _vectorHalfRegisters[32];
1956+
double _vectorHalfRegisters[32] = {};
19301957

19311958
// Miscellaneous/virtual registers. These are stored below the GPRs and FPRs
19321959
// as they do not correspond to physical registers, so do not need to be
@@ -1971,10 +1998,6 @@ Registers_arm64::operator=(const Registers_arm64 &other) {
19711998
return *this;
19721999
}
19732000

1974-
inline Registers_arm64::Registers_arm64() {
1975-
memset(static_cast<void *>(this), 0, sizeof(*this));
1976-
}
1977-
19782001
inline bool Registers_arm64::validRegister(int regNum) const {
19792002
if (regNum == UNW_REG_IP)
19802003
return true;

libunwind/src/UnwindRegistersSave.S

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -829,6 +829,67 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext)
829829
ret
830830
#endif
831831

832+
//
833+
// extern "C" int64_t __libunwind_Registers_arm64_za_disable()
834+
//
835+
// This function implements the requirements of the __arm_za_disable ABI
836+
// routine, except that it will not abort; it will return a non-zero value
837+
// to signify the routine failed.
838+
//
839+
// Note: This function uses SME instructions. It must only be called if SME
840+
// has been confirmed to be available.
841+
//
842+
// On return:
843+
//
844+
// A status is placed in x0. A zero value indicates success; any non-zero
845+
// value indicates failure.
846+
//
847+
.p2align 2
848+
DEFINE_LIBUNWIND_FUNCTION(__libunwind_Registers_arm64_za_disable)
849+
#if __has_feature(ptrauth_calls)
850+
pacibsp
851+
#endif
852+
// If TPIDR2_EL0 is null, the subroutine just disables ZA.
853+
.inst 0xd53bd0b0 // mrs x16, TPIDR2_EL0
854+
cbz x16, 1f
855+
856+
// If any of the reserved bytes in the first 16 bytes of the TPIDR2 block are
857+
// nonzero, return a non-zero value (libunwind will then abort).
858+
ldrh w0, [x16, #10]
859+
cbnz w0, 2f
860+
ldr w0, [x16, #12]
861+
cbnz w0, 2f
862+
863+
// If num_za_save_slices is zero, the subroutine just disables ZA.
864+
ldrh w0, [x16, #8]
865+
cbz x0, 1f
866+
867+
// If za_save_buffer is NULL, the subroutine just disables ZA.
868+
ldr x16, [x16]
869+
cbz x16, 1f
870+
871+
// Store ZA to za_save_buffer.
872+
mov x15, xzr
873+
0:
874+
.inst 0xe1206200 // str za[w15,0], [x16]
875+
.inst 0x04305830 // addsvl x16, x16, #1
876+
add x15, x15, #1
877+
cmp x0, x15
878+
b.ne 0b
879+
1:
880+
// * Set TPIDR2_EL0 to null.
881+
.inst 0xd51bd0bf // msr TPIDR2_EL0, xzr
882+
// * Set PSTATE.ZA to 0.
883+
.inst 0xd503447f // smstop za
884+
// * Return zero (success)
885+
mov x0, xzr
886+
2:
887+
#if __has_feature(ptrauth_calls)
888+
retab
889+
#else
890+
ret
891+
#endif
892+
832893
#elif defined(__arm__) && !defined(__APPLE__)
833894

834895
#if !defined(__ARM_ARCH_ISA_ARM)

0 commit comments

Comments
 (0)