1 | //===-- RegisterContextPOSIXCore_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 "RegisterContextPOSIXCore_arm64.h" |
10 | #include "Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h" |
11 | |
12 | #include "Plugins/Process/Utility/AuxVector.h" |
13 | #include "Plugins/Process/Utility/RegisterFlagsLinux_arm64.h" |
14 | #include "Plugins/Process/elf-core/ProcessElfCore.h" |
15 | #include "Plugins/Process/elf-core/RegisterUtilities.h" |
16 | #include "lldb/Target/Thread.h" |
17 | #include "lldb/Utility/RegisterValue.h" |
18 | |
19 | #include <memory> |
20 | |
21 | using namespace lldb_private; |
22 | |
23 | std::unique_ptr<RegisterContextCorePOSIX_arm64> |
24 | RegisterContextCorePOSIX_arm64::(Thread &thread, const ArchSpec &arch, |
25 | const DataExtractor &gpregset, |
26 | llvm::ArrayRef<CoreNote> notes) { |
27 | Flags opt_regsets = RegisterInfoPOSIX_arm64::eRegsetMaskDefault; |
28 | |
29 | DataExtractor ssve_data = |
30 | getRegset(Notes: notes, Triple: arch.GetTriple(), RegsetDescs: AARCH64_SSVE_Desc); |
31 | if (ssve_data.GetByteSize() >= sizeof(sve::user_sve_header)) |
32 | opt_regsets.Set(RegisterInfoPOSIX_arm64::eRegsetMaskSSVE); |
33 | |
34 | DataExtractor sve_data = getRegset(Notes: notes, Triple: arch.GetTriple(), RegsetDescs: AARCH64_SVE_Desc); |
35 | if (sve_data.GetByteSize() >= sizeof(sve::user_sve_header)) |
36 | opt_regsets.Set(RegisterInfoPOSIX_arm64::eRegsetMaskSVE); |
37 | |
38 | // Pointer Authentication register set data is based on struct |
39 | // user_pac_mask declared in ptrace.h. See reference implementation |
40 | // in Linux kernel source at arch/arm64/include/uapi/asm/ptrace.h. |
41 | DataExtractor pac_data = getRegset(Notes: notes, Triple: arch.GetTriple(), RegsetDescs: AARCH64_PAC_Desc); |
42 | if (pac_data.GetByteSize() >= sizeof(uint64_t) * 2) |
43 | opt_regsets.Set(RegisterInfoPOSIX_arm64::eRegsetMaskPAuth); |
44 | |
45 | DataExtractor tls_data = getRegset(Notes: notes, Triple: arch.GetTriple(), RegsetDescs: AARCH64_TLS_Desc); |
46 | // A valid note will always contain at least one register, "tpidr". It may |
47 | // expand in future. |
48 | if (tls_data.GetByteSize() >= sizeof(uint64_t)) |
49 | opt_regsets.Set(RegisterInfoPOSIX_arm64::eRegsetMaskTLS); |
50 | |
51 | DataExtractor za_data = getRegset(Notes: notes, Triple: arch.GetTriple(), RegsetDescs: AARCH64_ZA_Desc); |
52 | // Nothing if ZA is not present, just the header if it is disabled. |
53 | if (za_data.GetByteSize() >= sizeof(sve::user_za_header)) |
54 | opt_regsets.Set(RegisterInfoPOSIX_arm64::eRegsetMaskZA); |
55 | |
56 | DataExtractor mte_data = getRegset(Notes: notes, Triple: arch.GetTriple(), RegsetDescs: AARCH64_MTE_Desc); |
57 | if (mte_data.GetByteSize() >= sizeof(uint64_t)) |
58 | opt_regsets.Set(RegisterInfoPOSIX_arm64::eRegsetMaskMTE); |
59 | |
60 | DataExtractor zt_data = getRegset(Notes: notes, Triple: arch.GetTriple(), RegsetDescs: AARCH64_ZT_Desc); |
61 | // Although ZT0 can be in a disabled state like ZA can, the kernel reports |
62 | // its content as 0s in that state. Therefore even a disabled ZT0 will have |
63 | // a note containing those 0s. ZT0 is a 512 bit / 64 byte register. |
64 | if (zt_data.GetByteSize() >= 64) |
65 | opt_regsets.Set(RegisterInfoPOSIX_arm64::eRegsetMaskZT); |
66 | |
67 | auto register_info_up = |
68 | std::make_unique<RegisterInfoPOSIX_arm64>(args: arch, args&: opt_regsets); |
69 | return std::unique_ptr<RegisterContextCorePOSIX_arm64>( |
70 | new RegisterContextCorePOSIX_arm64(thread, std::move(register_info_up), |
71 | gpregset, notes)); |
72 | } |
73 | |
74 | RegisterContextCorePOSIX_arm64::( |
75 | Thread &thread, std::unique_ptr<RegisterInfoPOSIX_arm64> register_info, |
76 | const DataExtractor &gpregset, llvm::ArrayRef<CoreNote> notes) |
77 | : RegisterContextPOSIX_arm64(thread, std::move(register_info)) { |
78 | ::memset(s: &m_sme_pseudo_regs, c: 0, n: sizeof(m_sme_pseudo_regs)); |
79 | |
80 | ProcessElfCore *process = |
81 | static_cast<ProcessElfCore *>(thread.GetProcess().get()); |
82 | if (process->GetArchitecture().GetTriple().getOS() == llvm::Triple::Linux) { |
83 | AuxVector aux_vec(process->GetAuxvData()); |
84 | std::optional<uint64_t> auxv_at_hwcap = |
85 | aux_vec.GetAuxValue(entry_type: AuxVector::AUXV_AT_HWCAP); |
86 | std::optional<uint64_t> auxv_at_hwcap2 = |
87 | aux_vec.GetAuxValue(entry_type: AuxVector::AUXV_AT_HWCAP2); |
88 | |
89 | m_linux_register_flags.DetectFields(hwcap: auxv_at_hwcap.value_or(u: 0), |
90 | hwcap2: auxv_at_hwcap2.value_or(u: 0)); |
91 | m_linux_register_flags.UpdateRegisterInfo(reg_info: GetRegisterInfo(), |
92 | num_regs: GetRegisterCount()); |
93 | } |
94 | |
95 | m_gpr_data.SetData(data_sp: std::make_shared<DataBufferHeap>(args: gpregset.GetDataStart(), |
96 | args: gpregset.GetByteSize())); |
97 | m_gpr_data.SetByteOrder(gpregset.GetByteOrder()); |
98 | |
99 | const llvm::Triple &target_triple = |
100 | m_register_info_up->GetTargetArchitecture().GetTriple(); |
101 | m_fpr_data = getRegset(Notes: notes, Triple: target_triple, RegsetDescs: FPR_Desc); |
102 | |
103 | if (m_register_info_up->IsSSVEPresent()) { |
104 | m_sve_data = getRegset(Notes: notes, Triple: target_triple, RegsetDescs: AARCH64_SSVE_Desc); |
105 | lldb::offset_t flags_offset = 12; |
106 | uint16_t flags = m_sve_data.GetU32(offset_ptr: &flags_offset); |
107 | if ((flags & sve::ptrace_regs_mask) == sve::ptrace_regs_sve) |
108 | m_sve_state = SVEState::Streaming; |
109 | } |
110 | |
111 | if (m_sve_state != SVEState::Streaming && m_register_info_up->IsSVEPresent()) |
112 | m_sve_data = getRegset(Notes: notes, Triple: target_triple, RegsetDescs: AARCH64_SVE_Desc); |
113 | |
114 | if (m_register_info_up->IsPAuthPresent()) |
115 | m_pac_data = getRegset(Notes: notes, Triple: target_triple, RegsetDescs: AARCH64_PAC_Desc); |
116 | |
117 | if (m_register_info_up->IsTLSPresent()) |
118 | m_tls_data = getRegset(Notes: notes, Triple: target_triple, RegsetDescs: AARCH64_TLS_Desc); |
119 | |
120 | if (m_register_info_up->IsZAPresent()) |
121 | m_za_data = getRegset(Notes: notes, Triple: target_triple, RegsetDescs: AARCH64_ZA_Desc); |
122 | |
123 | if (m_register_info_up->IsMTEPresent()) |
124 | m_mte_data = getRegset(Notes: notes, Triple: target_triple, RegsetDescs: AARCH64_MTE_Desc); |
125 | |
126 | if (m_register_info_up->IsZTPresent()) |
127 | m_zt_data = getRegset(Notes: notes, Triple: target_triple, RegsetDescs: AARCH64_ZT_Desc); |
128 | |
129 | ConfigureRegisterContext(); |
130 | } |
131 | |
132 | RegisterContextCorePOSIX_arm64::~RegisterContextCorePOSIX_arm64() = default; |
133 | |
134 | bool RegisterContextCorePOSIX_arm64::ReadGPR() { return true; } |
135 | |
136 | bool RegisterContextCorePOSIX_arm64::ReadFPR() { return false; } |
137 | |
138 | bool RegisterContextCorePOSIX_arm64::WriteGPR() { |
139 | assert(0); |
140 | return false; |
141 | } |
142 | |
143 | bool RegisterContextCorePOSIX_arm64::WriteFPR() { |
144 | assert(0); |
145 | return false; |
146 | } |
147 | |
148 | const uint8_t *RegisterContextCorePOSIX_arm64::GetSVEBuffer(uint64_t offset) { |
149 | return m_sve_data.GetDataStart() + offset; |
150 | } |
151 | |
152 | void RegisterContextCorePOSIX_arm64::ConfigureRegisterContext() { |
153 | if (m_sve_data.GetByteSize() > sizeof(sve::user_sve_header)) { |
154 | uint64_t = 8; |
155 | m_sve_vector_length = m_sve_data.GetU16(offset_ptr: &sve_header_field_offset); |
156 | |
157 | if (m_sve_state != SVEState::Streaming) { |
158 | sve_header_field_offset = 12; |
159 | uint16_t = |
160 | m_sve_data.GetU16(offset_ptr: &sve_header_field_offset); |
161 | if ((sve_header_flags_field & sve::ptrace_regs_mask) == |
162 | sve::ptrace_regs_fpsimd) |
163 | m_sve_state = SVEState::FPSIMD; |
164 | else if ((sve_header_flags_field & sve::ptrace_regs_mask) == |
165 | sve::ptrace_regs_sve) |
166 | m_sve_state = SVEState::Full; |
167 | } |
168 | |
169 | if (!sve::vl_valid(vl: m_sve_vector_length)) { |
170 | m_sve_state = SVEState::Disabled; |
171 | m_sve_vector_length = 0; |
172 | } |
173 | } else |
174 | m_sve_state = SVEState::Disabled; |
175 | |
176 | if (m_sve_state != SVEState::Disabled) |
177 | m_register_info_up->ConfigureVectorLengthSVE( |
178 | sve_vq: sve::vq_from_vl(vl: m_sve_vector_length)); |
179 | |
180 | if (m_sve_state == SVEState::Streaming) |
181 | m_sme_pseudo_regs.ctrl_reg |= 1; |
182 | |
183 | if (m_za_data.GetByteSize() >= sizeof(sve::user_za_header)) { |
184 | lldb::offset_t vlen_offset = 8; |
185 | uint16_t svl = m_za_data.GetU16(offset_ptr: &vlen_offset); |
186 | m_sme_pseudo_regs.svg_reg = svl / 8; |
187 | m_register_info_up->ConfigureVectorLengthZA(za_vq: svl / 16); |
188 | |
189 | // If there is register data then ZA is active. The size of the note may be |
190 | // misleading here so we use the size field of the embedded header. |
191 | lldb::offset_t size_offset = 0; |
192 | uint32_t size = m_za_data.GetU32(offset_ptr: &size_offset); |
193 | if (size > sizeof(sve::user_za_header)) |
194 | m_sme_pseudo_regs.ctrl_reg |= 1 << 1; |
195 | } |
196 | } |
197 | |
198 | uint32_t RegisterContextCorePOSIX_arm64::CalculateSVEOffset( |
199 | const RegisterInfo *reg_info) { |
200 | // Start of Z0 data is after GPRs plus 8 bytes of vg register |
201 | uint32_t sve_reg_offset = LLDB_INVALID_INDEX32; |
202 | if (m_sve_state == SVEState::FPSIMD) { |
203 | const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; |
204 | sve_reg_offset = sve::ptrace_fpsimd_offset + (reg - GetRegNumSVEZ0()) * 16; |
205 | } else if (m_sve_state == SVEState::Full || |
206 | m_sve_state == SVEState::Streaming) { |
207 | uint32_t sve_z0_offset = GetGPRSize() + 16; |
208 | sve_reg_offset = |
209 | sve::SigRegsOffset() + reg_info->byte_offset - sve_z0_offset; |
210 | } |
211 | |
212 | return sve_reg_offset; |
213 | } |
214 | |
215 | bool RegisterContextCorePOSIX_arm64::ReadRegister(const RegisterInfo *reg_info, |
216 | RegisterValue &value) { |
217 | Status error; |
218 | lldb::offset_t offset; |
219 | |
220 | offset = reg_info->byte_offset; |
221 | if (offset + reg_info->byte_size <= GetGPRSize()) { |
222 | value.SetFromMemoryData(reg_info: *reg_info, src: m_gpr_data.GetDataStart() + offset, |
223 | src_len: reg_info->byte_size, src_byte_order: lldb::eByteOrderLittle, error); |
224 | return error.Success(); |
225 | } |
226 | |
227 | const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; |
228 | if (reg == LLDB_INVALID_REGNUM) |
229 | return false; |
230 | |
231 | if (IsFPR(reg)) { |
232 | if (m_sve_state == SVEState::Disabled) { |
233 | // SVE is disabled take legacy route for FPU register access |
234 | offset -= GetGPRSize(); |
235 | if (offset < m_fpr_data.GetByteSize()) { |
236 | value.SetFromMemoryData(reg_info: *reg_info, src: m_fpr_data.GetDataStart() + offset, |
237 | src_len: reg_info->byte_size, src_byte_order: lldb::eByteOrderLittle, |
238 | error); |
239 | return error.Success(); |
240 | } |
241 | } else { |
242 | // FPSR and FPCR will be located right after Z registers in |
243 | // SVEState::FPSIMD while in SVEState::Full/SVEState::Streaming they will |
244 | // be located at the end of register data after an alignment correction |
245 | // based on currently selected vector length. |
246 | uint32_t sve_reg_num = LLDB_INVALID_REGNUM; |
247 | if (reg == GetRegNumFPSR()) { |
248 | sve_reg_num = reg; |
249 | if (m_sve_state == SVEState::Full || m_sve_state == SVEState::Streaming) |
250 | offset = sve::PTraceFPSROffset(vq: sve::vq_from_vl(vl: m_sve_vector_length)); |
251 | else if (m_sve_state == SVEState::FPSIMD) |
252 | offset = sve::ptrace_fpsimd_offset + (32 * 16); |
253 | } else if (reg == GetRegNumFPCR()) { |
254 | sve_reg_num = reg; |
255 | if (m_sve_state == SVEState::Full || m_sve_state == SVEState::Streaming) |
256 | offset = sve::PTraceFPCROffset(vq: sve::vq_from_vl(vl: m_sve_vector_length)); |
257 | else if (m_sve_state == SVEState::FPSIMD) |
258 | offset = sve::ptrace_fpsimd_offset + (32 * 16) + 4; |
259 | } else { |
260 | // Extract SVE Z register value register number for this reg_info |
261 | if (reg_info->value_regs && |
262 | reg_info->value_regs[0] != LLDB_INVALID_REGNUM) |
263 | sve_reg_num = reg_info->value_regs[0]; |
264 | offset = CalculateSVEOffset(reg_info: GetRegisterInfoAtIndex(reg: sve_reg_num)); |
265 | } |
266 | |
267 | assert(sve_reg_num != LLDB_INVALID_REGNUM); |
268 | assert(offset < m_sve_data.GetByteSize()); |
269 | value.SetFromMemoryData(reg_info: *reg_info, src: GetSVEBuffer(offset), |
270 | src_len: reg_info->byte_size, src_byte_order: lldb::eByteOrderLittle, |
271 | error); |
272 | } |
273 | } else if (IsSVE(reg)) { |
274 | if (IsSVEVG(reg)) { |
275 | value = GetSVERegVG(); |
276 | return true; |
277 | } |
278 | |
279 | switch (m_sve_state) { |
280 | case SVEState::FPSIMD: { |
281 | // In FPSIMD state SVE payload mirrors legacy fpsimd struct and so just |
282 | // copy 16 bytes of v register to the start of z register. All other |
283 | // SVE register will be set to zero. |
284 | uint64_t byte_size = 1; |
285 | uint8_t zeros = 0; |
286 | const uint8_t *src = &zeros; |
287 | if (IsSVEZ(reg)) { |
288 | byte_size = 16; |
289 | offset = CalculateSVEOffset(reg_info); |
290 | assert(offset < m_sve_data.GetByteSize()); |
291 | src = GetSVEBuffer(offset); |
292 | } |
293 | value.SetFromMemoryData(reg_info: *reg_info, src, src_len: byte_size, src_byte_order: lldb::eByteOrderLittle, |
294 | error); |
295 | } break; |
296 | case SVEState::Full: |
297 | case SVEState::Streaming: |
298 | offset = CalculateSVEOffset(reg_info); |
299 | assert(offset < m_sve_data.GetByteSize()); |
300 | value.SetFromMemoryData(reg_info: *reg_info, src: GetSVEBuffer(offset), |
301 | src_len: reg_info->byte_size, src_byte_order: lldb::eByteOrderLittle, |
302 | error); |
303 | break; |
304 | case SVEState::Disabled: |
305 | default: |
306 | return false; |
307 | } |
308 | } else if (IsPAuth(reg)) { |
309 | offset = reg_info->byte_offset - m_register_info_up->GetPAuthOffset(); |
310 | assert(offset < m_pac_data.GetByteSize()); |
311 | value.SetFromMemoryData(reg_info: *reg_info, src: m_pac_data.GetDataStart() + offset, |
312 | src_len: reg_info->byte_size, src_byte_order: lldb::eByteOrderLittle, error); |
313 | } else if (IsTLS(reg)) { |
314 | offset = reg_info->byte_offset - m_register_info_up->GetTLSOffset(); |
315 | assert(offset < m_tls_data.GetByteSize()); |
316 | value.SetFromMemoryData(reg_info: *reg_info, src: m_tls_data.GetDataStart() + offset, |
317 | src_len: reg_info->byte_size, src_byte_order: lldb::eByteOrderLittle, error); |
318 | } else if (IsMTE(reg)) { |
319 | offset = reg_info->byte_offset - m_register_info_up->GetMTEOffset(); |
320 | assert(offset < m_mte_data.GetByteSize()); |
321 | value.SetFromMemoryData(reg_info: *reg_info, src: m_mte_data.GetDataStart() + offset, |
322 | src_len: reg_info->byte_size, src_byte_order: lldb::eByteOrderLittle, error); |
323 | } else if (IsSME(reg)) { |
324 | // If you had SME in the process, active or otherwise, there will at least |
325 | // be a ZA header. No header, no SME at all. |
326 | if (m_za_data.GetByteSize() < sizeof(sve::user_za_header)) |
327 | return false; |
328 | |
329 | if (m_register_info_up->IsSMERegZA(reg)) { |
330 | // Don't use the size of the note to tell whether ZA is enabled. There may |
331 | // be non-register padding data after the header. Use the embedded |
332 | // header's size field instead. |
333 | lldb::offset_t size_offset = 0; |
334 | uint32_t size = m_za_data.GetU32(offset_ptr: &size_offset); |
335 | bool za_enabled = size > sizeof(sve::user_za_header); |
336 | |
337 | size_t za_note_size = m_za_data.GetByteSize(); |
338 | // For a disabled ZA we fake a value of all 0s. |
339 | if (!za_enabled) { |
340 | uint64_t svl = m_sme_pseudo_regs.svg_reg * 8; |
341 | za_note_size = sizeof(sve::user_za_header) + (svl * svl); |
342 | } |
343 | |
344 | const uint8_t *src = nullptr; |
345 | std::vector<uint8_t> disabled_za_data; |
346 | |
347 | if (za_enabled) |
348 | src = m_za_data.GetDataStart(); |
349 | else { |
350 | disabled_za_data.resize(new_size: za_note_size); |
351 | std::fill(first: disabled_za_data.begin(), last: disabled_za_data.end(), value: 0); |
352 | src = disabled_za_data.data(); |
353 | } |
354 | |
355 | value.SetFromMemoryData(reg_info: *reg_info, src: src + sizeof(sve::user_za_header), |
356 | src_len: reg_info->byte_size, src_byte_order: lldb::eByteOrderLittle, |
357 | error); |
358 | } else if (m_register_info_up->IsSMERegZT(reg)) { |
359 | value.SetFromMemoryData(reg_info: *reg_info, src: m_zt_data.GetDataStart(), |
360 | src_len: reg_info->byte_size, src_byte_order: lldb::eByteOrderLittle, |
361 | error); |
362 | } else { |
363 | offset = reg_info->byte_offset - m_register_info_up->GetSMEOffset(); |
364 | assert(offset < sizeof(m_sme_pseudo_regs)); |
365 | // Host endian since these values are derived instead of being read from a |
366 | // core file note. |
367 | value.SetFromMemoryData( |
368 | reg_info: *reg_info, src: reinterpret_cast<uint8_t *>(&m_sme_pseudo_regs) + offset, |
369 | src_len: reg_info->byte_size, src_byte_order: lldb_private::endian::InlHostByteOrder(), error); |
370 | } |
371 | } else |
372 | return false; |
373 | |
374 | return error.Success(); |
375 | } |
376 | |
377 | bool RegisterContextCorePOSIX_arm64::ReadAllRegisterValues( |
378 | lldb::WritableDataBufferSP &data_sp) { |
379 | return false; |
380 | } |
381 | |
382 | bool RegisterContextCorePOSIX_arm64::WriteRegister(const RegisterInfo *reg_info, |
383 | const RegisterValue &value) { |
384 | return false; |
385 | } |
386 | |
387 | bool RegisterContextCorePOSIX_arm64::WriteAllRegisterValues( |
388 | const lldb::DataBufferSP &data_sp) { |
389 | return false; |
390 | } |
391 | |
392 | bool RegisterContextCorePOSIX_arm64::HardwareSingleStep(bool enable) { |
393 | return false; |
394 | } |
395 | |