//----------------------------------------------------------------- // xhciboot.s // // The purpose of this program is to show us what is going on // with our USB 3.0 eXtensible Host Controller at 'boot time' // before any operating system's device-drivers get launched. // // to assemble: $ as xhciboot.s -o xhciboot.o // and to link: $ ld xhciboot.o -T ldscript -o xhciboot.b // and install: $ dd if=xhciboot.b of=/dev/sda4 seek=1 // // NOTE: This code begins executing with CS:IP = 1000:0002. // // programmer: ALLAN CRUSE // written on: 06 OCT 2010 //----------------------------------------------------------------- .equ VENDOR_ID, 0x1033 # Renesas Electronics, Inc. .equ DEVICE_ID, 0x0194 # USB 3.0 Host Controller .equ CONF_ADDR, 0x0CF8 # PCI Config address-port .equ CONF_DATA, 0x0CFC # PCI Config data-port .equ ARENA, 0x00010000 # code's physical address .section .text #------------------------------------------------------------------ .short 0xABCD # application's signature #------------------------------------------------------------------ main: .code16 # x86 real-mode execution mov %sp, %cs:ipltos+0 # save loader's SP-value mov %ss, %cs:ipltos+2 # also loader's SS-value mov %cs, %ax # address program arena mov %ax, %ds # with DS register mov %ax, %es # also ES register mov %ax, %ss # also SS register lea tos, %sp # establish local stack call erase_vga_display call read_config_space call show_config_space call enable_4GB_access call init_reg_counters call init_reg_pointers call show_capabilities call show_operationals call display_port_regs call show_runtime_regs call wait_for_keypress call standard_textmode lss %cs:ipltos, %sp # recover loader's stack lret # transfer back to loader #------------------------------------------------------------------ ipltos: .short 0, 0 # holds loader's SP and SS #------------------------------------------------------------------ #------------------------------------------------------------------ progID: .ascii "xhciboot.s - 10/06/2010 " #------------------------------------------------------------------ #================================================================== #------------------------------------------------------------------ wait_for_keypress: mov $0x00, %ah # get_keyboard_input int $0x16 # invoke BIOS service ret #------------------------------------------------------------------ erase_vga_display: mov $0x4F02, %ax # VESA Set_Mode mov $0x0105, %bx # 1024x768 256-colors int $0x10 # invoke BIOS service ret #------------------------------------------------------------------ standard_textmode: mov $0x0003, %ax # set 80x25 text display int $0x10 # invoke BIOS service ret #------------------------------------------------------------------ draw_message_text: pushal push %ecx push %ebx mov $0x0F, %ah # get display page int $0x10 # invoke BIOS service mov $0x03, %ah # get cursor position int $0x10 # invoke BIOS service pop %ecx mov %cl, %bl pop %ecx mov $0x1301, %ax # write_string function int $0x10 # invoke BIOS service popal ret #------------------------------------------------------------------ hex: .ascii "0123456789ABCDEF" # table of hex numerals #------------------------------------------------------------------ eax2hex: # convert value on EAX to hexadecimal string at DS:(EDI) pushal mov $8, %ecx # number of nybbles nxnyb: rol $4, %eax # next nybble into AL mov %al, %bl # copy nybble into BL and $0xF, %bx # extend nybble to BX mov hex(%bx), %dl # lookup ascii-code mov %dl, (%edi) # copy numeral to buffer inc %edi # advance buffer index loop nxnyb # again for next nybble popal ret #------------------------------------------------------------------ tmp: .ascii "xxxxxxxxxxxxxxxx" # temporary buffer area #------------------------------------------------------------------ #================================================================== #------------------------------------------------------------------ pci_ID: .short VENDOR_ID, DEVICE_ID # Device's Identification busdev: .int 0 # PCI bus/device/function config: .space 0x100 # PCI Configuration Space #------------------------------------------------------------------ read_config_space: nx_fn: # search for PCI-address of the xHCI controller mov $CONF_ADDR, %dx # i/o port for PCI address mov busdev, %eax # current bus/dev/function bts $31, %eax # access PCI address-space out %eax, %dx # select register to input mov $CONF_DATA, %dx # i/o port for data-value in %dx, %eax # input register's value cmp pci_ID, %eax # matches xHCI controller? je found # yes, search is finished addl $0x00000100, busdev # next PCI function-address cmpl $0x01000000, busdev # PCI space is exhausted? jb nx_fn # no, continue searching freeze: jmp freeze # else we cannot continue found: # read the array of doublewords in xHCI's Config Space xor %edi, %edi # initialize array-index nx_dw: mov $CONF_ADDR, %dx # i/o port for PCI address mov busdev, %eax # device bus/dev/function add %edi, %eax # plus doubleword offset bts $31, %eax # access PCI address-space out %eax, %dx # select register to input mov $CONF_DATA, %dx # i/o port for data-value in %dx, %eax # input register's value mov %eax, config(%edi) # save the value obtained add $4, %edi # next doubleword offset cmp $0x100, %edi # more registers to read? jb nx_dw # yes, get the next one ret #------------------------------------------------------------------ msg1: .ascii "\r\n PCI Configuration Space" .ascii " for Renesas USB 3.0 Host Controller \r\n\n" len1: .short . - msg1 # length of message att1: .byte 0x07 # display attribute #------------------------------------------------------------------ show_config_space: # display output title lea msg1, %bp mov len1, %cx mov att1, %bl call draw_message_text # display the configuration space register-values xor %esi, %esi nxline: # format the register-offset mov %esi, %eax lea tmp, %edi call eax2hex mov tmp+6, %ax mov %ax, msg2+3 # format the line's eight entries lea buf2, %edi nxitem: mov config(%esi), %eax call eax2hex add $9, %edi add $4, %esi test $0x1F, %esi jnz nxitem # show the formatted message lea msg2, %bp mov len2, %cx mov att2, %bl call draw_message_text cmp $0x100, %esi # more entries to show? jb nxline # yes, do another line ret #------------------------------------------------------------------ msg2: .ascii " 0xXX: " buf2: .ascii "xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx " .ascii "xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx " .ascii "\r\n" len2: .short . - msg2 # length of message att2: .byte 0x07 # display attribute #------------------------------------------------------------------ #================================================================== #------------------------------------------------------------------ theGDT: .quad 0, 0x008F92000000FFFF # Global Descriptor Table .equ limGDT, (.-theGDT)-1 # the GDT's segment-limit regGDT: .short limGDT, theGDT, 0x0001 # image for GDTR register #------------------------------------------------------------------ enable_4GB_access: lgdt %cs:regGDT # setup GDTR register cli # turn off interrupts mov %cr0, %eax # current machine status bts $0, %eax # set image of PE-bit mov %eax, %cr0 # enter protected-mode mov $0x0008, %ax # 4GB segment-selector mov %ax, %fs # into FS register mov %ax, %gs # also GS register mov %cr0, %eax # current machine status btr $0, %eax # clear image of PE-bit mov %eax, %cr0 # enter protected-mode sti # interrupts on again in $0x92, %al # system control port or $0x02, %al # turn on A20-Line out %al, $0x92 # to address memory ret #------------------------------------------------------------------ #================================================================== #------------------------------------------------------------------ nslots: .int 0 # number of device slots nintrs: .int 0 # number of interrupters nports: .int 0 # number of adapter ports #------------------------------------------------------------------ init_reg_counters: # point FS:EBX to the xhci register-space xor %ax, %ax # address base segment mov %ax, %gs # with GS register mov config+0x10, %ebx # PCI Configuration BAR0 and $0xFFFFFFF0, %ebx # clear lowest nybble # setup count of device slots mov %gs:0x04(%ebx), %eax # HCSPARAMS1 and $0xFF, %eax # isolate MaxSlots mov %eax, nslots # save as 'nslots' # setup count of interrupters mov %gs:0x04(%ebx), %eax # HCSPARAMS1 shr $8, %eax # bits 18..8 and $0x3FF, %eax # isolate MaxIntrs mov %eax, nintrs # save as 'nintrs' # setup count of adapter ports mov %gs:0x04(%ebx), %eax # HCSPARAMS1 shr $24, %eax # bits 31..24 and $0xFF, %eax # isolate MaxPorts mov %eax, nports # save as 'nports' 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 caps regs #------------------------------------------------------------------ init_reg_pointers: # initialize pointer to the XHCI memory-mapped registers mov config+0x10, %eax # get BAR0 register-value and $0xFFFFFFF0, %eax # mask its lowest nybble mov %eax, mmbase # for xhci register-space # initialize pointer to the XHCI operational registers lfs mmbase, %ebx # point FS:EBX to regs movzxw %fs:0x00(%ebx), %eax # get CAPLENGTH value add %ebx, %eax # to reg base-address mov %eax, opbase # for operational regs # initialize pointer to the XHCI runtime registers lfs mmbase, %ebx # point FS:EBX to regs mov %fs:0x18(%ebx), %eax # get RTSOFF value add %ebx, %eax # to reg base-address mov %eax, rtbase # for runtime regs # initialize pointer to the XHCI doorbell registers lfs mmbase, %ebx # point FS:EBX to regs mov %fs:0x14(%ebx), %eax # get DBOFF value add %ebx, %eax # to reg base-address mov %eax, dbbase # for doorbell regs # initialize pointer to the XHCI extended capability registers lfs mmbase, %ebx # point FS:EBX to regs mov %fs:0x10(%ebx), %eax # get HCCPARAMS value and $0xFFFF0000, %eax # isolate xECP value shr $14, %eax # add the xECP offset add %ebx, %eax # to reg base-address mov %eax, ecbase # for ext cap regs ret #------------------------------------------------------------------ #================================================================== #------------------------------------------------------------------ show_capabilities: # format the capability register-values for display lfs mmbase, %ebx # point to capability regs mov %fs:0x04(%ebx), %eax # HCSPARAMS1 lea hcs1, %edi call eax2hex mov %fs:0x08(%ebx), %eax # HCSPARAMS2 lea hcs2, %edi call eax2hex mov %fs:0x0C(%ebx), %eax # HCSPARAMS3 lea hcs3, %edi call eax2hex mov %fs:0x10(%ebx), %eax # HCCPARAMS1 lea hcc, %edi call eax2hex mov %fs:0x14(%ebx), %eax # DBOFF lea dboff, %edi call eax2hex mov %fs:0x18(%ebx), %eax # RTSOFF lea rtoff, %edi call eax2hex mov %fs:0x00(%ebx), %eax # CAPLENGTH, HCIVERSION lea tmp, %edi call eax2hex mov tmp+0, %eax # HCIVERSION mov %eax, hciver mov tmp+6, %ax # CAPLENHTH mov %ax, caplen # display the capability registers report lea msg3, %bp mov len3, %cx mov att3, %bl call draw_message_text # loop to format and display the extended capabilities lfs ecbase, %ebx # point to ext cap regs nxcap: xor %esi, %esi lea buf4, %edi nxitm: mov %fs:(%ebx, %esi, 4), %eax call eax2hex add $9, %edi inc %esi test $3, %esi jnz nxitm # display the next extended capability report push %ebx lea msg4, %bp mov len4, %cx mov att4, %bl call draw_message_text pop %ebx mov %fs:(%ebx), %eax or %ah, %ah jz ecapx movzx %ah, %eax shl $2, %eax add %eax, %ebx jmp nxcap ecapx: ret #------------------------------------------------------------------ msg3: .ascii "\r\n capability registers: " .ascii "\r\n " .ascii "HCSPARAMS1=" hcs1: .ascii "xxxxxxxx " .ascii "HCSPARAMS2=" hcs2: .ascii "xxxxxxxx " .ascii "HCSPARAMS3=" hcs3: .ascii "xxxxxxxx " .ascii "HCCPARAMS=" hcc: .ascii "xxxxxxxx" .ascii "\r\n " .ascii "DBOFF=" dboff: .ascii "xxxxxxxx " .ascii "RTSOFF=" rtoff: .ascii "xxxxxxxx " .ascii "CAPLENGTH=" caplen: .ascii "xx " .ascii "HCIVERSION=" hciver: .ascii "xxxx " .ascii "\r\n " .ascii "\r\n extended capability registers: " .ascii "\r\n" len3: .short . - msg3 # length of message att3: .byte 0x07 # display attribute #------------------------------------------------------------------ msg4: .ascii " " buf4: .ascii "xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx " .ascii "\r\n" len4: .short . - msg4 # length of message att4: .byte 0x07 # display attribute #------------------------------------------------------------------ #================================================================== #------------------------------------------------------------------ show_operationals: # format the operational register-values for display lfs opbase, %ebx # point to operational regs mov %fs:0x00(%ebx), %eax # USBCMD lea usbcmd, %edi call eax2hex mov %fs:0x04(%ebx), %eax # USBSTS lea usbsts, %edi call eax2hex mov %fs:0x08(%ebx), %eax # PAGESIZE lea pgsize, %edi call eax2hex mov %fs:0x14(%ebx), %eax # DNCTRL lea dnctrl, %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:0x30(%ebx), %eax # DCBAAP - low lea dcbaap+8, %edi call eax2hex mov %fs:0x34(%ebx), %eax # DCBAAP - high lea dcbaap+0, %edi call eax2hex mov %fs:0x38(%ebx), %eax # CONFIG lea tmp, %edi call eax2hex mov tmp+6, %ax mov %ax, usbcfg # display the operational registers report lea msg5, %bp mov len5, %cx mov att5, %bl call draw_message_text ret #------------------------------------------------------------------ msg5: .ascii "\r\n operational registers: " .ascii "\r\n " .ascii "USBCMD=" usbcmd: .ascii "xxxxxxxx " .ascii "USBSTS=" usbsts: .ascii "xxxxxxxx " .ascii "PAGESIZE=" pgsize: .ascii "xxxxxxxx " .ascii "DNCTRL=" dnctrl: .ascii "xxxxxxxx " .ascii "\r\n " .ascii "CRCR=" crcr: .ascii "xxxxxxxxxxxxxxxx " .ascii "DCBAAP=" dcbaap: .ascii "xxxxxxxxxxxxxxxx " .ascii "CONFIG=0x" usbcfg: .ascii "xx " .ascii "\r\n" len5: .short . - msg5 # length of message att5: .byte 0x07 # display attribute #------------------------------------------------------------------ display_port_regs: xor %esi, %esi # initialize port index nxport: # format the port-number lea 1(%esi), %eax # port-umber is 1-based lea tmp, %edi call eax2hex mov tmp+7, %al mov %al, msg6+7 # format the PORTSC register lfs opbase, %ebx # point to operational regs imul $16, %esi, %edx # offset for PORTSC mov %fs:0x400(%ebx, %edx), %eax # fetch PORTSC lea portsc, %edi call eax2hex # interpret selected status-bits mov %eax, %edx # PORTSC shr $0, %edx # CCS-bit (bit 0) and $1, %dl or $'0', %dl mov %dl, pconn mov %eax, %edx # PORTSC shr $1, %edx # CED-bit (bit 1) and $1, %dl or $'0', %dl mov %dl, penabl mov %eax, %edx # PORTSC shr $5, %edx # PLS-bit (bits 8..5) and $0x0F, %edx mov hex(%edx), %dl mov %dl, pstate mov %eax, %edx # PORTSC shr $9, %edx # CED-bit (bit 9) and $1, %dl or $'0', %dl mov %dl, ppower mov %eax, %edx # PORTSC shr $10, %edx # PORT SPEED (bits 13..10) and $0x0F, %edx mov hex(%edx), %dl mov %dl, pspeed # display the port report lea msg6, %bp mov len6, %cx mov att6, %bl call draw_message_text inc %esi # increment port-index cmp nports, %esi # any more ports? jb nxport # yes, do the next one ret #------------------------------------------------------------------ msg6: .ascii " port #x:" .ascii " PORTSC=" 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" len6: .short . - msg6 # length of message att6: .byte 0x07 # display attribute #------------------------------------------------------------------ #================================================================== #------------------------------------------------------------------ show_runtime_regs: # format the MFINDEX 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 # display the MFINDEX register and register-legend lea msg7, %bp mov len7, %cx mov att7, %bl call draw_message_text # loop to format and display the interrupter registers xor %esi, %esi # initialize array-index nxintr: # format the interrupter-number mov %esi, %eax # interrupter number lea tmp, %edi call eax2hex mov tmp+7, %al mov %al, inum8 # format the interrupter registers lfs rtbase, %ebx # for runtime registers imul $32, %esi, %edx # offset to register-bank mov %fs:0x20(%ebx, %edx), %eax # IMAN lea iman8, %edi call eax2hex mov %fs:0x24(%ebx, %edx), %eax # IMOD lea imod8, %edi call eax2hex mov %fs:0x28(%ebx, %edx), %eax # ERSTSZ lea ersz8, %edi call eax2hex mov %fs:0x30(%ebx, %edx), %eax # ERSTBA - low lea erba8+8, %edi call eax2hex mov %fs:0x34(%ebx, %edx), %eax # ERSTBA - high lea erba8+0, %edi call eax2hex mov %fs:0x38(%ebx, %edx), %eax # ERDP - low lea erdp8+8, %edi call eax2hex mov %fs:0x3C(%ebx, %edx), %eax # ERDP - high lea erdp8+0, %edi call eax2hex # display this interrupter's registers lea msg8, %bp mov len8, %cx mov att8, %bl call draw_message_text inc %esi # increment interrupter number cmp nintrs, %esi # any more interrupters? jb nxintr # yes, do the next interrupter ret #------------------------------------------------------------------ msg7: .ascii "\r\n runtime registers: " .ascii "\r\n " .ascii "MFINDEX=" mfindx: .ascii "xxxx " .ascii " IMAN IMOD ERSTSZ " .ascii " ERSTBA ERDP " .ascii "\r\n" len7: .short . - msg7 # length of message att7: .byte 0x07 # display attribute #------------------------------------------------------------------ msg8: .ascii " intr #" inum8: .ascii "x " iman8: .ascii "xxxxxxxx " imod8: .ascii "xxxxxxxx " ersz8: .ascii "xxxxxxxx " erba8: .ascii "xxxxxxxxxxxxxxxx " erdp8: .ascii "xxxxxxxxxxxxxxxx " .ascii "\r\n" len8: .short . - msg8 # length of message att8: .byte 0x07 # display attribute #------------------------------------------------------------------ #================================================================== #------------------------------------------------------------------ .align 16 # insures word-alignment .space 512 # space for stack to use tos: # label for top-of-stack #------------------------------------------------------------------ .end ONLINE REFERENCE: "eXtensible Host Controller Interface for Universal Serial Bus (xHCI)," Revision 1.0, Intel Corporation (21 May 2010).