Page 1 of 1

Bochs+VirtualBox connect all PCI devices to IRQ 11?

Posted: Mon Sep 28, 2015 4:34 pm
by mariuszp
I have written the following function to detect PCI routing in my OS using ACPICA:

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");
		};
		
		char *scan = (char*) prtbuf.Pointer;
		while (1)
		{
			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);
				};
				
				// get the IRQ it is using
				ACPI_BUFFER resbuf;
				resbuf.Length = ACPI_ALLOCATE_BUFFER;
				resbuf.Pointer = NULL;
				
				status = AcpiGetCurrentResources(linkObject, &resbuf);
				if (status != AE_OK)
				{
					panic("AcpiGetCurrentResources failed for '%s'", table->Source);
				};
				
				char *rscan = (char*) resbuf.Pointer;
				int devIRQ = -1;
				while (1)
				{
					ACPI_RESOURCE *res = (ACPI_RESOURCE*) rscan;
					if (res->Type == ACPI_RESOURCE_TYPE_END_TAG)
					{
						break;
					};
					
					if (res->Type == ACPI_RESOURCE_TYPE_IRQ)
					{
						devIRQ = res->Data.Irq.Interrupts[table->SourceIndex];
						break;
					};
					
					rscan += res->Length;
				};
				
				kfree(resbuf.Pointer);
				
				if (devIRQ == -1)
				{
					panic("failed to derive IRQ for device %d from '%s'", (int)slot, table->Source);
				};
				
				kprintf("DEVICE %d intpin INT%c# -> IRQ %d (from '%s')\n", (int) slot, 'A'+table->Pin, devIRQ, table->Source);
			};
			
			scan += table->Length;
		};
		
		kfree(prtbuf.Pointer);
		panic("end of tables");
	};
	
	ACPI_FREE(info);
	return AE_OK;
};
The Bochs debug log, while booting up, shows IRQ routing:

Code: Select all

00001032696i[P2ISA ] PCI IRQ routing: PIRQA# set to 0x0b
00001032715i[P2ISA ] PCI IRQ routing: PIRQB# set to 0x09
00001032734i[P2ISA ] PCI IRQ routing: PIRQC# set to 0x0b
00001032753i[P2ISA ] PCI IRQ routing: PIRQD# set to 0x09
So we can see that INTA# and INTC# are connected to IRQ 11 and INTB# and INTD# are connected to IRQ 9. But my OS disagrees:

Code: Select all

