You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1397 lines
35 KiB
1397 lines
35 KiB
/*++
|
|
|
|
Copyright (c) Microsoft Corporation. All rights reserved.
|
|
|
|
Module Name:
|
|
|
|
pnpmemio.c
|
|
|
|
Abstract:
|
|
|
|
Root IO Port and Memory arbiter
|
|
|
|
Author:
|
|
|
|
Andy Thornton (andrewth) 04/17/97
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "pnpmgrp.h"
|
|
#pragma hdrstop
|
|
|
|
#define BUGFEST_HACKS
|
|
|
|
//
|
|
// Constants
|
|
//
|
|
|
|
#define MAX_ULONGLONG ((ULONGLONG) -1)
|
|
#define MAX_ALIAS_PORT 0x0000FFFF
|
|
|
|
typedef struct _PORT_ARBITER_EXTENSION {
|
|
|
|
PRTL_RANGE_LIST Aliases;
|
|
PRTL_RANGE_LIST PossibleAliases;
|
|
RTL_RANGE_LIST RangeLists[2];
|
|
|
|
} PORT_ARBITER_EXTENSION, *PPORT_ARBITER_EXTENSION;
|
|
|
|
//
|
|
// Prototypes
|
|
//
|
|
|
|
VOID
|
|
IopPortBacktrackAllocation(
|
|
IN PARBITER_INSTANCE Arbiter,
|
|
IN PARBITER_ALLOCATION_STATE State
|
|
);
|
|
|
|
BOOLEAN
|
|
IopPortGetNextAlias(
|
|
ULONG IoDescriptorFlags,
|
|
ULONGLONG LastAlias,
|
|
PULONGLONG NextAlias
|
|
);
|
|
|
|
BOOLEAN
|
|
IopPortFindSuitableRange(
|
|
PARBITER_INSTANCE Arbiter,
|
|
PARBITER_ALLOCATION_STATE State
|
|
);
|
|
|
|
BOOLEAN
|
|
IopMemFindSuitableRange(
|
|
PARBITER_INSTANCE Arbiter,
|
|
PARBITER_ALLOCATION_STATE State
|
|
);
|
|
|
|
|
|
NTSTATUS
|
|
IopGenericUnpackRequirement(
|
|
IN PIO_RESOURCE_DESCRIPTOR Descriptor,
|
|
OUT PULONGLONG Minimum,
|
|
OUT PULONGLONG Maximum,
|
|
OUT PULONG Length,
|
|
OUT PULONG Alignment
|
|
);
|
|
|
|
NTSTATUS
|
|
IopGenericPackResource(
|
|
IN PIO_RESOURCE_DESCRIPTOR Requirement,
|
|
IN ULONGLONG Start,
|
|
OUT PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor
|
|
);
|
|
|
|
LONG
|
|
IopGenericScoreRequirement(
|
|
IN PIO_RESOURCE_DESCRIPTOR Descriptor
|
|
);
|
|
|
|
NTSTATUS
|
|
IopGenericUnpackResource(
|
|
IN PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor,
|
|
OUT PULONGLONG Start,
|
|
OUT PULONG Length
|
|
);
|
|
|
|
BOOLEAN
|
|
IopPortIsAliasedRangeAvailable(
|
|
IN PARBITER_INSTANCE Arbiter,
|
|
IN PARBITER_ALLOCATION_STATE State
|
|
);
|
|
|
|
NTSTATUS
|
|
IopMemInitialize(
|
|
VOID
|
|
);
|
|
|
|
VOID
|
|
IopPortAddAllocation(
|
|
IN PARBITER_INSTANCE Arbiter,
|
|
IN PARBITER_ALLOCATION_STATE State
|
|
);
|
|
|
|
NTSTATUS
|
|
IopTranslateBusAddress(
|
|
IN PHYSICAL_ADDRESS SourceAddress,
|
|
IN UCHAR SourceResourceType,
|
|
OUT PPHYSICAL_ADDRESS TargetAddress,
|
|
OUT PUCHAR TargetResourceType
|
|
);
|
|
|
|
|
|
//
|
|
// Make everything pageable
|
|
//
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
|
|
NTSTATUS
|
|
IopGenericTranslateOrdering(
|
|
OUT PIO_RESOURCE_DESCRIPTOR Target,
|
|
IN PIO_RESOURCE_DESCRIPTOR Source
|
|
);
|
|
#pragma alloc_text(PAGE, IopGenericTranslateOrdering)
|
|
#pragma alloc_text(PAGE, IopPortInitialize)
|
|
#pragma alloc_text(PAGE, IopMemInitialize)
|
|
#pragma alloc_text(PAGE, IopGenericUnpackRequirement)
|
|
#pragma alloc_text(PAGE, IopGenericPackResource)
|
|
#pragma alloc_text(PAGE, IopGenericScoreRequirement)
|
|
#pragma alloc_text(PAGE, IopGenericUnpackResource)
|
|
#pragma alloc_text(PAGE, IopPortBacktrackAllocation)
|
|
#pragma alloc_text(PAGE, IopPortFindSuitableRange)
|
|
#pragma alloc_text(PAGE, IopMemFindSuitableRange)
|
|
#pragma alloc_text(PAGE, IopPortGetNextAlias)
|
|
#pragma alloc_text(PAGE, IopPortAddAllocation)
|
|
#pragma alloc_text(PAGE, IopPortIsAliasedRangeAvailable)
|
|
#pragma alloc_text(PAGE, IopTranslateBusAddress)
|
|
#endif // ALLOC_PRAGMA
|
|
|
|
|
|
#define ADDRESS_SPACE_MEMORY 0x0
|
|
#define ADDRESS_SPACE_PORT 0x1
|
|
#define ADDRESS_SPACE_USER_MEMORY 0x2
|
|
#define ADDRESS_SPACE_USER_PORT 0x3
|
|
#define ADDRESS_SPACE_DENSE_MEMORY 0x4
|
|
#define ADDRESS_SPACE_USER_DENSE_MEMORY 0x6
|
|
|
|
NTSTATUS
|
|
IopTranslateBusAddress(
|
|
IN PHYSICAL_ADDRESS SourceAddress,
|
|
IN UCHAR SourceResourceType,
|
|
OUT PPHYSICAL_ADDRESS TargetAddress,
|
|
OUT PUCHAR TargetResourceType
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine translates addresses.
|
|
|
|
Parameters:
|
|
|
|
SourceAddress - The address to translate
|
|
|
|
ResourceType - The resource type (IO or Memory) we are translaing. If the
|
|
address space changes from IO->Memory this will be updated.
|
|
|
|
TargetAddress - Pointer to where the target should be translated to.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS or an error status
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG sourceAddressSpace, targetAddressSpace;
|
|
BOOLEAN translated;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Select the appropriate address space
|
|
//
|
|
|
|
if (SourceResourceType == CmResourceTypeMemory) {
|
|
sourceAddressSpace = ADDRESS_SPACE_MEMORY;
|
|
} else if (SourceResourceType == CmResourceTypePort) {
|
|
sourceAddressSpace = ADDRESS_SPACE_PORT;
|
|
} else {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
ARB_PRINT(
|
|
2,
|
|
("Translating %s address 0x%I64x => ",
|
|
SourceResourceType == CmResourceTypeMemory ? "Memory" : "I/O",
|
|
SourceAddress.QuadPart
|
|
));
|
|
|
|
//
|
|
// HACKHACK Ask the HAL to translate on ISA bus - if we can't then just
|
|
// don't translate because this must be a PCI system so the root arbiters
|
|
// don't do much (Yes it's a steaming hack but it'll work for beta 1)
|
|
//
|
|
|
|
targetAddressSpace = sourceAddressSpace;
|
|
translated = HalTranslateBusAddress(
|
|
Isa,
|
|
0,
|
|
SourceAddress,
|
|
&targetAddressSpace,
|
|
TargetAddress
|
|
);
|
|
|
|
if (!translated) {
|
|
ARB_PRINT(2,("Translation failed!\n"));
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
//
|
|
// Update the resource type in the target if we have gone from Io to Memory
|
|
//
|
|
|
|
|
|
//
|
|
// BUBBUG - update the length for IO -> Memory (Dense vs Sparse)
|
|
// I think the answer is dense -> spares is multiply length by 32
|
|
//
|
|
|
|
if (targetAddressSpace == ADDRESS_SPACE_MEMORY
|
|
|| targetAddressSpace == ADDRESS_SPACE_USER_MEMORY
|
|
|| targetAddressSpace == ADDRESS_SPACE_DENSE_MEMORY
|
|
|| targetAddressSpace == ADDRESS_SPACE_USER_DENSE_MEMORY) {
|
|
*TargetResourceType = CmResourceTypeMemory;
|
|
} else if (targetAddressSpace == ADDRESS_SPACE_PORT
|
|
|| targetAddressSpace == ADDRESS_SPACE_USER_PORT) {
|
|
*TargetResourceType = CmResourceTypePort;
|
|
} else {
|
|
ASSERT(0 && "Translation has returned an unknown address space");
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
ARB_PRINT(
|
|
2,
|
|
("%s address 0x%I64x\n",
|
|
*TargetResourceType == CmResourceTypeMemory ? "Memory" : "I/O",
|
|
TargetAddress->QuadPart
|
|
));
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
IopGenericTranslateOrdering(
|
|
OUT PIO_RESOURCE_DESCRIPTOR Target,
|
|
IN PIO_RESOURCE_DESCRIPTOR Source
|
|
)
|
|
|
|
/*
|
|
|
|
Routine Description:
|
|
|
|
This routine is called during arbiter initialization to translate the
|
|
orderings.
|
|
|
|
Parameters:
|
|
|
|
Target - Place to put the translated descriptor
|
|
|
|
Source - Descriptor to translate
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS
|
|
|
|
|
|
|
|
*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
UCHAR initialResourceType, minResourceType, maxResourceType;
|
|
PAGED_CODE();
|
|
|
|
|
|
*Target = *Source;
|
|
|
|
if (Source->Type != CmResourceTypeMemory
|
|
&& Source->Type != CmResourceTypePort) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
initialResourceType = Source->Type;
|
|
|
|
//
|
|
// Translate the minimum
|
|
//
|
|
|
|
status = IopTranslateBusAddress(Source->u.Generic.MinimumAddress,
|
|
initialResourceType,
|
|
&Target->u.Generic.MinimumAddress,
|
|
&minResourceType
|
|
);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Translate the maximum iff we could translate the minimum
|
|
//
|
|
|
|
status = IopTranslateBusAddress(Source->u.Generic.MaximumAddress,
|
|
initialResourceType,
|
|
&Target->u.Generic.MaximumAddress,
|
|
&maxResourceType
|
|
);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
ASSERT(minResourceType == maxResourceType);
|
|
Target->Type = minResourceType;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we couldn't translate both ends of the range then we want to skip this
|
|
// range - set its type to CmResourceTypeNull
|
|
//
|
|
|
|
ASSERT (!NT_SUCCESS(status));
|
|
Target->Type = CmResourceTypeNull;
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
//
|
|
// Implementation
|
|
//
|
|
|
|
NTSTATUS
|
|
IopPortInitialize(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initializes the arbiter
|
|
|
|
Parameters:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Fill in the non-default action handlers
|
|
//
|
|
|
|
IopRootPortArbiter.FindSuitableRange = IopPortFindSuitableRange;
|
|
IopRootPortArbiter.AddAllocation = IopPortAddAllocation;
|
|
IopRootPortArbiter.BacktrackAllocation = IopPortBacktrackAllocation;
|
|
|
|
IopRootPortArbiter.UnpackRequirement = IopGenericUnpackRequirement;
|
|
IopRootPortArbiter.PackResource = IopGenericPackResource;
|
|
IopRootPortArbiter.UnpackResource = IopGenericUnpackResource;
|
|
IopRootPortArbiter.ScoreRequirement = IopGenericScoreRequirement;
|
|
|
|
return ArbInitializeArbiterInstance(&IopRootPortArbiter,
|
|
NULL, // Indicates ROOT arbiter
|
|
CmResourceTypePort,
|
|
L"RootPort",
|
|
L"Root",
|
|
IopGenericTranslateOrdering
|
|
);
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
IopMemInitialize(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initializes the arbiter
|
|
|
|
Parameters:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
IopRootMemArbiter.UnpackRequirement = IopGenericUnpackRequirement;
|
|
IopRootMemArbiter.PackResource = IopGenericPackResource;
|
|
IopRootMemArbiter.UnpackResource = IopGenericUnpackResource;
|
|
IopRootMemArbiter.ScoreRequirement = IopGenericScoreRequirement;
|
|
|
|
IopRootMemArbiter.FindSuitableRange = IopMemFindSuitableRange;
|
|
|
|
status = ArbInitializeArbiterInstance(&IopRootMemArbiter,
|
|
NULL, // Indicates ROOT arbiter
|
|
CmResourceTypeMemory,
|
|
L"RootMemory",
|
|
L"Root",
|
|
IopGenericTranslateOrdering
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Allocate the first page of physical memory as the firmware uses it and
|
|
// doesn't report it as so Mm doesn't reuse it.
|
|
//
|
|
|
|
status = RtlAddRange(IopRootMemArbiter.Allocation,
|
|
0,
|
|
PAGE_SIZE - 1,
|
|
0, // RangeAttributes
|
|
0, // Flags
|
|
NULL,
|
|
NULL
|
|
);
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Arbiter callbacks
|
|
//
|
|
|
|
NTSTATUS
|
|
IopGenericUnpackRequirement(
|
|
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.
|
|
|
|
--*/
|
|
|
|
{
|
|
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) {
|
|
*Maximum = 0xFFFFFF;
|
|
}
|
|
|
|
ARB_PRINT(2,
|
|
("Unpacking %s requirement %p => 0x%I64x-0x%I64x length 0x%x alignment 0x%x\n",
|
|
Descriptor->Type == CmResourceTypePort ? "port" : "memory",
|
|
Descriptor,
|
|
*Minimum,
|
|
*Maximum,
|
|
*Length,
|
|
*Alignment
|
|
));
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
LONG
|
|
IopGenericScoreRequirement(
|
|
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;
|
|
LONGLONG bigscore;
|
|
ULONG alignment;
|
|
|
|
PAGED_CODE();
|
|
|
|
#define MAX_SCORE MAXLONG
|
|
|
|
ASSERT(Descriptor);
|
|
ASSERT((Descriptor->Type == CmResourceTypePort) ||
|
|
(Descriptor->Type == CmResourceTypeMemory));
|
|
|
|
alignment = Descriptor->u.Generic.Alignment;
|
|
|
|
//
|
|
// Fix the broken hardware that reports 0 alignment
|
|
// Since this is not a PCI device, set the alignment to 1.
|
|
//
|
|
//
|
|
|
|
if (alignment == 0) {
|
|
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;
|
|
|
|
score = (LONG)bigscore;
|
|
if (bigscore < 0) {
|
|
score = -1;
|
|
} else if (bigscore > MAX_SCORE) {
|
|
score = MAX_SCORE;
|
|
}
|
|
|
|
ARB_PRINT(2,
|
|
("Scoring port resource %p(0x%I64x-0x%I64x) => %i\n",
|
|
Descriptor->Type == CmResourceTypePort ? "port" : "memory",
|
|
Descriptor,
|
|
Descriptor->u.Generic.MinimumAddress.QuadPart,
|
|
end,
|
|
score
|
|
));
|
|
|
|
return score;
|
|
}
|
|
|
|
NTSTATUS
|
|
IopGenericPackResource(
|
|
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;
|
|
|
|
ARB_PRINT(2,
|
|
("Packing %s resource %p => 0x%I64x length 0x%x\n",
|
|
Descriptor->Type == CmResourceTypePort ? "port" : "memory",
|
|
Descriptor,
|
|
Descriptor->u.Port.Start.QuadPart,
|
|
Descriptor->u.Port.Length
|
|
));
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
IopGenericUnpackResource(
|
|
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.
|
|
|
|
Length - Pointer to where the length value should be unpacked to.
|
|
|
|
Return Value:
|
|
|
|
Returns the status of this operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PAGED_CODE();
|
|
ASSERT(Descriptor);
|
|
ASSERT(Descriptor->Type == CmResourceTypePort
|
|
|| Descriptor->Type == CmResourceTypeMemory);
|
|
|
|
*Start = Descriptor->u.Generic.Start.QuadPart;
|
|
*Length = Descriptor->u.Generic.Length;
|
|
|
|
ARB_PRINT(2,
|
|
("Unpacking %s resource %p => 0x%I64x Length 0x%x\n",
|
|
Descriptor->Type == CmResourceTypePort ? "port" : "memory",
|
|
Descriptor,
|
|
*Start,
|
|
*Length
|
|
));
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
#if 0
|
|
NTSTATUS
|
|
IopPortRetestAllocation(
|
|
IN PARBITER_INSTANCE Arbiter,
|
|
IN OUT PLIST_ENTRY ArbitrationList
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This providesa port specific implementation of the RetestAllocation action
|
|
which takes into account ISA aliases and adds them where appropriate.
|
|
It walks the arbitration list and updates the possible allocation to reflect
|
|
the allocation entries of the list. For these entries to be valid
|
|
TestAllocation must have been performed on this arbitration list.
|
|
|
|
Parameters:
|
|
|
|
Arbiter - The arbiter instance data for the arbiter being called.
|
|
|
|
ArbitrationList - A list of ARBITER_LIST_ENTRY entries which contain the
|
|
requirements and associated devices. TestAllocation for this arbiter
|
|
should have been called on this list.
|
|
|
|
Return Value:
|
|
|
|
Status code that indicates whether or not the function was successful.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
PARBITER_LIST_ENTRY current;
|
|
PIO_RESOURCE_DESCRIPTOR alternative;
|
|
ULONGLONG start;
|
|
ULONG length;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Copy the current allocation and reserved
|
|
//
|
|
|
|
ARB_PRINT(3, ("Retest: Copy current allocation\n"));
|
|
status = RtlCopyRangeList(Arbiter->PossibleAllocation, Arbiter->Allocation);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Free all the resources currently allocated to all the devices we
|
|
// are arbitrating for
|
|
//
|
|
|
|
FOR_ALL_IN_LIST(ARBITER_LIST_ENTRY, ArbitrationList, current) {
|
|
|
|
ARB_PRINT(2, ("Retest: Delete 0x%08x's resources\n", current->PhysicalDeviceObject));
|
|
|
|
status = RtlDeleteOwnersRanges(Arbiter->PossibleAllocation,
|
|
(PVOID) current->PhysicalDeviceObject
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Copy the previous allocation into the range list
|
|
//
|
|
|
|
FOR_ALL_IN_LIST(ARBITER_LIST_ENTRY, ArbitrationList, current) {
|
|
|
|
ASSERT(current->Assignment);
|
|
|
|
status = Arbiter->UnpackResource(current->Assignment,
|
|
&start,
|
|
&length
|
|
);
|
|
|
|
ASSERT(NT_SUCCESS(status));
|
|
|
|
//
|
|
// If we had a requirement for length 0 then that will be seen as
|
|
// end == start - 1 here so don't attempt to add the range - it will
|
|
// fail!
|
|
//
|
|
|
|
if (length != 0) {
|
|
|
|
status = RtlAddRange(
|
|
Arbiter->PossibleAllocation,
|
|
start,
|
|
start + length - 1,
|
|
0,
|
|
RTL_RANGE_LIST_ADD_IF_CONFLICT +
|
|
(current->Assignment->ShareDisposition == CmResourceShareShared ?
|
|
RTL_RANGE_LIST_ADD_SHARED : 0),
|
|
NULL,
|
|
current->PhysicalDeviceObject
|
|
);
|
|
|
|
ASSERT(NT_SUCCESS(status));
|
|
|
|
//
|
|
// Retireve the alternative from which the assignment was chosen from
|
|
// then
|
|
//
|
|
|
|
alternative = current->SelectedAlternative;
|
|
|
|
//
|
|
// Add the aliases
|
|
//
|
|
|
|
if (alternative->Flags & CM_RESOURCE_PORT_10_BIT_DECODE
|
|
|| alternative->Flags & CM_RESOURCE_PORT_12_BIT_DECODE) {
|
|
|
|
ULONGLONG alias = start;
|
|
BOOLEAN shared = current->Assignment->ShareDisposition ==
|
|
CmResourceShareShared;
|
|
|
|
ARB_PRINT(3, ("Adding aliases\n"));
|
|
|
|
while (IopPortGetNextAlias(alternative->Flags,
|
|
alias,
|
|
&alias)) {
|
|
|
|
status = RtlAddRange(
|
|
Arbiter->PossibleAllocation,
|
|
alias,
|
|
alias + length - 1,
|
|
ARBITER_RANGE_ALIAS,
|
|
RTL_RANGE_LIST_ADD_IF_CONFLICT +
|
|
(shared ? RTL_RANGE_LIST_SHARED_OK : 0),
|
|
NULL,
|
|
current->PhysicalDeviceObject
|
|
);
|
|
|
|
//
|
|
// We have already checked if these ranges are available
|
|
// so we should not fail...
|
|
//
|
|
|
|
ASSERT(NT_SUCCESS(status));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return status;
|
|
|
|
cleanup:
|
|
|
|
RtlFreeRangeList(Arbiter->PossibleAllocation);
|
|
return status;
|
|
}
|
|
#endif
|
|
VOID
|
|
IopPortBacktrackAllocation(
|
|
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.
|
|
|
|
--*/
|
|
|
|
|
|
{
|
|
|
|
NTSTATUS status;
|
|
ULONGLONG alias = State->Start;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Delete the aliases
|
|
//
|
|
|
|
ARB_PRINT(2, ("\t\tDeleting aliases\n"));
|
|
|
|
while (IopPortGetNextAlias(State->CurrentAlternative->Flags,
|
|
alias,
|
|
&alias)) {
|
|
|
|
status = RtlDeleteRange(
|
|
Arbiter->PossibleAllocation,
|
|
alias,
|
|
alias + State->CurrentAlternative->Length - 1,
|
|
State->Entry->PhysicalDeviceObject
|
|
);
|
|
|
|
//
|
|
// We should not fail...
|
|
//
|
|
|
|
ASSERT(NT_SUCCESS(status));
|
|
}
|
|
|
|
//
|
|
// Now call the original function to delete the base range
|
|
//
|
|
|
|
ArbBacktrackAllocation(Arbiter, State);
|
|
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
IopPortFindSuitableRange(
|
|
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 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;
|
|
UCHAR userFlagsMask = 0;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// If we are asking for zero ports then trivially succeed with the minimum
|
|
// value
|
|
//
|
|
|
|
if (State->CurrentAlternative->Length == 0) {
|
|
State->End = State->Start;
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// 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) {
|
|
|
|
userFlagsMask = ARBITER_RANGE_BOOT_ALLOCATED;
|
|
}
|
|
|
|
//
|
|
// Try to satisfy the request
|
|
//
|
|
|
|
while (State->CurrentMinimum <= State->CurrentMaximum) {
|
|
|
|
//
|
|
// Select the first free alternative from the current alternative
|
|
//
|
|
|
|
status = RtlFindRange(
|
|
Arbiter->PossibleAllocation,
|
|
State->CurrentMinimum,
|
|
State->CurrentMaximum,
|
|
State->CurrentAlternative->Length,
|
|
State->CurrentAlternative->Alignment,
|
|
State->CurrentAlternative->Flags &
|
|
ARBITER_ALTERNATIVE_FLAG_SHARED ?
|
|
RTL_RANGE_LIST_SHARED_OK : 0,
|
|
userFlagsMask,
|
|
Arbiter->ConflictCallbackContext,
|
|
Arbiter->ConflictCallback,
|
|
&State->Start
|
|
);
|
|
|
|
|
|
//
|
|
// Did we find a range and if not can we override any conflict
|
|
//
|
|
if (NT_SUCCESS(status)
|
|
|| Arbiter->OverrideConflict(Arbiter, State)) {
|
|
|
|
State->End = State->Start + State->CurrentAlternative->Length - 1;
|
|
|
|
//
|
|
// Check if the aliases are available
|
|
//
|
|
if (IopPortIsAliasedRangeAvailable(Arbiter, State)) {
|
|
|
|
//
|
|
// We found a suitable range so return
|
|
//
|
|
|
|
return TRUE;
|
|
|
|
} else {
|
|
|
|
//
|
|
// This range's aliases arn't available so try the next range
|
|
//
|
|
|
|
State->Start += State->CurrentAlternative->Length;
|
|
|
|
continue;
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// We couldn't find a base range
|
|
//
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN
|
|
IopPortGetNextAlias(
|
|
ULONG IoDescriptorFlags,
|
|
ULONGLONG LastAlias,
|
|
PULONGLONG NextAlias
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine calculates the next alias of an IO port up to MAX_ALIAS_PORT.
|
|
|
|
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 + (((ULONGLONG)1) << 10);
|
|
} else if (IoDescriptorFlags & CM_RESOURCE_PORT_12_BIT_DECODE) {
|
|
next = LastAlias + (((ULONGLONG)1) << 12);
|
|
} else {
|
|
|
|
//
|
|
// There are no aliases
|
|
//
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Check that we are below the maximum aliased port
|
|
//
|
|
|
|
if (next > MAX_ALIAS_PORT) {
|
|
return FALSE;
|
|
} else {
|
|
*NextAlias = next;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
IopPortAddAllocation(
|
|
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.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
ULONGLONG alias;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(Arbiter);
|
|
ASSERT(State);
|
|
|
|
status = RtlAddRange(Arbiter->PossibleAllocation,
|
|
State->Start,
|
|
State->End,
|
|
State->RangeAttributes,
|
|
RTL_RANGE_LIST_ADD_IF_CONFLICT +
|
|
(State->CurrentAlternative->Flags & ARBITER_ALTERNATIVE_FLAG_SHARED
|
|
? RTL_RANGE_LIST_ADD_SHARED : 0),
|
|
NULL,
|
|
State->Entry->PhysicalDeviceObject
|
|
);
|
|
|
|
ASSERT(NT_SUCCESS(status));
|
|
|
|
//
|
|
// Add any aliases
|
|
//
|
|
|
|
alias = State->Start;
|
|
ARB_PRINT(2, ("Adding aliases\n"));
|
|
|
|
while (IopPortGetNextAlias(State->CurrentAlternative->Descriptor->Flags,
|
|
alias,
|
|
&alias)) {
|
|
|
|
status = RtlAddRange(Arbiter->PossibleAllocation,
|
|
alias,
|
|
alias + State->CurrentAlternative->Length - 1,
|
|
(UCHAR) (State->RangeAttributes | ARBITER_RANGE_ALIAS),
|
|
RTL_RANGE_LIST_ADD_IF_CONFLICT +
|
|
(State->CurrentAlternative->Flags & ARBITER_ALTERNATIVE_FLAG_SHARED
|
|
? RTL_RANGE_LIST_ADD_SHARED : 0),
|
|
NULL,
|
|
State->Entry->PhysicalDeviceObject
|
|
);
|
|
|
|
//
|
|
// We have already checked if these ranges are available
|
|
// so we should not fail...
|
|
//
|
|
|
|
ASSERT(NT_SUCCESS(status));
|
|
}
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
IopPortIsAliasedRangeAvailable(
|
|
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.
|
|
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// NTRAID #61146-2000/03/31-andrewth Root IO arbiter don't deal with aliases
|
|
//
|
|
// This is only an issue on machines where the root arbiters are called upon
|
|
// to arbitrated aliased ranges - this means a pure ISA machine with no PCI.
|
|
// I hope we won't support these soon and the root arbiters can be made a
|
|
// lot worse.
|
|
//
|
|
|
|
#if defined(BUGFEST_HACKS)
|
|
|
|
UNREFERENCED_PARAMETER( Arbiter );
|
|
UNREFERENCED_PARAMETER( State );
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// For the purposes of the Bug^H^H^HPlugFest don't mind is aliases conflict
|
|
// with any devices but still add them...
|
|
//
|
|
return TRUE;
|
|
#else
|
|
NTSTATUS status;
|
|
ULONGLONG alias = State->Start;
|
|
BOOLEAN aliasAvailable;
|
|
UCHAR userFlagsMask = 0;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// 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) {
|
|
|
|
userFlagsMask |= ARBITER_RANGE_BOOT_ALLOCATED;
|
|
}
|
|
|
|
while (IopPortGetNextAlias(State->CurrentAlternative->Descriptor->Flags,
|
|
alias,
|
|
&alias)) {
|
|
|
|
status = RtlIsRangeAvailable(
|
|
Arbiter->PossibleAllocation,
|
|
alias,
|
|
alias + State->CurrentAlternative->Length - 1,
|
|
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
|
|
//
|
|
|
|
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;
|
|
#endif
|
|
}
|
|
|
|
BOOLEAN
|
|
IopMemFindSuitableRange(
|
|
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);
|
|
}
|
|
|