Thursday, October 11, 2018

[Python] How to do Text Recognition based on BIOS Setup Snapshot

import math
import cv2
import numpy as np
from matplotlib import pyplot as plt
import pytesseract
from PIL import Image
#alias new='C:\Program Files (x86)\Tesseract-OCR\tesseract.exe'

def main():

#------------------------------------------------------------------------------------------------

#INPUT Image Path

# 1. 讀取原圖 Pre_1_Load_Image
    
load_image = cv2.imread('amd_setup.bmp',1)

#------------------------------------------------------------------------------------------------

# 2. 非銳化濾鏡 (Un-Sharp Masking) (銳化功能), 
        #      Pre_2_sharpen_image, Pre_1_Load_Image -> Pre_2_sharpen_image
#
# 2.1. GaussianBlur
#         (5,5) = K-Size, 濾鏡尺寸
#          0.0 = SigmaX, standard deviation in X direction, If set to 0, then this value is computed from K-Size
#
# 2.2. addWeighted  把兩張 Images 合而為一
#    1.5 = load_image 的權重, -0.5 = Gaussian_Blur_Image 的權重

Gaussian_Blur_Image = cv2.GaussianBlur(load_image, (5,5), 0.0)
sharpen_load_image = cv2.addWeighted(load_image, 1.5, Gaussian_Blur_Image, -0.5, 0, load_image)
cv2.imwrite("Pre_2_sharpen_image.jpg", sharpen_load_image)

#------------------------------------------------------------------------------------------------

# 3. CANNY 邊緣偵測, Pre_3_edges_image, sharpen_image -> edges
#
#    照理說本來應該先做 GaussianBlur 再做 Canny, 但是因為我們的圖是截圖, 不會有雜訊問題
#
# 3.1. gray_load_image 來自 sharpen_load_image 的轉灰階

# 3.2. edges 來自於 gray_load_image 的 Canny 邊緣偵測
#      50 = 最小門檻值, 距離小於此值的 edges會被連通, 150 = 如果該 Pixel 的 gradient 大於此值
        #      則標示為可能的 edges 點
#

gray_load_image = cv2.cvtColor(sharpen_load_image, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray_load_image,50,150)
cv2.imwrite("Pre_3_edges.jpg", edges)

#------------------------------------------------------------------------------------------------

# 4. 取得圖片的 Height/ Width of (edges)

print ('height, width:')
print (edges.shape)

height, width = edges.shape

#------------------------------------------------------------------------------------------------

# 5. 定義水平與垂直線段的區間門檻值, i.e. 多少距離內的 edges 點可以被視為同一線段?

#Height = 768, Width = 1024, 水平線寬: 3 pixels 0.39%, 垂直線寬: 4 pixels 0.39%, 
        #水平第一條線與第二條線垂直距離: 9 pixels, 考慮 0.5% 的誤差, 
#水平線之間的寬可以容忍到 3.84 pixels (取 4 pixles (含以下))
#垂直線之間的寬可以容忍到 5.12 pixels (取 6 pixles (含以下))

tolerance_hori_lines_Dis = int(math.ceil(float(height) * 0.005))
tolerance_vert_lines_Dis = int(math.ceil(float(width) * 0.005))

print ('tolerance_hori_lines_Dis, tolerance_vert_lines_Dis:')
print (tolerance_hori_lines_Dis, tolerance_vert_lines_Dis)

#------------------------------------------------------------------------------------------------

# 6. Hough 直線偵測
#    minimum pixel number = minimum line length = 0.8 * height, 因為
        #    我們最小想要偵測到的線段是最大方框兩邊的垂直線, 原 Layout 比例為 85% * height

#                               img  ,rho, angle meter , minimum pixel number, 
        #                               minimum line length, maximum distance within one line
#detect_lines = cv2.HoughLinesP(edges,1  , np.pi/180   , 30                  , minLineLength=60   , maxLineGap=10)
detect_lines = cv2.HoughLinesP(edges,1,np.pi/2, int(0.8*(float(height))), int(0.8*(float(height))), int(math.ceil(0.5*tolerance_vert_lines_Dis)))

TwoDim_detect_lines = detect_lines[:,0,:] #Convert the line info into 2D

#------------------------------------------------------------------------------------------------

# 7. 把偵測到的線段輸出以備參考 load_image -> LineDetected_load_image -> Origin_Add_Line.jpg

LineDetected_load_image=cv2.cvtColor(load_image,cv2.COLOR_BGR2GRAY)

print ('-----x1,y1,x2,y2-----')

for x1,y1,x2,y2 in TwoDim_detect_lines[:]: 
cv2.line(LineDetected_load_image,(x1,y1),(x2,y2),(0,0,255),1)
print(x1,y1,x2,y2)

cv2.imwrite("Pre_7_Origin_Add_Line.jpg", LineDetected_load_image)

#------------------------------------------------------------------------------------------------

# 8. 列印出所有線段資訊做參考

# 8.1 挑出所有的水平線, left_top, left_down, right_top, right_down 代表四個角點 -----------------

Detect_Hori_lines = []

# 找水平線 

for index in range(len(TwoDim_detect_lines)):
if TwoDim_detect_lines[index][3] == TwoDim_detect_lines[index][1]:
Detect_Hori_lines.append(TwoDim_detect_lines[index])