DEVICE 0 intpin INTA# -> IRQ 11 (from '\_SB_.LNKD')
DEVICE 0 intpin INTB# -> IRQ 11 (from '\_SB_.LNKA')
DEVICE 0 intpin INTC# -> IRQ 11 (from '\_SB_.LNKB')
DEVICE 0 intpin INTD# -> IRQ 11 (from '\_SB_.LNKC')
DEVICE 1 intpin INTA# -> IRQ 11 (from '\_SB_.LNKA')
DEVICE 1 intpin INTB# -> IRQ 11 (from '\_SB_.LNKB')
DEVICE 1 intpin INTC# -> IRQ 11 (from '\_SB_.LNKC')
DEVICE 1 intpin INTD# -> IRQ 11 (from '\_SB_.LNKD')
DEVICE 2 intpin INTA# -> IRQ 11 (from '\_SB_.LNKB')
DEVICE 2 intpin INTB# -> IRQ 11 (from '\_SB_.LNKC')
DEVICE 2 intpin INTC# -> IRQ 11 (from '\_SB_.LNKD')
DEVICE 2 intpin INTD# -> IRQ 11 (from '\_SB_.LNKA')
DEVICE 3 intpin INTA# -> IRQ 11 (from '\_SB_.LNKC')
DEVICE 3 intpin INTB# -> IRQ 11 (from '\_SB_.LNKD')
DEVICE 3 intpin INTC# -> IRQ 11 (from '\_SB_.LNKA')
DEVICE 3 intpin INTD# -> IRQ 11 (from '\_SB_.LNKB')
DEVICE 4 intpin INTA# -> IRQ 11 (from '\_SB_.LNKD')
DEVICE 4 intpin INTB# -> IRQ 11 (from '\_SB_.LNKA')
DEVICE 4 intpin INTC# -> IRQ 11 (from '\_SB_.LNKB')
DEVICE 4 intpin INTD# -> IRQ 11 (from '\_SB_.LNKC')
DEVICE 5 intpin INTA# -> IRQ 11 (from '\_SB_.LNKA')
DEVICE 5 intpin INTB# -> IRQ 11 (from '\_SB_.LNKB')
DEVICE 5 intpin INTC# -> IRQ 11 (from '\_SB_.LNKC')
DEVICE 5 intpin INTD# -> IRQ 11 (from '\_SB_.LNKD')
DEVICE 6 intpin INTA# -> IRQ 11 (from '\_SB_.LNKB')
DEVICE 6 intpin INTB# -> IRQ 11 (from '\_SB_.LNKC')
DEVICE 6 intpin INTC# -> IRQ 11 (from '\_SB_.LNKD')
DEVICE 6 intpin INTD# -> IRQ 11 (from '\_SB_.LNKA')
DEVICE 7 intpin INTA# -> IRQ 11 (from '\_SB_.LNKC')
DEVICE 7 intpin INTB# -> IRQ 11 (from '\_SB_.LNKD')
DEVICE 7 intpin INTC# -> IRQ 11 (from '\_SB_.LNKA')
DEVICE 7 intpin INTD# -> IRQ 11 (from '\_SB_.LNKB')
DEVICE 8 intpin INTA# -> IRQ 11 (from '\_SB_.LNKD')
DEVICE 8 intpin INTB# -> IRQ 11 (from '\_SB_.LNKA')
DEVICE 8 intpin INTC# -> IRQ 11 (from '\_SB_.LNKB')
DEVICE 8 intpin INTD# -> IRQ 11 (from '\_SB_.LNKC')
DEVICE 9 intpin INTA# -> IRQ 11 (from '\_SB_.LNKA')
DEVICE 9 intpin INTB# -> IRQ 11 (from '\_SB_.LNKB')
DEVICE 9 intpin INTC# -> IRQ 11 (from '\_SB_.LNKC')
DEVICE 9 intpin INTD# -> IRQ 11 (from '\_SB_.LNKD')
DEVICE 10 intpin INTA# -> IRQ 11 (from '\_SB_.LNKB')
DEVICE 10 intpin INTB# -> IRQ 11 (from '\_SB_.LNKC')
DEVICE 10 intpin INTC# -> IRQ 11 (from '\_SB_.LNKD')
DEVICE 10 intpin INTD# -> IRQ 11 (from '\_SB_.LNKA')
DEVICE 11 intpin INTA# -> IRQ 11 (from '\_SB_.LNKC')
DEVICE 11 intpin INTB# -> IRQ 11 (from '\_SB_.LNKD')
DEVICE 11 intpin INTC# -> IRQ 11 (from '\_SB_.LNKA')
DEVICE 11 intpin INTD# -> IRQ 11 (from '\_SB_.LNKB')
DEVICE 12 intpin INTA# -> IRQ 11 (from '\_SB_.LNKD')
DEVICE 12 intpin INTB# -> IRQ 11 (from '\_SB_.LNKA')
DEVICE 12 intpin INTC# -> IRQ 11 (from '\_SB_.LNKB')
DEVICE 12 intpin INTD# -> IRQ 11 (from '\_SB_.LNKC')
DEVICE 13 intpin INTA# -> IRQ 11 (from '\_SB_.LNKA')
DEVICE 13 intpin INTB# -> IRQ 11 (from '\_SB_.LNKB')
DEVICE 13 intpin INTC# -> IRQ 11 (from '\_SB_.LNKC')
DEVICE 13 intpin INTD# -> IRQ 11 (from '\_SB_.LNKD')
DEVICE 14 intpin INTA# -> IRQ 11 (from '\_SB_.LNKB')
DEVICE 14 intpin INTB# -> IRQ 11 (from '\_SB_.LNKC')
DEVICE 14 intpin INTC# -> IRQ 11 (from '\_SB_.LNKD')
DEVICE 14 intpin INTD# -> IRQ 11 (from '\_SB_.LNKA')
DEVICE 15 intpin INTA# -> IRQ 11 (from '\_SB_.LNKC')
DEVICE 15 intpin INTB# -> IRQ 11 (from '\_SB_.LNKD')
DEVICE 15 intpin INTC# -> IRQ 11 (from '\_SB_.LNKA')
DEVICE 15 intpin INTD# -> IRQ 11 (from '\_SB_.LNKB')
DEVICE 16 intpin INTA# -> IRQ 11 (from '\_SB_.LNKD')
DEVICE 16 intpin INTB# -> IRQ 11 (from '\_SB_.LNKA')
DEVICE 16 intpin INTC# -> IRQ 11 (from '\_SB_.LNKB')
DEVICE 16 intpin INTD# -> IRQ 11 (from '\_SB_.LNKC')
DEVICE 17 intpin INTA# -> IRQ 11 (from '\_SB_.LNKA')
DEVICE 17 intpin INTB# -> IRQ 11 (from '\_SB_.LNKB')
DEVICE 17 intpin INTC# -> IRQ 11 (from '\_SB_.LNKC')
DEVICE 17 intpin INTD# -> IRQ 11 (from '\_SB_.LNKD')
DEVICE 18 intpin INTA# -> IRQ 11 (from '\_SB_.LNKB')
DEVICE 18 intpin INTB# -> IRQ 11 (from '\_SB_.LNKC')
DEVICE 18 intpin INTC# -> IRQ 11 (from '\_SB_.LNKD')
DEVICE 18 intpin INTD# -> IRQ 11 (from '\_SB_.LNKA')
DEVICE 19 intpin INTA# -> IRQ 11 (from '\_SB_.LNKC')
DEVICE 19 intpin INTB# -> IRQ 11 (from '\_SB_.LNKD')
DEVICE 19 intpin INTC# -> IRQ 11 (from '\_SB_.LNKA')
DEVICE 19 intpin INTD# -> IRQ 11 (from '\_SB_.LNKB')
DEVICE 20 intpin INTA# -> IRQ 11 (from '\_SB_.LNKD')
DEVICE 20 intpin INTB# -> IRQ 11 (from '\_SB_.LNKA')
DEVICE 20 intpin INTC# -> IRQ 11 (from '\_SB_.LNKB')
DEVICE 20 intpin INTD# -> IRQ 11 (from '\_SB_.LNKC')
DEVICE 21 intpin INTA# -> IRQ 11 (from '\_SB_.LNKA')
DEVICE 21 intpin INTB# -> IRQ 11 (from '\_SB_.LNKB')
DEVICE 21 intpin INTC# -> IRQ 11 (from '\_SB_.LNKC')
DEVICE 21 intpin INTD# -> IRQ 11 (from '\_SB_.LNKD')
DEVICE 22 intpin INTA# -> IRQ 11 (from '\_SB_.LNKB')
DEVICE 22 intpin INTB# -> IRQ 11 (from '\_SB_.LNKC')
DEVICE 22 intpin INTC# -> IRQ 11 (from '\_SB_.LNKD')
DEVICE 22 intpin INTD# -> IRQ 11 (from '\_SB_.LNKA')
DEVICE 23 intpin INTA# -> IRQ 11 (from '\_SB_.LNKC')
DEVICE 23 intpin INTB# -> IRQ 11 (from '\_SB_.LNKD')
DEVICE 23 intpin INTC# -> IRQ 11 (from '\_SB_.LNKA')
DEVICE 23 intpin INTD# -> IRQ 11 (from '\_SB_.LNKB')
DEVICE 24 intpin INTA# -> IRQ 11 (from '\_SB_.LNKD')
DEVICE 24 intpin INTB# -> IRQ 11 (from '\_SB_.LNKA')
DEVICE 24 intpin INTC# -> IRQ 11 (from '\_SB_.LNKB')
DEVICE 24 intpin INTD# -> IRQ 11 (from '\_SB_.LNKC')
DEVICE 25 intpin INTA# -> IRQ 11 (from '\_SB_.LNKA')
DEVICE 25 intpin INTB# -> IRQ 11 (from '\_SB_.LNKB')
DEVICE 25 intpin INTC# -> IRQ 11 (from '\_SB_.LNKC')
DEVICE 25 intpin INTD# -> IRQ 11 (from '\_SB_.LNKD')
DEVICE 26 intpin INTA# -> IRQ 11 (from '\_SB_.LNKB')
DEVICE 26 intpin INTB# -> IRQ 11 (from '\_SB_.LNKC')
DEVICE 26 intpin INTC# -> IRQ 11 (from '\_SB_.LNKD')
DEVICE 26 intpin INTD# -> IRQ 11 (from '\_SB_.LNKA')
DEVICE 27 intpin INTA# -> IRQ 11 (from '\_SB_.LNKC')
DEVICE 27 intpin INTB# -> IRQ 11 (from '\_SB_.LNKD')
DEVICE 27 intpin INTC# -> IRQ 11 (from '\_SB_.LNKA')
DEVICE 27 intpin INTD# -> IRQ 11 (from '\_SB_.LNKB')
DEVICE 28 intpin INTA# -> IRQ 11 (from '\_SB_.LNKD')
DEVICE 28 intpin INTB# -> IRQ 11 (from '\_SB_.LNKA')
DEVICE 28 intpin INTC# -> IRQ 11 (from '\_SB_.LNKB')
DEVICE 28 intpin INTD# -> IRQ 11 (from '\_SB_.LNKC')
DEVICE 29 intpin INTA# -> IRQ 11 (from '\_SB_.LNKA')
DEVICE 29 intpin INTB# -> IRQ 11 (from '\_SB_.LNKB')
DEVICE 29 intpin INTC# -> IRQ 11 (from '\_SB_.LNKC')
DEVICE 29 intpin INTD# -> IRQ 11 (from '\_SB_.LNKD')
DEVICE 30 intpin INTA# -> IRQ 11 (from '\_SB_.LNKB')
DEVICE 30 intpin INTB# -> IRQ 11 (from '\_SB_.LNKC')
DEVICE 30 intpin INTC# -> IRQ 11 (from '\_SB_.LNKD')
DEVICE 30 intpin INTD# -> IRQ 11 (from '\_SB_.LNKA')
DEVICE 31 intpin INTA# -> IRQ 11 (from '\_SB_.LNKC')
DEVICE 31 intpin INTB# -> IRQ 11 (from '\_SB_.LNKD')
DEVICE 31 intpin INTC# -> IRQ 11 (from '\_SB_.LNKA')
DEVICE 31 intpin INTD# -> IRQ 11 (from '\_SB_.LNKB')
I get the same result in VirtualBox (all pins of all devices mapped to IRQ 11).

