pcibios.c revision 2f7420faa558a13001c2f19ce4804ac3862f9857
/* $Id$ */
/** @file
* PCI BIOS support.
*/
/*
* Copyright (C) 2004-2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
#include <stdint.h>
#include <string.h>
#include "biosint.h"
#include "inlines.h"
#if DEBUG_PCI
#else
# define BX_DEBUG_PCI(...)
#endif
/* PCI function codes. */
enum pci_func {
};
enum pci_error {
};
// @todo: merge with system.c
/* The 16-bit PCI BIOS service must be callable from both real and protected
* mode. In protected mode, the caller must set the CS selector base to F0000h
* (but the CS selector value is not specified!). The caller does not always
* provide a DS which covers the BIOS segment.
*
* Unlike APM, there are no provisions for the 32-bit PCI BIOS interface
* calling the 16-bit implementation.
*
* The PCI Firmware Specification requires that the PCI BIOS service is called
* with at least 1,024 bytes of stack space available, that interrupts are not
* enabled during execution, and that the routines are re-entrant.
*
* Implementation notes:
* - The PCI BIOS interface already uses certain 32-bit registers even in
* may be used by helper routines (notably for 32-bit port I/O).
*/
#define PCI_CFG_ADDR 0xCF8
#define PCI_CFG_DATA 0xCFC
#ifdef __386__
/* The stack layout is different in 32-bit mode. */
typedef struct {
} pci_regs_t;
/* In 32-bit mode, don't do any output; not technically impossible but needs
* a lot of extra code.
*/
#define BX_INFO(...)
#define BX_DEBUG_PCI(...)
#else
typedef struct {
} pci_regs_t;
#endif
#ifdef __386__
/* 32-bit code can just use the compiler intrinsics. */
#else
//@todo: merge with AHCI code
/* Warning: Destroys high bits of EAX. */
".386" \
"in eax, dx" \
"mov dx, ax" \
"shr eax, 16" \
"xchg ax, dx" \
/* Warning: Destroys high bits of EAX. */
".386" \
"xchg ax, cx" \
"shl eax, 16" \
"mov ax, cx" \
"out dx, eax" \
#endif
/* PCI IRQ routing expansion buffer descriptor. */
typedef struct {
/* Defined in assembler module .*/
extern char pci_routing_table[];
extern uint16_t pci_routing_table_size;
/* Write the CONFIG_ADDRESS register to prepare for data access. Requires
* the register offset to be DWORD aligned (low two bits clear). Warning:
* destroys high bits of EAX.
*/
#pragma aux pci16_w_addr = \
".386" \
"movzx eax, ax" \
"shl eax, 8" \
"or eax, 80000000h" \
"mov al, bl" \
"out dx, eax" \
* This is largely a wrapper to avoid excessive inlining.
*/
{
}
/* Selected configuration space offsets. */
#define PCI_VEN_ID 0x00
#define PCI_DEV_ID 0x02
#define PCI_REV_ID 0x08
#define PCI_CLASS_CODE 0x09
#define PCI_HEADER_TYPE 0x0E
#define PCI_BRIDGE_SUBORD 0x1A
/* To avoid problems with 16-bit code, we reserve the last possible
* probing will end.
*/
#define BUSDEVFN_NOT_FOUND 0xFFFF
/* In the search algorithm, we decrement the device index every time
* a matching device is found. If the requested device is indeed found,
* the index will have decremented down to -1/0xFFFF.
*/
#define INDEX_DEV_FOUND 0xFFFF
/* Find a specified PCI device, either by vendor+device ID or class.
* If index is non-zero, the n-th device will be located.
*
* Note: This function is somewhat performance critical; since it may
* generate a high number of port I/O accesses, it can take a significant
* amount of time in cases where the caller is looking for a number of
* non-present devices.
*/
{
int step;
int found;
if (search_class) {
BX_DEBUG_PCI("PCI: Find class %08lX index %u\n",
search_item, index);
} else
BX_DEBUG_PCI("PCI: Find device %04X:%04X index %u\n",
bus_dev_fn = 0; /* Start at the beginning. */
max_bus = 0; /* Initially assume primary bus only. */
do {
/* For the first function of a device, read the device's header type.
* If the header type has all bits set, there's no device. A PCI
* multi-function device must implement function 0 and the header type
* will be something other than 0xFF. If the header type has the high
* bit clear, there is a device but it's not multi-function, so we can
* skip probing the next 7 sub-functions.
*/
if ((bus_dev_fn & 7) == 0) {
if (hdr_type == 0xFF) {
continue;
}
if (hdr_type & 0x80)
else
}
/* If the header type indicates a bus, we're interested. The secondary
* and subordinate bus numbers will indicate which buses are present;
* thus we can determine the highest bus number. In the common case,
* there will be only the primary bus (i.e. bus 0) and we can avoid
* looking at the remaining 255 theoretically present buses. This check
* only needs to be done on the primary bus, since bridges must report
* all bridges potentially behind them.
*/
/* Read the subordinate (last) bridge number. */
if (subordinate > max_bus)
}
/* Select the appropriate register. */
found = 0;
/* Only 3 bytes are compared for class searches. */
if (search_class)
data >>= 8;
#if 0
#endif
if (data == search_item)
found = 1;
/* If device was found but index is non-zero, decrement index and
* continue looking. If requested device was found, index will be -1!
*/
break;
bus_dev_fn += step;
if (index == INDEX_DEV_FOUND)
}
{
CLEAR_CF();
switch (GET_AL()) {
case PCI_BIOS_PRESENT:
//@todo: return true max bus # in CL
CX = 0; /* Maximum bus number. */
break;
case FIND_PCI_DEVICE:
/* Vendor ID FFFFh is reserved so that non-present devices can
* be easily detected.
*/
if (DX == 0xFFFF) {
SET_CF();
} else {
if (device == BUSDEVFN_NOT_FOUND) {
SET_CF();
} else {
}
}
break;
case FIND_PCI_CLASS_CODE:
if (device == BUSDEVFN_NOT_FOUND) {
SET_CF();
} else {
}
break;
case READ_CONFIG_BYTE:
case READ_CONFIG_WORD:
case READ_CONFIG_DWORD:
case WRITE_CONFIG_BYTE:
case WRITE_CONFIG_WORD:
case WRITE_CONFIG_DWORD:
if (DI >= 256) {
SET_CF();
} else {
switch (GET_AL()) {
case READ_CONFIG_BYTE:
break;
case READ_CONFIG_WORD:
break;
case READ_CONFIG_DWORD:
break;
case WRITE_CONFIG_BYTE:
break;
case WRITE_CONFIG_WORD:
break;
case WRITE_CONFIG_DWORD:
break;
}
}
break;
case GET_IRQ_ROUTING:
BX_DEBUG_PCI("PCI: Route Buf %04X:%04X size %04X (at %04X:%04X)\n",
SET_CF();
} else {
/* IRQs 9 and 11 are PCI only. */
}
break;
default:
SET_CF();
}
}