1. 程式人生 > >第二篇:欲善其事,先利其器-USB3.0 Kernel debug extension

第二篇:欲善其事,先利其器-USB3.0 Kernel debug extension

從第一份工作,主要從事USB dongle PCTV AVStream/BDA Windows驅動的開發,到現在從事USB3.0 device/xHCI host IP的開發,我的工作內容中,始終離不開一個詞:通用序列介面--USB。

所以,我的第一篇技術博文,也從USB開始談起。

USB3.0 IP的開發包括兩大部分.

第一大部分是遵照<<Universal Serial Bus 3.0 Specification>>協議的USB3.0 device IP: 在開發這一款IP的過程中,作為軟體工程師,我們經歷了以下一些過程: 第一步,當然是對Spec的Cross Training 由於USB3.0較USB2.0/1.1/1.0有了本質的變化,它主要體現地Link Layer,尤其是其中的LTSSM(Link Training and Status State Machine), Physical Layer中的LFPS(Low Frequency Periodic Signaling), Protocol layer的Burst, Bulk Streaming, 以及Power Management,它在前面三個層面的均有協議設計上實實在在的體現. 這些重點與難點,也是IP開發前期花最多時間的,例如,幾位同事花了非常多的時間用來仔細分析USB Analyzer抓下的各種存在可疑點的LFPS. 好多底層軟體工程師的觀念中根深蒂固地認為,軟體工程師,只需要寫出好的軟體,對於軟體工程師去分析USB analyzer, PCIe analyzer抓到的資料,有種不值得一提的態度。 這種想法大錯特錯,底層軟體與應用層軟體,有著本質的區別,如何體現你作為底層軟體工程師的特點,就在於做到這些應用層軟體工程師不能作的事情。 第二步,先將USB device的default control Pipe-EP0的基本功能搞定 EP0承載著USB device的列舉(Enumeration)與配置(Configuration)任務,一個USB device可以沒有(Bulk,ISO,Interrupt)型別的EP,但絕對不能沒有USB control EP. 在這個過程中,就非常需要一個通用的USB device functional driver. 擺在我們面前,有三個方案可以選擇,他們是WinUSB, osrusbfx2,以及usbsamp.經過對三個方案的比較,最終選擇了usbsamp,因為,前面兩者均不支援ISO傳輸,對於後期對ISO傳輸的除錯需要再另外再建立一份驅動程式碼是不能接受的,而且WinUSB不包含原始碼,這就阻礙了我們根據需要修改驅動程式的想法. 在這一時間節點(2011.6),我們的工程師第一次參加了位於美國 Portland的USB-IF的workshop, 第三步:增加對Bulk傳輸的支援 USB3.0目前有幾大應用,第一類就是儲存,第二類是音視訊,至於USB HID,Network目前的USB2.0,1.1,1.0已經夠用. 首先,利用USB device functional driver的Bulk Loopback功能對IP的Bulk In/out功能進行驗證. 另外,軟體工程師需完成一個依託於USB3.0 IP的儲存裝置(Mass storage),其主要工作就是編寫基於ARM的Firmware, 以便使帶有USB3.0 xHCI host的PC系統能夠將裝置列舉為一個USB storage device. 在這一時間節點(2011.9 & 2011.11),我們軟體工程師又參加了兩次USB-IF的官方測試,其中一次是workshop,另外一次是Lab. 第四步:與第三步類似,但增加的是ISO傳輸與Bulk Streaming/UAS的支援 軟體工程師編寫符合USB Audio class,UAS的firmware. 由於有了前三次的經驗總結與教訓,這一次的USB-IF官方測試(2013.5)比較順利. 第二部分,便是遵循<<eXtensible Host Controller Interface for Universal Serial Bus>>協議的
USB3.0 HOST IP. 作為USB subsystem, HOST IP的開發工作負載明顯地大於DEVICE IP.對於軟體工程師來講,主要工作在於探尋IP不符合xHCI spec中的邏輯. 在IP不成熟時期(問題太多導致,根本不能在PC Windows驅動上跑),移植Linux的xHCI驅動到ARM平臺上. 在IP可以執行在Windows 8/8.1的xHCI驅動上時,我們的工作任務就更加明顯地增加了,因為HOST IP與DEVICE IP的測試要求不同,HOST需要與市面上的150個HS DEVICE進步相容性測試,之後最難的就是以HOST IP為測試目標的Golden tree/InterOp test, 後期也對HOST IP進行了WHQL/WHCK(Windows Hardware Certification)測試. 就是在這個Host InterOP/Golden tree Test過程中,在Windows 8/8.1系統出現了一系列的BSOD,需要分析. 我拿到其中一個BSOD DUMP FILE就是我的這篇博文將要展現給大家的對它的分析過程: 使用!analyze -v命令,如下(部分輸出內容省略)

0

