| 1 | //===-- RegisterFlagsDetector_arm64.cpp -----------------------------------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | #include "RegisterFlagsDetector_arm64.h" |
| 10 | #include "lldb/lldb-private-types.h" |
| 11 | |
| 12 | // This file is built on all systems because it is used by native processes and |
| 13 | // core files, so we manually define the needed HWCAP values here. |
| 14 | // These values are the same for Linux and FreeBSD. |
| 15 | |
| 16 | #define HWCAP_FPHP (1ULL << 9) |
| 17 | #define HWCAP_ASIMDHP (1ULL << 10) |
| 18 | #define HWCAP_DIT (1ULL << 24) |
| 19 | #define HWCAP_SSBS (1ULL << 28) |
| 20 | #define HWCAP_GCS (1ULL << 32) |
| 21 | |
| 22 | #define HWCAP2_BTI (1ULL << 17) |
| 23 | #define HWCAP2_MTE (1ULL << 18) |
| 24 | #define HWCAP2_AFP (1ULL << 20) |
| 25 | #define HWCAP2_SME (1ULL << 23) |
| 26 | #define HWCAP2_EBF16 (1ULL << 32) |
| 27 | #define HWCAP2_FPMR (1ULL << 48) |
| 28 | |
| 29 | using namespace lldb_private; |
| 30 | |
| 31 | Arm64RegisterFlagsDetector::Fields |
| 32 | Arm64RegisterFlagsDetector::DetectFPMRFields(uint64_t hwcap, uint64_t hwcap2) { |
| 33 | (void)hwcap; |
| 34 | |
| 35 | if (!(hwcap2 & HWCAP2_FPMR)) |
| 36 | return {}; |
| 37 | |
| 38 | static const FieldEnum fp8_format_enum("fp8_format_enum" , { |
| 39 | {0, "FP8_E5M2" }, |
| 40 | {1, "FP8_E4M3" }, |
| 41 | }); |
| 42 | return { |
| 43 | {"LSCALE2" , 32, 37}, |
| 44 | {"NSCALE" , 24, 31}, |
| 45 | {"LSCALE" , 16, 22}, |
| 46 | {"OSC" , 15}, |
| 47 | {"OSM" , 14}, |
| 48 | {"F8D" , 6, 8, &fp8_format_enum}, |
| 49 | {"F8S2" , 3, 5, &fp8_format_enum}, |
| 50 | {"F8S1" , 0, 2, &fp8_format_enum}, |
| 51 | }; |
| 52 | } |
| 53 | |
| 54 | Arm64RegisterFlagsDetector::Fields |
| 55 | Arm64RegisterFlagsDetector::DetectGCSFeatureFields(uint64_t hwcap, |
| 56 | uint64_t hwcap2) { |
| 57 | (void)hwcap2; |
| 58 | |
| 59 | if (!(hwcap & HWCAP_GCS)) |
| 60 | return {}; |
| 61 | |
| 62 | return { |
| 63 | {"PUSH" , 2}, |
| 64 | {"WRITE" , 1}, |
| 65 | {"ENABLE" , 0}, |
| 66 | }; |
| 67 | } |
| 68 | |
| 69 | Arm64RegisterFlagsDetector::Fields |
| 70 | Arm64RegisterFlagsDetector::DetectSVCRFields(uint64_t hwcap, uint64_t hwcap2) { |
| 71 | (void)hwcap; |
| 72 | |
| 73 | if (!(hwcap2 & HWCAP2_SME)) |
| 74 | return {}; |
| 75 | |
| 76 | // Represents the pseudo register that lldb-server builds, which itself |
| 77 | // matches the architectural register SCVR. The fields match SVCR in the Arm |
| 78 | // manual. |
| 79 | return { |
| 80 | {"ZA" , 1}, |
| 81 | {"SM" , 0}, |
| 82 | }; |
| 83 | } |
| 84 | |
| 85 | Arm64RegisterFlagsDetector::Fields |
| 86 | Arm64RegisterFlagsDetector::DetectMTECtrlFields(uint64_t hwcap, |
| 87 | uint64_t hwcap2) { |
| 88 | (void)hwcap; |
| 89 | |
| 90 | if (!(hwcap2 & HWCAP2_MTE)) |
| 91 | return {}; |
| 92 | |
| 93 | // Represents the contents of NT_ARM_TAGGED_ADDR_CTRL and the value passed |
| 94 | // to prctl(PR_TAGGED_ADDR_CTRL...). Fields are derived from the defines |
| 95 | // used to build the value. |
| 96 | |
| 97 | static const FieldEnum tcf_enum( |
| 98 | "tcf_enum" , |
| 99 | {{0, "TCF_NONE" }, {1, "TCF_SYNC" }, {2, "TCF_ASYNC" }, {3, "TCF_ASYMM" }}); |
| 100 | return {{"TAGS" , 3, 18}, // 16 bit bitfield shifted up by PR_MTE_TAG_SHIFT. |
| 101 | {"TCF" , 1, 2, &tcf_enum}, |
| 102 | {"TAGGED_ADDR_ENABLE" , 0}}; |
| 103 | } |
| 104 | |
| 105 | Arm64RegisterFlagsDetector::Fields |
| 106 | Arm64RegisterFlagsDetector::DetectFPCRFields(uint64_t hwcap, uint64_t hwcap2) { |
| 107 | static const FieldEnum rmode_enum( |
| 108 | "rmode_enum" , {{0, "RN" }, {1, "RP" }, {2, "RM" }, {3, "RZ" }}); |
| 109 | |
| 110 | std::vector<RegisterFlags::Field> fpcr_fields{ |
| 111 | {"AHP" , 26}, {"DN" , 25}, {"FZ" , 24}, {"RMode" , 22, 23, &rmode_enum}, |
| 112 | // Bits 21-20 are "Stride" which is unused in AArch64 state. |
| 113 | }; |
| 114 | |
| 115 | // FEAT_FP16 is indicated by the presence of FPHP (floating point half |
| 116 | // precision) and ASIMDHP (Advanced SIMD half precision) features. |
| 117 | if ((hwcap & HWCAP_FPHP) && (hwcap & HWCAP_ASIMDHP)) |
| 118 | fpcr_fields.push_back(x: {"FZ16" , 19}); |
| 119 | |
| 120 | // Bits 18-16 are "Len" which is unused in AArch64 state. |
| 121 | |
| 122 | fpcr_fields.push_back(x: {"IDE" , 15}); |
| 123 | |
| 124 | // Bit 14 is unused. |
| 125 | if (hwcap2 & HWCAP2_EBF16) |
| 126 | fpcr_fields.push_back(x: {"EBF" , 13}); |
| 127 | |
| 128 | fpcr_fields.push_back(x: {"IXE" , 12}); |
| 129 | fpcr_fields.push_back(x: {"UFE" , 11}); |
| 130 | fpcr_fields.push_back(x: {"OFE" , 10}); |
| 131 | fpcr_fields.push_back(x: {"DZE" , 9}); |
| 132 | fpcr_fields.push_back(x: {"IOE" , 8}); |
| 133 | // Bits 7-3 reserved. |
| 134 | |
| 135 | if (hwcap2 & HWCAP2_AFP) { |
| 136 | fpcr_fields.push_back(x: {"NEP" , 2}); |
| 137 | fpcr_fields.push_back(x: {"AH" , 1}); |
| 138 | fpcr_fields.push_back(x: {"FIZ" , 0}); |
| 139 | } |
| 140 | |
| 141 | return fpcr_fields; |
| 142 | } |
| 143 | |
| 144 | Arm64RegisterFlagsDetector::Fields |
| 145 | Arm64RegisterFlagsDetector::DetectFPSRFields(uint64_t hwcap, uint64_t hwcap2) { |
| 146 | // fpsr's contents are constant. |
| 147 | (void)hwcap; |
| 148 | (void)hwcap2; |
| 149 | |
| 150 | return { |
| 151 | // Bits 31-28 are N/Z/C/V, only used by AArch32. |
| 152 | {"QC" , 27}, |
| 153 | // Bits 26-8 reserved. |
| 154 | {"IDC" , 7}, |
| 155 | // Bits 6-5 reserved. |
| 156 | {"IXC" , 4}, |
| 157 | {"UFC" , 3}, |
| 158 | {"OFC" , 2}, |
| 159 | {"DZC" , 1}, |
| 160 | {"IOC" , 0}, |
| 161 | }; |
| 162 | } |
| 163 | |
| 164 | Arm64RegisterFlagsDetector::Fields |
| 165 | Arm64RegisterFlagsDetector::DetectCPSRFields(uint64_t hwcap, uint64_t hwcap2) { |
| 166 | // The fields here are a combination of the Arm manual's SPSR_EL1, |
| 167 | // plus a few changes where Linux has decided not to make use of them at all, |
| 168 | // or at least not from userspace. |
| 169 | |
| 170 | // Status bits that are always present. |
| 171 | std::vector<RegisterFlags::Field> cpsr_fields{ |
| 172 | {"N" , 31}, {"Z" , 30}, {"C" , 29}, {"V" , 28}, |
| 173 | // Bits 27-26 reserved. |
| 174 | }; |
| 175 | |
| 176 | if (hwcap2 & HWCAP2_MTE) |
| 177 | cpsr_fields.push_back(x: {"TCO" , 25}); |
| 178 | if (hwcap & HWCAP_DIT) |
| 179 | cpsr_fields.push_back(x: {"DIT" , 24}); |
| 180 | |
| 181 | // UAO and PAN are bits 23 and 22 and have no meaning for userspace so |
| 182 | // are treated as reserved by the kernels. |
| 183 | |
| 184 | cpsr_fields.push_back(x: {"SS" , 21}); |
| 185 | cpsr_fields.push_back(x: {"IL" , 20}); |
| 186 | // Bits 19-14 reserved. |
| 187 | |
| 188 | // Bit 13, ALLINT, requires FEAT_NMI that isn't relevant to userspace, and we |
| 189 | // can't detect either, don't show this field. |
| 190 | if (hwcap & HWCAP_SSBS) |
| 191 | cpsr_fields.push_back(x: {"SSBS" , 12}); |
| 192 | if (hwcap2 & HWCAP2_BTI) |
| 193 | cpsr_fields.push_back(x: {"BTYPE" , 10, 11}); |
| 194 | |
| 195 | cpsr_fields.push_back(x: {"D" , 9}); |
| 196 | cpsr_fields.push_back(x: {"A" , 8}); |
| 197 | cpsr_fields.push_back(x: {"I" , 7}); |
| 198 | cpsr_fields.push_back(x: {"F" , 6}); |
| 199 | // Bit 5 reserved |
| 200 | // Called "M" in the ARMARM. |
| 201 | cpsr_fields.push_back(x: {"nRW" , 4}); |
| 202 | // This is a 4 bit field M[3:0] in the ARMARM, we split it into parts. |
| 203 | cpsr_fields.push_back(x: {"EL" , 2, 3}); |
| 204 | // Bit 1 is unused and expected to be 0. |
| 205 | cpsr_fields.push_back(x: {"SP" , 0}); |
| 206 | |
| 207 | return cpsr_fields; |
| 208 | } |
| 209 | |
| 210 | void Arm64RegisterFlagsDetector::DetectFields(uint64_t hwcap, uint64_t hwcap2) { |
| 211 | for (auto ® : m_registers) |
| 212 | reg.m_flags.SetFields(reg.m_detector(hwcap, hwcap2)); |
| 213 | m_has_detected = true; |
| 214 | } |
| 215 | |
| 216 | void Arm64RegisterFlagsDetector::UpdateRegisterInfo( |
| 217 | const RegisterInfo *reg_info, uint32_t num_regs) { |
| 218 | assert(m_has_detected && |
| 219 | "Must call DetectFields before updating register info." ); |
| 220 | |
| 221 | // Register names will not be duplicated, so we do not want to compare against |
| 222 | // one if it has already been found. Each time we find one, we erase it from |
| 223 | // this list. |
| 224 | std::vector<std::pair<llvm::StringRef, const RegisterFlags *>> |
| 225 | search_registers; |
| 226 | for (const auto ® : m_registers) { |
| 227 | // It is possible that a register is all extension dependent fields, and |
| 228 | // none of them are present. |
| 229 | if (reg.m_flags.GetFields().size()) |
| 230 | search_registers.push_back(x: {reg.m_name, ®.m_flags}); |
| 231 | } |
| 232 | |
| 233 | // Walk register information while there are registers we know need |
| 234 | // to be updated. Example: |
| 235 | // Register information: [a, b, c, d] |
| 236 | // To be patched: [b, c] |
| 237 | // * a != b, a != c, do nothing and move on. |
| 238 | // * b == b, patch b, new patch list is [c], move on. |
| 239 | // * c == c, patch c, patch list is empty, exit early without looking at d. |
| 240 | for (uint32_t idx = 0; idx < num_regs && search_registers.size(); |
| 241 | ++idx, ++reg_info) { |
| 242 | auto reg_it = std::find_if( |
| 243 | first: search_registers.cbegin(), last: search_registers.cend(), |
| 244 | pred: [reg_info](auto reg) { return reg.first == reg_info->name; }); |
| 245 | |
| 246 | if (reg_it != search_registers.end()) { |
| 247 | // Attach the field information. |
| 248 | reg_info->flags_type = reg_it->second; |
| 249 | // We do not expect to see this name again so don't look for it again. |
| 250 | search_registers.erase(position: reg_it); |
| 251 | } |
| 252 | } |
| 253 | |
| 254 | // We do not assert that search_registers is empty here, because it may |
| 255 | // contain registers from optional extensions that are not present on the |
| 256 | // current target. |
| 257 | } |
| 258 | |