forked from ARM-software/CSAL
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcsscan.py
executable file
·2499 lines (2316 loc) · 103 KB
/
csscan.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#!/usr/bin/python
"""
Scan the ROM table and report on CoreSight devices.
Also do ATB and CTI topology detection.
---
Copyright (C) ARM Ltd. 2018-2021. All rights reserved.
SPDX-License-Identifer: Apache 2.0
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.
---
We report three levels of status:
- the "hard wired" configuration selected at SoC design time
- the "programming" configuration, e.g. address comparator settings
- the actual status, e.g. busy/ready bits, values of counters etc.
To do:
- ETMv3.x/PTF
- power requestors
"""
from __future__ import print_function
import os, sys, struct, time, json
# We provide our own implementation of the mmap module which gives us
# more control over access to volatile registers.
import iommap as mmap
# We support accessing a remote device via our simple 'devmemd' daemon
import devmemd
o_max_devices = 9999999
o_top_only = False
o_verbose = 0
o_show_programming = False
o_show_all_status = False
o_show_integration = False
o_show_authstatus = False
o_show_sample = False # Destructive sampling
o_exclusions = []
g_devmem = None # Physical memory provider
def bit(x, pos):
return (x >> pos) & 1
def bits(x, pos, n):
return (x >> pos) & ((1<<n)-1)
# JEDEC codes: lower 7 bits are the main code, higher bits are the
# continuation code. So Arm is 4 continuation codes followed by 0x3b.
JEDEC_ARM = 0x23b
jedec_designers = {
JEDEC_ARM:"Arm"
}
# Device architectures defined by Arm (i.e. vaild when Arm is the architect)
# Sources:
# IHI0029E CoreSight Architecture Specification 3.0, Table B2-8
# other product information
#
# The CoreSight architecture defines:
# DEVARCH[15:0] ARCHID
# DEVARCH[19:16] REVISION
#
# Conventionally Arm uses:
# DEVARCH[11:0] ARCHPART architecture
# DEVARCH[15:12] ARCHREV major-rev
# DEVARCH[19:16] REVISION minor-rev
ARM_ARCHID_ETM = 0x4a13
ARM_ARCHID_CTI = 0x1a14
ARM_ARCHID_PMU = 0x2a16
ARM_ARCHID_MEMAP = 0x0a17
ARM_ARCHID_STM = 0x0a63
ARM_ARCHID_ELA = 0x0a75
ARM_ARCHID_ROM = 0x0af7
#
# Architecture identifiers indicate the programming interface which a device conforms to.
# Multiple parts may have the same architecture identifier.
#
arm_archids = {
0x0a00:"RAS",
0x1a01:"ITM",
0x1a02:"DWT",
0x2a04:"v8-M",
0x6a05:"v8-R",
0x0a11:"ETR",
0x4a13:"ETMv4", # REVISION indicates the ETMv4 minor version
0x1a14:"CTI",
0x6a15:"v8.0-A",
0x7a15:"v8.1-A",
0x8a15:"v8.2-A",
0x2a16:"PMUv3",
0x0a17:"MEM-AP",
0x0a34:"pwr-rq", # not 0x0a37 as stated in Issue E
0x0a41:"CATU",
0x0a50:"HSSTP",
0x0a63:"STM",
0x0a75:"ELA",
0x0af7:"ROM"
}
# match [11:0] if the major-rev doesn't match (Arm-defined architecture only)
arm_archparts = {
0xa13:"cpu-trace",
0xa15:"v8A-debug",
0xa16:"PMU"
}
# Source: CoreSight Architecture Specification 3.0 Table B2-9
cs_majors = {1:"sink", 2:"link", 3:"source", 4:"control", 5:"logic", 6:"PMU"}
cs_types = {(1,1):"port", (1,2):"buffer", (1,3):"router",
(2,1):"funnel", (2,2):"replicator", (2,3):"fifo",
(3,1):"ETM", (3,4):"HTM", (3,6):"STM",
(4,1):"CTI", (4,2):"auth", (4,3):"power",
(5,1):"core-debug", (5,7):"ELA",
(6,1):"PMU (core)", (6,5):"PMU (SMMU)"}
#
# Read the 3-digit hex part numbers from part-numbers.json.
# Where DEVARCH is not set, the part numbers can be used to find the
# programmer's model for the part.
#
# For older CPUs, there will be separate part numbers for
# the CPU's separate interfaces: debug, ETM, PMU, CTI, ELA etc.
#
# More recent CPUs have the same part number for all these,
# relying on the combination of that common part number,
# DEVTYPE and DEVARCH to indicate the programming model.
#
arm_part_numbers = {}
with open(os.path.join(os.path.dirname(__file__), "part-numbers.json")) as f:
pj = json.load(f)
for p in pj["parts"]:
pn = int(p["part"],16)
assert pn not in arm_part_numbers, "duplicate part number: 0x%03x" % part_no
arm_part_numbers[pn] = p["name"]
def binstr(n,w=None):
if w is None:
return "{0:b}".format(n)
else:
return ("{0:0%ub}" % w).format(n)
def bits_set(w,m):
s = []
for k in sorted(m.keys()):
if bit(w,k):
s.append(m[k])
if s:
return ' '.join(s)
else:
return "-"
assert bits_set(0x011,{0:"x",2:"y",4:"z"}) == "x z"
def decode_one_hot(x,n):
bs = []
for i in range(n):
if bit(x,i):
bs.append(i)
if len(bs) == 1:
return str(bs[0])
else:
return "?%s" % str(bs)
class DevicePhy:
"""
Access a memory-mapped device
"""
def __init__(self, devmem, base_address, write=False):
self.devmem = devmem
self.memap = None
self.mmap_offset = base_address % devmem.page_size
mmap_address = base_address - self.mmap_offset
self.m = devmem.map(mmap_address, write=write)
def __del__(self):
self.devmem.unmap(self.m)
def read32(self, off):
off += self.mmap_offset
raw = self.m[off:off+4]
x = struct.unpack("I", raw)[0]
return x
def write32(self, off, value):
s = struct.pack("I", value)
off += self.mmap_offset
self.m[off:off+4] = s
def write64(self, off, value):
s = struct.pack("Q", value)
off += self.mmap_offset
self.m[off:off+8] = s
class MemAP:
"""
MEM-AP device, with some optimization e.g. use of Direct Access registers
and local cacheing of the current value of TAR.
For more details of MEM-AP operation, see
"Arm Debug Interface (ADI) Architecture Specification".
"""
def __init__(self, memap):
assert isinstance(memap, Device)
assert memap.is_arm_architecture(ARM_ARCHID_MEMAP), "%s: expected MEM-AP" % memap
self.memap = memap
self.n_client_reads = 0
self.n_client_writes = 0
self.memap.claim() # Claim for self-hosted use
self.current_TAR = None
# Using direct/banked access register banks minimizes address writes.
self.CFG = self.memap.read32(0xDF4)
self.use_DAR = (bits(self.CFG,4,4) == 0xA)
self.use_BDR = True # only in effect if not using DAR
def __str__(self):
return "MEM-AP(%s)" % self.memap
def align(self, addr):
"""
Align an address to the granule suitable for the transfer register(s).
"""
if self.use_DAR:
return addr & ~0x3ff
elif self.use_BDR:
return addr & ~0xf
else:
return addr
def set_TAR(self, addr):
"""
Prepare to transfer to/from an address. Set the TAR if necessary
and return the offset of a data transfer register (DAR, BDR or DRR).
"""
eaddr = self.align(addr)
if self.current_TAR is None or eaddr != self.current_TAR:
self.memap.write32(0xD04, eaddr) # write Transfer Address Register
self.current_TAR = eaddr
if self.use_DAR:
return 0x000 + (addr - eaddr) # Direct Access Register 0..255
elif self.use_BDR:
return 0xD10 + (addr - eaddr) # Banked Data Register 0..3
else:
return 0xD0C # Data Read/Write Register
def read32(self, addr):
self.n_client_reads += 1
return self.memap.read32(self.set_TAR(addr))
def write32(self, addr, data):
self.n_client_writes += 1
self.memap.write32(self.set_TAR(addr), data)
class DeviceViaMemAP:
"""
Device accessed via a MEM-AP. This is not the MEM-AP device itself.
"""
def __init__(self, memap, base_address, write=False):
assert isinstance(memap, MemAP)
self.memap = memap
self.offset = base_address # Device base offset within MEM-AP's target space
def read32(self, off):
return self.memap.read32(off + self.offset)
def write32(self, off, data):
self.memap.write32(off + self.offset, data)
class Device:
"""
A single CoreSight device mapped by a ROM table (including ROM tables themselves).
"""
def __init__(self, cs, addr):
self.we_unlocked = False
self.we_claimed = 0
self.n_reads = 0
self.n_writes = 0
self.phy = None
self.cs = cs # Device address space
assert (addr & 0xfff) == 0, "Device must be located on 4K boundary: 0x%x" % addr
self.base_address = addr # Device base address within its address space
self.affine_core = None # Link to the Device object for the core debug block
self.affinity_group = None # AffinityGroup containing related devices
self.map_is_write = False
self.map()
# For convenience, CIDR is formed from CIDR3..CIDR0.
self.CIDR = self.idbytes([0xFFC, 0xFF8, 0xFF4, 0xFF0])
self.PIDR = self.idbytes([0xFD0, 0xFEC, 0xFE8, 0xFE4, 0xFE0])
self.jedec_designer = (((self.PIDR>>32)&15) << 7) | ((self.PIDR >> 12) & 0x3f)
# The part number is selected by the component designer.
self.part_number = self.PIDR & 0xfff
self.devtype = None
self.devarch = None
self.is_checking = (o_verbose >= 1)
if self.is_coresight():
arch = self.read32(0xFBC)
if (arch & 0x00100000) != 0:
self.devarch = arch
self.devtype = self.read32(0xFCC)
def map(self, write=False):
# The mmap() base address must be a multiple of the OS page size.
# But CoreSight devices might be on a smaller granularity.
# E.g. devices might be at 4K boundaries but the OS is using 64K pages.
# So we need to adjust the mmap address and size to page granularity.
# This might mean we end up mapping the same page-sized range several
# times for different 4K devices located within it.
if self.phy is None:
if self.cs.devmem is not None:
self.phy = DevicePhy(self.cs.devmem, self.base_address, write=write)
else:
self.phy = DeviceViaMemAP(self.cs.memap, self.base_address, write=write)
def unmap(self):
if self.phy is not None:
self.phy = None
def write_enable(self):
if not self.map_is_write:
if o_verbose:
print("%s: enabling for write" % str(self))
self.unmap()
self.map(write=True)
self.map_is_write = True
def address_string(self):
"""
A string describing how to locate this device.
"""
s = "@0x%x" % self.base_address
if self.phy.memap is not None:
s = self.phy.memap.memap.address_string() + "." + s
return s
def __str__(self):
s = self.cs_device_type_name()
if s == "UNKNOWN":
aname = self.architecture_name()
if aname is not None:
s = aname
s += " %s" % self.address_string()
if self.is_affine_to_core():
s += " (core)"
return s
def __del__(self):
self.close()
def close(self):
if self.we_claimed:
self.unclaim(self.we_claimed)
if self.we_unlocked and self.cs.restore_locks:
self.lock()
self.unmap()
def read32(self, off):
"""
Read a device register. The register may be volatile, so we should take
care to only read it once.
"""
self.n_reads += 1
self.map()
if o_verbose >= 2:
print(" 0x%x[%03x] R4" % (self.base_address, off), end="")
if o_verbose >= 3:
time.sleep(0.1)
x = self.phy.read32(off)
if o_verbose >= 2:
print(" = 0x%08x" % x)
return x
def do_check(self, check):
# We can read-back to check that the write has taken effect.
# But not when the caller has indicated that the register is volatile.
return (check is None and self.is_checking) or check == True
def write32(self, off, value, check=None, mask=None):
assert self.map_is_write, "0x%x: device should have been write-enabled" % self.base_address
if mask is not None:
# Write value under mask. The mask specifies the bits to act on.
# Other bits retain their previous value.
assert (value | mask) == mask, "trying to write value 0x%x outside mask 0x%x" % (value, mask)
ovalue = self.read32(off)
value = (ovalue & ~mask) | value
if o_verbose >= 2:
print(" 0x%x[%03x] W4 := 0x%08x" % (self.base_address, off, value))
if o_verbose >= 3:
time.sleep(0.1)
self.n_writes += 1
self.phy.write32(off, value)
if self.do_check(check):
readback = self.read32(off)
if readback != value:
print(" 0x%x[%03x] wrote 0x%08x but read back 0x%08x" % (self.base_address, off, value, readback))
return readback == value
def set32(self, off, value, check=None):
self.write32(off, self.read32(off) | value, check=False)
if self.do_check(check):
return (self.read32(off) & value) == value
def clr32(self, off, value, check=None):
self.write32(off, self.read32(off) & ~value, check=False)
if self.do_check(check):
return (self.read32(off) & value) == 0
def write64(self, off, value):
# Write an aligned 64-bit value, atomically. Cannot do with MEM-AP?
if o_verbose >= 2:
print(" 0x%x[%03x] W8 := 0x%016x" % (self.base_address, off, value))
self.phy.write64(off, value)
def read32x2(self, hi, lo):
# CoreSight (APB-connected) devices are generally 32-bit wide,
# and 64-bit values are read as a pair of registers.
# We assume that we're not dealing with volatile data (e.g. counters)
# where special action is needed to return a consistent result.
return (self.read32(hi) << 32) | self.read32(lo)
def read64(self, off):
# assume little-endian
return self.read32x2(off+4,off)
def read64counter(self, hi, lo):
# Read a live 64-bit counter value from a pair of registers.
# We follow the usual procedure of reading the low word between
# two reads of the high word, which we require to be identical.
vhia = self.read32(hi)
while True:
vlo = self.read32(lo)
vhib = self.read32(hi)
if vhia == vhib:
break
vhia = vhib
return (vhib << 32) | vlo
def idbytes(self, x):
id = 0
for wa in x:
id = (id << 8) | (self.read32(wa) & 0xff)
return id
def is_arm_part(self):
return self.jedec_designer == JEDEC_ARM
def is_arm_part_number(self, n=None):
return self.is_arm_part() and (n is None or self.part_number == n)
def arm_part_number(self):
if self.is_arm_part():
return self.part_number
else:
return None
def device_class(self):
# The overall device class (9 for CoreSight, 15 for generic PrimeCell, 1 for old-style ROM tables)
return (self.CIDR >> 12) & 15
def is_coresight(self):
return self.device_class() == 9
def is_coresight_timestamp(self):
# Strangely, a CoreSight global timestamp generator doesn't report as a CoreSight device
return self.device_class() == 0xF and self.is_arm_part_number(0x101)
def is_rom_table(self):
return self.device_class() == 1 or (self.is_coresight() and self.is_arm_architecture(ARM_ARCHID_ROM))
def coresight_device_type(self):
assert self.is_coresight()
assert self.devtype is not None
major = self.devtype & 15
minor = (self.devtype >> 4) & 15
return (major, minor)
def is_coresight_device_type(self, major, minor=None):
if self.is_coresight():
(dmaj, dmin) = self.coresight_device_type()
return (dmaj == major) and (minor is None or dmin == minor)
else:
return False
def is_core_debug(self):
return self.is_coresight_device_type(5,1)
def is_funnel(self):
return self.is_coresight_device_type(2,1)
def is_replicator(self):
return self.is_coresight_device_type(2,2)
def is_cti(self):
return self.is_arm_architecture(ARM_ARCHID_CTI) or self.is_arm_part_number(0x906) or self.is_arm_part_number(0x9ED)
def cs_device_type_name(self):
devtype = self.coresight_device_type()
(major, minor) = devtype
if devtype in cs_types:
desc = cs_types[devtype]
elif major in cs_majors:
desc = "UNKNOWN %s" % cs_majors[major]
elif devtype != (0,0):
desc = "UNKNOWN %s" % str(devtype)
else:
desc = "UNKNOWN"
return desc
def architecture_name(self):
if self.architecture() is None:
return None
if self.is_arm_architecture():
archid = self.architecture()
archpart = archid & 0xfff
archrev = (self.devarch >> 16) & 15
if archid in arm_archids:
archdesc = "Arm %s rev%u" % (arm_archids[archid], archrev)
elif archpart in arm_archparts:
archdesc = "?Arm %s rev%u.%u" % (arm_archparts[archpart], (archid >> 12), archrev)
else:
archdesc = "?Arm:0x%04x rev %u" % (archid, archrev)
else:
archdesc = "?ARCH:0x%x:0x%x" % (self.architect(), self.architecture())
return archdesc
def atb_in_ports(self):
if self.is_funnel():
# TBD: strictly, device-type=funnel doesn't mean it has the CoreSight programming interface
return self.read32(0xFC8) & 15 # read DEVID
elif self.coresight_device_type()[0] in [1,2]: # sink or link
return 1
else:
return 0
def atb_out_ports(self):
if self.is_replicator():
return 2
elif self.coresight_device_type()[0] in [2,3]:
return 1
else:
return 0
def affinity_id(self):
# Return the affinity descriptor as reported by the device.
# Architecturally, this is only standardized for CoreSight devices.
if self.is_coresight():
aff = self.read32x2(0xFAC,0xFA8)
if aff != 0:
return aff
return None
def is_affine_to_core(self):
return self.affine_core is not None
def affine_device(self, typ):
if self.is_affine_to_core:
return self.affinity_group.affine_device(typ)
else:
return None
def architect(self):
# CoreSight devices have a DEVARCH register which specifies the architect and architecture as a JEDEC code.
assert self.is_coresight()
if self.devarch is None:
return None
return self.devarch >> 21
def architecture(self):
assert self.is_coresight()
if self.devarch is None:
return None
return self.devarch & 0xffff
def is_arm_architecture(self, arch=None):
return self.is_coresight() and (self.architect() == JEDEC_ARM) and (arch is None or arch == self.architecture())
def is_arm_architecture_core(self):
# Return true if the device is (the debug interface to) an Arm-architecture core
return self.is_arm_architecture() and (self.architecture() & 0x0fff) == 0x0a15
def is_unlocked(self):
"""
Return True if the device is unlocked and writeable. This is only
valid for CoreSight devices.
"""
return (self.read32(0xFB4) & 0x02) == 0
def is_claimed(self, mask=0xffffffff):
"""
Read CLAIMCLR and check if any of the mask bits (default all) are set
"""
return self.read32(0xFA4) & mask
def claim(self, mask=0x01):
"""
Write CLAIMSET to set claim tag(s).
"""
assert mask != 0
self.write32(0xFA0, mask)
self.we_claimed |= mask
def unclaim(self, mask=0x01):
"""
Write CLAIMCLR to release claim tag(s).
"""
if mask:
self.write32(0xFA4, mask)
self.we_claimed &= ~mask
def is_in_integration_mode(self):
return (self.read32(0xF00) & 0x01) != 0
def unlock(self):
self.write_enable()
if not self.is_unlocked():
if o_verbose >= 2:
print("%s: unlocking" % self)
self.write32(0xFB0, 0xC5ACCE55, check=False)
self.we_unlocked = True
def lock(self):
if self.is_unlocked():
self.write32(0xFB0, 0x00000000, check=False)
def set_integration_mode(self, flag):
if flag:
self.set32(0xF00, 0x00000001, check=True)
else:
self.clr32(0xF00, 0x00000001, check=True)
class ROMTableEntry:
"""
An entry in a ROM table. Contains information from the table itself.
"""
def __init__(self, td, offset, width, e):
self.table = td # table device
self.offset = offset # byte offset of the entry within the table
self.width = width # entry width in bytes
self.descriptor = e # the 4-byte or 8-byte table entry (device offset, power req)
self.device = None # may be populated later
def __str__(self):
s = "0x%x[0x%03x]: " % (self.table.base_address, self.offset)
s += ("%%0%ux" % (self.width*2)) % self.descriptor
if self.is_present():
s += " -> 0x%x" % self.device_address()
return s
def is_present(self):
return (self.descriptor & 1) != 0
def device_offset(self):
# offset is at the top of the word and can be negative
if self.width == 4:
off = (self.descriptor & 0xfffff000)
if (off & 0x80000000) != 0:
off -= 0x100000000
else:
off = (self.descriptor & 0xfffffffffffff000)
if (off & 0x8000000000000000) != 0:
off -= 0x10000000000000000
return off
def device_address(self):
return self.table.base_address + self.device_offset()
class AffinityGroup:
"""
An affinity group groups related components together, e.g.
a core debug, PMU, ETM and CTI.
The grouping may be by DEVAFF or by proximity in the ROM table.
"""
def __init__(self, id=None):
self.id = id
self.devices = {} # indexed by type
def set_affine_device(self, d, typ):
assert typ not in self.devices, "attempt to put two devices of type '%s' in group %s" % (typ, self)
self.devices[typ] = d
# As well as its affinity group, each device has a direct link to its core
# We fix this up regardless of the order in which we add the devices to the affinity group
if typ == "core":
for od in self.devices.values():
od.affine_core = d
elif "core" in self.devices:
d.affine_core = self.devices["core"]
def affine_device(self, typ):
if typ in self.devices:
return self.devices[typ]
else:
return None
def __str__(self):
return "AffinityGroup(0x%016x)" % self.id
class DevMem:
"""
Access physical memory via /dev/mem. This object creates mappings into
page-aligned regions of physical address space.
"""
def __init__(self):
self.page_size = os.sysconf("SC_PAGE_SIZE")
self.fd = None
try:
# This may fail because not present or access-restricted.
self.fd = open("/dev/mem", "r+b")
except:
try:
self.fd = open("/dev/csmem", "r+b")
except:
#print("Can't access /dev/mem or /dev/csmem - are you running as superuser?")
raise
self.fno = self.fd.fileno()
self.n_mappings = 0
def __del__(self):
if self.fd is not None:
self.fd.close()
def map(self, addr, write=False):
"""
Return a memory area mapping that can be used to access a CoreSight peripheral.
The address passed in must be page-aligned (even if the device is not
(e.g. 4K-aligned devices with 64K OS pages) - the caller is responsible for
sorting that out.
The caller is also responsible for releasing the mapping when finished with.
"""
try:
if write:
prot = (mmap.PROT_READ|mmap.PROT_WRITE)
else:
prot = mmap.PROT_READ
m = mmap.mmap(self.fno, self.page_size, mmap.MAP_SHARED, prot, offset=addr)
except EnvironmentError as e:
print("** failed to map 0x%x size 0x%x on fileno %d (currently %u mappings): %s" % (addr, self.page_size, self.fno, self.n_mappings, e))
raise
self.n_mappings += 1
return m
def unmap(self, m):
"""
Unmap device memory. Called when a device object is deleted.
"""
m.close()
self.n_mappings -= 1
class DevMemRemote:
"""
Compatible with class DevMem, but accesses remote via devmemd.
"""
def __init__(self, addr, port):
self.page_size = 4096
self.devmemd = devmemd.Devmem(addr, port)
def map(self, addr, write=False):
"""
Return a mmap-compatible object that indirects via devmemd.
"""
return self.devmemd.map(addr, self.page_size)
def unmap(self, m):
pass
class CSROM:
"""
Container for the overall ROM table scan.
Owns the mechanism by which we access physical memory - e.g. a
mapping on to /dev/mem. Individual device mappings are owned by the
device objects.
"""
def __init__(self, memap=None):
self.fd = None
self.memap = memap
self.device_by_base_address = {}
self.affinity_group_map = {}
self.restore_locks = True
if memap is None:
if g_devmem is not None:
self.devmem = g_devmem
else:
self.devmem = DevMem()
else:
self.devmem = None
def close(self):
for d in self.device_by_base_address.values():
d.close()
self.device_by_base_address = {}
def __del__(self):
self.close()
def map(self, addr, write=False):
if self.devmem is not None:
return self.devmem.map(addr, write)
else:
return None
def unmap(self, addr):
if self.devmem is not None:
return self.devmem.unmap(addr)
else:
return None
def device_at(self, addr, unlock=True):
assert addr in self.device_by_base_address, "missing device at 0x%x" % addr
d = self.device_by_base_address[addr]
if unlock:
d.unlock()
return d
def create_device_at(self, addr, rom_table_entry=None, write=False):
assert not addr in self.device_by_base_address, "device at 0x%x already collected" % addr
d = Device(self, addr)
d.rom_table_entry = rom_table_entry
self.device_by_base_address[addr] = d
if write:
d.write_enable()
return d
def affinity_group(self, id):
if id not in self.affinity_group_map:
self.affinity_group_map[id] = AffinityGroup(id)
return self.affinity_group_map[id]
def list_table(self, td, include_empty=False, recurse=True):
"""
Iterate (perhaps recursively) over a ROM Table, returning
table entries which contain device objects.
We assume ROM tables all have the same format. They may have a
vendor part number, and DEVARCH is not set, but the CIDR device class
identifies them as a Class 1 ROM table.
The first entry is at address 0x000. Each subsequent entry is at
the next 4-byte boundary, until a value of 0x00000000 is read which
is the final entry.
"""
assert td.is_rom_table()
if td.is_coresight():
# Class 9 (new) ROM Table
etop = 0x800
devid = td.read32(0xFC8)
format = devid & 15
if format == 0:
ewidth = 4
elif format == 1:
ewidth = 8
else:
assert False, "unknown Class 9 ROM Table format: %u" % format
else:
# Class 1 (old) ROM Table
etop = 0xF00
ewidth = 4
cpus_in_this_table = []
for a in range(0, etop, ewidth):
if ewidth == 4:
eword = td.read32(a)
else:
eword = td.read64(a)
if eword == 0:
break
if (eword & 1) == 0 and not include_empty:
continue
e = ROMTableEntry(td, a, ewidth, eword)
if e.is_present():
if e.device_offset() == 0:
# ROM table points back to itself - shouldn't happen
continue
if e.device_address() in o_exclusions:
yield e
continue
# We don't want to fault when we encounter the same device in multiple
# ROM tables. Ideally, for a given recursive scan, we'd return each device
# at most once.
if e.device_address() in self.device_by_base_address:
dd = self.device_by_base_address[e.device_address()]
if dd.rom_table_entry is not None:
print("** note: redundant ROM table entries for device 0x%x:" % e.device_address())
print(" %s" % dd.rom_table_entry)
print(" %s" % e)
continue
# if we're scanning recursively, we have to map the device even if
# we aren't otherwise interested in devices. A ROM Table entry doesn't
# indicate that it points to a sub-table as opposed to some other device -
# we have to map the device and find out if it's another table.
d = self.create_device_at(e.device_address(), rom_table_entry=e)
e.device = d
# Fix up device affinity - in the absence of anywhere better.
# We could either do this using DEVAFF or heuristically.
id = d.affinity_id()
if id:
d.affinity_group = self.affinity_group(id)
adtype = None
if d.is_coresight_device_type(6,1):
adtype = "PMU"
elif d.is_coresight_device_type(3,1):
adtype = "ETM"
elif d.is_coresight_device_type(4,1):
adtype = "CTI"
if d.is_core_debug():
if not id:
d.affinity_group = AffinityGroup() # anonymous group
d.affinity_group.set_affine_device(d, "core")
cpus_in_this_table.append(d)
elif adtype is not None:
if id:
d.affinity_group.set_affine_device(d, adtype)
else:
# Allocate to the first CPU that hasn't yet got an affine device of this type
for c in cpus_in_this_table:
if c.affine_device(adtype) is None:
# If not using DEVAFF, core should be at -64K offset from the CTI
if adtype == "CTI" and c.base_address != (d.base_address - 0x10000):
continue
c.affinity_group.set_affine_device(d, adtype)
break
yield e
if recurse and d.is_rom_table():
for se in self.list_table(d, include_empty=include_empty, recurse=True):
yield se
# after yielding the device to whoever wants it, unmap it so we
# don't run out of memory mappings. Even a limit of 1000 will be
# exhausted if we have a 300-core SoC with 5 devices per core.
d.unmap()
else:
yield e
def show_coresight_device(self, d):
"""
Show some information about the device.
We organize this to show information progressively from the static
and abstract, to the dynamic and device-specific:
- device class: e.g. trace source
- architecture or product: e.g. ETMv4.1, or CoreSight ETB
- configuration chosen by designer: e.g. ETMv4.1 with four counters, 16K ETB
- programming: e.g. ETM counter transition rules, ETF in circular mode
- state: ETM current counter values, ETB buffer occupancy
"""
# Registers architected by CoreSight, with architected values
devtype = d.coresight_device_type()
desc = d.cs_device_type_name()
archdesc = d.architecture_name()
if archdesc is None:
archdesc = "<no arch>"
# architected regs with imp def values
affinity = d.affinity_id()
devid = d.read32(0xFC8)
authstatus = d.read32(0xFB8)
if o_show_authstatus:
# Authorization status works in three dimensions:
# functionality: invasive debug / non-invasive debug
# accessor: nonsecure, secure, hypervisor
# status: unsupported, supported and disabled, supported and enabled
print("", end=" ")
#print("auth=%04x" % authstatus, end=" ")