True! You can call the PCI bios functions from pmode and its quite easy to do, and it does not require any mode switching back into real-mode to do it.

How do i access BIOS32 PCI ?

First thing is, you have to locate the BIOS32 service directory entry point. This is done by scanning for the 4 bytes of "magic" that is _32_ (0x5F32335F). The BIOS32 SD can lie in memory from 0xE0000 to 0x100000 and it always lies on a paragraph alignment.

CALL xxxxh:xxxxh - BIOS32 Service Directory
InstallCheck:   scan paragraph boundaries E000h to FFFFh for signature
                string "_32_", followed by a valid header structure
                (see #F0021)

Notes:  a 32-bit-code alternate PCI BIOS entry point may be found (if
        supported) by requesting the entry point for the API with
        identifier "$PCI".
        an alternate entry point for INT 1A/AH=B4h may be found (if
        supported) by requesting the entry point for the API with
        identifier "$ACF"
        other known identifiers are "$WDS" and "MPTN"
SeeAlso: INT 1A/AX=B100h

Format of BIOS32 Service Directory header structure:
Offset  Size    Description     (Table F0021)
 00h  4 BYTEs   signature "_32_"
 04h    DWORD   physical address of BSD entry point (see #F0022)
 08h    BYTE    header structure version number (currently 00h)
 09h    BYTE    header structure length in paragraphs (currently 01h)
 0Ah    BYTE    checksum (8-bit sum of all bytes in structure,
                including this one, should equal zero)
 0Bh  5 BYTEs   reserved (0)

(Table F0022)
Call BIOS32 Service Directory entry point with:
        EBX = function
            00000000h get service entry point
                EAX = service identifier
                    46434124h ("FCA$") Plug-and-Play Auto-Configuration
                    49435024h ("ICP$") PCI BIOS
                    4E54504Dh ("NTPM") ??? MPTN [PhoenixBIOS4 Rev. 6.0]
                    54435724h ("SDW$") ??? WDS$ [PhoenixBIOS4 Rev. 6.0]
                Return: AL = status
                            00h successful
                                 EBX = base address of handler's code seg
                                 ECX = size of code segment
                                 EDX = offset of handler in code seg
                            80h unknown service identifier
            else
                Return: AL = 81h invalid function
Notes:  the BSD handler assumes that it is running in a 32-bit code
        segment the returned entry points for PCI BIOS and Auto-Config
        must be called with the same registers as the real-mode INT
        1Ah interface, including the value B1h or B4h in AH (AMI BIOS
        v1.00.05.AX1 returns the same entry point for both interfaces
        and uses AH to distinguish which API is desired)

        some references indicate that only BL is used for the function
        number, though at least one implementation actually checks the
        entire EBX register; for maximum compatibility, the upper 24
        bits of EBX should be cleared when calling the entry point

Here is an example of detecting the BIOS32 service directory.

typedef struct BIOS32
{
        ULONG   magic                   __attribute__ ((packed));
        ULONG   phys_bsd_entry          __attribute__ ((packed));
        UCHAR   vers                    __attribute__ ((packed));
        UCHAR   prg_lens                __attribute__ ((packed));
        UCHAR   crc                     __attribute__ ((packed));
} BIOS32;

BIOS32 *master_bios32;
ULONG bios32_call;

UCHAR search_pci_bios(void)
{
        UCHAR           *p=(UCHAR*)0xE0000;
        BIOS32          *x;
        UCHAR           flag=0;
        UCHAR           crc;
        int                     i;

        master_bios32=NULL;
        bios32_call=0;
        while(flag==0 && (ULONG)p<0X100000)
        {
                X=(BIOS32*)P;
                IF(X->magic==0x5F32335F)                /* _32_ */
                {
                        for(i=0, crc=0; i<(X->prg_lens*16); i++)
                                crc+=*(p+i);
                        if(crc==0)
                        {
                            flag=1;
                            master_bios32=x;
                                bios32_call=master_bios32->phys_bsd_entry;
                        }
                }
                else
                        p+=0x10;
        }
}

Once you have located the service directory you can do a far call (InlineAssembly helping you for registers passing) to its physical entry point and ask it if PCI bios32 calls exist. (PCI v2.0c+)

void bios32_scan_pci_entry(void)
{
        ULONG   cseg_size, offset, base_addr;

        /* call the BIOS32 BSD for the PCI address
           BSD calls terminate in RETF not RET */

        /* eax is loaded with "$PCI" magic */
        asm("movl       $0x49435024, %%eax\n"
                "xorl   %%ebx, %%ebx\n"
                "movl   _bios32_call, %%ebp\n"
                "pushl  %%cs\n"
                "call   %%ebp\n"
                : "=c" (cseg_size),
                  "=d" (offset),
                  "=b" (base_addr)
                :
                : "eax", "ebx", "ecx", "edx", "ebp", "memory" );

        /* setup two new selectors of pci_code32, pci_data32, etc. */
}

Once you have determined that PCI v2.0c+ BIOS calls exist for pmode applications, you can call the PCI v2.0c+ calls (see INT 0x1A, function 0xB101 down to 0xB18F in RalfBrown's INT List).

But ... It doesn't work ?

Watch out! The BIOS32 may be present even if no PCI bios is available ... This is especially the case on the BOCHS virtual PC. Check every step, and make sure you didn't stop your search too early (i.e. finding the magic _32_ string means nothing if the checksum is wrong) ...

And what is it good for ?

Good question. Afaik, the BIOS32 will offer a 'select device where vendor_ID==x' and a 'select device where function==x' queries as well as calls to read/write byte/word/dword from PCI configuration space. In version 2.1 of the standard there are also some functions to provide the O.S. or the PNP driver with PCI Interrupt Routing Options.

configuration mechanisms

As explained on Where can I find programming info on PCI?, there are 2 incompatible configuration mechanism for PCI. Using the wrong one will cause chaos, and the PCI BIOS can help you in telling which one is the one to use

(int 0xa1, ax=0xb101, mechanism i supported if al&i is set). RBIL for details

telling the number of buses that exists

When enumerating devices, you'll love to know how many they are. This is returned in cl for the same int 0xa1, ax=0xb101 call. On each bus, you'll have to poll each of the 32 devices (which all may have up to 8 functions).

Where do i get a more complete reference ?

The PCI BIOS Specification, revision 2.1, could be what you need. It details all the standardized calls.


Categories: HowTo, HardWareBus, UsingBios