WdfIoTargetSendIoctlSynchronously (IOCTL_ACPI_EVAL_METHOD) failing STATUS_BUFFER_TOO_

Hi

I have below Get() func. to execute an ACPI _DSM method (@ bottom) in my device ACPI namespace().

It fails when I do WdfIoTargetSendIoctlSynchronously(IOCTL_ACPI_EVAL_METHOD) with STATUS_BUFFER_TOO_SMALL.

I am invoking it as

DEFINE_GUID(GUID_M,…)
ULONG ValueLength = sizeof(ULONG);
LONG Value = -1;

GetAcpiDsm_L3Data(WdfDevice, ‘abcd’,
(PUCHAR)(&GUID_M), 0, 1,0, ValueLength, (PVOID)&Value);

If I change the IOCTL to IOCTL_ACPI_EVAL_METHOD_EX I get STATUS_INFO_LENGTH_MISMATCH.

I seem to be messing the args, but unable to find where.

Also what do I call on my memory desc/handle/buffers
WDF_MEMORY_DESCRIPTOR_INIT_BUFFER()
WDF_MEMORY_DESCRIPTOR_INIT_HANDLE()
When I change the i/p buff to use WDF_MEMORY_DESCRIPTOR_INIT_HANDLE() and o/p buff to WDF_MEMORY_DESCRIPTOR_INIT_BUFFER() I get the error code STATUS_OBJECT_NAME_NOT_FOUND instead.