# 過濾水平長度不足者 (abs(x2-x1) > 0.95 * width)

for index in range(len(Detect_Hori_lines)):
if( (abs(Detect_Hori_lines[index][2]-Detect_Hori_lines[index][0])) < (0.95 * width)):
Detect_Hori_lines[index] = [0,0,0,0]
#print('too short')

print('x1,y1,x2,y2, hori lines with enough length')
for x1,y1,x2,y2 in Detect_Hori_lines[:]: 
print(x1,y1,x2,y2)

# 找出距離圖片中心最近的兩條線

minimum_distance_upper_line = height
minimum_distance_lower_line = height

for index in range(len(Detect_Hori_lines)):

#print('Processing')
#print(Detect_Hori_lines[index])

if(Detect_Hori_lines[index][1] < 0.5 * height):
#print('Processing Upper')
#print('0.5 * height - Detect_Hori_lines[index][1]), minimum_distance_upper_line')
#print(0.5 * height - Detect_Hori_lines[index][1], minimum_distance_upper_line)
if((0.5 * height - Detect_Hori_lines[index][1]) < minimum_distance_upper_line):
minimum_distance_upper_line = 0.5 * height - Detect_Hori_lines[index][1]
Detect_Upper_lines = Detect_Hori_lines[index]
#print('Detect_Upper_lines')
#print(Detect_Upper_lines)
else:
#print('Processing Lower')
#print('Detect_Hori_lines[index][1]) - 0.5 * height < minimum_distance_lower_line')
#print(Detect_Hori_lines[index][1] - 0.5 * height, minimum_distance_lower_line)
if((Detect_Hori_lines[index][1]) - 0.5 * height < minimum_distance_lower_line):
minimum_distance_lower_line = Detect_Hori_lines[index][1] - 0.5 * height
Detect_Lower_lines = Detect_Hori_lines[index]
#print('Detect_Lower_lines')
#print(Detect_Lower_lines)

print('Detect_Upper_lines, Detect_Lower_lines')
print(Detect_Upper_lines, Detect_Lower_lines)

#HoriLineDetected_load_image = load_image
#cv2.line(HoriLineDetected_load_image,(Detect_Upper_lines[0],Detect_Upper_lines[1]),(Detect_Upper_lines[2],Detect_Upper_lines[3]),(0,0,255),1)
#cv2.line(HoriLineDetected_load_image,(Detect_Lower_lines[0],Detect_Lower_lines[1]),(Detect_Lower_lines[2],Detect_Lower_lines[3]),(0,0,255),1)
#cv2.imwrite("Origin_Add_Hori_Line.jpg", HoriLineDetected_load_image)

# 標記出四個角點, 或是 left_top, right_down 就好

Inner_X_Line = (max(Detect_Upper_lines[0], Detect_Lower_lines[0]) + tolerance_vert_lines_Dis, min(Detect_Upper_lines[2], Detect_Lower_lines[2]) - tolerance_vert_lines_Dis) 
Inner_Y_Line = (Detect_Upper_lines[1] + tolerance_hori_lines_Dis, Detect_Lower_lines[1] - tolerance_hori_lines_Dis) 

print('Inner_X_Line, Inner_Y_Line')
print(Inner_X_Line, Inner_Y_Line)

left_top = (Inner_X_Line[0], Inner_Y_Line[0])
right_down = (Inner_X_Line[1], Inner_Y_Line[1]) 

print('left_top, right_down')
print(left_top, right_down)

# Debug: 輸出ROI區域線段

ROI_Lined_image=cv2.cvtColor(load_image,cv2.COLOR_BGR2GRAY)
cv2.line(ROI_Lined_image, left_top, right_down, (0,0,255), 1)
cv2.imwrite("Pre_8_1_Origin_Add_ROI_Rec.jpg", ROI_Lined_image)

# HoriLineDetected_load_image = load_image
# cv2.line(HoriLineDetected_load_image, left_top, right_down, (0,0,255), 1)
# cv2.imwrite("Origin_Add_Inner_Rec.jpg", HoriLineDetected_load_image)
# cv2.line(HoriLineDetected_load_image,(Detect_Lower_lines[0],Detect_Lower_lines[1]),(Detect_Lower_lines[2],Detect_Lower_lines[3]),(0,0,255),1)

# 8.2 挑出中間偏右的垂直分隔線 -----------------

Inner_Vert_Lines = []

# 找所有垂直線 

for index in range(len(TwoDim_detect_lines)):
if TwoDim_detect_lines[index][0] == TwoDim_detect_lines[index][2]:
Inner_Vert_Lines.append(TwoDim_detect_lines[index])

# 過濾垂直長度不足者 (abs(y1-y2) > 0.80 * height)

for index in range(len(Inner_Vert_Lines)):
if( (abs(Inner_Vert_Lines[index][1]-Inner_Vert_Lines[index][3])) < (0.80 * height)):
Inner_Vert_Lines[index] = [0,0,0,0]
#print('too short')

print('x1,y1,x2,y2, vertical lines with enough length')
for x1,y1,x2,y2 in Inner_Vert_Lines[:]: 
print(x1,y1,x2,y2)

# 挑選最靠近中心點的垂直線

