pyGdbToolkit 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,504 @@
1
+ # SPDX-FileCopyrightText: 2026 H2Lab Development Team
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
+ """Cortex-M core descriptions, CPUID decoding, and SCB inspection."""
5
+
6
+ from __future__ import annotations
7
+
8
+ from dataclasses import dataclass
9
+ from enum import IntEnum, StrEnum, unique
10
+
11
+ from ...target_memory import TargetMemory, TargetReadError
12
+ from ..base import Architecture, ProbeResult, RegisterValue, SystemRegisterSet, TargetDescription
13
+ from .mpu import MpuDescription, PMSA_V7_MPU, PMSA_V8_MPU, PMSA_V8_PXN_MPU
14
+ from .sau import ARMV8_M_SAU, SauDescription
15
+ from .target import ArmProfile, ArmTargetDescription
16
+
17
+ SCB_BASE_ADDRESS = 0xE000ED00
18
+ CPUID_ADDRESS = SCB_BASE_ADDRESS
19
+ ARM_IMPLEMENTER = 0x41
20
+
21
+
22
+ @unique
23
+ class CortexMPart(IntEnum):
24
+ """Architected Cortex-M CPUID part numbers."""
25
+
26
+ M0 = 0xC20
27
+ M1 = 0xC21
28
+ M3 = 0xC23
29
+ M4 = 0xC24
30
+ M7 = 0xC27
31
+ M0_PLUS = 0xC60
32
+ M23 = 0xD20
33
+ M33 = 0xD21
34
+ M55 = 0xD22
35
+ M85 = 0xD23
36
+ M35P = 0xD31
37
+ M52 = 0xD32
38
+
39
+
40
+ @unique
41
+ class CortexMArchitecture(StrEnum):
42
+ """Cortex-M architecture versions represented by the core catalog."""
43
+
44
+ ARMV6_M = "Armv6-M"
45
+ ARMV7_M = "Armv7-M"
46
+ ARMV7E_M = "Armv7E-M"
47
+ ARMV8_M_BASELINE = "Armv8-M Baseline"
48
+ ARMV8_M_MAINLINE = "Armv8-M Mainline"
49
+ ARMV8_1_M_MAINLINE = "Armv8.1-M Mainline"
50
+
51
+
52
+ @unique
53
+ class CortexMFeature(StrEnum):
54
+ """Architectural feature categories used to describe Cortex-M cores."""
55
+
56
+ CONFIGURABLE_FAULTS = "configurable-faults"
57
+ DSP_EXTENSION = "dsp-extension"
58
+ SECURITY_EXTENSION = "security-extension"
59
+ CACHE_CONTROL = "cache-control"
60
+ AHB_SLAVE_CONTROL = "ahb-slave-control"
61
+ RAS_FAULT_STATUS = "ras-fault-status"
62
+
63
+
64
+ @unique
65
+ class ScbRegister(IntEnum):
66
+ """System Control Block register offsets from ``SCB_BASE_ADDRESS``."""
67
+
68
+ CPUID = 0x000
69
+ ICSR = 0x004
70
+ VTOR = 0x008
71
+ AIRCR = 0x00C
72
+ SCR = 0x010
73
+ CCR = 0x014
74
+ SHPR1 = 0x018
75
+ SHPR2 = 0x01C
76
+ SHPR3 = 0x020
77
+ SHCSR = 0x024
78
+ CFSR = 0x028
79
+ HFSR = 0x02C
80
+ DFSR = 0x030
81
+ MMFAR = 0x034
82
+ BFAR = 0x038
83
+ AFSR = 0x03C
84
+ ID_PFR0 = 0x040
85
+ ID_PFR1 = 0x044
86
+ ID_DFR0 = 0x048
87
+ ID_AFR0 = 0x04C
88
+ ID_MMFR0 = 0x050
89
+ ID_MMFR1 = 0x054
90
+ ID_MMFR2 = 0x058
91
+ ID_MMFR3 = 0x05C
92
+ ID_ISAR0 = 0x060
93
+ ID_ISAR1 = 0x064
94
+ ID_ISAR2 = 0x068
95
+ ID_ISAR3 = 0x06C
96
+ ID_ISAR4 = 0x070
97
+ ID_ISAR5 = 0x074
98
+ CLIDR = 0x078
99
+ CTR = 0x07C
100
+ CCSIDR = 0x080
101
+ CSSELR = 0x084
102
+ CPACR = 0x088
103
+ NSACR = 0x08C
104
+ SFSR = 0x0E4
105
+ SFAR = 0x0E8
106
+ RFSR = 0x204
107
+ AHBSCR = 0x2A0
108
+
109
+
110
+ @dataclass(frozen=True)
111
+ class CortexMCoreDescription:
112
+ """Immutable architectural description for one Cortex-M CPUID part."""
113
+
114
+ part: CortexMPart
115
+ core_name: str
116
+ architecture_version: CortexMArchitecture
117
+ features: frozenset[CortexMFeature]
118
+ scb_registers: tuple[ScbRegister, ...]
119
+ mpu: MpuDescription | None
120
+ sau: SauDescription | None = None
121
+
122
+
123
+ @dataclass(frozen=True)
124
+ class CortexMTargetDescription(ArmTargetDescription):
125
+ """Decoded Cortex-M target identity from its architected CPUID register."""
126
+
127
+ raw_cpuid: int
128
+ implementer: int
129
+ variant: int
130
+ cpuid_architecture: int
131
+ part_number: int
132
+ patch: int
133
+ core: CortexMCoreDescription | None
134
+
135
+ @property
136
+ def rnp_revision(self) -> str:
137
+ """Return the standard Arm rNp revision notation."""
138
+ return f"r{self.variant}p{self.patch}"
139
+
140
+ @property
141
+ def implementer_name(self) -> str:
142
+ """Return the output-compatible CPUID implementer display name."""
143
+ if self.implementer == ARM_IMPLEMENTER:
144
+ return "Arm"
145
+ return f"Unknown (0x{self.implementer:02X})"
146
+
147
+
148
+ _BASELINE_SCB = (
149
+ ScbRegister.CPUID,
150
+ ScbRegister.ICSR,
151
+ ScbRegister.AIRCR,
152
+ ScbRegister.SCR,
153
+ ScbRegister.CCR,
154
+ ScbRegister.SHPR2,
155
+ ScbRegister.SHPR3,
156
+ ScbRegister.SHCSR,
157
+ )
158
+ _BASELINE_WITH_VTOR_SCB = _BASELINE_SCB[:2] + (ScbRegister.VTOR,) + _BASELINE_SCB[2:]
159
+ _BASELINE_SECURITY_SCB = _BASELINE_WITH_VTOR_SCB + (
160
+ ScbRegister.SFSR,
161
+ ScbRegister.SFAR,
162
+ )
163
+ _MAINLINE_SCB = (
164
+ ScbRegister.CPUID,
165
+ ScbRegister.ICSR,
166
+ ScbRegister.VTOR,
167
+ ScbRegister.AIRCR,
168
+ ScbRegister.SCR,
169
+ ScbRegister.CCR,
170
+ ScbRegister.SHPR1,
171
+ ScbRegister.SHPR2,
172
+ ScbRegister.SHPR3,
173
+ ScbRegister.SHCSR,
174
+ ScbRegister.CFSR,
175
+ ScbRegister.HFSR,
176
+ ScbRegister.DFSR,
177
+ ScbRegister.MMFAR,
178
+ ScbRegister.BFAR,
179
+ ScbRegister.AFSR,
180
+ ScbRegister.ID_PFR0,
181
+ ScbRegister.ID_PFR1,
182
+ ScbRegister.ID_DFR0,
183
+ ScbRegister.ID_AFR0,
184
+ ScbRegister.ID_MMFR0,
185
+ ScbRegister.ID_MMFR1,
186
+ ScbRegister.ID_MMFR2,
187
+ ScbRegister.ID_MMFR3,
188
+ ScbRegister.ID_ISAR0,
189
+ ScbRegister.ID_ISAR1,
190
+ ScbRegister.ID_ISAR2,
191
+ ScbRegister.ID_ISAR3,
192
+ ScbRegister.ID_ISAR4,
193
+ ScbRegister.CPACR,
194
+ )
195
+ _MAINLINE_WITH_CACHE_SCB = _MAINLINE_SCB + (
196
+ ScbRegister.CLIDR,
197
+ ScbRegister.CTR,
198
+ ScbRegister.CCSIDR,
199
+ ScbRegister.CSSELR,
200
+ ScbRegister.AHBSCR,
201
+ )
202
+ _MAINLINE_WITH_CACHES_SCB = _MAINLINE_SCB + (
203
+ ScbRegister.CLIDR,
204
+ ScbRegister.CTR,
205
+ ScbRegister.CCSIDR,
206
+ ScbRegister.CSSELR,
207
+ )
208
+ _V8_MAINLINE_CORE_SCB = _MAINLINE_SCB[:-1] + (
209
+ ScbRegister.ID_ISAR5,
210
+ ScbRegister.CPACR,
211
+ )
212
+ _V8_MAINLINE_SCB = _V8_MAINLINE_CORE_SCB + (
213
+ ScbRegister.NSACR,
214
+ ScbRegister.SFSR,
215
+ ScbRegister.SFAR,
216
+ )
217
+ _V8_1_MAINLINE_SCB = _V8_MAINLINE_CORE_SCB + (
218
+ ScbRegister.CLIDR,
219
+ ScbRegister.CTR,
220
+ ScbRegister.CCSIDR,
221
+ ScbRegister.CSSELR,
222
+ ScbRegister.NSACR,
223
+ ScbRegister.SFSR,
224
+ ScbRegister.SFAR,
225
+ ScbRegister.RFSR,
226
+ )
227
+
228
+ CORTEX_M_CORES: dict[CortexMPart, CortexMCoreDescription] = {
229
+ CortexMPart.M0: CortexMCoreDescription(
230
+ CortexMPart.M0,
231
+ "Cortex-M0",
232
+ CortexMArchitecture.ARMV6_M,
233
+ frozenset(),
234
+ _BASELINE_SCB,
235
+ None,
236
+ ),
237
+ CortexMPart.M1: CortexMCoreDescription(
238
+ CortexMPart.M1,
239
+ "Cortex-M1",
240
+ CortexMArchitecture.ARMV6_M,
241
+ frozenset(),
242
+ _BASELINE_SCB,
243
+ None,
244
+ ),
245
+ CortexMPart.M0_PLUS: CortexMCoreDescription(
246
+ CortexMPart.M0_PLUS,
247
+ "Cortex-M0+",
248
+ CortexMArchitecture.ARMV6_M,
249
+ frozenset(),
250
+ _BASELINE_WITH_VTOR_SCB,
251
+ None,
252
+ ),
253
+ CortexMPart.M3: CortexMCoreDescription(
254
+ CortexMPart.M3,
255
+ "Cortex-M3",
256
+ CortexMArchitecture.ARMV7_M,
257
+ frozenset({CortexMFeature.CONFIGURABLE_FAULTS}),
258
+ _MAINLINE_SCB,
259
+ PMSA_V7_MPU,
260
+ ),
261
+ CortexMPart.M4: CortexMCoreDescription(
262
+ CortexMPart.M4,
263
+ "Cortex-M4",
264
+ CortexMArchitecture.ARMV7E_M,
265
+ frozenset({CortexMFeature.CONFIGURABLE_FAULTS, CortexMFeature.DSP_EXTENSION}),
266
+ _MAINLINE_SCB,
267
+ PMSA_V7_MPU,
268
+ ),
269
+ CortexMPart.M7: CortexMCoreDescription(
270
+ CortexMPart.M7,
271
+ "Cortex-M7",
272
+ CortexMArchitecture.ARMV7E_M,
273
+ frozenset(
274
+ {
275
+ CortexMFeature.CONFIGURABLE_FAULTS,
276
+ CortexMFeature.DSP_EXTENSION,
277
+ CortexMFeature.CACHE_CONTROL,
278
+ CortexMFeature.AHB_SLAVE_CONTROL,
279
+ }
280
+ ),
281
+ _MAINLINE_WITH_CACHE_SCB,
282
+ PMSA_V7_MPU,
283
+ ),
284
+ CortexMPart.M23: CortexMCoreDescription(
285
+ CortexMPart.M23,
286
+ "Cortex-M23",
287
+ CortexMArchitecture.ARMV8_M_BASELINE,
288
+ frozenset({CortexMFeature.SECURITY_EXTENSION}),
289
+ _BASELINE_SECURITY_SCB,
290
+ PMSA_V8_MPU,
291
+ ARMV8_M_SAU,
292
+ ),
293
+ CortexMPart.M33: CortexMCoreDescription(
294
+ CortexMPart.M33,
295
+ "Cortex-M33",
296
+ CortexMArchitecture.ARMV8_M_MAINLINE,
297
+ frozenset({CortexMFeature.CONFIGURABLE_FAULTS, CortexMFeature.SECURITY_EXTENSION}),
298
+ _V8_MAINLINE_SCB,
299
+ PMSA_V8_MPU,
300
+ ARMV8_M_SAU,
301
+ ),
302
+ CortexMPart.M35P: CortexMCoreDescription(
303
+ CortexMPart.M35P,
304
+ "Cortex-M35P",
305
+ CortexMArchitecture.ARMV8_M_MAINLINE,
306
+ frozenset({CortexMFeature.CONFIGURABLE_FAULTS, CortexMFeature.SECURITY_EXTENSION}),
307
+ _V8_MAINLINE_SCB,
308
+ PMSA_V8_MPU,
309
+ ARMV8_M_SAU,
310
+ ),
311
+ CortexMPart.M52: CortexMCoreDescription(
312
+ CortexMPart.M52,
313
+ "Cortex-M52",
314
+ CortexMArchitecture.ARMV8_1_M_MAINLINE,
315
+ frozenset(
316
+ {
317
+ CortexMFeature.CONFIGURABLE_FAULTS,
318
+ CortexMFeature.SECURITY_EXTENSION,
319
+ CortexMFeature.CACHE_CONTROL,
320
+ CortexMFeature.RAS_FAULT_STATUS,
321
+ }
322
+ ),
323
+ _V8_1_MAINLINE_SCB,
324
+ PMSA_V8_PXN_MPU,
325
+ ARMV8_M_SAU,
326
+ ),
327
+ CortexMPart.M55: CortexMCoreDescription(
328
+ CortexMPart.M55,
329
+ "Cortex-M55",
330
+ CortexMArchitecture.ARMV8_1_M_MAINLINE,
331
+ frozenset(
332
+ {
333
+ CortexMFeature.CONFIGURABLE_FAULTS,
334
+ CortexMFeature.SECURITY_EXTENSION,
335
+ CortexMFeature.CACHE_CONTROL,
336
+ CortexMFeature.RAS_FAULT_STATUS,
337
+ }
338
+ ),
339
+ _V8_1_MAINLINE_SCB,
340
+ PMSA_V8_PXN_MPU,
341
+ ARMV8_M_SAU,
342
+ ),
343
+ CortexMPart.M85: CortexMCoreDescription(
344
+ CortexMPart.M85,
345
+ "Cortex-M85",
346
+ CortexMArchitecture.ARMV8_1_M_MAINLINE,
347
+ frozenset(
348
+ {
349
+ CortexMFeature.CONFIGURABLE_FAULTS,
350
+ CortexMFeature.SECURITY_EXTENSION,
351
+ CortexMFeature.CACHE_CONTROL,
352
+ CortexMFeature.RAS_FAULT_STATUS,
353
+ }
354
+ ),
355
+ _V8_1_MAINLINE_SCB,
356
+ PMSA_V8_PXN_MPU,
357
+ ARMV8_M_SAU,
358
+ ),
359
+ }
360
+
361
+
362
+ def decode_cpuid(raw: int) -> CortexMTargetDescription:
363
+ """Decode an architected Cortex-M CPUID register value.
364
+
365
+ Parameters
366
+ ----------
367
+ raw
368
+ The unsigned 32-bit CPUID value from the System Control Block.
369
+
370
+ Returns
371
+ -------
372
+ CortexMTargetDescription
373
+ Core identity, revision, and a registered core description when known.
374
+
375
+ Raises
376
+ ------
377
+ ValueError
378
+ If ``raw`` is outside the unsigned 32-bit CPUID range.
379
+ """
380
+ if not 0 <= raw <= 0xFFFFFFFF:
381
+ raise ValueError("CPUID must be an unsigned 32-bit value")
382
+
383
+ implementer = raw >> 24
384
+ variant = (raw >> 20) & 0xF
385
+ cpuid_architecture = (raw >> 16) & 0xF
386
+ part_number = (raw >> 4) & 0xFFF
387
+ patch = raw & 0xF
388
+ part = _cortex_m_part(part_number)
389
+ core = CORTEX_M_CORES.get(part) if part is not None else None
390
+ core_name = core.core_name if core is not None else f"Unknown Cortex-M part 0x{part_number:03X}"
391
+ return CortexMTargetDescription(
392
+ architecture=Architecture.ARM,
393
+ family="Arm",
394
+ core_name=core_name,
395
+ revision=f"r{variant}p{patch}",
396
+ profile=ArmProfile.CORTEX_M,
397
+ raw_cpuid=raw,
398
+ implementer=implementer,
399
+ variant=variant,
400
+ cpuid_architecture=cpuid_architecture,
401
+ part_number=part_number,
402
+ patch=patch,
403
+ core=core,
404
+ )
405
+
406
+
407
+ def read_scb(
408
+ reader: TargetMemory,
409
+ target: CortexMTargetDescription,
410
+ *,
411
+ cpuid_value: int | None = None,
412
+ ) -> SystemRegisterSet:
413
+ """Read the SCB registers applicable to a known Cortex-M core.
414
+
415
+ Parameters
416
+ ----------
417
+ reader
418
+ Target-memory reader used for 32-bit SCB accesses.
419
+ target
420
+ Cortex-M description returned by :func:`decode_cpuid`.
421
+ cpuid_value
422
+ Optional already-read CPUID value used to avoid a duplicate target read.
423
+
424
+ Returns
425
+ -------
426
+ SystemRegisterSet
427
+ An ``SCB`` block containing available values and individual read errors.
428
+ """
429
+ if target.core is None:
430
+ return SystemRegisterSet(Architecture.ARM, "SCB", ())
431
+
432
+ register_values: list[RegisterValue] = []
433
+ for register in target.core.scb_registers:
434
+ address = SCB_BASE_ADDRESS + register.value
435
+ if register is ScbRegister.CPUID and cpuid_value is not None:
436
+ register_values.append(RegisterValue.known(register.name, address, 32, cpuid_value))
437
+ continue
438
+ try:
439
+ value = reader.read_uint32(address)
440
+ except TargetReadError as error:
441
+ register_values.append(
442
+ RegisterValue.unavailable(register.name, address, 32, str(error))
443
+ )
444
+ else:
445
+ register_values.append(RegisterValue.known(register.name, address, 32, value))
446
+ return SystemRegisterSet(Architecture.ARM, "SCB", tuple(register_values))
447
+
448
+
449
+ def inspect_cortex_m(reader: TargetMemory) -> tuple[CortexMTargetDescription, SystemRegisterSet]:
450
+ """Read CPUID then inspect the SCB register set selected by its core part.
451
+
452
+ Parameters
453
+ ----------
454
+ reader
455
+ Target-memory reader used for CPUID and SCB accesses.
456
+
457
+ Returns
458
+ -------
459
+ tuple[CortexMTargetDescription, SystemRegisterSet]
460
+ Decoded target description and its known-core SCB register results.
461
+ """
462
+ raw_cpuid = reader.read_uint32(CPUID_ADDRESS)
463
+ target = decode_cpuid(raw_cpuid)
464
+ return target, read_scb(reader, target, cpuid_value=raw_cpuid)
465
+
466
+
467
+ class CortexMProbe:
468
+ """Registry adapter for recognized Cortex-M CPUID parts."""
469
+
470
+ architecture = Architecture.ARM
471
+
472
+ def probe(self, reader: TargetMemory) -> ProbeResult:
473
+ """Recognize a known Cortex-M part from CPUID."""
474
+ try:
475
+ target = decode_cpuid(reader.read_uint32(CPUID_ADDRESS))
476
+ except TargetReadError as error:
477
+ return ProbeResult.unavailable(
478
+ f"could not read Cortex-M CPUID: {error}", access_error=True
479
+ )
480
+ if target.implementer != ARM_IMPLEMENTER or target.cpuid_architecture not in (0xC, 0xF):
481
+ return ProbeResult.unavailable("CPUID does not identify an Arm Cortex-M architecture")
482
+ if target.core is None:
483
+ return ProbeResult.unavailable(
484
+ f"unrecognized Cortex-M CPUID part 0x{target.part_number:03X}"
485
+ )
486
+ return ProbeResult.detected(target)
487
+
488
+ def read_system_registers(
489
+ self,
490
+ reader: TargetMemory,
491
+ target: TargetDescription,
492
+ ) -> SystemRegisterSet:
493
+ """Read SCB registers for a target returned by this probe."""
494
+ if not isinstance(target, CortexMTargetDescription):
495
+ raise ValueError("CortexMProbe requires a CortexMTargetDescription")
496
+ return read_scb(reader, target)
497
+
498
+
499
+ def _cortex_m_part(part_number: int) -> CortexMPart | None:
500
+ """Convert a raw 12-bit part number to its enum member when known."""
501
+ try:
502
+ return CortexMPart(part_number)
503
+ except ValueError:
504
+ return None