83
83
import os
84
84
import sys
85
85
import time
86
+ import math
86
87
87
88
from chipsec .module_common import BaseModule
88
89
from chipsec .library .returncode import ModuleResult
145
146
# very obscure option, don't even try to understand
146
147
GPR_2ADDR = False
147
148
148
- # Defines the time percentage increase at which the SMI call is considered to
149
- # be long-running
150
- OUTLIER_THRESHOLD = 33.3
149
+ # Defines the threshold in standard deviations at which the SMI call is
150
+ # considered long-running
151
+ OUTLIER_STD_DEV = 2
151
152
152
- # Scan mode delay before retry
153
- SCAN_MODE_RETRY_DELAY = 0.01
153
+ # Number of samples used for initial calibration
154
+ SCAN_CALIB_SAMPLES = 50
154
155
155
156
# SMI count MSR
156
157
MSR_SMI_COUNT = 0x00000034
@@ -207,6 +208,12 @@ def __init__(self):
207
208
self .helper = OsHelper ().get_default_helper ()
208
209
self .helper .init ()
209
210
self .smi_count = self .get_smi_count ()
211
+ self .needs_calibration = True
212
+ self .calib_samples = 0
213
+ self .stdev = 0
214
+ self .m2 = 0
215
+ self .stdev_hist = 0
216
+ self .m2_hist = 0
210
217
211
218
def __del__ (self ):
212
219
self .helper .close ()
@@ -254,6 +261,10 @@ def clear(self):
254
261
self .outliers = 0
255
262
self .code = None
256
263
self .confirmed = False
264
+ self .needs_calibration = True
265
+ self .calib_samples = 0
266
+ self .stdev = 0
267
+ self .m2 = 0
257
268
258
269
def add (self , duration , code , data , gprs , confirmed = False ):
259
270
if not self .code :
@@ -262,6 +273,7 @@ def add(self, duration, code, data, gprs, confirmed=False):
262
273
if not outlier :
263
274
self .acc_smi_duration += duration
264
275
self .acc_smi_num += 1
276
+ self .update_stdev (duration )
265
277
if duration > self .max .duration :
266
278
self .max .update (duration , code , data , gprs .copy ())
267
279
elif duration < self .min .duration :
@@ -281,24 +293,49 @@ def avg(self):
281
293
self .acc_smi_duration = 0
282
294
self .acc_smi_num = 0
283
295
296
+ #
297
+ # Computes the standard deviation using the Welford's online algorithm
298
+ #
299
+ def update_stdev (self , value ):
300
+ difference = value - self .avg_smi_duration
301
+ self .difference_hist = value - self .hist_smi_duration
302
+ self .avg ()
303
+ self .m2 += difference * (value - self .avg_smi_duration )
304
+ self .m2_hist += self .difference_hist * (value - self .hist_smi_duration )
305
+ variance = self .m2 / self .avg_smi_num
306
+ variance_hist = self .m2_hist / self .hist_smi_num
307
+ self .stdev = math .sqrt (variance )
308
+ self .stdev_hist = math .sqrt (variance_hist )
309
+
310
+ def update_calibration (self , duration ):
311
+ if not self .needs_calibration :
312
+ return
313
+ self .acc_smi_duration += duration
314
+ self .acc_smi_num += 1
315
+ self .update_stdev (duration )
316
+ self .calib_samples += 1
317
+ if self .calib_samples >= SCAN_CALIB_SAMPLES :
318
+ self .needs_calibration = False
319
+
284
320
def is_slow_outlier (self , value ):
285
321
ret = False
286
- if self . avg_smi_duration and value > self .avg_smi_duration * ( 1 + OUTLIER_THRESHOLD / 100 ) :
322
+ if value > self .avg_smi_duration + OUTLIER_STD_DEV * self . stdev :
287
323
ret = True
288
- if self . hist_smi_duration and value > self .hist_smi_duration * ( 1 + OUTLIER_THRESHOLD / 100 ) :
324
+ if value > self .hist_smi_duration + OUTLIER_STD_DEV * self . stdev_hist :
289
325
ret = True
290
326
return ret
291
327
292
328
def is_fast_outlier (self , value ):
293
329
ret = False
294
- if self . avg_smi_duration and value < self .avg_smi_duration * ( 1 - OUTLIER_THRESHOLD / 100 ) :
330
+ if value < self .avg_smi_duration - OUTLIER_STD_DEV * self . stdev :
295
331
ret = True
296
- if self . hist_smi_duration and value < self .hist_smi_duration * ( 1 - OUTLIER_THRESHOLD / 100 ) :
332
+ if value < self .hist_smi_duration - OUTLIER_STD_DEV * self . stdev_hist :
297
333
ret = True
298
334
return ret
299
335
300
336
def is_outlier (self , value ):
301
- self .avg ()
337
+ if self .needs_calibration :
338
+ return False
302
339
ret = False
303
340
if self .is_slow_outlier (value ):
304
341
ret = True
@@ -501,13 +538,17 @@ def smi_fuzz_iter(self, thread_id, _addr, _smi_desc, fill_contents=True, restore
501
538
else :
502
539
while True :
503
540
_ , duration = self .send_smi_timed (thread_id , _smi_desc .smi_code , _smi_desc .smi_data , _smi_desc .name , _smi_desc .desc , _rax , _rbx , _rcx , _rdx , _rsi , _rdi )
504
- if scan .valid_smi_count ():
541
+ if not scan .valid_smi_count ():
542
+ continue
543
+ if scan .needs_calibration :
544
+ scan .update_calibration (duration )
545
+ continue
546
+ else :
505
547
break
506
548
#
507
549
# Re-do the call if it was identified as an outlier, due to periodic SMI delays
508
550
#
509
551
if scan .is_outlier (duration ):
510
- time .sleep (SCAN_MODE_RETRY_DELAY )
511
552
while True :
512
553
_ , duration = self .send_smi_timed (thread_id , _smi_desc .smi_code , _smi_desc .smi_data , _smi_desc .name , _smi_desc .desc , _rax , _rbx , _rcx , _rdx , _rsi , _rdi )
513
554
if scan .valid_smi_count ():
0 commit comments