minimum_distance_inner_vert_line = 0.5 * width

for index in range(len(Inner_Vert_Lines)):
if( abs(Inner_Vert_Lines[index][0]-0.5 * width) < minimum_distance_inner_vert_line):
minimum_distance_inner_vert_line = abs(Inner_Vert_Lines[index][0]-0.5 * width)
Detect_Inner_Vert_Line = Inner_Vert_Lines[index]

print('Detect_Inner_Vert_Line')
print(Detect_Inner_Vert_Line)

# 取出中間分隔線的終點, 並且內縮

#cv2.line(HoriLineDetected_load_image, left_top, (Detect_Inner_Vert_Line[0], Detect_Inner_Vert_Line[1]), (0,0,255), 1)
#cv2.imwrite("Origin_Add_Two_Inner_Rec.jpg", HoriLineDetected_load_image)

Inner_right_down = (Detect_Inner_Vert_Line[0] - tolerance_vert_lines_Dis, max(Detect_Inner_Vert_Line[1],Detect_Inner_Vert_Line[3])-tolerance_hori_lines_Dis)

print('Inner_right_down')
print(Inner_right_down)

#cv2.line(HoriLineDetected_load_image, left_top, (Inner_right_down[0], Inner_right_down[1]), (0,0,255), 1)
#cv2.imwrite("Origin_Add_Two_Inner_Rec.jpg", HoriLineDetected_load_image)

# 8.3 定出中間最大區塊的座標值

# 左上角 left_top, 右下角 right_down, 中間區塊的右下角 Inner_right_down, 格式皆為 (x,y), y 由上往下算

#----------------------------------------------------------------------------------------------------

# 9. 切割出整行的文字影像

# edges 是經過銳化 + Canny 取邊緣的黑白圖

# 9.1. 取中間主要區塊的子影像 (edges)

Region_Interest = edges[left_top[1]:Inner_right_down[1], left_top[0]:Inner_right_down[0]] #y1,y2 and x1,x2

cv2.imwrite("Pre_9_1_Region_Interest.jpg", Region_Interest)

# 9.2. 針對 Region_Interest (Cut from edges) 取一次垂直的直方圖 

SumCols = []
for Vert_Y in range(Inner_right_down[1]-left_top[1]):
SumCols.append(0)
for Hori_X in range(Inner_right_down[0]-left_top[0]):
if(Region_Interest[Vert_Y][Hori_X]!=0):
SumCols[Vert_Y]+=1

print('len(SumCols)')
print(len(SumCols))

print('index,Sumcol_Value')
for index in range(len(SumCols)):
print(index,SumCols[index])

# 9.3 從 SumCols 計算出垂直的每行文字的起始位置, 直的是 row, 橫的是 column

IsInText = False
Start_End_Each_Texts = []
Start_End_Each_Texts.append([])
Start_End_Each_Texts.append([])

#Start_End_Each_Texts[0] = Start
#Start_End_Each_Texts[1] = End

for index in range(len(SumCols)):
if (SumCols[index]!=0):                       # Text 區
if(IsInText==False):                      # 新的 Text 區首個行數
Start_End_Each_Texts[0].append(index + left_top[1]) # Start Point
IsInText = True
else:                       # 非Text區首個行數
if(SumCols[index+1]==0):
Start_End_Each_Texts[1].append(index + left_top[1] + 1)
else:                       # 非 Text 區
IsInText=False

print('Start_End_Each_Texts')
print(Start_End_Each_Texts[0])
print(Start_End_Each_Texts[1])


# 9.4 每個 Text 區塊的子影像做局部二值化 (因為有反白區域)

for index in range(len(Start_End_Each_Texts[0])):

Sub_ROI = sharpen_load_image[Start_End_Each_Texts[0][index]:Start_End_Each_Texts[1][index],left_top[0]:Inner_right_down[0]] # 要用這個 
Sub_ROI = cv2.cvtColor(Sub_ROI, cv2.COLOR_BGR2GRAY)
ret1,th1 = cv2.threshold(Sub_ROI,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)

#Sub_ROI = load_image[Start_End_Each_Texts[0][index]:Start_End_Each_Texts[1][index],left_top[0]:Inner_right_down[0]] #看圖用而已

cv2.imwrite('Sub_ROI_TH_%s.jpg'%str(index), th1)
cv2.imwrite('Sub_ROI_Gray_%s.jpg'%str(index), Sub_ROI)

#----------------------------------------------------------------------------
text = pytesseract.image_to_string(Image.open("Sub_ROI_TH_%s.jpg"%str(index)))
#text = pytesseract.image_to_string(th1)
print(text)
#----------------------------------------------------------------------------
    

# 9.5 只是為了把 ROI 中的每行文字用方框標示的圖示輸出

for index in range(len(Start_End_Each_Texts[0])):
cv2.rectangle(load_image,(left_top[0],Start_End_Each_Texts[0][index]),(Inner_right_down[0],Start_End_Each_Texts[1][index]),(55,255,155),1)

cv2.imwrite('Rec.jpg', load_image)

#------------------------------------------------------------------------------------------------------------------------------------------


if __name__ == '__main__':
    main()
    #sys.exit(main())

Wednesday, September 26, 2018

[ASL] (1) How to Safely R/W I2C Device Using ASL