: kd>!analyze -v DRIVER_IRQL_NOT_LESS_OR_EQUAL (d1) Arguments: Arg1:ac6e2f68, memory referenced Arg2:00000002, IRQL Arg3:00000000, value 0= read operation,1= write operation Arg4:8ddcbcbf, address which referenced memory DebuggingDetails: ------------------ READ_ADDRESS: ac6e2f68 Special pool CURRENT_IRQL:2 FAULTING_IP
: USBXHCI!TransferRing_TransferEventHandler+403 8ddcbcbf8b00 mov eax,dword ptr [eax]

TRAP_FRAME:810f38c4--(.trap 0xffffffff810f38c4) ErrCode=00000000 eax=ac6e2f68 ebx=00000000 ecx=0f048005 edx=ac6e2f68 esi=00000000 edi=ad625d10 eip=8ddcbcbf esp=810f3938 ebp=810f3990 iopl=0 nv up ei pl zr na pe nc cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00010246 USBXHCI!TransferRing_TransferEventHandler+0x403: 8ddcbcbf8b00 mov eax,dword ptr [eax] ds:0023:ac6e2f68=???????? Resettingdefault scope STACK_TEXT: 810f38a481f9dd9b0000000a ac6e2f68 00000002 nt!KiBugCheck2 810f38a48ddcbcbf0000000a ac6e2f68 00000002 nt!KiTrap0E+0x1b3 810f39908ddc303e ad625d10 000000008b8c6ee8 USBXHCI!TransferRing_TransferEventHandler+0x403 810f39fc8589b49a4e5871a07156318800000000 USBXHCI!Interrupter_WdfEvtInterruptDpc+0x31e ---------

從!analyze -v的輸出,可以看出:

CPU在讀訪問屬於special pool區域的ac6e2f68這一記憶體虛擬地址的時候,產生了Bugcheck, 當時的的IRQL為2:DPC_LEVEL. 通過ac6e2f68=????????的這一串?,初步判斷,系統在DPC_LEVEL訪問了已經被換出(paged out)的分頁記憶體. 使用!pool !pte對這一地址進行確認,也確實如此:

0: kd>!pool ac6e2f68 Pool page ac6e2f68 region isSpecial pool Address ac6e2000 does not belong to any pool ac6e2000:Unable to get contents of special pool block

0: kd>!pte ac6e2f68 VA ac6e2f68 PDE at C0602B18 PTE at C0563710 contains 000000002F2EA863 contains 0008204600000000 pfn 2f2ea---DA--KWEV not valid PageFile:0 Offset:82046 Protect:0

現在的問題就是,為什麼CPU會在DPC_LEVEL去訪問一塊已經被換出的記憶體地址呢?是Windows 8的native xHCI USB3.0 host driver存在問題,還是我們的xHCI host IP存在問題呢? 最後歸結為,這一地址,是誰給出來的,為什麼要去訪問這一地址? 這個時候,我看了一下stack trace,想從中得出一些啟發, USBXHCI!TransferRing_TransferEventHandler+0x403 這個STACK中的FRAME,讓我自然而然地聯想到,這個問題是不是發生在Transfer Event產生之時,DRIVER處理之季? 說到這裡,需要簡單介紹一下,xHCI的工作流程: xHC有三條型別的操作佇列,一條是整個HOST的命令佇列(command ring),一條是每一個EP對應的傳輸佇列(transfer ring),還有一條是整個HOST的事件佇列(event ring). 與這個問題相關的是後面兩條. 軟體作為生產者(這裡就是Windows8 xHCI HOST DRIVER)將需要傳輸的內容以TRB(transfer request block)的形式放入transfer ring. xHC host作為消費者,取出這些TRB,進行相應的資料傳輸操作. 在xHC host執行完相應的操作,無論成功還是失敗,它將作為生產者,向event ring中放入一個TRB, 作為向軟體對一筆傳輸事務的結果報告,軟體在相應中斷產生後的DPC處理中,作為消費者從event ring中取出該TRB,進行相應的處理. 而USBXHCI!TransferRing_TransferEventHandler就是DPC階段所呼叫的event ring/TRB的處理函式. 接下來,如何辦呢? 正好前陣子在微軟Dev Center-Hardware中看到Windbg中對USB3.0除錯的擴充套件功能: 迅速把相關內容過了一篇,之後馬上使用. 第一個命令是使用!usb_tree,顯示了當前系統中的USB Topology結構,但是沒有我需要的event ring的內容. 第二個命令,!xhci_dumpall 非常好,輸入內容中,列出了當前系統,所有xHC下的event ring. 根據PCI: VendorId,找到我們開發的xHCI host IP. 使用DML,直接輸出event ring中的內容: 是不是可以看到,!analyze -v所輸出的一些關鍵數字與event ring中的是相同的? eax=ac6e2f68 <---> 207,209 TRANSFER_EVENT edx=ac6e2f68 <---> 207,209 TRANSFER_EVENT edi=ad625d10 <--->209 TRANSFER_EVENT

0: kd>!usb3kd.xhci_eventring 0x8ea9cff0

Dumping dt _PRIMARY_INTERRUPTER_DATA b8668fc0
-----------------------------------------------------

