628 lines
20 KiB
C++
628 lines
20 KiB
C++
//++
|
|
//
|
|
// Copyright (c) Microsoft Corporation
|
|
//
|
|
// Module Name:
|
|
//
|
|
// blpci.cpp
|
|
//
|
|
// Abstract:
|
|
//
|
|
// This module implements PCI support for the boot loader environment.
|
|
//
|
|
//--
|
|
|
|
|
|
#include "bl.h"
|
|
|
|
#define PCI_MAX_BUSES 128
|
|
#define PCI_MAX_DEVICES 32
|
|
#define PCI_MAX_FUNCTIONS 8
|
|
|
|
#define PCI_ADDRESS_PORT 0x0CF8
|
|
#define PCI_DATA_PORT 0x0CFC
|
|
|
|
#define PCI_INVALID_VENDORID 0xFFFF
|
|
|
|
#define PCI_MULTI_FUNCTION 0x80
|
|
#define PCI_TYPE_MASK 0x7F
|
|
#define PCI_DEVICE 0x00
|
|
#define PCI_BRIDGE 0x01
|
|
|
|
#define PCI_DEVICE_BASE_ADDRESS_COUNT 6
|
|
|
|
#define PCI_BASE_ADDRESS_MEMORY 0
|
|
#define PCI_BASE_ADDRESS_IO 1
|
|
|
|
#define PCI_BASE_ADDRESS_MEMORY_32 0
|
|
#define PCI_BASE_ADDRESS_MEMORY_32_1MB 1
|
|
#define PCI_BASE_ADDRESS_MEMORY_64 2
|
|
|
|
#define PCI_BASE_ADDRESS_SHIFT 4
|
|
#define PCI_BASE_ADDRESS_FLAGS_MASK 0xF
|
|
|
|
#pragma pack(1)
|
|
|
|
typedef struct _PCI_CONFIGURATION_SPACE_HEADER {
|
|
UINT16 VendorId;
|
|
UINT16 DeviceId;
|
|
UINT16 Command;
|
|
UINT16 Status;
|
|
UINT8 RevisionId;
|
|
UINT8 ProgrammingInterface;
|
|
UINT8 SubClass;
|
|
UINT8 BaseClass;
|
|
UINT8 CacheLineSize;
|
|
UINT8 LatencyTimer;
|
|
UINT8 HeaderType;
|
|
UINT8 BIST;
|
|
|
|
union {
|
|
|
|
struct {
|
|
UINT32 BaseAddressRegister[PCI_DEVICE_BASE_ADDRESS_COUNT];
|
|
UINT32 CardBusCISPointer;
|
|
UINT16 SubsystemVendorId;
|
|
UINT16 SubsystemId;
|
|
UINT32 ExpansionRomBaseAddress;
|
|
UINT32 Reserved[2];
|
|
UINT8 InterruptLine;
|
|
UINT8 InterruptPin;
|
|
UINT8 MinimumGrant;
|
|
UINT8 MaximumLatency;
|
|
UINT8 __End;
|
|
} Device;
|
|
|
|
UINT8 DynamicStart;
|
|
} u1;
|
|
|
|
} PCI_CONFIGURATION_SPACE_HEADER, *PPCI_CONFIGURATION_SPACE_HEADER;
|
|
|
|
C_ASSERT(FIELD_OFFSET(PCI_CONFIGURATION_SPACE_HEADER, u1.DynamicStart) == 0x10);
|
|
C_ASSERT(FIELD_OFFSET(PCI_CONFIGURATION_SPACE_HEADER, u1.Device.__End) == 0x40);
|
|
|
|
typedef struct _PCI_CONFIG_ADDRESS {
|
|
|
|
union {
|
|
|
|
struct {
|
|
UINT32 Zero:2;
|
|
UINT32 RegisterNumber:6;
|
|
UINT32 FunctionNumber:3;
|
|
UINT32 DeviceNumber:5;
|
|
UINT32 BusNumber:8;
|
|
UINT32 Reserved:7;
|
|
UINT32 Enable:1;
|
|
} s1;
|
|
|
|
UINT32 Value;
|
|
} u1;
|
|
} PCI_CONFIG_ADDRESS, *PPCI_CONFIG_ADDRESS;
|
|
|
|
C_ASSERT(sizeof(PCI_CONFIG_ADDRESS) == sizeof(UINT32));
|
|
|
|
typedef struct _PCI_BASE_ADDRESS {
|
|
|
|
union {
|
|
|
|
struct {
|
|
UINT32 Type:1;
|
|
} Common;
|
|
|
|
struct {
|
|
UINT32 Zero:1;
|
|
UINT32 Type:2;
|
|
UINT32 Prefetch:1;
|
|
UINT32 Base:28;
|
|
} Memory;
|
|
|
|
struct {
|
|
UINT64 Zero:1;
|
|
UINT64 Type:2;
|
|
UINT64 Prefetch:1;
|
|
UINT64 Base:60;
|
|
} Memory64;
|
|
|
|
struct {
|
|
UINT32 One:1;
|
|
UINT32 Reserved:1;
|
|
UINT32 Base:30;
|
|
} Io;
|
|
} u1;
|
|
} PCI_BASE_ADDRESS, *PPCI_BASE_ADDRESS;
|
|
|
|
C_ASSERT(sizeof(((PPCI_BASE_ADDRESS) 0)->u1.Memory) == sizeof(UINT32));
|
|
C_ASSERT(sizeof(((PPCI_BASE_ADDRESS) 0)->u1.Memory64) == sizeof(UINT64));
|
|
C_ASSERT(sizeof(((PPCI_BASE_ADDRESS) 0)->u1.Io) == sizeof(UINT32));
|
|
|
|
#pragma pack()
|
|
|
|
PCI_INSTALLATION_CHECK BlPciInstallationCheck;
|
|
UINT32 BlPciOhci1394BaseAddress;
|
|
|
|
BOOLEAN
|
|
BlPciCheckBios(
|
|
PPCI_INSTALLATION_CHECK PciInstallationCheck
|
|
)
|
|
|
|
//++
|
|
//
|
|
// Routine Description:
|
|
//
|
|
// This function checks for the presence of PCI BIOS.
|
|
//
|
|
// Arguments:
|
|
//
|
|
// PciInstallationCheck - Receives PCI BIOS information.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// TRUE, if PCI BIOS is present.
|
|
// FALSE, otherwise.
|
|
//
|
|
//--
|
|
|
|
{
|
|
BL_LEGACY_CALL_CONTEXT Context;
|
|
|
|
//
|
|
// Call PCI detection service.
|
|
//
|
|
|
|
BlRtlZeroMemory(&Context, sizeof(Context));
|
|
|
|
Context.eax = 0xB101;
|
|
|
|
BlRtlCallLegacyInterruptService(0x1A,
|
|
&Context,
|
|
&Context);
|
|
|
|
//
|
|
// If CF is set, AH is not zero, or if the signature is not ' ICP', then
|
|
// there is no PCI BIOS.
|
|
//
|
|
|
|
if (((Context.eflags & RFLAGS_CF) != 0) ||
|
|
(((Context.eax >> 8) & 0xFF) != 0) ||
|
|
(Context.edx != 0x20494350)) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Populate the provided installation check structure and return success.
|
|
//
|
|
|
|
PciInstallationCheck->Eax = Context.eax;
|
|
PciInstallationCheck->Ebx = Context.ebx;
|
|
PciInstallationCheck->Ecx = Context.ecx;
|
|
PciInstallationCheck->Edx = Context.edx;
|
|
PciInstallationCheck->HardwareCharacteristics = (UINT8) (Context.eax & 0xFF);
|
|
PciInstallationCheck->LastBusNumber = (UINT8) (Context.ecx & 0xFF);
|
|
PciInstallationCheck->MajorVersion = (UINT8) ((Context.ebx >> 8) & 0xFF);
|
|
PciInstallationCheck->MinorVersion = (UINT8) (Context.ebx & 0xFF);
|
|
PciInstallationCheck->ProtectedModeEntryPoint = Context.edi;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
UINT32
|
|
BlPciReadConfigurationRegister(
|
|
UINT8 BusNumber,
|
|
UINT8 DeviceNumber,
|
|
UINT8 FunctionNumber,
|
|
UINT8 RegisterNumber
|
|
)
|
|
|
|
//++
|
|
//
|
|
// Routine Description:
|
|
//
|
|
// This function reads from the specified PCI configuration register.
|
|
//
|
|
// Arguments:
|
|
//
|
|
// BusNumber - Supplies the PCI bus number.
|
|
//
|
|
// DeviceNumber - Supplies the PCI device number.
|
|
//
|
|
// FunctionNumber - Supplies the PCI function number.
|
|
//
|
|
// RegisterNumber - Supplies the PCI configuration register number.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// The value of the configuration register.
|
|
//
|
|
//--
|
|
|
|
{
|
|
PCI_CONFIG_ADDRESS ConfigAddress;
|
|
UINT32 Value;
|
|
|
|
BLASSERT(DeviceNumber < PCI_MAX_DEVICES);
|
|
BLASSERT(FunctionNumber < PCI_MAX_FUNCTIONS);
|
|
BLASSERT((RegisterNumber % sizeof(UINT32)) == 0);
|
|
|
|
BlRtlZeroMemory(&ConfigAddress, sizeof(ConfigAddress));
|
|
|
|
ConfigAddress.u1.s1.BusNumber = BusNumber;
|
|
ConfigAddress.u1.s1.DeviceNumber = DeviceNumber;
|
|
ConfigAddress.u1.s1.FunctionNumber = FunctionNumber;
|
|
ConfigAddress.u1.s1.RegisterNumber = RegisterNumber >> 2;
|
|
ConfigAddress.u1.s1.Enable = 1;
|
|
|
|
BlRtlWritePort32(PCI_ADDRESS_PORT, ConfigAddress.u1.Value);
|
|
|
|
Value = BlRtlReadPort32(PCI_DATA_PORT);
|
|
|
|
return Value;
|
|
}
|
|
|
|
VOID
|
|
BlPciWriteConfigurationRegister(
|
|
UINT8 BusNumber,
|
|
UINT8 DeviceNumber,
|
|
UINT8 FunctionNumber,
|
|
UINT8 RegisterNumber,
|
|
UINT32 Value
|
|
)
|
|
|
|
//++
|
|
//
|
|
// Routine Description:
|
|
//
|
|
// This function writes to the specified PCI configuration register.
|
|
//
|
|
// Arguments:
|
|
//
|
|
// BusNumber - Supplies the PCI bus number.
|
|
//
|
|
// DeviceNumber - Supplies the PCI device number.
|
|
//
|
|
// FunctionNumber - Supplies the PCI function number.
|
|
//
|
|
// RegisterNumber - Supplies the PCI configuration register number.
|
|
//
|
|
// Value - Supplies the value to write.
|
|
//
|
|
//--
|
|
|
|
{
|
|
PCI_CONFIG_ADDRESS ConfigAddress;
|
|
|
|
BLASSERT(DeviceNumber < PCI_MAX_DEVICES);
|
|
BLASSERT(FunctionNumber < PCI_MAX_FUNCTIONS);
|
|
BLASSERT((RegisterNumber % sizeof(UINT32)) == 0);
|
|
|
|
BlRtlZeroMemory(&ConfigAddress, sizeof(ConfigAddress));
|
|
|
|
ConfigAddress.u1.s1.BusNumber = BusNumber;
|
|
ConfigAddress.u1.s1.DeviceNumber = DeviceNumber;
|
|
ConfigAddress.u1.s1.FunctionNumber = FunctionNumber;
|
|
ConfigAddress.u1.s1.RegisterNumber = RegisterNumber >> 2;
|
|
ConfigAddress.u1.s1.Enable = 1;
|
|
|
|
BlRtlWritePort32(PCI_ADDRESS_PORT, ConfigAddress.u1.Value);
|
|
|
|
BlRtlWritePort32(PCI_DATA_PORT, Value);
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
BlPciReadConfigurationSpace(
|
|
UINT8 BusNumber,
|
|
UINT8 DeviceNumber,
|
|
UINT8 FunctionNumber,
|
|
UINT8 RegisterNumber,
|
|
PVOID Buffer,
|
|
UINT16 BufferSize
|
|
)
|
|
|
|
//++
|
|
//
|
|
// Routine Description:
|
|
//
|
|
// This function reads the specified PCI configuration space range.
|
|
//
|
|
// Arguments:
|
|
//
|
|
// BusNumber - Supplies the PCI bus number.
|
|
//
|
|
// DeviceNumber - Supplies the PCI device number.
|
|
//
|
|
// FunctionNumber - Supplies the PCI function number.
|
|
//
|
|
// RegisterNumber - Supplies the PCI configuration register number.
|
|
//
|
|
// Buffer - Receives configuration data.
|
|
//
|
|
// BufferSize - Supplies the number of bytes to read.
|
|
//
|
|
//--
|
|
|
|
{
|
|
UINT16 Count;
|
|
UINT16 Index;
|
|
|
|
BLASSERT((RegisterNumber % sizeof(UINT32)) == 0);
|
|
BLASSERT((BufferSize % sizeof(UINT32)) == 0);
|
|
|
|
Count = BufferSize / sizeof(UINT32);
|
|
|
|
for (Index = 0; Index < Count; Index += 1) {
|
|
|
|
((PUINT32) Buffer)[Index] = BlPciReadConfigurationRegister(BusNumber,
|
|
DeviceNumber,
|
|
FunctionNumber,
|
|
(UINT8) (RegisterNumber + (Index * sizeof(UINT32))));
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
BlPciScanDevices(
|
|
VOID
|
|
)
|
|
|
|
//++
|
|
//
|
|
// Routine Description:
|
|
//
|
|
// This function scans all PCI buses on the system.
|
|
//
|
|
//--
|
|
|
|
{
|
|
UINT64 Address;
|
|
PPCI_BASE_ADDRESS BaseAddress;
|
|
UINT8 BaseAddressRegister;
|
|
UINT8 BusNumber;
|
|
PCI_CONFIGURATION_SPACE_HEADER Config;
|
|
UINT8 DeviceNumber;
|
|
UINT8 FunctionNumber;
|
|
UINT8 Index;
|
|
UINT8 NodeType;
|
|
UINT32 OldValue;
|
|
UINT32 Size;
|
|
|
|
for (BusNumber = 0; BusNumber <= BlPciInstallationCheck.LastBusNumber; BusNumber += 1) {
|
|
|
|
for (DeviceNumber = 0; DeviceNumber < PCI_MAX_DEVICES; DeviceNumber += 1) {
|
|
|
|
for (FunctionNumber = 0; FunctionNumber < PCI_MAX_FUNCTIONS; FunctionNumber += 1) {
|
|
|
|
BlRtlZeroMemory(&Config, sizeof(Config));
|
|
|
|
Config.VendorId = PCI_INVALID_VENDORID;
|
|
|
|
BlPciReadConfigurationSpace(BusNumber,
|
|
DeviceNumber,
|
|
FunctionNumber,
|
|
0,
|
|
&Config,
|
|
FIELD_OFFSET(PCI_CONFIGURATION_SPACE_HEADER, u1.DynamicStart));
|
|
|
|
if ((Config.VendorId == PCI_INVALID_VENDORID) || (Config.VendorId == 0)) {
|
|
|
|
continue;
|
|
}
|
|
|
|
NodeType = Config.HeaderType & PCI_TYPE_MASK;
|
|
|
|
switch (NodeType) {
|
|
|
|
case PCI_DEVICE: {
|
|
|
|
#if PCI_VERBOSE
|
|
|
|
BlRtlPrintf("PCI: %02x:%02x:%02x: Device %04x:%04x [BC=%02x SC=%02x PI=%02x]\n",
|
|
BusNumber,
|
|
DeviceNumber,
|
|
FunctionNumber,
|
|
Config.VendorId,
|
|
Config.DeviceId,
|
|
Config.BaseClass,
|
|
Config.SubClass,
|
|
Config.ProgrammingInterface
|
|
);
|
|
|
|
#endif
|
|
|
|
BlPciReadConfigurationSpace(BusNumber,
|
|
DeviceNumber,
|
|
FunctionNumber,
|
|
FIELD_OFFSET(PCI_CONFIGURATION_SPACE_HEADER, u1.DynamicStart),
|
|
&Config.u1.DynamicStart,
|
|
FIELD_OFFSET(PCI_CONFIGURATION_SPACE_HEADER, u1.Device.__End) - FIELD_OFFSET(PCI_CONFIGURATION_SPACE_HEADER, u1.DynamicStart));
|
|
|
|
for (Index = 0; Index < PCI_DEVICE_BASE_ADDRESS_COUNT; Index += 1) {
|
|
|
|
BaseAddress = (PPCI_BASE_ADDRESS) &Config.u1.Device.BaseAddressRegister[Index];
|
|
BaseAddressRegister = (UINT8) FIELD_OFFSET(PCI_CONFIGURATION_SPACE_HEADER, u1.Device.BaseAddressRegister[Index]);
|
|
Address = 0;
|
|
|
|
switch (BaseAddress->u1.Common.Type) {
|
|
|
|
case PCI_BASE_ADDRESS_MEMORY: {
|
|
|
|
SATISFY_OVERZEALOUS_COMPILER(Size = 0);
|
|
|
|
switch (BaseAddress->u1.Memory.Type) {
|
|
|
|
case PCI_BASE_ADDRESS_MEMORY_32:
|
|
case PCI_BASE_ADDRESS_MEMORY_32_1MB: {
|
|
|
|
Address = BaseAddress->u1.Memory.Base << PCI_BASE_ADDRESS_SHIFT;
|
|
|
|
OldValue = Config.u1.Device.BaseAddressRegister[Index];
|
|
|
|
BlPciWriteConfigurationRegister(BusNumber,
|
|
DeviceNumber,
|
|
FunctionNumber,
|
|
BaseAddressRegister,
|
|
(UINT32) -1);
|
|
|
|
Size = BlPciReadConfigurationRegister(BusNumber,
|
|
DeviceNumber,
|
|
FunctionNumber,
|
|
BaseAddressRegister);
|
|
|
|
BlPciWriteConfigurationRegister(BusNumber,
|
|
DeviceNumber,
|
|
FunctionNumber,
|
|
BaseAddressRegister,
|
|
OldValue);
|
|
|
|
Size &= ~(PCI_BASE_ADDRESS_FLAGS_MASK);
|
|
|
|
Size = (~Size) + 1;
|
|
|
|
break;
|
|
}
|
|
|
|
case PCI_BASE_ADDRESS_MEMORY_64: {
|
|
|
|
Address = BaseAddress->u1.Memory64.Base << PCI_BASE_ADDRESS_SHIFT;
|
|
|
|
OldValue = Config.u1.Device.BaseAddressRegister[Index];
|
|
|
|
BlPciWriteConfigurationRegister(BusNumber,
|
|
DeviceNumber,
|
|
FunctionNumber,
|
|
BaseAddressRegister,
|
|
(UINT32) -1);
|
|
|
|
Size = BlPciReadConfigurationRegister(BusNumber,
|
|
DeviceNumber,
|
|
FunctionNumber,
|
|
BaseAddressRegister);
|
|
|
|
BlPciWriteConfigurationRegister(BusNumber,
|
|
DeviceNumber,
|
|
FunctionNumber,
|
|
BaseAddressRegister,
|
|
OldValue);
|
|
|
|
Size &= ~(PCI_BASE_ADDRESS_FLAGS_MASK);
|
|
|
|
Size = (~Size) + 1;
|
|
|
|
Index += 1;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Address != 0) {
|
|
|
|
BLASSERT(Size > 0);
|
|
|
|
#if PCI_VERBOSE
|
|
|
|
BlRtlPrintf("PCI: %02x:%02x:%02x: IO Memory [%016I64x ... %016I64x]\n",
|
|
BusNumber,
|
|
DeviceNumber,
|
|
FunctionNumber,
|
|
Address,
|
|
Address + Size - 1);
|
|
|
|
#endif
|
|
|
|
if ((Address >= LEGACY_MEMORY_LIMIT) &&
|
|
((Address + Size) > Address) &&
|
|
((Address + Size) <= 0x100000000UI64)
|
|
) {
|
|
|
|
BlMmMapVirtualRange((PVOID) (ULONG_PTR) Address,
|
|
(PVOID) (ULONG_PTR) Address,
|
|
Size,
|
|
TRUE,
|
|
(BOOLEAN) BaseAddress->u1.Memory.Prefetch,
|
|
FALSE);
|
|
}
|
|
|
|
//
|
|
// Check if this memory range maps OHCI 1394 registers.
|
|
//
|
|
|
|
if ((Config.BaseClass == 0x0C) &&
|
|
(Config.SubClass == 0x00) &&
|
|
(Config.ProgrammingInterface == 0x10) &&
|
|
(BlPciOhci1394BaseAddress == 0)) {
|
|
|
|
BlPciOhci1394BaseAddress = (UINT32) Address;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case PCI_BRIDGE: {
|
|
|
|
#if PCI_VERBOSE
|
|
|
|
BlRtlPrintf("PCI: %02x:%02x:%02x: Bridge %04x:%04x\n",
|
|
BusNumber,
|
|
DeviceNumber,
|
|
FunctionNumber,
|
|
Config.VendorId,
|
|
Config.DeviceId);
|
|
|
|
#endif
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ((Config.HeaderType & PCI_MULTI_FUNCTION) == 0) {
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
VOID
|
|
BlPciInitialize(
|
|
VOID
|
|
)
|
|
|
|
//++
|
|
//
|
|
// Routine Description:
|
|
//
|
|
// This function initializes PCI support.
|
|
//
|
|
//--
|
|
|
|
{
|
|
if (BlPciCheckBios(&BlPciInstallationCheck) == FALSE) {
|
|
|
|
BlRtlPrintf("pci: PCI BIOS not detected!\n");
|
|
BlRtlHalt();
|
|
}
|
|
|
|
#if PCI_VERBOSE
|
|
|
|
BlRtlPrintf("PCI: PCI BIOS detected.\n"
|
|
"PCI: Version : %u.%u\n"
|
|
"PCI: Last Bus Number : %u\n",
|
|
BlPciInstallationCheck.MajorVersion,
|
|
BlPciInstallationCheck.MinorVersion,
|
|
BlPciInstallationCheck.LastBusNumber);
|
|
|
|
#endif
|
|
|
|
BlPciScanDevices();
|
|
|
|
return;
|
|
}
|