OperationRegion(ICM7, SystemMemory, 0xA112C000, 256)
        Field(ICM7, DWordAcc, NoLock, Preserve) //Field
        {
            ICN7, 32,                   // R_IC_CON (I2C Control)
            Offset(0x04),
            TAR7, 32,                   // R_IC_TAR (I2C Target Address)
            Offset(0x08),
            SAR7, 32,                   // R_IC_SAR (I2C Slave Address) *
            Offset(0x0C),
            HMD7, 32,                   // R_IC_HS_MADDR (I2C HS MasterMode Code Address) *
            Offset(0x10),
            DCD7, 32,                   // R_IC_DATA_CMD (I2C Rx/Tx Data Buffer and Command)
            Offset(0x14),
            SSH7, 32,                   // R_IC_SS_SCL_HCNT (Standard Speed I2C Clock SCL High Count) *
            Offset(0x18),
            SSL7, 32,                   // R_IC_SS_SCL_LCNT (Standard Speed I2C Clock SCL Low Count) *
            Offset(0x1C),
            FSH7, 32,                   // R_IC_FS_SCL_HCNT (Full Speed I2C Clock SCL High Count) *
            Offset(0x20),
            FSL7, 32,                   // R_IC_FS_SCL_LCNT (Full Speed I2C Clock SCL Low Count) *
            Offset(0x24),
            HSH7, 32,                   // R_IC_HS_SCL_HCNT (High Speed I2C Clock SCL High Count) *
            Offset(0x28),
            HSL7, 32,                   // R_IC_HS_SCL_LCNT (High Speed I2C Clock SCL Low Count) *
            Offset(0x2C),
            ITS7, 32,                   // R_IC_INTR_STAT (I2C Inetrrupt Status) *
            Offset(0x30),
            ITM7, 32,                   // R_IC_INTR_MASK (I2C Interrupt Mask)
            Offset(0x34),
            RIS7, 32,                   // R_IC_RAW_INTR_STAT (I2C Raw Interrupt Status)
            Offset(0x38),
            IRT7, 32,                   // R_IC_RX_TL (I2C Receive FIFO Threshold)
            Offset(0x3C),
            ITT7, 32,                   // R_IC_TX_TL (I2C Transmit FIFO Threshold)
            Offset(0x40),
            CIT7, 32,                   // R_IC_CLR_INTR (Clear Combined and Individual Interrupts)
            Offset(0x44),
            CRU7, 32,                   // R_IC_CLR_RX_UNDER (Clear RX_UNDER Interrupt) *
            Offset(0x48),
            CRO7, 32,                   // R_IC_CLR_RX_OVER (Clear RX_OVERinterrupt) *
            Offset(0x4C),
            CTO7, 32,                   // R_IC_CLR_TX_OVER (Clear TX_OVER interrupt) *
            Offset(0x50),
            CRR7, 32,                   // R_IC_CLR_RD_REQ (Clear RD_REQ interrupt) *
            Offset(0x54),
            CTA7, 32,                   // R_IC_CLR_TX_ABRT (Clear TX_ABRT interrupt)
            Offset(0x58),
            CRD7, 32,                   // R_IC_CLR_RX_DONE (Clear RX_DONE interrupt) *
            Offset(0x5C),
            CAT7, 32,                   // R_IC_CLR_ACTIVITY (Clear ACTIVITY interrupt) *
            Offset(0x60),
            CSD7, 32,                   // R_IC_CLR_STOP_DET (Clear STOP_DET interrupt) *
            Offset(0x64),
            SAD7, 32,                   // R_IC_CLR_START_DET (Clear START_DET interrupt) *
            Offset(0x68),
            CGC7, 32,                   // R_IC_CLR_GEN_CALL (Clear GEN_CALL interrupt) *
            Offset(0x6C),
            IEN7, 32,                   // R_IC_ENABLE (I2C Enable)
            Offset(0x70),
            IST7, 32,                   // R_IC_STATUS (I2C Status)
            Offset(0x74),
            TFL7, 32,                   // R_IC_TXFLR (Transmit FIFO Level Register)
            Offset(0x78),
            RFL7, 32,                   // R_IC_RXFLR (Receive FIFO Level Register)       
            Offset(0x80),
            TAS7, 32,                   // R_IC_TX_ABRT_SOURCE (I2C Transmit Abort Status Register)
            Offset(0x84),
            SDN7, 32,                   // R_IC_SLV_DATA_NACK_ONLY (Generate SLV_DATA_NACK Register) *
            Offset(0x88),
            DCR7, 32,                   // R_IC_DMA_CR (DMA Control Register) *
            Offset(0x8C),
            DTL7, 32,                   // R_IC_DMA_TDLR (DMA Transmit Data Level) *
            Offset(0x90),
            DRL7, 32,                   // R_IC_DMA_RDLR (DMA Receive Data Level) *
            Offset(0x94),
            SST7, 32,                   // R_IC_SDA_SETUP (I2C SDA Setup Register) *
            Offset(0x98),
            AGC7, 32,                   // R_IC_ACK_GENERAL_CALL (I2C ACK General Call Register) *
            Offset(0x9C),
            ETS7, 32,                   // R_IC_ENABLE_STATUS (I2C Enable Status Register) *
            Offset(0xC0),
            CGT7, 32,                   // R_IC_CLK_GATE (Clock Gate)
            Offset(0xF4),
            CPP7, 32,                   // R_IC_COMP_PARAM (Component Parameter Register) *
            Offset(0xF8),
            CPV7, 32,                   // R_IC_COMP_VERSION (Component Version ID) *
            Offset(0xFC),
            CPT7, 32,                   // R_IC_COMP_TYPE (Component Type) *
        }
       
