windows-nt/Source/XPSP1/NT/base/busdrv/acpi/driver/nt/translate.c
2020-09-26 16:20:57 +08:00

755 lines
20 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1997 Microsoft Corporation
Module Name:
translate.c
Abstract:
This file implements translator interfaces for busses
enumerated by ACPI.
The actual translation information about this bus is
gotten from the _CRS associated with the bus. We put
the data into an IO_RESOURCE_REQUIREMENTS_LIST as
device private data of the following form:
DevicePrivate.Data[0]: child's CM_RESOURCE_TYPE
DevicePrivate.Data[1]: child's start address [31:0]
DevicePrivate.Data[2]: child's start address [63:32]
The descriptor that describes child-side translation
immediately follows the one that describes the
parent-side resources.
The Flags field of the IO_RESOURCE_REQUIREMENTS_LIST may have the
TRANSLATION_RANGE_SPARSE bit set.
Author:
Jake Oshins 7-Nov-97
Environment:
NT Kernel Model Driver only
--*/
#include "pch.h"
NTSTATUS
FindTranslationRange(
IN PHYSICAL_ADDRESS Start,
IN LONGLONG Length,
IN PBRIDGE_TRANSLATOR Translator,
IN RESOURCE_TRANSLATION_DIRECTION Direction,
IN UCHAR ResType,
OUT PBRIDGE_WINDOW *Window
);
NTSTATUS
TranslateBridgeResources(
IN PVOID Context,
IN PCM_PARTIAL_RESOURCE_DESCRIPTOR Source,
IN RESOURCE_TRANSLATION_DIRECTION Direction,
IN ULONG AlternativesCount, OPTIONAL
IN IO_RESOURCE_DESCRIPTOR Alternatives[], OPTIONAL
IN PDEVICE_OBJECT PhysicalDeviceObject,
OUT PCM_PARTIAL_RESOURCE_DESCRIPTOR Target
);
NTSTATUS
TranslateBridgeRequirements(
IN PVOID Context,
IN PIO_RESOURCE_DESCRIPTOR Source,
IN PDEVICE_OBJECT PhysicalDeviceObject,
OUT PULONG TargetCount,
OUT PIO_RESOURCE_DESCRIPTOR *Target
);
NTSTATUS
BuildTranslatorRanges(
IN PBRIDGE_TRANSLATOR Translator,
OUT ULONG *BridgeWindowCount,
OUT PBRIDGE_WINDOW *Window
);
#define MAX(a, b) \
((a) > (b) ? (a) : (b))
#define MIN(a, b) \
((a) < (b) ? (a) : (b))
HAL_PORT_RANGE_INTERFACE HalPortRangeInterface;
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, TranslateEjectInterface)
#pragma alloc_text(PAGE, TranslateBridgeResources)
#pragma alloc_text(PAGE, TranslateBridgeRequirements)
#pragma alloc_text(PAGE, FindTranslationRange)
#pragma alloc_text(PAGE, AcpiNullReference)
#pragma alloc_text(PAGE, BuildTranslatorRanges)
#endif
NTSTATUS
TranslateEjectInterface(
PDEVICE_OBJECT DeviceObject,
PIRP Irp
)
{
PIO_RESOURCE_REQUIREMENTS_LIST ioList = NULL;
PIO_RESOURCE_DESCRIPTOR transDesc;
PIO_RESOURCE_DESCRIPTOR parentDesc;
PTRANSLATOR_INTERFACE transInterface;
PBRIDGE_TRANSLATOR bridgeTrans;
PIO_STACK_LOCATION irpSp;
PDEVICE_EXTENSION devExtension;
BOOLEAN foundTranslations = FALSE;
NTSTATUS status;
PUCHAR crsBuf;
ULONG descCount;
ULONG parentResType;
ULONG childResType;
ULONG crsBufSize;
PHYSICAL_ADDRESS parentStart;
PHYSICAL_ADDRESS childStart;
PAGED_CODE();
devExtension = ACPIInternalGetDeviceExtension(DeviceObject);
ASSERT(devExtension);
ASSERT(devExtension->AcpiObject);
irpSp = IoGetCurrentIrpStackLocation(Irp);
ASSERT(irpSp->Parameters.QueryInterface.Size >= sizeof(TRANSLATOR_INTERFACE));
transInterface = (PTRANSLATOR_INTERFACE)irpSp->Parameters.QueryInterface.Interface;
ASSERT(transInterface);
//
// Get the resources for this bus.
//
status = ACPIGetBufferSync(
devExtension,
PACKED_CRS,
&crsBuf,
&crsBufSize
);
if (!NT_SUCCESS(status)) {
//
// This bus has no _CRS. So it doesn't need a translator.
//
return Irp->IoStatus.Status;
}
//
// Turn it into something meaningful.
//
status = PnpBiosResourcesToNtResources(
crsBuf,
PNP_BIOS_TO_IO_NO_CONSUMED_RESOURCES,
&ioList
);
if (!NT_SUCCESS(status)) {
goto TranslateEjectInterfaceExit;
}
//
// Cycle through the descriptors looking for device private data
// that contains translation information.
//
for (descCount = 0; descCount < ioList->List[0].Count; descCount++) {
transDesc = &ioList->List[0].Descriptors[descCount];
if (transDesc->Type == CmResourceTypeDevicePrivate) {
//
// Translation information is contained in
// a device private resource that has
// TRANSLATION_DATA_PARENT_ADDRESS in the
// flags field.
//
if (transDesc->Flags & TRANSLATION_DATA_PARENT_ADDRESS) {
// The first descriptor cannot be a translation descriptor
ASSERT(descCount != 0);
//
// The translation descriptor should follow the descriptor
// that it is trying to modify. The first, normal,
// descriptor is for the child-relative resources. The
// second, device private, descriptor is modifies the
// child-relative resources to generate the parent-relative
// resources.
//
parentResType = transDesc->u.DevicePrivate.Data[0];
parentStart.LowPart = transDesc->u.DevicePrivate.Data[1];
parentStart.HighPart = transDesc->u.DevicePrivate.Data[2];
childResType = ioList->List[0].Descriptors[descCount - 1].Type;
childStart.QuadPart = (transDesc - 1)->u.Generic.MinimumAddress.QuadPart;
if ((parentResType != childResType) ||
(parentStart.QuadPart != childStart.QuadPart)) {
foundTranslations = TRUE;
break;
}
}
}
}
if (!foundTranslations) {
//
// Didn't find any translation information for this bus.
//
status = Irp->IoStatus.Status;
ExFreePool(ioList);
goto TranslateEjectInterfaceExit;
}
//
// Build a translator interface.
//
bridgeTrans = ExAllocatePoolWithTag(
PagedPool,
sizeof (BRIDGE_TRANSLATOR),
ACPI_TRANSLATE_POOLTAG
);
if (!bridgeTrans) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto TranslateEjectInterfaceExit;
}
bridgeTrans->AcpiObject = devExtension->AcpiObject;
bridgeTrans->IoList = ioList;
//
// Build the array of bridge windows.
//
status = BuildTranslatorRanges(
bridgeTrans,
&bridgeTrans->RangeCount,
&bridgeTrans->Ranges
);
if (!NT_SUCCESS(status)) {
goto TranslateEjectInterfaceExit;
}
transInterface->Size = sizeof(TRANSLATOR_INTERFACE);
transInterface->Version = 1;
transInterface->Context = (PVOID)bridgeTrans;
transInterface->InterfaceReference = AcpiNullReference;
transInterface->InterfaceDereference = AcpiNullReference;
transInterface->TranslateResources = TranslateBridgeResources;
transInterface->TranslateResourceRequirements = TranslateBridgeRequirements;
status = STATUS_SUCCESS;
TranslateEjectInterfaceExit:
return status;
}
NTSTATUS
FindTranslationRange(
IN PHYSICAL_ADDRESS Start,
IN LONGLONG Length,
IN PBRIDGE_TRANSLATOR Translator,
IN RESOURCE_TRANSLATION_DIRECTION Direction,
IN UCHAR ResType,
OUT PBRIDGE_WINDOW *Window
)
{
LONGLONG beginning, end;
ULONG i;
UCHAR rangeType;
PAGED_CODE();
for (i = 0; i < Translator->RangeCount; i++) {
if (Direction == TranslateParentToChild) {
beginning = Translator->Ranges[i].ParentAddress.QuadPart;
rangeType = Translator->Ranges[i].ParentType;
} else {
beginning = Translator->Ranges[i].ChildAddress.QuadPart;
rangeType = Translator->Ranges[i].ChildType;
}
end = beginning + Translator->Ranges[i].Length;
if ((rangeType == ResType) &&
(!((Start.QuadPart < beginning) ||
(Start.QuadPart + Length > end)))) {
//
// The range lies within this bridge window
// and the resource types match.
//
*Window = &Translator->Ranges[i];
return STATUS_SUCCESS;
}
}
return STATUS_NOT_FOUND;
}
NTSTATUS
TranslateBridgeResources(
IN PVOID Context,
IN PCM_PARTIAL_RESOURCE_DESCRIPTOR Source,
IN RESOURCE_TRANSLATION_DIRECTION Direction,
IN ULONG AlternativesCount, OPTIONAL
IN IO_RESOURCE_DESCRIPTOR Alternatives[], OPTIONAL
IN PDEVICE_OBJECT PhysicalDeviceObject,
OUT PCM_PARTIAL_RESOURCE_DESCRIPTOR Target
)
/*++
Routine Description:
This function takes a set of resources that are
passed through a bridge and does any translation
that is necessary when changing from a parent-
relative viewpoint to a child-relative one or
back again.
In this function, we have the notion of a "window,"
which is an aperature within the bridge. Bridges
often have multiple windows, each with distinct
translations.
This function should never fail, as any resource
range that will fail translation should have already
been stripped out by TranslateBridgeRequirements.
Arguments:
Context - pointer to the translation data
Source - resource list to be translated
Direction - TranslateChildToParent or
TranslateParentToChild
AlternativesCount - not used
Alternatives - not used
PhysicalDeviceObject - not used
Target - translated resource list
Return Value:
Status
--*/
{
PBRIDGE_TRANSLATOR translator;
PBRIDGE_WINDOW window;
NTSTATUS status;
PAGED_CODE();
ASSERT(Context);
ASSERT((Source->Type == CmResourceTypePort) ||
(Source->Type == CmResourceTypeMemory));
translator = (PBRIDGE_TRANSLATOR)Context;
ASSERT(translator->RangeCount > 0);
//
// Find the window that this translation occurs
// within.
//
status = FindTranslationRange(Source->u.Generic.Start,
Source->u.Generic.Length,
translator,
Direction,
Source->Type,
&window);
if (!NT_SUCCESS(status)) {
//
// We should never get here. This fucntion
// should only be called for ranges that
// are valid. TranslateBridgeRequirements should
// weed out all the invalid ranges.
return status;
}
//
// Copy everything
//
*Target = *Source;
switch (Direction) {
case TranslateChildToParent:
//
// Target inherits the parent's resource type.
//
Target->Type = window->ParentType;
//
// Calculate the target's parent-relative start
// address.
//
Target->u.Generic.Start.QuadPart =
Source->u.Generic.Start.QuadPart +
window->ParentAddress.QuadPart -
window->ChildAddress.QuadPart;
//
// Make sure the length is still in bounds.
//
ASSERT(Target->u.Generic.Length <= (ULONG)(window->Length -
(Target->u.Generic.Start.QuadPart -
window->ParentAddress.QuadPart)));
status = STATUS_TRANSLATION_COMPLETE;
break;
case TranslateParentToChild:
//
// Target inherits the child's resource type.
//
Target->Type = window->ChildType;
//
// Calculate the target's child-relative start
// address.
//
Target->u.Generic.Start.QuadPart =
Source->u.Generic.Start.QuadPart +
window->ChildAddress.QuadPart -
window->ParentAddress.QuadPart;
//
// Make sure the length is still in bounds.
//
ASSERT(Target->u.Generic.Length <= (ULONG)(window->Length -
(Target->u.Generic.Start.QuadPart -
window->ChildAddress.QuadPart)));
status = STATUS_SUCCESS;
break;
default:
status = STATUS_INVALID_PARAMETER;
}
#if 0
if (Target->Type == CmResourceTypePort) {
DbgPrint("XXX: %s[%d]=0x%I64x -> %s[%d]=0x%I64x\n",
(Direction == TranslateChildToParent) ? "child" : "parent",
Source->Type,
Source->u.Generic.Start.QuadPart,
(Direction == TranslateChildToParent) ? "parent" : "child",
Target->Type,
Target->u.Generic.Start.QuadPart);
}
#endif
return status;
}
NTSTATUS
TranslateBridgeRequirements(
IN PVOID Context,
IN PIO_RESOURCE_DESCRIPTOR Source,
IN PDEVICE_OBJECT PhysicalDeviceObject,
OUT PULONG TargetCount,
OUT PIO_RESOURCE_DESCRIPTOR *Target
)
/*++
Routine Description:
This function takes a resource requirements list for
resources on the child side of the bridge and
translates them into resource requirements on the
parent side of the bridge. This may involve clipping
and there may be multiple target ranges.
Arguments:
Context - pointer to the translation data
Source - resource list to be translated
PhysicalDeviceObject - not used
TargetCount - number of resources in the target list
Target - translated resource requirements list
Return Value:
Status
--*/
{
PBRIDGE_TRANSLATOR translator;
PBRIDGE_WINDOW window;
NTSTATUS status;
LONGLONG rangeStart, rangeEnd, windowStart, windowEnd;
ULONG i;
PAGED_CODE();
ASSERT(Context);
ASSERT((Source->Type == CmResourceTypePort) ||
(Source->Type == CmResourceTypeMemory));
translator = (PBRIDGE_TRANSLATOR)Context;
//
// Allocate memory for the target range.
//
*Target = ExAllocatePoolWithTag(PagedPool,
sizeof(IO_RESOURCE_DESCRIPTOR),
ACPI_RESOURCE_POOLTAG);
if (!*Target) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto cleanup;
}
//
// Look at all the aperatures in the bridge to see which
// ones of them could possibly provide translations for
// this resource.
//
rangeStart = Source->u.Generic.MinimumAddress.QuadPart;
rangeEnd = Source->u.Generic.MaximumAddress.QuadPart;
for (i = 0; i < translator->RangeCount; i++) {
window = &translator->Ranges[i];
if (window->ChildType != Source->Type) {
//
// This window describes the wrong
// type of resource.
//
continue;
}
if (Source->u.Generic.Length > window->Length) {
//
// This resource won't fit in this aperature.
//
continue;
}
windowStart = window->ChildAddress.QuadPart;
windowEnd = window->ChildAddress.QuadPart + (LONGLONG)window->Length;
if (!(((rangeStart < windowStart) && (rangeEnd < windowStart)) ||
((rangeStart > windowEnd) && (rangeEnd > windowEnd)))) {
//
// The range and the window do intersect. So create
// a resource that clips the range to the window.
//
**Target = *Source;
*TargetCount = 1;
(*Target)->Type = window->ParentType;
(*Target)->u.Generic.MinimumAddress.QuadPart =
rangeStart + (window->ParentAddress.QuadPart - windowStart);
(*Target)->u.Generic.MaximumAddress.QuadPart =
rangeEnd + (window->ParentAddress.QuadPart - windowStart);
break;
}
}
if (i < translator->RangeCount) {
return STATUS_TRANSLATION_COMPLETE;
} else {
*TargetCount = 0;
status = STATUS_PNP_TRANSLATION_FAILED;
}
cleanup:
if (*Target) {
ExFreePool(*Target);
}
return status;
}
NTSTATUS
BuildTranslatorRanges(
IN PBRIDGE_TRANSLATOR Translator,
OUT ULONG *BridgeWindowCount,
OUT PBRIDGE_WINDOW *Window
)
{
PIO_RESOURCE_REQUIREMENTS_LIST ioList;
PIO_RESOURCE_DESCRIPTOR transDesc, resDesc;
ULONG descCount, windowCount, maxWindows;
PAGED_CODE();
ioList = Translator->IoList;
//
// Make an array of windows for holding the translation information.
//
maxWindows = ioList->List[0].Count / 2;
*Window = ExAllocatePoolWithTag(PagedPool,
maxWindows * sizeof(BRIDGE_WINDOW),
ACPI_TRANSLATE_POOLTAG);
if (!*Window) {
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Fill in the array with translations.
//
windowCount = 0;
for (descCount = 0; descCount < ioList->List[0].Count; descCount++) {
transDesc = &ioList->List[0].Descriptors[descCount];
if (transDesc->Type == CmResourceTypeDevicePrivate) {
//
// Translation information is contained in
// a device private resource that has
// TRANSLATION_DATA_PARENT_ADDRESS in the
// flags field.
//
if (transDesc->Flags & TRANSLATION_DATA_PARENT_ADDRESS) {
ASSERT(windowCount <= maxWindows);
//
// The translation descriptor is supposed to follow
// the resource that it is providing information about.
//
resDesc = &ioList->List[0].Descriptors[descCount - 1];
(*Window)[windowCount].ParentType =
(UCHAR)transDesc->u.DevicePrivate.Data[0];
(*Window)[windowCount].ChildType = resDesc->Type;
(*Window)[windowCount].ParentAddress.LowPart =
transDesc->u.DevicePrivate.Data[1];
(*Window)[windowCount].ParentAddress.HighPart =
transDesc->u.DevicePrivate.Data[2];
(*Window)[windowCount].ChildAddress.QuadPart =
resDesc->u.Generic.MinimumAddress.QuadPart;
(*Window)[windowCount].Length =
resDesc->u.Generic.Length;
//
// If the HAL has provided underlying sparse port translation
// services, allow for that.
//
if ((HalPortRangeInterface.QueryAllocateRange != NULL) &&
(resDesc->Type == CmResourceTypePort)) {
USHORT rangeId;
UCHAR parentType = (UCHAR)transDesc->u.DevicePrivate.Data[0];
BOOLEAN isSparse = transDesc->Flags & TRANSLATION_RANGE_SPARSE;
ULONG parentLength = resDesc->u.Generic.Length;
PHYSICAL_ADDRESS parentAddress;
NTSTATUS status;
PHYSICAL_ADDRESS rangeZeroBase;
parentAddress.LowPart = transDesc->u.DevicePrivate.Data[1];
parentAddress.HighPart = transDesc->u.DevicePrivate.Data[2];
rangeZeroBase.QuadPart = parentAddress.QuadPart - resDesc->u.Generic.MinimumAddress.QuadPart;
if (isSparse) {
parentLength = (parentLength + resDesc->u.Generic.MinimumAddress.LowPart) << 10;
}
status = HalPortRangeInterface.QueryAllocateRange(
isSparse,
parentType == CmResourceTypeMemory,
NULL,
rangeZeroBase,
parentLength,
&rangeId
);
if (NT_SUCCESS(status)) {
(*Window)[windowCount].ParentType = CmResourceTypePort;
(*Window)[windowCount].ParentAddress.QuadPart =
(rangeId << 16) |
((*Window)[windowCount].ChildAddress.QuadPart & 0xffff);
}
}
windowCount++;
}
}
}
*BridgeWindowCount = windowCount;
return STATUS_SUCCESS;
}