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
29using namespace lldb_private;
30
31Arm64RegisterFlagsDetector::Fields
32Arm64RegisterFlagsDetector::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
54Arm64RegisterFlagsDetector::Fields
55Arm64RegisterFlagsDetector::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
69Arm64RegisterFlagsDetector::Fields
70Arm64RegisterFlagsDetector::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
85Arm64RegisterFlagsDetector::Fields
86Arm64RegisterFlagsDetector::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
105Arm64RegisterFlagsDetector::Fields
106Arm64RegisterFlagsDetector::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
144Arm64RegisterFlagsDetector::Fields
145Arm64RegisterFlagsDetector::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
164Arm64RegisterFlagsDetector::Fields
165Arm64RegisterFlagsDetector::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
210void Arm64RegisterFlagsDetector::DetectFields(uint64_t hwcap, uint64_t hwcap2) {
211 for (auto &reg : m_registers)
212 reg.m_flags.SetFields(reg.m_detector(hwcap, hwcap2));
213 m_has_detected = true;
214}
215
216void 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 &reg : 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, &reg.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

source code of lldb/source/Plugins/Process/Utility/RegisterFlagsDetector_arm64.cpp