Merge cherrypicks of ['googleplex-android-review.googlesource.com/33294988', 'googleplex-android-review.googlesource.com/33313547'] into 25Q2-release.

Change-Id: I4cbf70423928c5722ce5a2d8738bff3eb27d3c67
diff --git a/libc/arch-arm64/bionic/setjmp.S b/libc/arch-arm64/bionic/setjmp.S
index c408998..e94e5f4 100644
--- a/libc/arch-arm64/bionic/setjmp.S
+++ b/libc/arch-arm64/bionic/setjmp.S
@@ -114,6 +114,9 @@
   .cfi_rel_offset x0, 0
   .cfi_rel_offset x30, 8
 
+  // Commit SME's ZA lazy save. Note that the call preserves x1.
+  bl __arm_za_disable
+
   // Get the cookie and store it along with the signal flag.
   mov x0, x1
   bl __bionic_setjmp_cookie_get
@@ -183,6 +186,17 @@
 
 // void siglongjmp(sigjmp_buf env, int value);
 ENTRY_WEAK_FOR_NATIVE_BRIDGE(siglongjmp)
+  // First of all, disable SME's ZA, so that it does not interfere
+  // with anything else. Note that __arm_za_disable is guaranteed to
+  // preserve x0 and x1.
+  str x30, [sp, #-16]!
+  .cfi_adjust_cfa_offset 16
+  .cfi_rel_offset x30, 0
+  bl __arm_za_disable
+  ldr x30, [sp], #16
+  .cfi_adjust_cfa_offset -16
+  .cfi_restore x30
+
   // Check the checksum before doing anything.
   m_calculate_checksum x12, x0, x2
   ldr x2, [x0, #(_JB_CHECKSUM * 8)]
diff --git a/tests/setjmp_test.cpp b/tests/setjmp_test.cpp
index 9469285..836aadc 100644
--- a/tests/setjmp_test.cpp
+++ b/tests/setjmp_test.cpp
@@ -18,6 +18,7 @@
 
 #include <setjmp.h>
 #include <stdlib.h>
+#include <sys/auxv.h>
 #include <sys/syscall.h>
 #include <unistd.h>
 
@@ -364,3 +365,42 @@
   GTEST_SKIP() << "tests uses functions not in glibc";
 #endif
 }
+
+#if defined(__aarch64__)
+TEST(setjmp, sigsetjmp_sme) {
+  if (!(getauxval(AT_HWCAP2) & HWCAP2_SME)) {
+    GTEST_SKIP() << "SME is not enabled on device.";
+  }
+
+  uint64_t svcr, za_state;
+  sigjmp_buf jb;
+  __asm__ __volatile__(".arch_extension sme; smstart za");
+  sigsetjmp(jb, 0);
+  __asm__ __volatile__(".arch_extension sme; mrs %0, SVCR" : "=r"(svcr));
+  __asm__ __volatile__(".arch_extension sme; smstop za");  // Turn ZA off anyway.
+  za_state = svcr & 0x2UL;
+  ASSERT_EQ(0UL, za_state);
+}
+
+TEST(setjmp, siglongjmp_sme) {
+  if (!(getauxval(AT_HWCAP2) & HWCAP2_SME)) {
+    GTEST_SKIP() << "SME is not enabled on device.";
+  }
+
+  uint64_t svcr, za_state;
+  int value;
+  sigjmp_buf jb;
+  if ((value = sigsetjmp(jb, 0)) == 0) {
+    __asm__ __volatile__(".arch_extension sme; smstart za");
+    siglongjmp(jb, 789);
+    __asm__ __volatile__(".arch_extension sme; smstop za");
+    FAIL();  // Unreachable.
+  } else {
+    __asm__ __volatile__(".arch_extension sme; mrs %0, SVCR" : "=r"(svcr));
+    __asm__ __volatile__(".arch_extension sme; smstop za");  // Turn ZA off anyway.
+    za_state = svcr & 0x2UL;
+    ASSERT_EQ(789, value);
+    ASSERT_EQ(0UL, za_state);
+  }
+}
+#endif
diff --git a/tests/signal_test.cpp b/tests/signal_test.cpp
index f8bfcef..27f5c6c 100644
--- a/tests/signal_test.cpp
+++ b/tests/signal_test.cpp
@@ -16,6 +16,7 @@
 
 #include <errno.h>
 #include <signal.h>
+#include <sys/auxv.h>
 #include <sys/cdefs.h>
 #include <sys/syscall.h>
 #include <sys/types.h>
@@ -1070,3 +1071,35 @@
   GTEST_SKIP() << "our old glibc doesn't have str2sig";
 #endif
 }
+
+#if defined(__aarch64__)
+__attribute__((target("arch=armv9+sme"))) __arm_new("za") static void FunctionUsingZA() {
+  raise(SIGUSR1);
+}
+
+TEST(signal, sme_tpidr2_clear) {
+  // When using SME, on entering a signal handler the kernel should clear TPIDR2_EL0, but this was
+  // not always correctly done. This tests checks if the kernel correctly clears it or not.
+  if (!(getauxval(AT_HWCAP2) & HWCAP2_SME)) {
+    GTEST_SKIP() << "SME is not enabled on device.";
+  }
+
+  static uint64_t tpidr2 = 0;
+  struct sigaction handler = {};
+  handler.sa_sigaction = [](int, siginfo_t*, void*) {
+    uint64_t zero = 0;
+    __asm__ __volatile__(".arch_extension sme; mrs %0, TPIDR2_EL0" : "=r"(tpidr2));
+    __asm__ __volatile__(".arch_extension sme; msr TPIDR2_EL0, %0" : : "r"(zero));  // Clear TPIDR2.
+  };
+  handler.sa_flags = SA_SIGINFO;
+
+  ASSERT_EQ(0, sigaction(SIGUSR1, &handler, nullptr));
+
+  FunctionUsingZA();
+
+  ASSERT_EQ(0x0UL, tpidr2)
+      << "Broken kernel! TPIDR2_EL0 was not null in the signal handler! "
+      << "Please make sure the following patch has been applied to the kernel: "
+      << "https://lore.kernel.org/linux-arm-kernel/20250417190113.3778111-1-mark.rutland@arm.com/";
+}
+#endif