Is this some subtle bug I'm not noticing or does your OS show the same thing?

Thanks.

Re: Bochs+VirtualBox connect all PCI devices to IRQ 11?

Posted: Tue Sep 29, 2015 2:08 am
by Combuster
I also don't really see a sane reason why you'd see ABCD pins listed for one device and BCDA for the next... Maybe they're correlated.

Re: Bochs+VirtualBox connect all PCI devices to IRQ 11?

Posted: Tue Sep 29, 2015 2:59 am
by mariuszp
It is actually listing ABCD for all devices, but for some reason, some show as for example INTA# connected to "LINKB" which makes no sense.

Re: Bochs+VirtualBox connect all PCI devices to IRQ 11?

Posted: Tue Sep 29, 2015 3:43 am
by Nable
mariuszp wrote:It is actually listing ABCD for all devices, but for some reason, some show as for example INTA# connected to "LINKB" which makes no sense.
It _does_ make sense for real hardware: most devices use only INTA#, so in attempt to avoid PCI IRQ sharing on most motherboards slots' INTx# pins are connected to interrupt links in interleaved order (e.g. ABCD for the first slot, BCDA for the second one, CDAB and so on).

Re: Bochs+VirtualBox connect all PCI devices to IRQ 11?

Posted: Tue Sep 29, 2015 6:43 am
by jnc100
I don't use bochs or ACPICA but rather VirtualBox and my own ACPI code. Having said that, I can confirm that on VirtualBox (in PIC mode rather than APIC mode), PIRQA# is 0x0b, PIRQB# is 0x09, PIRQC# is 0x0b and PIRQD# is 0x09. These can be confirmed by looking at the PCI configuration space in the vbox debugger (info pci verbose), looking for bus 0, device 0x1f, function 0, offsets 0x60-0x64 (these addresses are directly referenced in the AML for vbox).

