Am I detecting PCI interrupt routing correctly?

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
mariuszp
Member
Member
Posts: 587
Joined: Sat Oct 16, 2010 3:38 pm

Am I detecting PCI interrupt routing correctly?

Post by mariuszp »

My approach appears to work in VirtualBox, Bochs and QEMU, but not on my laptop.

The relevant part of the code is as follows:

Code: Select all

static ACPI_STATUS pciWalkCallback(ACPI_HANDLE object, UINT32 nestingLevel, void *context, void **returnvalue)
{
	ACPI_DEVICE_INFO *info;
	ACPI_STATUS status = AcpiGetObjectInfo(object, &info);
	
	if (status != AE_OK)
	{
		panic("AcpiGetObjectInfo failed");
	};
	
	if (info->Flags & ACPI_PCI_ROOT_BRIDGE)
	{
		foundRootBridge = 1;
		
		ACPI_BUFFER prtbuf;
		prtbuf.Length = ACPI_ALLOCATE_BUFFER;
		prtbuf.Pointer = NULL;
		
		status = AcpiGetIrqRoutingTable(object, &prtbuf);
		if (status != AE_OK)
		{
			panic("AcpiGetIrqRoutingTable failed for a root bridge!\n");
		};
     /* ... parsing the information ... */
};
And I call ACPICA's enumeration function as follows:

Code: Select all

void pciInitACPI()
{
	void *retval;
	ACPI_STATUS status = AcpiGetDevices(NULL, pciWalkCallback, NULL, &retval);
	if (status != AE_OK)
	{
		panic("AcpiGetDevices failed");
	};
	
	if (!foundRootBridge)
	{
		panic("failed to find PCI root bridge");
	};
	
	pciIntInitDone = 1;
};
And when running it on my laptop, I get the "AcpiGetIrqRoutingTable failed for a root bridge!\n" message. ACPICA also claims that "there is no handler for region ERAM".

Is this a firmware bug on the laptop, or am I doing something wrong?
MollenOS
Member
Member
Posts: 202
Joined: Wed Oct 26, 2011 12:00 pm

Re: Am I detecting PCI interrupt routing correctly?

Post by MollenOS »

I don't think you fully setup ACPI correctly before enumeration. I do ALL this before calling enumeration:

Code: Select all

/* Initializes the full access and functionality
 * of ACPICA / ACPI and allows for scanning of
 * ACPI devices */
void AcpiInitialize(void)
{
	ACPI_STATUS Status;
	ACPI_OBJECT arg1;
	ACPI_OBJECT_LIST args;

	/* Debug */
	LogInformation("ACPI", "Initializing");
	LogInformation("ACPI", "Installing OSI Interface");
	
	/* Install OSL Handler */
	Status = AcpiInstallInterfaceHandler(AcpiOsi);

	/* We fake Windows 7 */
	AcpiOsiSetup("Windows 2009");

	/* Install */
	AcpiOsiInstall();

	/* Initialize the ACPICA subsystem */
	LogInformation("ACPI", "Installing Subsystems");
	Status = AcpiInitializeSubsystem();
	if (ACPI_FAILURE(Status))
	{
		LogFatal("ACPI", "Failed to initialize subsystems, %u!", Status);
		for (;;);
	}

	/* Copy the root table list to dynamic memory */
	LogInformation("ACPI", "Reallocating Tables");
	Status = AcpiReallocateRootTable();
	if (ACPI_FAILURE(Status))
	{
		LogFatal("ACPI", "Failed AcpiReallocateRootTable, %u!", Status);
		for (;;);
	}

	/* Install the default address space handlers. */
	Status = AcpiInstallAddressSpaceHandler(ACPI_ROOT_OBJECT,
		ACPI_ADR_SPACE_SYSTEM_MEMORY, ACPI_DEFAULT_HANDLER, NULL, NULL);
	if (ACPI_FAILURE(Status)) {
		LogDebug("ACPI", "Could not initialise SystemMemory handler, %s!", 
			AcpiFormatException(Status));
	}

	Status = AcpiInstallAddressSpaceHandler(ACPI_ROOT_OBJECT,
		ACPI_ADR_SPACE_SYSTEM_IO, ACPI_DEFAULT_HANDLER, NULL, NULL);
	if (ACPI_FAILURE(Status)) {
		LogDebug("ACPI", "Could not initialise SystemIO handler, %s!",
			AcpiFormatException(Status));
	}

	Status = AcpiInstallAddressSpaceHandler(ACPI_ROOT_OBJECT,
		ACPI_ADR_SPACE_PCI_CONFIG, ACPI_DEFAULT_HANDLER, NULL, NULL);
	if (ACPI_FAILURE(Status)) {
		LogDebug("ACPI", "Could not initialise PciConfig handler, %s!",
			AcpiFormatException(Status));
	}

	/* Create the ACPI namespace from ACPI tables */
	LogInformation("ACPI", "Loading Tables");
	Status = AcpiLoadTables();
	if (ACPI_FAILURE(Status))
	{
		LogFatal("ACPI", "Failed LoadTables, %u!", Status);
		for (;;);
	}

	/* Initialize the ACPI hardware */
	LogInformation("ACPI", "Enabling Subsystems");
	Status = AcpiEnableSubsystem(ACPI_FULL_INITIALIZATION);
	if (ACPI_FAILURE(Status))
	{
		LogFatal("ACPI", "Failed AcpiEnableSubsystem, %u!", Status);
		for (;;);
	}
	
	/* Probe for EC here */

	/* Complete the ACPI namespace object initialization */
	LogInformation("ACPI", "Initializing Objects");
	Status = AcpiInitializeObjects(ACPI_FULL_INITIALIZATION);
	if (ACPI_FAILURE(Status))
	{
		LogFatal("ACPI", "Failed AcpiInitializeObjects, %u!", Status);
		for (;;);
	}

	/* Run _OSC on root, it should always be run after InitializeObjects */
	AcpiCheckBusOscSupport();

	/* Set APIC Mode */
	arg1.Type = ACPI_TYPE_INTEGER;
	arg1.Integer.Value = 1;				/* 0 - PIC, 1 - IOAPIC, 2 - IOSAPIC */
	args.Count = 1;
	args.Pointer = &arg1;

	AcpiEvaluateObject(ACPI_ROOT_OBJECT, "_PIC", &args, NULL);

	/* Info */
	LogInformation("ACPI", "Installing Event Handlers");

	/* Install a notify handler */
	AcpiInstallNotifyHandler(ACPI_ROOT_OBJECT, ACPI_SYSTEM_NOTIFY, AcpiBusNotifyHandler, NULL);

	/* Install a global event handler */
	AcpiInstallGlobalEventHandler(AcpiEventHandler, NULL);
	//AcpiInstallFixedEventHandler(ACPI_EVENT_POWER_BUTTON, acpi_shutdown, NULL);
	//AcpiInstallFixedEventHandler(ACPI_EVENT_SLEEP_BUTTON, acpi_sleep, NULL);
	//ACPI_BUTTON_TYPE_LID
mariuszp
Member
Member
Posts: 587
Joined: Sat Oct 16, 2010 3:38 pm

Re: Am I detecting PCI interrupt routing correctly?

Post by mariuszp »

Thank you. Another related question; if I find the PCI root bridge (as I do above), it will only give me the IRQ routing information for devices on PCI bus 0, right? In this case, how do I find interrupt routing for the other buses?
MollenOS
Member
Member
Posts: 202
Joined: Wed Oct 26, 2011 12:00 pm

Re: Am I detecting PCI interrupt routing correctly?

Post by MollenOS »

No, the pci root bus will in most cases contain all the irq routings (unless there is multiple pci roots). But, devices behind pci bridges need to have their pci routings sizzled (look it up, pci bridges reroute in some cases, but this is something fixed) in most cases, and therefore you usually can't directly use the IRQ routing without changing it.
mariuszp
Member
Member
Posts: 587
Joined: Sat Oct 16, 2010 3:38 pm

Re: Am I detecting PCI interrupt routing correctly?

Post by mariuszp »

Now it gives me a problem here, in VirtualBox:

Code: Select all

			ACPI_PCI_ROUTING_TABLE *table = (ACPI_PCI_ROUTING_TABLE*) scan;
			if (table->Length == 0)
			{
				break;
			};
			
			uint8_t slot = (uint8_t) (table->Address >> 16);
			if (table->Source == 0)
			{
				// static assignment
				pciMapInterruptFromGSI(0, slot, table->Pin+1, table->SourceIndex);
			}
			else
			{
				// get the PCI Link Object
				ACPI_HANDLE linkObject;
				status = AcpiGetHandle(object, table->Source, &linkObject);
				if (status != AE_OK)
				{
					panic("AcpiGetHandle failed for '%s'", table->Source);
				};
AcpiGetHandle fails, as table->Source is an empty string.
MollenOS
Member
Member
Posts: 202
Joined: Wed Oct 26, 2011 12:00 pm

Re: Am I detecting PCI interrupt routing correctly?

Post by MollenOS »

Code: Select all

if (table->Source[0] == '\0')
Is the check you should make
mariuszp
Member
Member
Posts: 587
Joined: Sat Oct 16, 2010 3:38 pm

Re: Am I detecting PCI interrupt routing correctly?

Post by mariuszp »

Well, now it doesn't panic but it appears that my network driver lost the ability to receive interrupts. It also seems suspicious since now I'm clearly getting GSIs instead of LINK objects for the interrupt routes.

EDIT: Here's the code for attaching a PCI device to GSI:

Code: Select all

static void pciMapInterruptFromGSI(uint8_t bus, uint8_t slot, uint8_t intpin, int gsi)
{
	kprintf("PCI %d/%d INT%c# -> GSI %d\n", (int) bus, (int) slot, 'A'+intpin, gsi);
	static int nextLocalInt = 0;
	
	int i;
	for (i=0; i<gsiMapSize; i++)
	{
		if (gsiMapCache[i].gsi == gsi)
		{
			pciMapLocalInterrupt(bus, slot, intpin, gsiMapCache[i].intNo);
			return;
		};
	};
	
	int intNo = I_PCI0 + nextLocalInt;
	nextLocalInt = (nextLocalInt + 1) % 16;
	
	PCIGlobalInterruptMapping *newCache = (PCIGlobalInterruptMapping*) kmalloc(sizeof(PCIGlobalInterruptMapping)*(gsiMapSize+1));
	memcpy(newCache, gsiMapCache, sizeof(PCIGlobalInterruptMapping)*gsiMapSize);
	newCache[gsiMapSize].gsi = gsi;
	newCache[gsiMapSize].intNo = intNo;
	if (gsiMapCache != NULL) kfree(gsiMapCache);
	gsiMapCache = newCache;
	
	// remap the interrupt
	uint32_t volatile* regsel = (uint32_t volatile*) 0xFFFF808000002000;
	uint32_t volatile* iowin = (uint32_t volatile*) 0xFFFF808000002010;
	*regsel = (0x10+2*gsi);
	__sync_synchronize();
	uint64_t entry = (uint64_t)(intNo) | ((uint64_t)(apic->id) << 56);
	*iowin = (uint32_t) entry;
	__sync_synchronize();
	*regsel = (0x10+2*gsi+1);
	__sync_synchronize();
	*iowin = (uint32_t)(entry>>32);
	__sync_synchronize();
	
	// and register with the device
	pciMapLocalInterrupt(bus, slot, intpin, intNo);
};
Is this the correct way of doing this? I'm basically mapping the supplied interrupt number (from table->SourceIndex) to one of my local PCI IRQs (named I_PCI0 - I_PCI15) using IOAPIC. The device driver is triggered when the IRQs it was mapped to arrives. Could it be that multiple devices are connected to the same GSI?
MollenOS
Member
Member
Posts: 202
Joined: Wed Oct 26, 2011 12:00 pm

Re: Am I detecting PCI interrupt routing correctly?

Post by MollenOS »

Uh, are you mapping your interrupts correctly with the correct set trigger / polarity? They should be set to Active Low, Level Triggered.

You should in most cases get GSI and not link objects. Only 1 in my 8 laptops have link objects.
mariuszp
Member
Member
Posts: 587
Joined: Sat Oct 16, 2010 3:38 pm

Re: Am I detecting PCI interrupt routing correctly?

Post by mariuszp »

The behaviour I spoke about above happens in VirtualBox.
I've tried this on my laptop and it gets stuck either on AcpiLoadTable() or AcpiEnableSubsystem(). I'm going to do some extra debugging and post another respoonse.

And I'll check if the polarity is correct.

Thanks for the help so far.
MollenOS
Member
Member
Posts: 202
Joined: Wed Oct 26, 2011 12:00 pm

Re: Am I detecting PCI interrupt routing correctly?

Post by MollenOS »

If it fails under the Acpi* Functions, it's probably because your glue-functions (or - LibC functions) are implemented wrong or incorrectly.
mariuszp
Member
Member
Posts: 587
Joined: Sat Oct 16, 2010 3:38 pm

Re: Am I detecting PCI interrupt routing correctly?

Post by mariuszp »

MollenOS wrote:If it fails under the Acpi* Functions, it's probably because your glue-functions (or - LibC functions) are implemented wrong or incorrectly.
That is very possible, so I'm going to debug it more to see why that is the case.

Also, it appears that multiple INTx# pins were connected to the same GSI, and my OS failed to recognise that due to a subtle bug, and the polarity and trigger were both wrong! But that is fixed now, and interrupts are working in VirtualBox again.

So now I just have to figure out why the Acpi functions get stuck and hopefully it will run on my laptop.
mariuszp
Member
Member
Posts: 587
Joined: Sat Oct 16, 2010 3:38 pm

Re: Am I detecting PCI interrupt routing correctly?

Post by mariuszp »

I've done some debugging and noticed something strange. I noticed it hangs on an AcpiOsWaitSemaphore(), so I made that function print a stack trace every time it's called, and it hanged on this:
14393255_1654373174874476_431390092_o.jpg
I then made it print a message whenever that function is called with that specific semaphore address and got this:
14394004_1654661588178968_2051502666_o.jpg
where the address after "waited by..." is the thread description. So it appears that the same ACPICa thread tries to lock the same mutex twice; I've also found that it is the ACPI_MTX_NAMESPACE.

What could be the reason behind this?
MollenOS
Member
Member
Posts: 202
Joined: Wed Oct 26, 2011 12:00 pm

Re: Am I detecting PCI interrupt routing correctly?

Post by MollenOS »

I would suspect your implementation of AcpiOsWaitSemaphore is incorrect
mariuszp
Member
Member
Posts: 587
Joined: Sat Oct 16, 2010 3:38 pm

Re: Am I detecting PCI interrupt routing correctly?

Post by mariuszp »

MollenOS wrote:I would suspect your implementation of AcpiOsWaitSemaphore is incorrect
That wouldn't explain why it's called twice without the signal function being called.

The implementation is as follows:

Code: Select all

ACPI_STATUS AcpiOsWaitSemaphore(ACPI_SEMAPHORE sem, UINT32 units, UINT16 timeout)
{
	TRACE();
	//kprintf("AcpiOsWaitSemaphore(%p, %u, %hu)\n", sem, units, timeout);
	//stackTraceHere();
	if (sem == (void*)0xFFFF810000290E80)
	{
		kprintf("Semaphore %p waited by %p\n", sem, getCurrentThread());
	};
	
	int count = (int) units;
	if (sem == NULL)
	{
		return AE_BAD_PARAMETER;
	};
	
	if (units == 0)
	{
		return AE_OK;
	};

	int flags;
	uint64_t nanoTimeout;
	
	if (timeout == 0)
	{
		flags = SEM_W_NONBLOCK;
		nanoTimeout = 0;
	}
	else if (timeout == 0xFFFF)
	{
		flags = 0;
		nanoTimeout = 0;
	}
	else
	{
		flags = 0;
		nanoTimeout = (uint64_t) timeout * 1000000;
	};
	
	uint64_t deadline = getNanotime() + nanoTimeout;
	int acquiredSoFar = 0;
	
	while (count > 0)
	{
		int got = semWaitGen(sem, count, flags, nanoTimeout);
		if (got < 0)
		{
			semSignal2(sem, acquiredSoFar);
			return AE_TIME;
		};
		
		acquiredSoFar += got;
		count -= got;
		
		uint64_t now = getNanotime();
		if (now < deadline)
		{
			nanoTimeout = deadline - now;
		}
		else if (nanoTimeout != 0)
		{
			semSignal2(sem, acquiredSoFar);
			return AE_TIME;
		};
	};
	
	return AE_OK;
};
The loop is there because semWaitGen() doesn't wait until ALL requested units are available; it waits until ANY are available and returns however many possible.
mariuszp
Member
Member
Posts: 587
Joined: Sat Oct 16, 2010 3:38 pm

Re: Am I detecting PCI interrupt routing correctly?

Post by mariuszp »

Does ACPICA need recursive mutexes or something? It clearly doesn't work when it implements mutexes with semaphores; after locking a mutex, it calls a function which then tries to lock the mutex again, as can be seen on the stack trace I've provided. I changes the AcpiExStartMethodTrace function so that it always fails to acquire the mutex but now something else hangs... has anyone else experienced a similar problem? This only happens on my TOSHIBA laptop, doesn't happen in VirtualBox.

EDIT: Apparently someone else had it hang like that too:
http://f.osdev.org/viewtopic.php?f=2&t=24229
Post Reply