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