Your code seems to be doing the correct thing given my unfamiliarity with ACPICA. The only thing I can see which looks a little strange is
mariuszp wrote:

Code: Select all

               if (res->Type == ACPI_RESOURCE_TYPE_IRQ)
               {
                  devIRQ = res->Data.Irq.Interrupts[table->SourceIndex];
                  break;
               };
The _CRS object on a LNK device should only return a single IRQ, rather than an array, and you shouldn't be indexing it by table->SourceIndex. Try devIRQ=res->Data.Irq.Interrupts[0] and see what that returns.

Regards,
John.

Re: Bochs+VirtualBox connect all PCI devices to IRQ 11?

Posted: Tue Sep 29, 2015 7:02 am
by mariuszp
The SourceIndex is always zero anyway, for the devices I'm getting. The ACPICA reference says SourceIndex is the index of the interrupt on the given source (the link device in this case). Perhaps it's not the "Interrupts" array that I'm supposed to read? There is no explanation for those fields anyway at all, strangely.

Re: Bochs+VirtualBox connect all PCI devices to IRQ 11?

Posted: Tue Sep 29, 2015 9:58 am
by jnc100
Okay, yes, the ACPI_RESOURCE_IRQ structure contains a 1 member array called Interrupts.

What exactly are your emulator settings (e.g. in VirtualBox which chipset etc?). Have you tried simply running AcpiEvaluateObject(NULL, "\_SB_.LNKA/B/C/D._CRS", NULL, &buf); and seeing what that returns? For reference, I get the following when doing that:

