//----------------------------------------------------------------- // uhciboot.s // // The purpose of this 'boot time' program is to show us what // is going on with the UHCI controllers before any operating // system has been launched. Specifically, it shows I/O-port // addresses for each of the installed UHCI controllers, plus // the dynamically updated values in all of the registers for // each of the UHCI controllers. From the display we can see // which ports have USB devices attached, among other things, // and watch changes occurring as new devices are plugged in. // (Press the -key to terminate the dynamic display.) // // to assemble: $ as uhciboot.s -o uhciboot.o // and to link: $ ld uhciboot.o -T ldscript -o uhciboot.b // and install: $ dd if=uhciboot.b of=/dev/sda4 seek=1 // // Reference: See "UHCI Controllers Registers (Chapter 16)" // of "Intel I/O Controller Hub 9 (ICH9) Family Datasheet". // // NOTE: This code begins executing with CS:IP = 1000:0002. // // programmer: ALLAN CRUSE // written on: 25 FEB 2010 //----------------------------------------------------------------- .equ CLASS_UHCI, 0x0C0300 # PCI Class-Code for UHCI .equ MAX_COUNT, 8 # maximum supported hosts .section .text #------------------------------------------------------------------ .short 0xABCD # application's signature #------------------------------------------------------------------ main: .code16 # for 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 this arena mov %ax, %ds # with DS register mov %ax, %es # also ES register mov %ax, %ss # also SS register lea tos, %sp # establish our stack call erase_vga_display call detect_uhci_hosts call dynamic_host_view call wait_for_keypress lss %cs:ipltos, %sp # recover the loader stack lret # return control to loader #------------------------------------------------------------------ ipltos: .short 0, 0 # holds the loader's SS:SP #------------------------------------------------------------------ progID: .ascii " uhciboot -- 2/25/10 " # aids in file recognition #------------------------------------------------------------------ #------------------------------------------------------------------ #---- Helper Routines for Input/Output and Numeric Conversions ---- #------------------------------------------------------------------ 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: pushal # save caller's registers push %cx # save message count push %bx # and attribute byte mov $0x0F, %ah # get_display_state_ int $0x10 # invoke BIOS service mov $0x03, %ah # get_cursor_location int $0x10 # invoke BIOS service pop %cx # recover attribute byte mov %cl, %bl # and place it into BL pop %cx # recover message size mov $0x1301, %ax # write_string function int $0x10 # invoke BIOS service popal # restore saved registers ret #------------------------------------------------------------------ hide_video_cursor: mov $0x03, %ah # get_cursor_size int $0x10 # invoke BIOS service or $0x20, %ch # set bit 5 in CH mov $0x01, %ah # set_cursor_size int $0x10 # invoke BIOS service ret #------------------------------------------------------------------ show_video_cursor: mov $0x03, %ah # get_cursor_size int $0x10 # invoke BIOS service and $0xDF, %ch # clear bit 5 in CH mov $0x01, %ah # set_cursor_size int $0x10 # invoke BIOS service ret #------------------------------------------------------------------ place: .byte 0, 0, 0 # for column, row, page #------------------------------------------------------------------ save_cursor_place: mov $0x0F, %ah # get_display_state int $0x10 # invoke BIOS service mov $0x03, %ah # get_cursor_position int $0x10 # invoke BIOS service mov %dx, place+0 # save column and row mov %bh, place+2 # and the page-number ret #------------------------------------------------------------------ #------------------------------------------------------------------ load_cursor_place: mov place+0, %dx # load column and row mov place+2, %bh # and the page-number mov $0x02, %ah # set_cursor_position int $0x10 # invoke BIOS service ret #------------------------------------------------------------------ #------------------------------------------------------------------ ten: .int 10 # radix for decimal system #------------------------------------------------------------------ cvtud: # convert EAX to unsigned decimal digit-string at DS:EDI pushal xor %ecx, %ecx # initialize digit count nxdiv: xor %edx, %edx # extend EAX for division divl ten # divide by decimal radix push %edx # preserve the remainder inc %ecx # and increment our count or %eax, %eax # test: was quotient zero? jnz nxdiv # no, do another division nxdgt: pop %edx # else recover remainder or $'0', %dl # convert binary to ascii mov %dl, (%edi) # store numeral in buffer inc %edi # advance buffer pointer loop nxdgt # again for next remainder popal ret #------------------------------------------------------------------ hex: .ascii "0123456789ABCDEF" # list of the hex numerals #------------------------------------------------------------------ eax2hex: # convert EAX to a hexadecimal digit-string at DS:EDI pushal mov $8, %ecx # number of the 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 its numeral mov %dl, (%edi) # store numeral in buffer inc %edi # advance buffer pointer loop nxnyb # again for next nybble popal ret #------------------------------------------------------------------ tmp: .ascii "xxxxxxxx" # temporary output buffer #------------------------------------------------------------------ ax2hex: # convert AX to a hexadecimal digit-string at DS:EDI push %edx # save scratch register push %edi # and destination address lea tmp, %edi # point to temporary buffer call eax2hex # convert EAX to hex-string pop %edi # recover saved destination mov tmp+4, %edx # copy lowest four numerals mov %edx, (%edi) # into the caller's buffer pop %edx # restore scratch register ret #------------------------------------------------------------------ #------------------------------------------------------------------ al2hex: # convert AL to a hexadecimal digit-string at DS:EDI push %edx # save scratch register push %edi # and destination address lea tmp, %edi # point to temporary buffer call eax2hex # convert EAX to hex-string pop %edi # recover saved destination mov tmp+6, %dx # copy lowest two numerals mov %dx, (%edi) # into the caller's buffer pop %edx # restore scratch register ret #------------------------------------------------------------------ #------------------------------------------------------------------ # This table describes the boot-time mapping of IRQs to vectors irqmap: .byte 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F .byte 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77 #------------------------------------------------------------------ n_hubs: .long 0 # number of visible hosts busdev: .word 0, 0, 0, 0, 0, 0, 0, 0 # PCI bus/device/function dev_id: .word 0, 0, 0, 0, 0, 0, 0, 0 # controller's 'deviceID' iobase: .word 0, 0, 0, 0, 0, 0, 0, 0 # I/O-port 'base-address' irq_id: .byte 0, 0, 0, 0, 0, 0, 0, 0 # controller's IRQ-number int_id: .byte 0, 0, 0, 0, 0, 0, 0, 0 # interrupt vector-number #------------------------------------------------------------------ msg0: .ascii "Number of visible UHCI Controllers = " buf0: .ascii " \r\n" len0: .short . - msg0 # length of message-text att0: .byte 0x07 # text-display attribute #------------------------------------------------------------------ msg1: .ascii " #" hub$: .ascii " bus=" bus$: .ascii "x, dev=" dev$: .ascii "xx, fnc=" fnc$: .ascii "x DeviceID=" did$: .ascii "xxxx I/O-Base=" iob$: .ascii "xxxx IRQ=0x" irq$: .ascii "xx INT=0x" int$: .ascii "xx " .ascii "\r\n" len1: .short . - msg1 # length of message-text att1: .byte 0x07 # text-display attribute #------------------------------------------------------------------ detect_uhci_hosts: xor %esi, %esi # initialize array-index nxhub: mov $0xB103, %ax # pci_get_class function mov $CLASS_UHCI, %ecx # specify the class-code int $0x1A # invoke BIOS service cmp $0x86, %ah # test: device was found? je findx # no, search is finished mov %bx, busdev(, %esi, 2) # store bus-address info mov $0xB109, %ax # pci_read_config_word mov $0x02, %edi # register offset of DevID int $0x1A # invoke BIOS service mov %cx, dev_id(, %esi, 2) # store the PCI Device-ID mov $0xB10A, %ax # pci_read_config_dword mov $0x20, %edi # register offset of BAR4 int $0x1A # invoke BIOS service and $0xFFFC, %cx # omit the I/O-indicator mov %cx, iobase(, %esi, 2) # store I/O-port address mov $0xB108, %ax # pci_read_config_byte mov $0x3C, %edi # register offset of IRQ_LN int $0x1A # invoke BIOS service mov %cl, irq_id(, %esi, 1) # store the IRQ number movzx %cl, %ecx # extend IRQ to 32-bits mov irqmap(%ecx), %dl # lookup its interrupt ID mov %dl, int_id(, %esi, 1) # store the vector number inc %esi # advance search index cmp $MAX_COUNT, %esi # maximum reached yet? jb nxhub # no, try for another findx: mov %esi, n_hubs # save the hub count mov n_hubs, %eax # number of hubs found lea buf0, %edi # point to output-field call cvtud # convert to digit-string lea msg0, %bp # offset of text-message mov len0, %cx # length of text-message mov att0, %bl # text's color attribute call draw_message_text # write message onscreen # sort hubs' records in order of their 'DeviceID' fields # for conformity with the numbering scheme used by Linux bubb0: mov $0, %esi # ESI = 1-st array-index mov $1, %edi # EDI = 2-nd array-index bubb1: mov dev_id(, %esi, 2), %cx # fetch 'DeviceID' value mov dev_id(, %edi, 2), %dx # fetch 'DeviceID' value cmp %cx, %dx # latter value is larger? jae bubb2 # yes, retain this order # swap all the fields and start over mov busdev(, %esi, 2), %cx mov busdev(, %edi, 2), %dx mov %dx, busdev(, %esi, 2) mov %cx, busdev(, %edi, 2) mov dev_id(, %esi, 2), %cx mov dev_id(, %edi, 2), %dx mov %dx, dev_id(, %esi, 2) mov %cx, dev_id(, %edi, 2) mov iobase(, %esi, 2), %cx mov iobase(, %edi, 2), %dx mov %dx, iobase(, %esi, 2) mov %cx, iobase(, %edi, 2) mov irq_id(, %esi, 1), %cl mov irq_id(, %edi, 1), %dl mov %dl, irq_id(, %esi, 1) mov %cl, irq_id(, %edi, 1) mov int_id(, %esi, 1), %cl mov int_id(, %edi, 1), %dl mov %dl, int_id(, %esi, 1) mov %cl, int_id(, %edi, 1) jmp bubb0 # start again from beginning bubb2: inc %esi # advance earlier index inc %edi # and successor index cmp n_hubs, %edi # index still within bounds jb bubb1 # yes, compare array-entries # loop to display each UHCI controller's parameters xor %esi, %esi # initialize array-index nxmsg: # format parameters for display mov %esi, %eax inc %eax lea hub$, %edi call cvtud mov dev_id(, %esi, 2), %ax lea did$, %edi call ax2hex mov busdev(, %esi, 2), %ax shr $8, %ax and $0xFF, %eax lea bus$, %edi call cvtud mov busdev(, %esi, 2), %ax shr $3, %ax and $0x1F, %eax lea dev$, %edi call cvtud mov busdev(, %esi, 2), %ax shr $0, %ax and $0x07, %eax lea fnc$, %edi call cvtud mov iobase(, %esi, 2), %ax lea iob$, %edi call ax2hex mov irq_id(, %esi, 1), %al lea irq$, %edi call al2hex mov int_id(, %esi, 1), %al lea int$, %edi call al2hex lea msg1, %bp # offset of text-message mov len1, %cx # length of text-message mov att1, %bl # text's color attribute call draw_message_text # write message onscreen inc %esi # increment array-index cmp n_hubs, %esi # still within range? jb nxmsg # yes, show next message ret #------------------------------------------------------------------ msg2: .ascii "\r\n HUB USBCMD USBSTS USBINTR FRNUM " .ascii " FRBASEADDR SOFMOD PORTSC0 PORTSC1 \r\n" len2: .short . - msg2 # length of message-text att2: .byte 0x07 # text-display attribute #------------------------------------------------------------------ msg3: .ascii " #" hnum$: .ascii "x: " ucmd$: .ascii "xxxx " usts$: .ascii "xxxx " uint$: .ascii "xxx " fnum$: .ascii "xxx " addr$: .ascii "xxxxxxxx " usof$: .ascii "xx " p0sc$: .ascii "xxxx " p1sc$: .ascii "xxxx \r\n" len3: .short . - msg3 # length of message-text att3: .byte 0x07 # text-display attribute #------------------------------------------------------------------ dynamic_host_view: # display our static title-line for the dynamic display lea msg2, %bp # offset of text-message mov len2, %cx # length of text-message mov att2, %bl # text's color attribute call draw_message_text # write message onscreen # main loop for dynamic display of controllers' registers call save_cursor_place # remember cursor location call hide_video_cursor # make movements invisible nxview: call load_cursor_place # draw from saved location xor %esi, %esi # initialize array-index nxline: # format HUB number mov %esi, %eax # copy the array-index inc %eax # and add 1 to value lea hnum$, %edi # point DS:EDI to buffer call cvtud # convert to digit-string # format USBCMD mov iobase(, %esi, 2), %dx in %dx, %ax lea ucmd$, %edi call ax2hex # format USBSTS mov iobase(, %esi, 2), %dx add $2, %dx in %dx, %ax lea usts$, %edi call ax2hex # format USBINTR mov iobase(, %esi, 2), %dx add $4, %dx in %dx, %ax lea tmp, %edi call eax2hex lea uint$, %edi mov tmp+5, %al mov %al, 0(%edi) mov tmp+6, %ax mov %ax, 1(%edi) # format FRNUM mov iobase(, %esi, 2), %dx add $6, %dx in %dx, %ax lea tmp, %edi call eax2hex lea fnum$, %edi mov tmp+5, %al mov %al, 0(%edi) mov tmp+6, %ax mov %ax, 1(%edi) # format FRBASEADDR mov iobase(, %esi, 2), %dx add $8, %dx in %dx, %eax lea addr$, %edi call eax2hex # format SOFMOD mov iobase(, %esi, 2), %dx add $12, %dx in %dx, %eax lea usof$, %edi call al2hex # format PORTSC0 mov iobase(, %esi, 2), %dx add $16, %dx in %dx, %ax lea p0sc$, %edi call ax2hex # format PORTSC1 mov iobase(, %esi, 2), %dx add $18, %dx in %dx, %ax lea p1sc$, %edi call ax2hex # display this hub's register-values lea msg3, %bp # offset of text-message mov len3, %cx # length of text-message mov att3, %bl # text's color-attribute call draw_message_text # write message onscreen inc %esi # increment array-index cmp n_hubs, %esi # still within range? jb nxline # yes, next line's info # check for -key mov $0x01, %ah # peek at keyboard queue int $0x16 # invoke BIOS service jz nxview # nothing new? continue mov $0x00, %ah # else get keyboard data int $0x16 # invoke BIOS service cmp $0x1B, %al # was pressed? je fini # yes, viewing finished jmp nxview # else continue viewing fini: call show_video_cursor # make the cursor visible ret #------------------------------------------------------------------ #------------------------------------------------------------------ .align 16 # insures word alignment .space 512, 0x55 # space for stack to use tos: # label for top-of-stack #------------------------------------------------------------------ .end