singrdk/base/boot/SingLdrPc/blpci.cpp

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;
}