[0]Interrupter: dt _INTERRUPTER_DATA 0xb1a78f68!rcdrlogdump USBXHCI -a 0x8e126008
------------------------------------------------------------------------------------------------------
DequeueSegment:3DequeueIndex:210TotalEventRingSegments:4TRBsPerSegment:256
CurrentBufferData: VA 0xad625000 LA 0xd7daf000Size4096
EventRingTableBufferData: VA 0xad631000 LA 0xd7dac000Size512

[0] VA 0xad61c000 LA 0xd7dbe000Size4096
[1] VA 0xad61f000 LA 0xd7dbb000Size4096
[2] VA 0xad622000 LA 0xd7db2000Size4096
[3] VA 0xad625000 LA 0xd7daf000Size4096

EventRingTRBs:
[200] TRANSFER_EVENT 0xad625c80CycleBit1SlotId24EndpointID3 ED 1Data0x007f7e80007b0001PacketId123Frame0x7f7e80 CC_SHORT_PACKET
[201] TRANSFER_EVENT 0xad625c90CycleBit1SlotId24EndpointID3 ED 1Data0x007f7e80007c0001PacketId124Frame0x7f7e80 CC_SHORT_PACKET
[202] TRANSFER_EVENT 0xad625ca0CycleBit1SlotId24EndpointID3 ED 1Data0x007f7e80007d0001PacketId125Frame0x7f7e80 CC_SUCCESS
[203] TRANSFER_EVENT 0xad625cb0CycleBit1SlotId24EndpointID3 ED 1Data0x007f7e80007e0001PacketId126Frame0x7f7e80 CC_SHORT_PACKET
[204] TRANSFER_EVENT 0xad625cc0CycleBit1SlotId13EndpointID18 ED 1Data0x007f7e8d00030001PacketId3Frame0x7f7e8d CC_SUCCESS
[205] TRANSFER_EVENT 0xad625cd0CycleBit1SlotId24EndpointID3 ED 1Data0x007f7e80007f0001PacketId127Frame0x7f7e80 CC_SHORT_PACKET
[206] TRANSFER_EVENT 0xad625ce0CycleBit1SlotId13EndpointID4 ED 1Data0x00000000b5310f68BytesTransferred65536 CC_SUCCESS
[207] TRANSFER_EVENT 0xad625cf0CycleBit1SlotId15EndpointID4 ED 1Data0x00000000ac6e2f68BytesTransferred31 CC_SUCCESS
[208] TRANSFER_EVENT 0xad625d00CycleBit1SlotId13EndpointID3 ED 1Data0x007f7e9000000001PacketId0Frame0x7f7e90 CC_SHORT_PACKET
[209] TRANSFER_EVENT 0xad625d10CycleBit1SlotId15EndpointID4 ED 1Data0x00000000ac6e2f68BytesTransferred31 CC_SUCCESS
>>>>[210] TRANSFER_EVENT 0xad625d20CycleBit1SlotId24EndpointID3 ED 1Data0x007f7e9000000001PacketId0Frame0x7f7e90 CC_SUCCESS
[211] TRANSFER_EVENT 0xad625d30CycleBit1SlotId18EndpointID13 ED 1Data0x007f7e8a00060001PacketId6Frame0x7f7e8a CC_SHORT_PACKET
[212] TRANSFER_EVENT 0xad625d40CycleBit1SlotId24EndpointID5 ED 1Data0x007f7e9000000001PacketId0Frame0x7f7e90 CC_SHORT_PACKET


問題就在這裡,為什麼會有兩個內容相同的event tring中的TRB? Why? 應該問xHC hardware IP, 為什麼要將兩個內容相同的TRB放入到event ring? 有些人會問,為什麼不可以? 答案是: 第一:Slot ID為15的usb device是一個USB MASS STORAGE裝置,而且符合BOT協議,31個位元組就是BULK OUT的CBW.有一個31位元組的CBW,就應該有一個13位元組的CSW(但是在另外一個BULK IN EP的ring中),而不是再一次31 位元組的CBW. 第二:通過ED標誌,得出該event TRB是由Transfer ring中event data TRB產生的, Event ring中的TRB會將event data trb中的一個TAG複製過去,往往一個完整的BOT傳輸,他們的三個event data trb中的TAG值是相同的. 如果說,這第二個31位元組不是CBW,而是DATA OUT,倒是可以說通,在這種情況下,確實有可能在EVENT RING當中存在三個相同的EVENT TRB. 那麼假設31是DATA OUT, 但為什麼CBW(31B)+(EVENT DATA TRB ac6e2f68 tag)-->DATA OUT( x BYTES)+(EVENT DATA TRB ac6e2f68 tag )-->CSW(13B) + (EVENT DATA TRB ac6e2f68 tag),當x不為31的時候,不產生這個問題?只有在x為31的時候才產生? 而且,後面的分析你將看到,該CBW所對應的是一個DATA IN,而非OUT,以假設與不符. 第三:通過檢查Slot ID為15,EP4的TRANSFER RING,EVENT RING中的每一個TRB. 前面的EVENT DATA TRB與EVENT TRB是一一對應的,當到了0xac6e2f68存在一對二的時候,(即硬體錯誤產生兩個EVENT TRB)的時候,系統產生了BSOD. 這是論證過程中,最可以證明的一個論點,所以列出詳細步驟: 第一: !xhci_deviceslots 0x8ea9cff0
第二: 通過找到Slot 15, EP4, [4] : dt USBXHCI!_ENDPOINT_DATA 0xb5088f58 dt _ENDPOINT_CONTEXT32 0xad7d3080 ES_RUNNING
    ------------------------------------------------------------------------------------------
        EndpointType_BulkOut Address: 0x2 PacketSize: 1024 Interval: 0
        !ucx_endpoint 0xb6902dc8 !rcdrlogdump USBXHCI -a 0x88399b98
        !xhci_esm 0xb5088f58 
        [0] dt _TRANSFERRING_DATA 0xb5f76f18 Events: 0x0 TransferRingState_Idle
        ------------------------------------------------------------------------------
            WdfQueue: !wdfqueue 0x4a0892e0 (0 waiting)
