//----------------------------------------------------------------- // redlight.s // // This boot-time program illuminates, and then extinguishes, // the red lamp in our Delcom USB Visual Indicator when it is // connected to a port on our Buffalo USB 3.0 eXtensible Host // Controller (xHCI) PCI-express add-in adapter-card, thereby // illustrating the data-structures and the programming steps // needed to detect, initialize, and operate the xHCI device. // // to assemble: $ as redlight.s -o redlight.o // and to link: $ ld redlight.o -T ldscript -o redlight.b // and install: $ dd if=redlight.b of=/dev/sdb4 seek=1 // // NOTE: This code begins execution with CS:IP = 1000:0002. // // programmer: ALLAN CRUSE // date begun: 30 NOV 2010 // completion: 20 DEC 2010 //----------------------------------------------------------------- .equ VENDOR_ID, 0x1033 # Renesas Electronics, Inc. .equ DEVICE_ID, 0x0194 # Buffalo USB 3.0 Adapter .equ CONF_ADDR, 0x0CF8 # PCI Config Address port .equ CONF_DATA, 0x0CFC # PCI Config Data port .equ ARENA, 0x10000 # code's physical address .section .text #------------------------------------------------------------------ .short 0xABCD # application's signature #------------------------------------------------------------------ main: .code16 # x86 real-mode execution mov %sp, %cs:ipltos+0 # preserve loader's SP mov %ss, %cs:ipltos+2 # preserve loader's SS mov %cs, %ax # address program's arena mov %ax, %ds # with DS register mov %ax, %es # also ES register mov %ax, %ss # also SS register lea tos, %sp # establish a local stack call erase_vga_display call read_config_space call show_config_space call enable_4GB_access call map_xhc_registers call do_initialization call enable_xhci_ports call tryto_enable_slot call issue_set_address call show_xhci_reginfo call show_runtime_regs call exhibit_port_regs call show_extcaps_info call show_dev_descript call output_cring_TRBs call output_ering_TRBs call exec_program_demo call output_tring_TRBs call output_ering_TRBs lss %cs:ipltos, %sp # recover loader's SS:SP lret # and exit to the loader #------------------------------------------------------------------ ipltos: .short 0, 0 # holds the loader's SS:SP #------------------------------------------------------------------ progID: .ascii " redlight 12/20/2010 " # to identify binary file #------------------------------------------------------------------ wait_for_keypress: mov $0x00, %ah # get_keyboard_input int $0x16 # invoke BIOS service ret #------------------------------------------------------------------ erase_vga_display: mov $0x0003, %ax # set standard 80x25 text int $0x10 # invoke BIOS service ret #------------------------------------------------------------------ draw_message_text: # # EXPECTS: ES:BP = message-address # CX = message-length # BL = text-attribute # pushal push %ecx push %ebx mov $0x0F, %ah # get_display_page int $0x10 # invoke BIOS service mov $0x03, %ah # get cursor location int $0x10 # invoke BIOS service pop %ecx mov %cl, %bl pop %ecx mov $0x1301, %ax # write_string int $0x10 # invoke BIOS service popal ret #------------------------------------------------------------------ draw_crlf_message: pushal xor %bx, %bx # specify display page mov $0x0E0D, %ax # write_tty_code CR int $0x10 # invoke BIOS service mov $0x0E0A, %ax # write_tty_code LF int $0x10 # invoke BIOS service popal ret #------------------------------------------------------------------ hex: .ascii "0123456789ABCDEF" # array of hex numerals #------------------------------------------------------------------ eax2hex: # converts EAX to a hexadecimal digit-string at DS:EDI pushal mov $8, %ecx # count of nybbles to show nxnyb: rol $4, %eax # next nybble into AL mov %al, %bl # copy nybble into BL and $0xF, %bx # zero-extend into BX mov hex(%bx), %dl # fetch nybble's numeral mov %dl, (%edi) # store numeral to buffer inc %edi # advance buffer index loop nxnyb # again if nybbles remain popal ret #------------------------------------------------------------------ tmp: .ascii "xxxxxxxxxxxxxxxx" # temporary output-buffer #------------------------------------------------------------------ #================================================================== #------------------------------------------------------------------ pci_id: .short VENDOR_ID, DEVICE_ID # for xHCI identification busdev: .int 0x00000000 # PCI bus/device/function config: .space 0x100 # PCI Configuration Space #------------------------------------------------------------------ read_config_space: # loop to locate the xHCI's PCI Configuration Space nxpci: mov $CONF_ADDR, %dx # i/o-port for address mov busdev, %eax # address to access bts $31, %eax # via config port out %eax, %dx # select the register mov $CONF_DATA, %dx # i/o-port for data in %dx, %eax # get register value cmp pci_id, %eax # is it xHCI device? je found # yes, search is done addl $0x00000100, busdev # advance PCI function testl $0x01000000, busdev # out-of-bounds yet? jz nxpci # no, check next space lea msg0, %bp # message address mov len0, %cx # message length mov att0, %bl # text attribute call draw_message_text # display the message lss %cs:ipltos, %sp # recover loader's SS:SP lret # and exit to the loader found: # loop to read and save xHCI's PCI Configuration Space xor %edi, %edi # initialize array index nxdata: mov $CONF_ADDR, %dx # i/o-port for address mov busdev, %eax # config base-address add %edi, %eax # plus array offset bts $31, %eax # access PCI register out %eax, %dx # select the register mov $CONF_DATA, %dx # i/o-port for data in %dx, %eax # get register value mov %eax, config(%edi) # store register value add $4, %edi # advance array index cmp $0x100, %edi # all registers read? jb nxdata # no, read another one ret #------------------------------------------------------------------ msg0: .ascii "\r\n Buffalo USB 3.0 Adapter not present \r\n\n" len0: .short . - msg0 # message length att0: .byte 0x07 # text attribute #------------------------------------------------------------------ show_config_space: # show title for the output to follow lea msg1, %bp # message address mov len1, %cx # message length mov att1, %bl # text attribute call draw_message_text # display message # outer loop to show the 64 register-values xor %esi, %esi # initialize array-index nxline: # format the register-offset as sideline-value mov %esi, %eax # register-offset lea tmp, %edi # buffer-address call eax2hex # convert to hex mov tmp+6, %ax # lowest digit-pair mov %ax, msg2+3 # as sideline value # inner loop to show the next 8 register-values lea buf2, %edi # buffer-address nxitem: mov config(%esi), %eax # fetch register-value call eax2hex # convert to hexadecimal add $9, %edi # advance buffer-address add $4, %esi # advance array-index test $0x1F, %esi # multiple of eight? jnz nxitem # no, format another lea msg2, %bp # message address mov len2, %cx # message length mov att2, %bl # text attribute call draw_message_text # display message cmp $0x100, %esi # all registers shown? jb nxline # no, show eight more ret #------------------------------------------------------------------ msg1: .ascii "\r\n PCI Configuration Space " .ascii "for Buffalo USB 3.0 Host Controller \r\n\n" len1: .short . - msg1 # message length att1: .byte 0x07 # text attribute #------------------------------------------------------------------ msg2: .ascii " 0xxx: " buf2: .ascii "xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx " .ascii "xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx " .ascii "\r\n" len2: .short . - msg2 # message length att2: .byte 0x07 # text attribute #------------------------------------------------------------------ #================================================================== #------------------------------------------------------------------ theGDT: .quad 0, 0x008F92000000FFFF # Global Descriptor Table .equ limGDT, (.-theGDT)-1 # the GDT's segment-limit regGDT: .word limGDT, theGDT, 0x0001 # image for GDTR register #------------------------------------------------------------------ enable_4GB_access: # raise default 64KB segment-limits for FS and GS cli # turn off interrupts mov %cr0, %eax # get machine status bts $0, %eax # set image of PE-bit mov %eax, %cr0 # enter protected mode lgdt %cs:regGDT # setup register GDTR mov $8, %ax # address 4GB data mov %ax, %fs # with FS register mov %ax, %gs # also GS register mov %cr0, %eax # get machine status btr $0, %eax # reset image of PE-bit mov %eax, %cr0 # leave protected mode sti # turn on interrupts # turn on the A20 address-line for 32-bit addressing in $0x92, %al # read system port or $0x02, %al # turn on A20-line out %al, $0x92 # for 4-GB addressing ret #------------------------------------------------------------------ #================================================================== #------------------------------------------------------------------ mmbase: .quad 0 # point to xhci registers opbase: .quad 0 # to operational registers rtbase: .quad 0 # to runtime registers dbbase: .quad 0 # to doorbell registers ecbase: .quad 0 # to extended capabilities #------------------------------------------------------------------ map_xhc_registers : # store the BAR0 entry for access to xHCI registers mov config+0x10, %eax # fetch BAR0 value and $0xFFFFFFF0, %eax # mask lowest nybble mov %eax, mmbase # save as 'mmbase' # setup pointer to 'operational' registers lfs mmbase, %ebx # to capability registers mov %fs:0x00(%ebx), %eax # CAPLENGTH offset and $0xFF, %eax # isolate lowest byte add %ebx, %eax # plus base address mov %eax, opbase # save as 'opbase' # setup pointer to 'runtime' registers lfs mmbase, %ebx # to capability registers mov %fs:0x18(%ebx), %eax # RTSOFF offset add %ebx, %eax # plus base address mov %eax, rtbase # save as 'rtbase' # setup pointer to 'doorbell' registers lfs mmbase, %ebx # to capability registers mov %fs:0x14(%ebx), %eax # DBOFF offset add %ebx, %eax # plus base address mov %eax, dbbase # save as 'dbbase' # setup pointer to 'extended capability' registers lfs mmbase, %ebx # to capability registers mov %fs:0x10(%ebx), %eax # HCCPARAMS register shr $16, %eax # extended caps pointer shl $2, %eax # times bytes-per-dword add %ebx, %eax # plus base address mov %eax, ecbase # save as 'ecbase' # get the maximum number of xHC slots lfs mmbase, %ebx # capability registers mov %fs:0x04(%ebx), %eax # fetch HCSPARAMS1 and $0xFF, %eax # isolate MaxSlots mov %eax, nslots # save as 'nslots' # get the maximum number of xHC interrupters lfs mmbase, %ebx # capability registers mov %fs:0x04(%ebx), %eax # fetch HCSPARAMS1 shr $8, %eax # retrieve bits 18..8 and $0x7FF, %eax # isolate MaxIntrs mov %eax, nintrs # save as 'nports' # get the maximum number of xHC ports lfs mmbase, %ebx # capability registers mov %fs:0x04(%ebx), %eax # fetch HCSPARAMS1 shr $24, %eax # isolate MaxPorts mov %eax, nports # save as 'nports' ret #------------------------------------------------------------------ #================================================================== #------------------------------------------------------------------ n_spds: .int 0 # count of sp descriptoes spdesc: .octa 0, 0, 0, 0 # array of sp descriptors #------------------------------------------------------------------ show_extcaps_info: # look for supported protocol capability descriptors lea spdesc, %edi # sp descriptor-array xor %edx, %edx # initial dword offset nxec: lfs ecbase, %ebx # extended capabilities mov %fs:(%ebx, %edx, 4), %eax # fetch dword cmp $0x02, %al # supported ptotocol? jne spdxx # no, disrecard entry lea (%ebx, %edx, 4), %esi # else do string-copy mov $4, %ecx # of four doublewords cld # do forward copying rep movsl %fs:(%esi), %es:(%edi) # of dwords incl n_spds # increment spd count spdxx: or %ah, %ah # final ext capability? jz ecxx # yes, search is done movzx %ah, %eax # else next cap offset add %eax, %edx # added to current EDX jmp nxec # check next ext cap ecxx: # show the supported protocol descriptors xor %esi, %esi # initial array offset nspd: # format the descriptor interpretation lea spdesc(, %esi, 4), %edx # address descriptor mov 4(%edx), %eax # fetch name-string mov %eax, spdX # store into message mov 0(%edx), %eax # fetch revision info mov $'.', %al # setup decimal point rol $8, %eax # major number into AL or $'0', %al # converted to numeral mov %ax, spdX+4 # store into message rol $4, %eax # minor number into AL or $'0', %al # converted to numeral mov %al, spdX+6 # store into message mov 8(%edx), %eax # compatible port offset or $'0', %al # converted to numeral mov %al, ptsX+0 # store into message movb $'.', ptsX+1 # append first dot movb $'.', ptsX+2 # append second dot mov 8(%edx), %eax # compatible port count add %ah, %al # added to port offset dec %al # adjust 1-based offset or $'0', %al # converted to numeral mov %al, ptsX+3 # store into message # format the descriptor registers lea bufX, %edi # initial buffer offset ndwd: mov spdesc(, %esi, 4), %eax # next descriptor dword call eax2hex # is converted to hex add $9, %edi # advance buffer offset inc %esi # increment dword index test $3, %esi # is multiple of four? jnz ndwd # no, format next dword lea msgX, %bp # message address mov lenX, %cx # message length mov attX, %bl # text attribute call draw_message_text # output message imul $4, n_spds, %eax # array-length in dwords cmp %eax, %esi # end of array reached? jb nspd # no, show another entry ret #------------------------------------------------------------------ msgX: .ascii " " bufX: .ascii "xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx " spdX: .ascii " protocol supported on ports " ptsX: .ascii "xxxx \r\n" lenX: .short . - msgX # message length attX: .byte 0x07 # text attribute #------------------------------------------------------------------ #================================================================== #------------------------------------------------------------------ do_initialization: lfs opbase, %ebx # operational registers # wait until CRN-bit (bit 11) in USBSTS is clear before # modifying any of the operational or runtime registers cnr1: btl $11, %fs:0x04(%ebx) # Controller Not Ready? jc cnr1 # yes, spin until clear # program the MaxSlotsEn field in the CONFIG register movl $2, %fs:0x038(%ebx) # Maximum Device Slots # program the Device Context Base Address Array Pointer lea dcbaa, %eax # offset to our DCBAA add $ARENA, %eax # plus load-address xor %edx, %edx # extend to quadword mov %eax, %fs:0x30(%ebx) # DCBAAP - low mov %edx, %fs:0x34(%ebx) # DCBAAP - high # program the Command Ring Control Register to define # the internal Command Ring Dequeue Pointer register lea cring, %eax # offset to our CR add $ARENA, %eax # plus load-address or $1, %eax # set RCS-bit in CRCR xor %edx, %edx # extend to quadword mov %eax, %fs:0x18(%ebx) # CRCR - low mov %edx, %fs:0x1C(%ebx) # CRCR - high # initialize the Host Controller interrupts (TODO) # Note that, according to Intel's xHCI Specification, # "Interrupts are optional. The xHC may be managed by # polling Event Rings." (footnote in section 4.2) # initialize registers for the default Interrupter lfs rtbase, %ebx # for runtime registers movl $2, %fs:0x20(%ebx) # enable interrupter movl $0, %fs:0x24(%ebx) # disable throttling movl $1, %fs:0x28(%ebx) # ERSTSZ for inter 0 lea ering, %eax # offset to ERING add $ARENA, %eax # plus load-address xor %edx, %edx # extend to quadword mov %eax, %fs:0x38(%ebx) # ERDP - low mov %edx, %fs:0x3C(%ebx) # ERDP - high # NOTE: writing register ERSTBA enables the event ring lea erst, %eax # offset to ERST add $ARENA, %eax # plus load-address xor %edx, %edx # extend to quadword mov %eax, %fs:0x30(%ebx) # ERSTBA - low mov %edx, %fs:0x34(%ebx) # ERSTBA - high # set the Run/Stop bit (bit 0) in the USBCMD register lfs opbase, %ebx # operational registers mov %fs:0x00(%ebx), %eax # fetch USBCMD settings bts $0, %eax # set Run/Stop bit to 1 mov %eax, %fs:0x00(%ebx) # start Host Controller # spin until the HCH bit (bit 0) in USBSTS has cleared hch1: mov %fs:0x04(%ebx), %eax # fetch USBSTS settings mov %eax, %fs:0x04(%ebx) # clear the WC bits bt $0, %eax # HC Halted? jc hch1 # yes, spin till clear ret #------------------------------------------------------------------ #================================================================== #------------------------------------------------------------------ nports: .int 0 # number of xhci ports nslots: .int 0 # number of xhci slots nintrs: .int 0 # number of xhci intrs #------------------------------------------------------------------ enable_xhci_ports: # loop to check that any connected ports are enabled xor %esi, %esi # initial port index nxpen: lfs opbase, %ebx # operational registers imul $16, %esi, %edx # offset to PORTSC mov %fs:0x400(%ebx, %edx), %eax # fetch PORTSC mov %eax, %fs:0x400(%ebx, %edx) # reset WC bits bt $0, %eax # port is connected? jnc pskip # no, skip port reset bt $1, %eax # port is enabled? jc pskip # yes, skip port reset # initiate port reset btsl $4, %fs:0x400(%ebx, %edx) # set PR-bit # spin until PRC==1, PR==0, PED==1, PLS==0 pspin: btl $21, %fs:0x400(%ebx, %edx) # PRC is set? jnc pspin # no, spin btl $4, %fs:0x400(%ebx, %edx) # PR clear? jc pspin # no, spin btl $1, %fs:0x400(%ebx, %edx) # PED is set? jnc pspin # no, spin testl $0x1E0, %fs:0x400(%ebx, %edx) # PLS is zero? jnz pspin # no, spin pskip: inc %esi # increment port index cmp nports, %esi # all ports checked? jb nxpen # no, check next port ret #------------------------------------------------------------------ #================================================================== #------------------------------------------------------------------ cindex: .int 0 # Command Ring index #------------------------------------------------------------------ tryto_enable_slot: # setup 'enable slot' command in the Command Ring lea cring, %ebx # command ring base mov cindex, %edi # element number imul $16, %edi # times element-size movl $0, 0x0(%ebx, %edi) # param - low movl $0, 0x4(%ebx, %edi) # param - high movl $0, 0x8(%ebx, %edi) # status mov $9, %eax shl $10, %eax bts $0, %eax # movl %eax, 0xC(%ebx, %edi) # command incl cindex # advance the ring's index # write to the primary Event Ring's Doorbell register lfs dbbase, %ebx # for doorbell registers movl $0, %fs:0x00(%ebx) # DB Target for 'Command' # spin until MicroFrame Index Register has advanced lfs rtbase, %ebx # for runtime registers mov %fs:0x00(%ebx), %eax # MFINDEX register add $1024, %eax # add 1024 microframes and $0x3FFF, %eax # mask for bits 13..0 spinX: cmp %eax, %fs:0x00(%ebx) # match? jne spinX ret #------------------------------------------------------------------ #================================================================== #------------------------------------------------------------------ output_ering_TRBs: # loop to display the TRBs on the primary Event Ring xor %esi, %esi # initialize index nxelt: # format physical address of the event TRB lea ering, %eax # event ring's offset add $ARENA, %eax # plus arena address add %esi, %eax # plus TRB's offset lea adr3, %edi # address output buffer call eax2hex # convert address to hex # format the four dwords of the event TRB lea buf3, %edi # address output buffer .rept 4 mov ering(%esi), %eax # get next TRB dword call eax2hex # and convert to hex add $9, %edi # advance dest'n index add $4, %esi # advance source index .endr # display the event TRB lea msg3, %bp # message-offset mov len3, %cx # message-length mov att3, %bl # text-attribute call draw_message_text # display the message # was this Event Ring TRB unused? or %eax, %eax # check control-dword jnz nxelt # nonzero? do another ret #------------------------------------------------------------------ msg3: .ascii " ering <0x" adr3: .ascii "xxxxxxxx>: " buf3: .ascii "xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx " .ascii "\r\n" len3: .short . - msg3 # message length att3: .byte 0x03 # text attribute #------------------------------------------------------------------ #================================================================== #------------------------------------------------------------------ output_cring_TRBs: # loop to display the TRBs on the Command Ring xor %esi, %esi # initialize index nxcelt: # format physical address of the command TRB lea cring, %eax # command ring's offset add $ARENA, %eax # plus arena address add %esi, %eax # plus TRB's offset lea adr4, %edi # address output buffer call eax2hex # convert address to hex # format the four dwords of the event TRB lea buf4, %edi # address output buffer .rept 4 mov cring(%esi), %eax # get next TRB dword call eax2hex # and convert to hex add $9, %edi # advance dest'n index add $4, %esi # advance source index .endr # display the command TRB lea msg4, %bp # message-offset mov len4, %cx # message-length mov att4, %bl # text-attribute call draw_message_text # display the message # was this Command Ring TRB unused? or %eax, %eax # check control-dword jnz nxcelt # nonzero? do another ret #------------------------------------------------------------------ msg4: .ascii " cring <0x" adr4: .ascii "xxxxxxxx>: " buf4: .ascii "xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx " .ascii "\r\n" len4: .short . - msg4 # message length att4: .byte 0x06 # text attribute #------------------------------------------------------------------ #================================================================== #------------------------------------------------------------------ output_tring_TRBs: # loop to display the TRBs on the Transfer Ring xor %esi, %esi # initialize index nxtelt: # format physical address of the transfer TRB lea tring, %eax # transfer ring's offset add $ARENA, %eax # plus arena address add %esi, %eax # plus TRB's offset lea adr5, %edi # address output buffer call eax2hex # convert address to hex # format the four dwords of the event TRB lea buf5, %edi # address output buffer .rept 4 mov tring(%esi), %eax # get next TRB dword call eax2hex # and convert to hex add $9, %edi # advance dest'n index add $4, %esi # advance source index .endr # display the transfer TRB lea msg5, %bp # message-offset mov len5, %cx # message-length mov att5, %bl # text-attribute call draw_message_text # display the message # was this Transfer Ring TRB unused? or %eax, %eax # check control-dword jnz nxtelt # nonzero? do another ret #------------------------------------------------------------------ msg5: .ascii " tring <0x" adr5: .ascii "xxxxxxxx>: " buf5: .ascii "xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx " .ascii "\r\n" len5: .short . - msg5 # message length att5: .byte 0x05 # text attribute #------------------------------------------------------------------ #================================================================== #------------------------------------------------------------------ issue_set_address: #--------------------------------------------------------- # Initialize the Input Device Context (see 4.3.3 of Spec) #--------------------------------------------------------- # Initialize the Control Context # --- Drop Context Flags # --- Add Context Flags lea in_ctx, %edi # offset to control context movl $0, 0(%edi) # all 'Drop' flags cleared movl $3, 4(%edi) # set 'Add' flags 0 and 1 # Initalize the Slot Context # --- Root Hub Port Number # --- Number of Context Entries # --- Speed Parameter lea in_ctx+32, %edi # offset to slot context mov $3, %eax # Root Hub Port Number = 3 shl $16, %eax # as bits 23..16 mov %eax, 4(%edi) # in doubleword 1 mov $1, %eax # Context Entries = 1 shl $27, %eax # as bits 31..27 mov %eax, 0(%edi) # in doubleword 0 mov $2, %eax # Speed Parameter = 2 shl $20, %eax # as bits 23..20 or %eax, 0(%edi) # in doubleword 0 # Initialize the Endpoint 0 Context # --- Maximum Packet Size # --- Endpoint Type # --- Error Count # --- Transfer Ring Base Address # --- Dequeue Cycle State lea in_ctx+64, %edi # offset to endpoint 0 context mov $8, %eax # Maximum Packet Size = 8 shl $16, %eax # as bits 31..16 mov %eax, 4(%edi) # in doubleword 1 mov $4, %eax # Endpoint Type =4 (Control) shl $3, %eax # as bits 5..3 or %eax, 4(%edi) # in doubleword 1 mov $3, %eax # Error Count = 3 shl $1, %eax # as bits 2..1 or %eax, 4(%edi) # in doubleword 1 lea tring, %eax # offset to Transfer Ring add $ARENA, %eax # plus program address xor %edx, %edx # extended to quadword or $1, %eax # Dequeue Cycle State = 1 mov %eax, 8(%edi) # Dequeue Pointer - Low mov %edx, 12(%edi) # Dequeue Pointer - High # Enqueue 'Address Device' command in the Command Ring lea cring, %ebx # Command Ring base address imul $16, cindex, %edi # offset to Ring's next TRB incl cindex # advance cring array index lea in_ctx, %eax # offset to Input Dev Context add $ARENA, %eax # plus program address xor %edx, %edx # extended to quadword mov %eax, 0(%ebx, %edi) # Input Context Pointer - Low mov %edx, 4(%ebx, %edi) # Input Context Pointer - High movl $0, 8(%ebx, %edi) # Status - reserved mov $1, %eax # Slot-ID = 1 shl $24, %eax # into bits 31..24 mov %eax, 12(%ebx, %edi) # of doubleword 3 mov $11, %eax # TRB Type = 11 (Address Device) shl $10, %eax # into bits 15..10 or %eax, 12(%ebx, %edi) # of doubleword 3 orl $1, 12(%ebx, %edi) # set Command's Cycle-bit # ring the doorbell lfs dbbase, %ebx # for doorbell registers movl $0, %fs:0x00(%ebx) # ring doorbell 0 ret #------------------------------------------------------------------ #================================================================== #------------------------------------------------------------------ .equ DELCOM_ID, 0xB0800FC5 # USB vendor-product IDs #------------------------------------------------------------------ exec_program_demo: # verify that Visual Indicator device was detected cmpl $DELCOM_ID, desc+8 # device was found? je dviok # yes, continue demo # else report failure to detect the Delcom device lea msg7, %bp # message address mov len7, %cx # message length mov att7, %bl # text attribute call draw_message_text # output message # then abandon this demo and exit to the loader lss %cs:ipltos, %sp # restore loader's SS:SP lret # and transfer to loader dviok: # prompt the user to continue lea msg8, %bp # message address mov len8, %cx # message length mov att8, %bl # text attribute call draw_message_text # output the message call wait_for_keypress # allow user to decide # Here we modify the TRB at 'idle1' in order to continue # execution of the subsequent TRBs in our Transfer Ring movl $0x00002001, idle1+12 # 'No Op' command dword # ring the doorbell for device context #1 lfs dbbase, %ebx # for doorbell registers movl $1, %fs:0x04(%ebx) # target=1 (control-endpt) # prompt the user to continue lea msg9, %bp # message address mov len9, %cx # message length mov att9, %bl # text attribute call draw_message_text # output the message call wait_for_keypress # allow user to decide # Here we modify the TRB at 'idle2' in order to continue # execution of the subsequent TRBs in our Transfer Ring movl $0x00002001, idle2+12 # 'No Op' command dword # ring the doorbell for device context #1 lfs dbbase, %ebx # for doorbell registers movl $1, %fs:0x04(%ebx) # target=1 (control-endpt) call draw_crlf_message # advance to new line ret #------------------------------------------------------------------ msg7: .ascii "\r\n Delcom Visual Indicator not detected \r\n\n" len7: .short . - msg7 # message length att7: .byte 0x0D # text attribute #------------------------------------------------------------------ msg8: .ascii "\r\n Press any key to turn red lamp on ... " len8: .short . - msg8 # message length att8: .byte 0x07 # text attribute #------------------------------------------------------------------ msg9: .ascii "\r\n Press any key to turn red lamp off ... " len9: .short . - msg9 # message length att9: .byte 0x07 # text attribute #------------------------------------------------------------------ #================================================================== #------------------------------------------------------------------ show_xhci_reginfo: lfs opbase, %ebx # operational registers mov %fs:0x00(%ebx), %eax # USBCMD lea usbcmd, %edi call eax2hex mov %fs:0x04(%ebx), %eax # USBSTS lea usbsts, %edi call eax2hex mov %fs:0x18(%ebx), %eax # CRCR - low lea crcr+8, %edi call eax2hex mov %fs:0x1C(%ebx), %eax # CRCR - high lea crcr+0, %edi call eax2hex mov %fs:0x34(%ebx), %eax # DCBAAP - low lea dcbaap+8, %edi call eax2hex mov %fs:0x38(%ebx), %eax # DCBAAP - high lea dcbaap+0, %edi call eax2hex lea msgO, %bp # message address mov lenO, %cx # message length mov attO, %bl # text attribute call draw_message_text # output message ret #------------------------------------------------------------------ msgO: .ascii "\r\n " .ascii "USBCMD=" usbcmd: .ascii "xxxxxxxx " .ascii "USBSTS=" usbsts: .ascii "xxxxxxxx " .ascii "CRCR=" crcr: .ascii "xxxxxxxxxxxxxxxx " .ascii "DCBAAP=" dcbaap: .ascii "xxxxxxxxxxxxxxxx " .ascii "\r\n" lenO: .short . - msgO # message length attO: .byte 0x07 # text attribute #------------------------------------------------------------------ #================================================================== #------------------------------------------------------------------ exhibit_port_regs: xor %esi, %esi # initialize index nxport: mov %esi, %eax # current port index inc %eax # 1-based port-numbering lea tmp, %edi # output buffer address call eax2hex # convert to hexadecimal mov tmp+7, %al # fetch final numeral mov %al, msgP+7 # store into message lfs opbase, %ebx # operational registers imul $16, %esi, %edx # compute register offset mov %fs:0x400(%ebx, %edx), %eax # get PORTSC value lea portsc, %edi # point to message buffer call eax2hex # convert to hexadecimal mov %eax, %edx # PORTSC shr $0, %edx # CCS (bit 0) and $1, %edx or $'0', %dl mov %dl, pconn mov %eax, %edx # PORTSC shr $1, %edx # PED (bit 1) and $1, %edx or $'0', %dl mov %dl, penabl mov %eax, %edx # PORTSC shr $5, %edx # PLS (bits 8..5) and $0xF, %edx mov hex(%edx), %dl mov %dl, pstate mov %eax, %edx # PORTSC shr $9, %edx # PP (bit 9) and $1, %edx or $'0', %dl mov %dl, ppower mov %eax, %edx # PORTSC shr $10, %edx # PORT SPEED (bits 13..10) and $0xF, %edx mov hex(%edx), %dl mov %dl, pspeed lea msgP, %bp # message address mov lenP, %cx # message length mov attP, %bl # text attribute call draw_message_text # output message inc %esi # increment index cmp nports, %esi # all ports displayed? jb nxport # no, show next portsc ret #------------------------------------------------------------------ msgP: .ascii " PORTxSC=" portsc: .ascii "xxxxxxxx " .ascii " speed=" pspeed: .ascii "x " .ascii " powered=" ppower: .ascii "x " .ascii " state=" pstate: .ascii "x " .ascii " enabled=" penabl: .ascii "x " .ascii " connected=" pconn: .ascii "x " .ascii "\r\n" lenP: .short . - msgP # message length attP: .byte 0x07 # text attribute #------------------------------------------------------------------ #================================================================== #------------------------------------------------------------------ show_runtime_regs: # format and display the MicroFrame Index Register lfs rtbase, %ebx # for runtime registers mov %fs:0x00(%ebx), %eax # MFINDEX lea tmp, %edi call eax2hex mov tmp+4, %eax mov %eax, mfindx lea msgR, %bp # message address mov lenR, %cx # message length mov attR, %bl # text attribute call draw_message_text # output message # loop to format and display the enabled Interrupters xor %esi, %esi # initial array index nxir: # check whether this Interrupter is 'enabled' lfs rtbase, %ebx # for runtime registers imul $32, %esi, %edx # offset to element cmpl $0, %fs:0x20(%ebx, %edx) # disabled? je noir # yes, skip # format the Interrupter number mov %esi, %eax # interrupter number lea tmp, %edi # pointer to buffer call eax2hex # convert to hex mov tmp+7, %al # copy final numeral mov %al, inumI # into message field # format the Interrupter registers lfs rtbase, %ebx # for runtime registers imul $32, %esi, %edx # offset to element mov %fs:0x20(%ebx, %edx), %eax # IMAN lea imanI, %edi call eax2hex mov %fs:0x24(%ebx, %edx), %eax # IMOD lea imodI, %edi call eax2hex mov %fs:0x28(%ebx, %edx), %eax # ERSTSZ lea erszI, %edi call eax2hex mov %fs:0x30(%ebx, %edx), %eax # ERSTBA - low lea erbaI+8, %edi call eax2hex mov %fs:0x34(%ebx, %edx), %eax # ERSTBA - high lea erbaI+0, %edi call eax2hex mov %fs:0x38(%ebx, %edx), %eax # ERDP - low lea erdpI+8, %edi call eax2hex mov %fs:0x3C(%ebx, %edx), %eax # ERDP - high lea erdpI+0, %edi call eax2hex lea msgI, %bp # message address mov lenI, %cx # message length mov attI, %bl # text attribute call draw_message_text # output message inc %esi # advance array index cmp nintrs, %esi # last one was shown? jb nxir # no, show another noir: ret #------------------------------------------------------------------ msgR: .ascii " MFINDEX=" mfindx: .ascii "xxxx " .ascii " IMAN IMOD ERSTSZ " .ascii " ERSTBA ERDP " .ascii "\r\n" lenR: .short . - msgR # message length attR: .byte 0x07 # text attribute #------------------------------------------------------------------ msgI: .ascii " intr #" inumI: .ascii "x " imanI: .ascii "xxxxxxxx " imodI: .ascii "xxxxxxxx " erszI: .ascii "xxxxxxxx " erbaI: .ascii "xxxxxxxxxxxxxxxx " erdpI: .ascii "xxxxxxxxxxxxxxxx " .ascii "\r\n" lenI: .short . - msgI # message length attI: .byte 0x07 # text attribute #------------------------------------------------------------------ #================================================================== #------------------------------------------------------------------ desc: .space 18 # for device descriptor #------------------------------------------------------------------ show_dev_descript: lfs opbase, %ebx # operational registers mov %fs:0x04(%ebx), %eax # USBSTS mov %eax, %fs:0x04(%ebx) # clear WC bits lfs rtbase, %ebx # for runtime registers btsl $0, %fs:0x20(%ebx) # clear the IP bit lfs dbbase, %ebx # for doorbell registers movl $1, %fs:0x04(%ebx) # ring doorbell #1 call wait_for_keypress # format device descriptor xor %esi, %esi # initialize word index mov $9, %ecx # number of words to show nxword: mov desc(, %esi, 2), %eax # fetch descriptor word lea tmp, %edi # output buffer address call eax2hex # convert to hexadecimal imul $5, %esi, %edi # destimation's offset mov tmp+4, %eax # copy bottom 4 numerals mov %eax, bufD(%edi) # into the output buffer inc %esi # increment word-index loop nxword # again for other words lea msgD, %bp # message address mov lenD, %cx # message length mov attD, %bl # text attribute call draw_message_text # output message ret #------------------------------------------------------------------ msgD: .ascii "\r\n Device Descriptor: " bufD: .ascii "xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx " .ascii "\r\n\n" lenD: .short . - msgD # message length attD: .byte 0x0E # text attribute #------------------------------------------------------------------ #================================================================== #------------------------------------------------------------------ delcom1: .int 0x00020C65, 0x00000000 # red lamp on delcom2: .int 0x02000C65, 0x00000000 # red lamp off #------------------------------------------------------------------ #================================================================== #------------------------------------------------------------------ .align 16 # insures word-alignment .space 512, 0x55 # space for stack to use tos: # label for top-of-stack #------------------------------------------------------------------ #================================================================== #------------------------------------------------------------------ .align 0x1000 # insure page-contiguity # Device Contexts # 32 entries of 64-bytes dc1: .zero 32 * 64 # maximum size for a DC dc2: .zero 32 * 64 # maximum size for a DC #------------------------------------------------------------------ .align 64 # must be 64-byte aligned dcbaa: # Device Context Base Address Array (quadword entries) .quad 0 # for the reserved entry .quad ARENA + dc1 # for Device Context #1 .quad ARENA + dc2 # for Device Context #2 .quad 0 .zero 256 #------------------------------------------------------------------ .align 64 # must be 64-byte aligned in_ctx: # Input Context (33 64-byte entries) .zero 33 * 64 # input context storage #------------------------------------------------------------------ .align 64 # must be 64-byte aligned erst: # Event Ring Segment Table for the Primary Event Ring .quad ARENA + ering, 32 # EvtRingSegTable 0 #------------------------------------------------------------------ .align 64 # must be 64-byte aligned ering: # Event Ring Segment 0 # cannot span 64K boundary .zero 32 * 16 # 32 entries of 16-bytes #------------------------------------------------------------------ .align 16 # must be 16-byte aligned cring: # Command Ring # cannot span 64K boundary .zero 32 * 16 # 32 entries of 16-bytes #----------------------------------------------------------------- .align 16 tring: # Transfer Ring # 'Get_Device_Descriptor' control-transfer .int 0x01000680, 0x00120000, 0x00000008, 0x00030841 # SETUP .int ARENA+desc, 0x00000000, 0x00000012, 0x00010C05 # DATA .int 0x00000000, 0x00000000, 0x00000000, 0x00001021 # HANDSHAKE # 'Set_Device_Configuration' control-transfer .int 0x00010900, 0x00000000, 0x00000008, 0x00000841 # SETUP .int 0x00000000, 0x00000000, 0x00000000, 0x00011021 # HANDSHAKE idle1: .int 0x00000000, 0x00000000, 0x00000000, 0x00000000 # undefined # 'Set_Interface_Descriptor' control-transfer .int 0x00000921, 0x00080000, 0x00000008, 0x00020841 # SETUP .int ARENA+delcom1, 0x00000000, 0x00000008, 0x00000C05 # DATA .int 0x00000000, 0x00000000, 0x00000000, 0x00011021 # HANDSHAKE idle2: .int 0x00000000, 0x00000000, 0x00000000, 0x00000000 # undefined # 'Set_Interface_Descriptor' control-transfer .int 0x00000921, 0x00080000, 0x00000008, 0x00020841 # SETUP .int ARENA+delcom2, 0x00000000, 0x00000008, 0x00000C05 # DATA .int 0x00000000, 0x00000000, 0x00000000, 0x00011021 # HANDSHAKE .zero 2048 #-----------------------------------------------------------------