// ------------------------------------------------------------------------------------
// *********************I2C R/W Registers and Definition*******************************
// ------------------------------------------------------------------------------------

        Name(RDST, 0) // RDST 是通用的 Read 回傳 Status
        Name(RDDT, 0) // RDDT 是 I2C READ Data 暫存

Name(RRRS, 0) // ByteWrite_Basic 專用 POST Code
Name(RRRT, 0) // ByteRead_Basic  專用 POST Code

// ------------------------------------------------------------------------------------
// ******************Method (IWBB) Debug POST Code Example*****************************
// ------------------------------------------------------------------------------------

// *************************
// ShiftLeft (0x01, 0, Local7)
// Or (RRRS, Local7, RRRS)
// *************************

// ------------------------------------------------------------------------------------
// ******************Method (IRBB) Debug POST Code Example*****************************
// ------------------------------------------------------------------------------------

// *************************
// ShiftLeft (0x01, 1, Local7)
// Or (RRRT, Local7, RRRT)
// *************************

// ------------------------------------------------------------------------------------
// ***************************ByteReadI2C**********************************************
// ------------------------------------------------------------------------------------

Method (IRBC, 1)
{
// 1. 呼叫一次 IWBB, 主要功能為將 Offset 寫入 I2C Command

// arg0 = "Offset"
// START = It controls whether a RESTART is issued before the byte is sent or received
// END = It controls whether a STOP is issued after the byte is sent or received.

IWBB (arg0, TRUE, FALSE)

// 2. RDST 是 IWBB 的返回值

Store(RDST, Local0)

if(LNotEqual(Local0, 0x0))
{    
Return(RDST)
}

// 3. 如果抵達此處, 則代表返回值為 0

IRBB(TRUE, TRUE)

// 以下是 I2C READ 的 Workaround-----------------------------
// 因為 Method (IRBB) 應該修好了, 所以不需要再用此 Workaround
// 但為了保險起見還是留著
//-----------------------------------------------------------

Return(RDST)
}

// ------------------------------------------------------------------------------------
// ***************************ByteReadI2C_Basic****************************************
// ------------------------------------------------------------------------------------

Method (IRBB, 2, NotSerialized)
{
// Return Status = SUCCESS

Store(0, RDST)

// 0. 因為 I2CBaseAddress 固定, 所以不需要維護

// 1. I2CInit 應該也不需要做事

// 2. arg0 = START (TRUE/FALSE), arg1 = END (TRUE/FALSE), RDDT 內含 Read Data

//    ReceiveDataEnd = 1 (固定值)
//    ReceiveRequest = Local0 = 初始值為 0, 區間應為 0~1
//    ReadBuffer     = Local1 = 初始值為 0, 區間應為 0~1
//    Counter        = Local2 = 初始值為 0, 區間應為 0~1024
//    檢查 Reg 專用  = Local3 = 初始值為 0
//    檢查 Reg 專用  = Local4 = 初始值為 0

//    while ((ReceiveDataEnd > ReceiveRequest) || (ReceiveDataEnd > ReadBuffer))

Store (0x0, Local0)
Store (0x0, Local1)
Store (0x0, Local2)
Store (0x0, Local3)
Store (0x0, Local4)

While(LOr(LGreater(0x01, Local0), LGreater(0x01, Local1)))
{

// 1. 讀取 R_IC_RAW_INTR_STAT (Local3), RIS7 = R_IC_RAW_INTR_STAT (I2C Raw Interrupt Status)

//    如果 Local1 = R_IC_RAW_INTR_STAT == ERROR, Return with ERROR

//    0x40 = BIT06 = I2C_INTR_TX_ABRT = NACK Set

Store (RIS7, Local3)

If (LNotEqual(And(Local1, 0x40), 0))
{
// Before EXIT, READ R_IC_CLR_TX_ABRT
// CTA7 = R_IC_CLR_TX_ABRT (Clear TX_ABRT interrupt)

Store (CTA7, Local3)

Store(0xFE, RDST)     // 0xFE = R_IC_RAW_INTR_STAT and I2C_INTR_TX_ABRT, ERROR
Return(RDST)
}

// 2. 讀取 R_IC_STATUS, IST7 = R_IC_STATUS (I2C Status) = Local3

//    If R_IC_STATUS == Not Empty, we will READ DATA
//    STAT_RFNE = BIT03 (0x08) = RX FIFO is not empty
//    if FIFO is not empty, 代表可讀資料了
//    DCD7 = R_IC_DATA_CMD (I2C Rx/Tx Data Buffer and Command)

Store (IST7, Local3)

If (LNotEqual(And(Local3, 0x08, Local3), 0))
{

// RDDT = READ DATA

Store (DCD7, Local3)
Store (Local3, RDDT)

// ReadBuffer  = Local1, ReadBuffer ++

Increment(Local1)
}

//  3. ReceiveDataEnd = 1, ReceiveRequest = Local0
// 首次進入, 不會跑入此條件, 因為ReceiveRequest 此時指向 Data
// 第二次進入, 代表 ReceiveRequest 已經加過一次 1, 已經指向 EOF, 我們會到此的唯一可能就是首次進入時 FIFO is empty, 所以 ReadBuffer ++ 沒發生
// 此處之所以需要等待 2028 毫秒, 是因為 FIFO empty 的情況情實很少見    

If (LEqual(0x01, Local0))
{
Stall (2)
Increment(Local2)
If (LLess(Local2, 1024))
{
Continue
}
else
{
Break
}
}

//  4.  If FIFO is full, Wait until a read request will fit
//      STAT_TFNF = BIT1 = TX FIFO = 0x02 is not full, 0 = FULL

Store (IST7, Local3)

If (LEqual(And(Local3, 0x02), 0))
{
Stall (10)
Continue
}

//  5. 這裡是重要的指令分歧點
//     START = TRUE,  END = TRUE,  第一個 if, 在 R_IC_DATA_CMD 直接寫入 READ + RESTART + STOP
//     START = TRUE,  END = FALSE, 第二個 if, 在 R_IC_DATA_CMD 直接寫入 READ + RESTART
//     START = FALSE, END = TRUE,  第一個 if, 在 R_IC_DATA_CMD 直接寫入 READ + STOP
//     START = FALSE, END = FALSE,  第一個 if, 在 R_IC_DATA_CMD 直接寫入 READ (通常沒人用這個)

// (Read Command = B_READ_CMD = BIT08 = 0x0100) 寫入 Local3
 
Store (0x0100, Local3)
 
// arg0 = START, arg1 = END
 
If (LEqual(arg0, TRUE))
{
If (LEqual(arg1, TRUE)) 
{
// Local0|B_CMD_RESTART|B_CMD_STOP, BIT09+BIT10

Or (Local3, 0x0600, Local3)
}
else
{
// Local0|B_CMD_RESTART, BIT10
Or (Local3, 0x0400, Local3)
}
}
else
{
If (LEqual(arg1, TRUE))
{
// Local0|B_CMD_STOP, BIT09
Or (Local3, 0x0200, Local3)
}
else
{
// Local3, Do nothing
}
}
 
// 將 (Read Command + 各種 MASK) 寫入 Command
 
Store(Local3 ,DCD7)

// 6. Wait after send cmd

Stall (2)

// 7. 累加 ReceiveRequest

Increment(Local0)

}

Return(RDST)
}

