DEXT crashes when accessing BAR0 offsets using MemoryWrite

We have developed a DEXT using PCIDriverKit with IOPCIDevice as its provider. DEXT crashes when accessing Memory Space using MemoryWriteXX APIs available in PCIDriverKit.

We are following the below steps as specified in the Modernize PCI and SCSI drivers with DriverKit presentation.

  1. We are calling Open() in Start() as PCI DEXTs must open their provider prior to memory/configuration space access. However, this method returns kIOReturnNotOpen error code. This has already been posted as a separate question here.
  2. We are enabling bus master and memory space as this has to be done before issuing any memory reads/writes.
  3. Though Open() fails, we are still able to read/write to the device's configuration space using ConfigurationReadxx/ConfigurationWritexx APIs.
  4. As per the presentation, device memory mapping is done by PCIDriverKit and MemoryReadxx is working fine for reading offsets in a specific memory index (say BAR0).

However, DEXT crashes when we try to use MemoryWritexx for writing to any offset (controller registers) in memory index 0 (BAR0). Our device has only BAR0 available. We verified from the crash log that the point of crash is the MemoryWrite method.

  • Are there any other steps that have to be performed before writing to the memory space using MemoryWrite?

  • Should the Open() call be successful in order to write to the memory space? If yes, how to open a session successfully without Open() returning an error code?

  • The above steps were performed on a M1 machine. We executed the same steps on an Intel machine but even MemoryReadxx API fails there. Is this related to device memory mapping on an Intel machine? How to overcome this issue if PCIDriverKit takes care of device memory mapping?

Any help or suggestions would be greatly appreciated.

Can you show some of your code, obfuscated if needed. Are you doing things like overriding "init" and calling "super::init" there? Are you calling "Start(provider, SUPERDISPATCH);" from your "Open" implementation? Without code, this question is a bit too vague to provide helpful feedback.

Yes, I have overriden init() to initialize DEXT IVars and I'm calling super::init().Also I have overriden Start method and calling Start(provider, SUPERDISPATCH). In Start(), I'm calling Open(this, 0) and this Open call in Start method is failing but still able to read/write configuration space and Memory Read is also successful.

Basically I'm following Modernize PCI and SCSI drivers with DriverKit.

I have posted the code that I have referenced in the answer section.

@Drewbadour

  1. Yes, I have overriden init() to initialize DEXT IVars and I'm calling super::init()
  2. Also I have overriden Start method and calling Start(provider, SUPERDISPATCH). In Start(), I'm calling Open(this, 0) and this Open call in Start method is failing with error code kIOReturnNotOpen(0x2cd) but still able to read/write configuration space and MemoryRead is also successful.

Below is the code I have implemented with reference from Modernize PCI and SCSI drivers with DriverKit

bool
MyDriver::init()
{
    if (!super::init())
    {
        return false;
    }

    ivars = IONewZero(MyDriver_IVars, 1);
    if (!ivars)
    {
        return false;
    }
    
    //Initialization of IVars here
	-------------------------------
    return true;
}


// Open fails with error code kIOReturnNotOpen(0x2cd)
kern_return_t
IMPL(MyDriver, Start)
{
    kern_return_t ret = kIOReturnSuccess;    
    ret = Start(provider, SUPERDISPATCH);
    if(ret != kIOReturnSuccess)
    {
        return kIOReturnNoDevice;
    }
    
    ivars->mIOPCIDeviceObj = OSDynamicCast(IOPCIDevice, provider);
    if(nullptr == ivars->mIOPCIDeviceObj)
    {
        return kIOReturnNoDevice;
    }

    ret = ivars->mIOPCIDeviceObj->Open(this, 0);
    if(kIOReturnSuccess != ret)
    {
    	// Open call is always failing with 0x2cd but still proceeding further
  	//return kIOReturnNoDevice;
    }

    // Enabling Bus Master and Memory Space
    ivars->mIOPCIDeviceObj->ConfigurationRead16(kIOPCIConfigurationOffsetCommand, &commandRegister);
    commandRegister |= (kIOPCICommandBusMaster | kIOPCICommandMemorySpace);
    ivars->mIOPCIDeviceObj->ConfigurationWrite16(kIOPCIConfigurationOffsetCommand, commandRegister);
    
    if (kIOReturnSuccess != RegisterService())
    {
      	return kIOReturnError;
    }
   
    return kIOReturnSuccess;
}


// Issue arises in this method, MemoryWriteXX always crashes
kern_return_t
MyDriver::CommunicateWithDevice(IOByteCount offset)
{
	uint64_t Offset = some offset in controller registers
	uint32_t Data = some data;
	
	// Memory Read, This works fine
	ivars->mIOPCIDeviceObj->MemoryRead32(0, Offset, &Data);
	
	//Memory Write, This crashes always
	ivars->mIOPCIDeviceObj->MemoryWrite32(0, Offset, Data);
	
	return kIOReturnSuccess;
}


void
IMPL(MyDriver, Stop)
{
    uint16_t commandRegister;
    
    // Disable Bus Master and Memory Space
	ivars->mIOPCIDeviceObj->ConfigurationRead16(kIOPCIConfigurationOffsetCommand, &commandRegister);
	commandRegister &= ~(kIOPCICommandBusMaster | kIOPCICommandMemorySpace);
	ivars->mIOPCIDeviceObj->ConfigurationWrite16(kIOPCIConfigurationOffsetCommand, commandRegister);
    
	ivars->mIOPCIDeviceObj->Close(this, 0);

    Stop(provider, SUPERDISPATCH);
}

void
MyDriver::free()
{
    IODelete(ivars, MyDriver_IVars, 1);
    super::free();
}

@dune_sea_jedi

Given below are the Info.plist and entitlement files we use for our driver. The driver matches against the device and we are able to communicate with the device. We want to know whether we are missing something in our steps of accessing memory space using MemoryWriteXX APIs.

Driver Info.plist details

IOClass: IOUserService
IOMatchCategory: MyDriver
IOPCIMatch: 0xDEVID and VID
IOPCISecondaryMatch: 0xDEVID and VID
IOResourceMatch: IOKit
IOUserClass: MyDriver
IOProviderClass: IOPCIDevice
IOUserServerName: com.mycompany.mypcidevice.MyDriver
UserClientProperties (Dictionary)
IOUserClass: MyDriverUserClient
IOClass: IOUserUserClient

Driver Entitlements

com.apple.developer.driverkit
com.apple.developer.driverkit.transport.pci (Array)
Item 0
IOPCIMatch: 0xVID and DEVID
Item 1:
IOPCISecondaryMatch: 0xVID and DEVID

App Entitlements

com.apple.developer.system-extension.install
com.apple.developer.system-extension.uninstall
com.apple.developer.driverkit.userclient-access (Array)
Item 0: com.mycompany.mypcidevice.MyDriver
DEXT crashes when accessing BAR0 offsets using MemoryWrite
 
 
Q