I just want to know what the problem at high level is

  • my arguments passing or
  • driver inferred semantics of the _DSM method vs. actual _DSM method (below)
  • Use the other IOCTL_ACPI_EVAL_METHOD / Ex
  • Else use GUID_ACPI_* (<wdmguid.h>) interfaces (probably not)

    NTSTATUS
    Get(
    IN WDFDEVICE Device,
    IN ULONG PoolTag,
    IN PUCHAR Arg0,
    IN ULONG Arg1,
    IN ULONG Arg2,
    IN ULONG Arg3,
    IN ULONG ValueLength,
    OUT PVOID Value
    )
    /
    Arg0: GUID
    Arg1: don?t care
    Arg2: Required Op Index
    Arg3: Don’t care
    IN ULONG ValueLength : Expected output value length 4
    /
    {
    WDFIOTARGET ioTarget = WdfDeviceGetIoTarget(Device);
    NTSTATUS status = STATUS_SUCCESS;
    WDF_MEMORY_DESCRIPTOR inputDescriptor;
    WDF_MEMORY_DESCRIPTOR outputDescriptor;
    ULONG_PTR bytesReturned = 0;
    ULONG outputSize;
    ULONG inputSize;
    ULONG inputArgSize;
    ACPI_EVAL_INPUT_BUFFER_COMPLEX input;
    ACPI_EVAL_OUTPUT_BUFFER output;
    PACPI_METHOD_ARGUMENT arg;
    WDFMEMORY inputMemoryHandle = NULL;
    WDFMEMORY outputMemoryHandle = NULL;

    //
    // Iutput
    //
    inputArgSize = ACPI_METHOD_ARGUMENT_LENGTH(sizeof(GUID));
    inputArgSize += 3 * ACPI_METHOD_ARGUMENT_LENGTH(sizeof(ULONG));

    inputSize = sizeof(ACPI_EVAL_INPUT_BUFFER_COMPLEX) + inputArgSize - sizeof(ACPI_METHOD_ARGUMENT);

    status = WdfMemoryCreate(WDF_NO_OBJECT_ATTRIBUTES,
    PagedPool, PoolTag,
    inputSize, &inputMemoryHandle, NULL);

    if (!(NT_SUCCESS(status)))
    {
    goto done;
    }

    input = (ACPI_EVAL_INPUT_BUFFER_COMPLEX
    )WdfMemoryGetBuffer(inputMemoryHandle, NULL);

    input->Signature = ACPI_EVAL_INPUT_BUFFER_COMPLEX_SIGNATURE;
    input->MethodNameAsUlong = (ULONG)‘MSD_’;
    input->Size = inputArgSize;
    input->ArgumentCount = 4;

    arg = input->Argument;
    ACPI_METHOD_SET_ARGUMENT_BUFFER(arg, Arg0, sizeof(GUID)); // Arg0
    arg = ACPI_METHOD_NEXT_ARGUMENT(arg);
    ACPI_METHOD_SET_ARGUMENT_INTEGER(arg, Arg1); // Arg1
    arg = ACPI_METHOD_NEXT_ARGUMENT(arg);
    ACPI_METHOD_SET_ARGUMENT_INTEGER(arg, Arg2); // Arg2
    arg = ACPI_METHOD_NEXT_ARGUMENT(arg);
    ACPI_METHOD_SET_ARGUMENT_INTEGER(arg, Arg3); // Arg3

    WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&inputDescriptor, input, inputSize);

    //
    // Output
    //
    outputSize = sizeof(ACPI_EVAL_OUTPUT_BUFFER) + ValueLength - sizeof(ACPI_METHOD_ARGUMENT);

    if (outputSize)
    status = WdfMemoryCreate(NULL,
    PagedPool, PoolTag,
    outputSize, &outputMemoryHandle, NULL);
    else
    status = STATUS_INVALID_PARAMETER;

    if (!(NT_SUCCESS(status)))
    {
    goto done;
    }

    output = (ACPI_EVAL_OUTPUT_BUFFER
    )WdfMemoryGetBuffer(outputMemoryHandle, NULL);

    //WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&outputDescriptor, output, outputSize);
    WDF_MEMORY_DESCRIPTOR_INIT_HANDLE(&outputDescriptor, outputMemoryHandle, NULL);

    //
    // Send IOCTL
    //
    status = WdfIoTargetSendIoctlSynchronously(
    ioTarget,
    NULL,
    IOCTL_ACPI_EVAL_METHOD,
    &inputDescriptor,
    &outputDescriptor,
    NULL,
    &bytesReturned);

    if (!(NT_SUCCESS(status)))
    {
    /////FAILL!!!
    goto done;
    }



    Method(_DSM, 4, Serialized)
    {
    If(LEqual(Arg0, ToUUID(“…”)}))
    {
    If(LEqual(Arg2, Zero))
    {
    Return(Buffer(One)
    {
    0x08
    })
    }
    If(LEqual(Arg2, One))
    {
    Return(XYZ)
    }
    If(LEqual(Arg2, 0x02))
    {
    Store(abc, XYZ)
    Return(Zero)
    }
    }
    Return(Buffer(One)
    {
    0x00
    })</wdmguid.h>

You’re passing three arguments, so why are you subtracting the size of one
of these arguments when calculating the total size of your input buffer?

//
// Iutput
//
inputArgSize = ACPI_METHOD_ARGUMENT_LENGTH(sizeof(GUID));
inputArgSize += 3 * ACPI_METHOD_ARGUMENT_LENGTH(sizeof(ULONG));

inputSize = sizeof(ACPI_EVAL_INPUT_BUFFER_COMPLEX) + inputArgSize -
sizeof(ACPI_METHOD_ARGUMENT);

For reference, this is the code I have code that calls a DSM which returns a
single USHORT (and, yes, it should probably use
ACPI_METHOD_ARGUMENT_LENGTH):

NTSTATUS status = STATUS_SUCCESS;
PACPI_EVAL_INPUT_BUFFER_COMPLEX acpiInputComplex = NULL;
PACPI_METHOD_ARGUMENT guidArg;
PACPI_METHOD_ARGUMENT revisionArg;
PACPI_METHOD_ARGUMENT indexArg;
PACPI_METHOD_ARGUMENT packageArg;
ULONG inputBufferSize;
ACPI_EVAL_OUTPUT_BUFFER acpiOutput = {0};
PACPI_METHOD_ARGUMENT registerArg;
ULONG_PTR bytesReturned;
USHORT registerValue = 0;

