blob: 1cc344f3f55273d9d5af0f3ef1676910e614220c [file] [log] [blame]
#!/usr/bin/env python3
#
# Copyright (C) 2024 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
"""etm_types.py: ctypes based structures to handle ocsd_generic_trace_elem from OpenCSD.
Please refer to the documentation of OpenCSD to see the fields available and their meaning:
https://github.com/Linaro/OpenCSD/blob/master/decoder/docs/prog_guide/prog_guide_generic_pkts.md
"""
import ctypes as ct
from enum import Enum, auto
import sys
# On 32 bit machines, ocsd_vaddr_t is 32 bit only.
VADDR_TYPE = ct.c_uint64 if sys.maxsize > 2**32 else ct.c_uint32
class SecLevel(Enum):
"""ocsd_sec_level in OpenCSD."""
SECURE = 0
NONSECURE = 1
ROOT = 2
REALM = 3
class ExLevel(Enum):
"""ocsd_ex_level in OpenCSD."""
UNKNOWN = -1
EL0 = 0
EL1 = 1
EL2 = 2
EL3 = 3
class PeContext(ct.Structure):
"""ocsd_pe_context in OpenCSD."""
_fields_ = [("_security_level", ct.c_int),
("_exception_level", ct.c_int),
("context_id", ct.c_uint32),
("vmid", ct.c_uint32),
("bits64", ct.c_uint32, 1),
("ctxt_id_valid", ct.c_uint32, 1),
("vmid_valid", ct.c_uint32, 1),
("el_valid", ct.c_uint32, 1)]
@property
def security_level(self) -> SecLevel:
return SecLevel(self._security_level)
@property
def exception_level(self) -> ExLevel:
return ExLevel(self._exception_level)
class TraceEvent(ct.Structure):
"""trace_event_t in OpenCSD."""
_fields_ = [("ev_type", ct.c_uint16),
("ev_number", ct.c_uint16)]
class TraceOnReason(Enum):
"""trace_on_reason_t in OpenCSD."""
NORMAL = 0
OVERFLOW = 1
EX_DEBUG = 2
class _SwtInfoInnerStruct(ct.Structure):
_fields_ = [("swt_payload_pkt_bitsize", ct.c_uint32, 8),
("swt_payload_num_packets", ct.c_uint32, 8),
("swt_marker_packet", ct.c_uint32, 1),
("swt_has_timestamp", ct.c_uint32, 1),
("swt_marker_first", ct.c_uint32, 1),
("swt_master_err", ct.c_uint32, 1),
("swt_global_err", ct.c_uint32, 1),
("swt_trigger_event", ct.c_uint32, 1),
("swt_frequency", ct.c_uint32, 1),
("swt_id_valid", ct.c_uint32, 1)]
class _SwtInfoInnerUnion(ct.Union):
_anonymous_ = ("_s",)
_fields_ = [("_s", _SwtInfoInnerStruct),
("swt_flag_bits", ct.c_uint32)]
class SwtInfo(ct.Structure):
"""ocsd_swt_info_t in OpenCSD."""
_anonymous_ = ("_u",)
_fields_ = [("swt_master_id", ct.c_uint16),
("swt_channel_id", ct.c_uint16),
("_u", _SwtInfoInnerUnion)]
class UnsyncInfo(Enum):
"""unsync_info_t in OpenCSD."""
UNSYNC_UNKNOWN = 0
UNSYNC_INIT_DECODER = 1
UNSYNC_RESET_DECODER = 2
UNSYNC_OVERFLOW = 3
UNSYNC_DISCARD = 4
UNSYNC_BAD_PACKET = 5
UNSYNC_EOT = 6
class TraceSyncMarker(Enum):
"""trace_sync_marker_t in OpenCSD."""
ELEM_MARKER_TS = 0
class TraceMarkerPayload(ct.Structure):
"""trace_marker_payload_t in OpenCSD."""
_fields_ = [("_type", ct.c_int),
("value", ct.c_uint32)]
@property
def type(self) -> TraceSyncMarker:
return TraceSyncMarker(self._type)
class TraceMemtrans(Enum):
"""trace_memtrans_t in OpenCSD."""
MEM_TRANS_TRACE_INIT = 0
MEM_TRANS_START = 1
MEM_TRANS_COMMIT = 2
MEM_TRANS_FAIL = 3
class TraceSwIte(ct.Structure):
"""trace_sw_ite_t in OpenCSD."""
_fields_ = [("el", ct.c_uint8),
("value", ct.c_uint64)]
class _ElementFlags(ct.Structure):
_fields_ = [("last_instr_exec", ct.c_uint32, 1),
("last_instr_sz", ct.c_uint32, 3),
("has_cc", ct.c_uint32, 1),
("cpu_freq_change", ct.c_uint32, 1),
("excep_ret_addr", ct.c_uint32, 1),
("excep_data_marker", ct.c_uint32, 1),
("extended_data", ct.c_uint32, 1),
("has_ts", ct.c_uint32, 1),
("last_instr_cond", ct.c_uint32, 1),
("excep_ret_addr_br_tgt", ct.c_uint32, 1),
("excep_M_tail_chain", ct.c_uint32, 1)]
class _ElementFlagsUnion(ct.Union):
_anonymous_ = ("_s",)
_fields_ = [("_s", _ElementFlags),
("flag_bits", ct.c_uint32)]
class _Payload(ct.Union):
_fields_ = [("exception_number", ct.c_uint32),
("trace_event", TraceEvent),
("_trace_on_reason", ct.c_int),
("sw_trace_info", SwtInfo),
("num_instr_range", ct.c_uint32),
("_unsync_eot_info", ct.c_int),
("sync_marker", TraceMarkerPayload),
("_mem_trans", ct.c_int),
("sw_ite", TraceSwIte)]
class ElemType(Enum):
"""ocsd_gen_trc_elem_t in OpenCSD."""
UNKNOWN = 0
NO_SYNC = 1
TRACE_ON = 2
EO_TRACE = 3
PE_CONTEXT = 4
INSTR_RANGE = 5
I_RANGE_NOPATH = 6
ADDR_NACC = 7
ADDR_UNKNOWN = 8
EXCEPTION = 9
EXCEPTION_RET = 10
TIMESTAMP = 11
CYCLE_COUNT = 12
EVENT = 13
SWTRACE = 14
SYNC_MARKER = 15
MEMTRANS = 16
INSTRUMENTATION = 17
CUSTOM = 18
class IsaType(Enum):
"""ocsd_isa in OpenCSD."""
ARM = 0
THUMB2 = 1
AARCH64 = 2
TEE = 3
JAZELLE = 4
CUSTOM = 5
UNKNOWN = 6
class InstrType(Enum):
"""ocsd_instr_type in OpenCSD."""
OTHER = 0
BR = 1
BR_INDIRECT = 2
ISB = 3
DSB_DMB = 4
WFI_WFE = 5
TSTART = 6
class InstrSubtype(Enum):
"""ocsd_instr_subtype in OpenCSD."""
NONE = 0
BR_LINK = 1
V8_RET = 2
V8_ERET = 3
V7_IMPLIED_RET = 4
class GenericTraceElement(ct.Structure):
"""ocsd_generic_trace_elem in OpenCSD."""
_anonymous_ = ("_payload", "_flags")
_fields_ = [("_elem_type", ct.c_int),
("_isa", ct.c_int),
("st_addr", VADDR_TYPE),
("en_addr", VADDR_TYPE),
("context", PeContext),
("timestamp", ct.c_uint64),
("cycle_count", ct.c_uint32),
("_last_i_type", ct.c_int),
("_last_i_subtype", ct.c_int),
("_flags", _ElementFlagsUnion),
("_payload", _Payload),
("ptr_extended_data", ct.c_void_p)]
@property
def elem_type(self) -> ElemType:
return ElemType(self._elem_type)
@property
def isa(self) -> IsaType:
return IsaType(self._isa)
@property
def last_i_type(self) -> InstrType:
return InstrType(self._last_i_type)
@property
def last_i_subtype(self) -> InstrSubtype:
return InstrSubtype(self._last_i_subtype)
@property
def trace_on_reason(self) -> TraceOnReason:
# This is from the anonymous structure that is called _Payload in _fields_.
return TraceOnReason(self._trace_on_reason)
@property
def unsync_eot_info(self) -> UnsyncInfo:
# This is from the anonymous structure that is called _Payload in _fields_.
return UnsyncInfo(self._unsync_eot_info)
@property
def mem_trans(self) -> TraceMemtrans:
# This is from the anonymous structure that is called _Payload in _fields_.
return TraceMemtrans(self._mem_trans)
def exception_type(self) -> str:
"""Return the exception type indicated by exception_number."""
# The values are taken from the Arm Architecture Reference Manual for A-profile, "D5.3.30
# Exception 64-bit Address IS0 Packet". The reasons for generating them are listed in
# "D4.4.4 Exceptions to Exception element encoding".
names = ["PE Reset", "Debug halt", "Call", "Trap", "System Error", "Unknown",
"Inst debug", "Data debug", "Unknown", "Unknown",
"Alignment", "Inst Fault", "Data Fault", "Unknown",
"IRQ", "FIQ",
"Implementation defined 0", "Implementation defined 1",
"Implementation defined 2", "Implementation defined 3",
"Implementation defined 4", "Implementation defined 5",
"Implementation defined 6", "Implementation defined 7",
"Reserved"]
if self.exception_number < len(names):
return names[self.exception_number]
return "Reserved"