622 lines
13 KiB
C
622 lines
13 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 2000 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
table.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
All the function related to actually loading an ACPI table
|
|||
|
are included herein.
|
|||
|
|
|||
|
This, however, is mostly bookkeeping since the actual mechanics
|
|||
|
of creating device extensions, and the name space tree are
|
|||
|
handled elsewhere
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Stephane Plante (splante)
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
Kernel Mode Only
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
03/22/00 - Created (from code in callback.c)
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "pch.h"
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ACPITableLoad(
|
|||
|
VOID
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is called when the AML interpreter has finished loading
|
|||
|
a Differentiated Data Block
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
BOOLEAN runRootIni = FALSE;
|
|||
|
KIRQL oldIrql;
|
|||
|
NTSTATUS status;
|
|||
|
PDEVICE_EXTENSION fixedButtonExtension = NULL;
|
|||
|
PNSOBJ iniObject;
|
|||
|
PNSOBJ nsObject;
|
|||
|
|
|||
|
//
|
|||
|
// At this point, we should do everything that we need to do once the
|
|||
|
// name space has been loaded. Note that we need to make sure that we
|
|||
|
// only do those things once...
|
|||
|
//
|
|||
|
KeAcquireSpinLock( &AcpiDeviceTreeLock, &oldIrql );
|
|||
|
|
|||
|
//
|
|||
|
// We need the ACPI object for the _SB tree
|
|||
|
//
|
|||
|
status = AMLIGetNameSpaceObject( "\\_SB", NULL, &nsObject, 0 );
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
|
|||
|
//
|
|||
|
// Ooops. Failure
|
|||
|
//
|
|||
|
ACPIPrint( (
|
|||
|
ACPI_PRINT_CRITICAL,
|
|||
|
"ACPICallBackLoadUnloadDDB: No SB Object!\n"
|
|||
|
) );
|
|||
|
ACPIInternalError( ACPI_CALLBACK );
|
|||
|
return STATUS_SUCCESS;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Make sure that the root device extension's object points to the correct
|
|||
|
// thing. We only want to run through this code path once...
|
|||
|
//
|
|||
|
if (RootDeviceExtension->AcpiObject == NULL) {
|
|||
|
|
|||
|
runRootIni = TRUE;
|
|||
|
InterlockedIncrement( &(RootDeviceExtension->ReferenceCount) );
|
|||
|
RootDeviceExtension->AcpiObject = nsObject;
|
|||
|
nsObject->Context = RootDeviceExtension;
|
|||
|
|
|||
|
//
|
|||
|
// Now, enumerate the fixed button
|
|||
|
//
|
|||
|
status = ACPIBuildFixedButtonExtension(
|
|||
|
RootDeviceExtension,
|
|||
|
&fixedButtonExtension
|
|||
|
);
|
|||
|
if (NT_SUCCESS(status) &&
|
|||
|
fixedButtonExtension != NULL) {
|
|||
|
|
|||
|
//
|
|||
|
// Incremement the reference count on the node. We do this because
|
|||
|
// we are going to be doing work (which will take a long time
|
|||
|
// to complete, anyways), and we don't want to hold the lock for that
|
|||
|
// entire time. If we incr the reference count, then we guarantee that
|
|||
|
// no one can come along and kick the feet out from underneath us
|
|||
|
//
|
|||
|
InterlockedIncrement( &(fixedButtonExtension->ReferenceCount) );
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// We now want to run the _INI through the entire tree, starting at
|
|||
|
// the _SB
|
|||
|
//
|
|||
|
status = ACPIBuildRunMethodRequest(
|
|||
|
RootDeviceExtension,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
PACKED_INI,
|
|||
|
(RUN_REQUEST_CHECK_STATUS | RUN_REQUEST_RECURSIVE | RUN_REQUEST_MARK_INI),
|
|||
|
FALSE
|
|||
|
);
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
|
|||
|
ACPIInternalError( ACPI_CALLBACK );
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
KeReleaseSpinLock( &AcpiDeviceTreeLock, oldIrql );
|
|||
|
|
|||
|
//
|
|||
|
// We also need to run the _INI method off of the root name space entry
|
|||
|
//
|
|||
|
if (runRootIni) {
|
|||
|
|
|||
|
iniObject = ACPIAmliGetNamedChild( nsObject->pnsParent, PACKED_INI );
|
|||
|
if (iniObject) {
|
|||
|
|
|||
|
AMLINestAsyncEvalObject(
|
|||
|
iniObject,
|
|||
|
NULL,
|
|||
|
0,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
NULL
|
|||
|
);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// We need a synchronization point after we finish running the
|
|||
|
// DPC engine. We want to be able to move anything in the Delayed
|
|||
|
// Power Queue over to the Power DPC engine
|
|||
|
//
|
|||
|
status = ACPIBuildSynchronizationRequest(
|
|||
|
RootDeviceExtension,
|
|||
|
ACPITableLoadCallBack,
|
|||
|
NULL,
|
|||
|
&AcpiBuildDeviceList,
|
|||
|
FALSE
|
|||
|
);
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
|
|||
|
ACPIInternalError( ACPI_CALLBACK );
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// We need to hold this spinlock
|
|||
|
//
|
|||
|
KeAcquireSpinLock( &AcpiBuildQueueLock, &oldIrql );
|
|||
|
|
|||
|
//
|
|||
|
// Do we need to run the DPC?
|
|||
|
//
|
|||
|
if (!AcpiBuildDpcRunning) {
|
|||
|
|
|||
|
KeInsertQueueDpc( &AcpiBuildDpc, 0, 0);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Done with the lock
|
|||
|
//
|
|||
|
KeReleaseSpinLock( &AcpiBuildQueueLock, oldIrql );
|
|||
|
|
|||
|
//
|
|||
|
// Done
|
|||
|
//
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
ACPITableLoadCallBack(
|
|||
|
IN PVOID BuildContext,
|
|||
|
IN PVOID Context,
|
|||
|
IN NTSTATUS Status
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is called when we have emptied all of the elements
|
|||
|
within the AcpiBuildDeviceList. This is a good time to move items
|
|||
|
from the AcpiPowerDelayedQueueList to the AcpiPowerQueueList.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
BuildContext - Not used (it is the RootDeviceExtension)
|
|||
|
Context - NULL
|
|||
|
Status - Status of the operation
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
UNREFERENCED_PARAMETER( BuildContext );
|
|||
|
UNREFERENCED_PARAMETER( Context );
|
|||
|
UNREFERENCED_PARAMETER( Status );
|
|||
|
|
|||
|
ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
|
|||
|
|
|||
|
//
|
|||
|
// We want to rebuilt the device based GPE mask here, so
|
|||
|
// we need the following locks
|
|||
|
//
|
|||
|
KeAcquireSpinLockAtDpcLevel( &AcpiDeviceTreeLock );
|
|||
|
KeAcquireSpinLockAtDpcLevel( &GpeTableLock );
|
|||
|
|
|||
|
//
|
|||
|
// Now, we need to walk the device namespace and find which events
|
|||
|
// are special, which are wake events, and which are run-time events
|
|||
|
// As a matter of practical theory, its not possible for there to
|
|||
|
// be a _PRW on the root device extension, so we should be safely
|
|||
|
// able to walk only the Root's children and thereon
|
|||
|
//
|
|||
|
ACPIGpeBuildWakeMasks(RootDeviceExtension);
|
|||
|
|
|||
|
//
|
|||
|
// We don't need these particular spin locks anymore
|
|||
|
//
|
|||
|
KeReleaseSpinLockFromDpcLevel( &GpeTableLock );
|
|||
|
KeReleaseSpinLockFromDpcLevel( &AcpiDeviceTreeLock );
|
|||
|
|
|||
|
//
|
|||
|
// We need the power lock to touch these Power Queues
|
|||
|
//
|
|||
|
KeAcquireSpinLockAtDpcLevel( &AcpiPowerQueueLock );
|
|||
|
|
|||
|
//
|
|||
|
// If we there are items on the delayed list, we need to put them
|
|||
|
// on the main list
|
|||
|
//
|
|||
|
if (!IsListEmpty( &AcpiPowerDelayedQueueList ) ) {
|
|||
|
|
|||
|
//
|
|||
|
// Move the list
|
|||
|
//
|
|||
|
ACPIInternalMoveList(
|
|||
|
&AcpiPowerDelayedQueueList,
|
|||
|
&AcpiPowerQueueList
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// Schedule the DPC, if necessary
|
|||
|
///
|
|||
|
if (!AcpiPowerDpcRunning) {
|
|||
|
|
|||
|
KeInsertQueueDpc( &AcpiPowerDpc, 0, 0 );
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Done with the lock
|
|||
|
//
|
|||
|
KeReleaseSpinLockFromDpcLevel( &AcpiPowerQueueLock );
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
EXPORT
|
|||
|
ACPITableNotifyFreeObject(
|
|||
|
ULONG Event,
|
|||
|
PVOID Context,
|
|||
|
ULONG ObjectType
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is called when interpreter tells us that an
|
|||
|
object has been freed
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Event - the step in unload
|
|||
|
Object - the object being unloaded
|
|||
|
ObjectType - the type of the object
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
LONG oldReferenceCount;
|
|||
|
PDEVICE_EXTENSION deviceExtension;
|
|||
|
PDEVICE_EXTENSION parentExtension;
|
|||
|
PKIRQL oldIrql;
|
|||
|
PNSOBJ object;
|
|||
|
|
|||
|
//
|
|||
|
// Start case
|
|||
|
//
|
|||
|
if (Event == DESTROYOBJ_START) {
|
|||
|
|
|||
|
ACPIPrint( (
|
|||
|
ACPI_PRINT_CRITICAL,
|
|||
|
"Unloading: Start\n"
|
|||
|
) );
|
|||
|
|
|||
|
oldIrql = (PKIRQL) Context;
|
|||
|
KeAcquireSpinLock( &AcpiDeviceTreeLock, oldIrql );
|
|||
|
return STATUS_SUCCESS;
|
|||
|
|
|||
|
}
|
|||
|
if (Event == DESTROYOBJ_END) {
|
|||
|
|
|||
|
ACPIPrint( (
|
|||
|
ACPI_PRINT_CRITICAL,
|
|||
|
"Unloading: End\n"
|
|||
|
) );
|
|||
|
|
|||
|
oldIrql = (PKIRQL) Context;
|
|||
|
KeReleaseSpinLock( &AcpiDeviceTreeLock, *oldIrql );
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// At this point, we have a either a valid unload request or
|
|||
|
// a bugcheck request
|
|||
|
//
|
|||
|
object = (PNSOBJ) Context;
|
|||
|
|
|||
|
//
|
|||
|
// Let the world Know...
|
|||
|
//
|
|||
|
ACPIPrint( (
|
|||
|
ACPI_PRINT_CRITICAL,
|
|||
|
"%x: Unloading: %x %x %x\n",
|
|||
|
(object ? object->Context : 0),
|
|||
|
object,
|
|||
|
ObjectType,
|
|||
|
Event
|
|||
|
) );
|
|||
|
|
|||
|
//
|
|||
|
// Handle the bugcheck cases
|
|||
|
//
|
|||
|
if (Event == DESTROYOBJ_CHILD_NOT_FREED) {
|
|||
|
|
|||
|
KeBugCheckEx(
|
|||
|
ACPI_BIOS_ERROR,
|
|||
|
ACPI_TABLE_UNLOAD,
|
|||
|
(ULONG_PTR) object,
|
|||
|
0,
|
|||
|
0
|
|||
|
);
|
|||
|
|
|||
|
}
|
|||
|
if (Event == DESTROYOBJ_BOGUS_PARENT) {
|
|||
|
|
|||
|
KeBugCheckEx(
|
|||
|
ACPI_BIOS_ERROR,
|
|||
|
ACPI_TABLE_UNLOAD,
|
|||
|
(ULONG_PTR) object,
|
|||
|
1,
|
|||
|
0
|
|||
|
);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// We only understand processors, thermal zones, and devices for right
|
|||
|
// now, we will have to add power resources at a later point
|
|||
|
//
|
|||
|
if (ObjectType == OBJTYPE_POWERRES) {
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Grab the device extension, and make sure that one exists
|
|||
|
//
|
|||
|
deviceExtension = object->Context;
|
|||
|
if (deviceExtension == NULL) {
|
|||
|
|
|||
|
//
|
|||
|
// No device extension, so we can free this thing *now*
|
|||
|
//
|
|||
|
AMLIDestroyFreedObjs( object );
|
|||
|
return STATUS_SUCCESS;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Mark the extension as no longer existing
|
|||
|
//
|
|||
|
ACPIInternalUpdateFlags(
|
|||
|
&(deviceExtension->Flags),
|
|||
|
DEV_PROP_UNLOADING,
|
|||
|
FALSE
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// Does this device have a parent extension? It might not
|
|||
|
// have an extension if the parent has been marked for removal
|
|||
|
//
|
|||
|
parentExtension = deviceExtension->ParentExtension;
|
|||
|
if (parentExtension != NULL) {
|
|||
|
|
|||
|
//
|
|||
|
// Mark the parent's relations as invalid
|
|||
|
//
|
|||
|
ACPIInternalUpdateFlags(
|
|||
|
&(parentExtension->Flags),
|
|||
|
DEV_PROP_INVALID_RELATIONS,
|
|||
|
FALSE
|
|||
|
);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Finally, decrement the reference count on the device...
|
|||
|
//
|
|||
|
oldReferenceCount = InterlockedDecrement(
|
|||
|
&(deviceExtension->ReferenceCount)
|
|||
|
);
|
|||
|
if (oldReferenceCount == 0) {
|
|||
|
|
|||
|
//
|
|||
|
// Free this extension
|
|||
|
//
|
|||
|
ACPIInitDeleteDeviceExtension( deviceExtension );
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Done
|
|||
|
//
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ACPITableUnload(
|
|||
|
VOID
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is called after a table has been unloaded.
|
|||
|
|
|||
|
The purpose of this routine is to go out and issue the invalidate
|
|||
|
device relations on all elements of the table whose children are
|
|||
|
going away...
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None
|
|||
|
|
|||
|
Return value:
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
KIRQL oldIrql;
|
|||
|
PDEVICE_EXTENSION deviceExtension;
|
|||
|
|
|||
|
//
|
|||
|
// We will need to hold the device tree lock for the following
|
|||
|
//
|
|||
|
KeAcquireSpinLock( &AcpiDeviceTreeLock, &oldIrql );
|
|||
|
|
|||
|
//
|
|||
|
// Check to see if we have to invalid the root's device extension?
|
|||
|
//
|
|||
|
deviceExtension = RootDeviceExtension;
|
|||
|
if (deviceExtension && !(deviceExtension->Flags & DEV_TYPE_NOT_FOUND) ) {
|
|||
|
|
|||
|
if (deviceExtension->Flags & DEV_PROP_INVALID_RELATIONS) {
|
|||
|
|
|||
|
ACPIInternalUpdateFlags(
|
|||
|
&(deviceExtension->Flags),
|
|||
|
DEV_PROP_INVALID_RELATIONS,
|
|||
|
TRUE
|
|||
|
);
|
|||
|
IoInvalidateDeviceRelations(
|
|||
|
deviceExtension->PhysicalDeviceObject,
|
|||
|
BusRelations
|
|||
|
);
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Walk the namespace looking for bogus relations
|
|||
|
//
|
|||
|
ACPITableUnloadInvalidateRelations( deviceExtension );
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Done with the lock
|
|||
|
//
|
|||
|
KeReleaseSpinLock( &AcpiDeviceTreeLock, oldIrql );
|
|||
|
|
|||
|
//
|
|||
|
// And with the function
|
|||
|
//
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ACPITableUnloadInvalidateRelations(
|
|||
|
IN PDEVICE_EXTENSION DeviceExtension
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This recursive routine is called to walke the namespace and issue
|
|||
|
the appropriate invalidates.
|
|||
|
|
|||
|
The device tree lock is owned during this call...
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceExtension - The device whose child extension we have to check
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
EXTENSIONLIST_ENUMDATA eled;
|
|||
|
PDEVICE_EXTENSION childExtension;
|
|||
|
|
|||
|
//
|
|||
|
// Setup the data structures that we will use to walk the
|
|||
|
// device extension tree
|
|||
|
//
|
|||
|
ACPIExtListSetupEnum(
|
|||
|
&eled,
|
|||
|
&(DeviceExtension->ChildDeviceList),
|
|||
|
NULL,
|
|||
|
SiblingDeviceList,
|
|||
|
WALKSCHEME_NO_PROTECTION
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// Look at all the children of the current device extension
|
|||
|
//
|
|||
|
for (childExtension = ACPIExtListStartEnum( &eled) ;
|
|||
|
ACPIExtListTestElement( &eled, TRUE);
|
|||
|
childExtension = ACPIExtListEnumNext( &eled) ) {
|
|||
|
|
|||
|
//
|
|||
|
// Does this object have any device objects?
|
|||
|
//
|
|||
|
if (!(childExtension->Flags & DEV_TYPE_NOT_FOUND) ) {
|
|||
|
|
|||
|
continue;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Do we have to invalidate this object's relations?
|
|||
|
//
|
|||
|
if (childExtension->Flags & DEV_PROP_INVALID_RELATIONS) {
|
|||
|
|
|||
|
ACPIInternalUpdateFlags(
|
|||
|
&(childExtension->Flags),
|
|||
|
DEV_PROP_INVALID_RELATIONS,
|
|||
|
TRUE
|
|||
|
);
|
|||
|
IoInvalidateDeviceRelations(
|
|||
|
childExtension->PhysicalDeviceObject,
|
|||
|
BusRelations
|
|||
|
);
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Recurse
|
|||
|
//
|
|||
|
ACPITableUnloadInvalidateRelations( childExtension );
|
|||
|
|
|||
|
} // for ( ... )
|
|||
|
|
|||
|
//
|
|||
|
// Done
|
|||
|
//
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|