//
// Per the ACPI spec…
//
// 9.14.1 _DSM (Device Specific Method)
//
// This optional object is a control method that enables devices
// to provide specific control functions that are consumed by the device
driver.
//
// Arguments: (4)
//
// Arg0 - A Buffer containing a UUID
//
// Arg1 - An Integer containing the Revision ID
//
// Arg2 - An Integer containing the Function Index
//
// Arg3 - A Package that contains function-specific arguments
//
// Return Value:
//
// If Function Index = 0, a Buffer containing a function index bitfield.
// Otherwise, the return value and type depends on the UUID and
// revision ID
//

//
// We need the header, which includes one argument that we’ll
// use for the GUID
//
inputBufferSize = sizeof(ACPI_EVAL_INPUT_BUFFER_COMPLEX);

//
// Plus the size of the GUID
//
inputBufferSize += sizeof(GUID);

//
// Plus a revision ID
//
inputBufferSize += sizeof(ACPI_METHOD_ARGUMENT);

//
// Plus a function index
//
inputBufferSize += sizeof(ACPI_METHOD_ARGUMENT);

//
// Plus a package
//
inputBufferSize += sizeof(ACPI_METHOD_ARGUMENT);

acpiInputComplex =
(PACPI_EVAL_INPUT_BUFFER_COMPLEX)ExAllocatePoolWithTag(NonPagedPool,
inputBufferSize,
‘MDtA’);

if (acpiInputComplex == NULL) {

status = STATUS_INSUFFICIENT_RESOURCES;

goto Exit;

}

RtlZeroMemory(acpiInputComplex,
inputBufferSize);

acpiInputComplex->Signature = ACPI_EVAL_INPUT_BUFFER_COMPLEX_SIGNATURE;
acpiInputComplex->Size = inputBufferSize;
acpiInputComplex->ArgumentCount = 4;

RtlCopyMemory(&acpiInputComplex->MethodName,
“_DSM”,
FIELD_SIZE(ACPI_EVAL_INPUT_BUFFER_COMPLEX, MethodName));

guidArg = &acpiInputComplex->Argument[0];

ACPI_METHOD_SET_ARGUMENT_BUFFER(guidArg,
&XXX_DSM_GUID,
sizeof(GUID));

revisionArg = ACPI_METHOD_NEXT_ARGUMENT(guidArg);

ACPI_METHOD_SET_ARGUMENT_INTEGER(revisionArg,
XXX_DSM_REVISION);

indexArg = ACPI_METHOD_NEXT_ARGUMENT(revisionArg);

ACPI_METHOD_SET_ARGUMENT_INTEGER(indexArg,
XXX_DSM_FUNCTION_INDEX);

packageArg = ACPI_METHOD_NEXT_ARGUMENT(indexArg);

packageArg->Type = ACPI_METHOD_ARGUMENT_PACKAGE;

//
// And send the eval method IOCTL
//
status = OsrUtilSendIoctlSynchronous(IoTarget,
IOCTL_ACPI_EVAL_METHOD,
acpiInputComplex,
inputBufferSize,
&acpiOutput,
sizeof(ACPI_EVAL_OUTPUT_BUFFER),
&bytesReturned);

if (!NT_SUCCESS(status)) {

goto Exit;

}

-scott
OSR
@OSRDrivers

Not sure if this matters I see below in Windbg at the system boot
CreateNameSpaceObject: object already exist - _DSM

>
You’re passing three arguments, so why are you subtracting the size of one
of these arguments when calculating the total size of your input buffer?
<<
I have Arg0 = GUID and Arg1, 2, 3 as ULONGs’.
I am adding appropriate sizes through ACPI_METHOD_ARGUMENT_LENGTH(sizeof(Arg*)) in other form.

I think my and your method return the same length.

