PCI device I/O ports work under Windows but not under Linux

338 views Asked by At

[Edited]

I am trying to access an I/O port of a PCI device under Linux x86_64, however

  • inl() only ever reads 0xFFFFFFFF
  • outl() does not effect the hardware

It works under Windows (XP x86) as long as any driver (I tested with a completely empty one) is loaded for that device.

The I/O port range is different under the OSes and seems to be auto-configured by PCI bus driver.

No amount of enabling/disabling/installing/configuring other devices, buses or BIOS settings changes the port range that is assigned to the device by either OS.

The linux driver does only the following:

  • From my kernel module init() function:
    • pci_register_driver() specifying relevant PCI vendor/device IDs
  • From my pci_probe() handler function:
    • pci_enable_device()
    • pci_resource_*() which return same PCI BAR data as lspci
    • pci_request_regions()
    • inl() / outl()
  • From my kernel module exit() function
    • pci_unregister_driver()

Here is the code:

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/uaccess.h>

static void pci_release(struct pci_dev* pcidev)
{
    pci_release_regions(pcidev);
    pci_disable_device(pcidev);
    pr_err("pci_disable_device");
    return;
}

static int pci_probe(struct pci_dev* pcidev, const struct pci_device_id* pcidev_id)
{
    long result;

    result = pci_enable_device(pcidev);
    pr_err("pci_enable_device()=%ld", result);

    pr_err("res0=0x%lX", (ulong)pci_resource_flags(pcidev, 0));
    pr_err("start=0x%lX", (ulong)pci_resource_start(pcidev, 0));
    pr_err("end=0x%lX", (ulong)pci_resource_end(pcidev, 0));

    result = pci_request_regions(pcidev, "iotest");
    pr_err("pci_request_regions()=%ld", result);

    result = inl(pci_resource_start(pcidev, 0));
    pr_err("inl()=%lX", result);

    return 0;
}

static struct pci_device_id pci_ids[] =
{
    { PCI_DEVICE(0x4321, 0x9876), },
    { 0, }
};
MODULE_DEVICE_TABLE(pci, pci_ids);

static struct pci_driver pcidriver =
{
    .probe = pci_probe,
    .remove = pci_release,
    .id_table = pci_ids,
    .name = "iotest"
};

static int __init kmodule_init(void)
{
    pr_err("init");
    return pci_register_driver(&pcidriver);
}

static void __exit kmodule_exit(void)
{
    pr_err("exit");
    pci_unregister_driver(&pcidriver);
}

module_init(kmodule_init);
module_exit(kmodule_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("iotest");
MODULE_DESCRIPTION("iotest");
MODULE_VERSION("1.0");

My pci_probe() function is called, no errors are returned, but I/O behaves as if ports are not truly allocated.

[ 8809.201100] init
[ 8809.203209] pci_enable_device()=0
[ 8809.205237] res0=0x40101
[ 8809.206911] start=0xEF00
[ 8809.208574] end=0xEF3F
[ 8809.210230] pci_request_regions()=0
[ 8809.211868] inl()=FFFFFFFF
[ 8820.426361] exit

The I/O ports are reported to be the same as in lspci -n -vv output:

03:0e.0 1100: 4321:9876
    Subsystem: 4321:9876
    Control: I/O+ Mem- BusMaster- SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx-
    Status: Cap- 66MHz- UDF- FastB2B+ ParErr- DEVSEL=fast >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-
    Interrupt: pin A routed to IRQ 22
    Region 0: I/O ports at ef00 [size=64]

relevant section of /proc/ioports:

0d00-ffff : PCI Bus 0000:00
  e000-efff : PCI Bus 0000:02
    e000-efff : PCI Bus 0000:03
      ef00-ef3f : 0000:03:0e.0
        ef00-ef3f : iotest

Does anyone have any ideas why can this be happening? Am I missing something?

0

There are 0 answers