Test for virtual counter readability on ARMv8

The upstream kernel makes the CNTVCT register readable from userspace to
enable fast gettimeofday() implementations.  Test that vendors have not
disabled this capability.

Create a new CpuInstructions test class for this, in anticipation of
other tests which follow the same pattern (check whether SIGILL is
signaled while running some operation).

Bug: 15940803

Change-Id: I4f1878eb68b42ce19e97f9257d4f633de18788f8
Signed-off-by: Greg Hackmann <ghackmann@google.com>
diff --git a/tests/jni/Android.mk b/tests/jni/Android.mk
index d9bc51b..a0c050a 100644
--- a/tests/jni/Android.mk
+++ b/tests/jni/Android.mk
@@ -23,6 +23,7 @@
 
 LOCAL_SRC_FILES := \
 		CtsJniOnLoad.cpp \
+		android_os_cts_CpuInstructions.cpp \
 		android_os_cts_TaggedPointer.cpp \
 		android_os_cts_OSFeatures.cpp \
 		android_os_cts_FileUtils.cpp \
diff --git a/tests/jni/CtsJniOnLoad.cpp b/tests/jni/CtsJniOnLoad.cpp
index 06be7c7..ef69732 100644
--- a/tests/jni/CtsJniOnLoad.cpp
+++ b/tests/jni/CtsJniOnLoad.cpp
@@ -19,6 +19,8 @@
 
 extern int register_android_os_cts_CpuFeatures(JNIEnv*);
 
+extern int register_android_os_cts_CpuInstructions(JNIEnv*);
+
 extern int register_android_os_cts_TaggedPointer(JNIEnv*);
 
 extern int register_android_os_cts_OSFeatures(JNIEnv*);
@@ -36,6 +38,10 @@
         return JNI_ERR;
     }
 
+    if (register_android_os_cts_CpuInstructions(env)) {
+        return JNI_ERR;
+    }
+
     if (register_android_os_cts_TaggedPointer(env)) {
         return JNI_ERR;
     }
diff --git a/tests/jni/android_os_cts_CpuInstructions.cpp b/tests/jni/android_os_cts_CpuInstructions.cpp
new file mode 100644
index 0000000..a32f28c
--- /dev/null
+++ b/tests/jni/android_os_cts_CpuInstructions.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <jni.h>
+
+#if defined(__aarch64__)
+#include <setjmp.h>
+#include <signal.h>
+
+static sigjmp_buf jmpenv;
+
+static void sigill_handler(int signum __attribute__((unused)))
+{
+    siglongjmp(jmpenv, 1);
+}
+
+static int do_sigsetjmp()
+{
+    return sigsetjmp(jmpenv, 1);
+}
+
+static jboolean test_instruction(void (*func)())
+{
+    struct sigaction sigill_act;
+    struct sigaction oldact;
+    int err;
+    jboolean ret = true;
+
+    memset(&sigill_act, 0, sizeof(sigill_act));
+    sigill_act.sa_handler = sigill_handler;
+
+    err = sigaction(SIGILL, &sigill_act, &oldact);
+    if (err) {
+        ret = false;
+        goto err_sigaction;
+    }
+
+    if (do_sigsetjmp()) {
+        ret = false;
+        goto err_segill;
+    }
+
+    func();
+
+err_segill:
+    sigaction(SIGILL, &oldact, NULL);
+err_sigaction:
+    return ret;
+}
+
+static void cntvct()
+{
+    asm volatile ( "mrs x0, cntvct_el0" : : : "x0" );
+}
+
+jboolean android_os_cts_CpuInstructions_canReadCntvct(JNIEnv *, jobject)
+{
+    return test_instruction(cntvct);
+}
+#else
+jboolean android_os_cts_CpuInstructions_canReadCntvct(JNIEnv *, jobject)
+{
+    return false;
+}
+#endif
+
+static JNINativeMethod gMethods[] = {
+    { "canReadCntvct", "()Z", (void *)android_os_cts_CpuInstructions_canReadCntvct },
+};
+
+int register_android_os_cts_CpuInstructions(JNIEnv *env)
+{
+    jclass clazz = env->FindClass("android/os/cts/CpuInstructions");
+
+    return env->RegisterNatives(clazz, gMethods,
+            sizeof(gMethods) / sizeof(JNINativeMethod));
+}
diff --git a/tests/src/android/os/cts/CpuInstructions.java b/tests/src/android/os/cts/CpuInstructions.java
new file mode 100644
index 0000000..089ae43
--- /dev/null
+++ b/tests/src/android/os/cts/CpuInstructions.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.cts;
+
+public class CpuInstructions {
+
+    static {
+        System.loadLibrary("cts_jni");
+    }
+
+    public static native boolean canReadCntvct();
+}
diff --git a/tests/tests/os/src/android/os/cts/CpuInstructionsTest.java b/tests/tests/os/src/android/os/cts/CpuInstructionsTest.java
new file mode 100644
index 0000000..fd5bc7e
--- /dev/null
+++ b/tests/tests/os/src/android/os/cts/CpuInstructionsTest.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.os.cts;
+
+import android.os.cts.CpuFeatures;
+import android.os.cts.CpuInstructions;
+
+import junit.framework.TestCase;
+
+public class CpuInstructionsTest extends TestCase {
+
+    public void testArmVirtualCounter() {
+        if (!CpuFeatures.isArm64Cpu()) {
+            return;
+        }
+
+        assertTrue("Machine does not allow access to CNTVCT register",
+                CpuInstructions.canReadCntvct());
+    }
+}