david/ipxe
Archived
1
0
This repository has been archived on 2020-12-06. You can view files and clone it, but cannot push or open issues or pull requests.
ipxe/src/arch/i386/core/pci_io.c

452 lines
12 KiB
C
Raw Normal View History

2005-03-08 19:53:11 +01:00
/*
** Support for NE2000 PCI clones added David Monro June 1997
** Generalised to other NICs by Ken Yap July 1997
**
** Most of this is taken from:
**
** /usr/src/linux/drivers/pci/pci.c
** /usr/src/linux/include/linux/pci.h
** /usr/src/linux/arch/i386/bios32.c
** /usr/src/linux/include/linux/bios32.h
** /usr/src/linux/drivers/net/ne.c
*/
#include "etherboot.h"
2006-04-30 03:08:52 +02:00
#include <gpxe/init.h>
#include <gpxe/pci.h>
2005-04-12 15:31:36 +02:00
#include "pci_io.h"
#ifdef KEEP_IT_REAL
#include "realmode.h"
#endif
2005-03-08 19:53:11 +01:00
2005-04-12 15:31:36 +02:00
/* Macros for direct PCI access */
#define CONFIG_ADDRESS 0xcf8
#define CONFIG_DATA 0xcfc
#define CONFIG_CMD( pci, where ) \
( 0x80000000 | ( pci->bus << 16 ) | ( pci->devfn << 8 ) | \
( where & ~3 ) )
2005-04-12 15:31:36 +02:00
/* Signatures for PCI BIOS */
#define BIOS_SIG(a,b,c,d) ( ( a<<0 ) + ( b<<8 ) + ( c<<16 ) + ( d<<24 ) )
#define PRINT_BIOS_SIG(x) ( (x) & 0xff ), ( ( (x)>>8 ) & 0xff ), \
( ( (x)>>16 ) & 0xff ),( ( (x)>>24 ) & 0xff )
#define BIOS32_SIGNATURE BIOS_SIG ( '_', '3', '2', '_' )
#define PCI_SIGNATURE BIOS_SIG ( 'P', 'C', 'I', ' ' )
#define PCI_SERVICE BIOS_SIG ( '$', 'P', 'C', 'I' )
/* BIOS32 structure as found in PCI BIOS ROM */
struct bios32 {
unsigned long signature; /* _32_ */
unsigned long entry; /* 32 bit physical address */
unsigned char revision; /* Revision level, 0 */
unsigned char length; /* Length in paragraphs */
unsigned char checksum; /* Should byte sum to zero */
unsigned char reserved[5]; /* Must be zero */
};
/* Values returned by BIOS32 service directory */
#define BIOS32_SERVICE_PRESENT 0x00
#define BIOS32_SERVICE_NOT_PRESENT 0x80
#define CF ( 1 << 0 )
/* PCI BIOS entry point */
#ifndef KEEP_IT_REAL
static unsigned long pcibios32_entry;
#endif
static int have_pcibios;
/* Macro for calling a 32-bit entry point with flat physical
* addresses. Use in a statement such as
* __asm__ ( FLAT_FAR_CALL_ESI,
* : "=S" ( discard, or real output ), <other output registers>
2005-04-12 15:31:36 +02:00
* : "S" ( entry_point ), <other input registers> );
* "=S" *must* be specified as an output, otherwise the compiler will
* assume that it remains unaltered.
2005-03-08 19:53:11 +01:00
*/
2005-04-12 15:31:36 +02:00
#define FLAT_FAR_CALL_ESI "call _virt_to_phys\n\t" \
"pushl %%cs\n\t" \
"call *%%esi\n\t" \
"cli\n\t" \
"cld\n\t" \
"call _phys_to_virt\n\t"
2005-03-08 19:53:11 +01:00
2005-04-12 15:31:36 +02:00
/*
* Functions for accessing PCI configuration space directly with type
* 1 accesses.
*
*/
2005-03-08 19:53:11 +01:00
2005-04-12 15:31:36 +02:00
static inline int pcidirect_read_config_byte ( struct pci_device *pci,
unsigned int where,
uint8_t *value ) {
outl ( CONFIG_CMD ( pci, where ), CONFIG_ADDRESS );
*value = inb ( CONFIG_DATA + ( where & 3 ) );
return 0;
2005-03-08 19:53:11 +01:00
}
2005-04-12 15:31:36 +02:00
static inline int pcidirect_read_config_word ( struct pci_device *pci,
unsigned int where,
uint16_t *value ) {
outl ( CONFIG_CMD ( pci, where ), CONFIG_ADDRESS );
*value = inw ( CONFIG_DATA + ( where & 2 ) );
return 0;
2005-03-08 19:53:11 +01:00
}
2005-04-12 15:31:36 +02:00
static inline int pcidirect_read_config_dword ( struct pci_device *pci,
unsigned int where,
uint32_t *value ) {
outl ( CONFIG_CMD ( pci, where ), CONFIG_ADDRESS );
*value = inl ( CONFIG_DATA );
return 0;
2005-03-08 19:53:11 +01:00
}
2005-04-12 15:31:36 +02:00
static inline int pcidirect_write_config_byte ( struct pci_device *pci,
unsigned int where,
uint8_t value ) {
outl ( CONFIG_CMD ( pci, where ), CONFIG_ADDRESS );
outb ( value, CONFIG_DATA + ( where & 3 ) );
return 0;
2005-03-08 19:53:11 +01:00
}
2005-04-12 15:31:36 +02:00
static inline int pcidirect_write_config_word ( struct pci_device *pci,
unsigned int where,
uint16_t value ) {
outl ( CONFIG_CMD ( pci, where ), CONFIG_ADDRESS );
outw ( value, CONFIG_DATA + ( where & 2 ) );
return 0;
2005-03-08 19:53:11 +01:00
}
2005-04-12 15:31:36 +02:00
static inline int pcidirect_write_config_dword ( struct pci_device *pci,
unsigned int where,
uint32_t value ) {
outl ( CONFIG_CMD ( pci, where ), CONFIG_ADDRESS );
outl ( value, CONFIG_DATA );
return 0;
2005-03-08 19:53:11 +01:00
}
2005-04-12 15:31:36 +02:00
/*
* Functions for accessing PCI configuration space directly via the
* PCI BIOS.
*
* Under -DKEEP_IT_REAL, we use INT 1A, otherwise we use the BIOS32
* interface.
2005-03-08 19:53:11 +01:00
*/
2005-04-12 15:31:36 +02:00
#ifdef KEEP_IT_REAL
static void find_pcibios16 ( void ) {
uint16_t present;
uint32_t signature;
uint16_t flags;
uint16_t revision;
uint8_t max_bus;
2005-04-12 15:31:36 +02:00
/* PCI BIOS installation check */
REAL_EXEC ( rm_pcibios_check,
"int $0x1a\n\t"
"pushfw\n\t"
"popw %%si\n\t",
5,
2005-04-12 15:31:36 +02:00
OUT_CONSTRAINTS ( "=a" ( present ), "=b" ( revision ),
"=c" ( max_bus ), "=d" ( signature ),
"=S" ( flags ) ),
2005-04-12 15:31:36 +02:00
IN_CONSTRAINTS ( "a" ( ( PCIBIOS_PCI_FUNCTION_ID << 8 ) +
PCIBIOS_PCI_BIOS_PRESENT ) ),
CLOBBER ( "edi", "ebp" ) );
2005-04-12 15:31:36 +02:00
if ( ( flags & CF ) ||
( ( present >> 8 ) != 0 ) ||
( signature != PCI_SIGNATURE ) ) {
DBG ( "PCI BIOS installation check failed\n" );
return;
}
2005-03-08 19:53:11 +01:00
2005-04-12 15:31:36 +02:00
/* We have a PCI BIOS */
DBG ( "Found 16-bit PCI BIOS interface with %d buses\n", max_bus + 1 );
2005-04-12 15:31:36 +02:00
have_pcibios = 1;
pci_max_bus = max_bus;
2005-04-12 15:31:36 +02:00
return;
2005-03-08 19:53:11 +01:00
}
2005-04-12 15:31:36 +02:00
INIT_FN ( INIT_PCIBIOS, find_pcibios16, NULL, NULL );
#define pcibios16_read_write( command, pci, where, value ) \
( { \
uint32_t discard_b, discard_D; \
uint16_t ret; \
\
REAL_EXEC ( 999, /* need a local label */ \
2005-04-12 15:31:36 +02:00
"int $0x1a\n\t" \
"jc 1f\n\t" \
"xorw %%ax, %%ax\n\t" \
2005-04-12 15:31:36 +02:00
"\n1:\n\t", \
5, \
OUT_CONSTRAINTS ( "=a" ( ret ), \
"=b" ( discard_b ), \
"=c" ( value ), \
"=D" ( discard_D ) ), \
IN_CONSTRAINTS ( "a" ( command + \
( PCIBIOS_PCI_FUNCTION_ID << 8 ) ), \
"b" ( pci->busdevfn ), \
"c" ( value ), \
"D" ( where ) ), \
CLOBBER ( "edx", "esi", "ebp" ) ); \
2005-04-12 15:31:36 +02:00
\
( ret >> 8 ); \
} )
#define pcibios_read_write pcibios16_read_write
#else /* KEEP_IT_REAL */
2005-03-08 19:53:11 +01:00
2005-04-12 15:31:36 +02:00
/*
* Locate the BIOS32 service directory by scanning for a valid BIOS32
* structure
*
*/
static struct bios32 * find_bios32 ( void ) {
uint32_t address;
2005-03-08 19:53:11 +01:00
/*
* Follow the standard procedure for locating the BIOS32 Service
* directory by scanning the permissible address range from
* 0xe0000 through 0xfffff for a valid BIOS32 structure.
*
*/
2005-04-12 15:31:36 +02:00
for ( address = 0xe0000 ; address < 0xffff0 ; address += 16 ) {
struct bios32 * candidate = phys_to_virt ( address );
unsigned int length, i;
unsigned char sum;
2005-03-08 19:53:11 +01:00
2005-04-12 15:31:36 +02:00
if ( candidate->signature != BIOS32_SIGNATURE )
2005-03-08 19:53:11 +01:00
continue;
2005-04-12 15:31:36 +02:00
length = candidate->length * 16;
if ( ! length )
2005-03-08 19:53:11 +01:00
continue;
2005-04-12 15:31:36 +02:00
for ( sum = 0, i = 0 ; i < length ; i++ )
sum += ( ( char * ) candidate ) [i];
if ( sum != 0 )
2005-03-08 19:53:11 +01:00
continue;
2005-04-12 15:31:36 +02:00
if ( candidate->revision != 0 ) {
DBG ( "unsupported BIOS32 revision %d at %#x\n",
candidate->revision, address );
2005-03-08 19:53:11 +01:00
continue;
}
2005-04-12 15:31:36 +02:00
DBG ( "BIOS32 Service Directory structure at %#x\n", address );
return candidate;
2005-03-08 19:53:11 +01:00
}
2005-04-12 15:31:36 +02:00
return NULL;
2005-03-08 19:53:11 +01:00
}
2005-04-12 15:31:36 +02:00
/*
* Look up a service in the BIOS32 service directory
*
*/
static unsigned long find_bios32_service ( struct bios32 * bios32,
unsigned long service ) {
uint8_t return_code;
uint32_t address;
uint32_t length;
uint32_t entry;
uint32_t discard;
__asm__ ( FLAT_FAR_CALL_ESI
: "=a" ( return_code ), "=b" ( address ),
"=c" ( length ), "=d" ( entry ), "=S" ( discard )
: "a" ( service ), "b" ( 0 ), "S" ( bios32->entry )
: "edi", "ebp" );
switch ( return_code ) {
case BIOS32_SERVICE_PRESENT:
2005-04-12 18:27:27 +02:00
DBG ( "BIOS32 service %c%c%c%c present at %#x\n",
PRINT_BIOS_SIG ( service ), ( address + entry ) );
2005-04-12 15:31:36 +02:00
return ( address + entry );
case BIOS32_SERVICE_NOT_PRESENT:
DBG ( "BIOS32 service %c%c%c%c : not present\n",
PRINT_BIOS_SIG ( service ) );
return 0;
default: /* Shouldn't happen */
DBG ( "BIOS32 returned %#x for service %c%c%c%c!\n",
return_code, PRINT_BIOS_SIG ( service ) );
return 0;
}
2005-03-08 19:53:11 +01:00
}
2005-04-12 18:27:27 +02:00
/*
* Find the 32-bit PCI BIOS interface, if present.
*
*/
2005-04-12 15:31:36 +02:00
static void find_pcibios32 ( void ) {
struct bios32 *bios32;
uint32_t signature;
uint16_t present;
uint32_t flags;
uint16_t revision;
uint8_t max_bus;
2005-04-12 15:31:36 +02:00
/* Locate BIOS32 service directory */
bios32 = find_bios32 ();
if ( ! bios32 ) {
DBG ( "No BIOS32\n" );
return;
}
/* Locate PCI BIOS service */
pcibios32_entry = find_bios32_service ( bios32, PCI_SERVICE );
if ( ! pcibios32_entry ) {
DBG ( "No PCI BIOS\n" );
return;
2005-03-08 19:53:11 +01:00
}
2005-04-12 15:31:36 +02:00
/* PCI BIOS installation check */
__asm__ ( FLAT_FAR_CALL_ESI
"pushfl\n\t"
"popl %%esi\n\t"
: "=a" ( present ), "=b" ( revision ), "=c" ( max_bus ),
"=d" ( signature ), "=S" ( flags )
2005-04-12 15:31:36 +02:00
: "a" ( ( PCIBIOS_PCI_FUNCTION_ID << 8 )
+ PCIBIOS_PCI_BIOS_PRESENT ),
"S" ( pcibios32_entry )
: "edi", "ebp" );
if ( ( flags & CF ) ||
( ( present >> 8 ) != 0 ) ||
( signature != PCI_SIGNATURE ) ) {
DBG ( "PCI BIOS installation check failed\n" );
2005-03-08 19:53:11 +01:00
return;
}
2005-04-12 15:31:36 +02:00
/* We have a PCI BIOS */
DBG ( "Found 32-bit PCI BIOS interface at %#x with %d bus(es)\n",
pcibios32_entry, max_bus + 1 );
2005-04-12 15:31:36 +02:00
have_pcibios = 1;
pci_max_bus = max_bus;
2005-04-12 15:31:36 +02:00
return;
}
INIT_FN ( INIT_PCIBIOS, find_pcibios32, NULL, NULL );
#define pcibios32_read_write( command, pci, where, value ) \
( { \
uint32_t discard_b, discard_D, discard_S; \
uint16_t ret; \
\
__asm__ ( FLAT_FAR_CALL_ESI \
"jc 1f\n\t" \
"xorl %%eax, %%eax\n\t" \
"\n1:\n\t" \
: "=a" ( ret ), "=b" ( discard_b ), \
"=c" ( value ), \
"=S" ( discard_S ), "=D" ( discard_D ) \
: "a" ( ( PCIBIOS_PCI_FUNCTION_ID << 8 ) \
+ command ), \
"b" ( ( pci->bus << 8 ) | pci->devfn ), \
"c" ( value ), "D" ( where ), \
"S" ( pcibios32_entry ) \
2005-04-12 15:31:36 +02:00
: "edx", "ebp" ); \
\
( ret >> 8 ); \
} )
#define pcibios_read_write pcibios32_read_write
#endif /* KEEP_IT_REAL */
static inline int pcibios_read_config_byte ( struct pci_device *pci,
unsigned int where,
uint8_t *value ) {
return pcibios_read_write ( PCIBIOS_READ_CONFIG_BYTE,
pci, where, *value );
}
static inline int pcibios_read_config_word ( struct pci_device *pci,
unsigned int where,
uint16_t *value ) {
return pcibios_read_write ( PCIBIOS_READ_CONFIG_WORD,
pci, where, *value );
}
static inline int pcibios_read_config_dword ( struct pci_device *pci,
unsigned int where,
uint32_t *value ) {
return pcibios_read_write ( PCIBIOS_READ_CONFIG_DWORD,
pci, where, *value );
}
static inline int pcibios_write_config_byte ( struct pci_device *pci,
unsigned int where,
uint8_t value ) {
return pcibios_read_write ( PCIBIOS_WRITE_CONFIG_BYTE,
pci, where, value );
}
static inline int pcibios_write_config_word ( struct pci_device *pci,
unsigned int where,
uint16_t value ) {
return pcibios_read_write ( PCIBIOS_WRITE_CONFIG_WORD,
pci, where, value );
}
static inline int pcibios_write_config_dword ( struct pci_device *pci,
unsigned int where,
uint32_t value ) {
return pcibios_read_write ( PCIBIOS_WRITE_CONFIG_DWORD,
pci, where, value );
}
/*
* Functions for accessing PCI configuration space via the PCI BIOS if
* present, otherwise directly via type 1 accesses.
*
*/
int pci_read_config_byte ( struct pci_device *pci, unsigned int where,
uint8_t *value ) {
return have_pcibios ?
pcibios_read_config_byte ( pci, where, value ) :
pcidirect_read_config_byte ( pci, where, value );
}
int pci_read_config_word ( struct pci_device *pci, unsigned int where,
uint16_t *value ) {
return have_pcibios ?
pcibios_read_config_word ( pci, where, value ) :
pcidirect_read_config_word ( pci, where, value );
}
int pci_read_config_dword ( struct pci_device *pci, unsigned int where,
uint32_t *value ) {
return have_pcibios ?
pcibios_read_config_dword ( pci, where, value ) :
pcidirect_read_config_dword ( pci, where, value );
}
int pci_write_config_byte ( struct pci_device *pci, unsigned int where,
uint8_t value ) {
return have_pcibios ?
pcibios_write_config_byte ( pci, where, value ) :
pcidirect_write_config_byte ( pci, where, value );
}
int pci_write_config_word ( struct pci_device *pci, unsigned int where,
uint16_t value ) {
return have_pcibios ?
pcibios_write_config_word ( pci, where, value ) :
pcidirect_write_config_word ( pci, where, value );
}
int pci_write_config_dword ( struct pci_device *pci, unsigned int where,
uint32_t value ) {
return have_pcibios ?
pcibios_write_config_dword ( pci, where, value ) :
pcidirect_write_config_dword ( pci, where, value );
}
2005-04-12 15:31:36 +02:00
unsigned long pci_bus_base ( struct pci_device *pci __unused ) {
/* architecturally this must be 0 */
return 0;
2005-03-08 19:53:11 +01:00
}