第三: 0: kd> dt _TRANSFERRING_DATA 0xb5f76f18
USBXHCI!_TRANSFERRING_DATA
   +0x000 ControllerHandle : 0x8eaeaee0 Void
   +0x004 UsbDeviceHandle  : 0xb4f56eb0 Void
   +0x008 EndpointHandle   : 0xb5088f58 Void
   +0x00c StreamId         : 0
   +0x010 WdfQueue         : 0x4a0892e0 WDFQUEUE__
   +0x014 WdfDpc           : 0x4b921080 WDFDPC__
   +0x018 Attributes       : _TRANSFER_RING_ATTRIBUTES
   +0x030 Lock             : 0
   +0x034 Events           : 0 ( TransferRingEvent_None )
   +0x038 State            : 1 ( TransferRingState_Idle )
   +0x03c DispatchingEvents : 0
   +0x040 TrbProcessedWaitCount : 0
   +0x044 TimerSet         : 0 ''
   +0x045 ResourcesAllocated : 0x1 ''
   +0x046 StopRequested    : 0 ''
   +0x047 IsochOverRunUnderRunEventSurelyExpected : 0 ''
   +0x048 WdfControlTimer  : (null) 
   +0x04c WdfStopTimer     : 0x497950b8 WDFTIMER__
   +0x050 MaxRingIndex     : 0x1f
   +0x054 CurrentRingIndex : 0x18
   +0x058 CycleState       : 1
   +0x05c CurrentRing      : 0xada77000 _TRB
   +0x060 CurrentRingBufferData : 0xae958e7c _BUFFER_DATA
   +0x064 PendingStageCount : 0
   +0x068 PendingCountAfterStopEvent : 0
   +0x06c InterrupterTarget : 0
   +0x06e RingDoorbellOnFirstTD : 0 ''
   +0x06f IsochPure        : 0 ''
   +0x070 FrameOfLastIsochTdInScheduleValid : 0 ''
   +0x074 NextTransferStartFrame : 0
   +0x078 FrameOfLastIsochTdInSchedule : 0
   +0x07c NumberOfPacketsPerFrame : 0
   +0x080 TransferRingList : _LIST_ENTRY [ 0xb9d64f0c - 0xb9d64e7c ]
   +0x088 PendingTransferList : _LIST_ENTRY [ 0xb5f76fa0 - 0xb5f76fa0 ]
   +0x090 DoubleBufferDataList : _LIST_ENTRY [ 0xb1bf0f3c - 0xb1bf0f3c ]
   +0x098 CancelledOnQueueList : _LIST_ENTRY [ 0xb5f76fb0 - 0xb5f76fb0 ]
   +0x0a0 CancelledDriverOwnedList : _LIST_ENTRY [ 0xb5f76fb8 - 0xb5f76fb8 ]
   +0x0a8 AsyncCompletionTransferList : _LIST_ENTRY [ 0xb5f76fc0 - 0xb5f76fc0 ]
   +0x0b0 PendingCancelledList : _LIST_ENTRY [ 0xb5f76fc8 - 0xb5f76fc8 ]
   +0x0b8 Counter          : _TRANSFERRING_COUNTER
   +0x0bc PendingTransferData : (null) 
   +0x0c0 CommonBufferCallbackData : _COMMON_BUFFER_CALLBACK_DATA
   +0x0dc ForwardProgressMdl : (null) 
   +0x0e0 CancelDpcQueued  : 0n0
   +0x0e4 RunCancelTransfersAfterSurpriseRemoval : 0n0
