windows-nt/Source/XPSP1/NT/base/busdrv/pci/ar_memio.c

2781 lines
72 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1997-2000 Microsoft Corporation
Module Name:
ar_memio.c
Abstract:
This module implements the PCI Memory and IO resource Arbiters.
Most functionality for these two arbiters is common and distinguished
by the "context".
Author:
Andrew Thornton (andrewth) 21-May-1997
Revision History:
--*/
#include "pcip.h"
#define BUGFEST_HACKS
#define ARMEMIO_VERSION 0
//
// Flags for WorkSpace
//
#define PORT_ARBITER_PREPROCESSED 0x00000001
#define PORT_ARBITER_IMPROVISED_DECODE 0x00000002
#define PORT_ARBITER_ISA_BIT_SET 0x00000004
#define PORT_ARBITER_BRIDGE_WINDOW 0x00000008
//
// ALLOCATE_ALIASES - this turns on the allocation of 10 & 12 bit decoded
// aliases.
//
#define ALLOCATE_ALIASES 1
#define IGNORE_PREFETCH_FOR_LEGACY_REPORTED 1
#define PASSIVE_DECODE_SUPPORTED 1
#define PREFETCHABLE_SUPPORTED 1
#define ISA_BIT_SUPPORTED 1
//
// Flags for range attributes
//
#define MEMORY_RANGE_ROM 0x10
//
// Constants
//
#define PCI_BRIDGE_WINDOW_GRANULARITY 0x1000
#define PCI_BRIDGE_ISA_BIT_STRIDE 0x400
#define PCI_BRIDGE_ISA_BIT_WIDTH 0x100
#define PCI_BRIDGE_ISA_BIT_MAX 0xFFFF
//
// Static data
//
ARBITER_ORDERING PciBridgeOrderings[] = {
{ 0x10000, MAXULONGLONG },
{ 0, 0xFFFF }
};
ARBITER_ORDERING_LIST PciBridgeOrderingList = {
sizeof(PciBridgeOrderings) / sizeof (ARBITER_ORDERING),
sizeof(PciBridgeOrderings) / sizeof (ARBITER_ORDERING),
PciBridgeOrderings
};
//
// Prototypes for routines exposed only thru the "interface"
// mechanism.
//
NTSTATUS
ario_Constructor(
PVOID DeviceExtension,
PVOID PciInterface,
PVOID InterfaceSpecificData,
USHORT Version,
USHORT Size,
PINTERFACE InterfaceReturn
);
NTSTATUS
ario_Initializer(
IN PPCI_ARBITER_INSTANCE Instance
);
NTSTATUS
armem_Constructor(
PVOID DeviceExtension,
PVOID PciInterface,
PVOID InterfaceSpecificData,
USHORT Version,
USHORT Size,
PINTERFACE InterfaceReturn
);
NTSTATUS
armem_Initializer(
IN PPCI_ARBITER_INSTANCE Instance
);
PCI_INTERFACE ArbiterInterfaceMemory = {
&GUID_ARBITER_INTERFACE_STANDARD, // InterfaceType
sizeof(ARBITER_INTERFACE), // MinSize
ARMEMIO_VERSION, // MinVersion
ARMEMIO_VERSION, // MaxVersion
PCIIF_FDO, // Flags
0, // ReferenceCount
PciArb_Memory, // Signature
armem_Constructor, // Constructor
armem_Initializer // Instance Initializer
};
PCI_INTERFACE ArbiterInterfaceIo = {
&GUID_ARBITER_INTERFACE_STANDARD, // InterfaceType
sizeof(ARBITER_INTERFACE), // MinSize
ARMEMIO_VERSION, // MinVersion
ARMEMIO_VERSION, // MaxVersion
PCIIF_FDO, // Flags
0, // ReferenceCount
PciArb_Io, // Signature
ario_Constructor, // Constructor
ario_Initializer // Instance Initializer
};
//
// Arbiter helper functions.
//
NTSTATUS
armemio_UnpackRequirement(
IN PIO_RESOURCE_DESCRIPTOR Descriptor,
OUT PULONGLONG Minimum,
OUT PULONGLONG Maximum,
OUT PULONG Length,
OUT PULONG Alignment
);
NTSTATUS
armemio_PackResource(
IN PIO_RESOURCE_DESCRIPTOR Requirement,
IN ULONGLONG Start,
OUT PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor
);
NTSTATUS
armemio_UnpackResource(
IN PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor,
OUT PULONGLONG Start,
OUT PULONG Length
);
NTSTATUS
armemio_ScoreRequirement(
IN PIO_RESOURCE_DESCRIPTOR Descriptor
);
VOID
ario_BacktrackAllocation(
IN PARBITER_INSTANCE Arbiter,
IN PARBITER_ALLOCATION_STATE State
);
BOOLEAN
ario_FindSuitableRange(
PARBITER_INSTANCE Arbiter,
PARBITER_ALLOCATION_STATE State
);
BOOLEAN
ario_GetNextAlias(
ULONG IoDescriptorFlags,
ULONGLONG LastAlias,
PULONGLONG NextAlias
);
BOOLEAN
ario_IsAliasedRangeAvailable(
PARBITER_INSTANCE Arbiter,
PARBITER_ALLOCATION_STATE State
);
VOID
ario_AddAllocation(
IN PARBITER_INSTANCE Arbiter,
IN PARBITER_ALLOCATION_STATE State
);
BOOLEAN
ario_OverrideConflict(
IN PARBITER_INSTANCE Arbiter,
IN PARBITER_ALLOCATION_STATE State
);
NTSTATUS
ario_PreprocessEntry(
IN PARBITER_INSTANCE Arbiter,
IN PARBITER_ALLOCATION_STATE State
);
NTSTATUS
armem_StartArbiter(
IN PARBITER_INSTANCE Arbiter,
IN PCM_RESOURCE_LIST StartResources
);
NTSTATUS
armem_PreprocessEntry(
IN PARBITER_INSTANCE Arbiter,
IN PARBITER_ALLOCATION_STATE State
);
BOOLEAN
armem_GetNextAllocationRange(
IN PARBITER_INSTANCE Arbiter,
IN PARBITER_ALLOCATION_STATE State
);
BOOLEAN
ario_GetNextAllocationRange(
IN PARBITER_INSTANCE Arbiter,
IN PARBITER_ALLOCATION_STATE State
);
NTSTATUS
ario_StartArbiter(
IN PARBITER_INSTANCE Arbiter,
IN PCM_RESOURCE_LIST StartResources
);
VOID
ario_AddOrBacktrackAllocation(
IN PARBITER_INSTANCE Arbiter,
IN PARBITER_ALLOCATION_STATE State,
IN PARBITER_BACKTRACK_ALLOCATION Callback
);
BOOLEAN
armem_FindSuitableRange(
PARBITER_INSTANCE Arbiter,
PARBITER_ALLOCATION_STATE State
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, ario_Constructor)
#pragma alloc_text(PAGE, armem_Constructor)
#pragma alloc_text(PAGE, ario_Initializer)
#pragma alloc_text(PAGE, armem_Initializer)
#pragma alloc_text(PAGE, armemio_UnpackRequirement)
#pragma alloc_text(PAGE, armemio_PackResource)
#pragma alloc_text(PAGE, armemio_UnpackResource)
#pragma alloc_text(PAGE, armemio_ScoreRequirement)
#pragma alloc_text(PAGE, ario_OverrideConflict)
#pragma alloc_text(PAGE, ario_BacktrackAllocation)
#pragma alloc_text(PAGE, ario_GetNextAlias)
#pragma alloc_text(PAGE, ario_FindSuitableRange)
#pragma alloc_text(PAGE, ario_GetNextAlias)
#pragma alloc_text(PAGE, ario_IsAliasedRangeAvailable)
#pragma alloc_text(PAGE, ario_AddAllocation)
#pragma alloc_text(PAGE, ario_PreprocessEntry)
#pragma alloc_text(PAGE, armem_StartArbiter)
#pragma alloc_text(PAGE, armem_PreprocessEntry)
#pragma alloc_text(PAGE, armem_GetNextAllocationRange)
#pragma alloc_text(PAGE, ario_AddOrBacktrackAllocation)
#pragma alloc_text(PAGE, armem_FindSuitableRange)
#endif
//
// Prefetchable memory support.
//
// Prefetchable memory is device memory that can be treated like normal memory
// in that reads have no side effects and we can combine writes. The
// CM_RESOURCE_MEMORY_PREFETCHABLE flag means that the device would *like*
// prefetchable memory but normal memory is just fine as well.
//
NTSTATUS
ario_Constructor(
PVOID DeviceExtension,
PVOID PciInterface,
PVOID InterfaceSpecificData,
USHORT Version,
USHORT Size,
PINTERFACE InterfaceReturn
)
/*++
Routine Description:
Check the InterfaceSpecificData to see if this is the correct
arbiter (we already know the required interface is an arbiter
from the GUID) and if so, allocate (and reference) a context
for this interface.
Arguments:
PciInterface Pointer to the PciInterface record for this
interface type.
InterfaceSpecificData
A ULONG containing the resource type for which
arbitration is required.
InterfaceReturn
Return Value:
TRUE is this device is not known to cause problems, FALSE
if the device should be skipped altogether.
--*/
{
PARBITER_INTERFACE arbiterInterface;
NTSTATUS status;
PAGED_CODE();
//
// This arbiter handles I/O ports, is that what they want?
//
if ((ULONG_PTR)InterfaceSpecificData != CmResourceTypePort) {
//
// No, it's not us then.
//
return STATUS_INVALID_PARAMETER_5;
}
//
// Have already verified that the InterfaceReturn variable
// points to an area in memory large enough to contain an
// ARBITER_INTERFACE. Fill it in for the caller.
//
arbiterInterface = (PARBITER_INTERFACE)InterfaceReturn;
arbiterInterface->Size = sizeof(ARBITER_INTERFACE);
arbiterInterface->Version = ARMEMIO_VERSION;
arbiterInterface->InterfaceReference = PciReferenceArbiter;
arbiterInterface->InterfaceDereference = PciDereferenceArbiter;
arbiterInterface->ArbiterHandler = ArbArbiterHandler;
arbiterInterface->Flags = 0;
status = PciArbiterInitializeInterface(DeviceExtension,
PciArb_Io,
arbiterInterface);
return status;
}
NTSTATUS
armem_Constructor(
PVOID DeviceExtension,
PVOID PciInterface,
PVOID InterfaceSpecificData,
USHORT Version,
USHORT Size,
PINTERFACE InterfaceReturn
)
/*++
Routine Description:
Check the InterfaceSpecificData to see if this is the correct
arbiter (we already know the required interface is an arbiter
from the GUID) and if so, allocate (and reference) a context
for this interface.
Arguments:
PciInterface Pointer to the PciInterface record for this
interface type.
InterfaceSpecificData
A ULONG containing the resource type for which
arbitration is required.
InterfaceReturn
Return Value:
TRUE is this device is not known to cause problems, FALSE
if the device should be skipped altogether.
--*/
{
PARBITER_INTERFACE arbiterInterface;
NTSTATUS status;
PAGED_CODE();
//
// This arbiter handles memory, is that what they want?
//
if ((ULONG_PTR)InterfaceSpecificData != CmResourceTypeMemory) {
//
// No, it's not us then.
//
return STATUS_INVALID_PARAMETER_5;
}
//
// Have already verified that the InterfaceReturn variable
// points to an area in memory large enough to contain an
// ARBITER_INTERFACE. Fill it in for the caller.
//
arbiterInterface = (PARBITER_INTERFACE)InterfaceReturn;
arbiterInterface->Size = sizeof(ARBITER_INTERFACE);
arbiterInterface->Version = ARMEMIO_VERSION;
arbiterInterface->InterfaceReference = PciReferenceArbiter;
arbiterInterface->InterfaceDereference = PciDereferenceArbiter;
arbiterInterface->ArbiterHandler = ArbArbiterHandler;
arbiterInterface->Flags = 0;
status = PciArbiterInitializeInterface(DeviceExtension,
PciArb_Memory,
arbiterInterface);
return status;
}
NTSTATUS
armem_Initializer(
IN PPCI_ARBITER_INSTANCE Instance
)
/*++
Routine Description:
This routine is called once per instantiation of an arbiter.
Performs initialization of this instantiation's context.
Arguments:
Instance Pointer to the arbiter context.
Return Value:
Returns the status of this operation.
--*/
{
NTSTATUS status;
RtlZeroMemory(&Instance->CommonInstance, sizeof(ARBITER_INSTANCE));
PAGED_CODE();
//
// Set the Action Handler entry points.
//
Instance->CommonInstance.UnpackRequirement = armemio_UnpackRequirement;
Instance->CommonInstance.PackResource = armemio_PackResource;
Instance->CommonInstance.UnpackResource = armemio_UnpackResource;
Instance->CommonInstance.ScoreRequirement = armemio_ScoreRequirement;
Instance->CommonInstance.FindSuitableRange = armem_FindSuitableRange;
#if PREFETCHABLE_SUPPORTED
Instance->CommonInstance.PreprocessEntry = armem_PreprocessEntry;
Instance->CommonInstance.StartArbiter = armem_StartArbiter;
Instance->CommonInstance.GetNextAllocationRange
= armem_GetNextAllocationRange;
//
// NTRAID #54671 - 2000/03/31 - andrewth
// When reference counting is working we need to release this
// extension when we dereference the arbiter to 0
//
//
// Allocate and zero the arbiter extension, it is initialized in
// armem_StartArbiter
//
Instance->CommonInstance.Extension
= ExAllocatePool(PagedPool | POOL_COLD_ALLOCATION, sizeof(ARBITER_MEMORY_EXTENSION));
if (!Instance->CommonInstance.Extension) {
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlZeroMemory(Instance->CommonInstance.Extension,
sizeof(ARBITER_MEMORY_EXTENSION)
);
#endif // PREFETCHABLE_SUPPORTED
//
// Initialize the rest of the common instance
//
return ArbInitializeArbiterInstance(&Instance->CommonInstance,
Instance->BusFdoExtension->FunctionalDeviceObject,
CmResourceTypeMemory,
Instance->InstanceName,
L"Pci",
NULL
);
}
NTSTATUS
ario_Initializer(
IN PPCI_ARBITER_INSTANCE Instance
)
/*++
Routine Description:
This routine is called once per instantiation of an arbiter.
Performs initialization of this instantiation's context.
Arguments:
Instance Pointer to the arbiter context.
Return Value:
Returns the status of this operation.
--*/
{
PAGED_CODE();
ASSERT(!(Instance->BusFdoExtension->BrokenVideoHackApplied));
RtlZeroMemory(&Instance->CommonInstance, sizeof(ARBITER_INSTANCE));
//
// Set the Action Handler entry points.
//
#if ALLOCATE_ALIASES
Instance->CommonInstance.PreprocessEntry = ario_PreprocessEntry;
Instance->CommonInstance.FindSuitableRange = ario_FindSuitableRange;
Instance->CommonInstance.AddAllocation = ario_AddAllocation;
Instance->CommonInstance.BacktrackAllocation = ario_BacktrackAllocation;
#endif
#if PASSIVE_DECODE_SUPPORTED
Instance->CommonInstance.OverrideConflict = ario_OverrideConflict;
#endif
#if ISA_BIT_SUPPORTED
Instance->CommonInstance.GetNextAllocationRange = ario_GetNextAllocationRange;
Instance->CommonInstance.StartArbiter = ario_StartArbiter;
#endif
Instance->CommonInstance.UnpackRequirement = armemio_UnpackRequirement;
Instance->CommonInstance.PackResource = armemio_PackResource;
Instance->CommonInstance.UnpackResource = armemio_UnpackResource;
Instance->CommonInstance.ScoreRequirement = armemio_ScoreRequirement;
//
// Initialize the rest of the common instance
//
return ArbInitializeArbiterInstance(&Instance->CommonInstance,
Instance->BusFdoExtension->FunctionalDeviceObject,
CmResourceTypePort,
Instance->InstanceName,
L"Pci",
NULL
);
}
NTSTATUS
armemio_UnpackRequirement(
IN PIO_RESOURCE_DESCRIPTOR Descriptor,
OUT PULONGLONG Minimum,
OUT PULONGLONG Maximum,
OUT PULONG Length,
OUT PULONG Alignment
)
/*++
Routine Description:
This routine unpacks an resource requirement descriptor.
Arguments:
Descriptor - The descriptor describing the requirement to unpack.
Minimum - Pointer to where the minimum acceptable start value should be
unpacked to.
Maximum - Pointer to where the maximum acceptable end value should be
unpacked to.
Length - Pointer to where the required length should be unpacked to.
Minimum - Pointer to where the required alignment should be unpacked to.
Return Value:
Returns the status of this operation.
--*/
{
NTSTATUS status = STATUS_SUCCESS;
PAGED_CODE();
ASSERT(Descriptor);
ASSERT((Descriptor->Type == CmResourceTypePort) ||
(Descriptor->Type == CmResourceTypeMemory));
*Minimum = (ULONGLONG)Descriptor->u.Generic.MinimumAddress.QuadPart;
*Maximum = (ULONGLONG)Descriptor->u.Generic.MaximumAddress.QuadPart;
*Length = Descriptor->u.Generic.Length;
*Alignment = Descriptor->u.Generic.Alignment;
//
// Fix the broken hardware that reports 0 alignment
//
if (*Alignment == 0) {
*Alignment = 1;
}
//
// Fix broken INF's that report they support 24bit memory > 0xFFFFFF
//
if (Descriptor->Type == CmResourceTypeMemory
&& Descriptor->Flags & CM_RESOURCE_MEMORY_24
&& Descriptor->u.Memory.MaximumAddress.QuadPart > 0xFFFFFF) {
if (Descriptor->u.Memory.MinimumAddress.QuadPart > 0xFFFFFF) {
PciDebugPrintf(0, "24 bit decode specified but both min and max are greater than 0xFFFFFF, most probably due to broken INF!\n");
ASSERT(Descriptor->u.Memory.MinimumAddress.QuadPart <= 0xFFFFFF);
status = STATUS_UNSUCCESSFUL;
} else {
*Maximum = 0xFFFFFF;
}
}
return status;
}
NTSTATUS
armemio_PackResource(
IN PIO_RESOURCE_DESCRIPTOR Requirement,
IN ULONGLONG Start,
OUT PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor
)
/*++
Routine Description:
This routine packs an resource descriptor.
Arguments:
Requirement - The requirement from which this resource was chosen.
Start - The start value of the resource.
Descriptor - Pointer to the descriptor to pack into.
Return Value:
Returns the status of this operation.
--*/
{
PAGED_CODE();
ASSERT(Descriptor);
ASSERT(Requirement);
ASSERT((Requirement->Type == CmResourceTypePort) ||
(Requirement->Type == CmResourceTypeMemory));
Descriptor->Type = Requirement->Type;
Descriptor->Flags = Requirement->Flags;
Descriptor->ShareDisposition = Requirement->ShareDisposition;
Descriptor->u.Generic.Start.QuadPart = Start;
Descriptor->u.Generic.Length = Requirement->u.Generic.Length;
return STATUS_SUCCESS;
}
NTSTATUS
armemio_UnpackResource(
IN PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor,
OUT PULONGLONG Start,
OUT PULONG Length
)
/*++
Routine Description:
This routine unpacks an resource descriptor.
Arguments:
Descriptor - The descriptor describing the resource to unpack.
Start - Pointer to where the start value should be unpacked to.
End - Pointer to where the end value should be unpacked to.
Return Value:
Returns the status of this operation.
--*/
{
PAGED_CODE();
ASSERT(Descriptor);
ASSERT(Start);
ASSERT(Length);
ASSERT((Descriptor->Type == CmResourceTypePort) ||
(Descriptor->Type == CmResourceTypeMemory));
*Start = Descriptor->u.Generic.Start.QuadPart;
*Length= Descriptor->u.Generic.Length;
return STATUS_SUCCESS;
}
LONG
armemio_ScoreRequirement(
IN PIO_RESOURCE_DESCRIPTOR Descriptor
)
/*++
Routine Description:
This routine scores a requirement based on how flexible it is. The least
flexible devices are scored the least and so when the arbitration list is
sorted we try to allocate their resources first.
Arguments:
Descriptor - The descriptor describing the requirement to score.
Return Value:
The score.
--*/
{
LONG score;
ULONGLONG start, end;
ULONGLONG bigscore;
ULONG alignment;
PAGED_CODE();
ASSERT(Descriptor);
ASSERT((Descriptor->Type == CmResourceTypePort) ||
(Descriptor->Type == CmResourceTypeMemory));
alignment = Descriptor->u.Generic.Alignment;
//
// Fix the broken hardware that reports 0 alignment
//
if (alignment == 0) {
//
// Set this to 1 here, because we arbitrate ISA
// devices in the context of PCI. If you don't understand
// you don't want to. Trust me. (hint: subtractive decode)
//
// Any PCI device that has alignment 0 will also
// have Length 0, which is horribly wrong to begin with.
// and we deal with it elsewhere.
//
alignment = 1;
}
start = ALIGN_ADDRESS_UP(
Descriptor->u.Generic.MinimumAddress.QuadPart,
alignment
);
end = Descriptor->u.Generic.MaximumAddress.QuadPart;
//
// The score is the number of possible allocations that could be made
// given the alignment and length constraints
//
bigscore = (((end - Descriptor->u.Generic.Length + 1) - start)
/ alignment) + 1;
//
// Note, the scores for each possibility are added together. To
// avoid overflowing the total, we need to limit the range returned.
//
// Make it a sort of logarithmic score. Find the highest byte
// set, weight it (add 0x100) and use the log (I said "sort of").
//
// This puts the result in the range 0xff80 down to 0x0100.
//
for (score = sizeof(bigscore) - 1; score >= 0; score--) {
UCHAR v = *(((PUCHAR)&bigscore) + score);
if (v != 0) {
score = (v + 0x100) << score;
break;
}
}
//
// The resulting TOTAL from scoring all the alternatives is used
// to sort the list. The highest total is considered the easiest
// to place,.... which is probably true,... What if we got some-
// thing like a single fit preferred setting followed by a fits
// anywhere setting? We don't want that to score higher than
// another device which only specified the fit anywhere setting,
// the preferred setting is harder to achieve.
//
// And, are two alternatives, each half as good an a 'fit anywhere'
// as good as the 'fit anywhere'. Not really.
//
// So, we weight the result even further depending on what options
// are set in this resource.
//
if (Descriptor->Option &
(IO_RESOURCE_PREFERRED | IO_RESOURCE_ALTERNATIVE)) {
score -= 0x100;
}
ARB_PRINT(
3,
(" %s resource %08x(0x%I64x-0x%I64x) => %i\n",
Descriptor->Type == CmResourceTypeMemory ? "Memory" : "Io",
Descriptor,
Descriptor->u.Generic.MinimumAddress.QuadPart,
end,
score
));
return score;
}
VOID
ario_BacktrackAllocation(
IN PARBITER_INSTANCE Arbiter,
IN PARBITER_ALLOCATION_STATE State
)
/*++
Routine Description:
This routine is called from AllocateEntry if the possible solution
(State->Start - State->End) does not allow us to allocate resources to
the rest of the devices being considered. It deletes the ranges that were
added to Arbiter->PossibleAllocation by AddAllocation including those
associated with ISA aliases.
Arguments:
Arbiter - The instance data of the arbiter who was called.
State - The state of the current arbitration.
Return Value:
None.
--*/
{
PAGED_CODE();
ario_AddOrBacktrackAllocation(Arbiter,
State,
ArbBacktrackAllocation
);
}
VOID
ario_AddOrBacktrackAllocation(
IN PARBITER_INSTANCE Arbiter,
IN PARBITER_ALLOCATION_STATE State,
IN PARBITER_BACKTRACK_ALLOCATION Callback
)
/*++
Routine Description:
This relies on the fact that PARBITER_BACKTRACK_ALLOCATION and
PARBITER_ADD_ALLOCATION are of the same type
Arguments:
Arbiter - The instance data of the arbiter who was called.
State - The state of the current arbitration.
Backtrack - TRUE for backtrack, false for ADD
Return Value:
None.
--*/
{
NTSTATUS status;
ARBITER_ALLOCATION_STATE localState;
PAGED_CODE();
ASSERT(Arbiter);
ASSERT(State);
//
// We are going to mess with the state so make our own local copy
//
RtlCopyMemory(&localState, State, sizeof(ARBITER_ALLOCATION_STATE));
#if ISA_BIT_SUPPORTED
//
// Check if this is a window for a bridge with the ISA bit set that is
// in 16 bit IO space. If so we need to do some special processing
//
if (State->WorkSpace & PORT_ARBITER_BRIDGE_WINDOW
&& State->WorkSpace & PORT_ARBITER_ISA_BIT_SET
&& State->Start < 0xFFFF) {
//
// We don't support IO windows that straddle the 16/32 bit boundry
//
ASSERT(State->End <= 0xFFFF);
//
// If the ISA bit is set on a bridge it means that the bridge only
// decodes the first 0x100 ports for each 0x400 ports in 16 bit IO space.
// Just remove these to the range list.
//
for (;
localState.Start < State->End && localState.Start < 0xFFFF;
localState.Start += 0x400) {
localState.End = localState.Start + 0xFF;
Callback(Arbiter, &localState);
}
return;
}
#endif
//
// Process the base range
//
Callback(Arbiter, State);
//
// Process any aliases with the alias flag set
//
ARB_PRINT(2, ("Adding aliases\n"));
//
// Lets see if we are processing positively decoded alases - yes you read that
// right - on a PCI-PCI or Cardbus brigde with the VGA bit set (and seeing
// as our friends now like AGP cards this is rather common) we decode all
// VGA ranges together with their 10bit aliases. This means that the normal
// rules of engagement with aliases don't apply so don't set the alias bit.
//
if (!(State->CurrentAlternative->Descriptor->Flags & CM_RESOURCE_PORT_POSITIVE_DECODE)) {
//
// We're processing aliases so set the alias flag
//
localState.RangeAttributes |= ARBITER_RANGE_ALIAS;
}
while (ario_GetNextAlias(State->CurrentAlternative->Descriptor->Flags,
localState.Start,
&localState.Start)) {
localState.End = localState.Start + State->CurrentAlternative->Length - 1;
Callback(Arbiter, &localState);
}
}
NTSTATUS
ario_PreprocessEntry(
IN PARBITER_INSTANCE Arbiter,
IN PARBITER_ALLOCATION_STATE State
)
/*++
Routine Description:
This routine is called from AllocateEntry to allow preprocessing of
entries
Arguments:
Arbiter - The instance data of the arbiter who was called.
State - The state of the current arbitration.
Return Value:
None.
--*/
{
PARBITER_ALTERNATIVE current;
ULONG defaultDecode;
BOOLEAN improviseDecode = FALSE, windowDetected = FALSE;
ULONGLONG greatestPort = 0;
PCI_OBJECT_TYPE type;
PPCI_PDO_EXTENSION pdoExtension;
PAGED_CODE();
if (State->WorkSpace & PORT_ARBITER_PREPROCESSED) {
//
// We have already proprocessed this entry so don't repeat the work
//
return STATUS_SUCCESS;
} else {
State->WorkSpace |= PORT_ARBITER_PREPROCESSED;
}
//
// Scan the alternatives and check if we have set any of the decode flags
// are set or if we have to improvise
//
FOR_ALL_IN_ARRAY(State->Alternatives,
State->AlternativeCount,
current) {
ASSERT(current->Descriptor->Type == CmResourceTypePort);
ASSERT(current->Descriptor->Flags == State->Alternatives->Descriptor->Flags);
//
// Remember the greatest value we come across
//
if (current->Maximum > greatestPort) {
greatestPort = current->Maximum;
}
//
// Remember if we ever encounter a bridge window
//
if (current->Descriptor->Flags & CM_RESOURCE_PORT_WINDOW_DECODE) {
//
// If the request is marked with the window flag all alternatives
// should also be marked with the window flag
//
#if DBG
if (current != State->Alternatives) {
//
// This is an alternative - make sure we have already set the
// window detected flag
//
ASSERT(windowDetected);
}
#endif
windowDetected = TRUE;
}
if (!(current->Descriptor->Flags &
(CM_RESOURCE_PORT_10_BIT_DECODE
| CM_RESOURCE_PORT_12_BIT_DECODE
| CM_RESOURCE_PORT_16_BIT_DECODE
| CM_RESOURCE_PORT_POSITIVE_DECODE))) {
improviseDecode = TRUE;
// ASSERT(LEGACY_REQUEST(State->Entry->Source)
if (!LEGACY_REQUEST(State->Entry)) {
ARB_PRINT(0,
("Pnp device (%p) did not specify decodes for IO ports\n",
State->Entry->PhysicalDeviceObject
));
}
}
}
if (improviseDecode) {
//
// Remember we improvised this
//
State->WorkSpace |= PORT_ARBITER_IMPROVISED_DECODE;
ARB_PRINT(1, ("Improvising decode "));
//
// Work out the default
//
switch (State->Entry->InterfaceType) {
case PNPISABus:
case Isa:
//
// if machine is NEC98, default decode is 16 bit.
//
if(IsNEC_98) {
defaultDecode = CM_RESOURCE_PORT_16_BIT_DECODE;
ARB_PRINT(1, ("of 16bit for NEC98 Isa\n"));
} else {
//
// If any of the ports is greater than can be decoded in 10 bits
// assume a 16 bit decode
//
if (greatestPort >= (1<<10)) {
defaultDecode = CM_RESOURCE_PORT_16_BIT_DECODE;
ARB_PRINT(1, ("of 16bit for Isa with ports > 0x3FF\n"));
} else {
defaultDecode = CM_RESOURCE_PORT_10_BIT_DECODE;
ARB_PRINT(1, ("of 10bit for Isa\n"));
}
}
break;
case Eisa:
case MicroChannel:
case PCMCIABus:
ARB_PRINT(1, ("of 16bit for Eisa/MicroChannel/Pcmcia\n"));
defaultDecode = CM_RESOURCE_PORT_16_BIT_DECODE;
break;
case PCIBus:
ARB_PRINT(1, ("of positive for PCI\n"));
defaultDecode = CM_RESOURCE_PORT_POSITIVE_DECODE;
break;
default:
//
// In NT < 5 we considered everything to be 16 bit decode so in the
// absence of better information continue that tradition.
//
ARB_PRINT(1, ("of 16bit for unknown bus\n"));
defaultDecode = CM_RESOURCE_PORT_16_BIT_DECODE;
break;
}
//
// Now set the flags
//
FOR_ALL_IN_ARRAY(State->Alternatives,
State->AlternativeCount,
current) {
current->Descriptor->Flags |= defaultDecode;
}
} else {
//
// Even if we are not improvising decodes make sure that they didn't
// report 10 bit decode for a range over 0x3FF - if so we assume 16 bit
// decode - think EISA net cards that say they're ISA
//
FOR_ALL_IN_ARRAY(State->Alternatives,
State->AlternativeCount,
current) {
if ((current->Descriptor->Flags & CM_RESOURCE_PORT_10_BIT_DECODE)
&& (greatestPort >= (1<<10) )) {
current->Descriptor->Flags &= ~CM_RESOURCE_PORT_10_BIT_DECODE;
current->Descriptor->Flags |= CM_RESOURCE_PORT_16_BIT_DECODE;
}
}
}
//
// If we detected a bridge window then check if the ISA bit is set on the bridge
//
if (windowDetected) {
//
// Make sure its a PCI bridge...
//
if (State->Entry->PhysicalDeviceObject->DriverObject != PciDriverObject) {
ASSERT(State->Entry->PhysicalDeviceObject->DriverObject == PciDriverObject);
return STATUS_INVALID_PARAMETER;
}
pdoExtension = (PPCI_PDO_EXTENSION) State->Entry->PhysicalDeviceObject->DeviceExtension;
if (pdoExtension->ExtensionType != PciPdoExtensionType) {
ASSERT(pdoExtension->ExtensionType == PciPdoExtensionType);
return STATUS_INVALID_PARAMETER;
}
type = PciClassifyDeviceType(pdoExtension);
if (type != PciTypePciBridge && type != PciTypeCardbusBridge) {
ASSERT(type == PciTypePciBridge || type == PciTypeCardbusBridge);
return STATUS_INVALID_PARAMETER;
}
if (pdoExtension->Dependent.type1.IsaBitSet) {
if (type == PciTypePciBridge) {
State->WorkSpace |= PORT_ARBITER_ISA_BIT_SET;
} else {
ASSERT(type == PciTypePciBridge);
}
}
State->WorkSpace |= PORT_ARBITER_BRIDGE_WINDOW;
}
//
// If this device is positive decode then we want the range to be added to
// the list with the positive decode flag set
//
if (State->Alternatives->Descriptor->Flags & CM_RESOURCE_PORT_POSITIVE_DECODE) {
State->RangeAttributes |= ARBITER_RANGE_POSITIVE_DECODE;
}
return STATUS_SUCCESS;
}
BOOLEAN
ario_FindWindowWithIsaBit(
IN PARBITER_INSTANCE Arbiter,
IN PARBITER_ALLOCATION_STATE State
)
{
NTSTATUS status;
ULONGLONG start, current;
BOOLEAN available = FALSE;
ULONG findRangeFlags = 0;
//
// We only support the ISA bit on Pci bridges
//
ASSERT_PCI_DEVICE_OBJECT(State->Entry->PhysicalDeviceObject);
ASSERT(PciClassifyDeviceType(((PPCI_PDO_EXTENSION) State->Entry->PhysicalDeviceObject->DeviceExtension)
) == PciTypePciBridge);
//
// Bridges with windows perform positive decode and so can conflict with
// aliases
//
ASSERT(State->CurrentAlternative->Descriptor->Flags & CM_RESOURCE_PORT_POSITIVE_DECODE);
State->RangeAvailableAttributes |= ARBITER_RANGE_ALIAS;
//
// The request should be correctly aligned - we generated it!
//
ASSERT(State->CurrentAlternative->Length % State->CurrentAlternative->Alignment == 0);
//
// CurrentMinimum/CurrentMaximum should have been correctly aligned by
// GetNextAllocationRange
//
ASSERT(State->CurrentMinimum % State->CurrentAlternative->Alignment == 0);
ASSERT((State->CurrentMaximum + 1) % State->CurrentAlternative->Alignment == 0);
//
// Conflicts with NULL occur when our parents IO space is sparse, for
// bridges that is if the ISA bit is set and so everything will line
// up here. If the root we are on is sparse then things arn't as easy.
if (State->Flags & ARBITER_STATE_FLAG_NULL_CONFLICT_OK) {
findRangeFlags |= RTL_RANGE_LIST_NULL_CONFLICT_OK;
}
//
// ...or we are shareable...
//
if (State->CurrentAlternative->Flags & ARBITER_ALTERNATIVE_FLAG_SHARED) {
findRangeFlags |= RTL_RANGE_LIST_SHARED_OK;
}
//
// Check the length is reasonable
//
if (State->CurrentMaximum < (State->CurrentAlternative->Length + 1)) {
return FALSE;
}
//
// Iterate through the possible window positions, top down like the rest of
// arbitration.
//
start = State->CurrentMaximum - State->CurrentAlternative->Length + 1;
while (!available) {
//
// Check the range is within the constraints specified
//
if (start < State->CurrentMinimum) {
break;
}
//
// Check if the ISA windows are available we don't care about the rest
// of the ranges are we don't decode them.
//
for (current = start;
(current < (start + State->CurrentAlternative->Length - 1)) && (current < PCI_BRIDGE_ISA_BIT_MAX);
current += PCI_BRIDGE_ISA_BIT_STRIDE) {
status = RtlIsRangeAvailable(
Arbiter->PossibleAllocation,
current,
current + PCI_BRIDGE_ISA_BIT_WIDTH - 1,
findRangeFlags,
State->RangeAvailableAttributes,
Arbiter->ConflictCallbackContext,
Arbiter->ConflictCallback,
&available
);
ASSERT(NT_SUCCESS(status));
if (!available) {
break;
}
}
//
// Now available indicates if all the ISA windows were available
//
if (available) {
State->Start = start;
State->End = start + State->CurrentAlternative->Length - 1;
ASSERT(State->Start >= State->CurrentMinimum);
ASSERT(State->End <= State->CurrentMaximum);
break;
} else {
//
// Move to next range if we can
//
if (start < PCI_BRIDGE_WINDOW_GRANULARITY) {
break;
}
start -= PCI_BRIDGE_WINDOW_GRANULARITY; // IO windows are 1000 byte aligned
continue;
}
}
return available;
}
BOOLEAN
ario_FindSuitableRange(
IN PARBITER_INSTANCE Arbiter,
IN PARBITER_ALLOCATION_STATE State
)
/*++
Routine Description:
This routine is called from AllocateEntry once we have decided where we want
to allocate from. It tries to find a free range that matches the
requirements in State while restricting its possible solutions to the range
State->Start to State->CurrentMaximum. On success State->Start and
State->End represent this range. Conflicts with ISA aliases are considered.
Arguments:
Arbiter - The instance data of the arbiter who was called.
State - The state of the current arbitration.
Return Value:
TRUE if we found a range, FALSE otherwise.
--*/
{
NTSTATUS status;
PPCI_PDO_EXTENSION parentPdo, childPdo;
PAGED_CODE();
//
// Assume we won't be allowing null conflicts
//
State->Flags &= ~ARBITER_STATE_FLAG_NULL_CONFLICT_OK;
//
// Now check if we really wanted to allow them
//
if (State->WorkSpace & PORT_ARBITER_BRIDGE_WINDOW) {
//
// If this isn't a PCI PDO we already failed in PreprocessEntry but
// paranoia reigns.
//
ASSERT_PCI_DEVICE_OBJECT(State->Entry->PhysicalDeviceObject);
childPdo = (PPCI_PDO_EXTENSION) State->Entry->PhysicalDeviceObject->DeviceExtension;
if (!PCI_PDO_ON_ROOT(childPdo)) {
parentPdo = PCI_BRIDGE_PDO(PCI_PARENT_FDOX(childPdo));
ASSERT(parentPdo);
} else {
parentPdo = NULL;
}
//
// Check if this is a PCI-PCI bridge that the bios configured that
// we have not moved or it is a root bus (which be definition was
// bios configured ...
//
if ((parentPdo == NULL ||
(parentPdo->HeaderType == PCI_BRIDGE_TYPE && !parentPdo->MovedDevice))
&& (State->CurrentAlternative->Flags & ARBITER_ALTERNATIVE_FLAG_FIXED)) {
//
// The BIOS configured and we have not moved its parent (thus
// invalidating the bioses configuration) then we will leave well
// alone. We then allow the null conflicts and only configure the
// arbiter to give out ranges that make it to this bus.
//
State->Flags |= ARBITER_STATE_FLAG_NULL_CONFLICT_OK;
} else {
//
// If the BIOS didn't configure it then we need to find somewhere to
// put the bridge. This will involve trying to find a contiguous 1000
// port window and then if one is not available, examining all locations
// to find the one with the most IO.
//
//
// NTRAID #62581 - 04/03/2000 - andrewth
// We are punting on this for Win2K, if 1000 contiguous ports
// arn't available then we are not going to configure the bridge. A fix
// for this will be required for hot plug to work. Setting the ISA bit
// on the bridge will increase the chances of configuring this
//
}
//
// Check if this is a window for a bridge with the ISA bit set in 16 bit IO
// space. If so we need to do some special processing...
//
if (State->WorkSpace & PORT_ARBITER_ISA_BIT_SET
&& State->CurrentMaximum <= 0xFFFF) {
return ario_FindWindowWithIsaBit(Arbiter, State);
}
}
//
// For legacy requests from IoAssignResources (directly or by way of
// HalAssignSlotResources) or IoReportResourceUsage we consider preallocated
// resources to be available for backward compatibility reasons.
//
// If we are allocating a devices boot config then we consider all other
// boot configs to be available.
//
if (State->Entry->RequestSource == ArbiterRequestLegacyReported
|| State->Entry->RequestSource == ArbiterRequestLegacyAssigned
|| State->Entry->Flags & ARBITER_FLAG_BOOT_CONFIG) {
State->RangeAvailableAttributes |= ARBITER_RANGE_BOOT_ALLOCATED;
}
//
// This request is for a device which performs positive decode so all
// aliased ranges should be considered available
//
if (State->CurrentAlternative->Descriptor->Flags & CM_RESOURCE_PORT_POSITIVE_DECODE) {
State->RangeAvailableAttributes |= ARBITER_RANGE_ALIAS;
}
while (State->CurrentMaximum >= State->CurrentMinimum) {
//
// Try to satisfy the request
//
if (ArbFindSuitableRange(Arbiter, State)) {
if (State->CurrentAlternative->Length == 0) {
ARB_PRINT(2,
("Zero length solution solution for %p = 0x%I64x-0x%I64x, %s\n",
State->Entry->PhysicalDeviceObject,
State->Start,
State->End,
State->CurrentAlternative->Flags & ARBITER_ALTERNATIVE_FLAG_SHARED ?
"shared" : "non-shared"
));
//
// Set the result in the arbiter appropriatley so that we
// don't try and translate this zero requirement - it won't!
//
State->Entry->Result = ArbiterResultNullRequest;
return TRUE;
} else if (ario_IsAliasedRangeAvailable(Arbiter, State)) {
//
// We found a suitable range so return
//
return TRUE;
} else {
//
// Check if we wrap on start - if so then we're finished.
//
if (State->Start - 1 > State->Start) {
break;
}
//
// This range's aliases arn't available so reduce allocation
// window to not include the range just returned and try again
//
State->CurrentMaximum = State->Start - 1;
continue;
}
} else {
//
// We couldn't find a base range
//
break;
}
}
return FALSE;
}
BOOLEAN
ario_GetNextAlias(
ULONG IoDescriptorFlags,
ULONGLONG LastAlias,
PULONGLONG NextAlias
)
/*++
Routine Description:
This routine calculates the next alias of an IO port up to 0xFFFF.
Arguments:
IoDescriptorFlags - The flags from the requirement descriptor indicating the
type of alias if any.
LastAlias - The alias previous to this one.
NextAlias - Point to where the next alias should be returned
Return Value:
TRUE if we found an alias, FALSE otherwise.
--*/
{
ULONGLONG next;
PAGED_CODE();
if (IoDescriptorFlags & CM_RESOURCE_PORT_10_BIT_DECODE) {
next = LastAlias + (1 << 10);
} else if (IoDescriptorFlags & CM_RESOURCE_PORT_12_BIT_DECODE) {
next = LastAlias + (1 << 12);
} else if ((IoDescriptorFlags & CM_RESOURCE_PORT_POSITIVE_DECODE)
|| (IoDescriptorFlags & CM_RESOURCE_PORT_16_BIT_DECODE)) {
//
// Positive decode implies 16 bit decode unless 10 or 12 bit flags are set
// There are no aliases as we decode all the bits... what a good idea
//
return FALSE;
} else {
//
// None of the CM_RESOURCE_PORT_*_DECODE flags are set - we should never
// get here as we should have set them in Preprocess
//
ASSERT(FALSE);
return FALSE;
}
//
// Check that we are below the maximum aliased port
//
if (next > 0xFFFF) {
return FALSE;
} else {
*NextAlias = next;
return TRUE;
}
}
BOOLEAN
ario_IsAliasedRangeAvailable(
PARBITER_INSTANCE Arbiter,
PARBITER_ALLOCATION_STATE State
)
/*++
Routine Description:
This routine determines if the range (Start-(Length-1)) is available taking
into account any aliases.
Arguments:
Arbiter - The instance data of the arbiter who was called.
State - The state of the current arbitration.
Return Value:
TRUE if the range is available, FALSE otherwise.
--*/
{
NTSTATUS status;
ULONGLONG alias = State->Start;
BOOLEAN aliasAvailable;
UCHAR userFlagsMask;
PAGED_CODE();
//
// If we improvised the decode then add the aliases but don't care it they
// conflict - more 4.0 compatibility.
//
if (State->WorkSpace & PORT_ARBITER_IMPROVISED_DECODE) {
return TRUE;
}
//
// Positive decode devices can conflict with aliases
//
userFlagsMask = ARBITER_RANGE_POSITIVE_DECODE;
//
// For legacy requests from IoAssignResources (directly or by way of
// HalAssignSlotResources) or IoReportResourceUsage we consider preallocated
// resources to be available for backward compatibility reasons.
//
if (State->Entry->RequestSource == ArbiterRequestLegacyReported
|| State->Entry->RequestSource == ArbiterRequestLegacyAssigned
|| State->Entry->Flags & ARBITER_FLAG_BOOT_CONFIG) {
userFlagsMask |= ARBITER_RANGE_BOOT_ALLOCATED;
}
while (ario_GetNextAlias(State->CurrentAlternative->Descriptor->Flags,
alias,
&alias)) {
status = RtlIsRangeAvailable(
Arbiter->PossibleAllocation,
alias,
alias + State->CurrentAlternative->Length - 1,
RTL_RANGE_LIST_NULL_CONFLICT_OK |
(State->CurrentAlternative->Flags & ARBITER_ALTERNATIVE_FLAG_SHARED ?
RTL_RANGE_LIST_SHARED_OK : 0),
userFlagsMask,
Arbiter->ConflictCallbackContext,
Arbiter->ConflictCallback,
&aliasAvailable
);
ASSERT(NT_SUCCESS(status));
if (!aliasAvailable) {
ARBITER_ALLOCATION_STATE tempState;
//
// Check if we allow this conflict by calling OverrideConflict -
// we will need to falsify ourselves an allocation state first
//
// NTRAID #62583 - 04/03/2000 - andrewth
// This works but relies on knowing what OverrideConflict
// looks at. A better fix invloves storing the aliases in another
// list but this it too much of a change for Win2k
//
RtlCopyMemory(&tempState, State, sizeof(ARBITER_ALLOCATION_STATE));
tempState.CurrentMinimum = alias;
tempState.CurrentMaximum = alias + State->CurrentAlternative->Length - 1;
if (Arbiter->OverrideConflict(Arbiter, &tempState)) {
//
// We decided this conflict was ok so contine checking the rest
// of the aliases
//
continue;
}
//
// An alias isn't available - get another possibility
//
ARB_PRINT(2,
("\t\tAlias 0x%x-0x%x not available\n",
alias,
alias + State->CurrentAlternative->Length - 1
));
return FALSE;
}
}
return TRUE;
}
VOID
ario_AddAllocation(
IN PARBITER_INSTANCE Arbiter,
IN PARBITER_ALLOCATION_STATE State
)
/*++
Routine Description:
This routine is called from AllocateEntry once we have found a possible
solution (State->Start - State->End). It adds the ranges that will not be
available if we commit to this solution to Arbiter->PossibleAllocation.
Arguments:
Arbiter - The instance data of the arbiter who was called.
State - The state of the current arbitration.
Return Value:
None.
--*/
{
PAGED_CODE();
ario_AddOrBacktrackAllocation(Arbiter,
State,
ArbAddAllocation
);
}
NTSTATUS
armem_PreprocessEntry(
IN PARBITER_INSTANCE Arbiter,
IN PARBITER_ALLOCATION_STATE State
)
{
PARBITER_MEMORY_EXTENSION extension = Arbiter->Extension;
BOOLEAN prefetchable;
PAGED_CODE();
ASSERT(extension);
//
// Check if this is a request for a ROM - it must be a fixed request with
// only 1 alternative and the ROM bit set
//
if ((State->Alternatives[0].Descriptor->Flags & CM_RESOURCE_MEMORY_READ_ONLY) ||
((State->Alternatives[0].Flags & ARBITER_ALTERNATIVE_FLAG_FIXED) &&
State->AlternativeCount == 1 &&
State->Entry->RequestSource == ArbiterRequestLegacyReported)) {
if (State->Alternatives[0].Descriptor->Flags & CM_RESOURCE_MEMORY_READ_ONLY) {
ASSERT(State->Alternatives[0].Flags & ARBITER_ALTERNATIVE_FLAG_FIXED);
// ASSERT(State->AlternativeCount == 1);
}
//
// Consider other ROMS to be available
//
State->RangeAvailableAttributes |= MEMORY_RANGE_ROM;
//
// Mark this range as a ROM
//
State->RangeAttributes |= MEMORY_RANGE_ROM;
//
// Allow NULL conflicts
//
State->Flags |= ARBITER_STATE_FLAG_NULL_CONFLICT_OK;
}
//
// Check if this is a request for prefetchable memory and select
// the correct ordering list
//
if (extension->PrefetchablePresent) {
#if IGNORE_PREFETCH_FOR_LEGACY_REPORTED
//
// In NT < 5 IoReportResourceUsage had no notion of prefetchable memory
// so in order to be backward compatible we hope the BIOS/firmware got
// it right!
//
if (State->Entry->RequestSource == ArbiterRequestLegacyReported) {
Arbiter->OrderingList = extension->OriginalOrdering;
return STATUS_SUCCESS;
}
#endif
prefetchable =
State->Alternatives[0].Descriptor->Flags
& CM_RESOURCE_MEMORY_PREFETCHABLE;
if (prefetchable) {
Arbiter->OrderingList = extension->PrefetchableOrdering;
} else {
Arbiter->OrderingList = extension->NonprefetchableOrdering;
}
#if DBG
{
PARBITER_ALTERNATIVE current;
//
// Make sure that all the alternatives are of the same type
//
FOR_ALL_IN_ARRAY(State->Alternatives,
State->AlternativeCount,
current) {
ASSERT((current->Descriptor->Flags & CM_RESOURCE_MEMORY_PREFETCHABLE)
== prefetchable
);
}
}
#endif
}
return STATUS_SUCCESS;
}
BOOLEAN
armem_GetNextAllocationRange(
IN PARBITER_INSTANCE Arbiter,
IN PARBITER_ALLOCATION_STATE State
)
{
PARBITER_MEMORY_EXTENSION extension = Arbiter->Extension;
//
// Call the default implementation
//
if (!ArbGetNextAllocationRange(Arbiter, State)) {
return FALSE;
}
if (extension->PrefetchablePresent
&& State->Entry->RequestSource != ArbiterRequestLegacyReported) {
//
// We have already precalculated the reserved ranges into the ordering
// so if we end up in the reserved ranges we're out of luck...
//
if (State->CurrentAlternative->Priority > ARBITER_PRIORITY_PREFERRED_RESERVED) {
return FALSE;
}
}
return TRUE;
}
NTSTATUS
armem_StartArbiter(
IN PARBITER_INSTANCE Arbiter,
IN PCM_RESOURCE_LIST StartResources
)
/*++
This is called after ArbInitializeArbiterInstance as it uses
information initialized there. The arbiter lock should be held.
Seeing as this is only applicable to memory descriptors we maniulate
the resource descriptors directlty as oppose to useing the pack/upack
routines in the arbiter.
Example:
StartResources contain the prefetchable range 0xfff00000 - 0xfffeffff
OriginalOrdering (from the registry) says:
0x00100000 - 0xFFFFFFFF
0x000F0000 - 0x000FFFFF
0x00080000 - 0x000BFFFF
0x00080000 - 0x000FFFFF
0x00080000 - 0xFFBFFFFF
ReservedList contains 0xfff0a000-0xfff0afff
Then out ordering lists will be:
PrefetchableOrdering NonprefetchableOrdering
0xFFF0B000 - 0xFFFEFFFF
0xFFF00000 - 0xFFF09FFF
0xFFFF0000 - 0xFFFFFFFF 0xFFF0b000 - 0xFFFFFFFF
0x00100000 - 0xFFFEFFFF 0x00100000 - 0xFFF09FFF
0x000F0000 - 0x000FFFFF 0x000F0000 - 0x000FFFFF
0x00080000 - 0x000BFFFF 0x00080000 - 0x000BFFFF
0x00080000 - 0x000FFFFF 0x00080000 - 0x000FFFFF
This means that when following the prefetchable ordering we try to
allocate in the prefetchable range and if we can't then we allocate
none prefetchable memory. In the Nonprefetchable ordering we avoid the
prefetchable ranges. GetNextAllocationRange is changed so that it will not
allow
--*/
{
NTSTATUS status;
PCM_PARTIAL_RESOURCE_DESCRIPTOR current;
PARBITER_MEMORY_EXTENSION extension = Arbiter->Extension;
PARBITER_ORDERING currentOrdering;
ULONGLONG start, end;
#if PCIFLAG_IGNORE_PREFETCHABLE_MEMORY_AT_ROOT_HACK
PPCI_FDO_EXTENSION fdoExtension;
#endif
PAGED_CODE();
//
// If this is the first time we have initialized the extension do some one
// only initialization
//
if (!extension->Initialized) {
//
// Copy the default memory ordering list from the arbiter
//
extension->OriginalOrdering = Arbiter->OrderingList;
RtlZeroMemory(&Arbiter->OrderingList, sizeof(ARBITER_ORDERING_LIST));
} else {
//
// We are reinitializing the arbiter
//
if (extension->PrefetchablePresent) {
//
// We had prefetchable memory before so free the orderings we
// created last time.
//
ArbFreeOrderingList(&extension->PrefetchableOrdering);
ArbFreeOrderingList(&extension->NonprefetchableOrdering);
}
}
extension->PrefetchablePresent = FALSE;
extension->PrefetchableCount = 0;
if (StartResources != NULL) {
ASSERT(StartResources->Count == 1);
//
// Check if we have any prefetchable memory - if not we're done
//
FOR_ALL_IN_ARRAY(StartResources->List[0].PartialResourceList.PartialDescriptors,
StartResources->List[0].PartialResourceList.Count,
current) {
if ((current->Type == CmResourceTypeMemory)
&& (current->Flags & CM_RESOURCE_MEMORY_PREFETCHABLE)) {
extension->PrefetchablePresent = TRUE;
break;
}
}
}
#if PCIFLAG_IGNORE_PREFETCHABLE_MEMORY_AT_ROOT_HACK
if (PciSystemWideHackFlags&PCIFLAG_IGNORE_PREFETCHABLE_MEMORY_AT_ROOT_HACK) {
fdoExtension = (PPCI_FDO_EXTENSION) Arbiter->BusDeviceObject->DeviceExtension;
ASSERT_PCI_FDO_EXTENSION(fdoExtension);
if (PCI_IS_ROOT_FDO(fdoExtension)) {
extension->PrefetchablePresent = FALSE;
}
}
#endif
if (!extension->PrefetchablePresent) {
//
// Restore the original ordering list
//
Arbiter->OrderingList = extension->OriginalOrdering;
return STATUS_SUCCESS;
}
status = ArbInitializeOrderingList(&extension->PrefetchableOrdering);
if (!NT_SUCCESS(status)) {
goto cleanup;
}
status = ArbInitializeOrderingList(&extension->NonprefetchableOrdering);
if (!NT_SUCCESS(status)) {
goto cleanup;
}
//
// Copy of the original ordering into the new Nonprefetchable ordering
//
status = ArbCopyOrderingList(&extension->NonprefetchableOrdering,
&extension->OriginalOrdering
);
if (!NT_SUCCESS(status)) {
goto cleanup;
}
//
// Add the range 0-MAXULONGLONG to the list so we will calculate the reserved
// orderings in the list. This will ensure that we don't give a half
// prefetchable and half not range to a device. Prefetchable devices should
// probably be able to deal with this but its asking for trouble!
//
status = ArbAddOrdering(&extension->NonprefetchableOrdering,
0,
MAXULONGLONG
);
if (!NT_SUCCESS(status)) {
goto cleanup;
}
//
// For each prefetchable range delete it from the nonprefetchabe ordering
// and add it to the prefetchable one.
//
// NB - We take it "to be self evident that that all prefetchable memory is
// created equal" and therefore initialize the ordering list in the order
// the prefetchable memory desciptors are found in the resource list.
//
FOR_ALL_IN_ARRAY(StartResources->List[0].PartialResourceList.PartialDescriptors,
StartResources->List[0].PartialResourceList.Count,
current) {
if ((current->Type == CmResourceTypeMemory)
&& (current->Flags & CM_RESOURCE_MEMORY_PREFETCHABLE)) {
extension->PrefetchableCount++;
start = current->u.Memory.Start.QuadPart,
end = current->u.Memory.Start.QuadPart + current->u.Memory.Length - 1;
//
// Add to the prefetchable ordering
//
status = ArbAddOrdering(&extension->PrefetchableOrdering,
start,
end
);
if (!NT_SUCCESS(status)) {
goto cleanup;
}
//
// And prune it from the Nonprefetchable ordering
//
status = ArbPruneOrdering(&extension->NonprefetchableOrdering, start, end);
if (!NT_SUCCESS(status)) {
goto cleanup;
}
ARB_PRINT(1,("Processed prefetchable range 0x%I64x-0x%I64x\n",
start,
end
));
}
}
//
// Now prune out any explicitly reserved ranges from our new prefetchable
// ordering - these have already been precalculated into the Nonprefetchable
// ordering
//
FOR_ALL_IN_ARRAY(Arbiter->ReservedList.Orderings,
Arbiter->ReservedList.Count,
currentOrdering) {
status = ArbPruneOrdering(&extension->PrefetchableOrdering,
currentOrdering->Start,
currentOrdering->End
);
if (!NT_SUCCESS(status)) {
goto cleanup;
}
}
//
// Finally append the Nonprefetchable ordering onto the end of the prefetchable
//
FOR_ALL_IN_ARRAY(extension->NonprefetchableOrdering.Orderings,
extension->NonprefetchableOrdering.Count,
currentOrdering) {
status = ArbAddOrdering(&extension->PrefetchableOrdering,
currentOrdering->Start,
currentOrdering->End
);
if (!NT_SUCCESS(status)) {
goto cleanup;
}
}
extension->Initialized = TRUE;
return STATUS_SUCCESS;
cleanup:
return status;
}
BOOLEAN
ario_IsBridge(
IN PDEVICE_OBJECT Pdo
)
/*++
Routine Description:
Determines if this device is a PCI enumberated PCI-PCI or Cardbus bridge
Arguments:
Pdo - The Pdo representing the device in question
Return Value:
TRUE if this Pdo is for a bridge
--*/
{
PSINGLE_LIST_ENTRY nextEntry;
PPCI_FDO_EXTENSION fdoExtension;
PCI_OBJECT_TYPE type;
PAGED_CODE();
//
// First of all see if this is a PCI FDO by walking the list of all our FDOs
//
for ( nextEntry = PciFdoExtensionListHead.Next;
nextEntry != NULL;
nextEntry = nextEntry->Next ) {
fdoExtension = CONTAINING_RECORD(nextEntry,
PCI_FDO_EXTENSION,
List);
if (fdoExtension->PhysicalDeviceObject == Pdo) {
//
// Ok this is our FDO so we can look at it and see if it is a
// PCI-PCI or Cardbus bridge
//
type = PciClassifyDeviceType(Pdo->DeviceExtension);
if (type == PciTypePciBridge || type == PciTypeCardbusBridge) {
return TRUE;
}
}
}
return FALSE;
}
BOOLEAN
ario_OverrideConflict(
IN PARBITER_INSTANCE Arbiter,
IN PARBITER_ALLOCATION_STATE State
)
/*++
Routine Description:
This is the default implementation of override conflict which
Arguments:
Arbiter - The instance data of the arbiter who was called.
State - The state of the current arbitration.
Return Value:
TRUE if the conflict is allowable, false otherwise
--*/
{
PRTL_RANGE current;
RTL_RANGE_LIST_ITERATOR iterator;
BOOLEAN ok = FALSE;
PAGED_CODE();
if (!(State->CurrentAlternative->Flags & ARBITER_ALTERNATIVE_FLAG_FIXED)) {
return FALSE;
}
FOR_ALL_RANGES(Arbiter->PossibleAllocation, &iterator, current) {
//
// Only test the overlapping ones
//
if (INTERSECT(current->Start, current->End, State->CurrentMinimum, State->CurrentMaximum)) {
if (current->Attributes & State->RangeAvailableAttributes) {
//
// We DON'T set ok to true because we are just ignoring the range,
// as RtlFindRange would have and thus it can't be the cause of
// RtlFindRange failing, so ignoring it can't fix the conflict.
//
continue;
}
//
// Check if we are conflicting with ourselves and the conflicting
// range is a fixed requirement
//
if (current->Owner == State->Entry->PhysicalDeviceObject
&& State->CurrentAlternative->Flags & ARBITER_ALTERNATIVE_FLAG_FIXED) {
ARB_PRINT(1,
("PnP Warning: Device reported self-conflicting requirement\n"
));
State->Start=State->CurrentMinimum;
State->End=State->CurrentMaximum;
ok = TRUE;
continue;
}
//
// If the passive decode flag is set and we conflict with a bridge then
// allow the conflict. We also allow the conflict if the range never
// makes it onto the bus (Owner == NULL)
//
// NTRAID #62584 - 04/03/2000 - andrewth
// Once the PCI bridge code is in we need to ensure that we
// don't put anything into the ranges that are being passively decoded
//
if (State->CurrentAlternative->Descriptor->Flags & CM_RESOURCE_PORT_PASSIVE_DECODE
&& (ario_IsBridge(current->Owner) || current->Owner == NULL)) {
State->Start=State->CurrentMinimum;
State->End=State->CurrentMaximum;
ok = TRUE;
continue;
}
//
// The conflict is still valid
//
return FALSE;
}
}
return ok;
}
VOID
ario_ApplyBrokenVideoHack(
IN PPCI_FDO_EXTENSION FdoExtension
)
{
NTSTATUS status;
PPCI_ARBITER_INSTANCE pciArbiter;
PARBITER_INSTANCE arbiter;
ASSERT(!FdoExtension->BrokenVideoHackApplied);
ASSERT(PCI_IS_ROOT_FDO(FdoExtension));
//
// Find the arbiter - we should always have one for a root bus.
//
pciArbiter = PciFindSecondaryExtension(FdoExtension, PciArb_Io);
ASSERT(pciArbiter);
arbiter = &pciArbiter->CommonInstance;
//
// We are reinitializing the orderings free the old ones
//
ArbFreeOrderingList(&arbiter->OrderingList);
ArbFreeOrderingList(&arbiter->ReservedList);
//
// Rebuild the ordering list reserving all the places these broken S3 and
// ATI cards might want to live - this should not fail.
//
status = ArbBuildAssignmentOrdering(arbiter,
L"Pci",
L"BrokenVideo",
NULL
);
ASSERT(NT_SUCCESS(status));
FdoExtension->BrokenVideoHackApplied = TRUE;
}
BOOLEAN
ario_GetNextAllocationRange(
IN PARBITER_INSTANCE Arbiter,
IN PARBITER_ALLOCATION_STATE State
)
{
BOOLEAN rangeFound, doIsaBit;
ARBITER_ORDERING_LIST savedOrderingList;
//
// If this is a bridge with the ISA bit set use the bridge ordering list
//
doIsaBit = (State->WorkSpace & PORT_ARBITER_BRIDGE_WINDOW)
&& (State->WorkSpace & PORT_ARBITER_ISA_BIT_SET);
if (doIsaBit) {
savedOrderingList = Arbiter->OrderingList;
Arbiter->OrderingList = PciBridgeOrderingList;
}
//
// Call the base function
//
rangeFound = ArbGetNextAllocationRange(Arbiter, State);
if (doIsaBit) {
//
// If we have reached preferred reserved priority then we fail as we
// have already considered both the 16 and 32 bit IO cases and using
// the reserved may allow us to stradle the boundry.
//
if (rangeFound
&& State->CurrentAlternative->Priority > ARBITER_PRIORITY_PREFERRED_RESERVED) {
rangeFound = FALSE;
}
Arbiter->OrderingList = savedOrderingList;
}
return rangeFound;
}
NTSTATUS
ario_StartArbiter(
IN PARBITER_INSTANCE Arbiter,
IN PCM_RESOURCE_LIST StartResources
)
{
NTSTATUS status;
PPCI_FDO_EXTENSION fdoExtension;
PPCI_PDO_EXTENSION pdoExtension = NULL;
PRTL_RANGE_LIST exclusionList = NULL;
PCM_PARTIAL_RESOURCE_DESCRIPTOR descriptor;
PPCI_FDO_EXTENSION rootFdo;
PPCI_ARBITER_INSTANCE pciArbiter;
BOOLEAN foundResource;
ULONGLONG dummy;
ArbAcquireArbiterLock(Arbiter);
fdoExtension = (PPCI_FDO_EXTENSION) Arbiter->BusDeviceObject->DeviceExtension;
ASSERT_PCI_FDO_EXTENSION(fdoExtension);
if (StartResources == NULL || PCI_IS_ROOT_FDO(fdoExtension)) {
//
// Root bridges don't have ISA bits - at least that we can see...
// Bridges with no resources also arn't effected by the ISA bit
//
status = STATUS_SUCCESS;
goto exit;
}
ASSERT(StartResources->Count == 1);
pdoExtension = PCI_BRIDGE_PDO(fdoExtension);
//
// Select the appropriate exclusion list
//
if (pdoExtension->Dependent.type1.IsaBitSet) {
if (pdoExtension->Dependent.type1.VgaBitSet) {
exclusionList = &PciVgaAndIsaBitExclusionList;
} else {
exclusionList = &PciIsaBitExclusionList;
}
}
//
// Find the port window and process it if the ISA bit is set
//
foundResource = FALSE;
FOR_ALL_IN_ARRAY(StartResources->List[0].PartialResourceList.PartialDescriptors,
StartResources->List[0].PartialResourceList.Count,
descriptor) {
//
// NTRAID #62585 - 04/03/2000 - andrewth
// Again we don't deal with bridges with BARS - for now assume
// that the first IO descriptor we encounter is for the window
//
if (descriptor->Type == CmResourceTypePort) {
if (exclusionList) {
status = PciExcludeRangesFromWindow(
descriptor->u.Port.Start.QuadPart,
descriptor->u.Port.Start.QuadPart
+ descriptor->u.Port.Length - 1,
Arbiter->Allocation,
exclusionList
);
if (!NT_SUCCESS(status)) {
return status;
}
}
foundResource = TRUE;
break;
}
}
if (foundResource == FALSE) {
//
// There are no IO resourcres on this bus so don't try
// to handle the sparse root case.
//
status = STATUS_SUCCESS;
goto exit;
}
//
// Now deal with sparse root busses
//
rootFdo = PCI_ROOT_FDOX(fdoExtension);
//
// Find the root FDO's arbiter
//
pciArbiter = PciFindSecondaryExtension(rootFdo, PciArb_Io);
if (!pciArbiter) {
status = STATUS_INVALID_PARAMETER;
goto exit;
}
//
// Use it as the exclusion list for this arbiter
//
ArbAcquireArbiterLock(&pciArbiter->CommonInstance);
status = PciExcludeRangesFromWindow(
descriptor->u.Port.Start.QuadPart,
descriptor->u.Port.Start.QuadPart
+ descriptor->u.Port.Length - 1,
Arbiter->Allocation,
pciArbiter->CommonInstance.Allocation
);
ArbReleaseArbiterLock(&pciArbiter->CommonInstance);
//
// Sanity check this to make sure that at least one port is available - if
// not then fail start. You could argue that we should really have this
// marked as insufficient resources (code 12) as oppose to failed start
// (code 10) but that is much harder and this has the desired effect.
// We check by seeing if we can find a range for the minimal PCI requirements
// of 4 ports alignment 4.
//
status = RtlFindRange(Arbiter->Allocation,
0,
MAXULONGLONG,
4,
4,
0, // Flags
0, // AttribureAvailableMask
NULL, // Context
NULL, // Callback
&dummy
);
if (!NT_SUCCESS(status)) {
//
// We can't start this bridge
//
status = STATUS_INSUFFICIENT_RESOURCES;
}
exit:
ArbReleaseArbiterLock(Arbiter);
return status;
}
BOOLEAN
armem_FindSuitableRange(
PARBITER_INSTANCE Arbiter,
PARBITER_ALLOCATION_STATE State
)
/*++
Routine Description:
This routine is called from AllocateEntry once we have decided where we want
to allocate from. It tries to find a free range that matches the
requirements in State while restricting its possible solutions to the range
State->Start to State->CurrentMaximum. On success State->Start and
State->End represent this range. Conflicts between boot configs are allowed
Arguments:
Arbiter - The instance data of the arbiter who was called.
State - The state of the current arbitration.
Return Value:
TRUE if we found a range, FALSE otherwise.
--*/
{
//
// If this was a boot config then consider other boot configs to be
// available
//
if (State->Entry->Flags & ARBITER_FLAG_BOOT_CONFIG) {
State->RangeAvailableAttributes |= ARBITER_RANGE_BOOT_ALLOCATED;
}
//
// Do the default thing
//
return ArbFindSuitableRange(Arbiter, State);
}