8383import struct
8484import sys
8585import time
86+ import math
8687
8788from chipsec .module_common import BaseModule
8889from chipsec .library .returncode import ModuleResult
147148# Defines the time percentage increase at which the SMI call is considered to
148149# be long-running
149150OUTLIER_THRESHOLD = 10
151+ OUTLIER_STD_DEV = 2
152+ SCAN_CALIB_SAMPLES = 50
150153
151154# Scan mode delay before SMI calls
152155SCAN_MODE_DELAY = 0.01
@@ -205,6 +208,15 @@ def __init__(self):
205208 self .outliers_hist = 0
206209 self .records = {'deltas' : [], 'times' : []}
207210 self .msr_count = self .get_msr_count ()
211+ self .stdev = 0
212+ self .stdev_hist = 0
213+ self .m2 = 0
214+ self .m2_hist = 0
215+ self .difference = 0
216+ self .difference_hist = 0
217+ self .needs_calibration = True
218+ self .calib_samples = 0
219+ self .first_measurement = True
208220
209221 def get_msr_count (self ):
210222 cpu = 0
@@ -214,6 +226,12 @@ def get_msr_count(self):
214226 os .close (fd )
215227 return count
216228
229+ def is_first_measurement (self ):
230+ is_first = self .first_measurement
231+ if self .first_measurement :
232+ self .first_measurement = False
233+ return is_first
234+
217235 def check_inc_msr (self ):
218236 valid = False
219237 count = self .get_msr_count ()
@@ -243,6 +261,12 @@ def clear(self):
243261 self .code = None
244262 self .confirmed = False
245263 self .records = {'deltas' : [], 'times' : []}
264+ self .stdev = 0
265+ self .m2 = 0
266+ self .difference = 0
267+ self .needs_calibration = True
268+ self .calib_samples = 0
269+ self .first_measurement = True
246270
247271 def add (self , duration , time , code , data , gprs , confirmed = False ):
248272 if not self .code :
@@ -252,6 +276,7 @@ def add(self, duration, time, code, data, gprs, confirmed=False):
252276 self .records ['times' ].append (time )
253277 self .acc_smi_duration += duration
254278 self .acc_smi_num += 1
279+ self .update_stdev (duration )
255280 if not outlier :
256281 if duration > self .max .duration :
257282 self .max .update (duration , code , data , gprs .copy ())
@@ -272,24 +297,49 @@ def avg(self):
272297 self .acc_smi_duration = 0
273298 self .acc_smi_num = 0
274299
300+ def update_stdev (self , value ):
301+ self .difference = value - self .avg_smi_duration
302+ self .difference_hist = value - self .hist_smi_duration
303+ self .avg ()
304+ self .m2 += self .difference * (value - self .avg_smi_duration )
305+ self .m2_hist += self .difference_hist * (value - self .hist_smi_duration )
306+ variance = self .m2 / self .avg_smi_num
307+ variance_hist = self .m2_hist / self .hist_smi_num
308+ self .stdev = math .sqrt (variance )
309+ self .stdev_hist = math .sqrt (variance_hist )
310+
311+ def update_calibration (self , duration ):
312+ if not self .needs_calibration :
313+ return
314+ self .acc_smi_duration += duration
315+ self .acc_smi_num += 1
316+ self .update_stdev (duration )
317+ self .calib_samples += 1
318+ if self .calib_samples >= SCAN_CALIB_SAMPLES :
319+ self .needs_calibration = False
320+ print (f"Calibration done. stdev: { self .stdev } , mean: { self .avg_smi_duration } , samples: { self .calib_samples } " )
321+
322+
275323 def is_slow_outlier (self , value ):
276324 ret = False
277- if self . avg_smi_duration and value > self .avg_smi_duration * ( 1 + OUTLIER_THRESHOLD / 100 ) :
325+ if value > self .avg_smi_duration + OUTLIER_STD_DEV * self . stdev :
278326 ret = True
279- if self . hist_smi_duration and value > self .hist_smi_duration * ( 1 + OUTLIER_THRESHOLD / 100 ) :
327+ if value > self .hist_smi_duration + OUTLIER_STD_DEV * self . stdev_hist :
280328 ret = True
281329 return ret
282330
283331 def is_fast_outlier (self , value ):
284332 ret = False
285- if self . avg_smi_duration and value < self .avg_smi_duration * ( 1 - OUTLIER_THRESHOLD / 100 ) :
333+ if value < self .avg_smi_duration - OUTLIER_STD_DEV * self . stdev :
286334 ret = True
287- if self . hist_smi_duration and value < self .hist_smi_duration * ( 1 - OUTLIER_THRESHOLD / 100 ) :
335+ if value < self .hist_smi_duration - OUTLIER_STD_DEV * self . stdev_hist :
288336 ret = True
289337 return ret
290338
291339 def is_outlier (self , value ):
292- self .avg ()
340+ if self .needs_calibration :
341+ return False
342+
293343 ret = False
294344 if self .is_slow_outlier (value ):
295345 ret = True
@@ -308,7 +358,6 @@ def get_total_outliers(self):
308358 return self .outliers_hist
309359
310360 def get_info (self ):
311- self .avg ()
312361 avg = self .avg_smi_duration or self .hist_smi_duration
313362 info = f"average { round (avg )} checked { self .avg_smi_num + self .outliers } "
314363 if self .outliers :
@@ -496,7 +545,14 @@ def smi_fuzz_iter(self, thread_id, _addr, _smi_desc, fill_contents=True, restore
496545 while True :
497546 #time.sleep(SCAN_MODE_DELAY)
498547 _ , duration , start = 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 )
499- if scan .check_inc_msr ():
548+ if scan .is_first_measurement ():
549+ continue
550+ if not scan .check_inc_msr ():
551+ continue
552+ if scan .needs_calibration :
553+ scan .update_calibration (duration )
554+ continue
555+ else :
500556 break
501557 #
502558 # Re-do the call if it was identified as an outlier, due to periodic SMI delays
0 commit comments