第五:列出相應的SLOT 15, EP4的transfer ring中的所有TRBs 0: kd> !xhci_transferring 0xada77000
        [  0] NORMAL       0xda7e5000 CycleBit 1 IOC 0 CH 1 BEI 0 InterrupterTarget 0 TransferLength    31 TDSize  0
        [  1] EVENT_DATA   0xda7e5010 CycleBit 1 IOC 1 CH 0 BEI 0 InterrupterTarget 0 Data 0xac65ef68 TotalBytes 31
        [  2] NORMAL       0xda7e5020 CycleBit 1 IOC 0 CH 1 BEI 0 InterrupterTarget 0 TransferLength    31 TDSize  0
        [  3] EVENT_DATA   0xda7e5030 CycleBit 1 IOC 1 CH 0 BEI 0 InterrupterTarget 0 Data 0xbd594f68 TotalBytes 31
        [  4] NORMAL       0xda7e5040 CycleBit 1 IOC 0 CH 1 BEI 0 InterrupterTarget 0 TransferLength    31 TDSize  0
        [  5] EVENT_DATA   0xda7e5050 CycleBit 1 IOC 1 CH 0 BEI 0 InterrupterTarget 0 Data 0xbd53cf68 TotalBytes 31
        [  6] NORMAL       0xda7e5060 CycleBit 1 IOC 0 CH 1 BEI 0 InterrupterTarget 0 TransferLength    31 TDSize  0
        [  7] EVENT_DATA   0xda7e5070 CycleBit 1 IOC 1 CH 0 BEI 0 InterrupterTarget 0 Data 0xac6a6f68 TotalBytes 31
        [  8] NORMAL       0xda7e5080 CycleBit 1 IOC 0 CH 1 BEI 0 InterrupterTarget 0 TransferLength    31 TDSize  0
        [  9] EVENT_DATA   0xda7e5090 CycleBit 1 IOC 1 CH 0 BEI 0 InterrupterTarget 0 Data 0xac6a4f68 TotalBytes 31
        [ 10] NORMAL       0xda7e50a0 CycleBit 1 IOC 0 CH 1 BEI 0 InterrupterTarget 0 TransferLength    31 TDSize  0
        [ 11] EVENT_DATA   0xda7e50b0 CycleBit 1 IOC 1 CH 0 BEI 0 InterrupterTarget 0 Data 0xbd490f68 TotalBytes 31
        [ 12] NORMAL       0xda7e50c0 CycleBit 1 IOC 0 CH 1 BEI 0 InterrupterTarget 0 TransferLength    31 TDSize  0
        [ 13] EVENT_DATA   0xda7e50d0 CycleBit 1 IOC 1 CH 0 BEI 0 InterrupterTarget 0 Data 0xbd564f68 TotalBytes 31
        [ 14] NORMAL       0xda7e50e0 CycleBit 1 IOC 0 CH 1 BEI 0 InterrupterTarget 0 TransferLength    31 TDSize  0
        [ 15] EVENT_DATA   0xda7e50f0 CycleBit 1 IOC 1 CH 0 BEI 0 InterrupterTarget 0 Data 0xac752f68 TotalBytes 31
        [ 16] NORMAL       0xda7e5100 CycleBit 1 IOC 0 CH 1 BEI 0 InterrupterTarget 0 TransferLength    31 TDSize  0
        [ 17] EVENT_DATA   0xda7e5110 CycleBit 1 IOC 1 CH 0 BEI 0 InterrupterTarget 0 Data 0x8eab6f68 TotalBytes 31
        [ 18] NORMAL       0xda7e5120 CycleBit 1 IOC 0 CH 1 BEI 0 InterrupterTarget 0 TransferLength    31 TDSize  0
        [ 19] EVENT_DATA   0xda7e5130 CycleBit 1 IOC 1 CH 0 BEI 0 InterrupterTarget 0 Data0xbf4e8f68 TotalBytes 31
        [ 20] NORMAL       0xda7e5140 CycleBit 1 IOC 0 CH 1 BEI 0 InterrupterTarget 0 TransferLength    31 TDSize  0
        [ 21] EVENT_DATA   0xda7e5150 CycleBit 1 IOC 1 CH 0 BEI 0 InterrupterTarget 0 Data0xb67a6f68 TotalBytes 31
        [ 22] NORMAL       0xda7e5160 CycleBit 1 IOC 0 CH 1 BEI 0 InterrupterTarget 0 TransferLength    31 TDSize  0
        [ 23] EVENT_DATA   0xda7e5170 CycleBit 1 IOC 1 CH 0 BEI 0 InterrupterTarget 0 Data0xac6e2f68 TotalBytes 31
第六: 回退所有的EVET TRING: 通!xhci_trb+EVENT TRB的地址,每回退一次,地址減去一個TRB的長度(0x10),最後得到: 0: kd> !xhci_trb 0xad625a10
        [  0] TRANSFER_EVENT      0xad625a10 CycleBit 1 SlotId 15 EndpointID  4 ED 1 Data 0x00000000b67a6f68 BytesTransferred       31 CC_SUCCESS
0: kd> !xhci_trb 0xad625810
        [  0] TRANSFER_EVENT      0xad625810 CycleBit 1 SlotId 15 EndpointID  4 ED 1 Data 0x00000000bf4e8f68 BytesTransferred       31 CC_SUCCESS

Transfer ring:

[18] NORMAL       0xda7e5120CycleBit1 IOC 0 CH 1 BEI0InterrupterTarget0TransferLength31TDSize0

