656 lines
15 KiB
C
656 lines
15 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1997 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
reg.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
These functions access the registry
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Stephane Plante (splante)
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
Kernel mode only.
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
03-Jun-97 Initial Revision
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "pch.h"
|
|||
|
#include "amlreg.h"
|
|||
|
#include <stdio.h>
|
|||
|
|
|||
|
//
|
|||
|
// This controls wether or not the various tables will be dumped to the
|
|||
|
// registry
|
|||
|
//
|
|||
|
UCHAR DoAcpiTableDump = 0xFF;
|
|||
|
|
|||
|
#ifdef ALLOC_PRAGMA
|
|||
|
#define alloc_text(PAGE,ACPIRegLocalCopyString)
|
|||
|
#define alloc_text(PAGE,ACPIRegDumpAcpiTable)
|
|||
|
#define alloc_text(PAGE,ACPIRegDumpAcpiTables)
|
|||
|
#define alloc_text(PAGE,ACPIRegReadEntireAcpiTable)
|
|||
|
#define alloc_text(PAGE,ACPIRegReadAMLRegistryEntry)
|
|||
|
#endif
|
|||
|
|
|||
|
|
|||
|
PUCHAR
|
|||
|
ACPIRegLocalCopyString (
|
|||
|
PUCHAR Destination,
|
|||
|
PUCHAR Source,
|
|||
|
ULONG MaxLength
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
A little routine to copy short strings, since using sprintf here is
|
|||
|
like hitting a tack with a sledgehammer. Terminates when a null or
|
|||
|
the max length is reached, whichever comes first. Translates blanks
|
|||
|
to underscores, since everyone hates blanks embedded in registry keys.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Destination - Where to copy the string to
|
|||
|
Source - Source string pointer
|
|||
|
MaxLength - Max number of bytes to copy
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Returns the destination pointer incremented past the copied string
|
|||
|
|
|||
|
__*/
|
|||
|
{
|
|||
|
ULONG i;
|
|||
|
|
|||
|
|
|||
|
for (i = 0; i < MaxLength; i++) {
|
|||
|
|
|||
|
if (Source[i] == 0) {
|
|||
|
break;
|
|||
|
|
|||
|
} else if (Source[i] == ' ') { // Translate blanks to underscores
|
|||
|
Destination [i] = '_';
|
|||
|
|
|||
|
} else {
|
|||
|
Destination [i] = Source[i];
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
Destination [i] = 0;
|
|||
|
return (Destination + i);
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
ACPIRegReadAMLRegistryEntry(
|
|||
|
IN PVOID *Table,
|
|||
|
IN BOOLEAN MemoryMapped
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This reads a table from the registry
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Table - Where to store the pointer to the table. If non-null, this
|
|||
|
contains a pointer to where the Original Table is stored
|
|||
|
MemorMapped - Indicates wether or not the table is memory mapped and should
|
|||
|
be unmapped once we are done with it
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE - Success
|
|||
|
FALSE - Failure
|
|||
|
|
|||
|
__*/
|
|||
|
{
|
|||
|
BOOLEAN rc = FALSE;
|
|||
|
HANDLE revisionKey = NULL;
|
|||
|
HANDLE tableIdKey = NULL;
|
|||
|
NTSTATUS status;
|
|||
|
PDESCRIPTION_HEADER header = (PDESCRIPTION_HEADER) *Table;
|
|||
|
PUCHAR key = NULL; // ACPI_PARAMETERS_REGISTRY_KEY;
|
|||
|
PUCHAR buffer;
|
|||
|
ULONG action;
|
|||
|
ULONG bytesRead;
|
|||
|
ULONG baseSize;
|
|||
|
ULONG totalSize;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
//
|
|||
|
// Build a full path name to the thing we want in the registry
|
|||
|
//
|
|||
|
baseSize = strlen( ACPI_PARAMETERS_REGISTRY_KEY);
|
|||
|
totalSize = baseSize + ACPI_MAX_TABLE_STRINGS + 4;
|
|||
|
|
|||
|
buffer = key = ExAllocatePool( PagedPool, totalSize );
|
|||
|
if (key == NULL) {
|
|||
|
|
|||
|
return FALSE;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Generate the path name to the key. This avoids a costly sprintf
|
|||
|
//
|
|||
|
RtlZeroMemory( buffer, totalSize );
|
|||
|
RtlCopyMemory(
|
|||
|
buffer,
|
|||
|
ACPI_PARAMETERS_REGISTRY_KEY,
|
|||
|
baseSize
|
|||
|
);
|
|||
|
buffer += baseSize;
|
|||
|
*buffer++ = '\\';
|
|||
|
|
|||
|
//
|
|||
|
// Table Signature (Up to 4 byte string)
|
|||
|
//
|
|||
|
buffer = ACPIRegLocalCopyString (buffer, (PUCHAR) &header->Signature, ACPI_MAX_SIGNATURE);
|
|||
|
*buffer++ = '\\';
|
|||
|
|
|||
|
//
|
|||
|
// OEM ID field (Up to 6 byte string)
|
|||
|
//
|
|||
|
buffer = ACPIRegLocalCopyString (buffer, (PUCHAR) &header->OEMID, ACPI_MAX_OEM_ID);
|
|||
|
*buffer++ = '\\';
|
|||
|
|
|||
|
//
|
|||
|
// OEM Table ID field (Up to 8 byte string)
|
|||
|
//
|
|||
|
buffer = ACPIRegLocalCopyString (buffer, (PUCHAR) &header->OEMTableID, ACPI_MAX_TABLE_ID);
|
|||
|
*buffer = 0; // Terminate
|
|||
|
|
|||
|
ACPIPrint ((
|
|||
|
ACPI_PRINT_REGISTRY,
|
|||
|
"ReadAMLRegistryEntry: opening key: %s\n",
|
|||
|
key));
|
|||
|
|
|||
|
//
|
|||
|
// Open the <TableId>/OemId>/<OemTableId> key
|
|||
|
//
|
|||
|
status = OSOpenHandle(key, NULL, &tableIdKey);
|
|||
|
if ( !NT_SUCCESS(status) ) {
|
|||
|
|
|||
|
ACPIPrint ((
|
|||
|
ACPI_PRINT_WARNING,
|
|||
|
"ReadAMLRegistryEntry: failed to open AML registry entry (rc=%x)\n",
|
|||
|
status));
|
|||
|
goto ReadAMLRegistryEntryExit;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Find the largest subkey that is equal or greater than the ROM
|
|||
|
// BIOS version of the table
|
|||
|
//
|
|||
|
status = OSOpenLargestSubkey(
|
|||
|
tableIdKey,
|
|||
|
&revisionKey,
|
|||
|
header->OEMRevision
|
|||
|
);
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
|
|||
|
ACPIPrint ((
|
|||
|
ACPI_PRINT_WARNING,
|
|||
|
"ReadAMLRegistryEntry: no valid <OemRevision> key (rc=%#08lx)\n",
|
|||
|
status));
|
|||
|
goto ReadAMLRegistryEntryExit;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Get the Action value for this table, which tells us what to do with
|
|||
|
// the table
|
|||
|
//
|
|||
|
bytesRead = sizeof(action);
|
|||
|
status = OSReadRegValue(
|
|||
|
"Action",
|
|||
|
revisionKey,
|
|||
|
&action,
|
|||
|
&bytesRead
|
|||
|
);
|
|||
|
if (!NT_SUCCESS(status) || bytesRead != sizeof(action)) {
|
|||
|
|
|||
|
ACPIPrint( (
|
|||
|
ACPI_PRINT_CRITICAL,
|
|||
|
"ReadAMLRegistryEntry: read action value = %#08lx. BytesRead=%d\n",
|
|||
|
status, bytesRead
|
|||
|
) );
|
|||
|
action = ACTION_LOAD_TABLE; // Default action
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Do the action
|
|||
|
//
|
|||
|
switch (action) {
|
|||
|
case ACTION_LOAD_TABLE:
|
|||
|
//
|
|||
|
// Overload entire ROM table
|
|||
|
//
|
|||
|
status = ACPIRegReadEntireAcpiTable(revisionKey, Table, MemoryMapped);
|
|||
|
if (NT_SUCCESS( status ) ) {
|
|||
|
|
|||
|
rc = TRUE;
|
|||
|
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case ACTION_LOAD_ROM:
|
|||
|
case ACTION_LOAD_NOTHING:
|
|||
|
//
|
|||
|
// Nothing to do for these cases (return FALSE however);
|
|||
|
//
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
//
|
|||
|
// Unsupported actions (return FALSE)
|
|||
|
//
|
|||
|
ACPIPrint( (
|
|||
|
ACPI_PRINT_CRITICAL,
|
|||
|
"ReadAMLRegistryEntry: Unsupported action value (action=%d)\n",
|
|||
|
action
|
|||
|
) );
|
|||
|
break;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Always close the open keys
|
|||
|
//
|
|||
|
ReadAMLRegistryEntryExit:
|
|||
|
if (key != NULL) {
|
|||
|
|
|||
|
ExFreePool( key );
|
|||
|
|
|||
|
}
|
|||
|
if (tableIdKey != NULL) {
|
|||
|
|
|||
|
OSCloseHandle( tableIdKey );
|
|||
|
|
|||
|
}
|
|||
|
if (revisionKey != NULL) {
|
|||
|
|
|||
|
OSCloseHandle( revisionKey );
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
return rc;
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ACPIRegReadEntireAcpiTable (
|
|||
|
IN HANDLE RevisionKey,
|
|||
|
IN PVOID *Table,
|
|||
|
IN BOOLEAN MemoryMapped
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Reads the table from the registry into memory
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
RevisionKey - Handle to the key containing the table
|
|||
|
Table - Pointer to the pointer to the table
|
|||
|
MemorymMapped - If True, indicates that we need to MmUnmapIo the table
|
|||
|
otherwise, if False, we do no free the table (the
|
|||
|
memory is static)
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS status;
|
|||
|
PUCHAR buffer;
|
|||
|
PVOID table;
|
|||
|
UCHAR value[9];
|
|||
|
ULONG bytesRead;
|
|||
|
ULONG index = 0;
|
|||
|
ULONG entry;
|
|||
|
PREGISTRY_HEADER entryHeader;
|
|||
|
PDESCRIPTION_HEADER descHeader = (PDESCRIPTION_HEADER) *Table;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
//
|
|||
|
// We need an 8k buffer
|
|||
|
//
|
|||
|
buffer = ExAllocatePool( PagedPool, 8 * 1024 );
|
|||
|
if (buffer == NULL) {
|
|||
|
|
|||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Repeat this forever
|
|||
|
//
|
|||
|
for (index = 0; ;index++) {
|
|||
|
|
|||
|
//
|
|||
|
// This is the first data value
|
|||
|
//
|
|||
|
sprintf(value, "%08lx", index );
|
|||
|
|
|||
|
//
|
|||
|
// Read the entry header to get the size of the table. This is stored
|
|||
|
// before the actual table
|
|||
|
//
|
|||
|
bytesRead = 8 * 1024;
|
|||
|
status = OSReadRegValue(
|
|||
|
value,
|
|||
|
RevisionKey,
|
|||
|
buffer,
|
|||
|
&bytesRead
|
|||
|
);
|
|||
|
if (!NT_SUCCESS(status) ) {
|
|||
|
|
|||
|
//
|
|||
|
// Not being able to read the table isn't a failure case
|
|||
|
//
|
|||
|
status = STATUS_SUCCESS;
|
|||
|
break;
|
|||
|
|
|||
|
} else if (bytesRead < sizeof(REGISTRY_HEADER) ) {
|
|||
|
|
|||
|
//
|
|||
|
// Not being to read the proper number of bytes is
|
|||
|
//
|
|||
|
ACPIPrint( (
|
|||
|
ACPI_PRINT_CRITICAL,
|
|||
|
"ReadEntireAcpiTable: read registry header (bytes=%d)\n",
|
|||
|
bytesRead
|
|||
|
) );
|
|||
|
return STATUS_UNSUCCESSFUL;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Loop while we still have bytes to process
|
|||
|
//
|
|||
|
for (entry = 0;
|
|||
|
entry < bytesRead;
|
|||
|
entry += (entryHeader->Length + sizeof(REGISTRY_HEADER) )
|
|||
|
) {
|
|||
|
|
|||
|
//
|
|||
|
// Grab a pointer to the entry record
|
|||
|
//
|
|||
|
entryHeader = (PREGISTRY_HEADER) &(buffer[entry]);
|
|||
|
|
|||
|
//
|
|||
|
// Crack the record
|
|||
|
//
|
|||
|
if (entryHeader->Length == 0) {
|
|||
|
|
|||
|
//
|
|||
|
// Special Case
|
|||
|
//
|
|||
|
if (entryHeader->Offset != descHeader->Length) {
|
|||
|
|
|||
|
//
|
|||
|
// Must change the table size
|
|||
|
//
|
|||
|
table = ExAllocatePoolWithTag(
|
|||
|
NonPagedPool,
|
|||
|
entryHeader->Offset,
|
|||
|
ACPI_SHARED_TABLE_POOLTAG
|
|||
|
);
|
|||
|
if (table == NULL) {
|
|||
|
|
|||
|
ExFreePool( buffer );
|
|||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// How much do we have to copy?
|
|||
|
//
|
|||
|
RtlCopyMemory(
|
|||
|
table,
|
|||
|
*Table,
|
|||
|
min( entryHeader->Offset, descHeader->Length )
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// Free the old table based on wether or not its mm mapped
|
|||
|
//
|
|||
|
if (MemoryMapped) {
|
|||
|
|
|||
|
MmUnmapIoSpace(*Table, descHeader->Length);
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
ExFreePool( *Table );
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Remember the address of the new table
|
|||
|
//
|
|||
|
descHeader = (PDESCRIPTION_HEADER) *Table = table;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Done with this record
|
|||
|
//
|
|||
|
continue;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Patch the memory
|
|||
|
//
|
|||
|
ASSERT( entryHeader->Offset < descHeader->Length );
|
|||
|
RtlCopyMemory(
|
|||
|
( (PUCHAR) *Table) + entryHeader->Offset,
|
|||
|
(PUCHAR) entryHeader + sizeof( REGISTRY_HEADER ),
|
|||
|
entryHeader->Length
|
|||
|
);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Normal exit
|
|||
|
//
|
|||
|
if (buffer != NULL) {
|
|||
|
|
|||
|
ExFreePool( buffer );
|
|||
|
}
|
|||
|
return status;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
/****************************************************************************
|
|||
|
*
|
|||
|
* DumpAcpiTable
|
|||
|
* Write an ACPI Table to the registry
|
|||
|
*
|
|||
|
* Not exported.
|
|||
|
*
|
|||
|
* ENTRY: pszName - Name of the table to write (4 byte string)
|
|||
|
* Table - Pointer to table data
|
|||
|
* Length - of the table
|
|||
|
* Header - Pointer to the table header
|
|||
|
*
|
|||
|
* EXIT: NONE
|
|||
|
*
|
|||
|
***************************************************************************/
|
|||
|
VOID
|
|||
|
ACPIRegDumpAcpiTable (
|
|||
|
PSZ pszName,
|
|||
|
PVOID Table,
|
|||
|
ULONG Length,
|
|||
|
PDESCRIPTION_HEADER Header
|
|||
|
)
|
|||
|
{
|
|||
|
//NTSTATUS status;
|
|||
|
UCHAR buffer [80] = "\\Registry\\Machine\\Hardware\\ACPI";
|
|||
|
HANDLE hSubKey;
|
|||
|
HANDLE hPrefixKey;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
//
|
|||
|
// Create /Registry/Machine/Hardware/ACPI subkey
|
|||
|
//
|
|||
|
if ( !NT_SUCCESS(OSCreateHandle (buffer, NULL, &hPrefixKey) ) ) {
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Create table name subkey (DSDT, FACP, FACS, or RSDT) - 4 bytes
|
|||
|
//
|
|||
|
if ( !NT_SUCCESS(OSCreateHandle (pszName, hPrefixKey, &hSubKey) ) ) {
|
|||
|
goto DumpAcpiTableExit;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// For tables with headers, add subkeys for
|
|||
|
// <OemId>/<OemTableID>/<OemRevision>
|
|||
|
//
|
|||
|
if (Header) {
|
|||
|
|
|||
|
OSCloseHandle(hPrefixKey);
|
|||
|
hPrefixKey = hSubKey;
|
|||
|
|
|||
|
//
|
|||
|
// OEM ID field (6 byte string)
|
|||
|
//
|
|||
|
ACPIRegLocalCopyString (buffer, Header->OEMID, ACPI_MAX_OEM_ID);
|
|||
|
if ( !NT_SUCCESS(OSCreateHandle (buffer, hPrefixKey, &hSubKey) ) ) {
|
|||
|
goto DumpAcpiTableExit;
|
|||
|
}
|
|||
|
|
|||
|
OSCloseHandle (hPrefixKey);
|
|||
|
hPrefixKey = hSubKey;
|
|||
|
|
|||
|
//
|
|||
|
// OEM Table ID field (8 byte string)
|
|||
|
//
|
|||
|
ACPIRegLocalCopyString (buffer, Header->OEMTableID, ACPI_MAX_TABLE_ID);
|
|||
|
if ( !NT_SUCCESS(OSCreateHandle (buffer, hPrefixKey, &hSubKey) ) ) {
|
|||
|
goto DumpAcpiTableExit;
|
|||
|
}
|
|||
|
|
|||
|
OSCloseHandle (hPrefixKey);
|
|||
|
hPrefixKey = hSubKey;
|
|||
|
|
|||
|
//
|
|||
|
// OEM Revision field (4 byte number)
|
|||
|
//
|
|||
|
sprintf (buffer, "%.8x", Header->OEMRevision);
|
|||
|
if ( !NT_SUCCESS(OSCreateHandle (buffer, hPrefixKey, &hSubKey) ) ) {
|
|||
|
goto DumpAcpiTableExit;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Finally, write the entire table
|
|||
|
//
|
|||
|
OSWriteRegValue ("00000000", hSubKey, Table, Length);
|
|||
|
|
|||
|
//
|
|||
|
// Delete open handles
|
|||
|
//
|
|||
|
OSCloseHandle (hSubKey);
|
|||
|
DumpAcpiTableExit:
|
|||
|
OSCloseHandle (hPrefixKey);
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/****************************************************************************
|
|||
|
*
|
|||
|
* DumpAcpiTables
|
|||
|
* Write the ACPI Tables to the registry. Should be called only
|
|||
|
* after table pointers have been initialized.
|
|||
|
*
|
|||
|
* Not exported.
|
|||
|
*
|
|||
|
* ENTRY: NONE
|
|||
|
* EXIT: NONE.
|
|||
|
*
|
|||
|
***************************************************************************/
|
|||
|
VOID
|
|||
|
ACPIRegDumpAcpiTables (VOID)
|
|||
|
{
|
|||
|
PDSDT dsdt = AcpiInformation->DiffSystemDescTable;
|
|||
|
PFACS facs = AcpiInformation->FirmwareACPIControlStructure;
|
|||
|
PFADT fadt = AcpiInformation->FixedACPIDescTable;
|
|||
|
PRSDT rsdt = AcpiInformation->RootSystemDescTable;
|
|||
|
|
|||
|
|
|||
|
if (DoAcpiTableDump) {
|
|||
|
|
|||
|
ACPIPrint ((
|
|||
|
ACPI_PRINT_REGISTRY,
|
|||
|
"DumpAcpiTables: Writing DSDT/FACS/FADT/RSDT to registry\n"));
|
|||
|
|
|||
|
if (dsdt) {
|
|||
|
|
|||
|
ACPIRegDumpAcpiTable(
|
|||
|
"DSDT",
|
|||
|
dsdt,
|
|||
|
dsdt->Header.Length,
|
|||
|
&(dsdt->Header)
|
|||
|
);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
if (facs) {
|
|||
|
|
|||
|
ACPIRegDumpAcpiTable(
|
|||
|
"FACS",
|
|||
|
facs,
|
|||
|
facs->Length,
|
|||
|
NULL
|
|||
|
);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
if (fadt) {
|
|||
|
|
|||
|
ACPIRegDumpAcpiTable(
|
|||
|
"FADT",
|
|||
|
fadt,
|
|||
|
fadt->Header.Length,
|
|||
|
&(fadt->Header)
|
|||
|
);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
if (rsdt) {
|
|||
|
|
|||
|
ACPIRegDumpAcpiTable(
|
|||
|
"RSDT",
|
|||
|
rsdt,
|
|||
|
rsdt->Header.Length,
|
|||
|
&(rsdt->Header)
|
|||
|
);
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|