[SOLVED] Issues configuring BARs

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.
Post Reply
tristanseifert
Posts: 10
Joined: Mon Oct 14, 2013 3:53 pm

[SOLVED] Issues configuring BARs

Post by tristanseifert »

Hello all,

I'm working on writing a driver for the Intel PIIX3 IDE interface. I've got it matched on the PCI bus and my driver is loaded, and it seems to properly identify the device and whatnot. The problem comes when I try to set the BAR addresses for the IO addresses. The code doesn't seem to properly make the write go through, as doing "info pci" in the QEMU monitor doesn't yield any changes, even on a BAR that's already been set. This is the code I use to do this:

Code: Select all

#define pci_config_address(bus, device, function, reg) ((uint32_t) ((bus & 0xFF) << 16) | ((device & 0x1F) << 11) | ((function & 0x07) << 8) | (reg & 0xFF) | 0x80000000)

void pci_config_write_l(uint32_t address, uint32_t value) {
	io_outl(0xCF8, address & 0xFFFFFFFC);
	io_outl(0xCFC, value);
}

/*
 * Updates the BAR of a specific function on a device, and updates the PCI
 * config space.
 */
void pci_device_update_bar(pci_device_t *d, int function, int bar, uint32_t value) {
	ASSERT(bar < 6);

	// Calculate length and address to write to
	uint32_t length = d->function[function].bar[bar].end - d->function[function].bar[bar].start;
	uint32_t addr = pci_config_address(d->location.bus, d->location.device, function, (0x10 + (bar << 2)));

	kprintf("PCI BAR UPDATE: bus %u, device %u, function %u, BAR %X, value 0x%X, address 0x%X\n", d->location.bus, d->location.device, function, (0x10 + (bar << 2)), value, addr);

	// Do write to PCI config space
	pci_config_write_l(addr, value);

	// Reset flags
	d->function[function].bar[bar].flags = 0;

	// IO BAR
	if(bar & 0x01) {
		d->function[function].bar[bar].start = value & 0xFFFFFFFC;
		d->function[function].bar[bar].end = (value & 0xFFFFFFFC) + length;
		d->function[function].bar[bar].flags = kPCIBARFlagsIOAddress;
	} else { // Memory address BAR
		d->function[function].bar[bar].start = value & 0xFFFFFFF0;
		d->function[function].bar[bar].end = (value & 0xFFFFFFF0) + length;

		// Is this BAR prefetchable?
		if(value & 0x8) {
			d->function[function].bar[bar].flags = kPCIBARFlagsPrefetchable;
		}

		// Get BAR type
		uint8_t type = (value & 0x6) >> 1;

		// Is the BAR 64 bits?
		if(type == 0x02) {
			d->function[function].bar[bar].flags |= kPCIBARFlags64Bits;
		} else if(type == 0x01) { // 16 bits?
			d->function[function].bar[bar].flags |= kPCIBARFlags16Bits;
		} else if(type == 0x00) { // 32 bits?
			d->function[function].bar[bar].flags |= kPCIBARFlags32Bits;
		}
	}
}
And this is the code I use to write the BARs, with all values being correct and expected:

Code: Select all

	// Command task file for primary is at 0x1F0
	pci_device_update_bar(piix3_device, 1, 0, (ATA_BUS_0_IOADDR | 0x0001));
	// Control task file for primary is at 0x1F8
	pci_device_update_bar(piix3_device, 1, 1, (ATA_BUS_0_CTRLADDR | 0x0001));

	// Command task file for secondary is at 0x170
	pci_device_update_bar(piix3_device, 1, 2, (ATA_BUS_1_IOADDR | 0x0001));
	// Control task file for secondary is at 0x178
	pci_device_update_bar(piix3_device, 1, 3, (ATA_BUS_1_CTRLADDR | 0x0001));
Does anybody know why this might be happening? Thanks for any pointers.
Last edited by tristanseifert on Sun Feb 23, 2014 3:23 pm, edited 1 time in total.
User avatar
thepowersgang
Member
Member
Posts: 734
Joined: Tue Dec 25, 2007 6:03 am
Libera.chat IRC: thePowersGang
Location: Perth, Western Australia
Contact:

Re: Issues configuring BARs

Post by thepowersgang »

First off, may I ask why you're attempting to set a BAR on x86? The PCI bios has already set semi-sensible values for you, so you should only need to read.

A quick look a the code seems sane-ish... but there are read-only bits in the BAR that you may need to take into account.
Kernel Development, It's the brain surgery of programming.
Acess2 OS (c) | Tifflin OS (rust) | mrustc - Rust compiler
Currently Working on: mrustc
tristanseifert
Posts: 10
Joined: Mon Oct 14, 2013 3:53 pm

Re: Issues configuring BARs

Post by tristanseifert »

According to the documentation of the PIIX3, I'm supposed to configure these BARs for proper operation, as when I read them out after the system boots, they're set to 0. I noticed I had skipped a few bits in the initialisation sequence. It does work now, but QEMU doesn't show the updated BARs.

Is there some specific way I need to allocate memory ranges for PCI devices, or can I just use my kernel allocator, and set the physical address in the BAR?
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Re: Issues configuring BARs

Post by Combuster »

As said, PCI addresses are configured by the BIOS on boot. It is not your task. This is also why emulators are not likely to honour your configuration requests.

And for IDE controllers, a zeroed BAR implies the legacy address.
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
tristanseifert
Posts: 10
Joined: Mon Oct 14, 2013 3:53 pm

Re: Issues configuring BARs

Post by tristanseifert »

Combuster wrote:As said, PCI addresses are configured by the BIOS on boot. It is not your task. This is also why emulators are not likely to honour your configuration requests.

And for IDE controllers, a zeroed BAR implies the legacy address.
That makes a lot more sense — the ATA driver does work now and as does reading from the disks. Thanks for pointing out my stupidity there!
Gigasoft
Member
Member
Posts: 856
Joined: Sat Nov 21, 2009 5:11 pm

Re: [SOLVED] Issues configuring BARs

Post by Gigasoft »

When an IDE controller is in legacy mode, the BARs are unused and set to zero. If you set it to native mode, you must configure the BARs.
Is there some specific way I need to allocate memory ranges for PCI devices, or can I just use my kernel allocator, and set the physical address in the BAR?
Memory ranges must not overlap any range reported in the BIOS memory map, or the A0000-EFFFF range, or any range assigned to another PCI device. I/O port ranges must not conflict with standard ports or any other PCI device, or any port range that the user has manually specified as in use. The required alignment is determined by writing all ones in the address portion of the BAR and reading it back.
Post Reply