[19] EVENT_DATA   0xda7e5130CycleBit1 IOC 1 CH 0 BEI 0InterrupterTarget0Data0xbf4e8f68 TotalBytes31

[20] NORMAL       0xda7e5140CycleBit1 IOC 0 CH 1 BEI0InterrupterTarget0TransferLength31TDSize0

[21] EVENT_DATA   0xda7e5150CycleBit1 IOC 1 CH 0 BEI 0InterrupterTarget0Data0xb67a6f68TotalBytes31

[22] NORMAL       0xda7e5160CycleBit1 IOC 0 CH 1 BEI0InterrupterTarget0TransferLength31TDSize0

[23] EVENT_DATA   0xda7e5170CycleBit1 IOC 1 CH 0 BEI 0InterrupterTarget0Data0xac6e2f68 TotalBytes31

0xda7e5180EmptyTransferRing

0xda7e5180EmptyTransferRing

Event ring: [0]TRANSFER_EVENT 0xad625810CycleBit1SlotId15EndpointID4 ED 1Data0x00000000bf4e8f68BytesTransferred31 CC_SUCCESS [0]TRANSFER_EVENT 0xad625a10CycleBit1SlotId15EndpointID4 ED 1Data0x00000000b67a6f68BytesTransferred31 CC_SUCCESS [207]TRANSFER_EVENT 0xad625cf0CycleBit1SlotId15EndpointID4 ED 1Data0x00000000ac6e2f68BytesTransferred31 CC_SUCCESS [209]TRANSFER_EVENT 0xad625d10CycleBit1SlotId15EndpointID4 ED 1Data0x00000000ac6e2f68BytesTransferred31 CC_SUCCESS

第五,還可以檢查這個出了問題的EVENT TRB所對應的Transfer TRB 中CBW中的欄位,看他是否是帶有DATA OUT傳輸的BOT。

通過剛才打印出來的SLOT 15, EP4的TRANSFER RING中,出問題的那個TRB實體地址:

0: kd> !dc 0xda7e5000 L200
#da7e5000 da7eb800 00000000 0000001f 00000413 ..~.............
#da7e5010 ac65ef68 00000000 00000000 00001c21 h.e.........!...
#da7e5020 da7eb800 00000000 0000001f 00000413 ..~.............
#da7e5030 bd594f68 00000000 00000000 00001c21 hOY.........!...
#da7e5040 da7eb800 00000000 0000001f 00000413 ..~.............
#da7e5050 bd53cf68 00000000 00000000 00001c21 h.S.........!...
#da7e5060 da7eb800 00000000 0000001f 00000413 ..~.............
#da7e5070 ac6a6f68 00000000 00000000 00001c21 hoj.........!...
#da7e5080 da7eb800 00000000 0000001f 00000413 ..~.............
#da7e5090 ac6a4f68 00000000 00000000 00001c21 hOj.........!...
#da7e50a0 da7eb800 00000000 0000001f 00000413 ..~.............
#da7e50b0 bd490f68 00000000 00000000 00001c21 h.I.........!...
#da7e50c0 da7eb800 00000000 0000001f 00000413 ..~.............
#da7e50d0 bd564f68 00000000 00000000 00001c21 hOV.........!...
#da7e50e0 da7eb800 00000000 0000001f 00000413 ..~.............
#da7e50f0 ac752f68 00000000 00000000 00001c21 h/u.........!...
#da7e5100 da7eb800 00000000 0000001f 00000413 ..~.............
#da7e5110 8eab6f68 00000000 00000000 00001c21 ho..........!...
#da7e5120 da7eb800 00000000 0000001f 00000413 ..~.............
#da7e5130 bf4e8f68 00000000 00000000 00001c21 h.N.........!...
#da7e5140 da7eb800 00000000 0000001f 00000413 ..~.............
#da7e5150 b67a6f68 00000000 00000000 00001c21 hoz.........!...
#da7e5160 da7eb800 00000000 0000001f 00000413 ..~.............
#da7e5170 ac6e2f68 00000000 00000000 00001c21 h/n.........!...

#da7e5180 00000000 00000000 00000000 00000000 ................
#da7e5190 00000000 00000000 00000000 00000000 ................

其中:

0: kd> !dc 0xda7e5160 


#da7e5160 da7eb800 00000000 0000001f 00000413 ..~.............
#da7e5170 ac6e2f68 00000000 00000000 00001c21 h/n.........!...

看一下XHCI SPEC關於NORMAL TRANSFER TRB的結構定義:

前8個位元組(0到7)是,該TRB所指向的需要傳輸的資料的實體地址,這處為da7eb800

第8到第11個位元組為:這個TRB所承載的傳輸資料的長度0x1f, 就是31個位元組

得出,這個TRANSFER TRB就是CBW所對應的TRB

而TRB中的第一個32BIT地址,就是CBW 31位元組的實體地址:

0: kd> !dc da7eb800
#da7eb800 43425355 b6870918 00020000 280a0080 USBC...........(
#da7eb810 2fdb0000 00010058 00000000 00000000 .../X...........
#da7eb820 00000000 00000000 00000000 00000000 ................

我們研究一下CBW 31位元組中各欄位的含義:

dCBWSignature: 43425355

dCBWTag:    b6870918

dCBWDataTransferLength: 00020000,需要傳輸資料的長度,
bmCBWFlags:  80 是一個讀操作(from device to host)

bCBWLUN:             00, LUN 0

bCBWCBLength:   (0,1010 b) CBW CBR 的長度,10位元組

CBWCB:                  28 2f db 00 00 00 01 00 58 00

而這十位元組的CBWCB,應該是一個SCSI命令

通過GOOGLE,發現這是一個READ (10 )命令

bit→
↓byte
7 6 5 4 3 2 1 0
0 Operation code = 28h
1 LUN DPO FUA Reserved RelAdr
2–5 LBA
6 Reserved
7–8 Transfer length
9 Control
通過這個命令,確認,這是一個DATA IN,而非DATA OUT,所以,0xac6e2f68

兩次SLOT15,EP4的EVENT TRB就是USB HOST IP產生的錯誤.

經過以上的分析,對該問題的解決提交給RTL code工程師去處理,把問題解決在源頭上. 還Windows xhci driver這個嫌疑物件一個清白. 只是,還是對微軟的xHCI driver如何產生這個EVENT DATA TRB中的TAG,以及後期如何檢查EVENT TRB中的TAG,產生了好奇,事實上,如果微軟對這個TAG的檢查不到位,就發現不了這個問題,我們反而應該感謝微軟幫助我們找到了一個IP設計中存在的一個問題. 後記: 如果沒有WINDBG這個USB3.0 kernel debug extension, 要能分析出這個問題,存在很大的難度,如題所述,欲善其事,先利其器,作為一名軟體工程師,除了寫高質量的程式碼,同時還需要學會使用最新的軟體工作,以達到事半功倍的效果. 在開發USB device IP的過程中,需要使用USB Analyzer,與其相配套的軟體,能很好的將USB BUS上的資料內容以圖形化的形式展現給使用者,以方便進一步的問題分析. 而USB xHCI IP的開發過程,是作為x86PC平臺上的一個PCIe EP裝置,必須使用PCIe analyzer.但是,PCIe analyzer相配套的軟體,是沒有將PCIe BUS上的資料內容以USB包圖形化的形式展現給使用者的,所以分析PCIe trace的過程,前期是一種痛苦的過程,後期工程師熟悉後,是一種麻木的過程,工程師除了分析存在問題的主要任務,還擔當了"人肉譯碼"的角色,是一種資源與時間上的浪費. 作為PCIe analyzer提供商,他們是沒有義務在軟體中加入xHCI的支援功能,因為這個針對性太強.如果需要增加這個功能,只能是xHCI IP開發軟體工程師,取得analyzer提供商的軟體API,在這個基礎上增加這個針對性的功能,從而從"人肉譯碼"的角色中解放出來.

相關推薦

第二USB3.0 Kernel debug extension

從第一份工作,主要從事USB dongle PCTV AVStream/BDA Windows驅動的開發,到現在從事USB3.0 device/xHCI host IP的開發,我的工作內容中,始終離不開一個詞:通用序列介面--USB。 所以,我的第一篇技術博文,也從USB

古語云 --> Eclipse開發環境配置

1、java環境 1.1、安裝jdk 淘淘商城系統使用java7開發,使用jdk1.7.0 _80版本開發,安裝jdk1.7步驟如下圖所示:第一步:在oracle官網下載跟系統相匹配的jdk,如下所示: 第二步:雙擊執行jdk-7u80-windows-x64.exe,點選“下一步”:

——辦公

前言 配套的合適的工具能更好的提高團隊溝通和協作能力、提高工作效率。 工具主要分為辦公軟體、開發工具、管理軟體和開發輔助工具。我們先來聊聊辦公篇。 原則 根據我們以往的經驗,可以初步確定以下兩個原則: 統一的環境、工具和軟體 善用工具 統一的環境、工具和軟體 看起來辦公軟體和產

——開發

前言 在閱讀此篇之前,建議先閱讀辦公篇。 我們先回顧下原則。 原則 和辦公軟體略有區別,原則上我們應該略有改動: 統一的環境、工具和軟體 善用工具 符合自身情況 說明 除非是存在特定的開發環境和要求,開發的環境、工具和軟體

——產品

前言 在閱讀此篇之前,建議先閱讀辦公篇和開發篇。我們先回顧下原則。 原則 和開發篇原則一致 統一的環境、工具和軟體 善用工具 符合自身情況 說明 善其事,關鍵在於搞明白產品經理相關的工作內容,針對工作,合理有效的利用軟體,才能達到事半功倍的效果。 產品經理