typedef struct _ACPI_EVAL_INPUT_BUFFER_COMPLEX {
ULONG Signature;
union {
UCHAR MethodName[4];
ULONG MethodNameAsUlong;
};
ULONG Size;
ULONG ArgumentCount;
ACPI_METHOD_ARGUMENT Argument[ANYSIZE_ARRAY];
} ACPI_EVAL_INPUT_BUFFER_COMPLEX, *PACPI_EVAL_INPUT_BUFFER_COMPLEX;

Also for output (in your case acpiOutput), do I have to initialize the structure (fields sig, len, count) or just provide the buffer and ACPI fills in those?

typedef struct _ACPI_EVAL_OUTPUT_BUFFER {
ULONG Signature;
ULONG Length;
ULONG Count;
ACPI_METHOD_ARGUMENT Argument[ANYSIZE_ARRAY];
} ACPI_EVAL_OUTPUT_BUFFER;

In your below code
acpiInputComplex->Size = inputBufferSize;

Should that just be sizeof(params), instead sizeof(params) + containing outer struct.
(I tried passing the length that way, still fails)

typedef struct _ACPI_EVAL_INPUT_BUFFER_COMPLEX {
ULONG Signature;
union {
UCHAR MethodName[4];
ULONG MethodNameAsUlong;
};
ULONG Size; *************
ULONG ArgumentCount;
ACPI_METHOD_ARGUMENT Argument[ANYSIZE_ARRAY];
} ACPI_EVAL_INPUT_BUFFER_COMPLEX, *PACPI_EVAL_INPUT_BUFFER_COMPLEX;

Size
The number of bytes that the Argument array contains.


Now I get STATUS_OBJECT_NAME_NOT_FOUND with below (changed to WDF_MEMORY_DESCRIPTOR_INIT_BUFFER() from WDF_MEMORY_DESCRIPTOR_INIT_HANDLE() on the outputDescriptor).

PS: My _DSM routine doesn’t even look at Arg1 and Arg3.

NTSTATUS
Get(
IN WDFDEVICE Device,
IN ULONG PoolTag,
IN PUCHAR Arg0,
IN ULONG Arg1,
IN ULONG Arg2,
IN ULONG Arg3,
IN ULONG ValueLength,
OUT PVOID Value
)
/*
Arg0: UUID Unique function identifier
Arg1: don?t care
Arg2: Required Op Index
(0 = Return Supported Functions, 1 - current L3 status, 2 - store L3 status)
Arg3: Don’t care
IN ULONG ValueLength : Expected output value length 4
*/
{
WDFIOTARGET ioTarget = WdfDeviceGetIoTarget(Device);
NTSTATUS status = STATUS_SUCCESS;
WDF_MEMORY_DESCRIPTOR inputDescriptor;
WDF_MEMORY_DESCRIPTOR outputDescriptor;
ULONG_PTR bytesReturned = 0;
ULONG outputSize;
ULONG inputSize;
ULONG inputArgSize;
ACPI_EVAL_INPUT_BUFFER_COMPLEX *input;
ACPI_EVAL_OUTPUT_BUFFER *output;
PACPI_METHOD_ARGUMENT arg;
WDFMEMORY inputMemoryHandle = NULL;
WDFMEMORY outputMemoryHandle = NULL;

//
// Iutput
//
#define NUM_ARGS 4
inputArgSize =
ACPI_METHOD_ARGUMENT_LENGTH(sizeof(GUID)) +
ACPI_METHOD_ARGUMENT_LENGTH(sizeof(ULONG)) * (NUM_ARGS-1);

inputSize =
FIELD_OFFSET(ACPI_EVAL_INPUT_BUFFER_COMPLEX, Argument) +
inputArgSize;

status = WdfMemoryCreate(WDF_NO_OBJECT_ATTRIBUTES,
PagedPool,
PoolTag,
inputSize,
&inputMemoryHandle,
(PVOID*)&input);

if (!(NT_SUCCESS(status)))
{
goto done;
}

input = (ACPI_EVAL_INPUT_BUFFER_COMPLEX*)WdfMemoryGetBuffer(inputMemoryHandle, NULL);

input->Signature = ACPI_EVAL_INPUT_BUFFER_COMPLEX_SIGNATURE;
input->MethodNameAsUlong = (ULONG)‘MSD_’;
input->Size = inputArgSize;
input->ArgumentCount = NUM_ARGS;

arg = input->Argument;
ACPI_METHOD_SET_ARGUMENT_BUFFER(arg, Arg0, sizeof(GUID)); // Arg0
arg = ACPI_METHOD_NEXT_ARGUMENT(arg);
ACPI_METHOD_SET_ARGUMENT_INTEGER(arg, Arg1); // Arg1
arg = ACPI_METHOD_NEXT_ARGUMENT(arg);
ACPI_METHOD_SET_ARGUMENT_INTEGER(arg, Arg2); // Arg2
//arg = ACPI_METHOD_NEXT_ARGUMENT(arg);
//ACPI_METHOD_SET_ARGUMENT_INTEGER(arg, Arg3); // Arg3
arg = ACPI_METHOD_NEXT_ARGUMENT(arg);
arg->Type = ACPI_METHOD_ARGUMENT_PACKAGE; //ACPI_METHOD_ARGUMENT_PACKAGE_EX
arg->DataLength = sizeof(ULONG);
arg->Argument = Arg3;

WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&inputDescriptor, input, inputSize);
//WDF_MEMORY_DESCRIPTOR_INIT_HANDLE(&inputDescriptor, inputMemoryHandle, NULL);

