-
Notifications
You must be signed in to change notification settings - Fork 115
Expand file tree
/
Copy pathxhci.asm
More file actions
1835 lines (1655 loc) · 77.5 KB
/
xhci.asm
File metadata and controls
1835 lines (1655 loc) · 77.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
; =============================================================================
; BareMetal -- a 64-bit OS written in Assembly for x86-64 systems
; Copyright (C) 2008-2025 Return Infinity -- see LICENSE.TXT
;
; xHCI (USB 3) Driver
; =============================================================================
; -----------------------------------------------------------------------------
; Initialize xHCI controller
; IN: RDX = Packed Bus address (as per syscalls/bus.asm)
xhci_init:
push rsi ; Used in init_usb
push rdx ; RDX should already point to a supported device for os_bus_read/write
; Gather the Base I/O Address of the device
mov al, 0 ; Read BAR0
call os_bus_read_bar
mov [os_xHCI_Base], rax
mov rsi, rax ; RSI holds the base for MMIO
; Set PCI Status/Command values
mov dl, 0x01 ; Read Status/Command
call os_bus_read
bts eax, 10 ; Set Interrupt Disable
bts eax, 2 ; Enable Bus Master
bts eax, 1 ; Enable Memory Space
call os_bus_write ; Write updated Status/Command
; Configure MSI-X (if available)
; TODO - Keep track of used vectors and increment as needed
mov al, 0xA0
call msix_init
jnc xhci_init_msix_msi_done
; Check for MSI in PCI Capabilities
xhci_init_msi_check:
mov dl, 1
call os_bus_read ; Read register 1 for Status/Command
bt eax, 20 ; Check bit 4 of the Status word (31:16)
jnc xhci_init_error ; If if doesn't exist then bail out
mov dl, 13
call os_bus_read ; Read register 13 for the Capabilities Pointer (7:0)
and al, 0xFC ; Clear the bottom two bits as they are reserved
xhci_init_msi_check_cap_next:
shr al, 2 ; Quick divide by 4
mov dl, al
call os_bus_read
cmp al, 0x05
je xhci_init_msi
xhci_init_msi_check_cap_next_offset:
shr eax, 8 ; Shift pointer to AL
cmp al, 0x00 ; End of linked list?
jne xhci_init_msi_check_cap_next ; If not, continue reading
jmp xhci_init_error ; Otherwise bail out
xhci_init_msi:
push rdx
; Enable MSI
; Example MSI Entry (From Intel test system)
; 00869005 <- Cap ID 0x05 (MSI), next ptr 0x90, message control 0x0x0086 (64-bit, MMC 8)
; 00000000 <- Message Address Low
; 00000000 <- Message Address High
; 00000000 <- Message Data (15:0)
; 00000000 <- Mask (only exists if Per-vector masking is enabled)
; 00000000 <- Pending (only exists if Per-vector masking is enabled)
; Message Control - Per-vector masking (8), 64-bit (7), Multiple Message Enable (6:4), Multiple Message Capable (3:1), Enable (0)
; MME/MMC 000b = 1, 001b = 2, 010b = 4, 011b = 8, 100b = 16, 101b = 32
; Todo - Test bit 7, Check Multiple Message Capable, copy to Multiple Message Enable
add dl, 1
mov rax, [os_LocalAPICAddress] ; 0xFEE for bits 31:20, Dest (19:12), RH (3), DM (2)
call os_bus_write ; Store Message Address Low
add dl, 1
shr rax, 32 ; Rotate the high bits to EAX
call os_bus_write ; Store Message Address High
add dl, 1
mov eax, 0x000040A0 ; Trigger Mode (15), Level (14), Delivery Mode (10:8), Vector (7:0)
call os_bus_write ; Store Message Data
sub dl, 3
call os_bus_read ; Get Message Control
bts eax, 21 ; Debug - See MME to 8
bts eax, 20 ; Debug - See MME to 8
bts eax, 16 ; Set Enable
call os_bus_write ; Update Message Control
pop rdx
xhci_init_msix_msi_done:
; Create a gate in the IDT
mov edi, 0xA0
mov rax, xhci_int0
call create_gate ; Create the gate for the Primary Interrupter
mov edi, 0xA1
mov rax, xhci_int1
call create_gate ; Create the gate for Interrupter 1 (Keyboard)
; Mark controller memory as un-cacheable
mov rax, [os_xHCI_Base]
shr rax, 18
and al, 0b11111000 ; Clear the last 3 bits
mov rdi, 0x10000 ; Base of low PDE
add rdi, rax
mov rax, [rdi]
btr rax, 3 ; Clear PWT to disable caching
bts rax, 4 ; Set PCD to disable caching
mov [rdi], rax
; Gather CAPLENGTH, check HCIVERSION, get offsets
mov [xhci_db], rsi ; Copy xHCI Base to DB, this gets incremented later
mov [xhci_rt], rsi ; Copy xHCI Base to RT, this gets incremented later
mov eax, [rsi+xHCI_CAPLENGTH] ; Read 4 bytes starting at CAPLENGTH
mov [xhci_caplen], al ; Save the CAPLENGTH offset
; Check for a valid version number
shr eax, 16 ; 16-bit version is in bits 31:16, shift to 15:0
cmp ax, 0x0100 ; Verify it is at least v1.0
jb xhci_init_error
mov eax, [rsi+xHCI_HCSPARAMS1] ; Gather MaxSlots (bits 7:0) and MaxPort (31:24)
mov byte [xhci_maxslots], al
rol eax, 8
mov byte [xhci_maxport], al
xor eax, eax
mov al, [xhci_caplen]
add rax, rsi ; RAX points to base of Host Controller Operational Registers
mov [xhci_op], rax
mov eax, [rsi+xHCI_HCCPARAMS1] ;
bt eax, 2 ; Context Size (CSZ)
jnc xhci_init_32bytecsz ; If bit is clear then use 32 bytes
mov dword [xhci_csz], 64 ; Otherwise set to 64
xhci_init_32bytecsz:
mov eax, [rsi+xHCI_DBOFF] ; Read the xHCI Doorbell Offset Register
and eax, 0xFFFFFFFC ; Clear bits 1:0
add [xhci_db], rax
mov eax, [rsi+xHCI_RTSOFF] ; Read the xHCI Runtime Register Base Offset Register
and eax, 0xFFFFFFE0 ; Clear bits 4:0
add [xhci_rt], rax
; TODO - Read HCSPARAMS2 to get Event Ring Segment Table Max (bits 7:4)
; QEMU xHCI Extended Capabilities Entries
; 00000000febf0020: 0x02 0x04 0x00 0x02 0x55 0x53 0x42 0x20 <- USB 2
; 00000000febf0028: 0x05 0x04 0x00 0x00 0x00 0x00 0x00 0x00 <- Offset 5, Count 4
; 00000000febf0030: 0x02 0x00 0x00 0x03 0x55 0x53 0x42 0x20 <- USB 3
; 00000000febf0038: 0x01 0x04 0x00 0x00 0x00 0x00 0x00 0x00 <- Offset 1, Count 4
; ; Process xHCI Extended Capabilities Entries (16 bytes each)
; xor ebx, ebx
; mov ebx, [rsi+xHCI_HCCPARAMS1] ; Gather xECP (bits 31:16)
; and ebx, 0xFFFF0000 ; Keep only bits 31:16
; shr ebx, 14 ; Shift right for xECP * 4
;xhci_xecp_read:
; mov eax, [rsi+rbx] ; Load first 4 bytes
; cmp al, 0x01 ; Legacy Entry
; je xhci_xecp_read_legacy
; cmp al, 0x02 ; Supported Protocols
; je xhci_xecp_read_supported_protocol
; jmp xhci_xecp_read_next
;xhci_xecp_read_legacy:
; ; Release BIOS ownership
; ; Set bit 24 to indicate to the BIOS to release ownership
; ; The BIOS should clear bit 16 indicating it has successfully done so
; ; Ownership is released when bit 24 is set *and* bit 16 is clear
; jmp xhci_xecp_read_next
;xhci_xecp_read_supported_protocol:
; ; Parse the supported protocol if needed
;xhci_xecp_read_next:
; mov eax, [rsi+rbx] ; Load first 4 bytes of entry again
; shr eax, 8 ; Shift Next to AL
; and eax, 0x000000FF ; Keep only AL
; jz xhci_xecp_end ; If AL = 0 then we are at the end
; shl eax, 2
; add rbx, rax
; jmp xhci_xecp_read
;xhci_xecp_end:
; Reset the controller
call xhci_reset
; Set flag that xHCI was enabled
or qword [os_SysConfEn], 1 << 5
xhci_init_done:
pop rdx
pop rsi
add rsi, 15
mov byte [rsi], 1 ; Mark driver as installed in Bus Table
sub rsi, 15
ret
xhci_init_error:
jmp $
; -----------------------------------------------------------------------------
; -----------------------------------------------------------------------------
; xhci_reset - Reset xHCI controller
xhci_reset:
push rdi
push rsi
push rcx
push rbx
push rax
; Halt the controller
xhci_reset_halt:
mov rsi, [xhci_op] ; xHCI Operational Registers Base
mov eax, [rsi+xHCI_USBCMD] ; Read current Command Register value
bt eax, 0 ; Check RS (bit 0)
jnc xhci_reset_halt_done ; If the bit was clear, proceed onward
btr eax, 0 ; Clear RS (bit 0)
mov [rsi+xHCI_USBCMD], eax ; Write updated Command Register value
mov rax, 20000 ; Wait 20ms (20000µs)
call b_delay
mov eax, [rsi+xHCI_USBSTS] ; Read Status Register
bt eax, 0 ; Check HCHalted (bit 0) - it should be 1
jnc xhci_reset_error ; Bail out if HCHalted wasn't cleared after 20ms
xhci_reset_halt_done:
; Clear memory controller will be using
mov rdi, os_usb_mem
xor eax, eax
mov ecx, 32768 ; 32768 * 8 = 262144 bytes
rep stosq
; Reset the controller
xhci_reset_reset:
mov eax, [rsi+xHCI_USBCMD] ; Read current Command Register value
bts eax, 1 ; Set HCRST (bit 1)
mov [rsi+xHCI_USBCMD], eax ; Write updated Command Register value
mov rax, 100000 ; Wait 100ms (100000µs)
call b_delay
mov eax, [rsi+xHCI_USBSTS] ; Read Status Register
bt eax, 11 ; Check CNR (bit 11)
jc xhci_reset_error ; Bail out if CNR wasn't cleared after 100ms
mov eax, [rsi+xHCI_USBCMD] ; Read current Command Register value
bt eax, 1 ; Check HCRST (bit 1)
jc xhci_reset_error ; Bail out if HCRST wasn't cleared after 100ms
; Configure the controller
mov rax, os_usb_DCI ; Load the address of the Device Context Index
mov [rsi+xHCI_DCBAPP], rax ; Set the Device Context Base Address Array Pointer Register
mov rax, os_usb_CR ; Load the address of the Command Ring
bts rax, 0 ; Set RCS (bit 0)
mov [rsi+xHCI_CRCR], rax ; Set the Command Ring Control Register
mov eax, [rsi+xHCI_USBSTS] ; Read Status Register
mov [rsi+xHCI_USBSTS], eax ; Write Status Register back
xor eax, eax
mov al, [xhci_maxslots]
mov [rsi+xHCI_CONFIG], eax
mov eax, 0
mov [rsi+xHCI_DNCTRL], eax
; Build entries in the Device Controller Index
; TODO - Build what is needed. QEMU starts with 8
mov rdi, os_usb_DCI
mov rax, os_usb_scratchpad
stosq ; Store the address of the scratchpad
mov ebx, [xhci_csz]
shl ebx, 5
mov rcx, 8
mov rax, os_usb_DC ; Start of the Device Context Entries
xhci_reset_build_DC:
stosq
add rax, rbx ; Add size of Context (1024 or 2048 bytes)
dec rcx
jnz xhci_reset_build_DC
; Build scratchpad entries
mov rdi, os_usb_scratchpad
mov rax, os_usb_scratchpad
mov rcx, 4 ; Create 4 4KiB scratchpad entries
xhci_reset_build_scratchpad:
add rax, 4096
stosq
dec rcx
jnz xhci_reset_build_scratchpad
; Configure Segment Tables
; ┌──────────────────────────────────────┐
; | 31 16 15 6 5 0|
; ├──────────────────────────────┬───────┤
; | Ring Segment Base Address Lo | RsvdZ |
; ├──────────────────────────────┴───────┤
; | Ring Segment Base Address Hi |
; ├──────────────────┬───────────────────┤
; | RsvdZ | Ring Segment Size |
; ├──────────────────┴───────────────────┤
; | RsvdZ |
; └──────────────────────────────────────┘
; Segment table for Interrupter 0
mov rax, os_usb_ERS+0 ; Starting Address of Event Ring Segment
mov rdi, os_usb_ERST+0 ; Starting Address of Event Ring Segment Table
mov [rdi], rax ; Ring Segment Base Address
mov eax, 256 ; 256 * 16 bytes each = 4096 bytes
mov [rdi+8], eax ; Ring Segment Size (bits 15:0)
xor eax, eax
mov [rdi+12], eax
; Segment table for Interrupter 1
mov rax, os_usb_ERS+4096 ; Starting Address of Event Ring Segment
mov rdi, os_usb_ERST+4096 ; Starting Address of Event Ring Segment Table
mov [rdi], rax ; Ring Segment Base Address
mov eax, 256 ; 256 * 16 bytes each = 4096 bytes
mov [rdi+8], eax ; Ring Segment Size (bits 15:0)
xor eax, eax
mov [rdi+12], eax
; Configure Interrupter Event Rings
; Event Ring for Primary Interrupter (Interrupt 0)
mov rdi, [xhci_rt]
add rdi, xHCI_IR_0 ; Interrupt Register 0
mov eax, 2 ; Interrupt Enable (bit 1), Interrupt Pending (bit 0)
mov [rdi+0x00], eax ; Interrupter Management (IMAN)
mov eax, 64
mov [rdi+0x04], eax ; Interrupter Moderation (IMOD)
mov eax, 1 ; ERSTBA points to 1 Segment Table
mov [rdi+0x08], eax ; Event Ring Segment Table Size (ERSTSZ)
add rax, os_usb_ERS
mov [rdi+0x18], rax ; Event Ring Dequeue Pointer (ERDP)
mov rax, os_usb_ERST
mov [rdi+0x10], rax ; Event Ring Segment Table Base Address (ERSTBA)
; Event Ring for Interrupter 1
mov rdi, [xhci_rt]
add rdi, xHCI_IR_1 ; Interrupt Register 1
mov eax, 2 ; Interrupt Enable (bit 1), Interrupt Pending (bit 0)
mov [rdi+0x00], eax ; Interrupter Management (IMAN)
mov eax, 64
mov [rdi+0x04], eax ; Interrupter Moderation (IMOD)
mov eax, 1 ; ERSTBA points to 1 Segment Table
mov [rdi+0x08], eax ; Event Ring Segment Table Size (ERSTSZ)
add rax, os_usb_ERS+4096
mov [rdi+0x18], rax ; Event Ring Dequeue Pointer (ERDP)
mov rax, os_usb_ERST+4096
mov [rdi+0x10], rax ; Event Ring Segment Table Base Address (ERSTBA)
; Start Controller
mov eax, 0x05 ; Set bit 0 (RS) and bit 2 (INTE)
mov [rsi+xHCI_USBCMD], eax
; Verify HCHalted is clear
xhci_reset_check_start:
mov eax, [rsi+xHCI_USBSTS] ; Read Status Register
bt eax, 0 ; Check HCHalted (bit 0) - it should be 0
jc xhci_reset_check_start
xhci_reset_done:
pop rax
pop rbx
pop rcx
pop rsi
pop rdi
ret
xhci_reset_error:
jmp $
; -----------------------------------------------------------------------------
; -----------------------------------------------------------------------------
; xhci_enumerate_devices - Enumerate devices connected to xHCI controller
xhci_enumerate_devices:
push rdi
push rsi
push rdx
push rcx
push rbx
push rax
mov rsi, [xhci_op] ; xHCI Operational Registers Base
; Check the available ports and reset them
xor ecx, ecx ; Slot counter
xor edx, edx ; Max ports
mov dl, byte [xhci_maxport]
xhci_check_next:
mov ebx, 0x400 ; Offset to start of Port Registers
shl ecx, 4 ; Quick multiply by 16
add ebx, ecx ; Add offset to EBX
shr ecx, 4 ; Quick divide by 16
mov eax, [rsi+rbx] ; Read PORTSC
bt eax, 0 ; Current Connect Status
jnc xhci_reset_skip
bts eax, 4 ; Port Reset
mov [rsi+rbx], eax ; Write PORTSC
xhci_reset_skip:
inc ecx
cmp ecx, edx
jne xhci_check_next
; Wait for USB devices to be ready
mov eax, 100000
call b_delay
; At this point the event ring should contain some port status change event entries
; They should appear as follows:
; 0xXX000000 0x00000000 0x01000000 0x00008801
; dword 0 - Port ID number (31:24)
; dword 1 - Reserved
; dword 2 - Completion code (31:24)
; dword 3 - Type 34 (15:10), C (0)
; Check Event ring for xHCI_ETRB_PSC and gather enabled ports
xor ecx, ecx
mov rdi, xhci_portlist
mov rsi, os_usb_ERS
sub rsi, 16
xhci_check_port:
add rsi, 16
mov eax, [rsi+12] ; Load dword 3
shr eax, 10 ; Shift Type to AL
cmp al, 0 ; End of list
je xhci_check_port_end
cmp al, xHCI_ETRB_PSC
je xhci_check_port_store
jmp xhci_check_port
xhci_check_port_store:
inc cl
mov al, [rsi+3]
stosb
jmp xhci_check_port
xhci_check_port_end:
mov byte [xhci_portcount], cl
xhci_search_devices:
; Check that at least 1 port was enabled
cmp byte [xhci_portcount], 0
je xhci_enumerate_devices_end ; If no active ports then bail out
; At this point xhci_portcount contains the number of activated ports
; and xhci_portlist is a list of the port numbers
; Clear Transfer and Event ring (in case this xhci_search_devices is called more than once)
mov rdi, os_usb_TR0
mov ecx, 512
xor eax, eax
rep stosq
; Enable Slot Command TRB
; ┌───────────────────────────────────────────────────────────────────────────────────────────────┐
; |31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00|
; ├───────────────────────────────────────────────────────────────────────────────────────────────┤
; | Zero |
; ├───────────────────────────────────────────────────────────────────────────────────────────────┤
; | Zero |
; ├───────────────────────────────────────────────────────────────────────────────────────────────┤
; | Zero |
; ├───────────────────────────────────────────────┬─────────────────┬──────────────────────────┬──┤
; | Zero | 9 | Reserved Zero |C |
; └───────────────────────────────────────────────┴─────────────────┴──────────────────────────┴──┘
; Ex:
; 0x00000000 0x00000000 0x00000000 0x00002401
; Build a TRB for Enable Slot in the Command Ring
mov rdi, os_usb_CR
add rdi, [xhci_croff]
push rdi ; Save the Address of the Enable Slot command
xor eax, eax
stosd ; Store dword 0
stosd ; Store dword 1
stosd ; Store dword 2
mov al, xHCI_CTRB_ESLOT ; Enable Slot opcode
shl eax, 10 ; Shift opcode to bits 15:10
bts eax, 0 ; Cycle Bit
stosd ; Store dword 3
add qword [xhci_croff], 16
; Ring the Doorbell for the Command Ring
xor eax, eax
xor ecx, ecx
call xhci_ring_doorbell
; Enable Slot Event TRB
; ┌───────────────────────────────────────────────────────────────────────────────────────────────┐
; |31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00|
; ├───────────────────────────────────────────────────────────────────────────────────────────────┤
; | Address of Enable Slot TRB Lo |
; ├───────────────────────────────────────────────────────────────────────────────────────────────┤
; | Address of Enable Slot TRB Hi |
; ├───────────────────────┬───────────────────────────────────────────────────────────────────────┤
; | CompCode | Reserved Zero |
; ├───────────────────────┴───────────────────────┬─────────────────┬──────────────────────────┬──┤
; | Slot ID | Reserved Zero | 33 | Reserved Zero |C |
; └───────────────────────┴───────────────────────┴─────────────────┴──────────────────────────┴──┘
; Ex:
; 0xXXXXXXXX 0xXXXXXXXX 0x01000000 0xXX008401
; Gather result from event ring
pop rbx ; Restore the Address of the Enable Slot command
call xhci_check_command_event ; Check for the event and return result in RAX
; Check CompCode and gather Slot ID from event
ror rax, 24 ; Rotate RAX right by 24 bits to put CompCode in AL
cmp al, 0x01
jne xhci_enumerate_devices_end
ror rax, 32 ; Rotate RAX right by 32 bits to put Slot ID in AL
mov [currentslot], al
; Clear the IDC (Maximum of 2112 bytes)
mov rdi, os_usb_IDC
xor eax, eax
mov ecx, 264 ; 2112 / 8
rep stosq
; Build the Input Context (6.2.5)
; Each section of the Input Context is either 32 or 64-bytes in length (depending on HCPARAMS1.CSZ)
; Entries are as follows:
;
; Input Control Context (6.2.5.1)
; Slot Context
; Endpoint Context 0
; Endpoint Context 1 OUT
; Endpoint Context 1 IN
; ...
; Endpoint Context 15 OUT
; Endpoint Context 15 IN
;
;
; Input Control Context
; ┌───────────────────────────────────────────────────────────────────────────────────────────────┐
; |31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00|
; ├───────────────────────────────────────────────────────────────────────────────────────────────┤
; | Drop Flags |
; ├───────────────────────────────────────────────────────────────────────────────────────────────┤
; | Add Flags |
; ├───────────────────────────────────────────────────────────────────────────────────────────────┤
; | Reserved Zero Padding |
; └───────────────────────────────────────────────────────────────────────────────────────────────┘
;
; Slot Context
; ┌───────────────────────────────────────────────────────────────────────────────────────────────┐
; |31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00|
; ├──────────────┬──┬──┬──┬───────────┬───────────────────────────────────────────────────────────┤
; | Cont Entries |Hu|MT|RZ| Speed | Route String |
; ├──────────────┴──┴──┴──┼───────────┴───────────┬───────────────────────────────────────────────┤
; | Number of Ports | Root Hub Port Number | Max Exit Latency |
; ├───────────────────────┴─────┬───────────┬─────┼───────────────────────┬───────────────────────┤
; | Interrupter Target | RZ | TTT | TT Port Num | TT Hub Slot ID |
; ├──────────────┬──────────────┴───────────┴─────┴───────────────────────┼───────────────────────┤
; | State | Reserved Zero | Device Address |
; ├──────────────┴────────────────────────────────────────────────────────┴───────────────────────┤
; | Reserved Zero Padding |
; └───────────────────────────────────────────────────────────────────────────────────────────────┘
;
; Endpoint Context
; ┌───────────────────────────────────────────────────────────────────────────────────────────────┐
; |31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00|
; ├───────────────────────┬───────────────────────┬──┬──────────────┬─────┬──────────────┬────────┤
; | Max ESIT Payload High | Interval |LS| MaxP Streams |Mult | Reserved |EP State|
; ├───────────────────────┴───────────────────────┼──┴──────────────┴─────┼──┬──┬────────┼─────┬──┤
; | Max Packet Size | Max Burst Size |H |RZ|EP Type |CErr |RZ|
; ├───────────────────────────────────────────────┴───────────────────────┴──┴──┴─────┬──┴─────┴──┤
; | TR Dequeue Pointer Low | Resv |DC|
; ├───────────────────────────────────────────────────────────────────────────────────┴────────┴──┤
; | TR Dequeue Pointer High |
; ├───────────────────────────────────────────────┬───────────────────────────────────────────────┤
; | Max ESIT Payload Low | Average TRB Length |
; ├───────────────────────────────────────────────┴───────────────────────────────────────────────┤
; | Reserved Zero Padding |
; └───────────────────────────────────────────────────────────────────────────────────────────────┘
mov rdi, os_usb_IDC
; Set Input Control Context
; Skip Drop Flags
mov dword [rdi+4], 0x00000003 ; dword 1 - Add Flags - Set A01 and A00 as we want Endpoint Context 0 and Slot Context, respectively
; Skip the rest of Input Control Context as it is already cleared
; Set Slot Context
mov eax, [xhci_csz]
add rdi, rax
; Read port speed from port register
xor eax, eax
mov al, [xhci_portlist]
dec al
shl eax, 4 ; Multiply by 16
add eax, 0x400 ; Add 0x400 for Port Base
add rax, [xhci_op] ; Add op base
mov eax, [rax] ; Get PORTSC
; Todo SHL by 10 and do a proper AND
shr eax, 10 ; Shift Port Speed (13:10) to (3:0)
and eax, 0xF ; Clear upper bits of EAX
shl eax, 20 ; Shift Port Speed (3:0) to (23:20)
bts eax, 27 ; Set bit 27 for 1 Context Entry (31:27)
mov dword [rdi+0], eax ; dword 0 - Context Entries (31:27) to 1, set Speed (23:20)
xor eax, eax
mov al, [xhci_portlist] ; Collect port number
shl eax, 16 ; Shift value to 23:16
mov dword [rdi+4], eax ; dword 1 - Root Hub Port Number (23:16)
; Skip the rest of the Slot Context as it is already cleared
; Set Endpoint Context 0
mov eax, [xhci_csz]
add rdi, rax
; TODO Set interval
; Skip dword 0
mov dword [rdi+4], 0x00080026 ; dword 1 - Max Packet Size (31:16) to 8, EP Type (5:3) to 4 (Control), CErr (2:1) to 3
mov rax, os_usb_TR0 ; Address of Transfer Ring
bts rax, 0 ; DCS
mov qword [rdi+8], rax ; dword 2 & 3
mov dword [rdi+16], 0x00000008 ; dword 4 - Average TRB Length (15:0)
; Skip the rest of Endpoint Context 0 as it is already cleared
; Set Address Command TRB
; ┌───────────────────────────────────────────────────────────────────────────────────────────────┐
; |31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00|
; ├───────────────────────────────────────────────────────────────────────────────────────────────┤
; | Address of Input Context Lo |
; ├───────────────────────────────────────────────────────────────────────────────────────────────┤
; | Address of Input Context Hi |
; ├───────────────────────────────────────────────────────────────────────────────────────────────┤
; | Reserved Zero |
; ├───────────────────────┬───────────────────────┬─────────────────┬──┬───────────────────────┬──┤
; | Slot ID | Reserved Zero | 11 |B | Reserved Zero |C |
; └───────────────────────┴───────────────────────┴─────────────────┴──┴───────────────────────┴──┘
; Ex:
; 0xXXXXXXXX 0xXXXXXXXX 0x00000000 0xXX002E01 (or 0xXX002C01 depending on B)
; Build a TRB for Set Address in the Command Ring
mov rdi, os_usb_CR
add rdi, [xhci_croff]
push rdi
mov rax, os_usb_IDC ; Address of the Input Context
stosq ; dword 0 & 1
xor eax, eax ; Reserved
stosd ; dword 2
mov al, [currentslot]
shl eax, 24 ; Set Slot ID (31:24)
mov al, xHCI_CTRB_ADDRD
shl ax, 10
; bts eax, 9 ; B
bts eax, 0 ; Cycle
stosd ; dword 3
add qword [xhci_croff], 16
; Ring the Doorbell for the Command Ring
xor eax, eax
xor ecx, ecx
call xhci_ring_doorbell
; Set Address Event TRB
; ┌───────────────────────────────────────────────────────────────────────────────────────────────┐
; |31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00|
; ├───────────────────────────────────────────────────────────────────────────────────────────────┤
; | Address of Input Context Lo |
; ├───────────────────────────────────────────────────────────────────────────────────────────────┤
; | Address of Input Context Hi |
; ├───────────────────────┬───────────────────────────────────────────────────────────────────────┤
; | CompCode | Reserved Zero |
; ├───────────────────────┴───────────────────────┬─────────────────┬──────────────────────────┬──┤
; | Slot ID | Reserved Zero | 33 | Reserved Zero |C |
; └───────────────────────┴───────────────────────┴─────────────────┴──────────────────────────┴──┘
; Ex:
; 0xXXXXXXXX 0xXXXXXXXX 0x01000000 0xXX008401
; Gather result from event ring
pop rbx ; Restore the Address of the Set Address command
call xhci_check_command_event
; Check CompCode
ror rax, 24 ; Rotate RAX right by 24 bits to put CompCode in AL
cmp al, 0x01
jne xhci_enumerate_devices_end
; Clear os_usb_data0
mov rdi, os_usb_data0
xor eax, eax
mov ecx, 32 ; 256 bytes
rep stosq
; Add TRBs to Transfer ring
mov rdi, os_usb_TR0
; Request 8 bytes from Device Descriptor to get the length and the Max Packet Size
; Setup Stage
mov eax, 0x01000680 ; 0x01 Device Descriptor
stosd ; dword 0 - wValue (31:16), bRequest (15:8), bmRequestType (7:0)
mov eax, 0x00080000 ; Request 8 bytes
stosd ; dword 1 - wLength (31:16), wIndex (15:0)
mov eax, 0x00000008
stosd ; dword 2 - Interrupter Target (31:22), TRB Transfer Length (16:0)
mov eax, 0x00030841 ; TRT 3, TRB Type 2, IDT, C
stosd ; dword 3 - TRT (17:16), TRB Type (15:10), IDT (6), IOC (5), C (0)
; Data Stage
mov rax, os_usb_data0
stosq ; dword 0 & 1 - Data Buffer (63:0)
mov eax, 0x00000008 ; Request 8 bytes
stosd ; dword 2 - Interrupter Target (31:22), TD Size (21:17), TRB Transfer Length (16:0)
mov eax, 0x00010C01 ; DIR, TRB Type 3, C
stosd ; dword 3 - DIR (16), TRB Type (15:10), IDT (6), IOC (5), CH (4), NS (3), ISP (2), ENT (1), C (0)
; Status Stage
xor eax, eax
stosq ; dword 0 & 1 - Reserved Zero
stosd ; dword 2 - Interrupter Target (31:22)
mov eax, 0x00001013 ; TRB Type 4, CH, ENT, C
stosd ; dword 3 - DIR (16), TRB Type (15:10), IOC (5), CH (4), ENT (1), C (0)
; Event Data
add qword [os_usb_evtoken], 1
mov rax, [os_usb_evtoken]
push rax
stosq ; dword 0 & 1 - Data Buffer (63:0)
xor eax, eax
stosd ; dword 2 - Interrupter Target (31:22)
mov eax, 0x00001E21 ; TRB Type 7, BEI, IOC, C
stosd ; dword 3 - TRB Type (15:10), BEI (9), IOC (5), CH (4), ENT (1), Cycle (0)
; Ring the doorbell for current slot
mov eax, 1 ; EPID 1
xor ecx, ecx
mov cl, [currentslot]
call xhci_ring_doorbell
; Gather result from event ring
pop rbx ; Restore the token value command
call xhci_check_command_event
; Check CompCode
ror rax, 24 ; Rotate RAX right by 24 bits to put CompCode in AL
cmp al, 0x01
jne xhci_enumerate_devices_end
; ; Build a TRB for Set Address in the Command Ring (with B cleared this time)
; push rdi
; mov rdi, os_usb_CR
; add rdi, [xhci_croff]
; push rdi ; Save the Address of the Set Address command
; mov rax, os_usb_IDC ; Address of the Input Context
; stosq ; dword 0 & 1
; xor eax, eax ; Reserved
; stosd ; dword 2
; mov al, [currentslot]
; shl eax, 24 ; Set Slot ID (31:24)
; mov al, xHCI_CTRB_ADDRD
; shl ax, 10
; bts eax, 0 ; Cycle
; stosd ; dword 3
; add qword [xhci_croff], 16
;
; ; Ring the Doorbell for the Command Ring
; xor eax, eax
; xor ecx, ecx
; call xhci_ring_doorbell
;
; ; Gather result from event ring
; pop rbx ; Restore the Address of the Set Address command
; call xhci_check_command_event
; pop rdi
;
; ; Check CompCode
; ror rax, 24 ; Rotate RAX right by 24 bits to put CompCode in AL
; cmp al, 0x01
; jne xhci_enumerate_devices_end
; Check first 8 bytes of Device Descriptor
; Example from QEMU keyboard
;
; 0000: 0x12 0x01 0x00 0x02 0x00 0x00 0x00 0x40
;
; 1) Update Endpoint Context 0 Max Packet Size (to 0x40 in the case above)
push rdi
mov al, [os_usb_data0+7] ; Gather the Max Packet Size
cmp al, 0x08 ; Is it different from the default of 8?
je xhci_skip_update_idc ; If not, skip updating the IDC
mov rdi, os_usb_IDC
mov eax, [xhci_csz]
shl eax, 1
add rdi, rax
mov eax, [rdi+4]
ror eax, 16
mov al, [os_usb_data0+7]
ror eax, 16
mov [rdi+4], eax
; 2) Run Evaluate Context
; Build a TRB for Evaluate Context in the Command Ring
mov rdi, os_usb_CR
add rdi, [xhci_croff]
push rdi
mov rax, os_usb_IDC ; Address of the Input Context
stosq ; dword 0 & 1
xor eax, eax
stosd ; dword 2
mov al, [currentslot]
shl eax, 24 ; Set Slot ID (31:24)
mov al, xHCI_CTRB_EVALC
shl ax, 10
bts eax, 0 ; Cycle
stosd ; dword 3
add qword [xhci_croff], 16
; 0xXXXXXXXX 0xXXXXXXXX 0x0000000 0x01003401
; Ring the Doorbell for the Command Ring
xor eax, eax
xor ecx, ecx
call xhci_ring_doorbell
; Gather result from event ring
pop rbx ; Restore the Address of the Evaluate Context command
call xhci_check_command_event
; Check CompCode
ror rax, 24 ; Rotate RAX right by 24 bits to put CompCode in AL
cmp al, 0x01
jne xhci_enumerate_devices_end
xhci_skip_update_idc:
pop rdi
; Request full data from Device Descriptor
xor ebx, ebx
mov bl, [os_usb_data0] ; BL contains Device Descriptor length
; Setup Stage
mov eax, 0x01000680 ; 0x01 Device Descriptor
stosd ; dword 0 - wValue (31:16), bRequest (15:8), bmRequestType (7:0)
mov eax, ebx ; BL contains length
shl eax, 16
stosd ; dword 1 - wLength (31:16), wIndex (15:0)
mov eax, 0x00000008
stosd ; dword 2 - Interrupter Target (31:22), TRB Transfer Length (16:0)
mov eax, 0x00030841 ; TRT 3, TRB Type 2, IDT, C
stosd ; dword 3 - TRT (17:16), TRB Type (15:10), IDT (6), IOC (5), C (0)
; Data Stage
mov rax, os_usb_data0
stosq ; dword 0 & 1 - Data Buffer (63:0)
mov eax, ebx ; BL contains length
stosd ; dword 2 - Interrupter Target (31:22), TD Size (21:17), TRB Transfer Length (16:0)
mov eax, 0x00010C01 ; DIR, TRB Type 3, C
stosd ; dword 3 - DIR (16), TRB Type (15:10), IDT (6), IOC (5), CH (4), NS (3), ISP (2), ENT (1), C (0)
; Status Stage
xor eax, eax
stosq ; dword 0 & 1 - Reserved Zero
stosd ; dword 2 - Interrupter Target (31:22)
mov eax, 0x00001013 ; TRB Type 4, CH, ENT, C
stosd ; dword 3 - DIR (16), TRB Type (15:10), IOC (5), CH (4), ENT (1), C (0)
; Event Data
add qword [os_usb_evtoken], 1
mov rax, [os_usb_evtoken]
push rax
stosq ; dword 0 & 1 - Data Buffer (63:0)
xor eax, eax
stosd ; dword 2 - Interrupter Target (31:22)
mov eax, 0x00001E21 ; TRB Type 7, BEI, IOC, C
stosd ; dword 3 - TRB Type (15:10), BEI (9), IOC (5), CH (4), ENT (1), Cycle (0)
; Ring the doorbell for current slot
mov eax, 1 ; EPID 1
xor ecx, ecx
mov cl, [currentslot]
call xhci_ring_doorbell
; Gather result from event ring
xor eax, eax
pop rbx ; Restore the token value
call xhci_check_command_event
; Check CompCode
ror rax, 24 ; Rotate RAX right by 24 bits to put CompCode in AL
cmp al, 0x01
jne xhci_enumerate_devices_end
; TODO - Check full Device Descriptor
; Example from QEMU keyboard
;
; 0000: 0x12 0x01 0x00 0x02 0x00 0x00 0x00 0x40
; 0008: 0x27 0x06 0x01 0x00 0x00 0x00 0x01 0x04
; 0010: 0x0B 0x01
;
; Expanded out:
; Length 0x12
; Type 0x01
; Release Num 0x0200
; Device Class 0x00
; Sub Class 0x00
; Protocol 0x00
; Max Packet Size 0x40
; Vendor ID 0x0627
; Product ID 0x0001
; Device Release 0x0000
; Manufacturer 0x01
; Product 0x04
; Serial Number 0x0B
; Configurations 0x01
;
; Gather Vendor ID (offset 8) and Product ID (offset 10)
; Build a table
; Slot / Vendor / Product / Class / Protocol
; Request 9 bytes from Configuration Descriptor
; Setup Stage
mov eax, 0x02000680 ; 0x02 Configuration Descriptor
stosd ; dword 0 - wValue (31:16), bRequest (15:8), bmRequestType (7:0)
mov eax, 0x00090000
stosd ; dword 1 - wLength (31:16), wIndex (15:0)
mov eax, 0x00000008
stosd ; dword 2 - Interrupter Target (31:22), TRB Transfer Length (16:0)
mov eax, 0x00030841 ; TRT 3, TRB Type 2, IDT, C
stosd ; dword 3 - TRT (17:16), TRB Type (15:10), IDT (6), IOC (5), C (0)
; Data Stage
mov rax, os_usb_data0
add rax, 0x20
stosq ; dword 0 & 1 - Data Buffer (63:0)
mov eax, 0x00000009
stosd ; dword 2 - Interrupter Target (31:22), TD Size (21:17), TRB Transfer Length (16:0)
mov eax, 0x00010C01 ; DIR, TRB Type 3, C
stosd ; dword 3 - DIR (16), TRB Type (15:10), IDT (6), IOC (5), CH (4), NS (3), ISP (2), ENT (1), C (0)
; Status Stage
xor eax, eax
stosq ; dword 0 & 1 - Reserved Zero
stosd ; dword 2 - Interrupter Target (31:22)
mov eax, 0x00001013 ; TRB Type 4, CH, ENT, C
stosd ; dword 3 - DIR (16), TRB Type (15:10), IOC (5), CH (4), ENT (1), C (0)
; Event Data
add qword [os_usb_evtoken], 1
mov rax, [os_usb_evtoken]
push rax
stosq ; dword 0 & 1 - Data Buffer (63:0)
xor eax, eax
stosd ; dword 2 - Interrupter Target (31:22)
mov eax, 0x00001E21 ; TRB Type 7, BEI, IOC, C
stosd ; dword 3 - TRB Type (15:10), BEI (9), IOC (5), CH (4), ENT (1), Cycle (0)
; Ring the doorbell for current slot
mov eax, 1 ; EPID 1
xor ecx, ecx
mov cl, [currentslot]
call xhci_ring_doorbell
; Gather result from event ring
xor eax, eax
pop rbx ; Restore the token value
call xhci_check_command_event
; Check CompCode
ror rax, 24 ; Rotate RAX right by 24 bits to put CompCode in AL
cmp al, 0x01
jne xhci_enumerate_devices_end
; Check TotalLength
xor ebx, ebx
mov bx, [os_usb_data0+0x20+2] ; BX contains Configuration Descriptor length
; Request full data from Configuration Descriptor (includes Interface Descriptor (0x04) / HID Descriptor (0x21))
; Setup Stage
mov eax, 0x02000680 ; 0x02 Configuration Descriptor
stosd ; dword 0 - wValue (31:16), bRequest (15:8), bmRequestType (7:0)
mov eax, ebx ; BL contains length
shl eax, 16
stosd ; dword 1 - wLength (31:16), wIndex (15:0)
mov eax, 0x00000008
stosd ; dword 2 - Interrupter Target (31:22), TRB Transfer Length (16:0)
mov eax, 0x00030841 ; TRT 3, TRB Type 2, IDT, C
stosd ; dword 3 - TRT (17:16), TRB Type (15:10), IDT (6), IOC (5), C (0)
; Data Stage
mov rax, os_usb_data0
add rax, 0x20
stosq ; dword 0 & 1 - Data Buffer (63:0)
mov eax, ebx ; BL contains length
stosd ; dword 2 - Interrupter Target (31:22), TD Size (21:17), TRB Transfer Length (16:0)
mov eax, 0x00010C01 ; DIR, TRB Type 3, C
stosd ; dword 3 - DIR (16), TRB Type (15:10), IDT (6), IOC (5), CH (4), NS (3), ISP (2), ENT (1), C (0)
; Status Stage
xor eax, eax
stosq ; dword 0 & 1 - Reserved Zero
stosd ; dword 2 - Interrupter Target (31:22)
mov eax, 0x00001013 ; TRB Type 4, CH, ENT, C
stosd ; dword 3 - DIR (16), TRB Type (15:10), IOC (5), CH (4), ENT (1), C (0)
; Event Data
add qword [os_usb_evtoken], 1
mov rax, [os_usb_evtoken]
push rax
stosq ; dword 0 & 1 - Data Buffer (63:0)
xor eax, eax
stosd ; dword 2 - Interrupter Target (31:22)
mov eax, 0x00001E21 ; TRB Type 7, BEI, IOC, C
stosd ; dword 3 - TRB Type (15:10), BEI (9), IOC (5), CH (4), ENT (1), Cycle (0)
; Ring the doorbell for current slot
mov eax, 1 ; EPID 1
xor ecx, ecx
mov cl, [currentslot]
call xhci_ring_doorbell
; Gather result from event ring
xor eax, eax
pop rbx ; Restore the token value
call xhci_check_command_event
; Check CompCode
ror rax, 24 ; Rotate RAX right by 24 bits to put CompCode in AL
cmp al, 0x01
jne xhci_enumerate_devices_end
; TODO - Check Configuration Descriptor
; Example from QEMU keyboard
;
; 0000: 0x09 0x02 0x22 0x00 0x01 0x01 0x08 0xA0
; 0008: 0x32 0x09 0x04 0x00 0x00 0x01 0x03 0x01