部署成功,終於看到了夢寐以求似曾相識的介面哈哈。(工

大坑:關於,用了nginx不需要用這個設定了,因為json是自己拼接的不是標準json //linux設定跨域 本地不需要加跨域的設定 //Vue.http.options.emulateJSO

SecureCRT+VMware? Workstation_學習筆記

英文 參考 技術分享 vmware 技術 fff 1.4 tex img 時間:2017.12.31作者:李強參考:man,info,magedu講義,萬能的internet實驗環境:VMware? Workstation 12 Pro ,Centos 6.9,Centos

單片機開發——01工(Keil軟件安裝破解)

AI 分享圖片 第一篇 相關 inf target 公眾 man img 本文是博主《單片機開發》博客第一篇文章,主要講述51單片機編程軟件Keil uVision4的安裝及破解過程。 1. Keil uVision4安裝包文件 PATH:鏈接:https

單片機開發——02工(Proteus軟件安裝破解)

微信公眾號 漢化 -s width use 部分 deb 時間 相同 在單片機開發工程中,博主經常通過模擬軟件Proteus進行模擬仿真,將編譯生成的“HEX”文件下載在單片機芯片中,然後進行後期的debug工作,當模擬仿真完成之後,進行硬件測試部分。 本文

單片機開發——03工(AD軟件安裝破解)

例如 designer 步驟 安裝過程 備份 發現 ack 3.6 技術分享 在單片機開發中,有了Keil的程序編程,Protues的模擬仿真,那麽問題來了,怎麽去進行電路圖設計以及硬件調試呢?此刻就必須引入本文的Altium Designer (下文簡稱

www. pty csdn err block ioi core 最新 shrink   在我們使用Visual Studio 2017開發Asp.Net Core程序的時候,很多時候我們需要一些非常有用的小工具,借助Visual Studio 強大的插件系統,我們能夠安裝

C++ STM32 程式設計 001 工

        作為一個標準的Windows 程式設計師,使用VisualStudio,已經成為了我的習慣,當看到當前,大多數開發者,都在使用Keil5,這個軟體的時候,我的心態是涼涼的,也許大家的經歷都不同吧。      

——工具使用之Presentation Translator

不管是什麼原因,你有沒有需要翻譯PPT的時候? 幾頁、幾十頁甚至幾百頁? 這個時候你會怎麼做? 複製-貼上到翻譯軟體,谷歌翻譯、有道翻譯,然後再複製-貼上回來? 純文字,這樣做也許是可行的,但是如果格式複雜呢?大量的複製-貼上,枯燥無聊效率低下,費時費力讓人身心疲憊。

-PHP

工欲善其事必先利其器 工作幾年,每次被問到會些什麼,感覺自己能說的亮點很少,但是自己確實又做了不少事情。估計是總結的不夠多,也許這就是平凡的編碼生活意義所在吧。決定寫一些自己的體會,有興趣或者有更好的感想,歡迎留言評論。 不好的地方,也歡迎指正。

想學Python爬蟲不知從何入手?工

如果小夥伴正在學習python,那麼“爬蟲”絕對是你不可忽視的。為什麼,因為容易,你在學python其他的時候,也隨便把爬蟲學了。最主要的是學python爬蟲,能在爬取的時候很開心。在這個社會有資源才是王道。 爬蟲四大工具: NO.1 F12 開發者工具 看原始碼:快速定位元素 分析xpath:1、此

之番茄土豆

番茄工作法聽起來用起來都比較複雜,甚至在朋友的 Kindle 上還看到了專門為番茄工作法寫的書,然後朋友說:“GTD 那書真扯蛋、其實我覺得什麼 GTD 交流會也扯蛋”。番茄土豆能更簡單的完成整個過程,只需根據提示進行即可。 1. 開始一個番茄:認真投入工作 25 分

,必-Python編輯選擇(2)

回車 保存 win window alt 當我 生成 UNC isp 前言:工欲善其事、必先利其器 一款順手的好的編輯器可以讓程序員寫代碼更得心應手,效率也會更高,但是編輯器本身沒有好壞,只有使用者使用起來是否順手而已,這裏簡單給大家介紹幾款常用的可以編輯Python的軟

【iOS與EV3混合機器人編程系列之二】工(準備

style 混合 版權 相同 開發 code 操作系統 圖形 ipa 在上一篇文章中,我們論述了iOS與EV3結合後機器人開發的無限可能。那麽,大家要不要一起來Hacking一把呢?為了能夠完整地完畢我接下來我講的項目。我們須要做下面準備:1、一臺Mac執行MAC OS

python入門

未來將是一個全民程式設計的年代。本頭條號以科普為基礎,旨在向大家介紹如何快速掌握這門程式語言,從而能為自己的工作和生活帶來高效和便利。本期的主題是python入門,希望通過下面三個步驟把大家帶到python的世界中來。   本人對於Python學習建立了一個小小的學習圈子,為各位提

Vmware+gdb調試Linux內核——工

image rod font 介紹 執行 make group 進行 小技巧 今天我最終忍受不了qemu的低速跟不可理喻的各種bug,開始尋找新的調試內核的方法。然後想到了Vmware,那麽成熟的虛擬機怎麽可能調試不了內核。於是嘗試了一番,發現結果很的棒!