[kernel][arm64][phys] Use hardware RNG when available for stack-guard

Also convert uses of the ISA/CPU feature registers to arch::SysReg.

Change-Id: I848be7ffd20223bcae0434236b98235447b4d719
Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/500267
Commit-Queue: Roland McGrath <mcgrathr@google.com>
Reviewed-by: Travis Geiselbrecht <travisg@google.com>
Reviewed-by: Joshua Seaton <joshuaseaton@google.com>
diff --git a/zircon/kernel/arch/arm64/feature.cc b/zircon/kernel/arch/arm64/feature.cc
index 3a737b2..7e492f1 100644
--- a/zircon/kernel/arch/arm64/feature.cc
+++ b/zircon/kernel/arch/arm64/feature.cc
@@ -7,6 +7,7 @@
 
 #include <bits.h>
 #include <inttypes.h>
+#include <lib/arch/arm64/feature.h>
 #include <lib/arch/intrin.h>
 
 #include <arch/arm64.h>
@@ -318,60 +319,69 @@
 
     // parse the ISA feature bits
     arm64_isa_features |= ZX_HAS_CPU_FEATURES;
-    uint64_t isar0 = __arm_rsr64("id_aa64isar0_el1");
-    if (BITS_SHIFT(isar0, 7, 4) >= 1) {
-      arm64_isa_features |= ZX_ARM64_FEATURE_ISA_AES;
+
+    auto isar0 = arch::ArmIdAa64IsaR0El1::Read();
+
+    // Other values are reserved.  When assigned they will probably be
+    // supersets of FEAT_PMULL, but don't presume that.
+    switch (isar0.aes()) {
+      case arch::ArmIdAa64IsaR0El1::Aes::kPmull:
+        arm64_isa_features |= ZX_ARM64_FEATURE_ISA_PMULL;
+        [[fallthrough]];
+      case arch::ArmIdAa64IsaR0El1::Aes::kAes:
+        arm64_isa_features |= ZX_ARM64_FEATURE_ISA_AES;
+        break;
+      case arch::ArmIdAa64IsaR0El1::Aes::kNone:
+        break;
     }
-    if (BITS_SHIFT(isar0, 7, 4) >= 2) {
-      arm64_isa_features |= ZX_ARM64_FEATURE_ISA_PMULL;
-    }
-    if (BITS_SHIFT(isar0, 11, 8) >= 1) {
+
+    if (isar0.sha1() != arch::ArmIdAa64IsaR0El1::Sha1::kNone) {
       arm64_isa_features |= ZX_ARM64_FEATURE_ISA_SHA1;
     }
-    if (BITS_SHIFT(isar0, 15, 12) >= 1) {
+    if (isar0.sha2() != arch::ArmIdAa64IsaR0El1::Sha2::kNone) {
       arm64_isa_features |= ZX_ARM64_FEATURE_ISA_SHA2;
     }
-    if (BITS_SHIFT(isar0, 19, 16) >= 1) {
+    if (isar0.crc32() != arch::ArmIdAa64IsaR0El1::Crc32::kNone) {
       arm64_isa_features |= ZX_ARM64_FEATURE_ISA_CRC32;
     }
-    if (BITS_SHIFT(isar0, 23, 20) >= 1) {
+    if (isar0.atomic() != arch::ArmIdAa64IsaR0El1::Atomic::kNone) {
       arm64_isa_features |= ZX_ARM64_FEATURE_ISA_ATOMICS;
     }
-    if (BITS_SHIFT(isar0, 31, 28) >= 1) {
+    if (isar0.rdm() != arch::ArmIdAa64IsaR0El1::Rdm::kNone) {
       arm64_isa_features |= ZX_ARM64_FEATURE_ISA_RDM;
     }
-    if (BITS_SHIFT(isar0, 35, 32) >= 1) {
+    if (isar0.sha3() != arch::ArmIdAa64IsaR0El1::Sha3::kNone) {
       arm64_isa_features |= ZX_ARM64_FEATURE_ISA_SHA3;
     }
-    if (BITS_SHIFT(isar0, 39, 36) >= 1) {
+    if (isar0.sm3() != arch::ArmIdAa64IsaR0El1::Sm3::kNone) {
       arm64_isa_features |= ZX_ARM64_FEATURE_ISA_SM3;
     }
-    if (BITS_SHIFT(isar0, 43, 40) >= 1) {
+    if (isar0.sm4() != arch::ArmIdAa64IsaR0El1::Sm4::kNone) {
       arm64_isa_features |= ZX_ARM64_FEATURE_ISA_SM4;
     }
-    if (BITS_SHIFT(isar0, 47, 44) >= 1) {
+    if (isar0.dp() != arch::ArmIdAa64IsaR0El1::DotProd::kNone) {
       arm64_isa_features |= ZX_ARM64_FEATURE_ISA_DP;
     }
-    if (BITS_SHIFT(isar0, 51, 48) >= 1) {
+    if (isar0.fhm() != arch::ArmIdAa64IsaR0El1::Fhm::kNone) {
       arm64_isa_features |= ZX_ARM64_FEATURE_ISA_FHM;
     }
-    if (BITS_SHIFT(isar0, 55, 52) >= 1) {
+    if (isar0.ts() != arch::ArmIdAa64IsaR0El1::Ts::kNone) {
       arm64_isa_features |= ZX_ARM64_FEATURE_ISA_TS;
     }
-    if (BITS_SHIFT(isar0, 63, 60) >= 1) {
+    if (isar0.rndr() != arch::ArmIdAa64IsaR0El1::Rndr::kNone) {
       arm64_isa_features |= ZX_ARM64_FEATURE_ISA_RNDR;
     }
 
-    uint64_t isar1 = __arm_rsr64("id_aa64isar1_el1");
-    if (BITS_SHIFT(isar1, 3, 0) >= 1) {
+    auto isar1 = arch::ArmIdAa64IsaR1El1::Read();
+    if (isar1.dpb() != arch::ArmIdAa64IsaR1El1::Dpb::kNone) {
       arm64_isa_features |= ZX_ARM64_FEATURE_ISA_DPB;
     }
 
-    uint64_t pfr0 = __arm_rsr64("id_aa64pfr0_el1");
-    if (BITS_SHIFT(pfr0, 19, 16) < 0b1111) {
+    auto pfr0 = arch::ArmIdAa64Pfr0El1::Read();
+    if (pfr0.fp() != arch::ArmIdAa64Pfr0El1::Fp::kNone) {
       arm64_isa_features |= ZX_ARM64_FEATURE_ISA_FP;
     }
-    if (BITS_SHIFT(pfr0, 23, 20) < 0b1111) {
+    if (pfr0.advsimd() != arch::ArmIdAa64Pfr0El1::Fp::kNone) {
       arm64_isa_features |= ZX_ARM64_FEATURE_ISA_ASIMD;
     }
 
diff --git a/zircon/kernel/arch/arm64/phys/start.S b/zircon/kernel/arch/arm64/phys/start.S
index 279f015..4359d53 100644
--- a/zircon/kernel/arch/arm64/phys/start.S
+++ b/zircon/kernel/arch/arm64/phys/start.S
@@ -4,6 +4,7 @@
 // license that can be found in the LICENSE file or at
 // https://opensource.org/licenses/MIT
 
+#include <lib/arch/arm64/feature-asm.h>
 #include <lib/arch/asm.h>
 #include <lib/arch/ticks.h>
 #include <zircon/tls.h>
@@ -43,11 +44,26 @@
   adr_global tp, boot_thread_pointer
   msr tpidr_el1, tp
 
-  // Stack guard canary value.  The only "randomness" readily available is
-  // our own load address, so swizzle that in with some arbitrary bits.
+  // Stack guard canary value.
   boot_stack_guard .req x10
+
+  // If hardware random numbers are available, use them.
+  .arch armv8.5-a+rng
+  mrs boot_stack_guard, ID_AA64ISAR0_EL1
+  tst boot_stack_guard, #ID_AA64ISAR0_EL1_RNDR
+  beq .Lno_rndr
+  // Reading this register sets the Z flag if no good randomness is delivered.
+  mrs boot_stack_guard, RNDRRS
+  bne .Lstack_guard_done
+  mrs boot_stack_guard, RNDR
+  bne .Lstack_guard_done
+
+.Lno_rndr:
+  // The only "randomness" readily available is our own load address, so
+  // swizzle that in with some arbitrary bits.
   movlit boot_stack_guard, 0xdeadbeef1ee2d00d
   eor boot_stack_guard, tp, boot_stack_guard
+.Lstack_guard_done:
 
 #if __has_feature(safe_stack)
   boot_unsafe_stack_ptr .req x11
diff --git a/zircon/kernel/lib/arch/BUILD.gn b/zircon/kernel/lib/arch/BUILD.gn
index ed4c865..d273dd0 100644
--- a/zircon/kernel/lib/arch/BUILD.gn
+++ b/zircon/kernel/lib/arch/BUILD.gn
@@ -38,6 +38,7 @@
     # library need never realize it's distinct.
     deps += [ zircon_cpu ]
     public_deps += [
+      ":gen-arm64-feature-asm",
       ":gen-x86-msr-asm",
       "$zircon_cpu:headers",
     ]
@@ -50,3 +51,10 @@
   sources = [ "gen-x86-msr-asm.cc" ]
   deps = [ ":arch" ]
 }
+
+hwreg_asm_header("gen-arm64-feature-asm") {
+  visibility = [ ":*" ]
+  output_name = "lib/arch/arm64/feature-asm.h"
+  sources = [ "gen-arm64-feature-asm.cc" ]
+  deps = [ ":arch" ]
+}
diff --git a/zircon/kernel/lib/arch/gen-arm64-feature-asm.cc b/zircon/kernel/lib/arch/gen-arm64-feature-asm.cc
new file mode 100644
index 0000000..01a22f5
--- /dev/null
+++ b/zircon/kernel/lib/arch/gen-arm64-feature-asm.cc
@@ -0,0 +1,15 @@
+// Copyright 2021 The Fuchsia Authors
+//
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT
+
+#include <lib/arch/arm64/feature.h>
+
+#include <hwreg/asm.h>
+
+int main(int argc, char** argv) {
+  return hwreg::AsmHeader()  //
+      .Register<arch::ArmIdAa64IsaR0El1>("ID_AA64ISAR0_EL1_")
+      .Main(argc, argv);
+}
diff --git a/zircon/kernel/lib/arch/include/lib/arch/arm64/feature.h b/zircon/kernel/lib/arch/include/lib/arch/arm64/feature.h
new file mode 100644
index 0000000..400fc1b
--- /dev/null
+++ b/zircon/kernel/lib/arch/include/lib/arch/arm64/feature.h
@@ -0,0 +1,297 @@
+// Copyright 2021 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ZIRCON_KERNEL_LIB_ARCH_INCLUDE_LIB_ARCH_ARM64_FEATURE_H_
+#define ZIRCON_KERNEL_LIB_ARCH_INCLUDE_LIB_ARCH_ARM64_FEATURE_H_
+
+#include <lib/arch/sysreg.h>
+
+#include <hwreg/bitfields.h>
+
+namespace arch {
+
+// [arm/sysreg]/ID_AA64ISAR0_EL1: AArch64 Instruction Set Attribute Register 0
+struct ArmIdAa64IsaR0El1 : public SysRegBase<ArmIdAa64IsaR0El1, uint64_t, hwreg::EnablePrinter> {
+  enum class Rndr : uint8_t {
+    kNone = 0b0000,
+    kRng = 0b0001,
+  };
+
+  enum class Tlb : uint8_t {
+    kNone = 0b0000,
+    kTlbios = 0b0001,
+    kkTlbirange = 0b0010,
+  };
+
+  enum class Ts : uint8_t {
+    kNone = 0b0000,
+    kFlagM = 0b0001,
+    kFlagM2 = 0b0010,
+  };
+
+  enum class Fhm : uint8_t {
+    kNone = 0b0000,
+    kFhm = 0b0001,
+  };
+
+  enum class DotProd : uint8_t {
+    kNone = 0b0000,
+    kDotProd = 0b0001,
+  };
+
+  enum class Sm4 : uint8_t {
+    kNone = 0b0000,
+    kSm4 = 0b0001,
+  };
+
+  enum class Sm3 : uint8_t {
+    kNone = 0b0000,
+    kSm3 = 0b0001,
+  };
+
+  enum class Sha3 : uint8_t {
+    kNone = 0b0000,
+    kSha3 = 0b0001,
+  };
+
+  enum class Rdm : uint8_t {
+    kNone = 0b0000,
+    kRdm = 0b0001,
+  };
+
+  enum class Atomic : uint8_t {
+    kNone = 0b0000,
+    kLse = 0b0010,
+  };
+
+  enum class Crc32 : uint8_t {
+    kNone = 0b0000,
+    kCrc32 = 0b0001,
+  };
+
+  enum class Sha2 : uint8_t {
+    kNone = 0b0000,
+    kSha256 = 0b0001,
+    kSha512 = 0b0010,
+  };
+
+  enum class Sha1 : uint8_t {
+    kNone = 0b0000,
+    kSha1 = 0b0001,
+  };
+
+  enum class Aes : uint8_t {
+    kNone = 0b0000,
+    kAes = 0b0001,
+    kPmull = 0b0010,
+  };
+
+  DEF_ENUM_FIELD(Rndr, 63, 60, rndr);
+  DEF_ENUM_FIELD(Tlb, 59, 56, tlb);
+  DEF_ENUM_FIELD(Ts, 55, 52, ts);
+  DEF_ENUM_FIELD(Fhm, 51, 48, fhm);
+  DEF_ENUM_FIELD(DotProd, 47, 44, dp);
+  DEF_ENUM_FIELD(Sm4, 43, 40, sm4);
+  DEF_ENUM_FIELD(Sm3, 39, 36, sm3);
+  DEF_ENUM_FIELD(Sha3, 35, 32, sha3);
+  DEF_ENUM_FIELD(Rdm, 31, 28, rdm);
+  DEF_RSVDZ_FIELD(27, 24);
+  DEF_ENUM_FIELD(Atomic, 23, 20, atomic);
+  DEF_ENUM_FIELD(Crc32, 19, 16, crc32);
+  DEF_ENUM_FIELD(Sha2, 15, 12, sha2);
+  DEF_ENUM_FIELD(Sha1, 11, 8, sha1);
+  DEF_ENUM_FIELD(Aes, 7, 4, aes);
+  DEF_RSVDZ_FIELD(3, 0);
+};
+ARCH_ARM64_SYSREG(ArmIdAa64IsaR0El1, "ID_AA64ISAR0_EL1");
+
+// [arm/sysreg]/ID_AA64ISAR1_EL1: AArch64 Instruction Set Attribute Register 1
+struct ArmIdAa64IsaR1El1 : public SysRegBase<ArmIdAa64IsaR1El1> {
+  enum class Ls64 : uint8_t {
+    kNone = 0b0000,
+    kLs64 = 0b0001,
+    kLs64V = 0b0010,
+    kLs64Accdata = 0b0011,
+  };
+
+  enum class Xs : uint8_t {
+    kNone = 0b0000,
+    kXs = 0b0001,
+  };
+
+  enum class I8mm : uint8_t {
+    kNone = 0b0000,
+    kI8mm = 0b0001,
+  };
+
+  enum class Dgh : uint8_t {
+    kNone = 0b0000,
+    kDgh = 0b0001,
+  };
+
+  enum class Bf16 : uint8_t {
+    kNone = 0b0000,
+    kBf16 = 0b0001,
+  };
+
+  enum class Specres : uint8_t {
+    kNone = 0b0000,
+    kSpecres = 0b0001,
+  };
+
+  enum class Sb : uint8_t {
+    kNone = 0b0000,
+    kSb = 0b0001,
+  };
+
+  enum class Frintts : uint8_t {
+    kNone = 0b0000,
+    kFrintts = 0b0001,
+  };
+
+  enum class Gpi : uint8_t {
+    kNone = 0b0000,
+    kGpi = 0b0001,
+  };
+
+  enum class Gpa : uint8_t {
+    kNone = 0b0000,
+    kGpa = 0b0001,
+  };
+
+  enum class Lrcpc : uint8_t {
+    kNone = 0b0000,
+    kLrcpc = 0b0001,
+    kLrcpc2 = 0b0010,
+  };
+
+  enum class Fcma : uint8_t {
+    kNone = 0b0000,
+    kFcma = 0b0001,
+  };
+
+  enum class Jscvt : uint8_t {
+    kNone = 0b0000,
+    kJscvt = 0b0001,
+  };
+
+  enum class Pauth : uint8_t {
+    kNone = 0b0000,
+    kPauth = 0b0001,
+    kPauthEnhanced = 0b0010,
+    kPauth2 = 0b0011,
+    kFpac = 0b0100,
+    kFpacCombined = 0b0101,
+  };
+
+  enum class Dpb : uint8_t {
+    kNone = 0b0000,
+    kDpb = 0b0001,
+    kDpb2 = 0b0010,
+  };
+
+  DEF_ENUM_FIELD(Ls64, 63, 60, ls64);
+  DEF_ENUM_FIELD(Xs, 59, 56, xs);
+  DEF_ENUM_FIELD(I8mm, 55, 52, i8mm);
+  DEF_ENUM_FIELD(Dgh, 51, 48, dgh);
+  DEF_ENUM_FIELD(Bf16, 47, 44, bf16);
+  DEF_ENUM_FIELD(Specres, 43, 40, specres);
+  DEF_ENUM_FIELD(Sb, 39, 36, sb);
+  DEF_ENUM_FIELD(Frintts, 35, 32, frintts);
+  DEF_ENUM_FIELD(Gpi, 31, 28, gpi);
+  DEF_ENUM_FIELD(Gpa, 27, 24, gpa);
+  DEF_ENUM_FIELD(Lrcpc, 23, 20, lrcpc);
+  DEF_ENUM_FIELD(Fcma, 19, 16, fcma);
+  DEF_ENUM_FIELD(Jscvt, 15, 12, jscvt);
+  DEF_ENUM_FIELD(Pauth, 11, 8, api);
+  DEF_ENUM_FIELD(Pauth, 7, 4, apa);
+  DEF_ENUM_FIELD(Dpb, 3, 0, dpb);
+};
+ARCH_ARM64_SYSREG(ArmIdAa64IsaR1El1, "ID_AA64ISAR1_EL1");
+
+// [arm/sysreg]/ID_AA64PFR0_EL1: AArch64 Processor Feature Register 0
+struct ArmIdAa64Pfr0El1 : public SysRegBase<ArmIdAa64Pfr0El1> {
+  enum class Csv3 : uint8_t {
+    kNone = 0b0000,
+    kCsv3 = 0b0001,
+  };
+
+  enum class Csv2 : uint8_t {
+    kNone = 0b0000,
+    kCsv2 = 0b0001,
+    kCsv2_2 = 0b0010,
+  };
+
+  enum class Dit : uint8_t {
+    kNone = 0b0000,
+    kDit = 0b0001,
+  };
+
+  enum class Amu : uint8_t {
+    kNone = 0b0000,
+    kAmuv1 = 0b0001,
+    kAmuv1p1 = 0b0010,
+  };
+
+  enum class Mpam : uint8_t {
+    kNone = 0b0000,
+    kMpam = 0b0001,
+  };
+
+  enum class Sel2 : uint8_t {
+    kNone = 0b0000,
+    kSel2 = 0b0001,
+  };
+
+  enum class Sve : uint8_t {
+    kNone = 0b0000,
+    kSve = 0b0001,
+  };
+
+  enum class Ras : uint8_t {
+    kNone = 0b0000,
+    kRas = 0b0001,
+    kRasv1p1 = 0b0010,
+  };
+
+  enum class Gic : uint8_t {
+    kNone = 0b0000,
+    kGic4 = 0b0001,
+    kGic4_1 = 0b0010,
+  };
+
+  enum class Fp : uint8_t {
+    kFp = 0b0000,
+    kFp16 = 0b0001,
+    kNone = 0b1111,
+  };
+
+  enum class El : uint8_t {
+    kNone = 0b0000,  // EL[23] not implemented.
+    k64 = 0b0001,    // ELn supported in AAarch64 state
+    k32 = 0b0010,    // ELn supported in AAarch64 or AAarch32 state
+  };
+
+  DEF_ENUM_FIELD(Csv3, 63, 60, csv3);
+  DEF_ENUM_FIELD(Csv2, 59, 56, csv2);
+  DEF_RSVDZ_FIELD(55, 52);
+  DEF_ENUM_FIELD(Dit, 51, 48, dit);
+  DEF_ENUM_FIELD(Amu, 47, 44, amu);
+  DEF_ENUM_FIELD(Mpam, 43, 40, mpam);
+  DEF_ENUM_FIELD(Sel2, 39, 36, sel2);
+  DEF_ENUM_FIELD(Sve, 35, 32, sve);
+  DEF_ENUM_FIELD(Ras, 31, 28, ras);
+  DEF_ENUM_FIELD(Gic, 27, 24, gic);
+  DEF_ENUM_FIELD(Fp, 23, 20, advsimd);
+  DEF_ENUM_FIELD(Fp, 19, 16, fp);
+  DEF_ENUM_FIELD(El, 15, 12, el3);
+  DEF_ENUM_FIELD(El, 11, 8, el2);
+  DEF_ENUM_FIELD(El, 7, 4, el1);
+  DEF_ENUM_FIELD(El, 3, 0, el0);
+};
+ARCH_ARM64_SYSREG(ArmIdAa64Pfr0El1, "ID_AA64PFR0_EL1");
+
+}  // namespace arch
+
+#endif  // ZIRCON_KERNEL_LIB_ARCH_INCLUDE_LIB_ARCH_ARM64_FEATURE_H_
diff --git a/zircon/kernel/lib/arch/include/lib/arch/sysreg.h b/zircon/kernel/lib/arch/include/lib/arch/sysreg.h
index 6e0c90f..2a85148 100644
--- a/zircon/kernel/lib/arch/include/lib/arch/sysreg.h
+++ b/zircon/kernel/lib/arch/include/lib/arch/sysreg.h
@@ -210,8 +210,8 @@
 // layout type with arch::SysRegDerivedBase<LT> and then defining separate
 // register tag types using `struct T : public arch::SysRegDerived<T, LT> {};`.
 
-template <class RegisterType, typename IntType = uint64_t>
-class SysRegDerivedBase : public hwreg::RegisterBase<RegisterType, IntType, void> {
+template <class RegisterType, typename IntType = uint64_t, typename Printer = void>
+class SysRegDerivedBase : public hwreg::RegisterBase<RegisterType, IntType, Printer> {
  public:
   using SelfType = RegisterType;
   using ValueType = IntType;
@@ -234,8 +234,8 @@
   }
 };
 
-template <class RegisterTag, typename IntType = uint64_t>
-using SysRegBase = SysRegDerived<RegisterTag, SysRegDerivedBase<RegisterTag, IntType>>;
+template <class RegisterTag, typename IntType = uint64_t, typename Printer = void>
+using SysRegBase = SysRegDerived<RegisterTag, SysRegDerivedBase<RegisterTag, IntType, Printer>>;
 
 }  // namespace arch