Code: Select all

[125707739544] acpipc: \_SB_.LNKA -> ACPIInterrupt (IRQ 11) -> IOAPIC 2 pin 11 gsi 11
[125786888917] acpipc: \_SB_.LNKB -> ACPIInterrupt (IRQ 9) -> IOAPIC 2 pin 9 gsi 9
[126028303792] acpipc: \_SB_.LNKC -> ACPIInterrupt (IRQ 11) -> IOAPIC 2 pin 11 gsi 11
[126247636219] acpipc: \_SB_.LNKD -> ACPIInterrupt (IRQ 9) -> IOAPIC 2 pin 9 gsi 9
Finally, as noted previously, accessing the LNK objects reads the IRQ values from PCI configuration space. You may want to insert debug statements in your AcpiOsReadPciConfiguration function to dump exactly what it is reading from and the value you return.

Regards,
John.

Re: Bochs+VirtualBox connect all PCI devices to IRQ 11?

Posted: Tue Sep 29, 2015 2:13 pm
by mariuszp
My implementation of AcpiOsReadPciConfiguration is as follows:

Code: Select all

ACPI_STATUS AcpiOsReadPciConfiguration(ACPI_PCI_ID *id, UINT32 reg, UINT64 *value, UINT32 width)
{
	uint32_t addr;
	uint32_t lbus = (uint32_t) id->Bus;
	uint32_t lslot = (uint32_t) id->Device;
	uint32_t lfunc = (uint32_t) id->Function;
	addr = (lbus << 16) | (lslot << 11) | (lfunc << 8) | (1 << 31) | reg;

	uint32_t *put = (uint32_t*) value;
	int count = 1;
	if (width == 64) count = 2;

	while (count--)
	{
		outd(PCI_CONFIG_ADDR, addr);
		*put++ = ind(PCI_CONFIG_DATA);
		addr += 4;
	};
	
	UINT64 mask;
	switch (width)
	{
	case 8:
		mask = 0xFF;
		break;
	case 16:
		mask = 0xFFFF;
		break;
	case 32:
		mask = 0xFFFFFFFF;
		break;
	case 64:
		mask = 0xFFFFFFFFFFFFFFFF;
		break;
	};
	
	*value &= mask;
	return AE_OK;
};
So basically, I read a double-word for widths 8, 16, and 32, and 2 double-words for a width of 64, then masking out the appropriate subset of the value.

