42
42
#define WCR_LOAD ((uint32_t )(1u << 3 ))
43
43
#define WCR_STORE ((uint32_t )(1u << 4 ))
44
44
45
- // Enable breakpoint, watchpoint, and vector catch debug exceptions.
46
- // (MDE bit in the MDSCR_EL1 register. Equivalent to the MDBGen bit in
47
- // DBGDSCRext in Aarch32)
48
- #define MDE_ENABLE ((uint32_t )(1u << 15 ))
49
-
50
45
// Single instruction step
51
46
// (SS bit in the MDSCR_EL1 register)
52
47
#define SS_ENABLE ((uint32_t )(1u ))
@@ -807,8 +802,6 @@ uint32_t DNBArchMachARM64::EnableHardwareBreakpoint(nub_addr_t addr,
807
802
(uint64_t )m_state.dbg .__bvr [i],
808
803
(uint32_t )m_state.dbg .__bcr [i]);
809
804
810
- // The kernel will set the MDE_ENABLE bit in the MDSCR_EL1 for us
811
- // automatically, don't need to do it here.
812
805
kret = SetDBGState (also_set_on_task);
813
806
814
807
DNBLogThreadedIf (LOG_WATCHPOINTS,
@@ -848,9 +841,10 @@ DNBArchMachARM64::AlignRequestedWatchpoint(nub_addr_t requested_addr,
848
841
849
842
// / Round up \a requested_size to the next power-of-2 size, at least 8
850
843
// / bytes
851
- // / requested_size == 3 -> aligned_size == 8
852
- // / requested_size == 13 -> aligned_size == 16
853
- // / requested_size == 16 -> aligned_size == 16
844
+ // / requested_size == 8 -> aligned_size == 8
845
+ // / requested_size == 9 -> aligned_size == 16
846
+ // / requested_size == 15 -> aligned_size == 16
847
+ // / requested_size == 192 -> aligned_size == 256
854
848
// / Could be `std::bit_ceil(aligned_size)` when we build with C++20?
855
849
aligned_size = 1ULL << (addr_bit_size - __builtin_clzll (aligned_size - 1 ));
856
850
@@ -922,7 +916,7 @@ uint32_t DNBArchMachARM64::EnableHardwareWatchpoint(nub_addr_t addr,
922
916
if (wps[0 ].aligned_size <= 8 )
923
917
return SetBASWatchpoint (wps[0 ], read, write, also_set_on_task);
924
918
else
925
- return INVALID_NUB_HW_INDEX ;
919
+ return SetMASKWatchpoint (wps[ 0 ], read, write, also_set_on_task) ;
926
920
}
927
921
928
922
// We have multiple WatchpointSpecs
@@ -1024,9 +1018,6 @@ uint32_t DNBArchMachARM64::SetBASWatchpoint(DNBArchMachARM64::WatchpointSpec wp,
1024
1018
(uint64_t )m_state.dbg .__wvr [i],
1025
1019
(uint32_t )m_state.dbg .__wcr [i]);
1026
1020
1027
- // The kernel will set the MDE_ENABLE bit in the MDSCR_EL1 for us
1028
- // automatically, don't need to do it here.
1029
-
1030
1021
kret = SetDBGState (also_set_on_task);
1031
1022
// DumpDBGState(m_state.dbg);
1032
1023
@@ -1042,6 +1033,81 @@ uint32_t DNBArchMachARM64::SetBASWatchpoint(DNBArchMachARM64::WatchpointSpec wp,
1042
1033
return INVALID_NUB_HW_INDEX;
1043
1034
}
1044
1035
1036
+ uint32_t
1037
+ DNBArchMachARM64::SetMASKWatchpoint (DNBArchMachARM64::WatchpointSpec wp,
1038
+ bool read, bool write,
1039
+ bool also_set_on_task) {
1040
+ const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints ();
1041
+
1042
+ // Read the debug state
1043
+ kern_return_t kret = GetDBGState (false );
1044
+ if (kret != KERN_SUCCESS)
1045
+ return INVALID_NUB_HW_INDEX;
1046
+
1047
+ // Check to make sure we have the needed hardware support
1048
+ uint32_t i = 0 ;
1049
+
1050
+ for (i = 0 ; i < num_hw_watchpoints; ++i) {
1051
+ if ((m_state.dbg .__wcr [i] & WCR_ENABLE) == 0 )
1052
+ break ; // We found an available hw watchpoint slot
1053
+ }
1054
+ if (i == num_hw_watchpoints) {
1055
+ DNBLogThreadedIf (LOG_WATCHPOINTS,
1056
+ " DNBArchMachARM64::"
1057
+ " SetMASKWatchpoint(): All "
1058
+ " hardware resources (%u) are in use." ,
1059
+ num_hw_watchpoints);
1060
+ return INVALID_NUB_HW_INDEX;
1061
+ }
1062
+
1063
+ DNBLogThreadedIf (LOG_WATCHPOINTS,
1064
+ " DNBArchMachARM64::"
1065
+ " SetMASKWatchpoint() "
1066
+ " set hardware register %d to MASK watchpoint "
1067
+ " aligned start address 0x%llx, aligned size %zu" ,
1068
+ i, wp.aligned_start , wp.aligned_size );
1069
+
1070
+ // Clear any previous LoHi joined-watchpoint that may have been in use
1071
+ LoHi[i] = 0 ;
1072
+
1073
+ // MASK field is the number of low bits that are masked off
1074
+ // when comparing the address with the DBGWVR<n>_EL1 values.
1075
+ // If aligned size is 16, that means we ignore low 4 bits, 0b1111.
1076
+ // popcount(16 - 1) give us the correct value of 4.
1077
+ // 2GB is max watchable region, which is 31 bits (low bits 0x7fffffff
1078
+ // masked off) -- a MASK value of 31.
1079
+ const uint64_t mask = __builtin_popcountl (wp.aligned_size - 1 ) << 24 ;
1080
+ // A '0b11111111' BAS value needed for mask watchpoints plus a
1081
+ // nonzero mask value.
1082
+ const uint64_t not_bas_wp = 0xff << 5 ;
1083
+
1084
+ m_state.dbg .__wvr [i] = wp.aligned_start ;
1085
+ m_state.dbg .__wcr [i] = mask | not_bas_wp | S_USER | // Stop only in user mode
1086
+ (read ? WCR_LOAD : 0 ) | // Stop on read access?
1087
+ (write ? WCR_STORE : 0 ) | // Stop on write access?
1088
+ WCR_ENABLE; // Enable this watchpoint;
1089
+
1090
+ DNBLogThreadedIf (LOG_WATCHPOINTS,
1091
+ " DNBArchMachARM64::SetMASKWatchpoint() "
1092
+ " adding watchpoint on address 0x%llx with control "
1093
+ " register value 0x%llx" ,
1094
+ (uint64_t )m_state.dbg .__wvr [i],
1095
+ (uint64_t )m_state.dbg .__wcr [i]);
1096
+
1097
+ kret = SetDBGState (also_set_on_task);
1098
+
1099
+ DNBLogThreadedIf (LOG_WATCHPOINTS,
1100
+ " DNBArchMachARM64::"
1101
+ " SetMASKWatchpoint() "
1102
+ " SetDBGState() => 0x%8.8x." ,
1103
+ kret);
1104
+
1105
+ if (kret == KERN_SUCCESS)
1106
+ return i;
1107
+
1108
+ return INVALID_NUB_HW_INDEX;
1109
+ }
1110
+
1045
1111
bool DNBArchMachARM64::ReenableHardwareWatchpoint (uint32_t hw_index) {
1046
1112
// If this logical watchpoint # is actually implemented using
1047
1113
// two hardware watchpoint registers, re-enable both of them.
@@ -1068,14 +1134,11 @@ bool DNBArchMachARM64::ReenableHardwareWatchpoint_helper(uint32_t hw_index) {
1068
1134
1069
1135
DNBLogThreadedIf (LOG_WATCHPOINTS,
1070
1136
" DNBArchMachARM64::"
1071
- " SetBASWatchpoint ( %u ) - WVR%u = "
1137
+ " ReenableHardwareWatchpoint_helper ( %u ) - WVR%u = "
1072
1138
" 0x%8.8llx WCR%u = 0x%8.8llx" ,
1073
1139
hw_index, hw_index, (uint64_t )m_state.dbg .__wvr [hw_index],
1074
1140
hw_index, (uint64_t )m_state.dbg .__wcr [hw_index]);
1075
1141
1076
- // The kernel will set the MDE_ENABLE bit in the MDSCR_EL1 for us
1077
- // automatically, don't need to do it here.
1078
-
1079
1142
kret = SetDBGState (false );
1080
1143
1081
1144
return (kret == KERN_SUCCESS);
@@ -1177,30 +1240,61 @@ uint32_t DNBArchMachARM64::GetHardwareWatchpointHit(nub_addr_t &addr) {
1177
1240
uint32_t i, num = NumSupportedHardwareWatchpoints ();
1178
1241
for (i = 0 ; i < num; ++i) {
1179
1242
nub_addr_t wp_addr = GetWatchAddress (debug_state, i);
1180
- uint32_t byte_mask = bits (debug_state.__wcr [i], 12 , 5 );
1181
1243
1182
1244
DNBLogThreadedIf (LOG_WATCHPOINTS,
1183
1245
" DNBArchImplARM64::"
1184
1246
" GetHardwareWatchpointHit() slot: %u "
1185
- " (addr = 0x%llx; byte_mask = 0x%x )" ,
1186
- i, static_cast < uint64_t >( wp_addr), byte_mask );
1247
+ " (addr = 0x%llx, WCR = 0x%llx )" ,
1248
+ i, wp_addr, debug_state. __wcr [i] );
1187
1249
1188
1250
if (!IsWatchpointEnabled (debug_state, i))
1189
1251
continue ;
1190
1252
1191
- if (bits (wp_addr, 48 , 3 ) != bits (addr, 48 , 3 ))
1192
- continue ;
1253
+ // DBGWCR<n>EL1.BAS are the bits of the doubleword that are watched
1254
+ // with a BAS watchpoint.
1255
+ uint32_t bas_bits = bits (debug_state.__wcr [i], 12 , 5 );
1256
+ // DBGWCR<n>EL1.MASK is the number of bits that are masked off the
1257
+ // virtual address when comparing to DBGWVR<n>_EL1.
1258
+ uint32_t mask = bits (debug_state.__wcr [i], 28 , 24 );
1193
1259
1194
- // Sanity check the byte_mask
1195
- uint32_t lsb = LowestBitSet (byte_mask);
1196
- if (lsb < 0 )
1197
- continue ;
1260
+ const bool is_bas_watchpoint = mask == 0 ;
1198
1261
1199
- uint64_t byte_to_match = bits (addr, 2 , 0 );
1262
+ DNBLogThreadedIf (
1263
+ LOG_WATCHPOINTS,
1264
+ " DNBArchImplARM64::"
1265
+ " GetHardwareWatchpointHit() slot: %u %s" ,
1266
+ i, is_bas_watchpoint ? " is BAS watchpoint" : " is MASK watchpoint" );
1200
1267
1201
- if (byte_mask & (1 << byte_to_match)) {
1202
- addr = wp_addr + lsb;
1203
- return i;
1268
+ if (is_bas_watchpoint) {
1269
+ if (bits (wp_addr, 48 , 3 ) != bits (addr, 48 , 3 ))
1270
+ continue ;
1271
+ } else {
1272
+ if (bits (wp_addr, 48 , mask) == bits (addr, 48 , mask)) {
1273
+ DNBLogThreadedIf (LOG_WATCHPOINTS,
1274
+ " DNBArchImplARM64::"
1275
+ " GetHardwareWatchpointHit() slot: %u matched MASK "
1276
+ " ignoring %u low bits" ,
1277
+ i, mask);
1278
+ return i;
1279
+ }
1280
+ }
1281
+
1282
+ if (is_bas_watchpoint) {
1283
+ // Sanity check the bas_bits
1284
+ uint32_t lsb = LowestBitSet (bas_bits);
1285
+ if (lsb < 0 )
1286
+ continue ;
1287
+
1288
+ uint64_t byte_to_match = bits (addr, 2 , 0 );
1289
+
1290
+ if (bas_bits & (1 << byte_to_match)) {
1291
+ addr = wp_addr + lsb;
1292
+ DNBLogThreadedIf (LOG_WATCHPOINTS,
1293
+ " DNBArchImplARM64::"
1294
+ " GetHardwareWatchpointHit() slot: %u matched BAS" ,
1295
+ i);
1296
+ return i;
1297
+ }
1204
1298
}
1205
1299
}
1206
1300
}
0 commit comments