// ------------------------------------------------------------------------------------
// ***************************ByteWriteI2C*********************************************
// ------------------------------------------------------------------------------------

        // AKA: ByteWriteI2C

Method (IWBC, 2, NotSerialized)
{

// 1. 呼叫一次 IWBB, 主要功能為將 Offset 寫入 I2C Command

// arg0 = "Offset to be Written"
// arg1 = "Data to be Written"

// START = It controls whether a RESTART is issued before the byte is sent or received
// END   = It controls whether a STOP is issued after the byte is sent or received.

IWBB(arg0, TRUE, FALSE)

// 2. RDST 是 IWBB 的返回值

Store(RDST, Local0)

if(LNotEqual(Local0, 0x0))
{  
Return(RDST)
}

// 3. 如果抵達此處, 則代表返回值為 0

IWBB(arg1, FALSE, TRUE)
Return(RDST)
}

// ------------------------------------------------------------------------------------
// ***************************ByteWriteI2C_Basic***************************************
// ------------------------------------------------------------------------------------

// arg0 = "Offset" or "Data to be Written"
// arg1 = It controls whether a RESTART is issued before the byte is sent or received
// arg2 = It controls whether a STOP is issued after the byte is sent or received.

// 0xFE = R_IC_RAW_INTR_STAT and I2C_INTR_TX_ABRT, ERROR
// 0xFD = R_IC_STATUS and STAT_TFNF, ERROR
// 0xFC = EFI_TIMEOUT

