Adjust to buildsystem changes
[libxhcidbg.git] / src / hw-dbc.adb
1 --
2 -- Copyright (C) 2016-2017 secunet Security Networks AG
3 --
4 -- This program is free software; you can redistribute it and/or modify
5 -- it under the terms of the GNU General Public License as published by
6 -- the Free Software Foundation; either version 2 of the License, or
7 -- (at your option) any later version.
8 --
9 -- This program is distributed in the hope that it will be useful,
10 -- but WITHOUT ANY WARRANTY; without even the implied warranty of
11 -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 -- GNU General Public License for more details.
13 --
14
15 with System;
16
17 with HW.Time;
18 with HW.Debug;
19
20 with HW.DbC.Intel_Quirk;
21 with HW.DbC.DMA_Buffers;
22 with HW.DbC.Transfer_Info;
23 with HW.DbC.Transfer_Rings;
24 with HW.DbC.Transfers;
25 with HW.DbC.Contexts;
26 with HW.DbC.Events;
27 with HW.DbC.TRBs;
28
29 package body HW.DbC
30 with
31    SPARK_Mode    => Off,
32    Refined_State => (State => (Connected, Running,
33                                DbC_Run_Deadline, DbC_Poll_Deadline,
34                                DbC_Stat_Deadline, Events.State,
35                                Transfer_Info.State, Transfer_Rings.State),
36                      DMA   => (ERST, DbC_Context, Desc_Strings, Events.DMA,
37                                Transfer_Rings.DMA))
38 is
39
40    Apply_Intel_Quirk : constant Boolean := True;
41    Debug_xCap        : constant Boolean := False;
42
43    Connected         : Boolean := False;
44    Running           : Boolean := False;
45    DbC_Run_Deadline  : Time.T  := Time.T_First;
46    DbC_Poll_Deadline : Time.T  := Time.T_First;
47    DbC_Stat_Deadline : Time.T  := Time.T_First;
48
49    ----------------------------------------------------------------------------
50
51    ERST : Events.ERST_Entry
52    with
53       Address => System'To_Address (DMA_Buffers.Event_Ring_Segment_Table_Base);
54
55    DbC_Context : Contexts.DbC_Context
56    with
57       Address => System'To_Address (DMA_Buffers.DbC_Context_Base);
58
59    ----------------------------------------------------------------------------
60
61    subtype Desc_Str_Range is Natural range 0 .. 14;
62    type Desc_Str is array (Desc_Str_Range) of Word16 with Pack;
63    type String_Descriptor is record
64       bLength           : Byte;
65       bDescriptor_Type  : Byte;
66       wData             : Desc_Str;
67    end record with Pack;
68
69    Null_String_Desc : constant String_Descriptor
70      := (bLength          => 0,
71          bDescriptor_Type => 0,
72          wData            => (others => 0));
73
74    type Desc_Strings_Type is (String0, Manufacturer, Product, Serial_Number);
75    type Desc_Strings_Array is
76       array (Desc_Strings_Type) of String_Descriptor with Pack;
77    Desc_Strings : Desc_Strings_Array
78    with
79       Address => System'To_Address (DMA_Buffers.Descriptor_Strings_Base);
80
81    procedure String_To_Desc (Dst : out String_Descriptor; Src : in String)
82    is
83       use type Byte;
84    begin
85       Dst.bLength := 2 + 2 * Byte'Min (Dst.wData'Length, Src'Length);
86       Dst.bDescriptor_Type := 16#03#;
87       for I in Desc_Str_Range loop
88          if I < Src'Last then
89             Dst.wData (I) := Character'Pos (Src (I + 1));
90          else
91             Dst.wData (I) := 16#0000#;
92          end if;
93       end loop;
94    end String_To_Desc;
95
96    ----------------------------------------------------------------------------
97
98    procedure Find_Next_xCap (Cap_Id : in Word8; Success : out Boolean)
99    is
100       use type Word8;
101       use type Word32;
102       Current_Id : Word8;
103       Temp_Offset : Word32;
104    begin
105       Success := False;
106       if xCap_Regs.Byte_Offset = 0 then
107          Cap_Regs.Read (Temp_Offset, XHCI_Extended_Caps);
108       else
109          xCap_Regs.Read (Temp_Offset, Next_xCap);
110       end if;
111       loop
112          Temp_Offset := Shift_Left (Temp_Offset, 2);
113          pragma Debug (Debug_xCap, Debug.Put_Reg32
114            ("Find_Next_xCap Offset", Temp_Offset));
115          exit when
116             Temp_Offset = 0 or else
117             xCap_Regs.Byte_Offset > MMIO_Size - Natural (Temp_Offset) - 2;
118
119          xCap_Regs.Byte_Offset := xCap_Regs.Byte_Offset + Natural (Temp_Offset);
120
121          xCap_Regs.Read (Current_Id, Capability_ID);
122          Success := Current_Id = Cap_Id;
123          pragma Debug (Debug_xCap, Debug.Put_Reg8
124            ("Find_Next_xCap Cap_Id", Current_Id));
125          exit when Success;
126
127          xCap_Regs.Read (Temp_Offset, Next_xCap);
128       end loop;
129    end Find_Next_xCap;
130
131    ----------------------------------------------------------------------------
132
133    procedure BIOS_Handover (Success : out Boolean)
134    is
135       use type Word8;
136       BIOS_Owned  : Word8;
137       Deadline    : Time.T;
138    begin
139       xCap_Regs.Byte_Offset := 0;
140       Find_Next_xCap (1, Success);
141       if Success then
142          Legacy_Support_Regs.Byte_Offset := xCap_Regs.Byte_Offset;
143          -- See if the BIOS claims ownership
144          Legacy_Support_Regs.Read (BIOS_Owned, HC_BIOS_Owned_Semaphore);
145          if BIOS_Owned = 1 then
146             pragma Debug (Debug.Put_Line ("DbC: BIOS claims ownership."));
147
148             Legacy_Support_Regs.Write (HC_OS_Owned_Semaphore, Word8'(1));
149
150             Deadline := Time.MS_From_Now (5_000);
151             loop
152                Legacy_Support_Regs.Read (BIOS_Owned, HC_BIOS_Owned_Semaphore);
153                exit when BIOS_Owned = 0;
154                declare
155                   Timeout : constant Boolean := Time.Timed_Out (Deadline);
156                begin
157                   Success := not Timeout;
158                end;
159                exit when not Success;
160                pragma Warnings (GNATprove, Off, "statement has no effect");
161                for I in 0 .. 1234 loop
162                   null; -- Busy loop to reduce pressure on HC BIOS Owned
163                         -- Semaphore. It shouldn't generate an SMI but
164                         -- might congest the xHC?
165                end loop;
166                pragma Warnings (GNATprove, On, "statement has no effect");
167             end loop;
168
169             pragma Debug (not Success, Debug.Put_Line
170               ("ERROR: BIOS didn't hand over xHC within 5s."));
171             pragma Debug (Success, Debug.Put_Line
172               ("DbC: BIOS hand-over succeeded."));
173          end if;
174       end if;
175    end BIOS_Handover;
176
177    procedure Reset (Initial_Reset : Boolean := False);
178
179    procedure Init
180    is
181       use type Word8;
182       CNR : Word8;
183       Deadline : Time.T;
184       Success : Boolean;
185       Cap_Length : Word8;
186    begin
187       Cap_Regs.Read (Cap_Length, Capability_Registers_Length);
188       Op_Regs.Byte_Offset := Natural (Cap_Length);
189
190       Op_Regs.Read (CNR, Controller_Not_Ready);
191       Success := CNR = 0;
192
193       if not Success then
194          pragma Debug (Debug.Put_Line ("WARNING: xHCI not ready!"));
195          Deadline := Time.MS_From_Now (1_000);
196          Success := True;
197          loop
198             Op_Regs.Read (CNR, Controller_Not_Ready);
199             exit when CNR = 0;
200             declare
201                Timed_Out : constant Boolean := Time.Timed_Out (Deadline);
202             begin
203                Success := not Timed_Out;
204             end;
205             exit when not Success;
206          end loop;
207          pragma Debug (not Success, Debug.Put_Line
208            ("ERROR: xHC not ready after 1s."));
209       end if;
210
211       if Success then
212          BIOS_Handover (Success);
213       end if;
214
215       if Success then
216          xCap_Regs.Byte_Offset := 0;
217          Find_Next_xCap (10, Success);
218       end if;
219
220       pragma Debug (not Success, Debug.Put_Line
221                     ("ERROR: Couldn't find xHCI debug capability."));
222
223       if Success then
224          Regs.Byte_Offset := xCap_Regs.Byte_Offset;
225
226          ERST := Events.ERST_Entry'
227            (Segment_Base   => DMA_Buffers.Event_Ring_Base,
228             Segment_Size   => TRBs.TRBs_Per_Ring,
229             Reserved_Z     => 0);
230
231          Desc_Strings (String0).bLength            := 16#04#;
232          Desc_Strings (String0).bDescriptor_Type   := 16#03#;
233          Desc_Strings (String0).wData := (0 => 16#0409#, others => 16#0000#);
234          String_To_Desc (Desc_Strings (Manufacturer), "secunet");
235          String_To_Desc (Desc_Strings (Product), "HW.DbC");
236          String_To_Desc (Desc_Strings (Serial_Number), "1");
237
238          Reset (Initial_Reset => True);
239       end if;
240    end Init;
241
242    ----------------------------------------------------------------------------
243
244    procedure Reset (Initial_Reset : Boolean := False)
245    is
246       use type Word8;
247       use type Word16;
248       use type Word64;
249       DCE,
250       MBS : Word8;
251    begin
252       if Regs.Byte_Offset /= 0 then
253          Regs.Write (DbC_Enable, Word8'(0));
254          loop
255             Regs.Read (DCE, DbC_Enable);
256             exit when DCE = 0;
257          end loop;
258
259          Transfers.Reset (Initial_Reset);
260
261          Regs.Write (ERST_Size, Word16'(1));
262          Regs.Write (ERST_Base_Lo, Word32
263            (DMA_Buffers.Event_Ring_Segment_Table_Base mod 16#1_0000_0000#));
264          Regs.Write (ERST_Base_Hi, Word32
265            (DMA_Buffers.Event_Ring_Segment_Table_Base  /  16#1_0000_0000#));
266          Events.Reset_Ring;
267
268          Regs.Write (ER_Dequeue_Ptr_Lo, Word32
269            (DMA_Buffers.Event_Ring_Base mod 16#1_0000_0000#));
270          Regs.Write (ER_Dequeue_Ptr_Hi, Word32
271            (DMA_Buffers.Event_Ring_Base  /  16#1_0000_0000#));
272
273          Regs.Write (Context_Pointer_Lo, Word32
274            (DMA_Buffers.DbC_Context_Base mod 16#1_0000_0000#));
275          Regs.Write (Context_Pointer_Hi, Word32
276            (DMA_Buffers.DbC_Context_Base  /  16#1_0000_0000#));
277
278          Contexts.Clear_DbC_Context (DbC_Context);
279          DbC_Context.DbC_Info :=
280            (String_0_Address              => DMA_Buffers.Descriptor_Strings_Base,
281             Manufacturer_String_Address   => DMA_Buffers.Descriptor_Strings_Base
282                                              + 1 * String_Descriptor'Size / 8,
283             Product_String_Address        => DMA_Buffers.Descriptor_Strings_Base
284                                              + 2 * String_Descriptor'Size / 8,
285             Serial_Number_String_Address  => DMA_Buffers.Descriptor_Strings_Base
286                                              + 3 * String_Descriptor'Size / 8,
287             String_0_Length               => Desc_Strings (String0).bLength,
288             Manufacturer_String_Length    => Desc_Strings (Manufacturer).bLength,
289             Product_String_Length         => Desc_Strings (Product).bLength,
290             Serial_Number_String_Length   => Desc_Strings (Serial_Number).bLength,
291             Reserved_Z                    => 0,
292             others                        => 0);
293
294          Regs.Read (MBS, Debug_Max_Burst_Size);
295          DbC_Context.OUT_EP.EP_Type                := Contexts.Bulk_O;
296          DbC_Context.OUT_EP.Max_Burst_Size         := MBS;
297          DbC_Context.OUT_EP.Max_Packet_Size        := 1024;
298          DbC_Context.OUT_EP.TR_Dequeue_Pointer_Lo  := Word28
299            (Shift_Right (Transfer_Rings.Physical (2),  4) and 16#0fff_ffff#);
300          DbC_Context.OUT_EP.TR_Dequeue_Pointer_Hi  := Word32
301            (Shift_Right (Transfer_Rings.Physical (2), 32) and 16#ffff_ffff#);
302          DbC_Context.OUT_EP.Dequeue_Cycle_State    := 1;
303          DbC_Context.OUT_EP.Average_TRB_Length     := Max_Bulk_Size / 2;
304          DbC_Context.IN_EP.EP_Type                 := Contexts.Bulk_I;
305          DbC_Context.IN_EP.Max_Burst_Size          := MBS;
306          DbC_Context.IN_EP.Max_Packet_Size         := 1024;
307          DbC_Context.IN_EP.TR_Dequeue_Pointer_Lo   := Word28
308            (Shift_Right (Transfer_Rings.Physical (3),  4) and 16#0fff_ffff#);
309          DbC_Context.IN_EP.TR_Dequeue_Pointer_Hi   := Word32
310            (Shift_Right (Transfer_Rings.Physical (3), 32) and 16#ffff_ffff#);
311          DbC_Context.IN_EP.Dequeue_Cycle_State     := 1;
312          DbC_Context.IN_EP.Average_TRB_Length      := Max_Bulk_Size / 2;
313
314          Regs.Write (DbC_Protocol, Word16'(0));  -- Debug Target vendor defined.
315          Regs.Write (Vendor_ID, Word16 (16#ffff#));
316          Regs.Write (Product_ID, Word16 (16#dbc1#));
317          Regs.Write (Device_Revision, Word16 (16#0001#));
318
319          Regs.Write (DbC_Enable, Word8'(1));
320          loop
321             Regs.Read (DCE, DbC_Enable);
322             exit when DCE = 1;
323          end loop;
324
325          if Apply_Intel_Quirk then
326             Intel_Quirk.Reset_Port;
327          end if;
328
329          Running := False;
330          Connected := False;
331          DbC_Poll_Deadline := Time.Now;
332          DbC_Stat_Deadline := Time.MS_From_Now (12_345);
333       end if;
334    end Reset;
335
336    procedure Poll (Now : Boolean := False)
337    is
338       use type Word8;
339
340       Temp8 : Word8;
341       Timed_Out : Boolean;
342    begin
343       if Regs.Byte_Offset /= 0 then
344          Timed_Out := Time.Timed_Out (DbC_Poll_Deadline);
345          if Now or else Timed_Out then
346             Regs.Read (Temp8, DbC_Enable);
347             if Temp8 = 1 then
348                Regs.Read (Temp8, Current_Connect_Status);
349                if Temp8 = 1 then
350                   -- Something is connected...
351                   DbC_Poll_Deadline := Time.MS_From_Now (10);
352                   if not Connected then
353                      pragma Debug (Debug.Put_Line ("DbC connected."));
354                      DbC_Run_Deadline := Time.MS_From_Now (333);
355                      Connected := True;
356                   end if;
357                   Regs.Read (Temp8, DbC_Run);
358                   if Temp8 = 1 then
359                      -- ...configured too
360                      if not Running then
361                         pragma Debug (Debug.Put_Line ("DbC configured."));
362                         Transfers.Start;
363                         Running := True;
364                      end if;
365                   elsif Running then
366                      pragma Debug (Debug.Put_Line
367                        ("DbC still connected but deconfigured."));
368                      DbC_Run_Deadline := Time.MS_From_Now (333);
369                      Running := False;
370                   else
371                      Timed_Out := Time.Timed_Out (DbC_Run_Deadline);
372                      if Timed_Out then
373                         pragma Debug (Debug.Put_Line
374                           ("DbC connection timed out."));
375                         Reset;
376                      end if;
377                   end if;
378                else
379                   -- Nothing connected
380                   DbC_Poll_Deadline := Time.MS_From_Now (333);
381                   if Connected then
382                      pragma Debug (Debug.Put_Line ("DbC disconnected."));
383                      Connected := False;
384                      Running := False;
385                   end if;
386                end if;
387             else
388                pragma Debug (Debug.Put_Line ("DbC got disabled, huh?"));
389                Reset;
390             end if;
391             Events.Handle_Events;
392             Timed_Out := Time.Timed_Out (DbC_Stat_Deadline);
393             if Timed_Out then
394                pragma Debug (Transfer_Info.Dump_Stats);
395                DbC_Stat_Deadline := Time.MS_From_Now (12_345);
396             end if;
397          end if;
398       end if;
399    end Poll;
400
401    procedure Receive (Buf : in out Buffer; Len : in out Natural)
402    is
403    begin
404       Poll (Now => True);
405
406       Transfers.Receive (Buf, Len);
407    end Receive;
408
409    procedure Send (Buf : Buffer; Len : in out Natural; Success : out Boolean)
410    is
411    begin
412       Poll (Now => True);
413
414       Transfers.Send
415         (Buf         => Buf,
416          Len         => Len,
417          Start_Now   => Running,
418          Success     => Success);
419    end Send;
420
421    procedure Ring_Doorbell (EP : Endpoint_Range)
422    is
423       use type Word8;
424    begin
425       Regs.Write (Doorbell_Target, Word8 (EP) - 2);
426    end Ring_Doorbell;
427
428    -------------------------------------------------------------------------
429
430 begin
431    for Str of Desc_Strings loop
432       Str := Null_String_Desc;
433    end loop;
434 end HW.DbC;
435
436 --  vim: set ts=8 sts=3 sw=3 et: