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