Method (IWBB, 3, NotSerialized)
{

  // 0. 初始化 RDST, 做為回傳值-------------------------------------------------------------------------
 
  Store(0xFF, RDST) // Set the status to FAIL

  // 1. 這個是 (I2CInit) -------------------------------------------------------------------------------
 
  //    將 SlaveAddress 寫到 IC_TAR
 
  Store (0x46, TAR7)
 
  // 2. 用 While 重覆跑前段的檢查 ----------------------------------------------------------------------
 
  // Local5 = TransmitEnd
  // Local6 = WriteBuffer
 
  Store (1, Local5)
  Store (0, Local6)
 
  While(LGreater(Local5, Local6))
  {
 
// 1. 讀取 R_IC_STATUS, IST7 = R_IC_STATUS (I2C Status)

Store (IST7, Local0)
 
    // 2. 讀取 R_IC_RAW_INTR_STAT (Local1), RIS7 = R_IC_RAW_INTR_STAT (I2C Raw Interrupt Status)

Store (RIS7, Local1)

// 3. 如果 Local1 = R_IC_RAW_INTR_STAT == ERROR, Return with ERROR

//    0x40 = BIT06 = I2C_INTR_TX_ABRT = NACK Set

If (LNotEqual(And(Local1, 0x40), 0))
{
// Before EXIT, READ R_IC_CLR_TX_ABRT
// CTA7 = R_IC_CLR_TX_ABRT (Clear TX_ABRT interrupt)

Store (CTA7, Local1)

Store(0xFE, RDST)     // 0xFE = R_IC_RAW_INTR_STAT and I2C_INTR_TX_ABRT, ERROR
Return(RDST)
}

// 4. If R_IC_STATUS == ERROR, Delay and Retry
//    STAT_TFNF = BIT1 = 0x02

If (LEqual(And(Local0, 0x02), 0))
{
//FIFO_WRITE_DELAY = 0x02

Stall (0x02)

Store(0xFD, RDST)     // 0xFD = R_IC_STATUS and STAT_TFNF, ERROR
Continue
}

// 這裡是重要的指令分歧點 -------------------------------------------
 
//      前導指令 (Read/Write) START = TRUE,  END = FALSE, 第二個 if, 在 R_IC_DATA_CMD 直接寫入 RESTART
//      後續指令 (Read)       START = TRUE,  END = TRUE,  第一個 if, 在 R_IC_DATA_CMD 直接寫入 RESTART + STOP
//      後續指令 (Write)      START = FALSE, END = TRUE,  第一個 if, 在 R_IC_DATA_CMD 直接寫入 STOP
 
// (Offset or Data) 寫入 Local0
 
Store (arg0, Local0)
 
// arg1 = START, arg2 = END
 
If (LEqual(arg1, TRUE))
{
If (LEqual(arg2, TRUE)) 
{
// Local0|B_CMD_RESTART|B_CMD_STOP, BIT09+BIT10

Or (Local0, 0x0600, Local0)
Increment (Local6)
}
else
{
// Local0|B_CMD_RESTART, BIT10

Or (Local0, 0x0400, Local0)
Increment (Local6)
}
}
else
{
If (LEqual(arg2, TRUE))
{
// Local0|B_CMD_STOP, BIT09
Or (Local0, 0x0200, Local0)
Increment (Local6)
}
else
{
// Local0, Do nothing
Increment (Local6)
}
}
 
// 將 (Offset or Data) 寫入 Command
 
Store(Local0 ,DCD7)

// Delay
 
Stall (2)

// ---------------------------------------------------------

//    Local2 做為 Counter 初始化
 
Store(0,Local2)

While(TRUE)
{  
// 1. 讀取 R_IC_RAW_INTR_STAT (Local1), RIS7 = R_IC_RAW_INTR_STAT (I2C Raw Interrupt Status)

Store (RIS7, Local1)
 
// 2. 如果 Local1 = R_IC_RAW_INTR_STAT == ERROR, Return with ERROR

//    0x40 = BIT06 = I2C_INTR_TX_ABRT = NACK Set

If (LNotEqual(And(Local1, 0x40), 0))
{
// Before EXIT, READ R_IC_CLR_TX_ABRT
// CTA7 = R_IC_CLR_TX_ABRT (Clear TX_ABRT interrupt)

Store (CTA7, Local1)
Store(0xFE, RDST)     // 0xFE = R_IC_RAW_INTR_STAT and I2C_INTR_TX_ABRT, ERROR
}
 
// 3. 如果 Local1 = R_IC_TXFLR == 0, 則 Break
//    TFL7 = R_IC_TXFLR (Transmit FIFO Level Register)

Store (TFL7, Local1)

If (LEqual(Local1, 0x0))
{
Store(0x00, RDST)
Break
}

// 4. Delay

Stall (2)

// 5. Timeout Test

Increment(Local2)

If(LLess(Local2, 1024))
{
Continue
}
else
{
Store(0xFC, RDST)     // 0xFC = EFI_TIMEOUT
Break
}
}
}
 
  // Return SUCCESS
 
  Store(0x00, RDST)
  Return(RDST)
 
}

[Clang] (1) How to Compile EDKII Based BIOS Source using Clang?