//
// Output
//
outputSize =
FIELD_OFFSET(ACPI_EVAL_OUTPUT_BUFFER, Argument) +
ACPI_METHOD_ARGUMENT_LENGTH(sizeof(ULONG));

if (outputSize)
status = WdfMemoryCreate(NULL,
PagedPool,
PoolTag,
outputSize,
&outputMemoryHandle,
(PVOID*)&output);
else
status = STATUS_INVALID_PARAMETER;

if (!(NT_SUCCESS(status)))
{

goto done;
}

output = (ACPI_EVAL_OUTPUT_BUFFER*)WdfMemoryGetBuffer(outputMemoryHandle, NULL);

output->Signature = ACPI_EVAL_OUTPUT_BUFFER_SIGNATURE;
output->Length = outputSize;
output->Count = 1;

//WDF_MEMORY_DESCRIPTOR_INIT_HANDLE(&outputDescriptor, outputMemoryHandle, NULL);
WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&outputDescriptor, output, outputSize);

//
// Send IOCTL
//
status = WdfIoTargetSendIoctlSynchronously(
ioTarget,
NULL,
IOCTL_ACPI_EVAL_METHOD,
&inputDescriptor,
&outputDescriptor,
NULL,
&bytesReturned);

if (!(NT_SUCCESS(status)))
{
goto done;
}

The code that I provided worked for me. Now I’m not sure what the size
should be as I found two other examples, one that includes the fixed header
in the Size:

https://github.com/Microsoft/Windows-driver-samples/blob/master/storage/miniports/storahci/src/pnppower.c#L2375

And one that doesn’t:

https://github.com/Microsoft/Windows-driver-samples/blob/360ed51d5a49fb859ba1f6e1bc4be8ceca39c1bc/usb/UcmCxUcsi/Acpi.cpp#L504

The thing to then would be to err on the side of the documentation. However,
given that it’s “working” both ways I suspect that no one is actually
validating this field.

The output structure is zeroed but I didn’t do anything else to initialize
it.

Are you sending this to your Local I/O Target? Did you confirm that the _DSM
is properly associated with your PDO?

-scott
OSR
@OSRDrivers

Thanks. After moving down the method from rootport to correct device scope it works (and I don’t see this ‘CreateNameSpaceObject: object already exist - _DSM’ print).

Success! Glad to hear you figured it out.

-scott
OSR
@OSRDrivers