xref: /freebsd/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterFlagsDetector_arm64.cpp (revision e64bea71c21eb42e97aa615188ba91f6cce0d36d)
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
DetectFPMRFields(uint64_t hwcap,uint64_t hwcap2,uint64_t hwcap3)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
DetectGCSFeatureFields(uint64_t hwcap,uint64_t hwcap2,uint64_t hwcap3)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
DetectSVCRFields(uint64_t hwcap,uint64_t hwcap2,uint64_t hwcap3)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
DetectMTECtrlFields(uint64_t hwcap,uint64_t hwcap2,uint64_t hwcap3)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(4);
107   if (hwcap3 & HWCAP3_MTE_STORE_ONLY)
108     fields.push_back({"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       std::end(fields),
116       {{"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
DetectFPCRFields(uint64_t hwcap,uint64_t hwcap2,uint64_t hwcap3)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({"FZ16", 19});
140 
141   // Bits 18-16 are "Len" which is unused in AArch64 state.
142 
143   fpcr_fields.push_back({"IDE", 15});
144 
145   // Bit 14 is unused.
146   if (hwcap2 & HWCAP2_EBF16)
147     fpcr_fields.push_back({"EBF", 13});
148 
149   fpcr_fields.push_back({"IXE", 12});
150   fpcr_fields.push_back({"UFE", 11});
151   fpcr_fields.push_back({"OFE", 10});
152   fpcr_fields.push_back({"DZE", 9});
153   fpcr_fields.push_back({"IOE", 8});
154   // Bits 7-3 reserved.
155 
156   if (hwcap2 & HWCAP2_AFP) {
157     fpcr_fields.push_back({"NEP", 2});
158     fpcr_fields.push_back({"AH", 1});
159     fpcr_fields.push_back({"FIZ", 0});
160   }
161 
162   return fpcr_fields;
163 }
164 
165 Arm64RegisterFlagsDetector::Fields
DetectFPSRFields(uint64_t hwcap,uint64_t hwcap2,uint64_t hwcap3)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
DetectCPSRFields(uint64_t hwcap,uint64_t hwcap2,uint64_t hwcap3)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({"TCO", 25});
204   if (hwcap & HWCAP_DIT)
205     cpsr_fields.push_back({"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({"SS", 21});
211   cpsr_fields.push_back({"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({"SSBS", 12});
218   if (hwcap2 & HWCAP2_BTI)
219     cpsr_fields.push_back({"BTYPE", 10, 11});
220 
221   cpsr_fields.push_back({"D", 9});
222   cpsr_fields.push_back({"A", 8});
223   cpsr_fields.push_back({"I", 7});
224   cpsr_fields.push_back({"F", 6});
225   // Bit 5 reserved
226   // Called "M" in the ARMARM.
227   cpsr_fields.push_back({"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({"EL", 2, 3});
230   // Bit 1 is unused and expected to be 0.
231   cpsr_fields.push_back({"SP", 0});
232 
233   return cpsr_fields;
234 }
235 
DetectFields(uint64_t hwcap,uint64_t hwcap2,uint64_t hwcap3)236 void Arm64RegisterFlagsDetector::DetectFields(uint64_t hwcap, uint64_t hwcap2,
237                                               uint64_t hwcap3) {
238   for (auto &reg : m_registers)
239     reg.m_flags.SetFields(reg.m_detector(hwcap, hwcap2, hwcap3));
240   m_has_detected = true;
241 }
242 
UpdateRegisterInfo(const RegisterInfo * reg_info,uint32_t num_regs)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 &reg : 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({reg.m_name, &reg.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         search_registers.cbegin(), search_registers.cend(),
271         [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(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