How to Compile EDKII Based BIOS Source using Clang (https://en.wikipedia.org/wiki/Clang)?

1. Files to be modified:

1.1. MSFT_DEF.txt = Microsoft Tool Chain Definition

--> This is main Tool Chain file

1.2. MSFT_DEF.txt, GCC_DEF.txt, ... 

MYTOOLS_DEF.txt --->

!include MSFT_DEF.txt
*_MYTOOLS_*_*_FAMILY = MSFT

VS2015_DEF.txt --->

!include MSFT_DEF.txt
*_VS2015_*_*_FAMILY = MSFT
DEBUG_VS2015_IA32_CC_FLAGS = DEF(DEBUG_CC_FLAGS) DEF(COMMON_CC_FLAGS) /arch:IA32
RELEASE_VS2015_IA32_CC_FLAGS = DEF(COMMON_CC_FLAGS) /arch:IA32

Since MYTOOLS_DEF.txt and VS2015_DEF.txt are sharing the usage of MSFT_DEF.txt, we need to list the difference between them. (MyTools as Default)

1.3. The usage of MYTOOLS_DEF.txt is selected in target.txt 

This file is generated in main.mak

target.txt --->

ACTIVE_PLATFORM    =  Build/Platform.dsc

TARGET = RELEASE

TOOL_CHAIN_CONF    = AmiPkg/Configuration/MYTOOLS_DEF.txt

TOOL_CHAIN_TAG = MYTOOLS

MAX_CONCURRENT_THREAD_NUMBER = 8

BUILD_RULE_CONF = AmiPkg/Configuration/build_rule.txt

The make command in main.mak as follows:

# Create Conf/target.txt, based on SDL tokens
Conf/target.txt : $(TOKEN_MAK) $(MAIN_MAK) Conf
@$(ECHO) \
"ACTIVE_PLATFORM    = $(subst \,/, $(ACTIVE_PLATFORM))\
$(EOL)\
$(EOL)TARGET = $(TARGET)\
$(EOL)\
$(EOL)TOOL_CHAIN_CONF    = $(TOOL_DEFINITION_FILE)\
$(EOL)\
$(EOL)TOOL_CHAIN_TAG = $(TOOL_CHAIN_TAG)\
$(EOL)\
$(EOL)MAX_CONCURRENT_THREAD_NUMBER = $(NUMBER_OF_BUILD_PROCESS_THREADS)\
$(EOL)\
$(EOL)BUILD_RULE_CONF = $(BUILD_RULE_FILE)" > Conf/target.txt

-----------------------------------------------------------------

TOKEN
Name  = "TOOL_DEFINITION_FILE"
Value  = "$(CONFIGURATION_DIR)$(TOOL_CHAIN_TAG)_DEF.txt"
Help  = "Name of the tool definition file.\This value goes to to auto-generated Conf/target.txt."
TokenType = Expression
TargetMAK = Yes
End

--------------------------------------------------------------

Conf/target.txt : $(TOKEN_MAK) $(MAIN_MAK) Conf
@$(ECHO) \
"ACTIVE_PLATFORM    = $(subst \,/, $(ACTIVE_PLATFORM))\
$(EOL)\
$(EOL)TARGET = $(TARGET)\
$(EOL)\
$(EOL)TOOL_CHAIN_CONF    = $(TOOL_DEFINITION_FILE)\
$(EOL)\
$(EOL)TOOL_CHAIN_TAG = $(TOOL_CHAIN_TAG)\
$(EOL)\
$(EOL)MAX_CONCURRENT_THREAD_NUMBER = $(NUMBER_OF_BUILD_PROCESS_THREADS)\
$(EOL)\
$(EOL)BUILD_RULE_CONF = $(BUILD_RULE_FILE)" > Conf/target.txt

---------------------------------------------------------------

1.4. Search "Clang Support" for building on Mac OS X (in EDK2_Build_Spec.pdf), where the full sample of Clang_DEF.txt 

1.5. Complete Flow:

1.5.1. Veb Calls Tools (make.exe) -> makefile

1.5.2. makefile gives the control to main.mak

1.5.3. main.mak in charge of including all makefiles came from module.mak

1.5.4. Based on definition of toolchain, main.mak will generate target.txt

1.5.5. main.mak calls EDK2 build.exe

1.5.6. Compile Example of a EDKII "Driver"

Control Tool:   C:\WinDDK\7600.16385.1\bin\x86\amd64\cl.exe
Generated File: v3_lib.obj
Source File:    v3_lib.c

---------------------------------------------------------------

2. target.txt

# Platform.dsc:

ACTIVE_PLATFORM    =  Build/Platform.dsc

TARGET = RELEASE

TOOL_CHAIN_CONF    = AmiPkg/Configuration/MYTOOLS_DEF.txt

TOOL_CHAIN_TAG = MYTOOLS

MAX_CONCURRENT_THREAD_NUMBER = 8

BUILD_RULE_CONF = AmiPkg/Configuration/build_rule.txt

Thursday, February 20, 2014

[x86] (9) PFAT aka. BIOS Guard and its flaw





Back to x86 again everyone, for responsible to curious feedback from 王御多, here's some little explain for PFAT aka. BIOS Guard.



Alright, I know some might feel it's too short for explaining the PFAT, however, it's really necessary to understand the basis of RSA first. If you do familiar with RSA, the only mist of PFAT to you is "Use HW solution to replace SMI method".

Using SMI method to verify and update BIOS package is okay in traditional way since in early days, people aren't so familiar with SMI. However, now days we have various software tools and spec. help us to catch and hijack the SMI.

So here's the PFAT (HW solution), the main idea is extremely simple, doing the verify and update in the hideout, block the interrupt and fetch the package data from HW-specified memory part. See, it's really simple so say so, PFAT is doing the exactly same thing except it's hiding in specified HW place.

Well, it's pretty nice, isn't?

However, someone just forgot a little thing about compatibility with BIOS recovery...what? how they get messed up since BIOS recovery is so conventional? Well, they did forget it. You may seem BIOS recovery as another kind BIOS update package only difference is the package itself is previously stored in BIOS ROM and it runs in PEI phase. Sounds fine...but PFAT module only runs in DXE phase....WTF?! BIOS recovery module could never pass the verify phase!

Anyway, I'm pretty sure it's just a little flaw had been fixed by now, just to share the experience that even the mighty one would make such funny issue.

Please leave me any comments or questions, I'd be thankful and response as soon as possible! Thanks!