Call ACPI control method failed from kernel mode driver with status = STATUS_INVALID_PARAMETER

I have a kernel mode driver that handles an acpi device called \_SB.SECO has a custom control method called FIRE. In ACPI UEFI, I define it as:

Scope (\_SB_)
{
      Device (SECO)
      {
          Name (_HID, "TEST2412")   // Hardware ID
          Name (_UID, 0x0)        // Unique ID

          Method (_STA, 0, NotSerialized)
          {
              Return (0x0F) // Device is present and enabled
          }

          Method (FIRE, 0, NotSerialized)
          {
              Notify (\_SB.FIRS, 0x80) // notify another device
          }
      }
}

In Device Manager, I can see the Physical Device Object name for SECO is \Device\00000071. So from my driver, I get the object pointer to it:

NTSTATUS GetAcpiDevice(PDEVICE_OBJECT* pAcpiDevice, PFILE_OBJECT* pFileObject) {
    UNICODE_STRING AcpiDeviceName;
    RtlInitUnicodeString(&AcpiDeviceName, L"\\Device\\00000071");

    return IoGetDeviceObjectPointer(&AcpiDeviceName, FILE_ALL_ACCESS, pFileObject, pAcpiDevice);
}

After getting physical device object, I build an IRP to evaluate FIRE control method and send it to ACPI driver, which in this case is the bus driver and will handle the IRP for the PDO:

NTSTATUS CallAcpiMethod(PDEVICE_OBJECT pAcpiDevice) {
    KdPrint(("CallAcpiMethod enter\n"));
    ACPI_EVAL_INPUT_BUFFER inputBuffer;
    ACPI_EVAL_OUTPUT_BUFFER outputBuffer;
    IO_STATUS_BLOCK ioStatus;
    KEVENT event;
    PIRP irp;

    // Initialize input buffer for method call
    RtlZeroMemory(&inputBuffer, sizeof(ACPI_EVAL_INPUT_BUFFER));
    inputBuffer.MethodNameAsUlong = (ULONG)('ERIF');
    inputBuffer.Signature = ACPI_EVAL_INPUT_BUFFER_SIGNATURE;

    // Create event for IRP completion
    KeInitializeEvent(&event, NotificationEvent, FALSE);

    // Build IRP for IOCTL_ACPI_EVAL_METHOD
    irp = IoBuildDeviceIoControlRequest(
        IOCTL_ACPI_EVAL_METHOD,
        pAcpiDevice,
        &inputBuffer, sizeof(ACPI_EVAL_INPUT_BUFFER),
        &outputBuffer, sizeof(ACPI_EVAL_OUTPUT_BUFFER),
        FALSE, &event, &ioStatus);
    KdPrint(("IRP Address: %p\n", irp));
    if (!irp) {
        KdPrint(("failed to create IRP\n"));
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    // Send request
    NTSTATUS status = IoCallDriver(pAcpiDevice, irp);
    if (status == STATUS_PENDING) {
        KdPrint(("IoCallDriver pending status=%ld\n", status));
        KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
        status = ioStatus.Status;
    }

    KdPrint(("CallAcpiMethod exit status=%ld\n", status));
    return status;
}

The log indicates that IRP was successfully created, but the status from IoCallDriver is -1073741811 (STATUS_INVALID_PARAMETER).

How can I fix this issue?

Interfacing with ACPI is one of the more frustrating things I did in my driver work.

Since your request has no output parameters, have you tried passing "NULL, 0" for the output buffer? I don't really think that's the issue, but it's worth a try. The requests I did all had output arguments, so I never dealt with this.

I did change the outputBuffer params to "NULL, 0" but the status code is still the same. As I understand, we need to pass physical device object to IoCallDriver. Do I use the right way to get it?

NTSTATUS GetAcpiDevice(PDEVICE_OBJECT* pAcpiDevice, PFILE_OBJECT* pFileObject) {
    UNICODE_STRING AcpiDeviceName;
    RtlInitUnicodeString(&AcpiDeviceName, L"\\Device\\00000071");

    return IoGetDeviceObjectPointer(&AcpiDeviceName, FILE_ALL_ACCESS, pFileObject, pAcpiDevice);
}

I don’t think you can do what you’re trying to do. IIRC, you need to be in the stack of the ACPI method you want to call.

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.