PCI_CONFIG_ADDR and PCI_CONFIG_DATA are definitely the right I/O port as another function uses them to read the entire configuration space of a device:

Code: Select all

void pciGetDeviceConfig(uint8_t bus, uint8_t slot, uint8_t func, PCIDeviceConfig *config)
{
	spinlockAcquire(&pciLock);
	uint32_t addr = 0;
	uint32_t lbus = (uint32_t) bus;
	uint32_t lslot = (uint32_t) slot;
	uint32_t lfunc = (uint32_t) func;
	addr = (lbus << 16) | (lslot << 11) | (lfunc << 8) | (1 << 31);

	uint32_t *put = (uint32_t*) config;
	int count = 16;

	while (count--)
	{
		outd(PCI_CONFIG_ADDR, addr);
		*put++ = ind(PCI_CONFIG_DATA);
		addr += 4;
	};
	spinlockRelease(&pciLock);
};
And this definitely works as it is used to extract BARs etc by drivers.

Am I missing something here?

Re: Bochs+VirtualBox connect all PCI devices to IRQ 11?

Posted: Tue Sep 29, 2015 3:07 pm
by jnc100
The bottom two bits of the register number are ignored by PCI. In other words, all reads must be 32 bits in length and aligned on a 32 bit boundary. The AML code in VirtualBox issues requests to read single bytes from offsets 0x60, 0x61, 0x62 and 0x63 to determine the IRQ routing. What your code is doing is issuing 32 bit reads to the appropriate addresses and then masking out the top 24 bits. Unfortunately, the PCI host is ignoring the bottom two bits of the address, thus all reads are coming from offset 0x60 (which is where PIRQA#'s IRQ is stored, namely 11).

You need something like:

Code: Select all

uint32_t aligned_offset = reg & 0xfffc;
int offset_shift = (int)(aligned_offset - reg) * 8;
reg = aligned_offset;

// calculate addr as before

outd(PCI_CONFIG_ADDR, addr);
uint32_t align_val = ind(PCI_CONFIG_DATA);

uint32_t mask = 0xff;    // assumes byte access, use a switch for 16/32 bits
uint32_t ret = (align_val >> offset_shift) & mask;
return ret
Note that I've not seen any implementation that tries to do 64 bit reads from PCI configuration space.

Regards,
John.

Re: Bochs+VirtualBox connect all PCI devices to IRQ 11?

Posted: Tue Sep 29, 2015 3:35 pm
by mariuszp
jnc100 wrote:The bottom two bits of the register number are ignored by PCI. In other words, all reads must be 32 bits in length and aligned on a 32 bit boundary. The AML code in VirtualBox issues requests to read single bytes from offsets 0x60, 0x61, 0x62 and 0x63 to determine the IRQ routing. What your code is doing is issuing 32 bit reads to the appropriate addresses and then masking out the top 24 bits. Unfortunately, the PCI host is ignoring the bottom two bits of the address, thus all reads are coming from offset 0x60 (which is where PIRQA#'s IRQ is stored, namely 11).

You need something like:

Code: Select all

uint32_t aligned_offset = reg & 0xfffc;
int offset_shift = (int)(aligned_offset - reg) * 8;
reg = aligned_offset;

// calculate addr as before

outd(PCI_CONFIG_ADDR, addr);
uint32_t align_val = ind(PCI_CONFIG_DATA);

uint32_t mask = 0xff;    // assumes byte access, use a switch for 16/32 bits
uint32_t ret = (align_val >> offset_shift) & mask;
return ret
Note that I've not seen any implementation that tries to do 64 bit reads from PCI configuration space.

Regards,
John.
Ah! I have fixed the alignment and the interrupt wiring is now correctly detected, thank you.