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