All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] add PCI ROMs to sysfs
@ 2004-07-30 21:09 Jesse Barnes
  2004-07-30 21:29 ` Greg KH
  0 siblings, 1 reply; 110+ messages in thread
From: Jesse Barnes @ 2004-07-30 21:09 UTC (permalink / raw)
  To: linux-kernel, linux-pci, Jon Smirl

[-- Attachment #1: Type: text/plain, Size: 918 bytes --]

Per a big thread earlier today, this patch adds a 'rom' file to sysfs in the 
same place where we currently have 'config', 'irq', etc.

Note that the ROM is *not* cached, so userspace will have to save the ROM when 
a device is first detected if the card doesn't support address cycles to ROM 
space at the same time as other memory accesses.  (I'm still open to changing 
this if people feel strongly, but as it stands, the 'rom' file is only 
slightly more dangerous than the 'config' file.)

Jon, am I missing something or is it possible for us to cache the ROM in 
userspace when it receives the hotplug event?  I saw your DRM code, and for 
the case of ROMs at a nonstandard address, we can fixup the address for 
pci_dev->resource[PCI_ROM_RESOURCE] in pci-quirks, can't we?

Thoughts?  I've tried to add cleanup code, but I'm not sure how acceptable it 
is and I don't have any way of testing it.

Thanks,
Jesse

[-- Attachment #2: pci-sysfs-rom-4.patch --]
[-- Type: text/plain, Size: 4456 bytes --]

===== drivers/pci/pci-sysfs.c 1.10 vs edited =====
--- 1.10/drivers/pci/pci-sysfs.c	2004-06-04 06:23:04 -07:00
+++ edited/drivers/pci/pci-sysfs.c	2004-07-30 14:04:40 -07:00
@@ -164,6 +164,86 @@
 	return count;
 }
 
+/**
+ * pci_enable_rom - enable ROM decoding for a PCI device
+ * @dev: PCI device to enable
+ *
+ * Enable ROM decoding on @dev.  This involves simply turning on the last
+ * bit of the PCI ROM BAR.  Note that some cards may share address decoders
+ * between the ROM and other resources, so enabling it may disable access
+ * to MMIO registers or other card memory.
+ */
+static void
+pci_enable_rom(struct pci_dev *dev)
+{
+	u32 rom_addr;
+
+	pci_read_config_dword(dev, PCI_ROM_ADDRESS, &rom_addr);
+	rom_addr |= PCI_ROM_ADDRESS_ENABLE;
+	pci_write_config_dword(dev, PCI_ROM_ADDRESS, rom_addr);
+}
+
+/**
+ * pci_disable_rom - disable ROM decoding for a PCI device
+ * @dev: PCI device to disable
+ *
+ * Disable ROM decoding on a PCI device by turning off the last bit in the
+ * ROM BAR.
+ */
+static void
+pci_disable_rom(struct pci_dev *dev)
+{
+	u32 rom_addr;
+
+	pci_read_config_dword(dev, PCI_ROM_ADDRESS, &rom_addr);
+	rom_addr &= ~PCI_ROM_ADDRESS_ENABLE;
+	pci_write_config_dword(dev, PCI_ROM_ADDRESS, rom_addr);
+}
+
+/**
+ * pci_read_rom - read a PCI ROM
+ * @kobj: kernel object handle
+ * @buf: where to put the data we read from the ROM
+ * @off: file offset
+ * @count: number of bytes to read
+ *
+ * Put @count bytes starting at @off into @buf from the ROM in the PCI
+ * device corresponding to @kobj.
+ */
+static ssize_t
+pci_read_rom(struct kobject *kobj, char *buf, loff_t off, size_t count)
+{
+	struct pci_dev *dev = to_pci_dev(container_of(kobj,struct device,kobj));
+	loff_t init_off = off;
+	unsigned long start = pci_resource_start(dev, PCI_ROM_RESOURCE);
+	int size = pci_resource_len(dev, PCI_ROM_RESOURCE);
+
+	if (off > size)
+		return 0;
+	if (off + count > size) {
+		size -= off;
+		count = size;
+	} else {
+		size = count;
+	}
+
+	/* Enable ROM space decodes and do the reads */
+	pci_enable_rom(dev);
+
+	while (size > 0) {
+		unsigned char val;
+		val = readb(start + off);
+		buf[off - init_off] = val;
+		off++;
+		--size;
+	}
+
+	/* Disable again before continuing */
+	pci_disable_rom(dev);
+
+	return count;
+}
+
 static struct bin_attribute pci_config_attr = {
 	.attr =	{
 		.name = "config",
@@ -193,6 +273,39 @@
 	else
 		sysfs_create_bin_file(&pdev->dev.kobj, &pcie_config_attr);
 
+	/* If the device has a ROM, try to expose it in sysfs. */
+	if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) {
+		struct bin_attribute *rom_attr;
+		rom_attr = kmalloc(sizeof(*rom_attr), GFP_ATOMIC);
+
+		if (!rom_attr)
+			goto out;
+
+		pdev->rom_attr = rom_attr;
+		rom_attr->attr.name = "rom";
+		rom_attr->attr.mode = S_IRUSR;
+		rom_attr->attr.owner = THIS_MODULE;
+		rom_attr->read = pci_read_rom;
+		rom_attr->size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
+		sysfs_create_bin_file(&pdev->dev.kobj, rom_attr);
+	}
+ out:
 	/* add platform-specific attributes */
 	pcibios_add_platform_entries(pdev);
+}
+
+/**
+ * pci_remove_sysfs_dev_files - cleanup PCI specific sysfs files
+ * @pdev: device whose entries we should free
+ *
+ * Cleanup when @pdev is removed from sysfs.
+ */
+void pci_remove_sysfs_dev_files(struct pci_dev *pdev)
+{
+	/* Don't need to free config entries since they're static & global */
+
+	if (pci_resource_len(pdev, PCI_ROM_RESOURCE) && pdev->rom_attr) {
+		sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr);
+		kfree(pdev->rom_attr);
+	}
 }
===== drivers/pci/remove.c 1.3 vs edited =====
--- 1.3/drivers/pci/remove.c	2004-02-03 09:17:30 -08:00
+++ edited/drivers/pci/remove.c	2004-07-30 13:35:18 -07:00
@@ -26,6 +26,7 @@
 static void pci_destroy_dev(struct pci_dev *dev)
 {
 	pci_proc_detach_device(dev);
+	pci_remove_sysfs_dev_files(dev);
 	device_unregister(&dev->dev);
 
 	/* Remove the device from the device lists, and prevent any further
===== include/linux/pci.h 1.130 vs edited =====
--- 1.130/include/linux/pci.h	2004-06-30 11:21:27 -07:00
+++ edited/include/linux/pci.h	2004-07-30 13:37:09 -07:00
@@ -537,6 +537,7 @@
 	unsigned int	is_busmaster:1; /* device is busmaster */
 	
 	unsigned int 	saved_config_space[16]; /* config space saved at suspend time */
+	struct bin_attribute *rom_attr; /* ROM attribute (if ROM is present) */
 #ifdef CONFIG_PCI_NAMES
 #define PCI_NAME_SIZE	96
 #define PCI_NAME_HALF	__stringify(43)	/* less than half to handle slop */

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-07-30 21:09 [PATCH] add PCI ROMs to sysfs Jesse Barnes
@ 2004-07-30 21:29 ` Greg KH
  2004-07-30 21:34   ` Jesse Barnes
  2004-07-30 21:53   ` Jon Smirl
  0 siblings, 2 replies; 110+ messages in thread
From: Greg KH @ 2004-07-30 21:29 UTC (permalink / raw)
  To: Jesse Barnes; +Cc: linux-kernel, linux-pci, Jon Smirl

On Fri, Jul 30, 2004 at 02:09:05PM -0700, Jesse Barnes wrote:
> 
> Thoughts?  I've tried to add cleanup code, but I'm not sure how acceptable it 
> is and I don't have any way of testing it.

You don't have access to a cardbus machine?  Or how about using the
fakephp driver to "remove" pci devices?  You should be able to test this
code path with either one of those methods...

> +void pci_remove_sysfs_dev_files(struct pci_dev *pdev)
> +{
> +	/* Don't need to free config entries since they're static & global */

What do you mean by this?  You should still remove all files we added in
the pci_create_sysfs_dev_files() function here, not just the rom file.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-07-30 21:29 ` Greg KH
@ 2004-07-30 21:34   ` Jesse Barnes
  2004-07-30 21:39     ` Greg KH
                       ` (2 more replies)
  2004-07-30 21:53   ` Jon Smirl
  1 sibling, 3 replies; 110+ messages in thread
From: Jesse Barnes @ 2004-07-30 21:34 UTC (permalink / raw)
  To: Greg KH; +Cc: linux-kernel, linux-pci, Jon Smirl

On Friday, July 30, 2004 2:29 pm, Greg KH wrote:
> On Fri, Jul 30, 2004 at 02:09:05PM -0700, Jesse Barnes wrote:
> > Thoughts?  I've tried to add cleanup code, but I'm not sure how
> > acceptable it is and I don't have any way of testing it.
>
> You don't have access to a cardbus machine?  Or how about using the
> fakephp driver to "remove" pci devices?  You should be able to test this
> code path with either one of those methods...

I've never tried fakephp, I'll look into it.  And I do have a laptop at home 
that I could mess around on too, though I don't think I have any devices with 
ROMs...

> > +void pci_remove_sysfs_dev_files(struct pci_dev *pdev)
> > +{
> > +	/* Don't need to free config entries since they're static & global */
>
> What do you mean by this?  You should still remove all files we added in
> the pci_create_sysfs_dev_files() function here, not just the rom file.

What will happen if we don't?  They didn't used to be removed, and there's 
only one global copy afaict.  I'll add code to kill them at any rate.

Thanks,
Jesse

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-07-30 21:34   ` Jesse Barnes
@ 2004-07-30 21:39     ` Greg KH
  2004-07-30 21:48     ` Jesse Barnes
  2004-07-30 22:15     ` Jon Smirl
  2 siblings, 0 replies; 110+ messages in thread
From: Greg KH @ 2004-07-30 21:39 UTC (permalink / raw)
  To: Jesse Barnes; +Cc: linux-kernel, linux-pci, Jon Smirl

On Fri, Jul 30, 2004 at 02:34:50PM -0700, Jesse Barnes wrote:
> > > +void pci_remove_sysfs_dev_files(struct pci_dev *pdev)
> > > +{
> > > +	/* Don't need to free config entries since they're static & global */
> >
> > What do you mean by this?  You should still remove all files we added in
> > the pci_create_sysfs_dev_files() function here, not just the rom file.
> 
> What will happen if we don't?

Then the driver core will clean them up, like it does today :)

But it's "not nice", and we should clean them up as we now have a
function in which we can do that, and based on changes we have planned
for the driver core in the future, it will be something that is required
to do later on.  Might as well do it now, as you are modifying the same
code...

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-07-30 21:34   ` Jesse Barnes
  2004-07-30 21:39     ` Greg KH
@ 2004-07-30 21:48     ` Jesse Barnes
  2004-07-30 22:15     ` Jon Smirl
  2 siblings, 0 replies; 110+ messages in thread
From: Jesse Barnes @ 2004-07-30 21:48 UTC (permalink / raw)
  To: linux-pci; +Cc: Greg KH, linux-kernel, Jon Smirl

[-- Attachment #1: Type: text/plain, Size: 815 bytes --]

On Friday, July 30, 2004 2:34 pm, Jesse Barnes wrote:
> On Friday, July 30, 2004 2:29 pm, Greg KH wrote:
> > On Fri, Jul 30, 2004 at 02:09:05PM -0700, Jesse Barnes wrote:
> > > Thoughts?  I've tried to add cleanup code, but I'm not sure how
> > > acceptable it is and I don't have any way of testing it.
> >
> > You don't have access to a cardbus machine?  Or how about using the
> > fakephp driver to "remove" pci devices?  You should be able to test this
> > code path with either one of those methods...
>
> I've never tried fakephp, I'll look into it.  And I do have a laptop at
> home that I could mess around on too, though I don't think I have any
> devices with ROMs...

Ok, I tested it with fakephp and things seem to work ok (no panics, all files 
are removed).

Does it look ok otherwise?

Thanks,
Jesse

[-- Attachment #2: pci-sysfs-rom-6.patch --]
[-- Type: text/plain, Size: 4569 bytes --]

===== drivers/pci/pci-sysfs.c 1.10 vs edited =====
--- 1.10/drivers/pci/pci-sysfs.c	2004-06-04 06:23:04 -07:00
+++ edited/drivers/pci/pci-sysfs.c	2004-07-30 14:37:04 -07:00
@@ -164,6 +164,86 @@
 	return count;
 }
 
+/**
+ * pci_enable_rom - enable ROM decoding for a PCI device
+ * @dev: PCI device to enable
+ *
+ * Enable ROM decoding on @dev.  This involves simply turning on the last
+ * bit of the PCI ROM BAR.  Note that some cards may share address decoders
+ * between the ROM and other resources, so enabling it may disable access
+ * to MMIO registers or other card memory.
+ */
+static void
+pci_enable_rom(struct pci_dev *dev)
+{
+	u32 rom_addr;
+
+	pci_read_config_dword(dev, PCI_ROM_ADDRESS, &rom_addr);
+	rom_addr |= PCI_ROM_ADDRESS_ENABLE;
+	pci_write_config_dword(dev, PCI_ROM_ADDRESS, rom_addr);
+}
+
+/**
+ * pci_disable_rom - disable ROM decoding for a PCI device
+ * @dev: PCI device to disable
+ *
+ * Disable ROM decoding on a PCI device by turning off the last bit in the
+ * ROM BAR.
+ */
+static void
+pci_disable_rom(struct pci_dev *dev)
+{
+	u32 rom_addr;
+
+	pci_read_config_dword(dev, PCI_ROM_ADDRESS, &rom_addr);
+	rom_addr &= ~PCI_ROM_ADDRESS_ENABLE;
+	pci_write_config_dword(dev, PCI_ROM_ADDRESS, rom_addr);
+}
+
+/**
+ * pci_read_rom - read a PCI ROM
+ * @kobj: kernel object handle
+ * @buf: where to put the data we read from the ROM
+ * @off: file offset
+ * @count: number of bytes to read
+ *
+ * Put @count bytes starting at @off into @buf from the ROM in the PCI
+ * device corresponding to @kobj.
+ */
+static ssize_t
+pci_read_rom(struct kobject *kobj, char *buf, loff_t off, size_t count)
+{
+	struct pci_dev *dev = to_pci_dev(container_of(kobj,struct device,kobj));
+	loff_t init_off = off;
+	unsigned long start = pci_resource_start(dev, PCI_ROM_RESOURCE);
+	int size = pci_resource_len(dev, PCI_ROM_RESOURCE);
+
+	if (off > size)
+		return 0;
+	if (off + count > size) {
+		size -= off;
+		count = size;
+	} else {
+		size = count;
+	}
+
+	/* Enable ROM space decodes and do the reads */
+	pci_enable_rom(dev);
+
+	while (size > 0) {
+		unsigned char val;
+		val = readb(start + off);
+		buf[off - init_off] = val;
+		off++;
+		--size;
+	}
+
+	/* Disable again before continuing */
+	pci_disable_rom(dev);
+
+	return count;
+}
+
 static struct bin_attribute pci_config_attr = {
 	.attr =	{
 		.name = "config",
@@ -193,6 +273,43 @@
 	else
 		sysfs_create_bin_file(&pdev->dev.kobj, &pcie_config_attr);
 
+	/* If the device has a ROM, try to expose it in sysfs. */
+	if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) {
+		struct bin_attribute *rom_attr;
+		rom_attr = kmalloc(sizeof(*rom_attr), GFP_ATOMIC);
+
+		pdev->rom_attr = NULL;
+		if (!rom_attr)
+			goto out;
+
+		pdev->rom_attr = rom_attr;
+		rom_attr->attr.name = "rom";
+		rom_attr->attr.mode = S_IRUSR;
+		rom_attr->attr.owner = THIS_MODULE;
+		rom_attr->read = pci_read_rom;
+		rom_attr->size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
+		sysfs_create_bin_file(&pdev->dev.kobj, rom_attr);
+	}
+ out:
 	/* add platform-specific attributes */
 	pcibios_add_platform_entries(pdev);
+}
+
+/**
+ * pci_remove_sysfs_dev_files - cleanup PCI specific sysfs files
+ * @pdev: device whose entries we should free
+ *
+ * Cleanup when @pdev is removed from sysfs.
+ */
+void pci_remove_sysfs_dev_files(struct pci_dev *pdev)
+{
+	if (pdev->cfg_size < 4096)
+		sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr);
+	else
+		sysfs_remove_bin_file(&pdev->dev.kobj, &pcie_config_attr);
+
+	if (pci_resource_len(pdev, PCI_ROM_RESOURCE) && pdev->rom_attr) {
+		sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr);
+		kfree(pdev->rom_attr);
+	}
 }
===== drivers/pci/remove.c 1.3 vs edited =====
--- 1.3/drivers/pci/remove.c	2004-02-03 09:17:30 -08:00
+++ edited/drivers/pci/remove.c	2004-07-30 13:35:18 -07:00
@@ -26,6 +26,7 @@
 static void pci_destroy_dev(struct pci_dev *dev)
 {
 	pci_proc_detach_device(dev);
+	pci_remove_sysfs_dev_files(dev);
 	device_unregister(&dev->dev);
 
 	/* Remove the device from the device lists, and prevent any further
===== include/linux/pci.h 1.130 vs edited =====
--- 1.130/include/linux/pci.h	2004-06-30 11:21:27 -07:00
+++ edited/include/linux/pci.h	2004-07-30 13:37:09 -07:00
@@ -537,6 +537,7 @@
 	unsigned int	is_busmaster:1; /* device is busmaster */
 	
 	unsigned int 	saved_config_space[16]; /* config space saved at suspend time */
+	struct bin_attribute *rom_attr; /* ROM attribute (if ROM is present) */
 #ifdef CONFIG_PCI_NAMES
 #define PCI_NAME_SIZE	96
 #define PCI_NAME_HALF	__stringify(43)	/* less than half to handle slop */

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-07-30 21:29 ` Greg KH
  2004-07-30 21:34   ` Jesse Barnes
@ 2004-07-30 21:53   ` Jon Smirl
  2004-07-31 10:03     ` Vojtech Pavlik
  1 sibling, 1 reply; 110+ messages in thread
From: Jon Smirl @ 2004-07-30 21:53 UTC (permalink / raw)
  To: Greg KH, Jesse Barnes; +Cc: linux-kernel, linux-pci, Jon Smirl

Here's another grungy thing I needed to do to PCI. Multi-headed video
cards don't really implement independent PCI devices even though they
look like independent devices. I've heard that this behavior is needed
for MS Windows compatibility.

I'm missing a PCI call to claim ownership for the secondary device
without also causing a second instance of my driver to be loaded.
Here's the code I'm using, what's the right way to do this?

struct pci_dev *secondary;
/* check the next function on the same card */
secondary = pci_find_slot(dev->pdev->bus->number, dev->pdev->devfn +
1);
if (secondary) {
	/* check if class is secondary video */
	if (secondary->class == 0x038000) {
		DRM_DEBUG("registering secondary video head\n");
		/* This code is need to bind the driver to the secondary device */
		/* There is no direct pci call to do this, there should be one */
		secondary->dev.driver = dev->pdev->dev.driver;
		device_bind_driver(&secondary->dev);
		/* dev->pdev->driver is not filled until after probe completes */
		secondary->driver = to_pci_driver(dev->pdev->dev.driver);
		pci_dev_get(secondary);
	}
}

=====
Jon Smirl
jonsmirl@yahoo.com


		
__________________________________
Do you Yahoo!?
Yahoo! Mail - 50x more storage than other providers!
http://promotions.yahoo.com/new_mail

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-07-30 21:34   ` Jesse Barnes
  2004-07-30 21:39     ` Greg KH
  2004-07-30 21:48     ` Jesse Barnes
@ 2004-07-30 22:15     ` Jon Smirl
  2004-07-31 15:59       ` Jesse Barnes
  2 siblings, 1 reply; 110+ messages in thread
From: Jon Smirl @ 2004-07-30 22:15 UTC (permalink / raw)
  To: Jesse Barnes, Greg KH; +Cc: linux-kernel, linux-pci, Jon Smirl

--- Jesse Barnes <jbarnes@engr.sgi.com> wrote:
> Jon, am I missing something or is it possible for us to cache the ROM
> in userspace when it receives the hotplug event?  I saw your DRM
code,
> and for the case of ROMs at a nonstandard address, we can fixup the
> address for pci_dev->resource[PCI_ROM_RESOURCE] in pci-quirks, 
> can't we?

When I was done with the ROM I did this:
if (r->parent) {
	release_resource(r);
	r->flags &= ~PCI_ROM_ADDRESS_ENABLE;
	r->end -= r->start;
	r->start = 0;
}
/* This will disable and set address to unassigned */
pci_write_config_dword(dev->pdev, PCI_ROM_ADDRESS, 0);
	
Then when I accessed them I did this:

/* assign the ROM an address if it doesn't have one */
if (r->parent == NULL)
	pci_assign_resource(dev->pdev, PCI_ROM_RESOURCE);

I believe I needed to do this because Xfree was enabling the ROMs
without telling the kernel. This caused the kernel resource struct to
get out of sync with the actual state of the ROM. 

If you set pci_dev->resource[PCI_ROM_RESOURCE] to C000:0 won't this
mess up pci_assign_resource()/release_resource()?

I wrote this code a while ago so I'm forgetting exactly why I did
things. There is a variation on this code in drivers/video/aty/radeon_base.c

=====
Jon Smirl
jonsmirl@yahoo.com


	
		
__________________________________
Do you Yahoo!?
New and Improved Yahoo! Mail - 100MB free storage!
http://promotions.yahoo.com/new_mail 

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-07-30 21:53   ` Jon Smirl
@ 2004-07-31 10:03     ` Vojtech Pavlik
  2004-07-31 13:28       ` Jon Smirl
  0 siblings, 1 reply; 110+ messages in thread
From: Vojtech Pavlik @ 2004-07-31 10:03 UTC (permalink / raw)
  To: Jon Smirl; +Cc: Greg KH, Jesse Barnes, linux-kernel, linux-pci

On Fri, Jul 30, 2004 at 02:53:25PM -0700, Jon Smirl wrote:
> Here's another grungy thing I needed to do to PCI. Multi-headed video
> cards don't really implement independent PCI devices even though they
> look like independent devices. I've heard that this behavior is needed
> for MS Windows compatibility.
> 
> I'm missing a PCI call to claim ownership for the secondary device
> without also causing a second instance of my driver to be loaded.
> Here's the code I'm using, what's the right way to do this?
> 
> struct pci_dev *secondary;
> /* check the next function on the same card */
> secondary = pci_find_slot(dev->pdev->bus->number, dev->pdev->devfn +
> 1);
> if (secondary) {
> 	/* check if class is secondary video */
> 	if (secondary->class == 0x038000) {
> 		DRM_DEBUG("registering secondary video head\n");
> 		/* This code is need to bind the driver to the secondary device */
> 		/* There is no direct pci call to do this, there should be one */
> 		secondary->dev.driver = dev->pdev->dev.driver;
> 		device_bind_driver(&secondary->dev);
> 		/* dev->pdev->driver is not filled until after probe completes */
> 		secondary->driver = to_pci_driver(dev->pdev->dev.driver);
> 		pci_dev_get(secondary);
> 	}
> }

You can do that, but where is the problem with your probe function being
called twice - once for each of the devices? You should be able to sort
out which one is which rather easily.

-- 
Vojtech Pavlik
SuSE Labs, SuSE CR

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-07-31 10:03     ` Vojtech Pavlik
@ 2004-07-31 13:28       ` Jon Smirl
  2004-07-31 15:42         ` Greg KH
  0 siblings, 1 reply; 110+ messages in thread
From: Jon Smirl @ 2004-07-31 13:28 UTC (permalink / raw)
  To: Vojtech Pavlik; +Cc: Greg KH, Jesse Barnes, linux-kernel, linux-pci

--- Vojtech Pavlik <vojtech@suse.cz> wrote:
> On Fri, Jul 30, 2004 at 02:53:25PM -0700, Jon Smirl wrote:
> > Here's another grungy thing I needed to do to PCI. Multi-headed
> video
> 
> You can do that, but where is the problem with your probe function
> being
> called twice - once for each of the devices? You should be able to
> sort
> out which one is which rather easily.

I wrote this a while ago, but I believe the problem was that by
accepting two probes I get two hotplug ADD events that I can't tell
apart. This scheme avoids triggering hotplug. The other problem was
that the ADD events get started in parallel and I couldn't figure out
how to serialize them. The parallel hotplug programs got into a race to
see who could initialize the card first.

Now that I know more about hotplug I could modify the hotplug
parameters to indicate primary vs secondary and ignore the secondary
one. But this will still cause two apps to be started in parallel. 

Another solution would be to modify the kernel API somehow to let me
suppress hotplug ADD/REMOVE on the secondary device.

=====
Jon Smirl
jonsmirl@yahoo.com


		
__________________________________
Do you Yahoo!?
Yahoo! Mail Address AutoComplete - You start. We finish.
http://promotions.yahoo.com/new_mail 

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-07-31 13:28       ` Jon Smirl
@ 2004-07-31 15:42         ` Greg KH
  0 siblings, 0 replies; 110+ messages in thread
From: Greg KH @ 2004-07-31 15:42 UTC (permalink / raw)
  To: Jon Smirl; +Cc: Vojtech Pavlik, Jesse Barnes, linux-kernel, linux-pci

On Sat, Jul 31, 2004 at 06:28:28AM -0700, Jon Smirl wrote:
> --- Vojtech Pavlik <vojtech@suse.cz> wrote:
> > On Fri, Jul 30, 2004 at 02:53:25PM -0700, Jon Smirl wrote:
> > > Here's another grungy thing I needed to do to PCI. Multi-headed
> > video
> > 
> > You can do that, but where is the problem with your probe function
> > being
> > called twice - once for each of the devices? You should be able to
> > sort
> > out which one is which rather easily.
> 
> I wrote this a while ago, but I believe the problem was that by
> accepting two probes I get two hotplug ADD events that I can't tell
> apart. This scheme avoids triggering hotplug. The other problem was
> that the ADD events get started in parallel and I couldn't figure out
> how to serialize them. The parallel hotplug programs got into a race to
> see who could initialize the card first.
> 
> Now that I know more about hotplug I could modify the hotplug
> parameters to indicate primary vs secondary and ignore the secondary
> one. But this will still cause two apps to be started in parallel. 

But as it's easy to tell which one is the secondary, just don't run the
userspace app for that device.  I don't recommend doing your "grab the
other device" hack in the driver, as it's not not a very nice thing to
do, and will probably break in the future.

> Another solution would be to modify the kernel API somehow to let me
> suppress hotplug ADD/REMOVE on the secondary device.

Well, as your driver isn't even loaded at that point in time, it's a bit
hard for it to control that :)

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-07-30 22:15     ` Jon Smirl
@ 2004-07-31 15:59       ` Jesse Barnes
  2004-08-02 17:02         ` Jesse Barnes
  0 siblings, 1 reply; 110+ messages in thread
From: Jesse Barnes @ 2004-07-31 15:59 UTC (permalink / raw)
  To: Jon Smirl; +Cc: Greg KH, linux-kernel, linux-pci

On Friday, July 30, 2004 3:15 pm, Jon Smirl wrote:
> If you set pci_dev->resource[PCI_ROM_RESOURCE] to C000:0 won't this
> mess up pci_assign_resource()/release_resource()?

Yeah, you're right, that wouldn't be a good thing to do.  I guess I'll have to 
hang a different structure off of the pci_dev so we can tell the sysfs rom 
handling code where to get the rom.  Doing it that way would allow us to deal 
with cards that really need a copy made too, though the default behavior 
would be to read it directly.

How does that sound?

Jesse

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-07-31 15:59       ` Jesse Barnes
@ 2004-08-02 17:02         ` Jesse Barnes
  2004-08-02 17:29           ` Jon Smirl
                             ` (3 more replies)
  0 siblings, 4 replies; 110+ messages in thread
From: Jesse Barnes @ 2004-08-02 17:02 UTC (permalink / raw)
  To: Jon Smirl; +Cc: Greg KH, linux-kernel, linux-pci

[-- Attachment #1: Type: text/plain, Size: 999 bytes --]

On Saturday, July 31, 2004 8:59 am, Jesse Barnes wrote:
> On Friday, July 30, 2004 3:15 pm, Jon Smirl wrote:
> > If you set pci_dev->resource[PCI_ROM_RESOURCE] to C000:0 won't this
> > mess up pci_assign_resource()/release_resource()?
>
> Yeah, you're right, that wouldn't be a good thing to do.  I guess I'll have
> to hang a different structure off of the pci_dev so we can tell the sysfs
> rom handling code where to get the rom.  Doing it that way would allow us
> to deal with cards that really need a copy made too, though the default
> behavior would be to read it directly.
>
> How does that sound?

Here's a new patch that implements that suggestion, though without any special 
cases for the various cards that might need the ROM copy (e.g. those with 
shared decoders or whose ROMs are in the system ROM somewhere).  How does it 
look, Greg?  It it suitable for the mainline yet?  I expect those familiar 
with the various cards to add the necessary quirks code as needed.

Thanks,
Jesse

[-- Attachment #2: pci-sysfs-rom-7.patch --]
[-- Type: text/plain, Size: 6230 bytes --]

===== drivers/pci/pci-sysfs.c 1.10 vs edited =====
--- 1.10/drivers/pci/pci-sysfs.c	2004-06-04 06:23:04 -07:00
+++ edited/drivers/pci/pci-sysfs.c	2004-08-02 09:57:11 -07:00
@@ -164,6 +164,92 @@
 	return count;
 }
 
+/**
+ * pci_enable_rom - enable ROM decoding for a PCI device
+ * @dev: PCI device to enable
+ *
+ * Enable ROM decoding on @dev.  This involves simply turning on the last
+ * bit of the PCI ROM BAR.  Note that some cards may share address decoders
+ * between the ROM and other resources, so enabling it may disable access
+ * to MMIO registers or other card memory.
+ */
+static void
+pci_enable_rom(struct pci_dev *dev)
+{
+	u32 rom_addr;
+
+	pci_read_config_dword(dev, PCI_ROM_ADDRESS, &rom_addr);
+	rom_addr |= PCI_ROM_ADDRESS_ENABLE;
+	pci_write_config_dword(dev, PCI_ROM_ADDRESS, rom_addr);
+}
+
+/**
+ * pci_disable_rom - disable ROM decoding for a PCI device
+ * @dev: PCI device to disable
+ *
+ * Disable ROM decoding on a PCI device by turning off the last bit in the
+ * ROM BAR.
+ */
+static void
+pci_disable_rom(struct pci_dev *dev)
+{
+	u32 rom_addr;
+
+	pci_read_config_dword(dev, PCI_ROM_ADDRESS, &rom_addr);
+	rom_addr &= ~PCI_ROM_ADDRESS_ENABLE;
+	pci_write_config_dword(dev, PCI_ROM_ADDRESS, rom_addr);
+}
+
+/**
+ * pci_read_rom - read a PCI ROM
+ * @kobj: kernel object handle
+ * @buf: where to put the data we read from the ROM
+ * @off: file offset
+ * @count: number of bytes to read
+ *
+ * Put @count bytes starting at @off into @buf from the ROM in the PCI
+ * device corresponding to @kobj.
+ */
+static ssize_t
+pci_read_rom(struct kobject *kobj, char *buf, loff_t off, size_t count)
+{
+	struct pci_dev *dev = to_pci_dev(container_of(kobj,struct device,kobj));
+	loff_t init_off = off;
+	unsigned long start = pci_resource_start(dev, PCI_ROM_RESOURCE);
+	int size = pci_resource_len(dev, PCI_ROM_RESOURCE);
+	char direct_access = dev->rom_info.rom ? 0 : 1;
+
+	if (off > size)
+		return 0;
+	if (off + count > size) {
+		size -= off;
+		count = size;
+	} else {
+		size = count;
+	}
+
+	/* Enable ROM space decodes and do the reads */
+	if (direct_access)
+		pci_enable_rom(dev);
+
+	while (size > 0) {
+		unsigned char val;
+		if (direct_access)
+			val = readb(start + off);
+		else
+			val = *(dev->rom_info.rom + off);
+		buf[off - init_off] = val;
+		off++;
+		--size;
+	}
+
+	/* Disable again before continuing */
+	if (direct_access)
+		pci_disable_rom(dev);
+
+	return count;
+}
+
 static struct bin_attribute pci_config_attr = {
 	.attr =	{
 		.name = "config",
@@ -186,13 +272,50 @@
 	.write = pci_write_config,
 };
 
-void pci_create_sysfs_dev_files (struct pci_dev *pdev)
+void pci_create_sysfs_dev_files(struct pci_dev *pdev)
 {
 	if (pdev->cfg_size < 4096)
 		sysfs_create_bin_file(&pdev->dev.kobj, &pci_config_attr);
 	else
 		sysfs_create_bin_file(&pdev->dev.kobj, &pcie_config_attr);
 
+	/* If the device has a ROM, try to expose it in sysfs. */
+	if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) {
+		struct bin_attribute *rom_attr;
+		rom_attr = kmalloc(sizeof(*rom_attr), GFP_ATOMIC);
+
+		pdev->rom_info.rom_attr = NULL;
+		if (!rom_attr)
+			goto out;
+
+		pdev->rom_info.rom_attr = rom_attr;
+		rom_attr->attr.name = "rom";
+		rom_attr->attr.mode = S_IRUSR;
+		rom_attr->attr.owner = THIS_MODULE;
+		rom_attr->read = pci_read_rom;
+		rom_attr->size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
+		sysfs_create_bin_file(&pdev->dev.kobj, rom_attr);
+	}
+ out:
 	/* add platform-specific attributes */
 	pcibios_add_platform_entries(pdev);
+}
+
+/**
+ * pci_remove_sysfs_dev_files - cleanup PCI specific sysfs files
+ * @pdev: device whose entries we should free
+ *
+ * Cleanup when @pdev is removed from sysfs.
+ */
+void pci_remove_sysfs_dev_files(struct pci_dev *pdev)
+{
+	if (pdev->cfg_size < 4096)
+		sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr);
+	else
+		sysfs_remove_bin_file(&pdev->dev.kobj, &pcie_config_attr);
+
+	if (pdev->rom_info.rom_attr) {
+		sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_info.rom_attr);
+		kfree(pdev->rom_info.rom_attr);
+	}
 }
===== drivers/pci/pci.h 1.12 vs edited =====
--- 1.12/drivers/pci/pci.h	2004-06-04 06:23:04 -07:00
+++ edited/drivers/pci/pci.h	2004-08-02 09:41:02 -07:00
@@ -3,6 +3,7 @@
 extern int pci_hotplug (struct device *dev, char **envp, int num_envp,
 			 char *buffer, int buffer_size);
 extern void pci_create_sysfs_dev_files(struct pci_dev *pdev);
+extern void pci_remove_sysfs_dev_files(struct pci_dev *pdev);
 extern int pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
 				  unsigned long size, unsigned long align,
 				  unsigned long min, unsigned int type_mask,
===== drivers/pci/probe.c 1.65 vs edited =====
--- 1.65/drivers/pci/probe.c	2004-05-21 11:45:27 -07:00
+++ edited/drivers/pci/probe.c	2004-08-02 09:43:21 -07:00
@@ -157,6 +157,8 @@
 #endif
 		}
 	}
+
+	dev->rom_info.rom = NULL;
 	if (rom) {
 		dev->rom_base_reg = rom;
 		res = &dev->resource[PCI_ROM_RESOURCE];
===== drivers/pci/remove.c 1.3 vs edited =====
--- 1.3/drivers/pci/remove.c	2004-02-03 09:17:30 -08:00
+++ edited/drivers/pci/remove.c	2004-08-02 09:41:52 -07:00
@@ -26,6 +26,9 @@
 static void pci_destroy_dev(struct pci_dev *dev)
 {
 	pci_proc_detach_device(dev);
+	/* Free the copy of the ROM if we made one */
+	kfree(dev->rom_info.rom);
+	pci_remove_sysfs_dev_files(dev);
 	device_unregister(&dev->dev);
 
 	/* Remove the device from the device lists, and prevent any further
===== include/linux/pci.h 1.130 vs edited =====
--- 1.130/include/linux/pci.h	2004-06-30 11:21:27 -07:00
+++ edited/include/linux/pci.h	2004-08-02 09:32:08 -07:00
@@ -471,6 +471,11 @@
 	pci_mmap_mem
 };
 
+struct rom_info {
+	char *rom; /* copy of the ROM if necessary */
+	struct bin_attribute *rom_attr;
+};
+
 /* This defines the direction arg to the DMA mapping routines. */
 #define PCI_DMA_BIDIRECTIONAL	0
 #define PCI_DMA_TODEVICE	1
@@ -537,6 +542,7 @@
 	unsigned int	is_busmaster:1; /* device is busmaster */
 	
 	unsigned int 	saved_config_space[16]; /* config space saved at suspend time */
+	struct rom_info rom_info; /* How and where to get the ROM info for this device */
 #ifdef CONFIG_PCI_NAMES
 #define PCI_NAME_SIZE	96
 #define PCI_NAME_HALF	__stringify(43)	/* less than half to handle slop */

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-02 17:02         ` Jesse Barnes
@ 2004-08-02 17:29           ` Jon Smirl
  2004-08-02 21:00           ` Jon Smirl
                             ` (2 subsequent siblings)
  3 siblings, 0 replies; 110+ messages in thread
From: Jon Smirl @ 2004-08-02 17:29 UTC (permalink / raw)
  To: Jesse Barnes; +Cc: Greg KH, linux-kernel, linux-pci

--- Jesse Barnes <jbarnes@engr.sgi.com> wrote:
> look, Greg?  It it suitable for the mainline yet?  I expect those
> familiar  with the various cards to add the necessary quirks code
> as needed.

Radeons need this quirk for broken VBIOS's that leave the ROM decoding
disabled.

   unsigned int temp;
   temp = DRM_READ32(mmio, RADEON_MPP_TB_CONFIG);
   temp &= 0x00ffffffu;
   temp |= 0x04 << 24;
   DRM_WRITE32(mmio, RADEON_MPP_TB_CONFIG, temp);
   temp = DRM_READ32(mmio, RADEON_MPP_TB_CONFIG);

Shouldn't this go into the radeon driver so that it will work with
hotplug? Or should it go in both places, a _devinit pci_quirk and the
radeon driver?


=====
Jon Smirl
jonsmirl@yahoo.com


		
__________________________________
Do you Yahoo!?
New and Improved Yahoo! Mail - Send 10MB messages!
http://promotions.yahoo.com/new_mail 

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-02 17:02         ` Jesse Barnes
  2004-08-02 17:29           ` Jon Smirl
@ 2004-08-02 21:00           ` Jon Smirl
  2004-08-02 21:05             ` Jesse Barnes
  2004-08-02 23:30           ` Alan Cox
  2004-08-04  6:08           ` Jon Smirl
  3 siblings, 1 reply; 110+ messages in thread
From: Jon Smirl @ 2004-08-02 21:00 UTC (permalink / raw)
  To: Jesse Barnes; +Cc: Greg KH, linux-kernel, linux-pci

--- Jesse Barnes <jbarnes@engr.sgi.com> wrote:
> It it suitable for the mainline yet?  I expect those familiar with 
> the various cards to add the necessary quirks code as needed.

Is tracking the boot video device and redirecting to C000:0 going to be
a quirk, architecture specific, or what? Where does this little piece
of code need to go?

=====
Jon Smirl
jonsmirl@yahoo.com


	
		
__________________________________
Do you Yahoo!?
New and Improved Yahoo! Mail - 100MB free storage!
http://promotions.yahoo.com/new_mail 

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-02 21:00           ` Jon Smirl
@ 2004-08-02 21:05             ` Jesse Barnes
  2004-08-02 23:32               ` Alan Cox
  0 siblings, 1 reply; 110+ messages in thread
From: Jesse Barnes @ 2004-08-02 21:05 UTC (permalink / raw)
  To: linux-pci; +Cc: Jon Smirl, Greg KH, linux-kernel

On Monday, August 2, 2004 2:00 pm, Jon Smirl wrote:
> --- Jesse Barnes <jbarnes@engr.sgi.com> wrote:
> > It it suitable for the mainline yet?  I expect those familiar with
> > the various cards to add the necessary quirks code as needed.
>
> Is tracking the boot video device and redirecting to C000:0 going to be
> a quirk, architecture specific, or what? Where does this little piece
> of code need to go?

I think that would be a quirk.  You'd copy ROMs like that into the rom pointer 
in the pci_dev structure I guess.

Jesse

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-02 17:02         ` Jesse Barnes
  2004-08-02 17:29           ` Jon Smirl
  2004-08-02 21:00           ` Jon Smirl
@ 2004-08-02 23:30           ` Alan Cox
  2004-08-03  2:03             ` Jesse Barnes
  2004-08-04  6:08           ` Jon Smirl
  3 siblings, 1 reply; 110+ messages in thread
From: Alan Cox @ 2004-08-02 23:30 UTC (permalink / raw)
  To: Jesse Barnes; +Cc: Jon Smirl, Greg KH, Linux Kernel Mailing List, linux-pci

What guarantees the ROM already has an assigned PCI address ?


^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-02 21:05             ` Jesse Barnes
@ 2004-08-02 23:32               ` Alan Cox
  0 siblings, 0 replies; 110+ messages in thread
From: Alan Cox @ 2004-08-02 23:32 UTC (permalink / raw)
  To: Jesse Barnes; +Cc: linux-pci, Jon Smirl, Greg KH, Linux Kernel Mailing List

On Llu, 2004-08-02 at 22:05, Jesse Barnes wrote:
> > Is tracking the boot video device and redirecting to C000:0 going to be
> > a quirk, architecture specific, or what? Where does this little piece
> > of code need to go?
> 
> I think that would be a quirk.  You'd copy ROMs like that into the rom pointer 
> in the pci_dev structure I guess.

Providing that quirk is scanned at hotplug time it can go into the quirk
code which is probably simplest. It may need a tiny hook in sysfs to map
the right object but that isnt hard.



^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-02 23:30           ` Alan Cox
@ 2004-08-03  2:03             ` Jesse Barnes
  2004-08-03  2:32               ` Jon Smirl
  2004-08-03 21:19               ` Jon Smirl
  0 siblings, 2 replies; 110+ messages in thread
From: Jesse Barnes @ 2004-08-03  2:03 UTC (permalink / raw)
  To: Alan Cox; +Cc: Jon Smirl, Greg KH, Linux Kernel Mailing List, linux-pci

On Monday, August 2, 2004 4:30 pm, Alan Cox wrote:
> What guarantees the ROM already has an assigned PCI address ?

Presumably the PCI core.  If that's a bad assumption, then clearly this code 
won't work as is and will need additional checks/setup code.

Jesse

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-03  2:03             ` Jesse Barnes
@ 2004-08-03  2:32               ` Jon Smirl
  2004-08-03 17:07                 ` Jesse Barnes
  2004-08-03 21:19               ` Jon Smirl
  1 sibling, 1 reply; 110+ messages in thread
From: Jon Smirl @ 2004-08-03  2:32 UTC (permalink / raw)
  To: Jesse Barnes, Alan Cox
  Cc: Jon Smirl, Greg KH, Linux Kernel Mailing List, linux-pci

My original version have this in it:

/* assign the ROM an address if it doesn't have one */
if (r->parent == NULL)
   pci_assign_resource(dev->pdev, PCI_ROM_RESOURCE);
                                                                       
                  
if (r->parent) {
    release_resource(r);
    r->flags &= ~PCI_ROM_ADDRESS_ENABLE;
    r->end -= r->start;
    r->start = 0;
}

I was running this code on both 2.4/2.6 but I may have needed to do
this for 2.4. Is it consistent to have pci_assign_resource() and then
use release_resource()?

My i875P AGP controller has a ROM on it as well as my two video cards.


--- Jesse Barnes <jbarnes@engr.sgi.com> wrote:

> On Monday, August 2, 2004 4:30 pm, Alan Cox wrote:
> > What guarantees the ROM already has an assigned PCI address ?
> 
> Presumably the PCI core.  If that's a bad assumption, then clearly
> this code 
> won't work as is and will need additional checks/setup code.
> 
> Jesse
> 

=====
Jon Smirl
jonsmirl@yahoo.com


		
__________________________________
Do you Yahoo!?
Yahoo! Mail - 50x more storage than other providers!
http://promotions.yahoo.com/new_mail

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-03  2:32               ` Jon Smirl
@ 2004-08-03 17:07                 ` Jesse Barnes
  0 siblings, 0 replies; 110+ messages in thread
From: Jesse Barnes @ 2004-08-03 17:07 UTC (permalink / raw)
  To: Jon Smirl; +Cc: Alan Cox, Greg KH, Linux Kernel Mailing List, linux-pci

On Monday, August 2, 2004 7:32 pm, Jon Smirl wrote:
> My original version have this in it:
>
> /* assign the ROM an address if it doesn't have one */
> if (r->parent == NULL)
>    pci_assign_resource(dev->pdev, PCI_ROM_RESOURCE);
>
>
> if (r->parent) {
>     release_resource(r);
>     r->flags &= ~PCI_ROM_ADDRESS_ENABLE;
>     r->end -= r->start;
>     r->start = 0;
> }
>
> I was running this code on both 2.4/2.6 but I may have needed to do
> this for 2.4. Is it consistent to have pci_assign_resource() and then
> use release_resource()?
>
> My i875P AGP controller has a ROM on it as well as my two video cards.

So it seems there's some redundancy here--some platforms will map the ROMs at 
discovery time while on others we have to map them explicitly?  Which should 
we count on?  I was assuming the former in the last patch I posted, and would 
like to keep it that way unless there's some reason not to.

Thanks,
Jesse

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-03  2:03             ` Jesse Barnes
  2004-08-03  2:32               ` Jon Smirl
@ 2004-08-03 21:19               ` Jon Smirl
  2004-08-03 21:28                 ` Jesse Barnes
  1 sibling, 1 reply; 110+ messages in thread
From: Jon Smirl @ 2004-08-03 21:19 UTC (permalink / raw)
  To: Jesse Barnes, Alan Cox
  Cc: Jon Smirl, Greg KH, Linux Kernel Mailing List, linux-pci

This is saying that my AGP bridge chip has a ROM right?

00:01.0 PCI bridge: Intel Corp. 82875P Processor to AGP Controller (rev
02) (prog-if 00 [Normal decode])
        Flags: bus master, 66Mhz, fast devsel, latency 64
        Bus: primary=00, secondary=01, subordinate=01, sec-latency=64
        I/O behind bridge: 0000d000-0000dfff
        Memory behind bridge: fe900000-feafffff
        Prefetchable memory behind bridge: f0000000-f7ffffff
        Expansion ROM at 0000d000 [disabled] [size=4K]
 
Each of my video controllers has one too:

01:00.0 VGA compatible controller: ATI Technologies Inc Radeon RV250 If
[Radeon 9000] (rev 01) (prog-if 00 [VGA])
        Subsystem: C.P. Technology Co. Ltd RV250 If [Radeon 9000 Pro
"Evil Commando"]
        Flags: stepping, 66Mhz, medium devsel, IRQ 177
        Memory at f4000000 (32-bit, prefetchable) [disabled]
[size=fea00000]
        I/O ports at de00 [disabled] [size=256]
        Memory at fe9e0000 (32-bit, non-prefetchable) [disabled]
[size=64K]
        Expansion ROM at 00020000 [disabled]
        Capabilities: <available only to root>
 
02:02.0 VGA compatible controller: ATI Technologies Inc Rage 128 PD/PRO
TMDS (prog-if 00 [VGA])
        Subsystem: ATI Technologies Inc Rage 128 AIW
        Flags: bus master, stepping, medium devsel, latency 64, IRQ 209
        Memory at f8000000 (32-bit, prefetchable) [size=fe800000]
        I/O ports at ce00 [size=256]
        Memory at fe7dc000 (32-bit, non-prefetchable) [size=16K]
        Expansion ROM at 00020000 [disabled]
        Capabilities: <available only to root>

Both of the video ROMs are at 00020000, won't they end up on top of
each other when enabled?

With the patch the video ROMs are in sysfs but the AGP bridge one is
not.


=====
Jon Smirl
jonsmirl@yahoo.com


		
__________________________________
Do you Yahoo!?
Yahoo! Mail - 50x more storage than other providers!
http://promotions.yahoo.com/new_mail

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-03 21:19               ` Jon Smirl
@ 2004-08-03 21:28                 ` Jesse Barnes
  2004-08-03 21:30                   ` Jesse Barnes
  2004-08-03 21:31                   ` Martin Mares
  0 siblings, 2 replies; 110+ messages in thread
From: Jesse Barnes @ 2004-08-03 21:28 UTC (permalink / raw)
  To: linux-pci; +Cc: Jon Smirl, Alan Cox, Greg KH, Linux Kernel Mailing List

On Tuesday, August 3, 2004 2:19 pm, Jon Smirl wrote:
> This is saying that my AGP bridge chip has a ROM right?
>
> 00:01.0 PCI bridge: Intel Corp. 82875P Processor to AGP Controller (rev
> 02) (prog-if 00 [Normal decode])
>         Flags: bus master, 66Mhz, fast devsel, latency 64
>         Bus: primary=00, secondary=01, subordinate=01, sec-latency=64
>         I/O behind bridge: 0000d000-0000dfff
>         Memory behind bridge: fe900000-feafffff
>         Prefetchable memory behind bridge: f0000000-f7ffffff
>         Expansion ROM at 0000d000 [disabled] [size=4K]
>
> Each of my video controllers has one too:
>
> 01:00.0 VGA compatible controller: ATI Technologies Inc Radeon RV250 If
> [Radeon 9000] (rev 01) (prog-if 00 [VGA])
>         Subsystem: C.P. Technology Co. Ltd RV250 If [Radeon 9000 Pro
> "Evil Commando"]
>         Flags: stepping, 66Mhz, medium devsel, IRQ 177
>         Memory at f4000000 (32-bit, prefetchable) [disabled]
> [size=fea00000]
>         I/O ports at de00 [disabled] [size=256]
>         Memory at fe9e0000 (32-bit, non-prefetchable) [disabled]
> [size=64K]
>         Expansion ROM at 00020000 [disabled]
>         Capabilities: <available only to root>
>
> 02:02.0 VGA compatible controller: ATI Technologies Inc Rage 128 PD/PRO
> TMDS (prog-if 00 [VGA])
>         Subsystem: ATI Technologies Inc Rage 128 AIW
>         Flags: bus master, stepping, medium devsel, latency 64, IRQ 209
>         Memory at f8000000 (32-bit, prefetchable) [size=fe800000]
>         I/O ports at ce00 [size=256]
>         Memory at fe7dc000 (32-bit, non-prefetchable) [size=16K]
>         Expansion ROM at 00020000 [disabled]
>         Capabilities: <available only to root>
>
> Both of the video ROMs are at 00020000, won't they end up on top of
> each other when enabled?

Yeah, it doesn't look like they've been properly assigned addresses.  But then 
I've also seen lspci lie, you can check /sys/devices/.../config for the 
actual resource values.  If they're sane then things are more likely to work.

Jesse

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-03 21:28                 ` Jesse Barnes
@ 2004-08-03 21:30                   ` Jesse Barnes
  2004-08-03 21:31                   ` Martin Mares
  1 sibling, 0 replies; 110+ messages in thread
From: Jesse Barnes @ 2004-08-03 21:30 UTC (permalink / raw)
  To: linux-pci; +Cc: Jon Smirl, Alan Cox, Greg KH, Linux Kernel Mailing List

On Tuesday, August 3, 2004 2:28 pm, Jesse Barnes wrote:
> > Both of the video ROMs are at 00020000, won't they end up on top of
> > each other when enabled?
>
> Yeah, it doesn't look like they've been properly assigned addresses.  But
> then I've also seen lspci lie, you can check /sys/devices/.../config for
> the actual resource values.  If they're sane then things are more likely to
> work.

Oops, I mean /sys/devices/.../resource.  E.g.

jbarnes@mill:~$ cat /sys/devices/pci0002\:06/0002\:06\:0f.0/resource
0x00000000f5200000 0x00000000f53fffff 0x0000000000000200
0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 0x0000000000000000
0x00000000f5100000 0x00000000f51fffff 0x0000000000007200

Jesse

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-03 21:28                 ` Jesse Barnes
  2004-08-03 21:30                   ` Jesse Barnes
@ 2004-08-03 21:31                   ` Martin Mares
  2004-08-03 21:36                     ` Jon Smirl
  1 sibling, 1 reply; 110+ messages in thread
From: Martin Mares @ 2004-08-03 21:31 UTC (permalink / raw)
  To: Jesse Barnes
  Cc: linux-pci, Jon Smirl, Alan Cox, Greg KH, Linux Kernel Mailing List

> Yeah, it doesn't look like they've been properly assigned addresses.  But then 
> I've also seen lspci lie, you can check /sys/devices/.../config for the 
> actual resource values.  If they're sane then things are more likely to work.

... or try `lspci -b', it will dump the actual registers, not the kernel's
view.

				Have a nice fortnight
-- 
Martin `MJ' Mares   <mj@ucw.cz>   http://atrey.karlin.mff.cuni.cz/~mj/
Faculty of Math and Physics, Charles University, Prague, Czech Rep., Earth
A jury consists of 12 persons chosen to decide who has the better lawyer.

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-03 21:31                   ` Martin Mares
@ 2004-08-03 21:36                     ` Jon Smirl
  2004-08-03 21:39                       ` Martin Mares
  0 siblings, 1 reply; 110+ messages in thread
From: Jon Smirl @ 2004-08-03 21:36 UTC (permalink / raw)
  To: Martin Mares, Jesse Barnes
  Cc: linux-pci, Jon Smirl, Alan Cox, Greg KH, Linux Kernel Mailing List

lspci -b would be great but it doesn't seem to have implemented dumping
the ROM location. It dumps everything else.

--- Martin Mares <mj@ucw.cz> wrote:

> > Yeah, it doesn't look like they've been properly assigned
> addresses.  But then 
> > I've also seen lspci lie, you can check /sys/devices/.../config for
> the 
> > actual resource values.  If they're sane then things are more
> likely to work.
> 
> ... or try `lspci -b', it will dump the actual registers, not the
> kernel's
> view.
> 
> 				Have a nice fortnight
> -- 
> Martin `MJ' Mares   <mj@ucw.cz>  
> http://atrey.karlin.mff.cuni.cz/~mj/
> Faculty of Math and Physics, Charles University, Prague, Czech Rep.,
> Earth
> A jury consists of 12 persons chosen to decide who has the better
> lawyer.
> 


=====
Jon Smirl
jonsmirl@yahoo.com


		
__________________________________
Do you Yahoo!?
New and Improved Yahoo! Mail - Send 10MB messages!
http://promotions.yahoo.com/new_mail 

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-03 21:36                     ` Jon Smirl
@ 2004-08-03 21:39                       ` Martin Mares
  2004-08-04  6:00                         ` lspci and ROM on bridge, was: " Jon Smirl
  2004-08-05  5:05                         ` Jon Smirl
  0 siblings, 2 replies; 110+ messages in thread
From: Martin Mares @ 2004-08-03 21:39 UTC (permalink / raw)
  To: Jon Smirl
  Cc: Jesse Barnes, linux-pci, Alan Cox, Greg KH, Linux Kernel Mailing List

> lspci -b would be great but it doesn't seem to have implemented dumping
> the ROM location. It dumps everything else.

Ah well, I see the problem -- it doesn't display disabled ROMs.

The following quick hack should cure it:

--- orig/lib/generic.c
+++ mod/lib/generic.c
@@ -160,7 +160,9 @@
       if (reg)
 	{
 	  u32 a = pci_read_long(d, reg);
+#if 0
 	  if (a & PCI_ROM_ADDRESS_ENABLE)
+#endif
 	    d->rom_base_addr = a;
 	}
     }




				Have a nice fortnight
-- 
Martin `MJ' Mares   <mj@ucw.cz>   http://atrey.karlin.mff.cuni.cz/~mj/
Faculty of Math and Physics, Charles University, Prague, Czech Rep., Earth
A student who changes the course of history is probably taking an exam.

^ permalink raw reply	[flat|nested] 110+ messages in thread

* lspci and ROM on bridge, was: [PATCH] add PCI ROMs to sysfs
  2004-08-03 21:39                       ` Martin Mares
@ 2004-08-04  6:00                         ` Jon Smirl
  2004-08-04 16:04                           ` Martin Mares
  2004-08-05  5:05                         ` Jon Smirl
  1 sibling, 1 reply; 110+ messages in thread
From: Jon Smirl @ 2004-08-04  6:00 UTC (permalink / raw)
  To: Martin Mares; +Cc: Jesse Barnes, linux-pci, Linux Kernel Mailing List

lspci shows my AGP bridge

00:01.0 PCI bridge: Intel Corp. 82875P Processor to AGP Controller (rev
02) (prog-if 00 [Normal decode])
        Flags: bus master, 66Mhz, fast devsel, latency 64
        Bus: primary=00, secondary=01, subordinate=01, sec-latency=64
        I/O behind bridge: 0000d000-0000dfff
        Memory behind bridge: fe900000-feafffff
        Prefetchable memory behind bridge: f0000000-f7ffffff
        Expansion ROM at 0000d000 [disabled] [size=4K]
 
It shows an expansion ROM at d000. d000 is a normal address for a ROM.
But d000 also shows as I/O behind bridge.

If I list sysfs resources, the ROM is in slot 7, not 6 which is normal
for a ROM. Is this really a ROM or a bug in lspci?

[jonsmirl@smirl 0000:00:01.0]$ cat resource
0  0x0000000000000000 0x0000000000000000 0x0000000000000000
1  0x0000000000000000 0x0000000000000000 0x0000000000000000
2  0x0000000000000000 0x0000000000000000 0x0000000000000000
3  0x0000000000000000 0x0000000000000000 0x0000000000000000
4  0x0000000000000000 0x0000000000000000 0x0000000000000000
5  0x0000000000000000 0x0000000000000000 0x0000000000000000
6  0x0000000000000000 0x0000000000000000 0x0000000000000000
7  0x000000000000d000 0x000000000000dfff 0x0000000000000100
8  0x00000000fe900000 0x00000000feafffff 0x0000000000000200
9  0x00000000f0000000 0x00000000f7ffffff 0x0000000000001200
10 0x0000000000000000 0x0000000000000000 0x0000000000000000
11 0x0000000000000000 0x0000000000000000 0x0000000000000000
[jonsmirl@smirl 0000:00:01.0]$



=====
Jon Smirl
jonsmirl@yahoo.com


		
__________________________________
Do you Yahoo!?
Read only the mail you want - Yahoo! Mail SpamGuard.
http://promotions.yahoo.com/new_mail 

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-02 17:02         ` Jesse Barnes
                             ` (2 preceding siblings ...)
  2004-08-02 23:30           ` Alan Cox
@ 2004-08-04  6:08           ` Jon Smirl
  2004-08-04 15:56             ` Jesse Barnes
  3 siblings, 1 reply; 110+ messages in thread
From: Jon Smirl @ 2004-08-04  6:08 UTC (permalink / raw)
  To: Jesse Barnes; +Cc: Greg KH, linux-kernel, linux-pci

[-- Attachment #1: Type: text/plain, Size: 858 bytes --]

This is a new version of Jesse's PCI ROM patch.

It can read ROMs on x86. Main problem was that the PCI address space is
not part of the kernel address space on the x86 so ioremap() is needed.
I added the parts about assign/release resource but I am not sure that
they are needed.

The patch obviously needs more work for the not_direct case. The quirk
to record the boot video card has not been written.

I wasted a lot of time figuring out why it wouldn't work until I
remembered that my radeon card needs a kernel quirk to fix it's ROM
enabling. Most radeon cards have this problem so if you get FFFF the
code is problem working, I just need to write the quirk to enable the ROM.

=====
Jon Smirl
jonsmirl@yahoo.com


		
__________________________________
Do you Yahoo!?
Yahoo! Mail is new and improved - Check it out!
http://promotions.yahoo.com/new_mail

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: pci-sysfs-rom-8.patch --]
[-- Type: text/x-patch; name="pci-sysfs-rom-8.patch", Size: 6830 bytes --]

===== drivers/pci/pci-sysfs.c 1.10 vs edited =====
--- 1.10/drivers/pci/pci-sysfs.c	Fri Jun  4 09:23:04 2004
+++ edited/drivers/pci/pci-sysfs.c	Wed Aug  4 01:37:15 2004
@@ -164,6 +164,108 @@
 	return count;
 }
 
+/**
+ * pci_enable_rom - enable ROM decoding for a PCI device
+ * @dev: PCI device to enable
+ *
+ * Enable ROM decoding on @dev.  This involves simply turning on the last
+ * bit of the PCI ROM BAR.  Note that some cards may share address decoders
+ * between the ROM and other resources, so enabling it may disable access
+ * to MMIO registers or other card memory.
+ */
+static void
+pci_enable_rom(struct pci_dev *dev)
+{
+	u32 rom_addr;
+	
+	pci_read_config_dword(dev, dev->rom_base_reg, &rom_addr);
+	rom_addr |= PCI_ROM_ADDRESS_ENABLE;
+	pci_write_config_dword(dev, dev->rom_base_reg, rom_addr);
+}
+
+/**
+ * pci_disable_rom - disable ROM decoding for a PCI device
+ * @dev: PCI device to disable
+ *
+ * Disable ROM decoding on a PCI device by turning off the last bit in the
+ * ROM BAR.
+ */
+static void
+pci_disable_rom(struct pci_dev *dev)
+{
+	u32 rom_addr;
+
+	pci_read_config_dword(dev, dev->rom_base_reg, &rom_addr);
+	rom_addr &= ~PCI_ROM_ADDRESS_ENABLE;
+	pci_write_config_dword(dev, dev->rom_base_reg, rom_addr);
+}
+
+/**
+ * pci_read_rom - read a PCI ROM
+ * @kobj: kernel object handle
+ * @buf: where to put the data we read from the ROM
+ * @off: file offset
+ * @count: number of bytes to read
+ *
+ * Put @count bytes starting at @off into @buf from the ROM in the PCI
+ * device corresponding to @kobj.
+ */
+static ssize_t
+pci_read_rom(struct kobject *kobj, char *buf, loff_t off, size_t count)
+{
+	struct pci_dev *dev = to_pci_dev(container_of(kobj,struct device,kobj));
+	unsigned long start;
+	loff_t i, size;
+	char direct_access = dev->rom_info.rom ? 0 : 1;
+	unsigned char *rom = NULL;
+	struct resource *r = &dev->resource[PCI_ROM_RESOURCE];
+
+	if (off > size)
+		return 0;
+		
+	if (direct_access) {
+		/* assign the ROM an address if it doesn't have one */
+		if (r->parent == NULL)
+			pci_assign_resource(dev, PCI_ROM_RESOURCE);
+		/* Enable ROM space decodes and do the reads */
+		pci_enable_rom(dev);
+		start = pci_resource_start(dev, PCI_ROM_RESOURCE);
+		size = pci_resource_len(dev, PCI_ROM_RESOURCE);
+		rom = ioremap(start, size);
+		
+		printk("read_rom start %lx size %x\n", start, size);
+		printk("rom bytes %02x %02x\n", rom, rom + 1);
+	}
+	if (off + count > size) {
+		size -= off;
+		count = size;
+	} else
+		size = count;
+
+	i = 0;
+	while (size > 0) {
+		unsigned char val;
+		if (direct_access)
+			val = readb(rom + off);
+		else
+			val = *(dev->rom_info.rom + off);
+		buf[i++] = val;
+		off++;
+		--size;
+	}
+	if (direct_access) {
+		iounmap(rom);
+		/* Disable again before continuing */
+		pci_disable_rom(dev);
+		if (r->parent) {
+			release_resource(r);
+			r->end -= r->start;
+			r->start = 0;
+		}
+	}
+	return count;
+}
+
 static struct bin_attribute pci_config_attr = {
 	.attr =	{
 		.name = "config",
@@ -186,13 +288,51 @@
 	.write = pci_write_config,
 };
 
-void pci_create_sysfs_dev_files (struct pci_dev *pdev)
+void pci_create_sysfs_dev_files(struct pci_dev *pdev)
 {
 	if (pdev->cfg_size < 4096)
 		sysfs_create_bin_file(&pdev->dev.kobj, &pci_config_attr);
 	else
 		sysfs_create_bin_file(&pdev->dev.kobj, &pcie_config_attr);
 
+printk("devfn %x len %lx start %lx\n", pdev->devfn, pci_resource_len(pdev, PCI_ROM_RESOURCE), pci_resource_start(pdev, PCI_ROM_RESOURCE));
+	/* If the device has a ROM, try to expose it in sysfs. */
+	if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) {
+		struct bin_attribute *rom_attr;
+		rom_attr = kmalloc(sizeof(*rom_attr), GFP_ATOMIC);
+
+		pdev->rom_info.rom_attr = NULL;
+		if (!rom_attr)
+			goto out;
+
+		pdev->rom_info.rom_attr = rom_attr;
+		rom_attr->attr.name = "rom";
+		rom_attr->attr.mode = S_IRUSR;
+		rom_attr->attr.owner = THIS_MODULE;
+		rom_attr->read = pci_read_rom;
+		rom_attr->size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
+		sysfs_create_bin_file(&pdev->dev.kobj, rom_attr);
+	}
+ out:
 	/* add platform-specific attributes */
 	pcibios_add_platform_entries(pdev);
+}
+
+/**
+ * pci_remove_sysfs_dev_files - cleanup PCI specific sysfs files
+ * @pdev: device whose entries we should free
+ *
+ * Cleanup when @pdev is removed from sysfs.
+ */
+void pci_remove_sysfs_dev_files(struct pci_dev *pdev)
+{
+	if (pdev->cfg_size < 4096)
+		sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr);
+	else
+		sysfs_remove_bin_file(&pdev->dev.kobj, &pcie_config_attr);
+
+	if (pdev->rom_info.rom_attr) {
+		sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_info.rom_attr);
+		kfree(pdev->rom_info.rom_attr);
+	}
 }
===== drivers/pci/pci.h 1.12 vs edited =====
--- 1.12/drivers/pci/pci.h	Fri Jun  4 09:23:04 2004
+++ edited/drivers/pci/pci.h	Tue Aug  3 17:05:19 2004
@@ -3,6 +3,7 @@
 extern int pci_hotplug (struct device *dev, char **envp, int num_envp,
 			 char *buffer, int buffer_size);
 extern void pci_create_sysfs_dev_files(struct pci_dev *pdev);
+extern void pci_remove_sysfs_dev_files(struct pci_dev *pdev);
 extern int pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
 				  unsigned long size, unsigned long align,
 				  unsigned long min, unsigned int type_mask,
===== drivers/pci/probe.c 1.65 vs edited =====
--- 1.65/drivers/pci/probe.c	Fri May 21 14:45:27 2004
+++ edited/drivers/pci/probe.c	Tue Aug  3 17:05:19 2004
@@ -157,6 +157,8 @@
 #endif
 		}
 	}
+
+	dev->rom_info.rom = NULL;
 	if (rom) {
 		dev->rom_base_reg = rom;
 		res = &dev->resource[PCI_ROM_RESOURCE];
===== drivers/pci/remove.c 1.3 vs edited =====
--- 1.3/drivers/pci/remove.c	Tue Feb  3 12:17:30 2004
+++ edited/drivers/pci/remove.c	Tue Aug  3 17:05:20 2004
@@ -26,6 +26,9 @@
 static void pci_destroy_dev(struct pci_dev *dev)
 {
 	pci_proc_detach_device(dev);
+	/* Free the copy of the ROM if we made one */
+	kfree(dev->rom_info.rom);
+	pci_remove_sysfs_dev_files(dev);
 	device_unregister(&dev->dev);
 
 	/* Remove the device from the device lists, and prevent any further
===== include/linux/pci.h 1.132 vs edited =====
--- 1.132/include/linux/pci.h	Mon Aug  2 04:00:43 2004
+++ edited/include/linux/pci.h	Tue Aug  3 17:05:21 2004
@@ -471,6 +471,11 @@
 	pci_mmap_mem
 };
 
+struct rom_info {
+	char *rom; /* copy of the ROM if necessary */
+	struct bin_attribute *rom_attr;
+};
+
 /* This defines the direction arg to the DMA mapping routines. */
 #define PCI_DMA_BIDIRECTIONAL	0
 #define PCI_DMA_TODEVICE	1
@@ -537,6 +542,7 @@
 	unsigned int	is_busmaster:1; /* device is busmaster */
 	
 	unsigned int 	saved_config_space[16]; /* config space saved at suspend time */
+	struct rom_info rom_info; /* How and where to get the ROM info for this device */
 #ifdef CONFIG_PCI_NAMES
 #define PCI_NAME_SIZE	96
 #define PCI_NAME_HALF	__stringify(43)	/* less than half to handle slop */

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-04  6:08           ` Jon Smirl
@ 2004-08-04 15:56             ` Jesse Barnes
  0 siblings, 0 replies; 110+ messages in thread
From: Jesse Barnes @ 2004-08-04 15:56 UTC (permalink / raw)
  To: Jon Smirl; +Cc: Greg KH, linux-kernel, linux-pci

On Tuesday, August 3, 2004 11:08 pm, Jon Smirl wrote:
> This is a new version of Jesse's PCI ROM patch.
>
> It can read ROMs on x86. Main problem was that the PCI address space is
> not part of the kernel address space on the x86 so ioremap() is needed.
> I added the parts about assign/release resource but I am not sure that
> they are needed.

Ah, that would explain it.  Thanks for fixing it up.

Jesse

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: lspci and ROM on bridge, was: [PATCH] add PCI ROMs to sysfs
  2004-08-04  6:00                         ` lspci and ROM on bridge, was: " Jon Smirl
@ 2004-08-04 16:04                           ` Martin Mares
  0 siblings, 0 replies; 110+ messages in thread
From: Martin Mares @ 2004-08-04 16:04 UTC (permalink / raw)
  To: Jon Smirl; +Cc: Jesse Barnes, linux-pci, Linux Kernel Mailing List

> lspci shows my AGP bridge
> 
> 00:01.0 PCI bridge: Intel Corp. 82875P Processor to AGP Controller (rev
> 02) (prog-if 00 [Normal decode])
>         Flags: bus master, 66Mhz, fast devsel, latency 64
>         Bus: primary=00, secondary=01, subordinate=01, sec-latency=64
>         I/O behind bridge: 0000d000-0000dfff
>         Memory behind bridge: fe900000-feafffff
>         Prefetchable memory behind bridge: f0000000-f7ffffff
>         Expansion ROM at 0000d000 [disabled] [size=4K]
>  
> It shows an expansion ROM at d000. d000 is a normal address for a ROM.
> But d000 also shows as I/O behind bridge.

Was it with the `-b' option or not?

				Have a nice fortnight
-- 
Martin `MJ' Mares   <mj@ucw.cz>   http://atrey.karlin.mff.cuni.cz/~mj/
Faculty of Math and Physics, Charles University, Prague, Czech Rep., Earth
"#define QUESTION ((bb) || !(bb))"  -- Shakespeare

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-03 21:39                       ` Martin Mares
  2004-08-04  6:00                         ` lspci and ROM on bridge, was: " Jon Smirl
@ 2004-08-05  5:05                         ` Jon Smirl
  2004-08-05  5:41                           ` Benjamin Herrenschmidt
  2004-08-05 15:54                           ` Jesse Barnes
  1 sibling, 2 replies; 110+ messages in thread
From: Jon Smirl @ 2004-08-05  5:05 UTC (permalink / raw)
  To: Martin Mares
  Cc: Jesse Barnes, linux-pci, Alan Cox, Linux Kernel Mailing List,
	Petr Vandrovec, Benjamin Herrenschmidt

[-- Attachment #1: Type: text/plain, Size: 931 bytes --]

Version 10

implements an x86 quirk to record the boot video device. Is the
PCI_ROM_SHADOW flag a safe define? Quirk records boot video device by
looking at how the bridges route to the VGA device. It there some other
way to tell which video card is the boot one? What if there is more
than one VGA card on the PCI bus? I think the BIOS spec is to enable
the one in the lowest slot number. Can someone who own multiple PCI
video cards test this? I tested with one PCI, one AGP.

BenH, this should solve the problem of which video card owns the ROM
copy at C000:0. For the boot device this code returns the shadow copy,
else the real ROM on the card.

I did the x86 quirk, what do the quirks on ia64, ppc, x86_64 need? Can
they just copy the x86 one?

=====
Jon Smirl
jonsmirl@yahoo.com


	
		
__________________________________
Do you Yahoo!?
New and Improved Yahoo! Mail - 100MB free storage!
http://promotions.yahoo.com/new_mail 

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: pci-sysfs-rom-10.patch --]
[-- Type: text/x-patch; name="pci-sysfs-rom-10.patch", Size: 7275 bytes --]

===== arch/i386/pci/fixup.c 1.19 vs edited =====
--- 1.19/arch/i386/pci/fixup.c	Thu Jun  3 10:58:17 2004
+++ edited/arch/i386/pci/fixup.c	Thu Aug  5 00:20:08 2004
@@ -237,6 +237,29 @@
 	}
 }
 
+static void __devinit pci_fixup_video(struct pci_dev *pdev)
+{
+	struct pci_dev *bridge;
+	struct pci_bus *bus;
+	u16 l;
+	
+	if ((pdev->class >> 8) != PCI_CLASS_DISPLAY_VGA)
+		return;
+	       
+	/* Is VGA routed to us? */
+	bus = pdev->bus;
+	while (bus) {
+		bridge = bus->self;
+		if (bridge) {
+			pci_read_config_word(bridge, PCI_BRIDGE_CONTROL, &l);
+			if (!(l & PCI_BRIDGE_CTL_VGA))
+				return;
+		}
+		bus = bus->parent;
+	}
+	pdev->resource[PCI_ROM_RESOURCE].flags |= PCI_ROM_SHADOW;
+}
+
 struct pci_fixup pcibios_fixups[] = {
 	{
 		.pass		= PCI_FIXUP_HEADER,
@@ -345,6 +368,12 @@
 		.vendor		= PCI_VENDOR_ID_NVIDIA,
 		.device		= PCI_DEVICE_ID_NVIDIA_NFORCE2,
 		.hook		= pci_fixup_nforce2
+	},
+	{
+		.pass		= PCI_FIXUP_FINAL,
+		.vendor		= PCI_ANY_ID,
+		.device		= PCI_ANY_ID,
+		.hook		= pci_fixup_video
 	},
 	{ .pass = 0 }
 };
===== drivers/pci/pci-sysfs.c 1.10 vs edited =====
--- 1.10/drivers/pci/pci-sysfs.c	Fri Jun  4 09:23:04 2004
+++ edited/drivers/pci/pci-sysfs.c	Thu Aug  5 00:45:04 2004
@@ -164,6 +164,95 @@
 	return count;
 }
 
+/**
+ * pci_enable_rom - enable ROM decoding for a PCI device
+ * @dev: PCI device to enable
+ *
+ * Enable ROM decoding on @dev.  This involves simply turning on the last
+ * bit of the PCI ROM BAR.  Note that some cards may share address decoders
+ * between the ROM and other resources, so enabling it may disable access
+ * to MMIO registers or other card memory.
+ */
+static void
+pci_enable_rom(struct pci_dev *dev)
+{
+	u32 rom_addr;
+	
+	pci_read_config_dword(dev, dev->rom_base_reg, &rom_addr);
+	rom_addr |= PCI_ROM_ADDRESS_ENABLE;
+	pci_write_config_dword(dev, dev->rom_base_reg, rom_addr);
+}
+
+/**
+ * pci_disable_rom - disable ROM decoding for a PCI device
+ * @dev: PCI device to disable
+ *
+ * Disable ROM decoding on a PCI device by turning off the last bit in the
+ * ROM BAR.
+ */
+static void
+pci_disable_rom(struct pci_dev *dev)
+{
+	u32 rom_addr;
+
+	pci_read_config_dword(dev, dev->rom_base_reg, &rom_addr);
+	rom_addr &= ~PCI_ROM_ADDRESS_ENABLE;
+	pci_write_config_dword(dev, dev->rom_base_reg, rom_addr);
+}
+
+/**
+ * pci_read_rom - read a PCI ROM
+ * @kobj: kernel object handle
+ * @buf: where to put the data we read from the ROM
+ * @off: file offset
+ * @count: number of bytes to read
+ *
+ * Put @count bytes starting at @off into @buf from the ROM in the PCI
+ * device corresponding to @kobj.
+ */
+static ssize_t
+pci_read_rom(struct kobject *kobj, char *buf, loff_t off, size_t count)
+{
+	struct pci_dev *dev = to_pci_dev(container_of(kobj,struct device,kobj));
+	loff_t start;
+	size_t size;
+	unsigned char *rom;
+	struct resource *res = &dev->resource[PCI_ROM_RESOURCE];
+
+	if (res->flags & PCI_ROM_SHADOW) {	/* PCI_ROM_SHADOW only set on x86 */
+		start = (unsigned char *)0xC0000; /* primary video rom always starts here */
+		size = 0x20000;			/* cover C000:0 through E000:0 */
+	} else {
+		/* assign the ROM an address if it doesn't have one */
+		if (res->parent == NULL)
+			pci_assign_resource(dev, PCI_ROM_RESOURCE);
+			
+		start = pci_resource_start(dev, PCI_ROM_RESOURCE);
+		size = pci_resource_len(dev, PCI_ROM_RESOURCE);
+		
+		/* Enable ROM space decodes */
+		pci_enable_rom(dev);
+	}
+	if (off >= size)
+		return 0;
+		
+	if (off + count > size)
+		count = size - off;
+	
+	rom = ioremap(start, size);
+	if (rom) {
+		memcpy_fromio(buf, rom + off, count);
+		iounmap(rom);
+	} else
+		count = 0;
+		
+	/* Disable again before continuing, leave enabled if pci=rom */
+	if (!(res->flags & (PCI_ROM_ADDRESS_ENABLE|PCI_ROM_SHADOW)))
+		pci_disable_rom(dev);
+		
+	return count;
+}
+
 static struct bin_attribute pci_config_attr = {
 	.attr =	{
 		.name = "config",
@@ -186,13 +275,49 @@
 	.write = pci_write_config,
 };
 
-void pci_create_sysfs_dev_files (struct pci_dev *pdev)
+static struct bin_attribute rom_attr = {
+	.attr =	{
+		.name = "rom",
+		.mode = S_IRUSR,
+		.owner = THIS_MODULE,
+	},
+	/* .size is set individually for each device, sysfs copies it into dentry */
+	.read = pci_read_rom,
+};
+
+void pci_create_sysfs_dev_files(struct pci_dev *pdev)
 {
 	if (pdev->cfg_size < 4096)
 		sysfs_create_bin_file(&pdev->dev.kobj, &pci_config_attr);
 	else
 		sysfs_create_bin_file(&pdev->dev.kobj, &pcie_config_attr);
 
+	/* If the device has a ROM, try to expose it in sysfs. */
+	if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) {
+		struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+		if (res->flags & PCI_ROM_SHADOW) {
+			rom_attr.size = 0x20000;	/* cover C000:0 through E000:0 */
+		} else
+			rom_attr.size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
+		sysfs_create_bin_file(&pdev->dev.kobj, &rom_attr);
+	}
 	/* add platform-specific attributes */
 	pcibios_add_platform_entries(pdev);
+}
+
+/**
+ * pci_remove_sysfs_dev_files - cleanup PCI specific sysfs files
+ * @pdev: device whose entries we should free
+ *
+ * Cleanup when @pdev is removed from sysfs.
+ */
+void pci_remove_sysfs_dev_files(struct pci_dev *pdev)
+{
+	if (pdev->cfg_size < 4096)
+		sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr);
+	else
+		sysfs_remove_bin_file(&pdev->dev.kobj, &pcie_config_attr);
+
+	if (pci_resource_len(pdev, PCI_ROM_RESOURCE))
+		sysfs_remove_bin_file(&pdev->dev.kobj, &rom_attr);
 }
===== drivers/pci/pci.h 1.12 vs edited =====
--- 1.12/drivers/pci/pci.h	Fri Jun  4 09:23:04 2004
+++ edited/drivers/pci/pci.h	Tue Aug  3 17:05:19 2004
@@ -3,6 +3,7 @@
 extern int pci_hotplug (struct device *dev, char **envp, int num_envp,
 			 char *buffer, int buffer_size);
 extern void pci_create_sysfs_dev_files(struct pci_dev *pdev);
+extern void pci_remove_sysfs_dev_files(struct pci_dev *pdev);
 extern int pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
 				  unsigned long size, unsigned long align,
 				  unsigned long min, unsigned int type_mask,
===== drivers/pci/probe.c 1.65 vs edited =====
--- 1.65/drivers/pci/probe.c	Fri May 21 14:45:27 2004
+++ edited/drivers/pci/probe.c	Thu Aug  5 00:10:06 2004
@@ -157,6 +157,7 @@
 #endif
 		}
 	}
+
 	if (rom) {
 		dev->rom_base_reg = rom;
 		res = &dev->resource[PCI_ROM_RESOURCE];
===== drivers/pci/remove.c 1.3 vs edited =====
--- 1.3/drivers/pci/remove.c	Tue Feb  3 12:17:30 2004
+++ edited/drivers/pci/remove.c	Thu Aug  5 00:11:05 2004
@@ -26,6 +26,7 @@
 static void pci_destroy_dev(struct pci_dev *dev)
 {
 	pci_proc_detach_device(dev);
+	pci_remove_sysfs_dev_files(dev);
 	device_unregister(&dev->dev);
 
 	/* Remove the device from the device lists, and prevent any further
===== include/linux/pci.h 1.132 vs edited =====
--- 1.132/include/linux/pci.h	Mon Aug  2 04:00:43 2004
+++ edited/include/linux/pci.h	Thu Aug  5 00:13:54 2004
@@ -102,6 +102,8 @@
 #define PCI_SUBSYSTEM_ID	0x2e  
 #define PCI_ROM_ADDRESS		0x30	/* Bits 31..11 are address, 10..1 reserved */
 #define  PCI_ROM_ADDRESS_ENABLE	0x01
+#define  PCI_ROM_SHADOW		0x02	/* resource flag, ROM is copy at C000:0 */
+#define  PCI_ROM_COPY		0x04	/* resource flag, ROM is alloc'd copy */
 #define PCI_ROM_ADDRESS_MASK	(~0x7ffUL)
 
 #define PCI_CAPABILITY_LIST	0x34	/* Offset of first capability list entry */

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-05  5:05                         ` Jon Smirl
@ 2004-08-05  5:41                           ` Benjamin Herrenschmidt
  2004-08-05 11:53                             ` Jon Smirl
  2004-08-05 15:54                           ` Jesse Barnes
  1 sibling, 1 reply; 110+ messages in thread
From: Benjamin Herrenschmidt @ 2004-08-05  5:41 UTC (permalink / raw)
  To: Jon Smirl
  Cc: Martin Mares, Jesse Barnes, linux-pci, Alan Cox,
	Linux Kernel Mailing List, Petr Vandrovec

On Thu, 2004-08-05 at 15:05, Jon Smirl wrote:
> Version 10
> 
> implements an x86 quirk to record the boot video device. Is the
> PCI_ROM_SHADOW flag a safe define? Quirk records boot video device by
> looking at how the bridges route to the VGA device. It there some other
> way to tell which video card is the boot one? What if there is more
> than one VGA card on the PCI bus? I think the BIOS spec is to enable
> the one in the lowest slot number. Can someone who own multiple PCI
> video cards test this? I tested with one PCI, one AGP.
> 
> BenH, this should solve the problem of which video card owns the ROM
> copy at C000:0. For the boot device this code returns the shadow copy,
> else the real ROM on the card.

Looks ok for me. It would be nice though if the code returning the
shadow copy could be "hooked" by the driver, that way, the radeon
kernel driver can force-enable the ROM decoding... or do you want
to use pci quirks for that too ?

> I did the x86 quirk, what do the quirks on ia64, ppc, x86_64 need? Can
> they just copy the x86 one?

Probably... can't we have arch-independant quirks ? Especially with the
new quirk section stuff David just posted, we can have quirks pretty much
anywhere...

Ben.



^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-05  5:41                           ` Benjamin Herrenschmidt
@ 2004-08-05 11:53                             ` Jon Smirl
  0 siblings, 0 replies; 110+ messages in thread
From: Jon Smirl @ 2004-08-05 11:53 UTC (permalink / raw)
  To: Benjamin Herrenschmidt
  Cc: Martin Mares, Jesse Barnes, linux-pci, Alan Cox,
	Linux Kernel Mailing List, Petr Vandrovec

--- Benjamin Herrenschmidt <benh@kernel.crashing.org> wrote:
> Looks ok for me. It would be nice though if the code returning the
> shadow copy could be "hooked" by the driver, that way, the radeon
> kernel driver can force-enable the ROM decoding... or do you want
> to use pci quirks for that too ?

My current plan is for the radeon driver to keep the code for enabling
the ROM. Otherwise we have to build all of the radeon PCI IDs somewhere
else into the kernel. In my system if you dump if without the driver
loaded it is FFFF, load the driver and you see it. First thing the
driver does is fix the ROM so that it is visible to the hotplug event.

> > I did the x86 quirk, what do the quirks on ia64, ppc, x86_64 need?
> Can
> > they just copy the x86 one?
> 
> Probably... can't we have arch-independant quirks ? Especially with
> the new quirk section stuff David just posted, we can have quirks
> pretty much anywhere...

But isn't this architecture specific? The Mac doesn't shadow ROMs at
0xC0000 does it? How does it shadow ROMs?

I may change the API a little today and expose a map/unmap ROM function
to make things easier for the driver to get the right copy. Adding
those calls will let me add a pci_map_rom_copy, which the driver can
use to trigger the copy for ROMs that aren't fully decoded.

=====
Jon Smirl
jonsmirl@yahoo.com


		
__________________________________
Do you Yahoo!?
Yahoo! Mail - Helps protect you from nasty viruses.
http://promotions.yahoo.com/new_mail

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-05  5:05                         ` Jon Smirl
  2004-08-05  5:41                           ` Benjamin Herrenschmidt
@ 2004-08-05 15:54                           ` Jesse Barnes
  2004-08-05 16:25                             ` Jesse Barnes
  1 sibling, 1 reply; 110+ messages in thread
From: Jesse Barnes @ 2004-08-05 15:54 UTC (permalink / raw)
  To: Jon Smirl
  Cc: Martin Mares, linux-pci, Alan Cox, Linux Kernel Mailing List,
	Petr Vandrovec, Benjamin Herrenschmidt

On Wednesday, August 4, 2004 10:05 pm, Jon Smirl wrote:
> Version 10

For some reason this version doesn't work on ia64.  It just returns bytes 
containing 0 when I try to dump the ROM.

Jesse

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-05 15:54                           ` Jesse Barnes
@ 2004-08-05 16:25                             ` Jesse Barnes
  2004-08-05 20:45                               ` Jon Smirl
  0 siblings, 1 reply; 110+ messages in thread
From: Jesse Barnes @ 2004-08-05 16:25 UTC (permalink / raw)
  To: Jon Smirl
  Cc: Martin Mares, linux-pci, Alan Cox, Linux Kernel Mailing List,
	Petr Vandrovec, Benjamin Herrenschmidt

On Thursday, August 5, 2004 8:54 am, Jesse Barnes wrote:
> On Wednesday, August 4, 2004 10:05 pm, Jon Smirl wrote:
> > Version 10
>
> For some reason this version doesn't work on ia64.  It just returns bytes
> containing 0 when I try to dump the ROM.

pci_assign_resource is mucking with the values in 
pci_dev->resource[PCI_ROM_RESOURCE].  If I remove the call to 
pci_assign_resource, things work for me.  Is that call really necessary?  
Don't we just need ioremap?

Thanks,
Jesse

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-05 16:25                             ` Jesse Barnes
@ 2004-08-05 20:45                               ` Jon Smirl
  2004-08-05 21:12                                 ` Jesse Barnes
  0 siblings, 1 reply; 110+ messages in thread
From: Jon Smirl @ 2004-08-05 20:45 UTC (permalink / raw)
  To: Jesse Barnes
  Cc: Martin Mares, linux-pci, Alan Cox, Linux Kernel Mailing List,
	Petr Vandrovec, Benjamin Herrenschmidt

--- Jesse Barnes <jbarnes@engr.sgi.com> wrote:
> pci_assign_resource is mucking with the values in 
> pci_dev->resource[PCI_ROM_RESOURCE].  If I remove the call to 
> pci_assign_resource, things work for me.  Is that call really
> necessary?  
> Don't we just need ioremap?

/* assign the ROM an address if it doesn't have one */
if (res->parent == NULL)
	pci_assign_resource(dev, PCI_ROM_RESOURCE);

It is protected by the (res->parent == NULL). Looking at the code in
kernel/resource.c this is the correct check to see if the resource does
not have an address assigned. If (res->parent != NULL) then it is
supposed to muck with the addresses.

If you follow the code path of pci_assign_resource() it will program
the ROM to appear at the newly assigned address in
pci_update_resource(). I'd check these code paths and see if they are
64 bit broken. This process does work on ia32.

If you can read the ROM without a resource assigned it is just luck
that everything is still in the same place as boot. If you start
hotpluging the original ROM address could get used by another card
since is is not actively assigned.

I didn't check the error code from pci_assign_resource(). If it can't
match the PREFETCH type it will fail. That may be what is happening.
I'll add a check.




=====
Jon Smirl
jonsmirl@yahoo.com


	
		
__________________________________
Do you Yahoo!?
New and Improved Yahoo! Mail - 100MB free storage!
http://promotions.yahoo.com/new_mail 

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-05 20:45                               ` Jon Smirl
@ 2004-08-05 21:12                                 ` Jesse Barnes
  2004-08-06 21:14                                   ` Jon Smirl
  0 siblings, 1 reply; 110+ messages in thread
From: Jesse Barnes @ 2004-08-05 21:12 UTC (permalink / raw)
  To: Jon Smirl
  Cc: Martin Mares, linux-pci, Alan Cox, Linux Kernel Mailing List,
	Petr Vandrovec, Benjamin Herrenschmidt

On Thursday, August 5, 2004 1:45 pm, Jon Smirl wrote:
> If you follow the code path of pci_assign_resource() it will program
> the ROM to appear at the newly assigned address in
> pci_update_resource(). I'd check these code paths and see if they are
> 64 bit broken. This process does work on ia32.

I think just my platform is broken (i.e. pci_assign_resource doesn't do the 
right thing on sn2).  I'll take a look.

> If you can read the ROM without a resource assigned it is just luck
> that everything is still in the same place as boot. If you start
> hotpluging the original ROM address could get used by another card
> since is is not actively assigned.

Right.

Thanks,
Jesse

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-05 21:12                                 ` Jesse Barnes
@ 2004-08-06 21:14                                   ` Jon Smirl
  2004-08-06 22:33                                     ` Jesse Barnes
  2004-08-11 17:04                                     ` Jesse Barnes
  0 siblings, 2 replies; 110+ messages in thread
From: Jon Smirl @ 2004-08-06 21:14 UTC (permalink / raw)
  To: Jesse Barnes
  Cc: Martin Mares, linux-pci, Alan Cox, Linux Kernel Mailing List,
	Petr Vandrovec, Benjamin Herrenschmidt

[-- Attachment #1: Type: text/plain, Size: 1781 bytes --]

Version 11

I removed the call to pci_alloc_resource(). The sysfs attribute code
builds the attributes before the pci subsystem is fully initialized.
Specifically before arch pcibios_init() has been called. If
pci_alloc_resource() is called for the ROM before pcibios_init() the
kernel's resource maps have not been built yet. This will result in the
ROM being located on top of the framebuffer; as soon as it is enabled
the system will lock. Right now the code relies on the BIOS getting
things set up right. If we can figure out how to initialize the sysfs
attributes after pcibios_init() then I can put the alloc call back.

Changes the API to make it useful from a device driver:

pci_map_rom - map the rom and provide virtual address, transparently
return shadow/copy if needed. Normal drivers call this

pci_map_rom_copy - same as pci_map_rom except the ROM will copied,
future reads of ROM will use copy. Hardware with minimal decoding calls
this

pci_unmap_rom - release the ioremap if there is one

If the ROM code finds the 55 AA signature at the start of the ROM it
will use the len/512 from the third byte for the length. The PCI window
is often much larger than the ROM really is so this saves a lot of
memory for copied ROMs.

I added two new defines in pci.h
#define  PCI_ROM_SHADOW 0x02 /* resource flag, ROM is copy at C000:0 */
#define  PCI_ROM_COPY   0x04 /* resource flag, ROM is alloc'd copy */
I don't think these conflict with anything, please check to make sure.

Please check the code out and give it some testing. It will probably
needs some adjustment for other platforms.

=====
Jon Smirl
jonsmirl@yahoo.com


		
__________________________________
Do you Yahoo!?
Read only the mail you want - Yahoo! Mail SpamGuard.
http://promotions.yahoo.com/new_mail 

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: pci-sysfs-rom-11.patch --]
[-- Type: text/x-patch; name="pci-sysfs-rom-11.patch", Size: 10553 bytes --]

===== arch/i386/pci/fixup.c 1.19 vs edited =====
--- 1.19/arch/i386/pci/fixup.c	Thu Jun  3 10:58:17 2004
+++ edited/arch/i386/pci/fixup.c	Thu Aug  5 00:20:08 2004
@@ -237,6 +237,29 @@
 	}
 }
 
+static void __devinit pci_fixup_video(struct pci_dev *pdev)
+{
+	struct pci_dev *bridge;
+	struct pci_bus *bus;
+	u16 l;
+	
+	if ((pdev->class >> 8) != PCI_CLASS_DISPLAY_VGA)
+		return;
+	       
+	/* Is VGA routed to us? */
+	bus = pdev->bus;
+	while (bus) {
+		bridge = bus->self;
+		if (bridge) {
+			pci_read_config_word(bridge, PCI_BRIDGE_CONTROL, &l);
+			if (!(l & PCI_BRIDGE_CTL_VGA))
+				return;
+		}
+		bus = bus->parent;
+	}
+	pdev->resource[PCI_ROM_RESOURCE].flags |= PCI_ROM_SHADOW;
+}
+
 struct pci_fixup pcibios_fixups[] = {
 	{
 		.pass		= PCI_FIXUP_HEADER,
@@ -345,6 +368,12 @@
 		.vendor		= PCI_VENDOR_ID_NVIDIA,
 		.device		= PCI_DEVICE_ID_NVIDIA_NFORCE2,
 		.hook		= pci_fixup_nforce2
+	},
+	{
+		.pass		= PCI_FIXUP_FINAL,
+		.vendor		= PCI_ANY_ID,
+		.device		= PCI_ANY_ID,
+		.hook		= pci_fixup_video
 	},
 	{ .pass = 0 }
 };
===== drivers/pci/pci-sysfs.c 1.10 vs edited =====
--- 1.10/drivers/pci/pci-sysfs.c	Fri Jun  4 09:23:04 2004
+++ edited/drivers/pci/pci-sysfs.c	Fri Aug  6 16:38:00 2004
@@ -5,6 +5,7 @@
  * (C) Copyright 2002-2004 IBM Corp.
  * (C) Copyright 2003 Matthew Wilcox
  * (C) Copyright 2003 Hewlett-Packard
+ * (C) Copyright 2004 Jon Smirl
  *
  * File attributes for PCI devices
  *
@@ -164,6 +165,176 @@
 	return count;
 }
 
+/**
+ * pci_enable_rom - enable ROM decoding for a PCI device
+ * @dev: PCI device to enable
+ *
+ * Enable ROM decoding on @dev.  This involves simply turning on the last
+ * bit of the PCI ROM BAR.  Note that some cards may share address decoders
+ * between the ROM and other resources, so enabling it may disable access
+ * to MMIO registers or other card memory.
+ */
+static void
+pci_enable_rom(struct pci_dev *dev)
+{
+	u32 rom_addr;
+	
+	pci_read_config_dword(dev, dev->rom_base_reg, &rom_addr);
+	rom_addr |= PCI_ROM_ADDRESS_ENABLE;
+	pci_write_config_dword(dev, dev->rom_base_reg, rom_addr);
+}
+
+/**
+ * pci_disable_rom - disable ROM decoding for a PCI device
+ * @dev: PCI device to disable
+ *
+ * Disable ROM decoding on a PCI device by turning off the last bit in the
+ * ROM BAR.
+ */
+static void
+pci_disable_rom(struct pci_dev *dev)
+{
+	u32 rom_addr;
+	pci_read_config_dword(dev, dev->rom_base_reg, &rom_addr);
+	rom_addr &= ~PCI_ROM_ADDRESS_ENABLE;
+	pci_write_config_dword(dev, dev->rom_base_reg, rom_addr);
+}
+
+/**
+ * pci_map_rom - map a PCI ROM to kernel space
+ * @dev: pointer to pci device struct
+ * @size: pointer to receive size of pci window over ROM
+ * @return: kernel virtual pointer to image of ROM
+ *
+ * Map a PCI ROM into kernel space. If ROM is boot video ROM,
+ * the shadow BIOS copy will be returned instead of the 
+ * actual ROM.
+ */
+unsigned char *
+pci_map_rom(struct pci_dev *dev, size_t *size) {
+	struct resource *res = &dev->resource[PCI_ROM_RESOURCE];
+	loff_t start;
+	unsigned char *rom;
+	
+	if (res->flags & PCI_ROM_SHADOW) {	/* PCI_ROM_SHADOW only set on x86 */
+		start = (loff_t)0xC0000; 	/* primary video rom always starts here */
+		*size = 0x20000;		/* cover C000:0 through E000:0 */
+	} else if (res->flags & PCI_ROM_COPY) {
+		*size = pci_resource_len(dev, PCI_ROM_RESOURCE);
+		return (unsigned char *)pci_resource_start(dev, PCI_ROM_RESOURCE);
+	} else {
+		start = pci_resource_start(dev, PCI_ROM_RESOURCE);
+		*size = pci_resource_len(dev, PCI_ROM_RESOURCE);
+		if (*size == 0)
+			return NULL;
+		
+		/* Enable ROM space decodes */
+		pci_enable_rom(dev);
+	}
+	
+	rom = ioremap(start, *size);
+	if (!rom) {
+		/* restore enable if ioremap fails */
+		if (!(res->flags & (PCI_ROM_ADDRESS_ENABLE | PCI_ROM_SHADOW | PCI_ROM_COPY)))
+			pci_disable_rom(dev);
+		return NULL;
+	}		
+	/* Standard PCI ROMs start out with these three bytes 55 AA size/512 */
+	if ((*rom == 0x55) && (*(rom + 1) == 0xAA))
+		*size = *(rom + 2) * 512;	/* return true ROM size, not PCI window size */
+		
+	return rom;
+}
+
+/**
+ * pci_map_rom_copy - map a PCI ROM to kernel space, create a copy
+ * @dev: pointer to pci device struct
+ * @size: pointer to receive size of pci window over ROM
+ * @return: kernel virtual pointer to image of ROM
+ *
+ * Map a PCI ROM into kernel space. If ROM is boot video ROM,
+ * the shadow BIOS copy will be returned instead of the 
+ * actual ROM.
+ */
+unsigned char *
+pci_map_rom_copy(struct pci_dev *dev, size_t *size) {
+	struct resource *res = &dev->resource[PCI_ROM_RESOURCE];
+	unsigned char *rom;
+	
+	rom = pci_map_rom(dev, size);
+	if (!rom)
+		return NULL;
+		
+	if (res->flags & (PCI_ROM_COPY | PCI_ROM_SHADOW))
+		return rom;
+		
+	res->start = (unsigned long)kmalloc(*size, GFP_KERNEL);
+	if (!res->start) 
+		return rom;
+
+	res->end = res->start + *size; 
+	memcpy((void*)res->start, rom, *size);
+	pci_unmap_rom(dev, rom);
+	res->flags |= PCI_ROM_COPY;
+	
+	return (unsigned char *)res->start;
+}
+
+/**
+ * pci_unmap_rom - unmap the ROM from kernel space
+ * @dev: pointer to pci device struct
+ * @rom: virtual address of the previous mapping
+ *
+ * Remove a mapping of a previously mapped ROM
+ */
+void 
+pci_unmap_rom(struct pci_dev *dev, unsigned char *rom) {
+	struct resource *res = &dev->resource[PCI_ROM_RESOURCE];
+
+	if (res->flags & PCI_ROM_COPY)
+		return;
+		
+	iounmap(rom);
+		
+	/* Disable again before continuing, leave enabled if pci=rom */
+	if (!(res->flags & (PCI_ROM_ADDRESS_ENABLE | PCI_ROM_SHADOW)))
+		pci_disable_rom(dev);
+}
+
+/**
+ * pci_read_rom - read a PCI ROM
+ * @kobj: kernel object handle
+ * @buf: where to put the data we read from the ROM
+ * @off: file offset
+ * @count: number of bytes to read
+ *
+ * Put @count bytes starting at @off into @buf from the ROM in the PCI
+ * device corresponding to @kobj.
+ */
+static ssize_t
+pci_read_rom(struct kobject *kobj, char *buf, loff_t off, size_t count)
+{
+	struct pci_dev *dev = to_pci_dev(container_of(kobj,struct device,kobj));
+	unsigned char *rom;
+	size_t size;
+	
+	rom = pci_map_rom(dev, &size);	/* size starts out as PCI window size */
+	if (!rom)
+		return 0;
+		
+	if (off >= size)
+		count = 0;
+	else {
+		if (off + count > size)
+			count = size - off;
+		
+		memcpy_fromio(buf, rom + off, count);
+	}
+	pci_unmap_rom(dev, rom);
+		
+	return count;
+}
+
 static struct bin_attribute pci_config_attr = {
 	.attr =	{
 		.name = "config",
@@ -186,13 +357,53 @@
 	.write = pci_write_config,
 };
 
-void pci_create_sysfs_dev_files (struct pci_dev *pdev)
+static struct bin_attribute rom_attr = {
+	.attr =	{
+		.name = "rom",
+		.mode = S_IRUSR,
+		.owner = THIS_MODULE,
+	},
+	/* .size is set individually for each device, sysfs copies it into dentry */
+	.read = pci_read_rom,
+};
+
+void pci_create_sysfs_dev_files(struct pci_dev *pdev)
 {
 	if (pdev->cfg_size < 4096)
 		sysfs_create_bin_file(&pdev->dev.kobj, &pci_config_attr);
 	else
 		sysfs_create_bin_file(&pdev->dev.kobj, &pcie_config_attr);
 
+	/* If the device has a ROM, try to expose it in sysfs. */
+	if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) {
+		unsigned char *rom;
+		rom = pci_map_rom(pdev, &rom_attr.size);
+		if (rom) {
+			pci_unmap_rom(pdev, rom);
+			sysfs_create_bin_file(&pdev->dev.kobj, &rom_attr);
+		}
+	}
 	/* add platform-specific attributes */
 	pcibios_add_platform_entries(pdev);
 }
+
+/**
+ * pci_remove_sysfs_dev_files - cleanup PCI specific sysfs files
+ * @pdev: device whose entries we should free
+ *
+ * Cleanup when @pdev is removed from sysfs.
+ */
+void pci_remove_sysfs_dev_files(struct pci_dev *pdev)
+{
+	if (pdev->cfg_size < 4096)
+		sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr);
+	else
+		sysfs_remove_bin_file(&pdev->dev.kobj, &pcie_config_attr);
+
+	if (pci_resource_len(pdev, PCI_ROM_RESOURCE))
+		sysfs_remove_bin_file(&pdev->dev.kobj, &rom_attr);
+}
+
+EXPORT_SYMBOL(pci_map_rom);
+EXPORT_SYMBOL(pci_map_rom_copy);
+EXPORT_SYMBOL(pci_unmap_rom);
===== drivers/pci/pci.h 1.12 vs edited =====
--- 1.12/drivers/pci/pci.h	Fri Jun  4 09:23:04 2004
+++ edited/drivers/pci/pci.h	Tue Aug  3 17:05:19 2004
@@ -3,6 +3,7 @@
 extern int pci_hotplug (struct device *dev, char **envp, int num_envp,
 			 char *buffer, int buffer_size);
 extern void pci_create_sysfs_dev_files(struct pci_dev *pdev);
+extern void pci_remove_sysfs_dev_files(struct pci_dev *pdev);
 extern int pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
 				  unsigned long size, unsigned long align,
 				  unsigned long min, unsigned int type_mask,
===== drivers/pci/remove.c 1.3 vs edited =====
--- 1.3/drivers/pci/remove.c	Tue Feb  3 12:17:30 2004
+++ edited/drivers/pci/remove.c	Fri Aug  6 15:36:18 2004
@@ -12,12 +12,22 @@
 
 static void pci_free_resources(struct pci_dev *dev)
 {
+	struct resource *res;
 	int i;
 
  	msi_remove_pci_irq_vectors(dev);
 
+	if (dev->hdr_type == PCI_HEADER_TYPE_NORMAL) {
+		res = &dev->resource[PCI_ROM_RESOURCE];
+		if (res->flags & PCI_ROM_COPY) {
+			kfree((void*)res->start);
+			res->flags &= !PCI_ROM_COPY;
+			res->start = 0;
+			res->end = 0;
+		}
+	}
 	for (i = 0; i < PCI_NUM_RESOURCES; i++) {
-		struct resource *res = dev->resource + i;
+		res = dev->resource + i;
 		if (res->parent)
 			release_resource(res);
 	}
@@ -26,6 +36,7 @@
 static void pci_destroy_dev(struct pci_dev *dev)
 {
 	pci_proc_detach_device(dev);
+	pci_remove_sysfs_dev_files(dev);
 	device_unregister(&dev->dev);
 
 	/* Remove the device from the device lists, and prevent any further
===== include/linux/pci.h 1.132 vs edited =====
--- 1.132/include/linux/pci.h	Mon Aug  2 04:00:43 2004
+++ edited/include/linux/pci.h	Fri Aug  6 00:34:27 2004
@@ -102,6 +102,8 @@
 #define PCI_SUBSYSTEM_ID	0x2e  
 #define PCI_ROM_ADDRESS		0x30	/* Bits 31..11 are address, 10..1 reserved */
 #define  PCI_ROM_ADDRESS_ENABLE	0x01
+#define  PCI_ROM_SHADOW		0x02	/* resource flag, ROM is copy at C000:0 */
+#define  PCI_ROM_COPY		0x04	/* resource flag, ROM is alloc'd copy */
 #define PCI_ROM_ADDRESS_MASK	(~0x7ffUL)
 
 #define PCI_CAPABILITY_LIST	0x34	/* Offset of first capability list entry */
@@ -777,6 +779,9 @@
 int pci_dac_set_dma_mask(struct pci_dev *dev, u64 mask);
 int pci_set_consistent_dma_mask(struct pci_dev *dev, u64 mask);
 int pci_assign_resource(struct pci_dev *dev, int i);
+unsigned char *pci_map_rom(struct pci_dev *dev, size_t *size);
+unsigned char *pci_map_rom_copy(struct pci_dev *dev, size_t *size);
+void pci_unmap_rom(struct pci_dev *dev, unsigned char *rom);
 
 /* Power management related routines */
 int pci_save_state(struct pci_dev *dev, u32 *buffer);

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-06 21:14                                   ` Jon Smirl
@ 2004-08-06 22:33                                     ` Jesse Barnes
  2004-08-11 17:04                                     ` Jesse Barnes
  1 sibling, 0 replies; 110+ messages in thread
From: Jesse Barnes @ 2004-08-06 22:33 UTC (permalink / raw)
  To: Jon Smirl
  Cc: Martin Mares, linux-pci, Alan Cox, Linux Kernel Mailing List,
	Petr Vandrovec, Benjamin Herrenschmidt

On Friday, August 6, 2004 2:14 pm, Jon Smirl wrote:
> attributes after pcibios_init() then I can put the alloc call back.

I've fixed (well, hacked) the sn2 code such that your original check for 
res->parent will work correctly for me too, so it sounds like we're covered 
there.

> Please check the code out and give it some testing. It will probably
> needs some adjustment for other platforms.

Thanks, will do (though probably not until next week, I'm about to head out 
for the weekend).

Thanks,
Jesse

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-06 21:14                                   ` Jon Smirl
  2004-08-06 22:33                                     ` Jesse Barnes
@ 2004-08-11 17:04                                     ` Jesse Barnes
  2004-08-11 17:28                                       ` Greg KH
  1 sibling, 1 reply; 110+ messages in thread
From: Jesse Barnes @ 2004-08-11 17:04 UTC (permalink / raw)
  To: Jon Smirl
  Cc: greg, Martin Mares, linux-pci, Alan Cox,
	Linux Kernel Mailing List, Petr Vandrovec,
	Benjamin Herrenschmidt

On Friday, August 6, 2004 2:14 pm, Jon Smirl wrote:
> Please check the code out and give it some testing. It will probably
> needs some adjustment for other platforms.

Jon, this works on my machine too.  Greg, if it looks ok can you pull it in?  
And can you add:

 * (C) Copyright 2004 Silicon Graphics, Inc.
 *       Jesse Barnes <jbarnes@sgi.com>

to pci-sysfs.c if you do?

Greg was a little worried that your comment
	/* .size is set individually for each device, sysfs copies it into dentry */
might not be correct.  If not, you can reuse the code from my 
pci-sysfs-rom-7.patch if you want, otherwise it's probably fine aside from a 
bunch of trailing whitespace in the file.  I added the following to my .emacs 
to make it obvious so that I could kill it when I saw it:
(defun linux-c-mode ()
  "C mode with Linux kernel defaults"
  (interactive)
  (c-mode)
  (c-set-style "K&R")
  (setq c-basic-offset 8)
  (setq indent-tabs-mode t)
  (setq show-trailing-whitespace t))

Thanks,
Jesse

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-11 17:04                                     ` Jesse Barnes
@ 2004-08-11 17:28                                       ` Greg KH
  2004-08-11 18:02                                         ` Jesse Barnes
                                                           ` (2 more replies)
  0 siblings, 3 replies; 110+ messages in thread
From: Greg KH @ 2004-08-11 17:28 UTC (permalink / raw)
  To: Jesse Barnes
  Cc: Jon Smirl, Martin Mares, linux-pci, Alan Cox,
	Linux Kernel Mailing List, Petr Vandrovec,
	Benjamin Herrenschmidt

On Wed, Aug 11, 2004 at 10:04:02AM -0700, Jesse Barnes wrote:
> On Friday, August 6, 2004 2:14 pm, Jon Smirl wrote:
> > Please check the code out and give it some testing. It will probably
> > needs some adjustment for other platforms.
> 
> Jon, this works on my machine too.  Greg, if it looks ok can you pull it in?  
> And can you add:
> 
>  * (C) Copyright 2004 Silicon Graphics, Inc.
>  *       Jesse Barnes <jbarnes@sgi.com>
> 
> to pci-sysfs.c if you do?

Care to send me a new patch?  Oh, and that copyright line needs to look
like:
* Copyright (c) 2004 Silicon Graphics, Inc. Jesse Barnes <jbarnes@sgi.com>

to make it legal, or so my lawyers say :)

> Greg was a little worried that your comment
> 	/* .size is set individually for each device, sysfs copies it into dentry */
> might not be correct.

I looked at the code, and he's right.  But it's pretty scary that it
works correctly so I'd prefer to do it the way your patch did it (create
a new attribute for every entry.)

thnaks,

greg k-h

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-11 17:28                                       ` Greg KH
@ 2004-08-11 18:02                                         ` Jesse Barnes
  2004-08-11 18:12                                           ` Greg KH
  2004-08-11 19:24                                         ` Jon Smirl
  2004-08-12  0:45                                         ` Jon Smirl
  2 siblings, 1 reply; 110+ messages in thread
From: Jesse Barnes @ 2004-08-11 18:02 UTC (permalink / raw)
  To: Greg KH
  Cc: Jon Smirl, Martin Mares, linux-pci, Alan Cox,
	Linux Kernel Mailing List, Petr Vandrovec,
	Benjamin Herrenschmidt

On Wednesday, August 11, 2004 10:28 am, Greg KH wrote:
> On Wed, Aug 11, 2004 at 10:04:02AM -0700, Jesse Barnes wrote:
> > On Friday, August 6, 2004 2:14 pm, Jon Smirl wrote:
> > > Please check the code out and give it some testing. It will probably
> > > needs some adjustment for other platforms.
> >
> > Jon, this works on my machine too.  Greg, if it looks ok can you pull it
> > in? And can you add:
> >
> >  * (C) Copyright 2004 Silicon Graphics, Inc.
> >  *       Jesse Barnes <jbarnes@sgi.com>
> >
> > to pci-sysfs.c if you do?
>
> Care to send me a new patch?  Oh, and that copyright line needs to look
> like:
> * Copyright (c) 2004 Silicon Graphics, Inc. Jesse Barnes <jbarnes@sgi.com>
>
> to make it legal, or so my lawyers say :)

But I'm not the copyright holder, Silicon Graphics is, I just wanted people to 
know who to harass if something breaks :).

> > Greg was a little worried that your comment
> > 	/* .size is set individually for each device, sysfs copies it into
> > dentry */ might not be correct.
>
> I looked at the code, and he's right.  But it's pretty scary that it
> works correctly so I'd prefer to do it the way your patch did it (create
> a new attribute for every entry.)

Ok.  Jon?

Thanks,
Jesse

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-11 18:02                                         ` Jesse Barnes
@ 2004-08-11 18:12                                           ` Greg KH
  2004-08-12  1:28                                             ` Marcelo Tosatti
  2004-08-12  2:25                                             ` Miles Bader
  0 siblings, 2 replies; 110+ messages in thread
From: Greg KH @ 2004-08-11 18:12 UTC (permalink / raw)
  To: Jesse Barnes; +Cc: linux-pci, Linux Kernel Mailing List

On Wed, Aug 11, 2004 at 11:02:10AM -0700, Jesse Barnes wrote:
> On Wednesday, August 11, 2004 10:28 am, Greg KH wrote:
> > On Wed, Aug 11, 2004 at 10:04:02AM -0700, Jesse Barnes wrote:
> > > On Friday, August 6, 2004 2:14 pm, Jon Smirl wrote:
> > > > Please check the code out and give it some testing. It will probably
> > > > needs some adjustment for other platforms.
> > >
> > > Jon, this works on my machine too.  Greg, if it looks ok can you pull it
> > > in? And can you add:
> > >
> > >  * (C) Copyright 2004 Silicon Graphics, Inc.
> > >  *       Jesse Barnes <jbarnes@sgi.com>
> > >
> > > to pci-sysfs.c if you do?
> >
> > Care to send me a new patch?  Oh, and that copyright line needs to look
> > like:
> > * Copyright (c) 2004 Silicon Graphics, Inc. Jesse Barnes <jbarnes@sgi.com>
> >
> > to make it legal, or so my lawyers say :)
> 
> But I'm not the copyright holder, Silicon Graphics is, I just wanted people to 
> know who to harass if something breaks :).

That's fine.  It's the "Copyright (c) 2004" order and exact "(c)" that
really matters, from what I have been told to do.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-11 17:28                                       ` Greg KH
  2004-08-11 18:02                                         ` Jesse Barnes
@ 2004-08-11 19:24                                         ` Jon Smirl
  2004-08-11 19:44                                           ` Jesse Barnes
  2004-08-11 20:11                                           ` Alan Cox
  2004-08-12  0:45                                         ` Jon Smirl
  2 siblings, 2 replies; 110+ messages in thread
From: Jon Smirl @ 2004-08-11 19:24 UTC (permalink / raw)
  To: Greg KH, Jesse Barnes, Benjamin Herrenschmidt
  Cc: Martin Mares, linux-pci, Alan Cox, Linux Kernel Mailing List,
	Petr Vandrovec, Benjamin Herrenschmidt

[-- Attachment #1: Type: text/plain, Size: 2262 bytes --]

I can put together a new patch later tonight that reverts to the old
size scheme. It was more complicated since you need to allocate each
attribute.

I'll attach a newer version that incorporates a little feedback about
creating a function to remove the attribute for devices that don't want
to expose the ROM.

Alan Cox had concerns about copying the ROMs for those devices that
don't implement full address decoding. I'm using kmalloc for 40-60KB.
Would vmalloc be a better choice? Very few drivers will use the copy
option, mostly old hardware.

BenH said he would check it out on ppc but I haven't heard from him
yet.

Jesse, did you notice that the quirk for tracking the boot video device
is x86 only? I believe this needs to run on ia64 and x86_64 too. How do
we want to do that? It will do the wrong thing on architectures that
don't shadow video ROMs to C0000.

 
--- Greg KH <greg@kroah.com> wrote:

> On Wed, Aug 11, 2004 at 10:04:02AM -0700, Jesse Barnes wrote:
> > On Friday, August 6, 2004 2:14 pm, Jon Smirl wrote:
> > > Please check the code out and give it some testing. It will
> probably
> > > needs some adjustment for other platforms.
> > 
> > Jon, this works on my machine too.  Greg, if it looks ok can you
> pull it in?  
> > And can you add:
> > 
> >  * (C) Copyright 2004 Silicon Graphics, Inc.
> >  *       Jesse Barnes <jbarnes@sgi.com>
> > 
> > to pci-sysfs.c if you do?

I'll add this

> Care to send me a new patch?  Oh, and that copyright line needs to
> look
> like:
> * Copyright (c) 2004 Silicon Graphics, Inc. Jesse Barnes
> <jbarnes@sgi.com>
> 
> to make it legal, or so my lawyers say :)
> 
> > Greg was a little worried that your comment
> > 	/* .size is set individually for each device, sysfs copies it into
> dentry */
> > might not be correct.
> 
> I looked at the code, and he's right.  But it's pretty scary that it
> works correctly so I'd prefer to do it the way your patch did it
> (create
> a new attribute for every entry.)

I can revert back to the old code. The new scheme was just so much
simpler.

> 
> thanks,
> 
> greg k-h
> 

=====
Jon Smirl
jonsmirl@yahoo.com


		
__________________________________
Do you Yahoo!?
Yahoo! Mail - 50x more storage than other providers!
http://promotions.yahoo.com/new_mail

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: pci-sysfs-rom-13.patch --]
[-- Type: text/x-patch; name="pci-sysfs-rom-13.patch", Size: 11116 bytes --]

===== arch/i386/pci/fixup.c 1.19 vs edited =====
--- 1.19/arch/i386/pci/fixup.c	Thu Jun  3 10:58:17 2004
+++ edited/arch/i386/pci/fixup.c	Thu Aug  5 00:20:08 2004
@@ -237,6 +237,29 @@
 	}
 }
 
+static void __devinit pci_fixup_video(struct pci_dev *pdev)
+{
+	struct pci_dev *bridge;
+	struct pci_bus *bus;
+	u16 l;
+	
+	if ((pdev->class >> 8) != PCI_CLASS_DISPLAY_VGA)
+		return;
+	       
+	/* Is VGA routed to us? */
+	bus = pdev->bus;
+	while (bus) {
+		bridge = bus->self;
+		if (bridge) {
+			pci_read_config_word(bridge, PCI_BRIDGE_CONTROL, &l);
+			if (!(l & PCI_BRIDGE_CTL_VGA))
+				return;
+		}
+		bus = bus->parent;
+	}
+	pdev->resource[PCI_ROM_RESOURCE].flags |= PCI_ROM_SHADOW;
+}
+
 struct pci_fixup pcibios_fixups[] = {
 	{
 		.pass		= PCI_FIXUP_HEADER,
@@ -345,6 +368,12 @@
 		.vendor		= PCI_VENDOR_ID_NVIDIA,
 		.device		= PCI_DEVICE_ID_NVIDIA_NFORCE2,
 		.hook		= pci_fixup_nforce2
+	},
+	{
+		.pass		= PCI_FIXUP_FINAL,
+		.vendor		= PCI_ANY_ID,
+		.device		= PCI_ANY_ID,
+		.hook		= pci_fixup_video
 	},
 	{ .pass = 0 }
 };
===== drivers/pci/pci-sysfs.c 1.10 vs edited =====
--- 1.10/drivers/pci/pci-sysfs.c	Fri Jun  4 09:23:04 2004
+++ edited/drivers/pci/pci-sysfs.c	Sat Aug  7 13:37:14 2004
@@ -5,6 +5,7 @@
  * (C) Copyright 2002-2004 IBM Corp.
  * (C) Copyright 2003 Matthew Wilcox
  * (C) Copyright 2003 Hewlett-Packard
+ * (C) Copyright 2004 Jon Smirl
  *
  * File attributes for PCI devices
  *
@@ -164,6 +165,193 @@
 	return count;
 }
 
+/**
+ * pci_enable_rom - enable ROM decoding for a PCI device
+ * @dev: PCI device to enable
+ *
+ * Enable ROM decoding on @dev.  This involves simply turning on the last
+ * bit of the PCI ROM BAR.  Note that some cards may share address decoders
+ * between the ROM and other resources, so enabling it may disable access
+ * to MMIO registers or other card memory.
+ */
+static void
+pci_enable_rom(struct pci_dev *dev)
+{
+	u32 rom_addr;
+	
+	pci_read_config_dword(dev, dev->rom_base_reg, &rom_addr);
+	rom_addr |= PCI_ROM_ADDRESS_ENABLE;
+	pci_write_config_dword(dev, dev->rom_base_reg, rom_addr);
+}
+
+/**
+ * pci_disable_rom - disable ROM decoding for a PCI device
+ * @dev: PCI device to disable
+ *
+ * Disable ROM decoding on a PCI device by turning off the last bit in the
+ * ROM BAR.
+ */
+static void
+pci_disable_rom(struct pci_dev *dev)
+{
+	u32 rom_addr;
+	pci_read_config_dword(dev, dev->rom_base_reg, &rom_addr);
+	rom_addr &= ~PCI_ROM_ADDRESS_ENABLE;
+	pci_write_config_dword(dev, dev->rom_base_reg, rom_addr);
+}
+
+/**
+ * pci_map_rom - map a PCI ROM to kernel space
+ * @dev: pointer to pci device struct
+ * @size: pointer to receive size of pci window over ROM
+ * @return: kernel virtual pointer to image of ROM
+ *
+ * Map a PCI ROM into kernel space. If ROM is boot video ROM,
+ * the shadow BIOS copy will be returned instead of the 
+ * actual ROM.
+ */
+unsigned char *
+pci_map_rom(struct pci_dev *dev, size_t *size) {
+	struct resource *res = &dev->resource[PCI_ROM_RESOURCE];
+	loff_t start;
+	unsigned char *rom;
+	
+	if (res->flags & PCI_ROM_SHADOW) {	/* PCI_ROM_SHADOW only set on x86 */
+		start = (loff_t)0xC0000; 	/* primary video rom always starts here */
+		*size = 0x20000;		/* cover C000:0 through E000:0 */
+	} else if (res->flags & PCI_ROM_COPY) {
+		*size = pci_resource_len(dev, PCI_ROM_RESOURCE);
+		return (unsigned char *)pci_resource_start(dev, PCI_ROM_RESOURCE);
+	} else {
+		start = pci_resource_start(dev, PCI_ROM_RESOURCE);
+		*size = pci_resource_len(dev, PCI_ROM_RESOURCE);
+		if (*size == 0)
+			return NULL;
+		
+		/* Enable ROM space decodes */
+		pci_enable_rom(dev);
+	}
+	
+	rom = ioremap(start, *size);
+	if (!rom) {
+		/* restore enable if ioremap fails */
+		if (!(res->flags & (PCI_ROM_ADDRESS_ENABLE | PCI_ROM_SHADOW | PCI_ROM_COPY)))
+			pci_disable_rom(dev);
+		return NULL;
+	}		
+	/* Standard PCI ROMs start out with these three bytes 55 AA size/512 */
+	if ((*rom == 0x55) && (*(rom + 1) == 0xAA))
+		*size = *(rom + 2) * 512;	/* return true ROM size, not PCI window size */
+		
+	return rom;
+}
+
+/**
+ * pci_map_rom_copy - map a PCI ROM to kernel space, create a copy
+ * @dev: pointer to pci device struct
+ * @size: pointer to receive size of pci window over ROM
+ * @return: kernel virtual pointer to image of ROM
+ *
+ * Map a PCI ROM into kernel space. If ROM is boot video ROM,
+ * the shadow BIOS copy will be returned instead of the 
+ * actual ROM.
+ */
+unsigned char *
+pci_map_rom_copy(struct pci_dev *dev, size_t *size) {
+	struct resource *res = &dev->resource[PCI_ROM_RESOURCE];
+	unsigned char *rom;
+	
+	rom = pci_map_rom(dev, size);
+	if (!rom)
+		return NULL;
+		
+	if (res->flags & (PCI_ROM_COPY | PCI_ROM_SHADOW))
+		return rom;
+		
+	res->start = (unsigned long)kmalloc(*size, GFP_KERNEL);
+	if (!res->start) 
+		return rom;
+
+	res->end = res->start + *size; 
+	memcpy((void*)res->start, rom, *size);
+	pci_unmap_rom(dev, rom);
+	res->flags |= PCI_ROM_COPY;
+	
+	return (unsigned char *)res->start;
+}
+
+/**
+ * pci_unmap_rom - unmap the ROM from kernel space
+ * @dev: pointer to pci device struct
+ * @rom: virtual address of the previous mapping
+ *
+ * Remove a mapping of a previously mapped ROM
+ */
+void 
+pci_unmap_rom(struct pci_dev *dev, unsigned char *rom) {
+	struct resource *res = &dev->resource[PCI_ROM_RESOURCE];
+
+	if (res->flags & PCI_ROM_COPY)
+		return;
+		
+	iounmap(rom);
+		
+	/* Disable again before continuing, leave enabled if pci=rom */
+	if (!(res->flags & (PCI_ROM_ADDRESS_ENABLE | PCI_ROM_SHADOW)))
+		pci_disable_rom(dev);
+}
+
+static struct bin_attribute rom_attr;
+
+/**
+ * pci_remove_rom - disable the ROM and remove it's sysfs attribute
+ * @dev: pointer to pci device struct
+ *
+ */
+void 
+pci_remove_rom(struct pci_dev *dev) {
+	struct resource *res = &dev->resource[PCI_ROM_RESOURCE];
+	
+	if (pci_resource_len(dev, PCI_ROM_RESOURCE))
+		sysfs_remove_bin_file(&dev->dev.kobj, &rom_attr);
+	if (!(res->flags & (PCI_ROM_ADDRESS_ENABLE | PCI_ROM_SHADOW | PCI_ROM_COPY)))
+		pci_disable_rom(dev);
+}
+
+/**
+ * pci_read_rom - read a PCI ROM
+ * @kobj: kernel object handle
+ * @buf: where to put the data we read from the ROM
+ * @off: file offset
+ * @count: number of bytes to read
+ *
+ * Put @count bytes starting at @off into @buf from the ROM in the PCI
+ * device corresponding to @kobj.
+ */
+static ssize_t
+pci_read_rom(struct kobject *kobj, char *buf, loff_t off, size_t count)
+{
+	struct pci_dev *dev = to_pci_dev(container_of(kobj,struct device,kobj));
+	unsigned char *rom;
+	size_t size;
+	
+	rom = pci_map_rom(dev, &size);	/* size starts out as PCI window size */
+	if (!rom)
+		return 0;
+		
+	if (off >= size)
+		count = 0;
+	else {
+		if (off + count > size)
+			count = size - off;
+		
+		memcpy_fromio(buf, rom + off, count);
+	}
+	pci_unmap_rom(dev, rom);
+		
+	return count;
+}
+
 static struct bin_attribute pci_config_attr = {
 	.attr =	{
 		.name = "config",
@@ -186,13 +374,53 @@
 	.write = pci_write_config,
 };
 
-void pci_create_sysfs_dev_files (struct pci_dev *pdev)
+static struct bin_attribute rom_attr = {
+	.attr =	{
+		.name = "rom",
+		.mode = S_IRUSR,
+		.owner = THIS_MODULE,
+	},
+	/* .size is set individually for each device, sysfs copies it into dentry */
+	.read = pci_read_rom,
+};
+
+void pci_create_sysfs_dev_files(struct pci_dev *pdev)
 {
 	if (pdev->cfg_size < 4096)
 		sysfs_create_bin_file(&pdev->dev.kobj, &pci_config_attr);
 	else
 		sysfs_create_bin_file(&pdev->dev.kobj, &pcie_config_attr);
 
+	/* If the device has a ROM, try to expose it in sysfs. */
+	if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) {
+		unsigned char *rom;
+		rom = pci_map_rom(pdev, &rom_attr.size);
+		if (rom) {
+			pci_unmap_rom(pdev, rom);
+			sysfs_create_bin_file(&pdev->dev.kobj, &rom_attr);
+		}
+	}
 	/* add platform-specific attributes */
 	pcibios_add_platform_entries(pdev);
 }
+
+/**
+ * pci_remove_sysfs_dev_files - cleanup PCI specific sysfs files
+ * @pdev: device whose entries we should free
+ *
+ * Cleanup when @pdev is removed from sysfs.
+ */
+void pci_remove_sysfs_dev_files(struct pci_dev *pdev)
+{
+	if (pdev->cfg_size < 4096)
+		sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr);
+	else
+		sysfs_remove_bin_file(&pdev->dev.kobj, &pcie_config_attr);
+
+	if (pci_resource_len(pdev, PCI_ROM_RESOURCE))
+		sysfs_remove_bin_file(&pdev->dev.kobj, &rom_attr);
+}
+
+EXPORT_SYMBOL(pci_map_rom);
+EXPORT_SYMBOL(pci_map_rom_copy);
+EXPORT_SYMBOL(pci_unmap_rom);
===== drivers/pci/pci.h 1.12 vs edited =====
--- 1.12/drivers/pci/pci.h	Fri Jun  4 09:23:04 2004
+++ edited/drivers/pci/pci.h	Tue Aug  3 17:05:19 2004
@@ -3,6 +3,7 @@
 extern int pci_hotplug (struct device *dev, char **envp, int num_envp,
 			 char *buffer, int buffer_size);
 extern void pci_create_sysfs_dev_files(struct pci_dev *pdev);
+extern void pci_remove_sysfs_dev_files(struct pci_dev *pdev);
 extern int pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
 				  unsigned long size, unsigned long align,
 				  unsigned long min, unsigned int type_mask,
===== drivers/pci/remove.c 1.3 vs edited =====
--- 1.3/drivers/pci/remove.c	Tue Feb  3 12:17:30 2004
+++ edited/drivers/pci/remove.c	Fri Aug  6 15:36:18 2004
@@ -12,12 +12,22 @@
 
 static void pci_free_resources(struct pci_dev *dev)
 {
+	struct resource *res;
 	int i;
 
  	msi_remove_pci_irq_vectors(dev);
 
+	if (dev->hdr_type == PCI_HEADER_TYPE_NORMAL) {
+		res = &dev->resource[PCI_ROM_RESOURCE];
+		if (res->flags & PCI_ROM_COPY) {
+			kfree((void*)res->start);
+			res->flags &= !PCI_ROM_COPY;
+			res->start = 0;
+			res->end = 0;
+		}
+	}
 	for (i = 0; i < PCI_NUM_RESOURCES; i++) {
-		struct resource *res = dev->resource + i;
+		res = dev->resource + i;
 		if (res->parent)
 			release_resource(res);
 	}
@@ -26,6 +36,7 @@
 static void pci_destroy_dev(struct pci_dev *dev)
 {
 	pci_proc_detach_device(dev);
+	pci_remove_sysfs_dev_files(dev);
 	device_unregister(&dev->dev);
 
 	/* Remove the device from the device lists, and prevent any further
===== include/linux/pci.h 1.132 vs edited =====
--- 1.132/include/linux/pci.h	Mon Aug  2 04:00:43 2004
+++ edited/include/linux/pci.h	Sat Aug  7 13:33:34 2004
@@ -102,6 +102,8 @@
 #define PCI_SUBSYSTEM_ID	0x2e  
 #define PCI_ROM_ADDRESS		0x30	/* Bits 31..11 are address, 10..1 reserved */
 #define  PCI_ROM_ADDRESS_ENABLE	0x01
+#define  PCI_ROM_SHADOW		0x02	/* resource flag, ROM is copy at C000:0 */
+#define  PCI_ROM_COPY		0x04	/* resource flag, ROM is alloc'd copy */
 #define PCI_ROM_ADDRESS_MASK	(~0x7ffUL)
 
 #define PCI_CAPABILITY_LIST	0x34	/* Offset of first capability list entry */
@@ -777,6 +779,12 @@
 int pci_dac_set_dma_mask(struct pci_dev *dev, u64 mask);
 int pci_set_consistent_dma_mask(struct pci_dev *dev, u64 mask);
 int pci_assign_resource(struct pci_dev *dev, int i);
+
+/* ROM control related routines */
+unsigned char *pci_map_rom(struct pci_dev *dev, size_t *size);
+unsigned char *pci_map_rom_copy(struct pci_dev *dev, size_t *size);
+void pci_unmap_rom(struct pci_dev *dev, unsigned char *rom);
+void pci_remove_rom(struct pci_dev *dev);
 
 /* Power management related routines */
 int pci_save_state(struct pci_dev *dev, u32 *buffer);

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-11 19:24                                         ` Jon Smirl
@ 2004-08-11 19:44                                           ` Jesse Barnes
  2004-08-11 20:11                                           ` Alan Cox
  1 sibling, 0 replies; 110+ messages in thread
From: Jesse Barnes @ 2004-08-11 19:44 UTC (permalink / raw)
  To: Jon Smirl
  Cc: Greg KH, Benjamin Herrenschmidt, Martin Mares, linux-pci,
	Alan Cox, Linux Kernel Mailing List, Petr Vandrovec

On Wednesday, August 11, 2004 12:24 pm, Jon Smirl wrote:
> Jesse, did you notice that the quirk for tracking the boot video device
> is x86 only? I believe this needs to run on ia64 and x86_64 too. How do
> we want to do that? It will do the wrong thing on architectures that
> don't shadow video ROMs to C0000.

Yeah, but I don't know of any ia64 platforms that need the quirk.  All of them 
that I'm aware of use add-on boards.

Jesse

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-11 19:24                                         ` Jon Smirl
  2004-08-11 19:44                                           ` Jesse Barnes
@ 2004-08-11 20:11                                           ` Alan Cox
  2004-08-11 23:31                                             ` Jon Smirl
  1 sibling, 1 reply; 110+ messages in thread
From: Alan Cox @ 2004-08-11 20:11 UTC (permalink / raw)
  To: Jon Smirl
  Cc: Greg KH, Jesse Barnes, Benjamin Herrenschmidt, Martin Mares,
	linux-pci, Linux Kernel Mailing List, Petr Vandrovec

On Mer, 2004-08-11 at 20:24, Jon Smirl wrote:
> Alan Cox had concerns about copying the ROMs for those devices that
> don't implement full address decoding. I'm using kmalloc for 40-60KB.
> Would vmalloc be a better choice? Very few drivers will use the copy
> option, mostly old hardware.

As I said before you don't need to allocate big chunks of kernel memory
for this because you don't want to store ROM copies in kernel, you just
disallow mmap in such a case and let the user use read().

I am opposed to anything that keeps ROM copies in the kernel.


^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-11 20:11                                           ` Alan Cox
@ 2004-08-11 23:31                                             ` Jon Smirl
  2004-08-12 11:51                                               ` Alan Cox
  0 siblings, 1 reply; 110+ messages in thread
From: Jon Smirl @ 2004-08-11 23:31 UTC (permalink / raw)
  To: Alan Cox
  Cc: Greg KH, Jesse Barnes, Benjamin Herrenschmidt, Martin Mares,
	linux-pci, Linux Kernel Mailing List, Petr Vandrovec

--- Alan Cox <alan@lxorguk.ukuu.org.uk> wrote:
> On Mer, 2004-08-11 at 20:24, Jon Smirl wrote:
> > Alan Cox had concerns about copying the ROMs for those devices that
> > don't implement full address decoding. I'm using kmalloc for
> 40-60KB.
> > Would vmalloc be a better choice? Very few drivers will use the
> copy
> > option, mostly old hardware.
> 
> As I said before you don't need to allocate big chunks of kernel
> memory for this because you don't want to store ROM copies in kernel,
> you just disallow mmap in such a case and let the user use read().
> 
> I am opposed to anything that keeps ROM copies in the kernel.
> 

How are we supposed to implement this without a copy? Once the device
driver is loaded there is never a safe way access the ROM again because
an interrupt or another CPU might use the PCI decoders to access the
other hardware and disrupt the ROM read. You have to copy the ROM when
the driver says it is safe.

I provided two calls for the driver to pick from: 1) make an in kernel
copy of the ROM and leave it visible from sysfs or 2) remove the sysfs
attribute. 

Another possible scheme could have a user space daemon that copies the
ROMs out of the kernel at boot and then holds them for later access by
other apps, but that would break the ROM attribute in sysfs model. 

Still another scheme would be to make the drivers for this class of
card implement a lock around PCI address decoder use. That would get
complex with interrupt routines.

How much trouble do we want to go to handling a case that only applies
to very few cards? I believe old QLogic disk controllers have this
problem, are there others? I'm not aware of any video cards with this
problem. 99%+ of PCI ROMs don't need the copy.

=====
Jon Smirl
jonsmirl@yahoo.com


		
__________________________________
Do you Yahoo!?
Yahoo! Mail - 50x more storage than other providers!
http://promotions.yahoo.com/new_mail

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-11 17:28                                       ` Greg KH
  2004-08-11 18:02                                         ` Jesse Barnes
  2004-08-11 19:24                                         ` Jon Smirl
@ 2004-08-12  0:45                                         ` Jon Smirl
  2004-08-12  4:37                                           ` Greg KH
  2 siblings, 1 reply; 110+ messages in thread
From: Jon Smirl @ 2004-08-12  0:45 UTC (permalink / raw)
  To: Greg KH, Jesse Barnes
  Cc: Martin Mares, linux-pci, Alan Cox, Linux Kernel Mailing List,
	Petr Vandrovec, Benjamin Herrenschmidt

I have to add a pointer to struct pci_dev to track the attribute copy
with the size in it. Would you rather have me add the pointer or or
change the sysfs rules to state that a copy of the length is made?

--- Greg KH <greg@kroah.com> wrote:
> > Greg was a little worried that your comment
> > 	/* .size is set individually for each device, sysfs copies it into
> dentry */
> > might not be correct.
> 
> I looked at the code, and he's right.  But it's pretty scary that it
> works correctly so I'd prefer to do it the way your patch did it
> (create
> a new attribute for every entry.)
> 
> thnaks,
> 
> greg k-h
> 


=====
Jon Smirl
jonsmirl@yahoo.com


		
__________________________________
Do you Yahoo!?
New and Improved Yahoo! Mail - Send 10MB messages!
http://promotions.yahoo.com/new_mail 

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-11 18:12                                           ` Greg KH
@ 2004-08-12  1:28                                             ` Marcelo Tosatti
  2004-08-12 14:38                                               ` Jesse Barnes
  2004-08-12 17:29                                               ` Greg KH
  2004-08-12  2:25                                             ` Miles Bader
  1 sibling, 2 replies; 110+ messages in thread
From: Marcelo Tosatti @ 2004-08-12  1:28 UTC (permalink / raw)
  To: Greg KH; +Cc: Jesse Barnes, linux-pci, Linux Kernel Mailing List

On Wed, Aug 11, 2004 at 11:12:36AM -0700, Greg KH wrote:
> On Wed, Aug 11, 2004 at 11:02:10AM -0700, Jesse Barnes wrote:
> > On Wednesday, August 11, 2004 10:28 am, Greg KH wrote:
> > > On Wed, Aug 11, 2004 at 10:04:02AM -0700, Jesse Barnes wrote:
> > > > On Friday, August 6, 2004 2:14 pm, Jon Smirl wrote:
> > > > > Please check the code out and give it some testing. It will probably
> > > > > needs some adjustment for other platforms.
> > > >
> > > > Jon, this works on my machine too.  Greg, if it looks ok can you pull it
> > > > in? And can you add:
> > > >
> > > >  * (C) Copyright 2004 Silicon Graphics, Inc.
> > > >  *       Jesse Barnes <jbarnes@sgi.com>
> > > >
> > > > to pci-sysfs.c if you do?
> > >
> > > Care to send me a new patch?  Oh, and that copyright line needs to look
> > > like:
> > > * Copyright (c) 2004 Silicon Graphics, Inc. Jesse Barnes <jbarnes@sgi.com>
> > >
> > > to make it legal, or so my lawyers say :)
> > 
> > But I'm not the copyright holder, Silicon Graphics is, I just wanted people to 
> > know who to harass if something breaks :).
> 
> That's fine.  It's the "Copyright (c) 2004" order and exact "(c)" that
> really matters, from what I have been told to do.

Greg,

That made me curious, what is the rationale behind 

(C) Copyright 2004 Silicon Graphics, Inc. Jesse Barnes <jbarnes@sgi.com> 

works and the previous suggested

(C) Copyright 2004 Silicon Graphics, Inc. 
	Jesse Barnes <jbarnes@sgi.com> 

doesnt? 

I know its a bit offtopic, but still, if you know the reason, would be
great to hear :) Bet others will also like to hear that. 

Thanks!





^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-11 18:12                                           ` Greg KH
  2004-08-12  1:28                                             ` Marcelo Tosatti
@ 2004-08-12  2:25                                             ` Miles Bader
  2004-08-12  4:38                                               ` Greg KH
  1 sibling, 1 reply; 110+ messages in thread
From: Miles Bader @ 2004-08-12  2:25 UTC (permalink / raw)
  To: Greg KH; +Cc: Jesse Barnes, linux-pci, Linux Kernel Mailing List

Greg KH <greg@kroah.com> writes:
> That's fine.  It's the "Copyright (c) 2004" order and exact "(c)" that
> really matters, from what I have been told to do.

Is that true now?  It used to be said (including by some people who I
trust on such matters) that the ascii graphic "(c)" -- as opposed to a
real c-in-a-circle character -- was _not_ officially recognized as
meaning "Copyright", and so one needed the actual word "Copyright", or a
real c-in-a-circle, for such notices to have some meaning.  However the
word "Copyright" alone is apparently enough.

-Miles
-- 
Somebody has to do something, and it's just incredibly pathetic that it
has to be us.  -- Jerry Garcia

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-12  0:45                                         ` Jon Smirl
@ 2004-08-12  4:37                                           ` Greg KH
  0 siblings, 0 replies; 110+ messages in thread
From: Greg KH @ 2004-08-12  4:37 UTC (permalink / raw)
  To: Jon Smirl
  Cc: Jesse Barnes, Martin Mares, linux-pci, Alan Cox,
	Linux Kernel Mailing List, Petr Vandrovec,
	Benjamin Herrenschmidt

On Wed, Aug 11, 2004 at 05:45:25PM -0700, Jon Smirl wrote:
> I have to add a pointer to struct pci_dev to track the attribute copy
> with the size in it. Would you rather have me add the pointer or or
> change the sysfs rules to state that a copy of the length is made?

Adding a pointer is fine for now.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-12  2:25                                             ` Miles Bader
@ 2004-08-12  4:38                                               ` Greg KH
  2004-08-12  9:18                                                 ` Geert Uytterhoeven
  2004-08-12 22:01                                                 ` Matthew Wilcox
  0 siblings, 2 replies; 110+ messages in thread
From: Greg KH @ 2004-08-12  4:38 UTC (permalink / raw)
  To: Miles Bader; +Cc: Jesse Barnes, linux-pci, Linux Kernel Mailing List

On Thu, Aug 12, 2004 at 11:25:27AM +0900, Miles Bader wrote:
> Greg KH <greg@kroah.com> writes:
> > That's fine.  It's the "Copyright (c) 2004" order and exact "(c)" that
> > really matters, from what I have been told to do.
> 
> Is that true now?

That's what the copyright lawyers at a certian three letter company
said is needed, and for some reason I tend to believe what they say :)

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-12  4:38                                               ` Greg KH
@ 2004-08-12  9:18                                                 ` Geert Uytterhoeven
  2004-08-12 22:01                                                 ` Matthew Wilcox
  1 sibling, 0 replies; 110+ messages in thread
From: Geert Uytterhoeven @ 2004-08-12  9:18 UTC (permalink / raw)
  To: Greg KH; +Cc: Miles Bader, Jesse Barnes, linux-pci, Linux Kernel Mailing List

On Wed, 11 Aug 2004, Greg KH wrote:
> On Thu, Aug 12, 2004 at 11:25:27AM +0900, Miles Bader wrote:
> > Greg KH <greg@kroah.com> writes:
> > > That's fine.  It's the "Copyright (c) 2004" order and exact "(c)" that
> > > really matters, from what I have been told to do.
> >
> > Is that true now?
>
> That's what the copyright lawyers at a certian three letter company
> said is needed, and for some reason I tend to believe what they say :)

You mean the copyright lawyers at a certian three letter company in a certain
three letter country?

IIRC, according to copyright laws in the other part of the world, everything
you write is always copyrighted by you, whether `©' or `(c)' or `copyright' are
present or not.

Gr{oetje,eeting}s,

						Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
							    -- Linus Torvalds

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-11 23:31                                             ` Jon Smirl
@ 2004-08-12 11:51                                               ` Alan Cox
  2004-08-12 20:28                                                 ` Jon Smirl
  0 siblings, 1 reply; 110+ messages in thread
From: Alan Cox @ 2004-08-12 11:51 UTC (permalink / raw)
  To: Jon Smirl
  Cc: Greg KH, Jesse Barnes, Benjamin Herrenschmidt, Martin Mares,
	linux-pci, Linux Kernel Mailing List, Petr Vandrovec

On Iau, 2004-08-12 at 00:31, Jon Smirl wrote:
> How are we supposed to implement this without a copy? Once the device
> driver is loaded there is never a safe way access the ROM again because
> an interrupt or another CPU might use the PCI decoders to access the
> other hardware and disrupt the ROM read. You have to copy the ROM when
> the driver says it is safe.

It's never safe essentially. The only way you can make it safe for this
case is to put the knowledge in the device driver for that specific card
rather than sysfs.

> Still another scheme would be to make the drivers for this class of
> card implement a lock around PCI address decoder use. That would get
> complex with interrupt routines.

That would be impossible in fact because of DMA and SMM activity.

> How much trouble do we want to go to handling a case that only applies
> to very few cards? I believe old QLogic disk controllers have this
> problem, are there others? I'm not aware of any video cards with this
> problem. 99%+ of PCI ROMs don't need the copy.

It would be better to just to blacklist those corner cases


^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-12  1:28                                             ` Marcelo Tosatti
@ 2004-08-12 14:38                                               ` Jesse Barnes
  2004-08-12 17:29                                               ` Greg KH
  1 sibling, 0 replies; 110+ messages in thread
From: Jesse Barnes @ 2004-08-12 14:38 UTC (permalink / raw)
  To: Marcelo Tosatti; +Cc: Greg KH, linux-pci, Linux Kernel Mailing List

On Wednesday, August 11, 2004 6:28 pm, Marcelo Tosatti wrote:
> That made me curious, what is the rationale behind
>
> (C) Copyright 2004 Silicon Graphics, Inc. Jesse Barnes <jbarnes@sgi.com>
>
> works and the previous suggested
>
> (C) Copyright 2004 Silicon Graphics, Inc.
> 	Jesse Barnes <jbarnes@sgi.com>
>
> doesnt?
>
> I know its a bit offtopic, but still, if you know the reason, would be
> great to hear :) Bet others will also like to hear that.

My thought was that the second one doesn't list me as the copyright holder, 
only the author, which is just what I wanted.  To me, the first one seems to 
indicate that the copyright is shared between myself and the entity that owns 
it, which is incorrect.

Jesse

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-12  1:28                                             ` Marcelo Tosatti
  2004-08-12 14:38                                               ` Jesse Barnes
@ 2004-08-12 17:29                                               ` Greg KH
  1 sibling, 0 replies; 110+ messages in thread
From: Greg KH @ 2004-08-12 17:29 UTC (permalink / raw)
  To: Marcelo Tosatti; +Cc: Jesse Barnes, linux-pci, Linux Kernel Mailing List

On Wed, Aug 11, 2004 at 10:28:43PM -0300, Marcelo Tosatti wrote:
> > > > > Jon, this works on my machine too.  Greg, if it looks ok can you pull it
> > > > > in? And can you add:
> > > > >
> > > > >  * (C) Copyright 2004 Silicon Graphics, Inc.
> > > > >  *       Jesse Barnes <jbarnes@sgi.com>
> > > > >
> > > > > to pci-sysfs.c if you do?
> > > >
> > > > Care to send me a new patch?  Oh, and that copyright line needs to look
> > > > like:
> > > > * Copyright (c) 2004 Silicon Graphics, Inc. Jesse Barnes <jbarnes@sgi.com>
> > > >
> > > > to make it legal, or so my lawyers say :)
> > > 
> > > But I'm not the copyright holder, Silicon Graphics is, I just wanted people to 
> > > know who to harass if something breaks :).
> > 
> > That's fine.  It's the "Copyright (c) 2004" order and exact "(c)" that
> > really matters, from what I have been told to do.
> 
> Greg,
> 
> That made me curious, what is the rationale behind 
> 
> (C) Copyright 2004 Silicon Graphics, Inc. Jesse Barnes <jbarnes@sgi.com> 
> 
> works and the previous suggested
> 
> (C) Copyright 2004 Silicon Graphics, Inc. 
> 	Jesse Barnes <jbarnes@sgi.com> 
> 
> doesnt? 
> 
> I know its a bit offtopic, but still, if you know the reason, would be
> great to hear :) Bet others will also like to hear that. 

Ok, this is as per some copyright lawyers who know US Copyright law, and
how to defend it in court.  To make it legally binding in the US, so you
can file for a copyright in the proper government agencies (which is
necessary if you want to be able defend your copyright in court in an
easier manner, it's not necessary but it sure helps.) you need the
phrase to look like:
	Copyright (c) <DATE> <COMPANY_NAME>

Note that this is different from the above description of putting a
"(C)" and then the word "Copyright".

Now I have no idea if this is the same for all countries, I only know
what I was told to do.

Hope this helps, and I really don't want to start a big "copyright"
thread, as I'm not a lawyer, I only have to deal with them on a constant
basis :)

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-12 11:51                                               ` Alan Cox
@ 2004-08-12 20:28                                                 ` Jon Smirl
  0 siblings, 0 replies; 110+ messages in thread
From: Jon Smirl @ 2004-08-12 20:28 UTC (permalink / raw)
  To: Alan Cox
  Cc: Greg KH, Jesse Barnes, Benjamin Herrenschmidt, Martin Mares,
	linux-pci, Linux Kernel Mailing List, Petr Vandrovec

--- Alan Cox <alan@lxorguk.ukuu.org.uk> wrote:

> On Iau, 2004-08-12 at 00:31, Jon Smirl wrote:
> > How are we supposed to implement this without a copy? Once the
> device
> > driver is loaded there is never a safe way access the ROM again
> because
> > an interrupt or another CPU might use the PCI decoders to access
> the
> > other hardware and disrupt the ROM read. You have to copy the ROM
> when
> > the driver says it is safe.
> 
> It's never safe essentially. The only way you can make it safe for
> this
> case is to put the knowledge in the device driver for that specific
> card
> rather than sysfs.

I added these two calls to the pci API just for the case of partially
decoded hardware. The device driver for the hardware needs to make
these calls.

unsigned char *pci_map_rom_copy(struct pci_dev *dev, size_t *size);

Call this one from the driver when it is safe to read the ROM. It will
copy it and then provide a virtual address so that you can read it. The
copy is stored in the kernel so that the sysfs attribute will work
right.

void pci_remove_rom(struct pci_dev *dev);

Call this one from the driver to simply remove the ROM attribute.

Before the driver is loaded we have to assume that it safe to read the
ROM normally and the sysfs attribute will directly access the ROM.


unsigned char *pci_map_rom(struct pci_dev *dev, size_t *size);

For normal hardware that implements full decoding use this call. It
will automatically sort out the need to use the real ROM or a shadow
copy.

void pci_unmap_rom(struct pci_dev *dev, unsigned char *rom);

When you are done with a mapping use this

Normal ROMs do not make copies. The only time a copy happens is when a
device driver for a partially decoded device calls pci_map_rom_copy().

=====
Jon Smirl
jonsmirl@yahoo.com


		
__________________________________
Do you Yahoo!?
New and Improved Yahoo! Mail - Send 10MB messages!
http://promotions.yahoo.com/new_mail 

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-12  4:38                                               ` Greg KH
  2004-08-12  9:18                                                 ` Geert Uytterhoeven
@ 2004-08-12 22:01                                                 ` Matthew Wilcox
  1 sibling, 0 replies; 110+ messages in thread
From: Matthew Wilcox @ 2004-08-12 22:01 UTC (permalink / raw)
  To: Greg KH; +Cc: Miles Bader, Jesse Barnes, linux-pci, Linux Kernel Mailing List

On Wed, Aug 11, 2004 at 09:38:18PM -0700, Greg KH wrote:
> That's what the copyright lawyers at a certian three letter company
> said is needed, and for some reason I tend to believe what they say :)

On the other hand, you could believe what the US government tells you ...
http://www.copyright.gov/circs/circ1.html#noc

In particular, the (c) has no relevance; the important bit is the word
"Copyright":

   1. The symbol © (the letter C in a circle), or the word "Copyright,"
   or the abbreviation "Copr."; and

-- 
"Next the statesmen will invent cheap lies, putting the blame upon 
the nation that is attacked, and every man will be glad of those
conscience-soothing falsities, and will diligently study them, and refuse
to examine any refutations of them; and thus he will by and by convince 
himself that the war is just, and will thank God for the better sleep 
he enjoys after this process of grotesque self-deception." -- Mark Twain

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-10-08  2:20   ` Jon Smirl
@ 2004-11-05 23:06     ` Greg KH
  0 siblings, 0 replies; 110+ messages in thread
From: Greg KH @ 2004-11-05 23:06 UTC (permalink / raw)
  To: Jon Smirl
  Cc: Matthew Wilcox, Jesse Barnes, Martin Mares, Pallipadi, Venkatesh,
	linux-pci, Alan Cox, Linux Kernel Mailing List, Petr Vandrovec,
	Benjamin Herrenschmidt

On Thu, Oct 07, 2004 at 10:20:39PM -0400, Jon Smirl wrote:
> Linus has requested that the sysfs rom attribute be changed to require
> enabling before it works. echo "0" to the attribute to disable,
> echoing anything else enables the rom output. The concern is that
> something like a file browser could inadvertently read the attribute
> and change the state of the hardware without the user's knowledge.
> 
> The attached patch includes the previous patch plus the enabling logic.


Applied, thanks.

greg k-h


^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-09-08 23:50 ` Greg KH
@ 2004-10-08  2:20   ` Jon Smirl
  2004-11-05 23:06     ` Greg KH
  0 siblings, 1 reply; 110+ messages in thread
From: Jon Smirl @ 2004-10-08  2:20 UTC (permalink / raw)
  To: Greg KH
  Cc: Matthew Wilcox, Jesse Barnes, Martin Mares, Pallipadi, Venkatesh,
	linux-pci, Alan Cox, Linux Kernel Mailing List, Petr Vandrovec,
	Benjamin Herrenschmidt

[-- Attachment #1: Type: text/plain, Size: 929 bytes --]

Linus has requested that the sysfs rom attribute be changed to require
enabling before it works. echo "0" to the attribute to disable,
echoing anything else enables the rom output. The concern is that
something like a file browser could inadvertently read the attribute
and change the state of the hardware without the user's knowledge.

The attached patch includes the previous patch plus the enabling logic.

[root@smirl 0000:02:02.0]#
[root@smirl 0000:02:02.0]# cat rom
cat: rom: Invalid argument
[root@smirl 0000:02:02.0]# echo "1" >rom
[root@smirl 0000:02:02.0]# hexdump -C -n 20 rom
00000000  55 aa 60 e9 d6 01 00 00  00 00 00 00 00 00 00 00  |U.`.............|
00000010  00 00 00 00                                       |....|
00000014
[root@smirl 0000:02:02.0]# echo "0" >rom
[root@smirl 0000:02:02.0]# hexdump -C -n 20 rom
hexdump: rom: Invalid argument
[root@smirl 0000:02:02.0]#





-- 
Jon Smirl
jonsmirl@gmail.com

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: pci-sysfs-rom-23.patch --]
[-- Type: text/x-patch; name="pci-sysfs-rom-23.patch", Size: 17250 bytes --]

diff -Nru a/arch/i386/pci/fixup.c b/arch/i386/pci/fixup.c
--- a/arch/i386/pci/fixup.c	2004-10-07 22:11:55 -04:00
+++ b/arch/i386/pci/fixup.c	2004-10-07 22:11:55 -04:00
@@ -255,3 +255,41 @@
 }
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2, pci_fixup_nforce2);
 
+/*
+ * Fixup to mark boot BIOS video selected by BIOS before it changes
+ *
+ * From information provided by "Jon Smirl" <jonsmirl@yahoo.com>
+ *
+ * The standard boot ROM sequence for an x86 machine uses the BIOS
+ * to select an initial video card for boot display. This boot video 
+ * card will have it's BIOS copied to C0000 in system RAM. 
+ * IORESOURCE_ROM_SHADOW is used to associate the boot video
+ * card with this copy. On laptops this copy has to be used since
+ * the main ROM may be compressed or combined with another image.
+ * See pci_map_rom() for use of this flag. IORESOURCE_ROM_SHADOW
+ * is marked here since the boot video device will be the only enabled
+ * video device at this point.
+ *
+ */static void __devinit pci_fixup_video(struct pci_dev *pdev)
+{
+	struct pci_dev *bridge;
+	struct pci_bus *bus;
+	u16 l;
+
+	if ((pdev->class >> 8) != PCI_CLASS_DISPLAY_VGA)
+		return;
+
+	/* Is VGA routed to us? */
+	bus = pdev->bus;
+	while (bus) {
+		bridge = bus->self;
+		if (bridge) {
+			pci_read_config_word(bridge, PCI_BRIDGE_CONTROL, &l);
+			if (!(l & PCI_BRIDGE_CTL_VGA))
+				return;
+		}
+		bus = bus->parent;
+	}
+	pdev->resource[PCI_ROM_RESOURCE].flags |= IORESOURCE_ROM_SHADOW;
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, pci_fixup_video);
diff -Nru a/drivers/pci/Makefile b/drivers/pci/Makefile
--- a/drivers/pci/Makefile	2004-10-07 22:11:55 -04:00
+++ b/drivers/pci/Makefile	2004-10-07 22:11:55 -04:00
@@ -3,7 +3,8 @@
 #
 
 obj-y		+= access.o bus.o probe.o remove.o pci.o quirks.o \
-			names.o pci-driver.o search.o pci-sysfs.o
+			names.o pci-driver.o search.o pci-sysfs.o \
+			rom.o
 obj-$(CONFIG_PROC_FS) += proc.o
 
 ifndef CONFIG_SPARC64
diff -Nru a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
--- a/drivers/pci/pci-sysfs.c	2004-10-07 22:11:55 -04:00
+++ b/drivers/pci/pci-sysfs.c	2004-10-07 22:11:55 -04:00
@@ -5,6 +5,8 @@
  * (C) Copyright 2002-2004 IBM Corp.
  * (C) Copyright 2003 Matthew Wilcox
  * (C) Copyright 2003 Hewlett-Packard
+ * (C) Copyright 2004 Jon Smirl <jonsmirl@yahoo.com>
+ * (C) Copyright 2004 Silicon Graphics, Inc. Jesse Barnes <jbarnes@sgi.com>
  *
  * File attributes for PCI devices
  *
@@ -20,6 +22,8 @@
 
 #include "pci.h"
 
+static int sysfs_initialized;	/* = 0 */
+
 /* show configuration fields */
 #define pci_config_attr(field, format_string)				\
 static ssize_t								\
@@ -164,6 +168,65 @@
 	return count;
 }
 
+/**
+ * pci_write_rom - used to enable access to the PCI ROM display
+ * @kobj: kernel object handle
+ * @buf: user input
+ * @off: file offset
+ * @count: number of byte in input
+ *
+ * writing anything except 0 enables it
+ */
+static ssize_t
+pci_write_rom(struct kobject *kobj, char *buf, loff_t off, size_t count)
+{
+	struct pci_dev *pdev = to_pci_dev(container_of(kobj, struct device, kobj));
+
+	if ((off ==  0) && (*buf == '0') && (count == 2))
+		pdev->rom_attr_enabled = 0;
+	else
+		pdev->rom_attr_enabled = 1;
+
+	return count;
+}
+
+/**
+ * pci_read_rom - read a PCI ROM
+ * @kobj: kernel object handle
+ * @buf: where to put the data we read from the ROM
+ * @off: file offset
+ * @count: number of bytes to read
+ *
+ * Put @count bytes starting at @off into @buf from the ROM in the PCI
+ * device corresponding to @kobj.
+ */
+static ssize_t
+pci_read_rom(struct kobject *kobj, char *buf, loff_t off, size_t count)
+{
+	struct pci_dev *pdev = to_pci_dev(container_of(kobj, struct device, kobj));
+	void __iomem *rom;
+	size_t size;
+
+	if (!pdev->rom_attr_enabled)
+		return -EINVAL;
+	
+	rom = pci_map_rom(pdev, &size);	/* size starts out as PCI window size */
+	if (!rom)
+		return 0;
+		
+	if (off >= size)
+		count = 0;
+	else {
+		if (off + count > size)
+			count = size - off;
+		
+		memcpy_fromio(buf, rom + off, count);
+	}
+	pci_unmap_rom(pdev, rom);
+		
+	return count;
+}
+
 static struct bin_attribute pci_config_attr = {
 	.attr =	{
 		.name = "config",
@@ -186,13 +249,68 @@
 	.write = pci_write_config,
 };
 
-void pci_create_sysfs_dev_files (struct pci_dev *pdev)
+int pci_create_sysfs_dev_files (struct pci_dev *pdev)
 {
+	if (!sysfs_initialized)
+		return -EACCES;
+
 	if (pdev->cfg_size < 4096)
 		sysfs_create_bin_file(&pdev->dev.kobj, &pci_config_attr);
 	else
 		sysfs_create_bin_file(&pdev->dev.kobj, &pcie_config_attr);
 
+	/* If the device has a ROM, try to expose it in sysfs. */
+	if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) {
+		struct bin_attribute *rom_attr;
+		
+		rom_attr = kmalloc(sizeof(*rom_attr), GFP_ATOMIC);
+		if (rom_attr) {
+			pdev->rom_attr = rom_attr;
+			rom_attr->size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
+			rom_attr->attr.name = "rom";
+			rom_attr->attr.mode = S_IRUSR;
+			rom_attr->attr.owner = THIS_MODULE;
+			rom_attr->read = pci_read_rom;
+			rom_attr->write = pci_write_rom;
+			sysfs_create_bin_file(&pdev->dev.kobj, rom_attr);
+		}
+	}
 	/* add platform-specific attributes */
 	pcibios_add_platform_entries(pdev);
+	
+	return 0;
+}
+
+/**
+ * pci_remove_sysfs_dev_files - cleanup PCI specific sysfs files
+ * @pdev: device whose entries we should free
+ *
+ * Cleanup when @pdev is removed from sysfs.
+ */
+void pci_remove_sysfs_dev_files(struct pci_dev *pdev)
+{
+	if (pdev->cfg_size < 4096)
+		sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr);
+	else
+		sysfs_remove_bin_file(&pdev->dev.kobj, &pcie_config_attr);
+
+	if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) {
+		if (pdev->rom_attr) {
+			sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr);
+			kfree(pdev->rom_attr);
+		}
+	}
 }
+
+static int __init pci_sysfs_init(void)
+{
+	struct pci_dev *pdev = NULL;
+	
+	sysfs_initialized = 1;
+	while ((pdev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pdev)) != NULL)
+		pci_create_sysfs_dev_files(pdev);
+
+	return 0;
+}
+
+__initcall(pci_sysfs_init);
diff -Nru a/drivers/pci/pci.h b/drivers/pci/pci.h
--- a/drivers/pci/pci.h	2004-10-07 22:11:55 -04:00
+++ b/drivers/pci/pci.h	2004-10-07 22:11:55 -04:00
@@ -2,7 +2,9 @@
 
 extern int pci_hotplug (struct device *dev, char **envp, int num_envp,
 			 char *buffer, int buffer_size);
-extern void pci_create_sysfs_dev_files(struct pci_dev *pdev);
+extern int pci_create_sysfs_dev_files(struct pci_dev *pdev);
+extern void pci_remove_sysfs_dev_files(struct pci_dev *pdev);
+extern void pci_cleanup_rom(struct pci_dev *dev);
 extern int pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
 				  unsigned long size, unsigned long align,
 				  unsigned long min, unsigned int type_mask,
diff -Nru a/drivers/pci/probe.c b/drivers/pci/probe.c
--- a/drivers/pci/probe.c	2004-10-07 22:11:55 -04:00
+++ b/drivers/pci/probe.c	2004-10-07 22:11:55 -04:00
@@ -170,7 +170,7 @@
 		if (sz && sz != 0xffffffff) {
 			sz = pci_size(l, sz, PCI_ROM_ADDRESS_MASK);
 			if (sz) {
-				res->flags = (l & PCI_ROM_ADDRESS_ENABLE) |
+				res->flags = (l & IORESOURCE_ROM_ENABLE) |
 				  IORESOURCE_MEM | IORESOURCE_PREFETCH |
 				  IORESOURCE_READONLY | IORESOURCE_CACHEABLE;
 				res->start = l & PCI_ROM_ADDRESS_MASK;
diff -Nru a/drivers/pci/remove.c b/drivers/pci/remove.c
--- a/drivers/pci/remove.c	2004-10-07 22:11:55 -04:00
+++ b/drivers/pci/remove.c	2004-10-07 22:11:55 -04:00
@@ -16,6 +16,7 @@
 
  	msi_remove_pci_irq_vectors(dev);
 
+	pci_cleanup_rom(dev);
 	for (i = 0; i < PCI_NUM_RESOURCES; i++) {
 		struct resource *res = dev->resource + i;
 		if (res->parent)
@@ -26,6 +27,7 @@
 static void pci_destroy_dev(struct pci_dev *dev)
 {
 	pci_proc_detach_device(dev);
+	pci_remove_sysfs_dev_files(dev);
 	device_unregister(&dev->dev);
 
 	/* Remove the device from the device lists, and prevent any further
diff -Nru a/drivers/pci/rom.c b/drivers/pci/rom.c
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/pci/rom.c	2004-10-07 22:11:55 -04:00
@@ -0,0 +1,225 @@
+/*
+ * drivers/pci/rom.c
+ *
+ * (C) Copyright 2004 Jon Smirl <jonsmirl@yahoo.com>
+ * (C) Copyright 2004 Silicon Graphics, Inc. Jesse Barnes <jbarnes@sgi.com>
+ *
+ * PCI ROM access routines
+ *
+ */
+
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+
+#include "pci.h"
+
+/**
+ * pci_enable_rom - enable ROM decoding for a PCI device
+ * @dev: PCI device to enable
+ *
+ * Enable ROM decoding on @dev.  This involves simply turning on the last
+ * bit of the PCI ROM BAR.  Note that some cards may share address decoders
+ * between the ROM and other resources, so enabling it may disable access
+ * to MMIO registers or other card memory.
+ */
+static void
+pci_enable_rom(struct pci_dev *pdev)
+{
+	u32 rom_addr;
+	
+	pci_read_config_dword(pdev, pdev->rom_base_reg, &rom_addr);
+	rom_addr |= PCI_ROM_ADDRESS_ENABLE;
+	pci_write_config_dword(pdev, pdev->rom_base_reg, rom_addr);
+}
+
+/**
+ * pci_disable_rom - disable ROM decoding for a PCI device
+ * @dev: PCI device to disable
+ *
+ * Disable ROM decoding on a PCI device by turning off the last bit in the
+ * ROM BAR.
+ */
+static void
+pci_disable_rom(struct pci_dev *pdev)
+{
+	u32 rom_addr;
+	pci_read_config_dword(pdev, pdev->rom_base_reg, &rom_addr);
+	rom_addr &= ~PCI_ROM_ADDRESS_ENABLE;
+	pci_write_config_dword(pdev, pdev->rom_base_reg, rom_addr);
+}
+
+/**
+ * pci_map_rom - map a PCI ROM to kernel space
+ * @dev: pointer to pci device struct
+ * @size: pointer to receive size of pci window over ROM
+ * @return: kernel virtual pointer to image of ROM
+ *
+ * Map a PCI ROM into kernel space. If ROM is boot video ROM,
+ * the shadow BIOS copy will be returned instead of the 
+ * actual ROM.
+ */
+void __iomem *pci_map_rom(struct pci_dev *pdev, size_t *size)
+{
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+	loff_t start;
+	void __iomem *rom;
+	void __iomem *image;
+	int last_image;
+	
+	if (res->flags & IORESOURCE_ROM_SHADOW) {	/* IORESOURCE_ROM_SHADOW only set on x86 */
+		start = (loff_t)0xC0000; 	/* primary video rom always starts here */
+		*size = 0x20000;		/* cover C000:0 through E000:0 */
+	} else {
+		if (res->flags & IORESOURCE_ROM_COPY) {
+			*size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
+			return (void __iomem *)pci_resource_start(pdev, PCI_ROM_RESOURCE);
+		} else {
+			/* assign the ROM an address if it doesn't have one */
+			if (res->parent == NULL)
+				pci_assign_resource(pdev, PCI_ROM_RESOURCE);
+	
+			start = pci_resource_start(pdev, PCI_ROM_RESOURCE);
+			*size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
+			if (*size == 0)
+				return NULL;
+			
+			/* Enable ROM space decodes */
+			pci_enable_rom(pdev);
+		}
+	}
+	
+	rom = ioremap(start, *size);
+	if (!rom) {
+		/* restore enable if ioremap fails */
+		if (!(res->flags & (IORESOURCE_ROM_ENABLE | IORESOURCE_ROM_SHADOW | IORESOURCE_ROM_COPY)))
+			pci_disable_rom(pdev);
+		return NULL;
+	}		
+
+	/* Try to find the true size of the ROM since sometimes the PCI window */
+	/* size is much larger than the actual size of the ROM. */
+	/* True size is important if the ROM is going to be copied. */
+	image = rom;
+	do {
+		void __iomem *pds;
+		/* Standard PCI ROMs start out with these bytes 55 AA */
+		if (readb(image) != 0x55)
+			break;
+		if (readb(image + 1) != 0xAA)
+			break;
+		/* get the PCI data structure and check its signature */
+		pds = image + readw(image + 24);
+		if (readb(pds) != 'P')
+			break;
+		if (readb(pds + 1) != 'C')
+			break;
+		if (readb(pds + 2) != 'I')
+			break;
+		if (readb(pds + 3) != 'R')
+			break;
+		last_image = readb(pds + 21) & 0x80;
+		/* this length is reliable */
+		image += readw(pds + 16) * 512;
+	} while (!last_image);
+
+	*size = image - rom;
+
+	return rom;
+}
+
+/**
+ * pci_map_rom_copy - map a PCI ROM to kernel space, create a copy
+ * @dev: pointer to pci device struct
+ * @size: pointer to receive size of pci window over ROM
+ * @return: kernel virtual pointer to image of ROM
+ *
+ * Map a PCI ROM into kernel space. If ROM is boot video ROM,
+ * the shadow BIOS copy will be returned instead of the 
+ * actual ROM.
+ */
+void __iomem *pci_map_rom_copy(struct pci_dev *pdev, size_t *size)
+{
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+	void __iomem *rom;
+	
+	rom = pci_map_rom(pdev, size);
+	if (!rom)
+		return NULL;
+		
+	if (res->flags & (IORESOURCE_ROM_COPY | IORESOURCE_ROM_SHADOW))
+		return rom;
+		
+	res->start = (unsigned long)kmalloc(*size, GFP_KERNEL);
+	if (!res->start) 
+		return rom;
+
+	res->end = res->start + *size; 
+	memcpy_fromio((void*)res->start, rom, *size);
+	pci_unmap_rom(pdev, rom);
+	res->flags |= IORESOURCE_ROM_COPY;
+	
+	return (void __iomem *)res->start;
+}
+
+/**
+ * pci_unmap_rom - unmap the ROM from kernel space
+ * @dev: pointer to pci device struct
+ * @rom: virtual address of the previous mapping
+ *
+ * Remove a mapping of a previously mapped ROM
+ */
+void 
+pci_unmap_rom(struct pci_dev *pdev, void __iomem *rom)
+{
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+
+	if (res->flags & IORESOURCE_ROM_COPY)
+		return;
+		
+	iounmap(rom);
+		
+	/* Disable again before continuing, leave enabled if pci=rom */
+	if (!(res->flags & (IORESOURCE_ROM_ENABLE | IORESOURCE_ROM_SHADOW)))
+		pci_disable_rom(pdev);
+}
+
+/**
+ * pci_remove_rom - disable the ROM and remove its sysfs attribute
+ * @dev: pointer to pci device struct
+ *
+ */
+void 
+pci_remove_rom(struct pci_dev *pdev) 
+{
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+	
+	if (pci_resource_len(pdev, PCI_ROM_RESOURCE))
+		sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr);
+	if (!(res->flags & (IORESOURCE_ROM_ENABLE | IORESOURCE_ROM_SHADOW | IORESOURCE_ROM_COPY)))
+		pci_disable_rom(pdev);
+}
+
+/**
+ * pci_cleanup_rom - internal routine for freeing the ROM copy created 
+ * by pci_map_rom_copy called from remove.c
+ * @dev: pointer to pci device struct
+ *
+ */
+void 
+pci_cleanup_rom(struct pci_dev *pdev) 
+{
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+	if (res->flags & IORESOURCE_ROM_COPY) {
+		kfree((void*)res->start);
+		res->flags &= ~IORESOURCE_ROM_COPY;
+		res->start = 0;
+		res->end = 0;
+	}
+}
+
+EXPORT_SYMBOL(pci_map_rom);
+EXPORT_SYMBOL(pci_map_rom_copy);
+EXPORT_SYMBOL(pci_unmap_rom);
+EXPORT_SYMBOL(pci_remove_rom);
diff -Nru a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c
--- a/drivers/pci/setup-res.c	2004-10-07 22:11:55 -04:00
+++ b/drivers/pci/setup-res.c	2004-10-07 22:11:55 -04:00
@@ -56,7 +56,7 @@
 	if (resno < 6) {
 		reg = PCI_BASE_ADDRESS_0 + 4 * resno;
 	} else if (resno == PCI_ROM_RESOURCE) {
-		new |= res->flags & PCI_ROM_ADDRESS_ENABLE;
+		new |= res->flags & IORESOURCE_ROM_ENABLE;
 		reg = dev->rom_base_reg;
 	} else {
 		/* Hmm, non-standard resource. */
diff -Nru a/include/linux/ioport.h b/include/linux/ioport.h
--- a/include/linux/ioport.h	2004-10-07 22:11:55 -04:00
+++ b/include/linux/ioport.h	2004-10-07 22:11:55 -04:00
@@ -82,6 +82,11 @@
 #define IORESOURCE_MEM_SHADOWABLE	(1<<5)	/* dup: IORESOURCE_SHADOWABLE */
 #define IORESOURCE_MEM_EXPANSIONROM	(1<<6)
 
+/* PCI ROM control bits (IORESOURCE_BITS) */
+#define IORESOURCE_ROM_ENABLE		(1<<0)	/* ROM is enabled, same as PCI_ROM_ADDRESS_ENABLE */
+#define IORESOURCE_ROM_SHADOW		(1<<1)	/* ROM is copy at C000:0 */
+#define IORESOURCE_ROM_COPY		(1<<2)	/* ROM is alloc'd copy, resource field overlaid */
+
 /* PC/ISA/whatever - the normal PC address spaces: IO and memory */
 extern struct resource ioport_resource;
 extern struct resource iomem_resource;
diff -Nru a/include/linux/pci.h b/include/linux/pci.h
--- a/include/linux/pci.h	2004-10-07 22:11:55 -04:00
+++ b/include/linux/pci.h	2004-10-07 22:11:55 -04:00
@@ -537,6 +537,8 @@
 	unsigned int	is_busmaster:1; /* device is busmaster */
 	
 	u32		saved_config_space[16]; /* config space saved at suspend time */
+	struct bin_attribute *rom_attr; /* attribute descriptor for sysfs ROM entry */
+	int rom_attr_enabled;		/* has display of the rom attribute been enabled? */
 #ifdef CONFIG_PCI_NAMES
 #define PCI_NAME_SIZE	96
 #define PCI_NAME_HALF	__stringify(43)	/* less than half to handle slop */
@@ -777,6 +779,12 @@
 int pci_dac_set_dma_mask(struct pci_dev *dev, u64 mask);
 int pci_set_consistent_dma_mask(struct pci_dev *dev, u64 mask);
 int pci_assign_resource(struct pci_dev *dev, int i);
+
+/* ROM control related routines */
+void __iomem *pci_map_rom(struct pci_dev *pdev, size_t *size);
+void __iomem *pci_map_rom_copy(struct pci_dev *pdev, size_t *size);
+void pci_unmap_rom(struct pci_dev *pdev, void __iomem *rom);
+void pci_remove_rom(struct pci_dev *pdev);
 
 /* Power management related routines */
 int pci_save_state(struct pci_dev *dev, u32 *buffer);

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-09-08  3:15 Jon Smirl
  2004-09-08  6:07 ` Greg KH
@ 2004-09-08 23:50 ` Greg KH
  2004-10-08  2:20   ` Jon Smirl
  1 sibling, 1 reply; 110+ messages in thread
From: Greg KH @ 2004-09-08 23:50 UTC (permalink / raw)
  To: Jon Smirl
  Cc: Matthew Wilcox, Jesse Barnes, Martin Mares, Pallipadi, Venkatesh,
	linux-pci, Alan Cox, Linux Kernel Mailing List, Petr Vandrovec,
	Benjamin Herrenschmidt, jonsmirl

On Tue, Sep 07, 2004 at 08:15:37PM -0700, Jon Smirl wrote:
> Greg, are you going to send pci-sysfs-rom-22.patch upstream? Or do I
> need to do it, if so how? Jesse's objection turned out to be a problem
> in another piece of code.

Ok, I've applied this to my kernel trees and it will show up in the next
-mm release.  Nice job everyone.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-09-08  3:15 Jon Smirl
@ 2004-09-08  6:07 ` Greg KH
  2004-09-08 23:50 ` Greg KH
  1 sibling, 0 replies; 110+ messages in thread
From: Greg KH @ 2004-09-08  6:07 UTC (permalink / raw)
  To: Jon Smirl
  Cc: Matthew Wilcox, Jesse Barnes, Martin Mares, Pallipadi, Venkatesh,
	linux-pci, Alan Cox, Linux Kernel Mailing List, Petr Vandrovec,
	Benjamin Herrenschmidt, jonsmirl

On Tue, Sep 07, 2004 at 08:15:37PM -0700, Jon Smirl wrote:
> Greg, are you going to send pci-sysfs-rom-22.patch upstream? Or do I
> need to do it, if so how?

I'll do it.  I'm behind in patches, as I've been on the road for a week.
I'll get to it tommorrow.

Sorry for the delay.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
@ 2004-09-08  3:15 Jon Smirl
  2004-09-08  6:07 ` Greg KH
  2004-09-08 23:50 ` Greg KH
  0 siblings, 2 replies; 110+ messages in thread
From: Jon Smirl @ 2004-09-08  3:15 UTC (permalink / raw)
  To: Greg KH
  Cc: Matthew Wilcox, Jesse Barnes, Martin Mares, Pallipadi, Venkatesh,
	linux-pci, Alan Cox, Linux Kernel Mailing List, Petr Vandrovec,
	Benjamin Herrenschmidt, jonsmirl

[-- Attachment #1: Type: text/plain, Size: 347 bytes --]

Greg, are you going to send pci-sysfs-rom-22.patch upstream? Or do I
need to do it, if so how? Jesse's objection turned out to be a problem
in another piece of code.

=====
Jon Smirl
jonsmirl@yahoo.com


	
		
__________________________________
Do you Yahoo!?
New and Improved Yahoo! Mail - 100MB free storage!
http://promotions.yahoo.com/new_mail 

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: pci-sysfs-rom-22.patch --]
[-- Type: text/x-patch; name="pci-sysfs-rom-22.patch", Size: 16197 bytes --]

Exposes PCI ROMs via sysfs. Four new routines for drivers to use when 
accessing ROMs: pci_map_rom, pci_map_rom_copy, pci_unmap_rom, pci_remove_rom. 
Handles shadow ROMs for laptops that compress actual ROMs.

Signed-off-by: "Jon Smirl" <jonsmirl@yahoo.com>
diff -Nru a/arch/i386/pci/fixup.c b/arch/i386/pci/fixup.c
--- a/arch/i386/pci/fixup.c	Sun Aug 29 00:22:21 2004
+++ b/arch/i386/pci/fixup.c	Sun Aug 29 00:22:21 2004
@@ -255,3 +255,41 @@
 }
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2, pci_fixup_nforce2);
 
+/*
+ * Fixup to mark boot BIOS video selected by BIOS before it changes
+ *
+ * From information provided by "Jon Smirl" <jonsmirl@yahoo.com>
+ *
+ * The standard boot ROM sequence for an x86 machine uses the BIOS
+ * to select an initial video card for boot display. This boot video 
+ * card will have it's BIOS copied to C0000 in system RAM. 
+ * IORESOURCE_ROM_SHADOW is used to associate the boot video
+ * card with this copy. On laptops this copy has to be used since
+ * the main ROM may be compressed or combined with another image.
+ * See pci_map_rom() for use of this flag. IORESOURCE_ROM_SHADOW
+ * is marked here since the boot video device will be the only enabled
+ * video device at this point.
+ *
+ */static void __devinit pci_fixup_video(struct pci_dev *pdev)
+{
+	struct pci_dev *bridge;
+	struct pci_bus *bus;
+	u16 l;
+
+	if ((pdev->class >> 8) != PCI_CLASS_DISPLAY_VGA)
+		return;
+
+	/* Is VGA routed to us? */
+	bus = pdev->bus;
+	while (bus) {
+		bridge = bus->self;
+		if (bridge) {
+			pci_read_config_word(bridge, PCI_BRIDGE_CONTROL, &l);
+			if (!(l & PCI_BRIDGE_CTL_VGA))
+				return;
+		}
+		bus = bus->parent;
+	}
+	pdev->resource[PCI_ROM_RESOURCE].flags |= IORESOURCE_ROM_SHADOW;
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, pci_fixup_video);
diff -Nru a/drivers/pci/Makefile b/drivers/pci/Makefile
--- a/drivers/pci/Makefile	Sun Aug 29 00:22:21 2004
+++ b/drivers/pci/Makefile	Sun Aug 29 00:22:21 2004
@@ -3,7 +3,8 @@
 #
 
 obj-y		+= access.o bus.o probe.o remove.o pci.o quirks.o \
-			names.o pci-driver.o search.o pci-sysfs.o
+			names.o pci-driver.o search.o pci-sysfs.o \
+			rom.o
 obj-$(CONFIG_PROC_FS) += proc.o
 
 ifndef CONFIG_SPARC64
diff -Nru a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
--- a/drivers/pci/pci-sysfs.c	Sun Aug 29 00:22:21 2004
+++ b/drivers/pci/pci-sysfs.c	Sun Aug 29 00:22:21 2004
@@ -5,6 +5,8 @@
  * (C) Copyright 2002-2004 IBM Corp.
  * (C) Copyright 2003 Matthew Wilcox
  * (C) Copyright 2003 Hewlett-Packard
+ * (C) Copyright 2004 Jon Smirl <jonsmirl@yahoo.com>
+ * (C) Copyright 2004 Silicon Graphics, Inc. Jesse Barnes <jbarnes@sgi.com>
  *
  * File attributes for PCI devices
  *
@@ -20,6 +22,8 @@
 
 #include "pci.h"
 
+static int sysfs_initialized;	/* = 0 */
+
 /* show configuration fields */
 #define pci_config_attr(field, format_string)				\
 static ssize_t								\
@@ -164,6 +168,40 @@
 	return count;
 }
 
+/**
+ * pci_read_rom - read a PCI ROM
+ * @kobj: kernel object handle
+ * @buf: where to put the data we read from the ROM
+ * @off: file offset
+ * @count: number of bytes to read
+ *
+ * Put @count bytes starting at @off into @buf from the ROM in the PCI
+ * device corresponding to @kobj.
+ */
+static ssize_t
+pci_read_rom(struct kobject *kobj, char *buf, loff_t off, size_t count)
+{
+	struct pci_dev *pdev = to_pci_dev(container_of(kobj, struct device, kobj));
+	unsigned char *rom;
+	size_t size;
+	
+	rom = pci_map_rom(pdev, &size);	/* size starts out as PCI window size */
+	if (!rom)
+		return 0;
+		
+	if (off >= size)
+		count = 0;
+	else {
+		if (off + count > size)
+			count = size - off;
+		
+		memcpy_fromio(buf, rom + off, count);
+	}
+	pci_unmap_rom(pdev, rom);
+		
+	return count;
+}
+
 static struct bin_attribute pci_config_attr = {
 	.attr =	{
 		.name = "config",
@@ -186,13 +224,67 @@
 	.write = pci_write_config,
 };
 
-void pci_create_sysfs_dev_files (struct pci_dev *pdev)
+int pci_create_sysfs_dev_files (struct pci_dev *pdev)
 {
+	if (!sysfs_initialized)
+		return -EACCES;
+
 	if (pdev->cfg_size < 4096)
 		sysfs_create_bin_file(&pdev->dev.kobj, &pci_config_attr);
 	else
 		sysfs_create_bin_file(&pdev->dev.kobj, &pcie_config_attr);
 
+	/* If the device has a ROM, try to expose it in sysfs. */
+	if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) {
+		struct bin_attribute *rom_attr;
+		
+		rom_attr = kmalloc(sizeof(*rom_attr), GFP_ATOMIC);
+		if (rom_attr) {
+			pdev->rom_attr = rom_attr;
+			rom_attr->size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
+			rom_attr->attr.name = "rom";
+			rom_attr->attr.mode = S_IRUSR;
+			rom_attr->attr.owner = THIS_MODULE;
+			rom_attr->read = pci_read_rom;
+			sysfs_create_bin_file(&pdev->dev.kobj, rom_attr);
+		}
+	}
 	/* add platform-specific attributes */
 	pcibios_add_platform_entries(pdev);
+	
+	return 0;
+}
+
+/**
+ * pci_remove_sysfs_dev_files - cleanup PCI specific sysfs files
+ * @pdev: device whose entries we should free
+ *
+ * Cleanup when @pdev is removed from sysfs.
+ */
+void pci_remove_sysfs_dev_files(struct pci_dev *pdev)
+{
+	if (pdev->cfg_size < 4096)
+		sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr);
+	else
+		sysfs_remove_bin_file(&pdev->dev.kobj, &pcie_config_attr);
+
+	if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) {
+		if (pdev->rom_attr) {
+			sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr);
+			kfree(pdev->rom_attr);
+		}
+	}
+}
+
+static int __init pci_sysfs_init(void)
+{
+	struct pci_dev *pdev = NULL;
+	
+	sysfs_initialized = 1;
+	while ((pdev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pdev)) != NULL)
+		pci_create_sysfs_dev_files(pdev);
+
+	return 0;
 }
+
+__initcall(pci_sysfs_init);
diff -Nru a/drivers/pci/pci.h b/drivers/pci/pci.h
--- a/drivers/pci/pci.h	Sun Aug 29 00:22:21 2004
+++ b/drivers/pci/pci.h	Sun Aug 29 00:22:21 2004
@@ -2,7 +2,9 @@
 
 extern int pci_hotplug (struct device *dev, char **envp, int num_envp,
 			 char *buffer, int buffer_size);
-extern void pci_create_sysfs_dev_files(struct pci_dev *pdev);
+extern int pci_create_sysfs_dev_files(struct pci_dev *pdev);
+extern void pci_remove_sysfs_dev_files(struct pci_dev *pdev);
+extern void pci_cleanup_rom(struct pci_dev *dev);
 extern int pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
 				  unsigned long size, unsigned long align,
 				  unsigned long min, unsigned int type_mask,
diff -Nru a/drivers/pci/probe.c b/drivers/pci/probe.c
--- a/drivers/pci/probe.c	Sun Aug 29 00:22:21 2004
+++ b/drivers/pci/probe.c	Sun Aug 29 00:22:21 2004
@@ -170,7 +170,7 @@
 		if (sz && sz != 0xffffffff) {
 			sz = pci_size(l, sz, PCI_ROM_ADDRESS_MASK);
 			if (sz) {
-				res->flags = (l & PCI_ROM_ADDRESS_ENABLE) |
+				res->flags = (l & IORESOURCE_ROM_ENABLE) |
 				  IORESOURCE_MEM | IORESOURCE_PREFETCH |
 				  IORESOURCE_READONLY | IORESOURCE_CACHEABLE;
 				res->start = l & PCI_ROM_ADDRESS_MASK;
diff -Nru a/drivers/pci/remove.c b/drivers/pci/remove.c
--- a/drivers/pci/remove.c	Sun Aug 29 00:22:21 2004
+++ b/drivers/pci/remove.c	Sun Aug 29 00:22:21 2004
@@ -16,6 +16,7 @@
 
  	msi_remove_pci_irq_vectors(dev);
 
+	pci_cleanup_rom(dev);
 	for (i = 0; i < PCI_NUM_RESOURCES; i++) {
 		struct resource *res = dev->resource + i;
 		if (res->parent)
@@ -26,6 +27,7 @@
 static void pci_destroy_dev(struct pci_dev *dev)
 {
 	pci_proc_detach_device(dev);
+	pci_remove_sysfs_dev_files(dev);
 	device_unregister(&dev->dev);
 
 	/* Remove the device from the device lists, and prevent any further
diff -Nru a/drivers/pci/rom.c b/drivers/pci/rom.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/pci/rom.c	Sun Aug 29 00:22:21 2004
@@ -0,0 +1,226 @@
+/*
+ * drivers/pci/rom.c
+ *
+ * (C) Copyright 2004 Jon Smirl <jonsmirl@yahoo.com>
+ * (C) Copyright 2004 Silicon Graphics, Inc. Jesse Barnes <jbarnes@sgi.com>
+ *
+ * PCI ROM access routines
+ *
+ */
+
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+
+#include "pci.h"
+
+/**
+ * pci_enable_rom - enable ROM decoding for a PCI device
+ * @dev: PCI device to enable
+ *
+ * Enable ROM decoding on @dev.  This involves simply turning on the last
+ * bit of the PCI ROM BAR.  Note that some cards may share address decoders
+ * between the ROM and other resources, so enabling it may disable access
+ * to MMIO registers or other card memory.
+ */
+static void
+pci_enable_rom(struct pci_dev *pdev)
+{
+	u32 rom_addr;
+	
+	pci_read_config_dword(pdev, pdev->rom_base_reg, &rom_addr);
+	rom_addr |= PCI_ROM_ADDRESS_ENABLE;
+	pci_write_config_dword(pdev, pdev->rom_base_reg, rom_addr);
+}
+
+/**
+ * pci_disable_rom - disable ROM decoding for a PCI device
+ * @dev: PCI device to disable
+ *
+ * Disable ROM decoding on a PCI device by turning off the last bit in the
+ * ROM BAR.
+ */
+static void
+pci_disable_rom(struct pci_dev *pdev)
+{
+	u32 rom_addr;
+	pci_read_config_dword(pdev, pdev->rom_base_reg, &rom_addr);
+	rom_addr &= ~PCI_ROM_ADDRESS_ENABLE;
+	pci_write_config_dword(pdev, pdev->rom_base_reg, rom_addr);
+}
+
+/**
+ * pci_map_rom - map a PCI ROM to kernel space
+ * @dev: pointer to pci device struct
+ * @size: pointer to receive size of pci window over ROM
+ * @return: kernel virtual pointer to image of ROM
+ *
+ * Map a PCI ROM into kernel space. If ROM is boot video ROM,
+ * the shadow BIOS copy will be returned instead of the 
+ * actual ROM.
+ */
+unsigned char *
+pci_map_rom(struct pci_dev *pdev, size_t *size)
+{
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+	loff_t start;
+	unsigned char *rom, *image;
+	int last_image;
+	
+	if (res->flags & IORESOURCE_ROM_SHADOW) {	/* IORESOURCE_ROM_SHADOW only set on x86 */
+		start = (loff_t)0xC0000; 	/* primary video rom always starts here */
+		*size = 0x20000;		/* cover C000:0 through E000:0 */
+	} else {
+		if (res->flags & IORESOURCE_ROM_COPY) {
+			*size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
+			return (unsigned char *)pci_resource_start(pdev, PCI_ROM_RESOURCE);
+		} else {
+			/* assign the ROM an address if it doesn't have one */
+			if (res->parent == NULL)
+				pci_assign_resource(pdev, PCI_ROM_RESOURCE);
+	
+			start = pci_resource_start(pdev, PCI_ROM_RESOURCE);
+			*size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
+			if (*size == 0)
+				return NULL;
+			
+			/* Enable ROM space decodes */
+			pci_enable_rom(pdev);
+		}
+	}
+	
+	rom = ioremap(start, *size);
+	if (!rom) {
+		/* restore enable if ioremap fails */
+		if (!(res->flags & (IORESOURCE_ROM_ENABLE | IORESOURCE_ROM_SHADOW | IORESOURCE_ROM_COPY)))
+			pci_disable_rom(pdev);
+		return NULL;
+	}		
+
+	/* Try to find the true size of the ROM since sometimes the PCI window */
+	/* size is much larger than the actual size of the ROM. */
+	/* True size is important if the ROM is going to be copied. */
+	image = rom;
+	do {
+		char *pds;
+		/* Standard PCI ROMs start out with these bytes 55 AA */
+		if (readb(image) != 0x55)
+			break;
+		if (readb(image + 1) != 0xAA)
+			break;
+		/* get the PCI data structure and check its signature */
+		pds = image + readw(image + 24);
+		if (readb(pds) != 'P')
+			break;
+		if (readb(pds + 1) != 'C')
+			break;
+		if (readb(pds + 2) != 'I')
+			break;
+		if (readb(pds + 3) != 'R')
+			break;
+		last_image = readb(pds + 21) & 0x80;
+		/* this length is reliable */
+		image += readw(pds + 16) * 512;
+	} while (!last_image);
+
+	*size = image - rom;
+
+	return rom;
+}
+
+/**
+ * pci_map_rom_copy - map a PCI ROM to kernel space, create a copy
+ * @dev: pointer to pci device struct
+ * @size: pointer to receive size of pci window over ROM
+ * @return: kernel virtual pointer to image of ROM
+ *
+ * Map a PCI ROM into kernel space. If ROM is boot video ROM,
+ * the shadow BIOS copy will be returned instead of the 
+ * actual ROM.
+ */
+unsigned char *
+pci_map_rom_copy(struct pci_dev *pdev, size_t *size)
+{
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+	unsigned char *rom;
+	
+	rom = pci_map_rom(pdev, size);
+	if (!rom)
+		return NULL;
+		
+	if (res->flags & (IORESOURCE_ROM_COPY | IORESOURCE_ROM_SHADOW))
+		return rom;
+		
+	res->start = (unsigned long)kmalloc(*size, GFP_KERNEL);
+	if (!res->start) 
+		return rom;
+
+	res->end = res->start + *size; 
+	memcpy((void*)res->start, rom, *size);
+	pci_unmap_rom(pdev, rom);
+	res->flags |= IORESOURCE_ROM_COPY;
+	
+	return (unsigned char *)res->start;
+}
+
+/**
+ * pci_unmap_rom - unmap the ROM from kernel space
+ * @dev: pointer to pci device struct
+ * @rom: virtual address of the previous mapping
+ *
+ * Remove a mapping of a previously mapped ROM
+ */
+void 
+pci_unmap_rom(struct pci_dev *pdev, unsigned char *rom)
+{
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+
+	if (res->flags & IORESOURCE_ROM_COPY)
+		return;
+		
+	iounmap(rom);
+		
+	/* Disable again before continuing, leave enabled if pci=rom */
+	if (!(res->flags & (IORESOURCE_ROM_ENABLE | IORESOURCE_ROM_SHADOW)))
+		pci_disable_rom(pdev);
+}
+
+/**
+ * pci_remove_rom - disable the ROM and remove its sysfs attribute
+ * @dev: pointer to pci device struct
+ *
+ */
+void 
+pci_remove_rom(struct pci_dev *pdev) 
+{
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+	
+	if (pci_resource_len(pdev, PCI_ROM_RESOURCE))
+		sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr);
+	if (!(res->flags & (IORESOURCE_ROM_ENABLE | IORESOURCE_ROM_SHADOW | IORESOURCE_ROM_COPY)))
+		pci_disable_rom(pdev);
+}
+
+/**
+ * pci_cleanup_rom - internal routine for freeing the ROM copy created 
+ * by pci_map_rom_copy called from remove.c
+ * @dev: pointer to pci device struct
+ *
+ */
+void 
+pci_cleanup_rom(struct pci_dev *pdev) 
+{
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+	if (res->flags & IORESOURCE_ROM_COPY) {
+		kfree((void*)res->start);
+		res->flags &= ~IORESOURCE_ROM_COPY;
+		res->start = 0;
+		res->end = 0;
+	}
+}
+
+EXPORT_SYMBOL(pci_map_rom);
+EXPORT_SYMBOL(pci_map_rom_copy);
+EXPORT_SYMBOL(pci_unmap_rom);
+EXPORT_SYMBOL(pci_remove_rom);
diff -Nru a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c
--- a/drivers/pci/setup-res.c	Sun Aug 29 00:22:21 2004
+++ b/drivers/pci/setup-res.c	Sun Aug 29 00:22:21 2004
@@ -56,7 +56,7 @@
 	if (resno < 6) {
 		reg = PCI_BASE_ADDRESS_0 + 4 * resno;
 	} else if (resno == PCI_ROM_RESOURCE) {
-		new |= res->flags & PCI_ROM_ADDRESS_ENABLE;
+		new |= res->flags & IORESOURCE_ROM_ENABLE;
 		reg = dev->rom_base_reg;
 	} else {
 		/* Hmm, non-standard resource. */
diff -Nru a/include/linux/ioport.h b/include/linux/ioport.h
--- a/include/linux/ioport.h	Sun Aug 29 00:22:21 2004
+++ b/include/linux/ioport.h	Sun Aug 29 00:22:21 2004
@@ -82,6 +82,11 @@
 #define IORESOURCE_MEM_SHADOWABLE	(1<<5)	/* dup: IORESOURCE_SHADOWABLE */
 #define IORESOURCE_MEM_EXPANSIONROM	(1<<6)
 
+/* PCI ROM control bits (IORESOURCE_BITS) */
+#define IORESOURCE_ROM_ENABLE		(1<<0)	/* ROM is enabled, same as PCI_ROM_ADDRESS_ENABLE */
+#define IORESOURCE_ROM_SHADOW		(1<<1)	/* ROM is copy at C000:0 */
+#define IORESOURCE_ROM_COPY		(1<<2)	/* ROM is alloc'd copy, resource field overlaid */
+
 /* PC/ISA/whatever - the normal PC address spaces: IO and memory */
 extern struct resource ioport_resource;
 extern struct resource iomem_resource;
diff -Nru a/include/linux/pci.h b/include/linux/pci.h
--- a/include/linux/pci.h	Sun Aug 29 00:22:21 2004
+++ b/include/linux/pci.h	Sun Aug 29 00:22:21 2004
@@ -537,6 +537,7 @@
 	unsigned int	is_busmaster:1; /* device is busmaster */
 	
 	u32		saved_config_space[16]; /* config space saved at suspend time */
+	struct bin_attribute *rom_attr; /* attribute descriptor for sysfs ROM entry */
 #ifdef CONFIG_PCI_NAMES
 #define PCI_NAME_SIZE	96
 #define PCI_NAME_HALF	__stringify(43)	/* less than half to handle slop */
@@ -777,6 +778,12 @@
 int pci_dac_set_dma_mask(struct pci_dev *dev, u64 mask);
 int pci_set_consistent_dma_mask(struct pci_dev *dev, u64 mask);
 int pci_assign_resource(struct pci_dev *dev, int i);
+
+/* ROM control related routines */
+unsigned char *pci_map_rom(struct pci_dev *pdev, size_t *size);
+unsigned char *pci_map_rom_copy(struct pci_dev *pdev, size_t *size);
+void pci_unmap_rom(struct pci_dev *pdev, unsigned char *rom);
+void pci_remove_rom(struct pci_dev *pdev);
 
 /* Power management related routines */
 int pci_save_state(struct pci_dev *dev, u32 *buffer);

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-09-03 17:45   ` Jesse Barnes
@ 2004-09-03 18:06     ` Jesse Barnes
  0 siblings, 0 replies; 110+ messages in thread
From: Jesse Barnes @ 2004-09-03 18:06 UTC (permalink / raw)
  To: linux-pci
  Cc: Jon Smirl, Greg KH, Matthew Wilcox, Martin Mares, Pallipadi,
	Venkatesh, Alan Cox, Linux Kernel Mailing List, Petr Vandrovec,
	Benjamin Herrenschmidt

On Friday, September 3, 2004 10:45 am, Jesse Barnes wrote:
> On Friday, September 3, 2004 10:27 am, Jesse Barnes wrote:
> > On Thursday, September 2, 2004 6:40 pm, Jon Smirl wrote:
> > > This is a repost of the pci-sysfs-rom-22.patch. No one has made any
> > > comments on this version. All previous objections have been addressed.
> > > Any objections to sending it upstream?
> >
> > Hm, the last one I tried worked fine, but this one makes my qla card stop
> > working, but not right way.  The system gets to init and then falls over,
> > maybe when it starts doing writes?  The last version I tried seems to
> > work ok though.  Has something changed in the PCI layer that would affect
> > this?
>
> It looks like hald is reading the rom attribute at boot, which either
> disables decode for the qla mmio registers or otherwise panics.  Bad hald.

After disabling hald, things are ok.  I can read ROMs that have been mapped 
correctly.  The problem was that sn2 doesn't map all ROMs by default, so the 
qla card, which has a ROM, had it's resource structure filled with the value 
in its BARs, which was of course not a usable CPU address.  I can fix that 
for sn2, so the patch is ok with me.

Jesse

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-09-03 17:27 ` Jesse Barnes
@ 2004-09-03 17:45   ` Jesse Barnes
  2004-09-03 18:06     ` Jesse Barnes
  0 siblings, 1 reply; 110+ messages in thread
From: Jesse Barnes @ 2004-09-03 17:45 UTC (permalink / raw)
  To: linux-pci
  Cc: Jon Smirl, Greg KH, Matthew Wilcox, Martin Mares, Pallipadi,
	Venkatesh, Alan Cox, Linux Kernel Mailing List, Petr Vandrovec,
	Benjamin Herrenschmidt

On Friday, September 3, 2004 10:27 am, Jesse Barnes wrote:
> On Thursday, September 2, 2004 6:40 pm, Jon Smirl wrote:
> > This is a repost of the pci-sysfs-rom-22.patch. No one has made any
> > comments on this version. All previous objections have been addressed.
> > Any objections to sending it upstream?
>
> Hm, the last one I tried worked fine, but this one makes my qla card stop
> working, but not right way.  The system gets to init and then falls over,
> maybe when it starts doing writes?  The last version I tried seems to work
> ok though.  Has something changed in the PCI layer that would affect this?

It looks like hald is reading the rom attribute at boot, which either disables 
decode for the qla mmio registers or otherwise panics.  Bad hald.

Jesse

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-09-03  1:40 Jon Smirl
@ 2004-09-03 17:27 ` Jesse Barnes
  2004-09-03 17:45   ` Jesse Barnes
  0 siblings, 1 reply; 110+ messages in thread
From: Jesse Barnes @ 2004-09-03 17:27 UTC (permalink / raw)
  To: Jon Smirl
  Cc: Greg KH, Matthew Wilcox, Martin Mares, Pallipadi, Venkatesh,
	linux-pci, Alan Cox, Linux Kernel Mailing List, Petr Vandrovec,
	Benjamin Herrenschmidt

On Thursday, September 2, 2004 6:40 pm, Jon Smirl wrote:
> This is a repost of the pci-sysfs-rom-22.patch. No one has made any
> comments on this version. All previous objections have been addressed.
> Any objections to sending it upstream?

Hm, the last one I tried worked fine, but this one makes my qla card stop 
working, but not right way.  The system gets to init and then falls over, 
maybe when it starts doing writes?  The last version I tried seems to work ok 
though.  Has something changed in the PCI layer that would affect this?

Thanks,
Jesse

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
@ 2004-09-03  1:40 Jon Smirl
  2004-09-03 17:27 ` Jesse Barnes
  0 siblings, 1 reply; 110+ messages in thread
From: Jon Smirl @ 2004-09-03  1:40 UTC (permalink / raw)
  To: Greg KH
  Cc: Matthew Wilcox, Jesse Barnes, Martin Mares, Pallipadi, Venkatesh,
	linux-pci, Alan Cox, Linux Kernel Mailing List, Petr Vandrovec,
	Benjamin Herrenschmidt

[-- Attachment #1: Type: text/plain, Size: 361 bytes --]

This is a repost of the pci-sysfs-rom-22.patch. No one has made any
comments on this version. All previous objections have been addressed.
Any objections to sending it upstream?




=====
Jon Smirl
jonsmirl@yahoo.com


		
_______________________________
Do you Yahoo!?
Win 1 of 4,000 free domain names from Yahoo! Enter now.
http://promotions.yahoo.com/goldrush

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: pci-sysfs-rom-22.patch --]
[-- Type: text/x-patch; name="pci-sysfs-rom-22.patch", Size: 16197 bytes --]

Exposes PCI ROMs via sysfs. Four new routines for drivers to use when 
accessing ROMs: pci_map_rom, pci_map_rom_copy, pci_unmap_rom, pci_remove_rom. 
Handles shadow ROMs for laptops that compress actual ROMs.

Signed-off-by: "Jon Smirl" <jonsmirl@yahoo.com>
diff -Nru a/arch/i386/pci/fixup.c b/arch/i386/pci/fixup.c
--- a/arch/i386/pci/fixup.c	Sun Aug 29 00:22:21 2004
+++ b/arch/i386/pci/fixup.c	Sun Aug 29 00:22:21 2004
@@ -255,3 +255,41 @@
 }
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2, pci_fixup_nforce2);
 
+/*
+ * Fixup to mark boot BIOS video selected by BIOS before it changes
+ *
+ * From information provided by "Jon Smirl" <jonsmirl@yahoo.com>
+ *
+ * The standard boot ROM sequence for an x86 machine uses the BIOS
+ * to select an initial video card for boot display. This boot video 
+ * card will have it's BIOS copied to C0000 in system RAM. 
+ * IORESOURCE_ROM_SHADOW is used to associate the boot video
+ * card with this copy. On laptops this copy has to be used since
+ * the main ROM may be compressed or combined with another image.
+ * See pci_map_rom() for use of this flag. IORESOURCE_ROM_SHADOW
+ * is marked here since the boot video device will be the only enabled
+ * video device at this point.
+ *
+ */static void __devinit pci_fixup_video(struct pci_dev *pdev)
+{
+	struct pci_dev *bridge;
+	struct pci_bus *bus;
+	u16 l;
+
+	if ((pdev->class >> 8) != PCI_CLASS_DISPLAY_VGA)
+		return;
+
+	/* Is VGA routed to us? */
+	bus = pdev->bus;
+	while (bus) {
+		bridge = bus->self;
+		if (bridge) {
+			pci_read_config_word(bridge, PCI_BRIDGE_CONTROL, &l);
+			if (!(l & PCI_BRIDGE_CTL_VGA))
+				return;
+		}
+		bus = bus->parent;
+	}
+	pdev->resource[PCI_ROM_RESOURCE].flags |= IORESOURCE_ROM_SHADOW;
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, pci_fixup_video);
diff -Nru a/drivers/pci/Makefile b/drivers/pci/Makefile
--- a/drivers/pci/Makefile	Sun Aug 29 00:22:21 2004
+++ b/drivers/pci/Makefile	Sun Aug 29 00:22:21 2004
@@ -3,7 +3,8 @@
 #
 
 obj-y		+= access.o bus.o probe.o remove.o pci.o quirks.o \
-			names.o pci-driver.o search.o pci-sysfs.o
+			names.o pci-driver.o search.o pci-sysfs.o \
+			rom.o
 obj-$(CONFIG_PROC_FS) += proc.o
 
 ifndef CONFIG_SPARC64
diff -Nru a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
--- a/drivers/pci/pci-sysfs.c	Sun Aug 29 00:22:21 2004
+++ b/drivers/pci/pci-sysfs.c	Sun Aug 29 00:22:21 2004
@@ -5,6 +5,8 @@
  * (C) Copyright 2002-2004 IBM Corp.
  * (C) Copyright 2003 Matthew Wilcox
  * (C) Copyright 2003 Hewlett-Packard
+ * (C) Copyright 2004 Jon Smirl <jonsmirl@yahoo.com>
+ * (C) Copyright 2004 Silicon Graphics, Inc. Jesse Barnes <jbarnes@sgi.com>
  *
  * File attributes for PCI devices
  *
@@ -20,6 +22,8 @@
 
 #include "pci.h"
 
+static int sysfs_initialized;	/* = 0 */
+
 /* show configuration fields */
 #define pci_config_attr(field, format_string)				\
 static ssize_t								\
@@ -164,6 +168,40 @@
 	return count;
 }
 
+/**
+ * pci_read_rom - read a PCI ROM
+ * @kobj: kernel object handle
+ * @buf: where to put the data we read from the ROM
+ * @off: file offset
+ * @count: number of bytes to read
+ *
+ * Put @count bytes starting at @off into @buf from the ROM in the PCI
+ * device corresponding to @kobj.
+ */
+static ssize_t
+pci_read_rom(struct kobject *kobj, char *buf, loff_t off, size_t count)
+{
+	struct pci_dev *pdev = to_pci_dev(container_of(kobj, struct device, kobj));
+	unsigned char *rom;
+	size_t size;
+	
+	rom = pci_map_rom(pdev, &size);	/* size starts out as PCI window size */
+	if (!rom)
+		return 0;
+		
+	if (off >= size)
+		count = 0;
+	else {
+		if (off + count > size)
+			count = size - off;
+		
+		memcpy_fromio(buf, rom + off, count);
+	}
+	pci_unmap_rom(pdev, rom);
+		
+	return count;
+}
+
 static struct bin_attribute pci_config_attr = {
 	.attr =	{
 		.name = "config",
@@ -186,13 +224,67 @@
 	.write = pci_write_config,
 };
 
-void pci_create_sysfs_dev_files (struct pci_dev *pdev)
+int pci_create_sysfs_dev_files (struct pci_dev *pdev)
 {
+	if (!sysfs_initialized)
+		return -EACCES;
+
 	if (pdev->cfg_size < 4096)
 		sysfs_create_bin_file(&pdev->dev.kobj, &pci_config_attr);
 	else
 		sysfs_create_bin_file(&pdev->dev.kobj, &pcie_config_attr);
 
+	/* If the device has a ROM, try to expose it in sysfs. */
+	if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) {
+		struct bin_attribute *rom_attr;
+		
+		rom_attr = kmalloc(sizeof(*rom_attr), GFP_ATOMIC);
+		if (rom_attr) {
+			pdev->rom_attr = rom_attr;
+			rom_attr->size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
+			rom_attr->attr.name = "rom";
+			rom_attr->attr.mode = S_IRUSR;
+			rom_attr->attr.owner = THIS_MODULE;
+			rom_attr->read = pci_read_rom;
+			sysfs_create_bin_file(&pdev->dev.kobj, rom_attr);
+		}
+	}
 	/* add platform-specific attributes */
 	pcibios_add_platform_entries(pdev);
+	
+	return 0;
+}
+
+/**
+ * pci_remove_sysfs_dev_files - cleanup PCI specific sysfs files
+ * @pdev: device whose entries we should free
+ *
+ * Cleanup when @pdev is removed from sysfs.
+ */
+void pci_remove_sysfs_dev_files(struct pci_dev *pdev)
+{
+	if (pdev->cfg_size < 4096)
+		sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr);
+	else
+		sysfs_remove_bin_file(&pdev->dev.kobj, &pcie_config_attr);
+
+	if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) {
+		if (pdev->rom_attr) {
+			sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr);
+			kfree(pdev->rom_attr);
+		}
+	}
+}
+
+static int __init pci_sysfs_init(void)
+{
+	struct pci_dev *pdev = NULL;
+	
+	sysfs_initialized = 1;
+	while ((pdev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pdev)) != NULL)
+		pci_create_sysfs_dev_files(pdev);
+
+	return 0;
 }
+
+__initcall(pci_sysfs_init);
diff -Nru a/drivers/pci/pci.h b/drivers/pci/pci.h
--- a/drivers/pci/pci.h	Sun Aug 29 00:22:21 2004
+++ b/drivers/pci/pci.h	Sun Aug 29 00:22:21 2004
@@ -2,7 +2,9 @@
 
 extern int pci_hotplug (struct device *dev, char **envp, int num_envp,
 			 char *buffer, int buffer_size);
-extern void pci_create_sysfs_dev_files(struct pci_dev *pdev);
+extern int pci_create_sysfs_dev_files(struct pci_dev *pdev);
+extern void pci_remove_sysfs_dev_files(struct pci_dev *pdev);
+extern void pci_cleanup_rom(struct pci_dev *dev);
 extern int pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
 				  unsigned long size, unsigned long align,
 				  unsigned long min, unsigned int type_mask,
diff -Nru a/drivers/pci/probe.c b/drivers/pci/probe.c
--- a/drivers/pci/probe.c	Sun Aug 29 00:22:21 2004
+++ b/drivers/pci/probe.c	Sun Aug 29 00:22:21 2004
@@ -170,7 +170,7 @@
 		if (sz && sz != 0xffffffff) {
 			sz = pci_size(l, sz, PCI_ROM_ADDRESS_MASK);
 			if (sz) {
-				res->flags = (l & PCI_ROM_ADDRESS_ENABLE) |
+				res->flags = (l & IORESOURCE_ROM_ENABLE) |
 				  IORESOURCE_MEM | IORESOURCE_PREFETCH |
 				  IORESOURCE_READONLY | IORESOURCE_CACHEABLE;
 				res->start = l & PCI_ROM_ADDRESS_MASK;
diff -Nru a/drivers/pci/remove.c b/drivers/pci/remove.c
--- a/drivers/pci/remove.c	Sun Aug 29 00:22:21 2004
+++ b/drivers/pci/remove.c	Sun Aug 29 00:22:21 2004
@@ -16,6 +16,7 @@
 
  	msi_remove_pci_irq_vectors(dev);
 
+	pci_cleanup_rom(dev);
 	for (i = 0; i < PCI_NUM_RESOURCES; i++) {
 		struct resource *res = dev->resource + i;
 		if (res->parent)
@@ -26,6 +27,7 @@
 static void pci_destroy_dev(struct pci_dev *dev)
 {
 	pci_proc_detach_device(dev);
+	pci_remove_sysfs_dev_files(dev);
 	device_unregister(&dev->dev);
 
 	/* Remove the device from the device lists, and prevent any further
diff -Nru a/drivers/pci/rom.c b/drivers/pci/rom.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/pci/rom.c	Sun Aug 29 00:22:21 2004
@@ -0,0 +1,226 @@
+/*
+ * drivers/pci/rom.c
+ *
+ * (C) Copyright 2004 Jon Smirl <jonsmirl@yahoo.com>
+ * (C) Copyright 2004 Silicon Graphics, Inc. Jesse Barnes <jbarnes@sgi.com>
+ *
+ * PCI ROM access routines
+ *
+ */
+
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+
+#include "pci.h"
+
+/**
+ * pci_enable_rom - enable ROM decoding for a PCI device
+ * @dev: PCI device to enable
+ *
+ * Enable ROM decoding on @dev.  This involves simply turning on the last
+ * bit of the PCI ROM BAR.  Note that some cards may share address decoders
+ * between the ROM and other resources, so enabling it may disable access
+ * to MMIO registers or other card memory.
+ */
+static void
+pci_enable_rom(struct pci_dev *pdev)
+{
+	u32 rom_addr;
+	
+	pci_read_config_dword(pdev, pdev->rom_base_reg, &rom_addr);
+	rom_addr |= PCI_ROM_ADDRESS_ENABLE;
+	pci_write_config_dword(pdev, pdev->rom_base_reg, rom_addr);
+}
+
+/**
+ * pci_disable_rom - disable ROM decoding for a PCI device
+ * @dev: PCI device to disable
+ *
+ * Disable ROM decoding on a PCI device by turning off the last bit in the
+ * ROM BAR.
+ */
+static void
+pci_disable_rom(struct pci_dev *pdev)
+{
+	u32 rom_addr;
+	pci_read_config_dword(pdev, pdev->rom_base_reg, &rom_addr);
+	rom_addr &= ~PCI_ROM_ADDRESS_ENABLE;
+	pci_write_config_dword(pdev, pdev->rom_base_reg, rom_addr);
+}
+
+/**
+ * pci_map_rom - map a PCI ROM to kernel space
+ * @dev: pointer to pci device struct
+ * @size: pointer to receive size of pci window over ROM
+ * @return: kernel virtual pointer to image of ROM
+ *
+ * Map a PCI ROM into kernel space. If ROM is boot video ROM,
+ * the shadow BIOS copy will be returned instead of the 
+ * actual ROM.
+ */
+unsigned char *
+pci_map_rom(struct pci_dev *pdev, size_t *size)
+{
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+	loff_t start;
+	unsigned char *rom, *image;
+	int last_image;
+	
+	if (res->flags & IORESOURCE_ROM_SHADOW) {	/* IORESOURCE_ROM_SHADOW only set on x86 */
+		start = (loff_t)0xC0000; 	/* primary video rom always starts here */
+		*size = 0x20000;		/* cover C000:0 through E000:0 */
+	} else {
+		if (res->flags & IORESOURCE_ROM_COPY) {
+			*size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
+			return (unsigned char *)pci_resource_start(pdev, PCI_ROM_RESOURCE);
+		} else {
+			/* assign the ROM an address if it doesn't have one */
+			if (res->parent == NULL)
+				pci_assign_resource(pdev, PCI_ROM_RESOURCE);
+	
+			start = pci_resource_start(pdev, PCI_ROM_RESOURCE);
+			*size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
+			if (*size == 0)
+				return NULL;
+			
+			/* Enable ROM space decodes */
+			pci_enable_rom(pdev);
+		}
+	}
+	
+	rom = ioremap(start, *size);
+	if (!rom) {
+		/* restore enable if ioremap fails */
+		if (!(res->flags & (IORESOURCE_ROM_ENABLE | IORESOURCE_ROM_SHADOW | IORESOURCE_ROM_COPY)))
+			pci_disable_rom(pdev);
+		return NULL;
+	}		
+
+	/* Try to find the true size of the ROM since sometimes the PCI window */
+	/* size is much larger than the actual size of the ROM. */
+	/* True size is important if the ROM is going to be copied. */
+	image = rom;
+	do {
+		char *pds;
+		/* Standard PCI ROMs start out with these bytes 55 AA */
+		if (readb(image) != 0x55)
+			break;
+		if (readb(image + 1) != 0xAA)
+			break;
+		/* get the PCI data structure and check its signature */
+		pds = image + readw(image + 24);
+		if (readb(pds) != 'P')
+			break;
+		if (readb(pds + 1) != 'C')
+			break;
+		if (readb(pds + 2) != 'I')
+			break;
+		if (readb(pds + 3) != 'R')
+			break;
+		last_image = readb(pds + 21) & 0x80;
+		/* this length is reliable */
+		image += readw(pds + 16) * 512;
+	} while (!last_image);
+
+	*size = image - rom;
+
+	return rom;
+}
+
+/**
+ * pci_map_rom_copy - map a PCI ROM to kernel space, create a copy
+ * @dev: pointer to pci device struct
+ * @size: pointer to receive size of pci window over ROM
+ * @return: kernel virtual pointer to image of ROM
+ *
+ * Map a PCI ROM into kernel space. If ROM is boot video ROM,
+ * the shadow BIOS copy will be returned instead of the 
+ * actual ROM.
+ */
+unsigned char *
+pci_map_rom_copy(struct pci_dev *pdev, size_t *size)
+{
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+	unsigned char *rom;
+	
+	rom = pci_map_rom(pdev, size);
+	if (!rom)
+		return NULL;
+		
+	if (res->flags & (IORESOURCE_ROM_COPY | IORESOURCE_ROM_SHADOW))
+		return rom;
+		
+	res->start = (unsigned long)kmalloc(*size, GFP_KERNEL);
+	if (!res->start) 
+		return rom;
+
+	res->end = res->start + *size; 
+	memcpy((void*)res->start, rom, *size);
+	pci_unmap_rom(pdev, rom);
+	res->flags |= IORESOURCE_ROM_COPY;
+	
+	return (unsigned char *)res->start;
+}
+
+/**
+ * pci_unmap_rom - unmap the ROM from kernel space
+ * @dev: pointer to pci device struct
+ * @rom: virtual address of the previous mapping
+ *
+ * Remove a mapping of a previously mapped ROM
+ */
+void 
+pci_unmap_rom(struct pci_dev *pdev, unsigned char *rom)
+{
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+
+	if (res->flags & IORESOURCE_ROM_COPY)
+		return;
+		
+	iounmap(rom);
+		
+	/* Disable again before continuing, leave enabled if pci=rom */
+	if (!(res->flags & (IORESOURCE_ROM_ENABLE | IORESOURCE_ROM_SHADOW)))
+		pci_disable_rom(pdev);
+}
+
+/**
+ * pci_remove_rom - disable the ROM and remove its sysfs attribute
+ * @dev: pointer to pci device struct
+ *
+ */
+void 
+pci_remove_rom(struct pci_dev *pdev) 
+{
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+	
+	if (pci_resource_len(pdev, PCI_ROM_RESOURCE))
+		sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr);
+	if (!(res->flags & (IORESOURCE_ROM_ENABLE | IORESOURCE_ROM_SHADOW | IORESOURCE_ROM_COPY)))
+		pci_disable_rom(pdev);
+}
+
+/**
+ * pci_cleanup_rom - internal routine for freeing the ROM copy created 
+ * by pci_map_rom_copy called from remove.c
+ * @dev: pointer to pci device struct
+ *
+ */
+void 
+pci_cleanup_rom(struct pci_dev *pdev) 
+{
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+	if (res->flags & IORESOURCE_ROM_COPY) {
+		kfree((void*)res->start);
+		res->flags &= ~IORESOURCE_ROM_COPY;
+		res->start = 0;
+		res->end = 0;
+	}
+}
+
+EXPORT_SYMBOL(pci_map_rom);
+EXPORT_SYMBOL(pci_map_rom_copy);
+EXPORT_SYMBOL(pci_unmap_rom);
+EXPORT_SYMBOL(pci_remove_rom);
diff -Nru a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c
--- a/drivers/pci/setup-res.c	Sun Aug 29 00:22:21 2004
+++ b/drivers/pci/setup-res.c	Sun Aug 29 00:22:21 2004
@@ -56,7 +56,7 @@
 	if (resno < 6) {
 		reg = PCI_BASE_ADDRESS_0 + 4 * resno;
 	} else if (resno == PCI_ROM_RESOURCE) {
-		new |= res->flags & PCI_ROM_ADDRESS_ENABLE;
+		new |= res->flags & IORESOURCE_ROM_ENABLE;
 		reg = dev->rom_base_reg;
 	} else {
 		/* Hmm, non-standard resource. */
diff -Nru a/include/linux/ioport.h b/include/linux/ioport.h
--- a/include/linux/ioport.h	Sun Aug 29 00:22:21 2004
+++ b/include/linux/ioport.h	Sun Aug 29 00:22:21 2004
@@ -82,6 +82,11 @@
 #define IORESOURCE_MEM_SHADOWABLE	(1<<5)	/* dup: IORESOURCE_SHADOWABLE */
 #define IORESOURCE_MEM_EXPANSIONROM	(1<<6)
 
+/* PCI ROM control bits (IORESOURCE_BITS) */
+#define IORESOURCE_ROM_ENABLE		(1<<0)	/* ROM is enabled, same as PCI_ROM_ADDRESS_ENABLE */
+#define IORESOURCE_ROM_SHADOW		(1<<1)	/* ROM is copy at C000:0 */
+#define IORESOURCE_ROM_COPY		(1<<2)	/* ROM is alloc'd copy, resource field overlaid */
+
 /* PC/ISA/whatever - the normal PC address spaces: IO and memory */
 extern struct resource ioport_resource;
 extern struct resource iomem_resource;
diff -Nru a/include/linux/pci.h b/include/linux/pci.h
--- a/include/linux/pci.h	Sun Aug 29 00:22:21 2004
+++ b/include/linux/pci.h	Sun Aug 29 00:22:21 2004
@@ -537,6 +537,7 @@
 	unsigned int	is_busmaster:1; /* device is busmaster */
 	
 	u32		saved_config_space[16]; /* config space saved at suspend time */
+	struct bin_attribute *rom_attr; /* attribute descriptor for sysfs ROM entry */
 #ifdef CONFIG_PCI_NAMES
 #define PCI_NAME_SIZE	96
 #define PCI_NAME_HALF	__stringify(43)	/* less than half to handle slop */
@@ -777,6 +778,12 @@
 int pci_dac_set_dma_mask(struct pci_dev *dev, u64 mask);
 int pci_set_consistent_dma_mask(struct pci_dev *dev, u64 mask);
 int pci_assign_resource(struct pci_dev *dev, int i);
+
+/* ROM control related routines */
+unsigned char *pci_map_rom(struct pci_dev *pdev, size_t *size);
+unsigned char *pci_map_rom_copy(struct pci_dev *pdev, size_t *size);
+void pci_unmap_rom(struct pci_dev *pdev, unsigned char *rom);
+void pci_remove_rom(struct pci_dev *pdev);
 
 /* Power management related routines */
 int pci_save_state(struct pci_dev *dev, u32 *buffer);

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
@ 2004-08-29  4:58 Jon Smirl
  0 siblings, 0 replies; 110+ messages in thread
From: Jon Smirl @ 2004-08-29  4:58 UTC (permalink / raw)
  To: Matthew Wilcox
  Cc: Greg KH, Jesse Barnes, Martin Mares, Pallipadi, Venkatesh,
	linux-pci, Alan Cox, Linux Kernel Mailing List, Petr Vandrovec,
	Benjamin Herrenschmidt

[-- Attachment #1: Type: text/plain, Size: 851 bytes --]

On Sat, 28 Aug 2004 17:15:10 +0100, Matthew Wilcox <willy@debian.org>
wrote:
> BTW, what do you think to moving all the ROM code from pci-sysfs.c to
a
> new file rom.c?  There's not much sense to the ROM accessing code
being
> mixed in with the sysfs code like this.  After all, we might choose
to
> expose the ROM in some other way in the future.

After thinking about this for a while it's a good idea. New patch
splits core ROM support into a new file, rom.c.

Patch applies against both current Linus bk and mm now.

If the ROM attribute on a Radeon card is empty, modprobe in the new
radeonfb driver. The driver will correct a hardware bug and the ROM
will appear.

=====
Jon Smirl
jonsmirl@yahoo.com


	
		
__________________________________
Do you Yahoo!?
New and Improved Yahoo! Mail - 100MB free storage!
http://promotions.yahoo.com/new_mail 

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: pci-sysfs-rom-22.patch --]
[-- Type: text/x-patch; name="pci-sysfs-rom-22.patch", Size: 16197 bytes --]

Exposes PCI ROMs via sysfs. Four new routines for drivers to use when 
accessing ROMs: pci_map_rom, pci_map_rom_copy, pci_unmap_rom, pci_remove_rom. 
Handles shadow ROMs for laptops that compress actual ROMs.

Signed-off-by: "Jon Smirl" <jonsmirl@yahoo.com>
diff -Nru a/arch/i386/pci/fixup.c b/arch/i386/pci/fixup.c
--- a/arch/i386/pci/fixup.c	Sun Aug 29 00:22:21 2004
+++ b/arch/i386/pci/fixup.c	Sun Aug 29 00:22:21 2004
@@ -255,3 +255,41 @@
 }
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2, pci_fixup_nforce2);
 
+/*
+ * Fixup to mark boot BIOS video selected by BIOS before it changes
+ *
+ * From information provided by "Jon Smirl" <jonsmirl@yahoo.com>
+ *
+ * The standard boot ROM sequence for an x86 machine uses the BIOS
+ * to select an initial video card for boot display. This boot video 
+ * card will have it's BIOS copied to C0000 in system RAM. 
+ * IORESOURCE_ROM_SHADOW is used to associate the boot video
+ * card with this copy. On laptops this copy has to be used since
+ * the main ROM may be compressed or combined with another image.
+ * See pci_map_rom() for use of this flag. IORESOURCE_ROM_SHADOW
+ * is marked here since the boot video device will be the only enabled
+ * video device at this point.
+ *
+ */static void __devinit pci_fixup_video(struct pci_dev *pdev)
+{
+	struct pci_dev *bridge;
+	struct pci_bus *bus;
+	u16 l;
+
+	if ((pdev->class >> 8) != PCI_CLASS_DISPLAY_VGA)
+		return;
+
+	/* Is VGA routed to us? */
+	bus = pdev->bus;
+	while (bus) {
+		bridge = bus->self;
+		if (bridge) {
+			pci_read_config_word(bridge, PCI_BRIDGE_CONTROL, &l);
+			if (!(l & PCI_BRIDGE_CTL_VGA))
+				return;
+		}
+		bus = bus->parent;
+	}
+	pdev->resource[PCI_ROM_RESOURCE].flags |= IORESOURCE_ROM_SHADOW;
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, pci_fixup_video);
diff -Nru a/drivers/pci/Makefile b/drivers/pci/Makefile
--- a/drivers/pci/Makefile	Sun Aug 29 00:22:21 2004
+++ b/drivers/pci/Makefile	Sun Aug 29 00:22:21 2004
@@ -3,7 +3,8 @@
 #
 
 obj-y		+= access.o bus.o probe.o remove.o pci.o quirks.o \
-			names.o pci-driver.o search.o pci-sysfs.o
+			names.o pci-driver.o search.o pci-sysfs.o \
+			rom.o
 obj-$(CONFIG_PROC_FS) += proc.o
 
 ifndef CONFIG_SPARC64
diff -Nru a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
--- a/drivers/pci/pci-sysfs.c	Sun Aug 29 00:22:21 2004
+++ b/drivers/pci/pci-sysfs.c	Sun Aug 29 00:22:21 2004
@@ -5,6 +5,8 @@
  * (C) Copyright 2002-2004 IBM Corp.
  * (C) Copyright 2003 Matthew Wilcox
  * (C) Copyright 2003 Hewlett-Packard
+ * (C) Copyright 2004 Jon Smirl <jonsmirl@yahoo.com>
+ * (C) Copyright 2004 Silicon Graphics, Inc. Jesse Barnes <jbarnes@sgi.com>
  *
  * File attributes for PCI devices
  *
@@ -20,6 +22,8 @@
 
 #include "pci.h"
 
+static int sysfs_initialized;	/* = 0 */
+
 /* show configuration fields */
 #define pci_config_attr(field, format_string)				\
 static ssize_t								\
@@ -164,6 +168,40 @@
 	return count;
 }
 
+/**
+ * pci_read_rom - read a PCI ROM
+ * @kobj: kernel object handle
+ * @buf: where to put the data we read from the ROM
+ * @off: file offset
+ * @count: number of bytes to read
+ *
+ * Put @count bytes starting at @off into @buf from the ROM in the PCI
+ * device corresponding to @kobj.
+ */
+static ssize_t
+pci_read_rom(struct kobject *kobj, char *buf, loff_t off, size_t count)
+{
+	struct pci_dev *pdev = to_pci_dev(container_of(kobj, struct device, kobj));
+	unsigned char *rom;
+	size_t size;
+	
+	rom = pci_map_rom(pdev, &size);	/* size starts out as PCI window size */
+	if (!rom)
+		return 0;
+		
+	if (off >= size)
+		count = 0;
+	else {
+		if (off + count > size)
+			count = size - off;
+		
+		memcpy_fromio(buf, rom + off, count);
+	}
+	pci_unmap_rom(pdev, rom);
+		
+	return count;
+}
+
 static struct bin_attribute pci_config_attr = {
 	.attr =	{
 		.name = "config",
@@ -186,13 +224,67 @@
 	.write = pci_write_config,
 };
 
-void pci_create_sysfs_dev_files (struct pci_dev *pdev)
+int pci_create_sysfs_dev_files (struct pci_dev *pdev)
 {
+	if (!sysfs_initialized)
+		return -EACCES;
+
 	if (pdev->cfg_size < 4096)
 		sysfs_create_bin_file(&pdev->dev.kobj, &pci_config_attr);
 	else
 		sysfs_create_bin_file(&pdev->dev.kobj, &pcie_config_attr);
 
+	/* If the device has a ROM, try to expose it in sysfs. */
+	if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) {
+		struct bin_attribute *rom_attr;
+		
+		rom_attr = kmalloc(sizeof(*rom_attr), GFP_ATOMIC);
+		if (rom_attr) {
+			pdev->rom_attr = rom_attr;
+			rom_attr->size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
+			rom_attr->attr.name = "rom";
+			rom_attr->attr.mode = S_IRUSR;
+			rom_attr->attr.owner = THIS_MODULE;
+			rom_attr->read = pci_read_rom;
+			sysfs_create_bin_file(&pdev->dev.kobj, rom_attr);
+		}
+	}
 	/* add platform-specific attributes */
 	pcibios_add_platform_entries(pdev);
+	
+	return 0;
+}
+
+/**
+ * pci_remove_sysfs_dev_files - cleanup PCI specific sysfs files
+ * @pdev: device whose entries we should free
+ *
+ * Cleanup when @pdev is removed from sysfs.
+ */
+void pci_remove_sysfs_dev_files(struct pci_dev *pdev)
+{
+	if (pdev->cfg_size < 4096)
+		sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr);
+	else
+		sysfs_remove_bin_file(&pdev->dev.kobj, &pcie_config_attr);
+
+	if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) {
+		if (pdev->rom_attr) {
+			sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr);
+			kfree(pdev->rom_attr);
+		}
+	}
+}
+
+static int __init pci_sysfs_init(void)
+{
+	struct pci_dev *pdev = NULL;
+	
+	sysfs_initialized = 1;
+	while ((pdev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pdev)) != NULL)
+		pci_create_sysfs_dev_files(pdev);
+
+	return 0;
 }
+
+__initcall(pci_sysfs_init);
diff -Nru a/drivers/pci/pci.h b/drivers/pci/pci.h
--- a/drivers/pci/pci.h	Sun Aug 29 00:22:21 2004
+++ b/drivers/pci/pci.h	Sun Aug 29 00:22:21 2004
@@ -2,7 +2,9 @@
 
 extern int pci_hotplug (struct device *dev, char **envp, int num_envp,
 			 char *buffer, int buffer_size);
-extern void pci_create_sysfs_dev_files(struct pci_dev *pdev);
+extern int pci_create_sysfs_dev_files(struct pci_dev *pdev);
+extern void pci_remove_sysfs_dev_files(struct pci_dev *pdev);
+extern void pci_cleanup_rom(struct pci_dev *dev);
 extern int pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
 				  unsigned long size, unsigned long align,
 				  unsigned long min, unsigned int type_mask,
diff -Nru a/drivers/pci/probe.c b/drivers/pci/probe.c
--- a/drivers/pci/probe.c	Sun Aug 29 00:22:21 2004
+++ b/drivers/pci/probe.c	Sun Aug 29 00:22:21 2004
@@ -170,7 +170,7 @@
 		if (sz && sz != 0xffffffff) {
 			sz = pci_size(l, sz, PCI_ROM_ADDRESS_MASK);
 			if (sz) {
-				res->flags = (l & PCI_ROM_ADDRESS_ENABLE) |
+				res->flags = (l & IORESOURCE_ROM_ENABLE) |
 				  IORESOURCE_MEM | IORESOURCE_PREFETCH |
 				  IORESOURCE_READONLY | IORESOURCE_CACHEABLE;
 				res->start = l & PCI_ROM_ADDRESS_MASK;
diff -Nru a/drivers/pci/remove.c b/drivers/pci/remove.c
--- a/drivers/pci/remove.c	Sun Aug 29 00:22:21 2004
+++ b/drivers/pci/remove.c	Sun Aug 29 00:22:21 2004
@@ -16,6 +16,7 @@
 
  	msi_remove_pci_irq_vectors(dev);
 
+	pci_cleanup_rom(dev);
 	for (i = 0; i < PCI_NUM_RESOURCES; i++) {
 		struct resource *res = dev->resource + i;
 		if (res->parent)
@@ -26,6 +27,7 @@
 static void pci_destroy_dev(struct pci_dev *dev)
 {
 	pci_proc_detach_device(dev);
+	pci_remove_sysfs_dev_files(dev);
 	device_unregister(&dev->dev);
 
 	/* Remove the device from the device lists, and prevent any further
diff -Nru a/drivers/pci/rom.c b/drivers/pci/rom.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/pci/rom.c	Sun Aug 29 00:22:21 2004
@@ -0,0 +1,226 @@
+/*
+ * drivers/pci/rom.c
+ *
+ * (C) Copyright 2004 Jon Smirl <jonsmirl@yahoo.com>
+ * (C) Copyright 2004 Silicon Graphics, Inc. Jesse Barnes <jbarnes@sgi.com>
+ *
+ * PCI ROM access routines
+ *
+ */
+
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+
+#include "pci.h"
+
+/**
+ * pci_enable_rom - enable ROM decoding for a PCI device
+ * @dev: PCI device to enable
+ *
+ * Enable ROM decoding on @dev.  This involves simply turning on the last
+ * bit of the PCI ROM BAR.  Note that some cards may share address decoders
+ * between the ROM and other resources, so enabling it may disable access
+ * to MMIO registers or other card memory.
+ */
+static void
+pci_enable_rom(struct pci_dev *pdev)
+{
+	u32 rom_addr;
+	
+	pci_read_config_dword(pdev, pdev->rom_base_reg, &rom_addr);
+	rom_addr |= PCI_ROM_ADDRESS_ENABLE;
+	pci_write_config_dword(pdev, pdev->rom_base_reg, rom_addr);
+}
+
+/**
+ * pci_disable_rom - disable ROM decoding for a PCI device
+ * @dev: PCI device to disable
+ *
+ * Disable ROM decoding on a PCI device by turning off the last bit in the
+ * ROM BAR.
+ */
+static void
+pci_disable_rom(struct pci_dev *pdev)
+{
+	u32 rom_addr;
+	pci_read_config_dword(pdev, pdev->rom_base_reg, &rom_addr);
+	rom_addr &= ~PCI_ROM_ADDRESS_ENABLE;
+	pci_write_config_dword(pdev, pdev->rom_base_reg, rom_addr);
+}
+
+/**
+ * pci_map_rom - map a PCI ROM to kernel space
+ * @dev: pointer to pci device struct
+ * @size: pointer to receive size of pci window over ROM
+ * @return: kernel virtual pointer to image of ROM
+ *
+ * Map a PCI ROM into kernel space. If ROM is boot video ROM,
+ * the shadow BIOS copy will be returned instead of the 
+ * actual ROM.
+ */
+unsigned char *
+pci_map_rom(struct pci_dev *pdev, size_t *size)
+{
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+	loff_t start;
+	unsigned char *rom, *image;
+	int last_image;
+	
+	if (res->flags & IORESOURCE_ROM_SHADOW) {	/* IORESOURCE_ROM_SHADOW only set on x86 */
+		start = (loff_t)0xC0000; 	/* primary video rom always starts here */
+		*size = 0x20000;		/* cover C000:0 through E000:0 */
+	} else {
+		if (res->flags & IORESOURCE_ROM_COPY) {
+			*size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
+			return (unsigned char *)pci_resource_start(pdev, PCI_ROM_RESOURCE);
+		} else {
+			/* assign the ROM an address if it doesn't have one */
+			if (res->parent == NULL)
+				pci_assign_resource(pdev, PCI_ROM_RESOURCE);
+	
+			start = pci_resource_start(pdev, PCI_ROM_RESOURCE);
+			*size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
+			if (*size == 0)
+				return NULL;
+			
+			/* Enable ROM space decodes */
+			pci_enable_rom(pdev);
+		}
+	}
+	
+	rom = ioremap(start, *size);
+	if (!rom) {
+		/* restore enable if ioremap fails */
+		if (!(res->flags & (IORESOURCE_ROM_ENABLE | IORESOURCE_ROM_SHADOW | IORESOURCE_ROM_COPY)))
+			pci_disable_rom(pdev);
+		return NULL;
+	}		
+
+	/* Try to find the true size of the ROM since sometimes the PCI window */
+	/* size is much larger than the actual size of the ROM. */
+	/* True size is important if the ROM is going to be copied. */
+	image = rom;
+	do {
+		char *pds;
+		/* Standard PCI ROMs start out with these bytes 55 AA */
+		if (readb(image) != 0x55)
+			break;
+		if (readb(image + 1) != 0xAA)
+			break;
+		/* get the PCI data structure and check its signature */
+		pds = image + readw(image + 24);
+		if (readb(pds) != 'P')
+			break;
+		if (readb(pds + 1) != 'C')
+			break;
+		if (readb(pds + 2) != 'I')
+			break;
+		if (readb(pds + 3) != 'R')
+			break;
+		last_image = readb(pds + 21) & 0x80;
+		/* this length is reliable */
+		image += readw(pds + 16) * 512;
+	} while (!last_image);
+
+	*size = image - rom;
+
+	return rom;
+}
+
+/**
+ * pci_map_rom_copy - map a PCI ROM to kernel space, create a copy
+ * @dev: pointer to pci device struct
+ * @size: pointer to receive size of pci window over ROM
+ * @return: kernel virtual pointer to image of ROM
+ *
+ * Map a PCI ROM into kernel space. If ROM is boot video ROM,
+ * the shadow BIOS copy will be returned instead of the 
+ * actual ROM.
+ */
+unsigned char *
+pci_map_rom_copy(struct pci_dev *pdev, size_t *size)
+{
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+	unsigned char *rom;
+	
+	rom = pci_map_rom(pdev, size);
+	if (!rom)
+		return NULL;
+		
+	if (res->flags & (IORESOURCE_ROM_COPY | IORESOURCE_ROM_SHADOW))
+		return rom;
+		
+	res->start = (unsigned long)kmalloc(*size, GFP_KERNEL);
+	if (!res->start) 
+		return rom;
+
+	res->end = res->start + *size; 
+	memcpy((void*)res->start, rom, *size);
+	pci_unmap_rom(pdev, rom);
+	res->flags |= IORESOURCE_ROM_COPY;
+	
+	return (unsigned char *)res->start;
+}
+
+/**
+ * pci_unmap_rom - unmap the ROM from kernel space
+ * @dev: pointer to pci device struct
+ * @rom: virtual address of the previous mapping
+ *
+ * Remove a mapping of a previously mapped ROM
+ */
+void 
+pci_unmap_rom(struct pci_dev *pdev, unsigned char *rom)
+{
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+
+	if (res->flags & IORESOURCE_ROM_COPY)
+		return;
+		
+	iounmap(rom);
+		
+	/* Disable again before continuing, leave enabled if pci=rom */
+	if (!(res->flags & (IORESOURCE_ROM_ENABLE | IORESOURCE_ROM_SHADOW)))
+		pci_disable_rom(pdev);
+}
+
+/**
+ * pci_remove_rom - disable the ROM and remove its sysfs attribute
+ * @dev: pointer to pci device struct
+ *
+ */
+void 
+pci_remove_rom(struct pci_dev *pdev) 
+{
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+	
+	if (pci_resource_len(pdev, PCI_ROM_RESOURCE))
+		sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr);
+	if (!(res->flags & (IORESOURCE_ROM_ENABLE | IORESOURCE_ROM_SHADOW | IORESOURCE_ROM_COPY)))
+		pci_disable_rom(pdev);
+}
+
+/**
+ * pci_cleanup_rom - internal routine for freeing the ROM copy created 
+ * by pci_map_rom_copy called from remove.c
+ * @dev: pointer to pci device struct
+ *
+ */
+void 
+pci_cleanup_rom(struct pci_dev *pdev) 
+{
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+	if (res->flags & IORESOURCE_ROM_COPY) {
+		kfree((void*)res->start);
+		res->flags &= ~IORESOURCE_ROM_COPY;
+		res->start = 0;
+		res->end = 0;
+	}
+}
+
+EXPORT_SYMBOL(pci_map_rom);
+EXPORT_SYMBOL(pci_map_rom_copy);
+EXPORT_SYMBOL(pci_unmap_rom);
+EXPORT_SYMBOL(pci_remove_rom);
diff -Nru a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c
--- a/drivers/pci/setup-res.c	Sun Aug 29 00:22:21 2004
+++ b/drivers/pci/setup-res.c	Sun Aug 29 00:22:21 2004
@@ -56,7 +56,7 @@
 	if (resno < 6) {
 		reg = PCI_BASE_ADDRESS_0 + 4 * resno;
 	} else if (resno == PCI_ROM_RESOURCE) {
-		new |= res->flags & PCI_ROM_ADDRESS_ENABLE;
+		new |= res->flags & IORESOURCE_ROM_ENABLE;
 		reg = dev->rom_base_reg;
 	} else {
 		/* Hmm, non-standard resource. */
diff -Nru a/include/linux/ioport.h b/include/linux/ioport.h
--- a/include/linux/ioport.h	Sun Aug 29 00:22:21 2004
+++ b/include/linux/ioport.h	Sun Aug 29 00:22:21 2004
@@ -82,6 +82,11 @@
 #define IORESOURCE_MEM_SHADOWABLE	(1<<5)	/* dup: IORESOURCE_SHADOWABLE */
 #define IORESOURCE_MEM_EXPANSIONROM	(1<<6)
 
+/* PCI ROM control bits (IORESOURCE_BITS) */
+#define IORESOURCE_ROM_ENABLE		(1<<0)	/* ROM is enabled, same as PCI_ROM_ADDRESS_ENABLE */
+#define IORESOURCE_ROM_SHADOW		(1<<1)	/* ROM is copy at C000:0 */
+#define IORESOURCE_ROM_COPY		(1<<2)	/* ROM is alloc'd copy, resource field overlaid */
+
 /* PC/ISA/whatever - the normal PC address spaces: IO and memory */
 extern struct resource ioport_resource;
 extern struct resource iomem_resource;
diff -Nru a/include/linux/pci.h b/include/linux/pci.h
--- a/include/linux/pci.h	Sun Aug 29 00:22:21 2004
+++ b/include/linux/pci.h	Sun Aug 29 00:22:21 2004
@@ -537,6 +537,7 @@
 	unsigned int	is_busmaster:1; /* device is busmaster */
 	
 	u32		saved_config_space[16]; /* config space saved at suspend time */
+	struct bin_attribute *rom_attr; /* attribute descriptor for sysfs ROM entry */
 #ifdef CONFIG_PCI_NAMES
 #define PCI_NAME_SIZE	96
 #define PCI_NAME_HALF	__stringify(43)	/* less than half to handle slop */
@@ -777,6 +778,12 @@
 int pci_dac_set_dma_mask(struct pci_dev *dev, u64 mask);
 int pci_set_consistent_dma_mask(struct pci_dev *dev, u64 mask);
 int pci_assign_resource(struct pci_dev *dev, int i);
+
+/* ROM control related routines */
+unsigned char *pci_map_rom(struct pci_dev *pdev, size_t *size);
+unsigned char *pci_map_rom_copy(struct pci_dev *pdev, size_t *size);
+void pci_unmap_rom(struct pci_dev *pdev, unsigned char *rom);
+void pci_remove_rom(struct pci_dev *pdev);
 
 /* Power management related routines */
 int pci_save_state(struct pci_dev *dev, u32 *buffer);

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-28 16:35                                           ` Matthew Wilcox
@ 2004-08-28 21:53                                             ` Grant Grundler
  0 siblings, 0 replies; 110+ messages in thread
From: Grant Grundler @ 2004-08-28 21:53 UTC (permalink / raw)
  To: Matthew Wilcox
  Cc: Jon Smirl, Greg KH, Jesse Barnes, Martin Mares, Pallipadi,
	Venkatesh, linux-pci, Alan Cox, Linux Kernel Mailing List,
	Petr Vandrovec, Benjamin Herrenschmidt

On Sat, Aug 28, 2004 at 05:35:21PM +0100, Matthew Wilcox wrote:
> > If reading past 1MB for those ROMs causes a reboots, could something be
> > wrong in the IA64 fault handing code?
> 
> No, that's normal behaviour on ia64 -- unacknowledged PCI reads cause a
> machine check rather than reading ffffffff like x86 does.

Only on HP IA64 machines (just like on HP parisc machines).
Intel IA64 machines return -1 or garbage on Master Abort
(due to timeout) just like the IA32 platforms.

grant

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-28 16:15                                         ` Matthew Wilcox
  2004-08-28 17:33                                           ` Jon Smirl
@ 2004-08-28 17:38                                           ` Jon Smirl
  1 sibling, 0 replies; 110+ messages in thread
From: Jon Smirl @ 2004-08-28 17:38 UTC (permalink / raw)
  To: Matthew Wilcox
  Cc: Greg KH, Jesse Barnes, Martin Mares, Pallipadi, Venkatesh,
	linux-pci, Alan Cox, Linux Kernel Mailing List, Petr Vandrovec,
	Benjamin Herrenschmidt

[-- Attachment #1: Type: text/plain, Size: 282 bytes --]

One more time, last release was missing patch description and
signed-off-by. No changes to code.

=====
Jon Smirl
jonsmirl@yahoo.com


		
__________________________________
Do you Yahoo!?
Yahoo! Mail Address AutoComplete - You start. We finish.
http://promotions.yahoo.com/new_mail 

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: pci-sysfs-rom-21-mm.patch --]
[-- Type: text/x-patch; name="pci-sysfs-rom-21-mm.patch", Size: 15331 bytes --]

Exposes PCI ROMs via sysfs. Four new routines for drivers to use when 
accessing ROMs: pci_map_rom, pci_map_rom_copy, pci_unmap_rom, pci_remove_rom. 
Handles shadow ROMs for laptops that compress actual ROMs.

Signed-off-by: "Jon Smirl" <jonsmirl@yahoo.com>
===== arch/i386/pci/fixup.c 1.20 vs edited =====
--- 1.20/arch/i386/pci/fixup.c	Wed Aug  4 07:57:17 2004
+++ edited/arch/i386/pci/fixup.c	Sat Aug 28 13:15:51 2004
@@ -255,3 +255,41 @@
 }
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2, pci_fixup_nforce2);
 
+/*
+ * Fixup to mark boot BIOS video selected by BIOS before it changes
+ *
+ * From information provided by "Jon Smirl" <jonsmirl@yahoo.com>
+ *
+ * The standard boot ROM sequence for an x86 machine uses the BIOS
+ * to select an initial video card for boot display. This boot video 
+ * card will have it's BIOS copied to C0000 in system RAM. 
+ * IORESOURCE_ROM_SHADOW is used to associate the boot video
+ * card with this copy. On laptops this copy has to be used since
+ * the main ROM may be compressed or combined with another image.
+ * See pci_map_rom() for use of this flag. IORESOURCE_ROM_SHADOW
+ * is marked here since the boot video device will be the only enabled
+ * video device at this point.
+ *
+ */static void __devinit pci_fixup_video(struct pci_dev *pdev)
+{
+	struct pci_dev *bridge;
+	struct pci_bus *bus;
+	u16 l;
+
+	if ((pdev->class >> 8) != PCI_CLASS_DISPLAY_VGA)
+		return;
+
+	/* Is VGA routed to us? */
+	bus = pdev->bus;
+	while (bus) {
+		bridge = bus->self;
+		if (bridge) {
+			pci_read_config_word(bridge, PCI_BRIDGE_CONTROL, &l);
+			if (!(l & PCI_BRIDGE_CTL_VGA))
+				return;
+		}
+		bus = bus->parent;
+	}
+	pdev->resource[PCI_ROM_RESOURCE].flags |= IORESOURCE_ROM_SHADOW;
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, pci_fixup_video);
===== drivers/pci/pci-sysfs.c 1.10 vs edited =====
--- 1.10/drivers/pci/pci-sysfs.c	Fri Jun  4 09:23:04 2004
+++ edited/drivers/pci/pci-sysfs.c	Sat Aug 28 13:16:03 2004
@@ -5,6 +5,8 @@
  * (C) Copyright 2002-2004 IBM Corp.
  * (C) Copyright 2003 Matthew Wilcox
  * (C) Copyright 2003 Hewlett-Packard
+ * (C) Copyright 2004 Jon Smirl <jonsmirl@yahoo.com>
+ * (C) Copyright 2004 Silicon Graphics, Inc. Jesse Barnes <jbarnes@sgi.com>
  *
  * File attributes for PCI devices
  *
@@ -20,6 +22,8 @@
 
 #include "pci.h"
 
+static int sysfs_initialized;	/* = 0 */
+
 /* show configuration fields */
 #define pci_config_attr(field, format_string)				\
 static ssize_t								\
@@ -164,6 +168,245 @@
 	return count;
 }
 
+/**
+ * pci_enable_rom - enable ROM decoding for a PCI device
+ * @dev: PCI device to enable
+ *
+ * Enable ROM decoding on @dev.  This involves simply turning on the last
+ * bit of the PCI ROM BAR.  Note that some cards may share address decoders
+ * between the ROM and other resources, so enabling it may disable access
+ * to MMIO registers or other card memory.
+ */
+static void
+pci_enable_rom(struct pci_dev *pdev)
+{
+	u32 rom_addr;
+	
+	pci_read_config_dword(pdev, pdev->rom_base_reg, &rom_addr);
+	rom_addr |= PCI_ROM_ADDRESS_ENABLE;
+	pci_write_config_dword(pdev, pdev->rom_base_reg, rom_addr);
+}
+
+/**
+ * pci_disable_rom - disable ROM decoding for a PCI device
+ * @dev: PCI device to disable
+ *
+ * Disable ROM decoding on a PCI device by turning off the last bit in the
+ * ROM BAR.
+ */
+static void
+pci_disable_rom(struct pci_dev *pdev)
+{
+	u32 rom_addr;
+	pci_read_config_dword(pdev, pdev->rom_base_reg, &rom_addr);
+	rom_addr &= ~PCI_ROM_ADDRESS_ENABLE;
+	pci_write_config_dword(pdev, pdev->rom_base_reg, rom_addr);
+}
+
+/**
+ * pci_map_rom - map a PCI ROM to kernel space
+ * @dev: pointer to pci device struct
+ * @size: pointer to receive size of pci window over ROM
+ * @return: kernel virtual pointer to image of ROM
+ *
+ * Map a PCI ROM into kernel space. If ROM is boot video ROM,
+ * the shadow BIOS copy will be returned instead of the 
+ * actual ROM.
+ */
+unsigned char *
+pci_map_rom(struct pci_dev *pdev, size_t *size)
+{
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+	loff_t start;
+	unsigned char *rom, *image;
+	int last_image;
+	
+	if (res->flags & IORESOURCE_ROM_SHADOW) {	/* IORESOURCE_ROM_SHADOW only set on x86 */
+		start = (loff_t)0xC0000; 	/* primary video rom always starts here */
+		*size = 0x20000;		/* cover C000:0 through E000:0 */
+	} else {
+		if (res->flags & IORESOURCE_ROM_COPY) {
+			*size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
+			return (unsigned char *)pci_resource_start(pdev, PCI_ROM_RESOURCE);
+		} else {
+			/* assign the ROM an address if it doesn't have one */
+			if (res->parent == NULL)
+				pci_assign_resource(pdev, PCI_ROM_RESOURCE);
+	
+			start = pci_resource_start(pdev, PCI_ROM_RESOURCE);
+			*size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
+			if (*size == 0)
+				return NULL;
+			
+			/* Enable ROM space decodes */
+			pci_enable_rom(pdev);
+		}
+	}
+	
+	rom = ioremap(start, *size);
+	if (!rom) {
+		/* restore enable if ioremap fails */
+		if (!(res->flags & (IORESOURCE_ROM_ENABLE | IORESOURCE_ROM_SHADOW | IORESOURCE_ROM_COPY)))
+			pci_disable_rom(pdev);
+		return NULL;
+	}		
+
+	/* Try to find the true size of the ROM since sometimes the PCI window */
+	/* size is much larger than the actual size of the ROM. */
+	/* True size is important if the ROM is going to be copied. */
+	image = rom;
+	do {
+		char *pds;
+		/* Standard PCI ROMs start out with these bytes 55 AA */
+		if (readb(image) != 0x55)
+			break;
+		if (readb(image + 1) != 0xAA)
+			break;
+		/* get the PCI data structure and check its signature */
+		pds = image + readw(image + 24);
+		if (readb(pds) != 'P')
+			break;
+		if (readb(pds + 1) != 'C')
+			break;
+		if (readb(pds + 2) != 'I')
+			break;
+		if (readb(pds + 3) != 'R')
+			break;
+		last_image = readb(pds + 21) & 0x80;
+		/* this length is reliable */
+		image += readw(pds + 16) * 512;
+	} while (!last_image);
+
+	*size = image - rom;
+
+	return rom;
+}
+
+/**
+ * pci_map_rom_copy - map a PCI ROM to kernel space, create a copy
+ * @dev: pointer to pci device struct
+ * @size: pointer to receive size of pci window over ROM
+ * @return: kernel virtual pointer to image of ROM
+ *
+ * Map a PCI ROM into kernel space. If ROM is boot video ROM,
+ * the shadow BIOS copy will be returned instead of the 
+ * actual ROM.
+ */
+unsigned char *
+pci_map_rom_copy(struct pci_dev *pdev, size_t *size)
+{
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+	unsigned char *rom;
+	
+	rom = pci_map_rom(pdev, size);
+	if (!rom)
+		return NULL;
+		
+	if (res->flags & (IORESOURCE_ROM_COPY | IORESOURCE_ROM_SHADOW))
+		return rom;
+		
+	res->start = (unsigned long)kmalloc(*size, GFP_KERNEL);
+	if (!res->start) 
+		return rom;
+
+	res->end = res->start + *size; 
+	memcpy((void*)res->start, rom, *size);
+	pci_unmap_rom(pdev, rom);
+	res->flags |= IORESOURCE_ROM_COPY;
+	
+	return (unsigned char *)res->start;
+}
+
+/**
+ * pci_unmap_rom - unmap the ROM from kernel space
+ * @dev: pointer to pci device struct
+ * @rom: virtual address of the previous mapping
+ *
+ * Remove a mapping of a previously mapped ROM
+ */
+void 
+pci_unmap_rom(struct pci_dev *pdev, unsigned char *rom)
+{
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+
+	if (res->flags & IORESOURCE_ROM_COPY)
+		return;
+		
+	iounmap(rom);
+		
+	/* Disable again before continuing, leave enabled if pci=rom */
+	if (!(res->flags & (IORESOURCE_ROM_ENABLE | IORESOURCE_ROM_SHADOW)))
+		pci_disable_rom(pdev);
+}
+
+/**
+ * pci_remove_rom - disable the ROM and remove its sysfs attribute
+ * @dev: pointer to pci device struct
+ *
+ */
+void 
+pci_remove_rom(struct pci_dev *pdev) 
+{
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+	
+	if (pci_resource_len(pdev, PCI_ROM_RESOURCE))
+		sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr);
+	if (!(res->flags & (IORESOURCE_ROM_ENABLE | IORESOURCE_ROM_SHADOW | IORESOURCE_ROM_COPY)))
+		pci_disable_rom(pdev);
+}
+
+/**
+ * pci_cleanup_rom - internal routine for freeing the ROM copy created 
+ * by pci_map_rom_copy called from remove.c
+ * @dev: pointer to pci device struct
+ *
+ */
+void 
+pci_cleanup_rom(struct pci_dev *pdev) 
+{
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+	if (res->flags & IORESOURCE_ROM_COPY) {
+		kfree((void*)res->start);
+		res->flags &= ~IORESOURCE_ROM_COPY;
+		res->start = 0;
+		res->end = 0;
+	}
+}
+
+/**
+ * pci_read_rom - read a PCI ROM
+ * @kobj: kernel object handle
+ * @buf: where to put the data we read from the ROM
+ * @off: file offset
+ * @count: number of bytes to read
+ *
+ * Put @count bytes starting at @off into @buf from the ROM in the PCI
+ * device corresponding to @kobj.
+ */
+static ssize_t
+pci_read_rom(struct kobject *kobj, char *buf, loff_t off, size_t count)
+{
+	struct pci_dev *pdev = to_pci_dev(container_of(kobj, struct device, kobj));
+	unsigned char *rom;
+	size_t size;
+	
+	rom = pci_map_rom(pdev, &size);	/* size starts out as PCI window size */
+	if (!rom)
+		return 0;
+		
+	if (off >= size)
+		count = 0;
+	else {
+		if (off + count > size)
+			count = size - off;
+		
+		memcpy_fromio(buf, rom + off, count);
+	}
+	pci_unmap_rom(pdev, rom);
+		
+	return count;
+}
+
 static struct bin_attribute pci_config_attr = {
 	.attr =	{
 		.name = "config",
@@ -186,13 +429,72 @@
 	.write = pci_write_config,
 };
 
-void pci_create_sysfs_dev_files (struct pci_dev *pdev)
+int pci_create_sysfs_dev_files (struct pci_dev *pdev)
 {
+	if (!sysfs_initialized)
+		return -EACCES;
+
 	if (pdev->cfg_size < 4096)
 		sysfs_create_bin_file(&pdev->dev.kobj, &pci_config_attr);
 	else
 		sysfs_create_bin_file(&pdev->dev.kobj, &pcie_config_attr);
 
+	/* If the device has a ROM, try to expose it in sysfs. */
+	if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) {
+		struct bin_attribute *rom_attr;
+		
+		rom_attr = kmalloc(sizeof(*rom_attr), GFP_ATOMIC);
+		if (rom_attr) {
+			pdev->rom_attr = rom_attr;
+			rom_attr->size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
+			rom_attr->attr.name = "rom";
+			rom_attr->attr.mode = S_IRUSR;
+			rom_attr->attr.owner = THIS_MODULE;
+			rom_attr->read = pci_read_rom;
+			sysfs_create_bin_file(&pdev->dev.kobj, rom_attr);
+		}
+	}
 	/* add platform-specific attributes */
 	pcibios_add_platform_entries(pdev);
+	
+	return 0;
+}
+
+/**
+ * pci_remove_sysfs_dev_files - cleanup PCI specific sysfs files
+ * @pdev: device whose entries we should free
+ *
+ * Cleanup when @pdev is removed from sysfs.
+ */
+void pci_remove_sysfs_dev_files(struct pci_dev *pdev)
+{
+	if (pdev->cfg_size < 4096)
+		sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr);
+	else
+		sysfs_remove_bin_file(&pdev->dev.kobj, &pcie_config_attr);
+
+	if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) {
+		if (pdev->rom_attr) {
+			sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr);
+			kfree(pdev->rom_attr);
+		}
+	}
 }
+
+static int __init pci_sysfs_init(void)
+{
+	struct pci_dev *pdev = NULL;
+	
+	sysfs_initialized = 1;
+	while ((pdev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pdev)) != NULL)
+		pci_create_sysfs_dev_files(pdev);
+
+	return 0;
+}
+
+__initcall(pci_sysfs_init);
+
+EXPORT_SYMBOL(pci_map_rom);
+EXPORT_SYMBOL(pci_map_rom_copy);
+EXPORT_SYMBOL(pci_unmap_rom);
+EXPORT_SYMBOL(pci_remove_rom);
===== drivers/pci/pci.h 1.12 vs edited =====
--- 1.12/drivers/pci/pci.h	Fri Jun  4 09:23:04 2004
+++ edited/drivers/pci/pci.h	Sat Aug 28 13:15:52 2004
@@ -2,7 +2,9 @@
 
 extern int pci_hotplug (struct device *dev, char **envp, int num_envp,
 			 char *buffer, int buffer_size);
-extern void pci_create_sysfs_dev_files(struct pci_dev *pdev);
+extern int pci_create_sysfs_dev_files(struct pci_dev *pdev);
+extern void pci_remove_sysfs_dev_files(struct pci_dev *pdev);
+extern void pci_cleanup_rom(struct pci_dev *dev);
 extern int pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
 				  unsigned long size, unsigned long align,
 				  unsigned long min, unsigned int type_mask,
===== drivers/pci/probe.c 1.67 vs edited =====
--- 1.67/drivers/pci/probe.c	Thu Aug 26 13:10:09 2004
+++ edited/drivers/pci/probe.c	Sat Aug 28 13:15:52 2004
@@ -170,7 +170,7 @@
 		if (sz && sz != 0xffffffff) {
 			sz = pci_size(l, sz, PCI_ROM_ADDRESS_MASK);
 			if (sz) {
-				res->flags = (l & PCI_ROM_ADDRESS_ENABLE) |
+				res->flags = (l & IORESOURCE_ROM_ENABLE) |
 				  IORESOURCE_MEM | IORESOURCE_PREFETCH |
 				  IORESOURCE_READONLY | IORESOURCE_CACHEABLE;
 				res->start = l & PCI_ROM_ADDRESS_MASK;
===== drivers/pci/remove.c 1.4 vs edited =====
--- 1.4/drivers/pci/remove.c	Thu Aug 26 13:10:09 2004
+++ edited/drivers/pci/remove.c	Sat Aug 28 13:15:52 2004
@@ -16,6 +16,7 @@
 
  	msi_remove_pci_irq_vectors(dev);
 
+	pci_cleanup_rom(dev);
 	for (i = 0; i < PCI_NUM_RESOURCES; i++) {
 		struct resource *res = dev->resource + i;
 		if (res->parent)
@@ -26,6 +27,7 @@
 static void pci_destroy_dev(struct pci_dev *dev)
 {
 	pci_proc_detach_device(dev);
+	pci_remove_sysfs_dev_files(dev);
 	device_unregister(&dev->dev);
 
 	/* Remove the device from the device lists, and prevent any further
===== drivers/pci/setup-res.c 1.25 vs edited =====
--- 1.25/drivers/pci/setup-res.c	Thu Mar 18 18:40:56 2004
+++ edited/drivers/pci/setup-res.c	Sat Aug 28 13:15:53 2004
@@ -56,7 +56,7 @@
 	if (resno < 6) {
 		reg = PCI_BASE_ADDRESS_0 + 4 * resno;
 	} else if (resno == PCI_ROM_RESOURCE) {
-		new |= res->flags & PCI_ROM_ADDRESS_ENABLE;
+		new |= res->flags & IORESOURCE_ROM_ENABLE;
 		reg = dev->rom_base_reg;
 	} else {
 		/* Hmm, non-standard resource. */
===== include/linux/ioport.h 1.14 vs edited =====
--- 1.14/include/linux/ioport.h	Thu Dec 18 00:48:57 2003
+++ edited/include/linux/ioport.h	Sat Aug 28 13:15:53 2004
@@ -82,6 +82,11 @@
 #define IORESOURCE_MEM_SHADOWABLE	(1<<5)	/* dup: IORESOURCE_SHADOWABLE */
 #define IORESOURCE_MEM_EXPANSIONROM	(1<<6)
 
+/* PCI ROM control bits (IORESOURCE_BITS) */
+#define IORESOURCE_ROM_ENABLE		(1<<0)	/* ROM is enabled, same as PCI_ROM_ADDRESS_ENABLE */
+#define IORESOURCE_ROM_SHADOW		(1<<1)	/* ROM is copy at C000:0 */
+#define IORESOURCE_ROM_COPY		(1<<2)	/* ROM is alloc'd copy, resource field overlaid */
+
 /* PC/ISA/whatever - the normal PC address spaces: IO and memory */
 extern struct resource ioport_resource;
 extern struct resource iomem_resource;
===== include/linux/pci.h 1.136 vs edited =====
--- 1.136/include/linux/pci.h	Thu Aug 26 13:10:27 2004
+++ edited/include/linux/pci.h	Sat Aug 28 13:15:54 2004
@@ -537,6 +537,7 @@
 	unsigned int	is_busmaster:1; /* device is busmaster */
 	
 	u32		saved_config_space[16]; /* config space saved at suspend time */
+	struct bin_attribute *rom_attr; /* attribute descriptor for sysfs ROM entry */
 #ifdef CONFIG_PCI_NAMES
 #define PCI_NAME_SIZE	96
 #define PCI_NAME_HALF	__stringify(43)	/* less than half to handle slop */
@@ -777,6 +778,12 @@
 int pci_dac_set_dma_mask(struct pci_dev *dev, u64 mask);
 int pci_set_consistent_dma_mask(struct pci_dev *dev, u64 mask);
 int pci_assign_resource(struct pci_dev *dev, int i);
+
+/* ROM control related routines */
+unsigned char *pci_map_rom(struct pci_dev *pdev, size_t *size);
+unsigned char *pci_map_rom_copy(struct pci_dev *pdev, size_t *size);
+void pci_unmap_rom(struct pci_dev *pdev, unsigned char *rom);
+void pci_remove_rom(struct pci_dev *pdev);
 
 /* Power management related routines */
 int pci_save_state(struct pci_dev *dev, u32 *buffer);

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-28 16:15                                         ` Matthew Wilcox
@ 2004-08-28 17:33                                           ` Jon Smirl
  2004-08-28 17:38                                           ` Jon Smirl
  1 sibling, 0 replies; 110+ messages in thread
From: Jon Smirl @ 2004-08-28 17:33 UTC (permalink / raw)
  To: Matthew Wilcox
  Cc: Greg KH, Jesse Barnes, Martin Mares, Pallipadi, Venkatesh,
	linux-pci, Alan Cox, Linux Kernel Mailing List, Petr Vandrovec,
	Benjamin Herrenschmidt

[-- Attachment #1: Type: text/plain, Size: 1347 bytes --]

--- Matthew Wilcox <willy@debian.org> wrote:
> A few more bugfixes:
> 
>  - I was confused, it was hex 18, not decimal 16
>  - Its/it's again ;-)
>  - Only return the bits of the ROM that are in PCI format
>  - Remove the actual resource, not some strange static one
>  - Delete the static one that wasn't really used.

These are all good. The static resource was from an old version that I
missed deleting.

I'd still like for someone to check the code on ppc and sun.

> BTW, what do you think to moving all the ROM code from pci-sysfs.c to
> a new file rom.c?  There's not much sense to the ROM accessing code
> being  mixed in with the sysfs code like this.  After all, we might
> choose to expose the ROM in some other way in the future.

I'm ok with it either way, it only about 200 lines of code.

We could save that for the next round. Next round every usage of
PCI_ROM_ADDRESS_ENABLE in the kernel probably needs to be adjusted to
use the new API. I've looked at a few of these and they are almost
always broken in some respect. About thirty places need to be looked
at. Twenty versions of this patch implies that ROM reading wasn't a
trivial problem. 

=====
Jon Smirl
jonsmirl@yahoo.com


		
__________________________________
Do you Yahoo!?
Yahoo! Mail Address AutoComplete - You start. We finish.
http://promotions.yahoo.com/new_mail 

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: pci-sysfs-rom-20-mm.patch --]
[-- Type: text/x-patch; name="pci-sysfs-rom-20-mm.patch", Size: 15073 bytes --]

===== arch/i386/pci/fixup.c 1.20 vs edited =====
--- 1.20/arch/i386/pci/fixup.c	Wed Aug  4 07:57:17 2004
+++ edited/arch/i386/pci/fixup.c	Sat Aug 28 13:15:51 2004
@@ -255,3 +255,41 @@
 }
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2, pci_fixup_nforce2);
 
+/*
+ * Fixup to mark boot BIOS video selected by BIOS before it changes
+ *
+ * From information provided by "Jon Smirl" <jonsmirl@yahoo.com>
+ *
+ * The standard boot ROM sequence for an x86 machine uses the BIOS
+ * to select an initial video card for boot display. This boot video 
+ * card will have it's BIOS copied to C0000 in system RAM. 
+ * IORESOURCE_ROM_SHADOW is used to associate the boot video
+ * card with this copy. On laptops this copy has to be used since
+ * the main ROM may be compressed or combined with another image.
+ * See pci_map_rom() for use of this flag. IORESOURCE_ROM_SHADOW
+ * is marked here since the boot video device will be the only enabled
+ * video device at this point.
+ *
+ */static void __devinit pci_fixup_video(struct pci_dev *pdev)
+{
+	struct pci_dev *bridge;
+	struct pci_bus *bus;
+	u16 l;
+
+	if ((pdev->class >> 8) != PCI_CLASS_DISPLAY_VGA)
+		return;
+
+	/* Is VGA routed to us? */
+	bus = pdev->bus;
+	while (bus) {
+		bridge = bus->self;
+		if (bridge) {
+			pci_read_config_word(bridge, PCI_BRIDGE_CONTROL, &l);
+			if (!(l & PCI_BRIDGE_CTL_VGA))
+				return;
+		}
+		bus = bus->parent;
+	}
+	pdev->resource[PCI_ROM_RESOURCE].flags |= IORESOURCE_ROM_SHADOW;
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, pci_fixup_video);
===== drivers/pci/pci-sysfs.c 1.10 vs edited =====
--- 1.10/drivers/pci/pci-sysfs.c	Fri Jun  4 09:23:04 2004
+++ edited/drivers/pci/pci-sysfs.c	Sat Aug 28 13:16:03 2004
@@ -5,6 +5,8 @@
  * (C) Copyright 2002-2004 IBM Corp.
  * (C) Copyright 2003 Matthew Wilcox
  * (C) Copyright 2003 Hewlett-Packard
+ * (C) Copyright 2004 Jon Smirl <jonsmirl@yahoo.com>
+ * (C) Copyright 2004 Silicon Graphics, Inc. Jesse Barnes <jbarnes@sgi.com>
  *
  * File attributes for PCI devices
  *
@@ -20,6 +22,8 @@
 
 #include "pci.h"
 
+static int sysfs_initialized;	/* = 0 */
+
 /* show configuration fields */
 #define pci_config_attr(field, format_string)				\
 static ssize_t								\
@@ -164,6 +168,245 @@
 	return count;
 }
 
+/**
+ * pci_enable_rom - enable ROM decoding for a PCI device
+ * @dev: PCI device to enable
+ *
+ * Enable ROM decoding on @dev.  This involves simply turning on the last
+ * bit of the PCI ROM BAR.  Note that some cards may share address decoders
+ * between the ROM and other resources, so enabling it may disable access
+ * to MMIO registers or other card memory.
+ */
+static void
+pci_enable_rom(struct pci_dev *pdev)
+{
+	u32 rom_addr;
+	
+	pci_read_config_dword(pdev, pdev->rom_base_reg, &rom_addr);
+	rom_addr |= PCI_ROM_ADDRESS_ENABLE;
+	pci_write_config_dword(pdev, pdev->rom_base_reg, rom_addr);
+}
+
+/**
+ * pci_disable_rom - disable ROM decoding for a PCI device
+ * @dev: PCI device to disable
+ *
+ * Disable ROM decoding on a PCI device by turning off the last bit in the
+ * ROM BAR.
+ */
+static void
+pci_disable_rom(struct pci_dev *pdev)
+{
+	u32 rom_addr;
+	pci_read_config_dword(pdev, pdev->rom_base_reg, &rom_addr);
+	rom_addr &= ~PCI_ROM_ADDRESS_ENABLE;
+	pci_write_config_dword(pdev, pdev->rom_base_reg, rom_addr);
+}
+
+/**
+ * pci_map_rom - map a PCI ROM to kernel space
+ * @dev: pointer to pci device struct
+ * @size: pointer to receive size of pci window over ROM
+ * @return: kernel virtual pointer to image of ROM
+ *
+ * Map a PCI ROM into kernel space. If ROM is boot video ROM,
+ * the shadow BIOS copy will be returned instead of the 
+ * actual ROM.
+ */
+unsigned char *
+pci_map_rom(struct pci_dev *pdev, size_t *size)
+{
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+	loff_t start;
+	unsigned char *rom, *image;
+	int last_image;
+	
+	if (res->flags & IORESOURCE_ROM_SHADOW) {	/* IORESOURCE_ROM_SHADOW only set on x86 */
+		start = (loff_t)0xC0000; 	/* primary video rom always starts here */
+		*size = 0x20000;		/* cover C000:0 through E000:0 */
+	} else {
+		if (res->flags & IORESOURCE_ROM_COPY) {
+			*size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
+			return (unsigned char *)pci_resource_start(pdev, PCI_ROM_RESOURCE);
+		} else {
+			/* assign the ROM an address if it doesn't have one */
+			if (res->parent == NULL)
+				pci_assign_resource(pdev, PCI_ROM_RESOURCE);
+	
+			start = pci_resource_start(pdev, PCI_ROM_RESOURCE);
+			*size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
+			if (*size == 0)
+				return NULL;
+			
+			/* Enable ROM space decodes */
+			pci_enable_rom(pdev);
+		}
+	}
+	
+	rom = ioremap(start, *size);
+	if (!rom) {
+		/* restore enable if ioremap fails */
+		if (!(res->flags & (IORESOURCE_ROM_ENABLE | IORESOURCE_ROM_SHADOW | IORESOURCE_ROM_COPY)))
+			pci_disable_rom(pdev);
+		return NULL;
+	}		
+
+	/* Try to find the true size of the ROM since sometimes the PCI window */
+	/* size is much larger than the actual size of the ROM. */
+	/* True size is important if the ROM is going to be copied. */
+	image = rom;
+	do {
+		char *pds;
+		/* Standard PCI ROMs start out with these bytes 55 AA */
+		if (readb(image) != 0x55)
+			break;
+		if (readb(image + 1) != 0xAA)
+			break;
+		/* get the PCI data structure and check its signature */
+		pds = image + readw(image + 24);
+		if (readb(pds) != 'P')
+			break;
+		if (readb(pds + 1) != 'C')
+			break;
+		if (readb(pds + 2) != 'I')
+			break;
+		if (readb(pds + 3) != 'R')
+			break;
+		last_image = readb(pds + 21) & 0x80;
+		/* this length is reliable */
+		image += readw(pds + 16) * 512;
+	} while (!last_image);
+
+	*size = image - rom;
+
+	return rom;
+}
+
+/**
+ * pci_map_rom_copy - map a PCI ROM to kernel space, create a copy
+ * @dev: pointer to pci device struct
+ * @size: pointer to receive size of pci window over ROM
+ * @return: kernel virtual pointer to image of ROM
+ *
+ * Map a PCI ROM into kernel space. If ROM is boot video ROM,
+ * the shadow BIOS copy will be returned instead of the 
+ * actual ROM.
+ */
+unsigned char *
+pci_map_rom_copy(struct pci_dev *pdev, size_t *size)
+{
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+	unsigned char *rom;
+	
+	rom = pci_map_rom(pdev, size);
+	if (!rom)
+		return NULL;
+		
+	if (res->flags & (IORESOURCE_ROM_COPY | IORESOURCE_ROM_SHADOW))
+		return rom;
+		
+	res->start = (unsigned long)kmalloc(*size, GFP_KERNEL);
+	if (!res->start) 
+		return rom;
+
+	res->end = res->start + *size; 
+	memcpy((void*)res->start, rom, *size);
+	pci_unmap_rom(pdev, rom);
+	res->flags |= IORESOURCE_ROM_COPY;
+	
+	return (unsigned char *)res->start;
+}
+
+/**
+ * pci_unmap_rom - unmap the ROM from kernel space
+ * @dev: pointer to pci device struct
+ * @rom: virtual address of the previous mapping
+ *
+ * Remove a mapping of a previously mapped ROM
+ */
+void 
+pci_unmap_rom(struct pci_dev *pdev, unsigned char *rom)
+{
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+
+	if (res->flags & IORESOURCE_ROM_COPY)
+		return;
+		
+	iounmap(rom);
+		
+	/* Disable again before continuing, leave enabled if pci=rom */
+	if (!(res->flags & (IORESOURCE_ROM_ENABLE | IORESOURCE_ROM_SHADOW)))
+		pci_disable_rom(pdev);
+}
+
+/**
+ * pci_remove_rom - disable the ROM and remove its sysfs attribute
+ * @dev: pointer to pci device struct
+ *
+ */
+void 
+pci_remove_rom(struct pci_dev *pdev) 
+{
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+	
+	if (pci_resource_len(pdev, PCI_ROM_RESOURCE))
+		sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr);
+	if (!(res->flags & (IORESOURCE_ROM_ENABLE | IORESOURCE_ROM_SHADOW | IORESOURCE_ROM_COPY)))
+		pci_disable_rom(pdev);
+}
+
+/**
+ * pci_cleanup_rom - internal routine for freeing the ROM copy created 
+ * by pci_map_rom_copy called from remove.c
+ * @dev: pointer to pci device struct
+ *
+ */
+void 
+pci_cleanup_rom(struct pci_dev *pdev) 
+{
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+	if (res->flags & IORESOURCE_ROM_COPY) {
+		kfree((void*)res->start);
+		res->flags &= ~IORESOURCE_ROM_COPY;
+		res->start = 0;
+		res->end = 0;
+	}
+}
+
+/**
+ * pci_read_rom - read a PCI ROM
+ * @kobj: kernel object handle
+ * @buf: where to put the data we read from the ROM
+ * @off: file offset
+ * @count: number of bytes to read
+ *
+ * Put @count bytes starting at @off into @buf from the ROM in the PCI
+ * device corresponding to @kobj.
+ */
+static ssize_t
+pci_read_rom(struct kobject *kobj, char *buf, loff_t off, size_t count)
+{
+	struct pci_dev *pdev = to_pci_dev(container_of(kobj, struct device, kobj));
+	unsigned char *rom;
+	size_t size;
+	
+	rom = pci_map_rom(pdev, &size);	/* size starts out as PCI window size */
+	if (!rom)
+		return 0;
+		
+	if (off >= size)
+		count = 0;
+	else {
+		if (off + count > size)
+			count = size - off;
+		
+		memcpy_fromio(buf, rom + off, count);
+	}
+	pci_unmap_rom(pdev, rom);
+		
+	return count;
+}
+
 static struct bin_attribute pci_config_attr = {
 	.attr =	{
 		.name = "config",
@@ -186,13 +429,72 @@
 	.write = pci_write_config,
 };
 
-void pci_create_sysfs_dev_files (struct pci_dev *pdev)
+int pci_create_sysfs_dev_files (struct pci_dev *pdev)
 {
+	if (!sysfs_initialized)
+		return -EACCES;
+
 	if (pdev->cfg_size < 4096)
 		sysfs_create_bin_file(&pdev->dev.kobj, &pci_config_attr);
 	else
 		sysfs_create_bin_file(&pdev->dev.kobj, &pcie_config_attr);
 
+	/* If the device has a ROM, try to expose it in sysfs. */
+	if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) {
+		struct bin_attribute *rom_attr;
+		
+		rom_attr = kmalloc(sizeof(*rom_attr), GFP_ATOMIC);
+		if (rom_attr) {
+			pdev->rom_attr = rom_attr;
+			rom_attr->size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
+			rom_attr->attr.name = "rom";
+			rom_attr->attr.mode = S_IRUSR;
+			rom_attr->attr.owner = THIS_MODULE;
+			rom_attr->read = pci_read_rom;
+			sysfs_create_bin_file(&pdev->dev.kobj, rom_attr);
+		}
+	}
 	/* add platform-specific attributes */
 	pcibios_add_platform_entries(pdev);
+	
+	return 0;
+}
+
+/**
+ * pci_remove_sysfs_dev_files - cleanup PCI specific sysfs files
+ * @pdev: device whose entries we should free
+ *
+ * Cleanup when @pdev is removed from sysfs.
+ */
+void pci_remove_sysfs_dev_files(struct pci_dev *pdev)
+{
+	if (pdev->cfg_size < 4096)
+		sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr);
+	else
+		sysfs_remove_bin_file(&pdev->dev.kobj, &pcie_config_attr);
+
+	if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) {
+		if (pdev->rom_attr) {
+			sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr);
+			kfree(pdev->rom_attr);
+		}
+	}
 }
+
+static int __init pci_sysfs_init(void)
+{
+	struct pci_dev *pdev = NULL;
+	
+	sysfs_initialized = 1;
+	while ((pdev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pdev)) != NULL)
+		pci_create_sysfs_dev_files(pdev);
+
+	return 0;
+}
+
+__initcall(pci_sysfs_init);
+
+EXPORT_SYMBOL(pci_map_rom);
+EXPORT_SYMBOL(pci_map_rom_copy);
+EXPORT_SYMBOL(pci_unmap_rom);
+EXPORT_SYMBOL(pci_remove_rom);
===== drivers/pci/pci.h 1.12 vs edited =====
--- 1.12/drivers/pci/pci.h	Fri Jun  4 09:23:04 2004
+++ edited/drivers/pci/pci.h	Sat Aug 28 13:15:52 2004
@@ -2,7 +2,9 @@
 
 extern int pci_hotplug (struct device *dev, char **envp, int num_envp,
 			 char *buffer, int buffer_size);
-extern void pci_create_sysfs_dev_files(struct pci_dev *pdev);
+extern int pci_create_sysfs_dev_files(struct pci_dev *pdev);
+extern void pci_remove_sysfs_dev_files(struct pci_dev *pdev);
+extern void pci_cleanup_rom(struct pci_dev *dev);
 extern int pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
 				  unsigned long size, unsigned long align,
 				  unsigned long min, unsigned int type_mask,
===== drivers/pci/probe.c 1.67 vs edited =====
--- 1.67/drivers/pci/probe.c	Thu Aug 26 13:10:09 2004
+++ edited/drivers/pci/probe.c	Sat Aug 28 13:15:52 2004
@@ -170,7 +170,7 @@
 		if (sz && sz != 0xffffffff) {
 			sz = pci_size(l, sz, PCI_ROM_ADDRESS_MASK);
 			if (sz) {
-				res->flags = (l & PCI_ROM_ADDRESS_ENABLE) |
+				res->flags = (l & IORESOURCE_ROM_ENABLE) |
 				  IORESOURCE_MEM | IORESOURCE_PREFETCH |
 				  IORESOURCE_READONLY | IORESOURCE_CACHEABLE;
 				res->start = l & PCI_ROM_ADDRESS_MASK;
===== drivers/pci/remove.c 1.4 vs edited =====
--- 1.4/drivers/pci/remove.c	Thu Aug 26 13:10:09 2004
+++ edited/drivers/pci/remove.c	Sat Aug 28 13:15:52 2004
@@ -16,6 +16,7 @@
 
  	msi_remove_pci_irq_vectors(dev);
 
+	pci_cleanup_rom(dev);
 	for (i = 0; i < PCI_NUM_RESOURCES; i++) {
 		struct resource *res = dev->resource + i;
 		if (res->parent)
@@ -26,6 +27,7 @@
 static void pci_destroy_dev(struct pci_dev *dev)
 {
 	pci_proc_detach_device(dev);
+	pci_remove_sysfs_dev_files(dev);
 	device_unregister(&dev->dev);
 
 	/* Remove the device from the device lists, and prevent any further
===== drivers/pci/setup-res.c 1.25 vs edited =====
--- 1.25/drivers/pci/setup-res.c	Thu Mar 18 18:40:56 2004
+++ edited/drivers/pci/setup-res.c	Sat Aug 28 13:15:53 2004
@@ -56,7 +56,7 @@
 	if (resno < 6) {
 		reg = PCI_BASE_ADDRESS_0 + 4 * resno;
 	} else if (resno == PCI_ROM_RESOURCE) {
-		new |= res->flags & PCI_ROM_ADDRESS_ENABLE;
+		new |= res->flags & IORESOURCE_ROM_ENABLE;
 		reg = dev->rom_base_reg;
 	} else {
 		/* Hmm, non-standard resource. */
===== include/linux/ioport.h 1.14 vs edited =====
--- 1.14/include/linux/ioport.h	Thu Dec 18 00:48:57 2003
+++ edited/include/linux/ioport.h	Sat Aug 28 13:15:53 2004
@@ -82,6 +82,11 @@
 #define IORESOURCE_MEM_SHADOWABLE	(1<<5)	/* dup: IORESOURCE_SHADOWABLE */
 #define IORESOURCE_MEM_EXPANSIONROM	(1<<6)
 
+/* PCI ROM control bits (IORESOURCE_BITS) */
+#define IORESOURCE_ROM_ENABLE		(1<<0)	/* ROM is enabled, same as PCI_ROM_ADDRESS_ENABLE */
+#define IORESOURCE_ROM_SHADOW		(1<<1)	/* ROM is copy at C000:0 */
+#define IORESOURCE_ROM_COPY		(1<<2)	/* ROM is alloc'd copy, resource field overlaid */
+
 /* PC/ISA/whatever - the normal PC address spaces: IO and memory */
 extern struct resource ioport_resource;
 extern struct resource iomem_resource;
===== include/linux/pci.h 1.136 vs edited =====
--- 1.136/include/linux/pci.h	Thu Aug 26 13:10:27 2004
+++ edited/include/linux/pci.h	Sat Aug 28 13:15:54 2004
@@ -537,6 +537,7 @@
 	unsigned int	is_busmaster:1; /* device is busmaster */
 	
 	u32		saved_config_space[16]; /* config space saved at suspend time */
+	struct bin_attribute *rom_attr; /* attribute descriptor for sysfs ROM entry */
 #ifdef CONFIG_PCI_NAMES
 #define PCI_NAME_SIZE	96
 #define PCI_NAME_HALF	__stringify(43)	/* less than half to handle slop */
@@ -777,6 +778,12 @@
 int pci_dac_set_dma_mask(struct pci_dev *dev, u64 mask);
 int pci_set_consistent_dma_mask(struct pci_dev *dev, u64 mask);
 int pci_assign_resource(struct pci_dev *dev, int i);
+
+/* ROM control related routines */
+unsigned char *pci_map_rom(struct pci_dev *pdev, size_t *size);
+unsigned char *pci_map_rom_copy(struct pci_dev *pdev, size_t *size);
+void pci_unmap_rom(struct pci_dev *pdev, unsigned char *rom);
+void pci_remove_rom(struct pci_dev *pdev);
 
 /* Power management related routines */
 int pci_save_state(struct pci_dev *dev, u32 *buffer);

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-27 22:29                                         ` Jon Smirl
@ 2004-08-28 16:35                                           ` Matthew Wilcox
  2004-08-28 21:53                                             ` Grant Grundler
  0 siblings, 1 reply; 110+ messages in thread
From: Matthew Wilcox @ 2004-08-28 16:35 UTC (permalink / raw)
  To: Jon Smirl
  Cc: Matthew Wilcox, Greg KH, Jesse Barnes, Martin Mares, Pallipadi,
	Venkatesh, linux-pci, Alan Cox, Linux Kernel Mailing List,
	Petr Vandrovec, Benjamin Herrenschmidt

On Fri, Aug 27, 2004 at 03:29:38PM -0700, Jon Smirl wrote:
> I would think that it would return 4MB. If it can't find standard ROM
> headers it should return the window size. 

The patch I just sent changes this ... if we can find PCI headers,
we should return nothing.

> Are you sure you are getting the correct contents of those ROMs? Would
> it be worthwhile to try and get the author of the ROMs to add standard
> ROM headers? The content you included doesn't look that useful unless
> that is what IA64 instructions look like.

It looks like nothing that makes any sense to me.  Maybe it's a blanked
EEPROM or something.  This particular device is on the motherboard of a
prototype machine, so there could be almost anything going on with it ;-)

> If reading past 1MB for those ROMs causes a reboots, could something be
> wrong in the IA64 fault handing code?

No, that's normal behaviour on ia64 -- unacknowledged PCI reads cause a
machine check rather than reading ffffffff like x86 does.

-- 
"Next the statesmen will invent cheap lies, putting the blame upon 
the nation that is attacked, and every man will be glad of those
conscience-soothing falsities, and will diligently study them, and refuse
to examine any refutations of them; and thus he will by and by convince 
himself that the war is just, and will thank God for the better sleep 
he enjoys after this process of grotesque self-deception." -- Mark Twain

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-26 19:54                                       ` Jon Smirl
@ 2004-08-28 16:15                                         ` Matthew Wilcox
  2004-08-28 17:33                                           ` Jon Smirl
  2004-08-28 17:38                                           ` Jon Smirl
  0 siblings, 2 replies; 110+ messages in thread
From: Matthew Wilcox @ 2004-08-28 16:15 UTC (permalink / raw)
  To: Jon Smirl
  Cc: Matthew Wilcox, Greg KH, Jesse Barnes, Martin Mares, Pallipadi,
	Venkatesh, linux-pci, Alan Cox, Linux Kernel Mailing List,
	Petr Vandrovec, Benjamin Herrenschmidt

On Thu, Aug 26, 2004 at 12:54:59PM -0700, Jon Smirl wrote:
> New version that uses the PCI data structure to get the size. It's
> against 2.6.9-rc1-mm1, but that kernel is DOA on my machine.
> 
> I was confused about the type 0 versus other types and see that this
> code doesn't need to know the ROM type.

A few more bugfixes:

 - I was confused, it was hex 18, not decimal 16
 - Its/it's again ;-)
 - Only return the bits of the ROM that are in PCI format
 - Remove the actual resource, not some strange static one
 - Delete the static one that wasn't really used.

BTW, what do you think to moving all the ROM code from pci-sysfs.c to a
new file rom.c?  There's not much sense to the ROM accessing code being
mixed in with the sysfs code like this.  After all, we might choose to
expose the ROM in some other way in the future.

diff -u edited/drivers/pci/pci-sysfs.c vpd-2.6/drivers/pci/pci-sysfs.c
--- edited/drivers/pci/pci-sysfs.c	Thu Aug 26 15:21:02 2004
+++ vpd-2.6/drivers/pci/pci-sysfs.c	27 Aug 2004 19:33:37 -0000
@@ -262,8 +262,8 @@
 			break;
 		if (readb(image + 1) != 0xAA)
 			break;
-		/* get the PCI data structure and check it's signature */
-		pds = image + readw(image + 16);
+		/* get the PCI data structure and check its signature */
+		pds = image + readw(image + 24);
 		if (readb(pds) != 'P')
 			break;
 		if (readb(pds + 1) != 'C')
@@ -277,8 +277,7 @@
 		image += readw(pds + 16) * 512;
 	} while (!last_image);
 
-	if (image != rom)
-		*size = min((size_t)(image - rom), *size); /* make sure we didn't include an adjacent ROM */
+	*size = image - rom;
 
 	return rom;
 }
@@ -340,8 +342,6 @@
 		pci_disable_rom(pdev);
 }
 
-static struct bin_attribute rom_attr;
-
 /**
  * pci_remove_rom - disable the ROM and remove its sysfs attribute
  * @dev: pointer to pci device struct
@@ -353,7 +353,7 @@
 	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
 	
 	if (pci_resource_len(pdev, PCI_ROM_RESOURCE))
-		sysfs_remove_bin_file(&pdev->dev.kobj, &rom_attr);
+		sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr);
 	if (!(res->flags & (IORESOURCE_ROM_ENABLE | IORESOURCE_ROM_SHADOW | IORESOURCE_ROM_COPY)))
 		pci_disable_rom(pdev);
 }


-- 
"Next the statesmen will invent cheap lies, putting the blame upon 
the nation that is attacked, and every man will be glad of those
conscience-soothing falsities, and will diligently study them, and refuse
to examine any refutations of them; and thus he will by and by convince 
himself that the war is just, and will thank God for the better sleep 
he enjoys after this process of grotesque self-deception." -- Mark Twain

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-27 16:43                                       ` Matthew Wilcox
@ 2004-08-27 22:29                                         ` Jon Smirl
  2004-08-28 16:35                                           ` Matthew Wilcox
  0 siblings, 1 reply; 110+ messages in thread
From: Jon Smirl @ 2004-08-27 22:29 UTC (permalink / raw)
  To: Matthew Wilcox
  Cc: Greg KH, Jesse Barnes, Martin Mares, Pallipadi, Venkatesh,
	linux-pci, Alan Cox, Linux Kernel Mailing List, Petr Vandrovec,
	Benjamin Herrenschmidt

--- Matthew Wilcox <willy@debian.org> wrote:
> The expansion rom PCI region claims to be 4MB in size, but if we try
> to read past 1MB, the machine reboots.  This won't be a problem with
> the latest patch because it'll return a size of 0.

I would think that it would return 4MB. If it can't find standard ROM
headers it should return the window size. 

Are you sure you are getting the correct contents of those ROMs? Would
it be worthwhile to try and get the author of the ROMs to add standard
ROM headers? The content you included doesn't look that useful unless
that is what IA64 instructions look like.

If reading past 1MB for those ROMs causes a reboots, could something be
wrong in the IA64 fault handing code?

I'd feel a lot better if someone actually tried this patch on a ppc or
sun machine.

=====
Jon Smirl
jonsmirl@yahoo.com


		
_______________________________
Do you Yahoo!?
Win 1 of 4,000 free domain names from Yahoo! Enter now.
http://promotions.yahoo.com/goldrush

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-26 15:58                                     ` Matthew Wilcox
  2004-08-26 19:54                                       ` Jon Smirl
@ 2004-08-27 16:43                                       ` Matthew Wilcox
  2004-08-27 22:29                                         ` Jon Smirl
  1 sibling, 1 reply; 110+ messages in thread
From: Matthew Wilcox @ 2004-08-27 16:43 UTC (permalink / raw)
  To: Matthew Wilcox
  Cc: Jon Smirl, Greg KH, Jesse Barnes, Martin Mares, Pallipadi,
	Venkatesh, linux-pci, Alan Cox, Linux Kernel Mailing List,
	Petr Vandrovec, Benjamin Herrenschmidt

On Thu, Aug 26, 2004 at 04:58:03PM +0100, Matthew Wilcox wrote:
> I'm just tracking down another bug with the current code -- it maps the
> ROMs into the wrong address range on my ia64 zx1 box.  I'll send more
> mail once I've fixed that.

I fixed it in the ia64 code by ensuring that the ROM resource has a parent
and thus skipping the call to pci_assign_resource().

Of the 15 pci devfns on my system, 6 of them have ROMs:

/sys/devices/pci0000:80/0000:80:00.0/rom
/sys/devices/pci0000:60/0000:60:01.1/rom
/sys/devices/pci0000:60/0000:60:01.0/rom
/sys/devices/pci0000:40/0000:40:01.0/rom
/sys/devices/pci0000:20/0000:20:01.1/rom
/sys/devices/pci0000:20/0000:20:01.0/rom

0000:20:01.0 SCSI storage controller: LSI Logic / Symbios Logic 53c1030 PCI-X Fusion-MPT Dual Ultra320 SCSI (rev 03)
0000:20:01.1 SCSI storage controller: LSI Logic / Symbios Logic 53c1030 PCI-X Fusion-MPT Dual Ultra320 SCSI (rev 03)
0000:40:01.0 Token ring network controller: IBM 16/4 Token ring UTP/STP controller (rev 04)
0000:60:01.0 SCSI storage controller: LSI Logic / Symbios Logic 53c1010 66MHz  Ultra3 SCSI Adapter (rev 01)
0000:60:01.1 SCSI storage controller: LSI Logic / Symbios Logic 53c1010 66MHz  Ultra3 SCSI Adapter (rev 01)
0000:80:00.0 VGA compatible controller: Matrox Graphics, Inc. MGA G400 AGP (rev 04)

The only one that doesn't work is the pair of 1030 chips (these are on the
motherboard, so possibly there's something funky going on).  The contents of
these roms are:

00000000  2e 2e 2e 2e 2e 2e 2e 2e  3e 2e 2e 2e 3e 3e 2e 2e  |........>...>>..|
00000010  2e 2e 2e 2e 2e 2e 2e 2e  2e 2e 2e 2e 2e 2e 2e 2e  |................|
00000020  1e 1e 1e 1e 1e 1e 1e 1e  1e 1e 1e 1e 1e 1e 1e 1e  |................|
00000030  0e 0e 0e 0e 0e 0e 0e 0e  0e 0e 0e 0e 0e 0e 0e 0e  |................|
00000040  3e 2e 2e 2e 3e 2e 2e 2e  3e 3e 2e 2e 3e 3e 3e 2e  |>...>...>>..>>>.|
00000050  2e 2e 2e 2e 2e 2e 2e 2e  2e 2e 2e 2e 2e 2e 2e 2e  |................|
00000060  1e 1e 1e 1e 1e 1e 1e 1e  1e 1e 1e 1e 1e 1e 1e 1e  |................|
00000070  0e 0e 0e 0e 0e 0e 0e 0e  0e 0e 0e 0e 0e 0e 0e 0e  |................|
00000080  2e 2e 2e 2e 2e 2e 2e 2e  3e 3e 2e 2e 2e 2e 2e 2e  |........>>......|
00000090  2e 2e 2e 2e 2e 2e 2e 2e  2e 2e 2e 2e 2e 2e 2e 2e  |................|
000000a0  1e 1e 1e 1e 1e 1e 1e 1e  1e 1e 1e 1e 1e 1e 1e 1e  |................|
000000b0  0e 0e 0e 0e 0e 0e 0e 0e  0e 0e 0e 0e 0e 0e 0e 0e  |................|
000000c0  3e 3e 2e 2e 3e 2e 2e 2e  3e 3e 3e 3e 3e 3e 3e 3e  |>>..>...>>>>>>>>|
000000d0  2e 2e 2e 2e 2e 2e 2e 2e  2e 2e 2e 2e 2e 2e 2e 2e  |................|
000000e0  1e 1e 1e 1e 1e 1e 1e 1e  1e 1e 1e 1e 1e 1e 1e 1e  |................|
000000f0  0e 0e 0e 0e 0e 0e 0e 0e  0e 0e 0e 0e 0e 0e 0e 0e  |................|

The expansion rom PCI region claims to be 4MB in size, but if we try to
read past 1MB, the machine reboots.  This won't be a problem with the latest
patch because it'll return a size of 0.

-- 
"Next the statesmen will invent cheap lies, putting the blame upon 
the nation that is attacked, and every man will be glad of those
conscience-soothing falsities, and will diligently study them, and refuse
to examine any refutations of them; and thus he will by and by convince 
himself that the war is just, and will thank God for the better sleep 
he enjoys after this process of grotesque self-deception." -- Mark Twain

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-26 15:58                                     ` Matthew Wilcox
@ 2004-08-26 19:54                                       ` Jon Smirl
  2004-08-28 16:15                                         ` Matthew Wilcox
  2004-08-27 16:43                                       ` Matthew Wilcox
  1 sibling, 1 reply; 110+ messages in thread
From: Jon Smirl @ 2004-08-26 19:54 UTC (permalink / raw)
  To: Matthew Wilcox
  Cc: Greg KH, Jesse Barnes, Martin Mares, Pallipadi, Venkatesh,
	linux-pci, Alan Cox, Linux Kernel Mailing List, Petr Vandrovec,
	Benjamin Herrenschmidt

[-- Attachment #1: Type: text/plain, Size: 819 bytes --]

New version that uses the PCI data structure to get the size. It's
against 2.6.9-rc1-mm1, but that kernel is DOA on my machine.

I was confused about the type 0 versus other types and see that this
code doesn't need to know the ROM type.

--- Matthew Wilcox <willy@debian.org> wrote:
> > > Second, only images of code type 0 (Intel x86, PC-AT compatible)
> are
> > > specified to have length as the third byte.  OpenFirmware,
> PA-RISC
> > > and
> > > EFI may or may not have size as the third byte.  It's also
> possible
> > > that there are multiple images in the ROM.  So what you have to
> do to
> > > be correct is something like ...

=====
Jon Smirl
jonsmirl@yahoo.com


		
_______________________________
Do you Yahoo!?
Win 1 of 4,000 free domain names from Yahoo! Enter now.
http://promotions.yahoo.com/goldrush

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: pci-sysfs-rom-19-mm.patch --]
[-- Type: text/x-patch; name="pci-sysfs-rom-19-mm.patch", Size: 15460 bytes --]

Exposes PCI ROMs via sysfs. Four new routine for drivers to use when 
accessing ROMs: pci_map_rom, pci_map_rom_copy, pci_unmap_rom, pci_remove_rom. 
Handles shadow ROMs for laptops that compress actual ROMs.

Signed-off-by: "Jon Smirl" <jonsmirl@yahoo.com>
===== arch/i386/pci/fixup.c 1.20 vs edited =====
--- 1.20/arch/i386/pci/fixup.c	Wed Aug  4 07:57:17 2004
+++ edited/arch/i386/pci/fixup.c	Thu Aug 26 13:16:10 2004
@@ -255,3 +255,41 @@
 }
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2, pci_fixup_nforce2);
 
+/*
+ * Fixup to mark boot BIOS video selected by BIOS before it changes
+ *
+ * From information provided by "Jon Smirl" <jonsmirl@yahoo.com>
+ *
+ * The standard boot ROM sequence for an x86 machine uses the BIOS
+ * to select an initial video card for boot display. This boot video 
+ * card will have it's BIOS copied to C0000 in system RAM. 
+ * IORESOURCE_ROM_SHADOW is used to associate the boot video
+ * card with this copy. On laptops this copy has to be used since
+ * the main ROM may be compressed or combined with another image.
+ * See pci_map_rom() for use of this flag. IORESOURCE_ROM_SHADOW
+ * is marked here since the boot video device will be the only enabled
+ * video device at this point.
+ *
+ */static void __devinit pci_fixup_video(struct pci_dev *pdev)
+{
+	struct pci_dev *bridge;
+	struct pci_bus *bus;
+	u16 l;
+
+	if ((pdev->class >> 8) != PCI_CLASS_DISPLAY_VGA)
+		return;
+
+	/* Is VGA routed to us? */
+	bus = pdev->bus;
+	while (bus) {
+		bridge = bus->self;
+		if (bridge) {
+			pci_read_config_word(bridge, PCI_BRIDGE_CONTROL, &l);
+			if (!(l & PCI_BRIDGE_CTL_VGA))
+				return;
+		}
+		bus = bus->parent;
+	}
+	pdev->resource[PCI_ROM_RESOURCE].flags |= IORESOURCE_ROM_SHADOW;
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, pci_fixup_video);
===== drivers/pci/pci-sysfs.c 1.10 vs edited =====
--- 1.10/drivers/pci/pci-sysfs.c	Fri Jun  4 09:23:04 2004
+++ edited/drivers/pci/pci-sysfs.c	Thu Aug 26 15:21:02 2004
@@ -5,6 +5,8 @@
  * (C) Copyright 2002-2004 IBM Corp.
  * (C) Copyright 2003 Matthew Wilcox
  * (C) Copyright 2003 Hewlett-Packard
+ * (C) Copyright 2004 Jon Smirl <jonsmirl@yahoo.com>
+ * (C) Copyright 2004 Silicon Graphics, Inc. Jesse Barnes <jbarnes@sgi.com>
  *
  * File attributes for PCI devices
  *
@@ -20,6 +22,8 @@
 
 #include "pci.h"
 
+static int sysfs_initialized;	/* = 0 */
+
 /* show configuration fields */
 #define pci_config_attr(field, format_string)				\
 static ssize_t								\
@@ -164,6 +168,248 @@
 	return count;
 }
 
+/**
+ * pci_enable_rom - enable ROM decoding for a PCI device
+ * @dev: PCI device to enable
+ *
+ * Enable ROM decoding on @dev.  This involves simply turning on the last
+ * bit of the PCI ROM BAR.  Note that some cards may share address decoders
+ * between the ROM and other resources, so enabling it may disable access
+ * to MMIO registers or other card memory.
+ */
+static void
+pci_enable_rom(struct pci_dev *pdev)
+{
+	u32 rom_addr;
+	
+	pci_read_config_dword(pdev, pdev->rom_base_reg, &rom_addr);
+	rom_addr |= PCI_ROM_ADDRESS_ENABLE;
+	pci_write_config_dword(pdev, pdev->rom_base_reg, rom_addr);
+}
+
+/**
+ * pci_disable_rom - disable ROM decoding for a PCI device
+ * @dev: PCI device to disable
+ *
+ * Disable ROM decoding on a PCI device by turning off the last bit in the
+ * ROM BAR.
+ */
+static void
+pci_disable_rom(struct pci_dev *pdev)
+{
+	u32 rom_addr;
+	pci_read_config_dword(pdev, pdev->rom_base_reg, &rom_addr);
+	rom_addr &= ~PCI_ROM_ADDRESS_ENABLE;
+	pci_write_config_dword(pdev, pdev->rom_base_reg, rom_addr);
+}
+
+/**
+ * pci_map_rom - map a PCI ROM to kernel space
+ * @dev: pointer to pci device struct
+ * @size: pointer to receive size of pci window over ROM
+ * @return: kernel virtual pointer to image of ROM
+ *
+ * Map a PCI ROM into kernel space. If ROM is boot video ROM,
+ * the shadow BIOS copy will be returned instead of the 
+ * actual ROM.
+ */
+unsigned char *
+pci_map_rom(struct pci_dev *pdev, size_t *size)
+{
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+	loff_t start;
+	unsigned char *rom, *image;
+	int last_image;
+	
+	if (res->flags & IORESOURCE_ROM_SHADOW) {	/* IORESOURCE_ROM_SHADOW only set on x86 */
+		start = (loff_t)0xC0000; 	/* primary video rom always starts here */
+		*size = 0x20000;		/* cover C000:0 through E000:0 */
+	} else {
+		if (res->flags & IORESOURCE_ROM_COPY) {
+			*size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
+			return (unsigned char *)pci_resource_start(pdev, PCI_ROM_RESOURCE);
+		} else {
+			/* assign the ROM an address if it doesn't have one */
+			if (res->parent == NULL)
+				pci_assign_resource(pdev, PCI_ROM_RESOURCE);
+	
+			start = pci_resource_start(pdev, PCI_ROM_RESOURCE);
+			*size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
+			if (*size == 0)
+				return NULL;
+			
+			/* Enable ROM space decodes */
+			pci_enable_rom(pdev);
+		}
+	}
+	
+	rom = ioremap(start, *size);
+	if (!rom) {
+		/* restore enable if ioremap fails */
+		if (!(res->flags & (IORESOURCE_ROM_ENABLE | IORESOURCE_ROM_SHADOW | IORESOURCE_ROM_COPY)))
+			pci_disable_rom(pdev);
+		return NULL;
+	}		
+
+	/* Try to find the true size of the ROM since sometimes the PCI window */
+	/* size is much larger than the actual size of the ROM. */
+	/* True size is important if the ROM is going to be copied. */
+	image = rom;
+	do {
+		char *pds;
+		/* Standard PCI ROMs start out with these bytes 55 AA */
+		if (readb(image) != 0x55)
+			break;
+		if (readb(image + 1) != 0xAA)
+			break;
+		/* get the PCI data structure and check it's signature */
+		pds = image + readw(image + 16);
+		if (readb(pds) != 'P')
+			break;
+		if (readb(pds + 1) != 'C')
+			break;
+		if (readb(pds + 2) != 'I')
+			break;
+		if (readb(pds + 3) != 'R')
+			break;
+		last_image = readb(pds + 21) & 0x80;
+		/* this length is reliable */
+		image += readw(pds + 16) * 512;
+	} while (!last_image);
+
+	if (image != rom)
+		*size = min((size_t)(image - rom), *size); /* make sure we didn't include an adjacent ROM */
+
+	return rom;
+}
+
+/**
+ * pci_map_rom_copy - map a PCI ROM to kernel space, create a copy
+ * @dev: pointer to pci device struct
+ * @size: pointer to receive size of pci window over ROM
+ * @return: kernel virtual pointer to image of ROM
+ *
+ * Map a PCI ROM into kernel space. If ROM is boot video ROM,
+ * the shadow BIOS copy will be returned instead of the 
+ * actual ROM.
+ */
+unsigned char *
+pci_map_rom_copy(struct pci_dev *pdev, size_t *size)
+{
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+	unsigned char *rom;
+	
+	rom = pci_map_rom(pdev, size);
+	if (!rom)
+		return NULL;
+		
+	if (res->flags & (IORESOURCE_ROM_COPY | IORESOURCE_ROM_SHADOW))
+		return rom;
+		
+	res->start = (unsigned long)kmalloc(*size, GFP_KERNEL);
+	if (!res->start) 
+		return rom;
+
+	res->end = res->start + *size; 
+	memcpy((void*)res->start, rom, *size);
+	pci_unmap_rom(pdev, rom);
+	res->flags |= IORESOURCE_ROM_COPY;
+	
+	return (unsigned char *)res->start;
+}
+
+/**
+ * pci_unmap_rom - unmap the ROM from kernel space
+ * @dev: pointer to pci device struct
+ * @rom: virtual address of the previous mapping
+ *
+ * Remove a mapping of a previously mapped ROM
+ */
+void 
+pci_unmap_rom(struct pci_dev *pdev, unsigned char *rom)
+{
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+
+	if (res->flags & IORESOURCE_ROM_COPY)
+		return;
+		
+	iounmap(rom);
+		
+	/* Disable again before continuing, leave enabled if pci=rom */
+	if (!(res->flags & (IORESOURCE_ROM_ENABLE | IORESOURCE_ROM_SHADOW)))
+		pci_disable_rom(pdev);
+}
+
+static struct bin_attribute rom_attr;
+
+/**
+ * pci_remove_rom - disable the ROM and remove its sysfs attribute
+ * @dev: pointer to pci device struct
+ *
+ */
+void 
+pci_remove_rom(struct pci_dev *pdev) 
+{
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+	
+	if (pci_resource_len(pdev, PCI_ROM_RESOURCE))
+		sysfs_remove_bin_file(&pdev->dev.kobj, &rom_attr);
+	if (!(res->flags & (IORESOURCE_ROM_ENABLE | IORESOURCE_ROM_SHADOW | IORESOURCE_ROM_COPY)))
+		pci_disable_rom(pdev);
+}
+
+/**
+ * pci_cleanup_rom - internal routine for freeing the ROM copy created 
+ * by pci_map_rom_copy called from remove.c
+ * @dev: pointer to pci device struct
+ *
+ */
+void 
+pci_cleanup_rom(struct pci_dev *pdev) 
+{
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+	if (res->flags & IORESOURCE_ROM_COPY) {
+		kfree((void*)res->start);
+		res->flags &= ~IORESOURCE_ROM_COPY;
+		res->start = 0;
+		res->end = 0;
+	}
+}
+
+/**
+ * pci_read_rom - read a PCI ROM
+ * @kobj: kernel object handle
+ * @buf: where to put the data we read from the ROM
+ * @off: file offset
+ * @count: number of bytes to read
+ *
+ * Put @count bytes starting at @off into @buf from the ROM in the PCI
+ * device corresponding to @kobj.
+ */
+static ssize_t
+pci_read_rom(struct kobject *kobj, char *buf, loff_t off, size_t count)
+{
+	struct pci_dev *pdev = to_pci_dev(container_of(kobj, struct device, kobj));
+	unsigned char *rom;
+	size_t size;
+	
+	rom = pci_map_rom(pdev, &size);	/* size starts out as PCI window size */
+	if (!rom)
+		return 0;
+		
+	if (off >= size)
+		count = 0;
+	else {
+		if (off + count > size)
+			count = size - off;
+		
+		memcpy_fromio(buf, rom + off, count);
+	}
+	pci_unmap_rom(pdev, rom);
+		
+	return count;
+}
+
 static struct bin_attribute pci_config_attr = {
 	.attr =	{
 		.name = "config",
@@ -186,13 +432,72 @@
 	.write = pci_write_config,
 };
 
-void pci_create_sysfs_dev_files (struct pci_dev *pdev)
+int pci_create_sysfs_dev_files (struct pci_dev *pdev)
 {
+	if (!sysfs_initialized)
+		return -EACCES;
+
 	if (pdev->cfg_size < 4096)
 		sysfs_create_bin_file(&pdev->dev.kobj, &pci_config_attr);
 	else
 		sysfs_create_bin_file(&pdev->dev.kobj, &pcie_config_attr);
 
+	/* If the device has a ROM, try to expose it in sysfs. */
+	if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) {
+		struct bin_attribute *rom_attr;
+		
+		rom_attr = kmalloc(sizeof(*rom_attr), GFP_ATOMIC);
+		if (rom_attr) {
+			pdev->rom_attr = rom_attr;
+			rom_attr->size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
+			rom_attr->attr.name = "rom";
+			rom_attr->attr.mode = S_IRUSR;
+			rom_attr->attr.owner = THIS_MODULE;
+			rom_attr->read = pci_read_rom;
+			sysfs_create_bin_file(&pdev->dev.kobj, rom_attr);
+		}
+	}
 	/* add platform-specific attributes */
 	pcibios_add_platform_entries(pdev);
+	
+	return 0;
+}
+
+/**
+ * pci_remove_sysfs_dev_files - cleanup PCI specific sysfs files
+ * @pdev: device whose entries we should free
+ *
+ * Cleanup when @pdev is removed from sysfs.
+ */
+void pci_remove_sysfs_dev_files(struct pci_dev *pdev)
+{
+	if (pdev->cfg_size < 4096)
+		sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr);
+	else
+		sysfs_remove_bin_file(&pdev->dev.kobj, &pcie_config_attr);
+
+	if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) {
+		if (pdev->rom_attr) {
+			sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr);
+			kfree(pdev->rom_attr);
+		}
+	}
 }
+
+static int __init pci_sysfs_init(void)
+{
+	struct pci_dev *pdev = NULL;
+	
+	sysfs_initialized = 1;
+	while ((pdev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pdev)) != NULL)
+		pci_create_sysfs_dev_files(pdev);
+
+	return 0;
+}
+
+__initcall(pci_sysfs_init);
+
+EXPORT_SYMBOL(pci_map_rom);
+EXPORT_SYMBOL(pci_map_rom_copy);
+EXPORT_SYMBOL(pci_unmap_rom);
+EXPORT_SYMBOL(pci_remove_rom);
===== drivers/pci/pci.h 1.12 vs edited =====
--- 1.12/drivers/pci/pci.h	Fri Jun  4 09:23:04 2004
+++ edited/drivers/pci/pci.h	Thu Aug 26 15:18:44 2004
@@ -2,7 +2,9 @@
 
 extern int pci_hotplug (struct device *dev, char **envp, int num_envp,
 			 char *buffer, int buffer_size);
-extern void pci_create_sysfs_dev_files(struct pci_dev *pdev);
+extern int pci_create_sysfs_dev_files(struct pci_dev *pdev);
+extern void pci_remove_sysfs_dev_files(struct pci_dev *pdev);
+extern void pci_cleanup_rom(struct pci_dev *dev);
 extern int pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
 				  unsigned long size, unsigned long align,
 				  unsigned long min, unsigned int type_mask,
===== drivers/pci/probe.c 1.67 vs edited =====
--- 1.67/drivers/pci/probe.c	Thu Aug 26 13:10:09 2004
+++ edited/drivers/pci/probe.c	Thu Aug 26 13:16:12 2004
@@ -170,7 +170,7 @@
 		if (sz && sz != 0xffffffff) {
 			sz = pci_size(l, sz, PCI_ROM_ADDRESS_MASK);
 			if (sz) {
-				res->flags = (l & PCI_ROM_ADDRESS_ENABLE) |
+				res->flags = (l & IORESOURCE_ROM_ENABLE) |
 				  IORESOURCE_MEM | IORESOURCE_PREFETCH |
 				  IORESOURCE_READONLY | IORESOURCE_CACHEABLE;
 				res->start = l & PCI_ROM_ADDRESS_MASK;
===== drivers/pci/remove.c 1.4 vs edited =====
--- 1.4/drivers/pci/remove.c	Thu Aug 26 13:10:09 2004
+++ edited/drivers/pci/remove.c	Thu Aug 26 13:16:12 2004
@@ -16,6 +16,7 @@
 
  	msi_remove_pci_irq_vectors(dev);
 
+	pci_cleanup_rom(dev);
 	for (i = 0; i < PCI_NUM_RESOURCES; i++) {
 		struct resource *res = dev->resource + i;
 		if (res->parent)
@@ -26,6 +27,7 @@
 static void pci_destroy_dev(struct pci_dev *dev)
 {
 	pci_proc_detach_device(dev);
+	pci_remove_sysfs_dev_files(dev);
 	device_unregister(&dev->dev);
 
 	/* Remove the device from the device lists, and prevent any further
===== drivers/pci/setup-res.c 1.25 vs edited =====
--- 1.25/drivers/pci/setup-res.c	Thu Mar 18 18:40:56 2004
+++ edited/drivers/pci/setup-res.c	Thu Aug 26 13:16:13 2004
@@ -56,7 +56,7 @@
 	if (resno < 6) {
 		reg = PCI_BASE_ADDRESS_0 + 4 * resno;
 	} else if (resno == PCI_ROM_RESOURCE) {
-		new |= res->flags & PCI_ROM_ADDRESS_ENABLE;
+		new |= res->flags & IORESOURCE_ROM_ENABLE;
 		reg = dev->rom_base_reg;
 	} else {
 		/* Hmm, non-standard resource. */
===== include/linux/ioport.h 1.14 vs edited =====
--- 1.14/include/linux/ioport.h	Thu Dec 18 00:48:57 2003
+++ edited/include/linux/ioport.h	Thu Aug 26 13:16:13 2004
@@ -82,6 +82,11 @@
 #define IORESOURCE_MEM_SHADOWABLE	(1<<5)	/* dup: IORESOURCE_SHADOWABLE */
 #define IORESOURCE_MEM_EXPANSIONROM	(1<<6)
 
+/* PCI ROM control bits (IORESOURCE_BITS) */
+#define IORESOURCE_ROM_ENABLE		(1<<0)	/* ROM is enabled, same as PCI_ROM_ADDRESS_ENABLE */
+#define IORESOURCE_ROM_SHADOW		(1<<1)	/* ROM is copy at C000:0 */
+#define IORESOURCE_ROM_COPY		(1<<2)	/* ROM is alloc'd copy, resource field overlaid */
+
 /* PC/ISA/whatever - the normal PC address spaces: IO and memory */
 extern struct resource ioport_resource;
 extern struct resource iomem_resource;
===== include/linux/pci.h 1.136 vs edited =====
--- 1.136/include/linux/pci.h	Thu Aug 26 13:10:27 2004
+++ edited/include/linux/pci.h	Thu Aug 26 13:16:14 2004
@@ -537,6 +537,7 @@
 	unsigned int	is_busmaster:1; /* device is busmaster */
 	
 	u32		saved_config_space[16]; /* config space saved at suspend time */
+	struct bin_attribute *rom_attr; /* attribute descriptor for sysfs ROM entry */
 #ifdef CONFIG_PCI_NAMES
 #define PCI_NAME_SIZE	96
 #define PCI_NAME_HALF	__stringify(43)	/* less than half to handle slop */
@@ -777,6 +778,12 @@
 int pci_dac_set_dma_mask(struct pci_dev *dev, u64 mask);
 int pci_set_consistent_dma_mask(struct pci_dev *dev, u64 mask);
 int pci_assign_resource(struct pci_dev *dev, int i);
+
+/* ROM control related routines */
+unsigned char *pci_map_rom(struct pci_dev *pdev, size_t *size);
+unsigned char *pci_map_rom_copy(struct pci_dev *pdev, size_t *size);
+void pci_unmap_rom(struct pci_dev *pdev, unsigned char *rom);
+void pci_remove_rom(struct pci_dev *pdev);
 
 /* Power management related routines */
 int pci_save_state(struct pci_dev *dev, u32 *buffer);

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-26 15:40                                   ` Jon Smirl
@ 2004-08-26 15:58                                     ` Matthew Wilcox
  2004-08-26 19:54                                       ` Jon Smirl
  2004-08-27 16:43                                       ` Matthew Wilcox
  0 siblings, 2 replies; 110+ messages in thread
From: Matthew Wilcox @ 2004-08-26 15:58 UTC (permalink / raw)
  To: Jon Smirl
  Cc: Matthew Wilcox, Greg KH, Jesse Barnes, Martin Mares, Pallipadi,
	Venkatesh, linux-pci, Alan Cox, Linux Kernel Mailing List,
	Petr Vandrovec, Benjamin Herrenschmidt

On Thu, Aug 26, 2004 at 08:40:49AM -0700, Jon Smirl wrote:
> I was computing the length because of the need to copy for the
> partially decoded case. I have one card that maps a 40KB ROM with a
> 512KB window. In that case it makes a 450KB different in the copy size.

Youch.  That card's actually breaking the PCI spec, but never mind, it exists.

> The copy case is adding a lot of complexity to the code. We could just
> give it the boot and require that partially decoded drivers just turn
> the ROM attribute off.
> 
> If not, how do I get the size from your algorithm?

In words, what the algorithm is doing is:

	for_each_rom_image
		get_the_pci_data_structure
		get_the_rom_length_from_pds

At the end of the algorithm, 'image' should be pointing to the end of the
ROM data.

I'm just tracking down another bug with the current code -- it maps the
ROMs into the wrong address range on my ia64 zx1 box.  I'll send more
mail once I've fixed that.

> --- Matthew Wilcox <willy@debian.org> wrote:
> 
> > On Wed, Aug 25, 2004 at 01:06:38PM -0700, Jon Smirl wrote:
> > > New version attached fixes this and the function documentation.
> > 
> > Sorry, I found two more bugs ;-(
> > 
> > > +unsigned char *
> > > +pci_map_rom(struct pci_dev *pdev, size_t *size)
> > > +{
> > [...]
> > > +	/* Standard PCI ROMs start out with these three bytes 55 AA
> > size/512 */
> > > +	if ((*rom == 0x55) && (*(rom + 1) == 0xAA))
> > > +		*size = *(rom + 2) * 512;	/* return true ROM size, not PCI
> > window size */
> > 
> > First, you're dereferencing an ioremapped pointer directly instead of
> > using readb() et al.  This is a portability no-no but happens to work
> > on x86.
> > 
> > Second, only images of code type 0 (Intel x86, PC-AT compatible) are
> > specified to have length as the third byte.  OpenFirmware, PA-RISC
> > and
> > EFI may or may not have size as the third byte.  It's also possible
> > that there are multiple images in the ROM.  So what you have to do to
> > be correct is something like ...
> > 
> > 	int last_image;
> > 	char *image = rom;
> > 	do {
> > 		char *pds;
> > 		if (readb(image) != 0x55)
> > 			break;
> > 		if (readb(image + 1) != 0xAA)
> > 			break;
> > 		pds = image + readw(image + 16);
> > 		if (readb(pds) != 'P')
> > 			break;
> > 		if (readb(pds + 1) != 'C')
> > 			break;
> > 		if (readb(pds + 2) != 'I')
> > 			break;
> > 		if (readb(pds + 3) != 'R')
> > 			break;
> > 		last_image = readb(pds + 21) & 0x80;
> > 		image += readw(pds + 16) * 512;
> > 	} while (!last_image);
> > 
> > But I'm not sure it's worth it.  I'm inclined to just map the whole
> > thing.
> > 
> > -- 
> > "Next the statesmen will invent cheap lies, putting the blame upon 
> > the nation that is attacked, and every man will be glad of those
> > conscience-soothing falsities, and will diligently study them, and
> > refuse
> > to examine any refutations of them; and thus he will by and by
> > convince 
> > himself that the war is just, and will thank God for the better sleep
> > 
> > he enjoys after this process of grotesque self-deception." -- Mark
> > Twain
> > 
> 
> =====
> Jon Smirl
> jonsmirl@yahoo.com
> 
> 
> 		
> __________________________________
> Do you Yahoo!?
> Yahoo! Mail is new and improved - Check it out!
> http://promotions.yahoo.com/new_mail
> 

-- 
"Next the statesmen will invent cheap lies, putting the blame upon 
the nation that is attacked, and every man will be glad of those
conscience-soothing falsities, and will diligently study them, and refuse
to examine any refutations of them; and thus he will by and by convince 
himself that the war is just, and will thank God for the better sleep 
he enjoys after this process of grotesque self-deception." -- Mark Twain

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-26 13:13                                 ` Matthew Wilcox
@ 2004-08-26 15:40                                   ` Jon Smirl
  2004-08-26 15:58                                     ` Matthew Wilcox
  0 siblings, 1 reply; 110+ messages in thread
From: Jon Smirl @ 2004-08-26 15:40 UTC (permalink / raw)
  To: Matthew Wilcox
  Cc: Greg KH, Jesse Barnes, Martin Mares, Pallipadi, Venkatesh,
	linux-pci, Alan Cox, Linux Kernel Mailing List, Petr Vandrovec,
	Benjamin Herrenschmidt

I was computing the length because of the need to copy for the
partially decoded case. I have one card that maps a 40KB ROM with a
512KB window. In that case it makes a 450KB different in the copy size.

The copy case is adding a lot of complexity to the code. We could just
give it the boot and require that partially decoded drivers just turn
the ROM attribute off.

If not, how do I get the size from your algorithm?

--- Matthew Wilcox <willy@debian.org> wrote:

> On Wed, Aug 25, 2004 at 01:06:38PM -0700, Jon Smirl wrote:
> > New version attached fixes this and the function documentation.
> 
> Sorry, I found two more bugs ;-(
> 
> > +unsigned char *
> > +pci_map_rom(struct pci_dev *pdev, size_t *size)
> > +{
> [...]
> > +	/* Standard PCI ROMs start out with these three bytes 55 AA
> size/512 */
> > +	if ((*rom == 0x55) && (*(rom + 1) == 0xAA))
> > +		*size = *(rom + 2) * 512;	/* return true ROM size, not PCI
> window size */
> 
> First, you're dereferencing an ioremapped pointer directly instead of
> using readb() et al.  This is a portability no-no but happens to work
> on x86.
> 
> Second, only images of code type 0 (Intel x86, PC-AT compatible) are
> specified to have length as the third byte.  OpenFirmware, PA-RISC
> and
> EFI may or may not have size as the third byte.  It's also possible
> that there are multiple images in the ROM.  So what you have to do to
> be correct is something like ...
> 
> 	int last_image;
> 	char *image = rom;
> 	do {
> 		char *pds;
> 		if (readb(image) != 0x55)
> 			break;
> 		if (readb(image + 1) != 0xAA)
> 			break;
> 		pds = image + readw(image + 16);
> 		if (readb(pds) != 'P')
> 			break;
> 		if (readb(pds + 1) != 'C')
> 			break;
> 		if (readb(pds + 2) != 'I')
> 			break;
> 		if (readb(pds + 3) != 'R')
> 			break;
> 		last_image = readb(pds + 21) & 0x80;
> 		image += readw(pds + 16) * 512;
> 	} while (!last_image);
> 
> But I'm not sure it's worth it.  I'm inclined to just map the whole
> thing.
> 
> -- 
> "Next the statesmen will invent cheap lies, putting the blame upon 
> the nation that is attacked, and every man will be glad of those
> conscience-soothing falsities, and will diligently study them, and
> refuse
> to examine any refutations of them; and thus he will by and by
> convince 
> himself that the war is just, and will thank God for the better sleep
> 
> he enjoys after this process of grotesque self-deception." -- Mark
> Twain
> 

=====
Jon Smirl
jonsmirl@yahoo.com


		
__________________________________
Do you Yahoo!?
Yahoo! Mail is new and improved - Check it out!
http://promotions.yahoo.com/new_mail

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-25 20:06                               ` Jon Smirl
@ 2004-08-26 13:13                                 ` Matthew Wilcox
  2004-08-26 15:40                                   ` Jon Smirl
  0 siblings, 1 reply; 110+ messages in thread
From: Matthew Wilcox @ 2004-08-26 13:13 UTC (permalink / raw)
  To: Jon Smirl
  Cc: Greg KH, Jesse Barnes, Martin Mares, Pallipadi, Venkatesh,
	linux-pci, Alan Cox, Linux Kernel Mailing List, Petr Vandrovec,
	Benjamin Herrenschmidt

On Wed, Aug 25, 2004 at 01:06:38PM -0700, Jon Smirl wrote:
> New version attached fixes this and the function documentation.

Sorry, I found two more bugs ;-(

> +unsigned char *
> +pci_map_rom(struct pci_dev *pdev, size_t *size)
> +{
[...]
> +	/* Standard PCI ROMs start out with these three bytes 55 AA size/512 */
> +	if ((*rom == 0x55) && (*(rom + 1) == 0xAA))
> +		*size = *(rom + 2) * 512;	/* return true ROM size, not PCI window size */

First, you're dereferencing an ioremapped pointer directly instead of
using readb() et al.  This is a portability no-no but happens to work
on x86.

Second, only images of code type 0 (Intel x86, PC-AT compatible) are
specified to have length as the third byte.  OpenFirmware, PA-RISC and
EFI may or may not have size as the third byte.  It's also possible
that there are multiple images in the ROM.  So what you have to do to
be correct is something like ...

	int last_image;
	char *image = rom;
	do {
		char *pds;
		if (readb(image) != 0x55)
			break;
		if (readb(image + 1) != 0xAA)
			break;
		pds = image + readw(image + 16);
		if (readb(pds) != 'P')
			break;
		if (readb(pds + 1) != 'C')
			break;
		if (readb(pds + 2) != 'I')
			break;
		if (readb(pds + 3) != 'R')
			break;
		last_image = readb(pds + 21) & 0x80;
		image += readw(pds + 16) * 512;
	} while (!last_image);

But I'm not sure it's worth it.  I'm inclined to just map the whole thing.

-- 
"Next the statesmen will invent cheap lies, putting the blame upon 
the nation that is attacked, and every man will be glad of those
conscience-soothing falsities, and will diligently study them, and refuse
to examine any refutations of them; and thus he will by and by convince 
himself that the war is just, and will thank God for the better sleep 
he enjoys after this process of grotesque self-deception." -- Mark Twain

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-25 18:55                             ` Greg KH
@ 2004-08-25 20:06                               ` Jon Smirl
  2004-08-26 13:13                                 ` Matthew Wilcox
  0 siblings, 1 reply; 110+ messages in thread
From: Jon Smirl @ 2004-08-25 20:06 UTC (permalink / raw)
  To: Greg KH
  Cc: Jesse Barnes, Martin Mares, Pallipadi, Venkatesh, linux-pci,
	Alan Cox, Linux Kernel Mailing List, Petr Vandrovec,
	Benjamin Herrenschmidt

[-- Attachment #1: Type: text/plain, Size: 1170 bytes --]

PCI sysfs and proc were being initialized two different ways. sysfs was
being initialized very early and I couldn't call most pci_xxx calls
from the sysfs code. On the other hand proc was being initialized much
later. I moved pci sysfs initialization to match proc initialization.
Later sysfs initialization lets me assign the ROM an address if it
needs one.

pci_bus_add_devices is marked __devinit not __init so I see that it
wasn't safe to remove the proc_initialized code for the hotplug case.
I'll put that code back in but I needed to build a parallel flag for
sysfs_initialized.

New version attached fixes this and the function documentation.

--- Greg KH <greg@kroah.com> wrote:
> But if you remove those calls, any pci device added later on in a pci
> hotplug (or in a pcmcia) system, would not have their sysfs files, or
> proc files created, right?
> 
> That doesn't seem like a good change, and is unnecessary for this rom
> sysfs feature, right?
> 
> thanks,
> 
> greg k-h
> 

=====
Jon Smirl
jonsmirl@yahoo.com


		
_______________________________
Do you Yahoo!?
Win 1 of 4,000 free domain names from Yahoo! Enter now.
http://promotions.yahoo.com/goldrush

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: pci-sysfs-rom-18-mm.patch --]
[-- Type: text/x-patch; name="pci-sysfs-rom-18-mm.patch", Size: 14574 bytes --]

Exposes PCI ROMs via sysfs. Four new routine for drivers to use when 
accessing ROMs: pci_map_rom, pci_map_rom_copy, pci_unmap_rom, pci_remove_rom. 
Handles shadow ROMs for laptops that compress actual ROMs.

Signed-off-by: "Jon Smirl" <jonsmirl@yahoo.com>
===== arch/i386/pci/fixup.c 1.20 vs edited =====
--- 1.20/arch/i386/pci/fixup.c	Mon Aug 23 23:50:21 2004
+++ edited/arch/i386/pci/fixup.c	Wed Aug 25 15:34:33 2004
@@ -255,3 +255,41 @@
 }
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2, pci_fixup_nforce2);
 
+/*
+ * Fixup to mark boot BIOS video selected by BIOS before it changes
+ *
+ * From information provided by "Jon Smirl" <jonsmirl@yahoo.com>
+ *
+ * The standard boot ROM sequence for an x86 machine uses the BIOS
+ * to select an initial video card for boot display. This boot video 
+ * card will have it's BIOS copied to C0000 in system RAM. 
+ * IORESOURCE_ROM_SHADOW is used to associate the boot video
+ * card with this copy. On laptops this copy has to be used since
+ * the main ROM may be compressed or combined with another image.
+ * See pci_map_rom() for use of this flag. IORESOURCE_ROM_SHADOW
+ * is marked here since the boot video device will be the only enabled
+ * video device at this point.
+ *
+ */static void __devinit pci_fixup_video(struct pci_dev *pdev)
+{
+	struct pci_dev *bridge;
+	struct pci_bus *bus;
+	u16 l;
+
+	if ((pdev->class >> 8) != PCI_CLASS_DISPLAY_VGA)
+		return;
+
+	/* Is VGA routed to us? */
+	bus = pdev->bus;
+	while (bus) {
+		bridge = bus->self;
+		if (bridge) {
+			pci_read_config_word(bridge, PCI_BRIDGE_CONTROL, &l);
+			if (!(l & PCI_BRIDGE_CTL_VGA))
+				return;
+		}
+		bus = bus->parent;
+	}
+	pdev->resource[PCI_ROM_RESOURCE].flags |= IORESOURCE_ROM_SHADOW;
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, pci_fixup_video);
===== drivers/pci/pci-sysfs.c 1.10 vs edited =====
--- 1.10/drivers/pci/pci-sysfs.c	Fri Jun  4 09:23:04 2004
+++ edited/drivers/pci/pci-sysfs.c	Wed Aug 25 15:43:49 2004
@@ -5,6 +5,8 @@
  * (C) Copyright 2002-2004 IBM Corp.
  * (C) Copyright 2003 Matthew Wilcox
  * (C) Copyright 2003 Hewlett-Packard
+ * (C) Copyright 2004 Jon Smirl <jonsmirl@yahoo.com>
+ * (C) Copyright 2004 Silicon Graphics, Inc. Jesse Barnes <jbarnes@sgi.com>
  *
  * File attributes for PCI devices
  *
@@ -20,6 +22,8 @@
 
 #include "pci.h"
 
+static int sysfs_initialized;	/* = 0 */
+
 /* show configuration fields */
 #define pci_config_attr(field, format_string)				\
 static ssize_t								\
@@ -164,6 +168,221 @@
 	return count;
 }
 
+/**
+ * pci_enable_rom - enable ROM decoding for a PCI device
+ * @dev: PCI device to enable
+ *
+ * Enable ROM decoding on @dev.  This involves simply turning on the last
+ * bit of the PCI ROM BAR.  Note that some cards may share address decoders
+ * between the ROM and other resources, so enabling it may disable access
+ * to MMIO registers or other card memory.
+ */
+static void
+pci_enable_rom(struct pci_dev *pdev)
+{
+	u32 rom_addr;
+	
+	pci_read_config_dword(pdev, pdev->rom_base_reg, &rom_addr);
+	rom_addr |= PCI_ROM_ADDRESS_ENABLE;
+	pci_write_config_dword(pdev, pdev->rom_base_reg, rom_addr);
+}
+
+/**
+ * pci_disable_rom - disable ROM decoding for a PCI device
+ * @dev: PCI device to disable
+ *
+ * Disable ROM decoding on a PCI device by turning off the last bit in the
+ * ROM BAR.
+ */
+static void
+pci_disable_rom(struct pci_dev *pdev)
+{
+	u32 rom_addr;
+	pci_read_config_dword(pdev, pdev->rom_base_reg, &rom_addr);
+	rom_addr &= ~PCI_ROM_ADDRESS_ENABLE;
+	pci_write_config_dword(pdev, pdev->rom_base_reg, rom_addr);
+}
+
+/**
+ * pci_map_rom - map a PCI ROM to kernel space
+ * @dev: pointer to pci device struct
+ * @size: pointer to receive size of pci window over ROM
+ * @return: kernel virtual pointer to image of ROM
+ *
+ * Map a PCI ROM into kernel space. If ROM is boot video ROM,
+ * the shadow BIOS copy will be returned instead of the 
+ * actual ROM.
+ */
+unsigned char *
+pci_map_rom(struct pci_dev *pdev, size_t *size)
+{
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+	loff_t start;
+	unsigned char *rom;
+	
+	if (res->flags & IORESOURCE_ROM_SHADOW) {	/* IORESOURCE_ROM_SHADOW only set on x86 */
+		start = (loff_t)0xC0000; 	/* primary video rom always starts here */
+		*size = 0x20000;		/* cover C000:0 through E000:0 */
+	} else {
+		if (res->flags & IORESOURCE_ROM_COPY) {
+			*size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
+			return (unsigned char *)pci_resource_start(pdev, PCI_ROM_RESOURCE);
+		} else {
+			/* assign the ROM an address if it doesn't have one */
+			if (res->parent == NULL)
+				pci_assign_resource(pdev, PCI_ROM_RESOURCE);
+	
+			start = pci_resource_start(pdev, PCI_ROM_RESOURCE);
+			*size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
+			if (*size == 0)
+				return NULL;
+			
+			/* Enable ROM space decodes */
+			pci_enable_rom(pdev);
+		}
+	}
+	
+	rom = ioremap(start, *size);
+	if (!rom) {
+		/* restore enable if ioremap fails */
+		if (!(res->flags & (IORESOURCE_ROM_ENABLE | IORESOURCE_ROM_SHADOW | IORESOURCE_ROM_COPY)))
+			pci_disable_rom(pdev);
+		return NULL;
+	}		
+	/* Standard PCI ROMs start out with these three bytes 55 AA size/512 */
+	if ((*rom == 0x55) && (*(rom + 1) == 0xAA))
+		*size = *(rom + 2) * 512;	/* return true ROM size, not PCI window size */
+		
+	return rom;
+}
+
+/**
+ * pci_map_rom_copy - map a PCI ROM to kernel space, create a copy
+ * @dev: pointer to pci device struct
+ * @size: pointer to receive size of pci window over ROM
+ * @return: kernel virtual pointer to image of ROM
+ *
+ * Map a PCI ROM into kernel space. If ROM is boot video ROM,
+ * the shadow BIOS copy will be returned instead of the 
+ * actual ROM.
+ */
+unsigned char *
+pci_map_rom_copy(struct pci_dev *pdev, size_t *size)
+{
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+	unsigned char *rom;
+	
+	rom = pci_map_rom(pdev, size);
+	if (!rom)
+		return NULL;
+		
+	if (res->flags & (IORESOURCE_ROM_COPY | IORESOURCE_ROM_SHADOW))
+		return rom;
+		
+	res->start = (unsigned long)kmalloc(*size, GFP_KERNEL);
+	if (!res->start) 
+		return rom;
+
+	res->end = res->start + *size; 
+	memcpy((void*)res->start, rom, *size);
+	pci_unmap_rom(pdev, rom);
+	res->flags |= IORESOURCE_ROM_COPY;
+	
+	return (unsigned char *)res->start;
+}
+
+/**
+ * pci_unmap_rom - unmap the ROM from kernel space
+ * @dev: pointer to pci device struct
+ * @rom: virtual address of the previous mapping
+ *
+ * Remove a mapping of a previously mapped ROM
+ */
+void 
+pci_unmap_rom(struct pci_dev *pdev, unsigned char *rom)
+{
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+
+	if (res->flags & IORESOURCE_ROM_COPY)
+		return;
+		
+	iounmap(rom);
+		
+	/* Disable again before continuing, leave enabled if pci=rom */
+	if (!(res->flags & (IORESOURCE_ROM_ENABLE | IORESOURCE_ROM_SHADOW)))
+		pci_disable_rom(pdev);
+}
+
+static struct bin_attribute rom_attr;
+
+/**
+ * pci_remove_rom - disable the ROM and remove its sysfs attribute
+ * @dev: pointer to pci device struct
+ *
+ */
+void 
+pci_remove_rom(struct pci_dev *pdev) 
+{
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+	
+	if (pci_resource_len(pdev, PCI_ROM_RESOURCE))
+		sysfs_remove_bin_file(&pdev->dev.kobj, &rom_attr);
+	if (!(res->flags & (IORESOURCE_ROM_ENABLE | IORESOURCE_ROM_SHADOW | IORESOURCE_ROM_COPY)))
+		pci_disable_rom(pdev);
+}
+
+/**
+ * pci_cleanup_rom - internal routine for freeing the ROM copy created 
+ * by pci_map_rom_copy called from remove.c
+ * @dev: pointer to pci device struct
+ *
+ */
+void 
+pci_cleanup_rom(struct pci_dev *pdev) 
+{
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+	if (res->flags & IORESOURCE_ROM_COPY) {
+		kfree((void*)res->start);
+		res->flags &= ~IORESOURCE_ROM_COPY;
+		res->start = 0;
+		res->end = 0;
+	}
+}
+
+/**
+ * pci_read_rom - read a PCI ROM
+ * @kobj: kernel object handle
+ * @buf: where to put the data we read from the ROM
+ * @off: file offset
+ * @count: number of bytes to read
+ *
+ * Put @count bytes starting at @off into @buf from the ROM in the PCI
+ * device corresponding to @kobj.
+ */
+static ssize_t
+pci_read_rom(struct kobject *kobj, char *buf, loff_t off, size_t count)
+{
+	struct pci_dev *pdev = to_pci_dev(container_of(kobj, struct device, kobj));
+	unsigned char *rom;
+	size_t size;
+	
+	rom = pci_map_rom(pdev, &size);	/* size starts out as PCI window size */
+	if (!rom)
+		return 0;
+		
+	if (off >= size)
+		count = 0;
+	else {
+		if (off + count > size)
+			count = size - off;
+		
+		memcpy_fromio(buf, rom + off, count);
+	}
+	pci_unmap_rom(pdev, rom);
+		
+	return count;
+}
+
 static struct bin_attribute pci_config_attr = {
 	.attr =	{
 		.name = "config",
@@ -188,11 +407,68 @@
 
 void pci_create_sysfs_dev_files (struct pci_dev *pdev)
 {
+	if (!sysfs_initialized)
+		return -EACCES;
+
 	if (pdev->cfg_size < 4096)
 		sysfs_create_bin_file(&pdev->dev.kobj, &pci_config_attr);
 	else
 		sysfs_create_bin_file(&pdev->dev.kobj, &pcie_config_attr);
 
+	/* If the device has a ROM, try to expose it in sysfs. */
+	if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) {
+		struct bin_attribute *rom_attr;
+		
+		rom_attr = kmalloc(sizeof(*rom_attr), GFP_ATOMIC);
+		if (rom_attr) {
+			pdev->rom_attr = rom_attr;
+			rom_attr->size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
+			rom_attr->attr.name = "rom";
+			rom_attr->attr.mode = S_IRUSR;
+			rom_attr->attr.owner = THIS_MODULE;
+			rom_attr->read = pci_read_rom;
+			sysfs_create_bin_file(&pdev->dev.kobj, rom_attr);
+		}
+	}
 	/* add platform-specific attributes */
 	pcibios_add_platform_entries(pdev);
 }
+
+/**
+ * pci_remove_sysfs_dev_files - cleanup PCI specific sysfs files
+ * @pdev: device whose entries we should free
+ *
+ * Cleanup when @pdev is removed from sysfs.
+ */
+void pci_remove_sysfs_dev_files(struct pci_dev *pdev)
+{
+	if (pdev->cfg_size < 4096)
+		sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr);
+	else
+		sysfs_remove_bin_file(&pdev->dev.kobj, &pcie_config_attr);
+
+	if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) {
+		if (pdev->rom_attr) {
+			sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr);
+			kfree(pdev->rom_attr);
+		}
+	}
+}
+
+static int __init pci_sysfs_init(void)
+{
+	struct pci_dev *pdev = NULL;
+	
+	sysfs_initialized = 1;
+	while ((pdev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pdev)) != NULL)
+		pci_create_sysfs_dev_files(pdev);
+
+	return 0;
+}
+
+__initcall(pci_sysfs_init);
+
+EXPORT_SYMBOL(pci_map_rom);
+EXPORT_SYMBOL(pci_map_rom_copy);
+EXPORT_SYMBOL(pci_unmap_rom);
+EXPORT_SYMBOL(pci_remove_rom);
===== drivers/pci/pci.h 1.12 vs edited =====
--- 1.12/drivers/pci/pci.h	Fri Jun  4 09:23:04 2004
+++ edited/drivers/pci/pci.h	Wed Aug 25 15:36:32 2004
@@ -3,6 +3,8 @@
 extern int pci_hotplug (struct device *dev, char **envp, int num_envp,
 			 char *buffer, int buffer_size);
 extern void pci_create_sysfs_dev_files(struct pci_dev *pdev);
+extern void pci_remove_sysfs_dev_files(struct pci_dev *pdev);
+extern void pci_cleanup_rom(struct pci_dev *dev);
 extern int pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
 				  unsigned long size, unsigned long align,
 				  unsigned long min, unsigned int type_mask,
===== drivers/pci/probe.c 1.66 vs edited =====
--- 1.66/drivers/pci/probe.c	Mon Aug 23 23:50:45 2004
+++ edited/drivers/pci/probe.c	Wed Aug 25 15:34:40 2004
@@ -170,7 +170,7 @@
 		if (sz && sz != 0xffffffff) {
 			sz = pci_size(l, sz, PCI_ROM_ADDRESS_MASK);
 			if (sz) {
-				res->flags = (l & PCI_ROM_ADDRESS_ENABLE) |
+				res->flags = (l & IORESOURCE_ROM_ENABLE) |
 				  IORESOURCE_MEM | IORESOURCE_PREFETCH |
 				  IORESOURCE_READONLY | IORESOURCE_CACHEABLE;
 				res->start = l & PCI_ROM_ADDRESS_MASK;
===== drivers/pci/remove.c 1.3 vs edited =====
--- 1.3/drivers/pci/remove.c	Tue Feb  3 12:17:30 2004
+++ edited/drivers/pci/remove.c	Wed Aug 25 15:34:41 2004
@@ -16,6 +16,7 @@
 
  	msi_remove_pci_irq_vectors(dev);
 
+	pci_cleanup_rom(dev);
 	for (i = 0; i < PCI_NUM_RESOURCES; i++) {
 		struct resource *res = dev->resource + i;
 		if (res->parent)
@@ -26,6 +27,7 @@
 static void pci_destroy_dev(struct pci_dev *dev)
 {
 	pci_proc_detach_device(dev);
+	pci_remove_sysfs_dev_files(dev);
 	device_unregister(&dev->dev);
 
 	/* Remove the device from the device lists, and prevent any further
===== drivers/pci/setup-res.c 1.25 vs edited =====
--- 1.25/drivers/pci/setup-res.c	Thu Mar 18 18:40:56 2004
+++ edited/drivers/pci/setup-res.c	Wed Aug 25 15:34:42 2004
@@ -56,7 +56,7 @@
 	if (resno < 6) {
 		reg = PCI_BASE_ADDRESS_0 + 4 * resno;
 	} else if (resno == PCI_ROM_RESOURCE) {
-		new |= res->flags & PCI_ROM_ADDRESS_ENABLE;
+		new |= res->flags & IORESOURCE_ROM_ENABLE;
 		reg = dev->rom_base_reg;
 	} else {
 		/* Hmm, non-standard resource. */
===== include/linux/ioport.h 1.14 vs edited =====
--- 1.14/include/linux/ioport.h	Thu Dec 18 00:48:57 2003
+++ edited/include/linux/ioport.h	Wed Aug 25 15:34:42 2004
@@ -82,6 +82,11 @@
 #define IORESOURCE_MEM_SHADOWABLE	(1<<5)	/* dup: IORESOURCE_SHADOWABLE */
 #define IORESOURCE_MEM_EXPANSIONROM	(1<<6)
 
+/* PCI ROM control bits (IORESOURCE_BITS) */
+#define IORESOURCE_ROM_ENABLE		(1<<0)	/* ROM is enabled, same as PCI_ROM_ADDRESS_ENABLE */
+#define IORESOURCE_ROM_SHADOW		(1<<1)	/* ROM is copy at C000:0 */
+#define IORESOURCE_ROM_COPY		(1<<2)	/* ROM is alloc'd copy, resource field overlaid */
+
 /* PC/ISA/whatever - the normal PC address spaces: IO and memory */
 extern struct resource ioport_resource;
 extern struct resource iomem_resource;
===== include/linux/pci.h 1.133 vs edited =====
--- 1.133/include/linux/pci.h	Mon Aug 23 23:51:13 2004
+++ edited/include/linux/pci.h	Wed Aug 25 15:34:42 2004
@@ -537,6 +537,7 @@
 	unsigned int	is_busmaster:1; /* device is busmaster */
 	
 	u32		saved_config_space[16]; /* config space saved at suspend time */
+	struct bin_attribute *rom_attr; /* attribute descriptor for sysfs ROM entry */
 #ifdef CONFIG_PCI_NAMES
 #define PCI_NAME_SIZE	96
 #define PCI_NAME_HALF	__stringify(43)	/* less than half to handle slop */
@@ -777,6 +778,12 @@
 int pci_dac_set_dma_mask(struct pci_dev *dev, u64 mask);
 int pci_set_consistent_dma_mask(struct pci_dev *dev, u64 mask);
 int pci_assign_resource(struct pci_dev *dev, int i);
+
+/* ROM control related routines */
+unsigned char *pci_map_rom(struct pci_dev *pdev, size_t *size);
+unsigned char *pci_map_rom_copy(struct pci_dev *pdev, size_t *size);
+void pci_unmap_rom(struct pci_dev *pdev, unsigned char *rom);
+void pci_remove_rom(struct pci_dev *pdev);
 
 /* Power management related routines */
 int pci_save_state(struct pci_dev *dev, u32 *buffer);

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-25 18:45                           ` Jon Smirl
@ 2004-08-25 18:55                             ` Greg KH
  2004-08-25 20:06                               ` Jon Smirl
  0 siblings, 1 reply; 110+ messages in thread
From: Greg KH @ 2004-08-25 18:55 UTC (permalink / raw)
  To: Jon Smirl
  Cc: Jesse Barnes, Martin Mares, Pallipadi, Venkatesh, linux-pci,
	Alan Cox, Linux Kernel Mailing List, Petr Vandrovec,
	Benjamin Herrenschmidt

On Wed, Aug 25, 2004 at 11:45:02AM -0700, Jon Smirl wrote:
> it is related to this change drivers/pci/bus.c.
> -               pci_proc_attach_device(dev);
> -               pci_create_sysfs_dev_files(dev);
> 
> The pci subsystem was initializing proc and sysfs too early from bus.c.
> Both of these calls always failed and the proc_initialized flag was
> used to return the failure. These calls were never succeeding, they
> were always getting error returns.
> 
> pci_proc_init and pci_sysfs_init run later at _initcall() time and
> build proc/sys so these routines masked the initial failure in bus.c.
> 
> If you remove the calls in bus.s there is no need for the
> proc_initialized flag.

But if you remove those calls, any pci device added later on in a pci
hotplug (or in a pcmcia) system, would not have their sysfs files, or
proc files created, right?

That doesn't seem like a good change, and is unnecessary for this rom
sysfs feature, right?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-25 18:19                         ` Greg KH
@ 2004-08-25 18:45                           ` Jon Smirl
  2004-08-25 18:55                             ` Greg KH
  0 siblings, 1 reply; 110+ messages in thread
From: Jon Smirl @ 2004-08-25 18:45 UTC (permalink / raw)
  To: Greg KH
  Cc: Jesse Barnes, Martin Mares, Pallipadi, Venkatesh, linux-pci,
	Alan Cox, Linux Kernel Mailing List, Petr Vandrovec,
	Benjamin Herrenschmidt

it is related to this change drivers/pci/bus.c.
-               pci_proc_attach_device(dev);
-               pci_create_sysfs_dev_files(dev);

The pci subsystem was initializing proc and sysfs too early from bus.c.
Both of these calls always failed and the proc_initialized flag was
used to return the failure. These calls were never succeeding, they
were always getting error returns.

pci_proc_init and pci_sysfs_init run later at _initcall() time and
build proc/sys so these routines masked the initial failure in bus.c.

If you remove the calls in bus.s there is no need for the
proc_initialized flag.

--- Greg KH <greg@kroah.com> wrote:

> On Wed, Aug 25, 2004 at 11:06:06AM -0700, Jon Smirl wrote:
> > Final version, I hope, includes short decription and Signed-off-by
> at
> > top of patch.
> 
> Hm, one comment.  I must have missed something in all of the
> different
> versions of this patch, but why are you changing this code:
> 
> > diff -Nru a/drivers/pci/proc.c b/drivers/pci/proc.c
> > --- a/drivers/pci/proc.c	Wed Aug 25 13:56:18 2004
> > +++ b/drivers/pci/proc.c	Wed Aug 25 13:56:18 2004
> > @@ -16,7 +16,6 @@
> >  #include <asm/uaccess.h>
> >  #include <asm/byteorder.h>
> >  
> > -static int proc_initialized;	/* = 0 */
> >  
> >  static loff_t
> >  proc_bus_pci_lseek(struct file *file, loff_t off, int whence)
> > @@ -387,9 +386,6 @@
> >  	struct proc_dir_entry *de, *e;
> >  	char name[16];
> >  
> > -	if (!proc_initialized)
> > -		return -EACCES;
> > -
> >  	if (!(de = bus->procdir)) {
> >  		if (pci_name_bus(name, bus))
> >  			return -EEXIST;
> > @@ -425,9 +421,6 @@
> >  {
> >  	struct proc_dir_entry *de = bus->procdir;
> >  
> > -	if (!proc_initialized)
> > -		return -EACCES;
> > -
> >  	if (!de) {
> >  		char name[16];
> >  		sprintf(name, "%02x", bus->number);
> > @@ -583,6 +576,7 @@
> >  {
> >  	return seq_open(file, &proc_bus_pci_devices_op);
> >  }
> > +
> >  static struct file_operations proc_bus_pci_dev_operations = {
> >  	.open		= proc_bus_pci_dev_open,
> >  	.read		= seq_read,
> > @@ -593,16 +587,20 @@
> >  static int __init pci_proc_init(void)
> >  {
> >  	struct proc_dir_entry *entry;
> > -	struct pci_dev *dev = NULL;
> > +	struct pci_dev *pdev = NULL;
> > +
> >  	proc_bus_pci_dir = proc_mkdir("pci", proc_bus);
> > +
> >  	entry = create_proc_entry("devices", 0, proc_bus_pci_dir);
> >  	if (entry)
> >  		entry->proc_fops = &proc_bus_pci_dev_operations;
> > -	proc_initialized = 1;
> > -	while ((dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev)) !=
> NULL) {
> > -		pci_proc_attach_device(dev);
> > +
> > +	while ((pdev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pdev)) !=
> NULL) {
> > +		pci_proc_attach_device(pdev);
> >  	}
> > +
> >  	legacy_proc_init();
> > +
> >  	return 0;
> >  }
> 
> I see some gratitous whitespace changes, and the removal of the
> proc_initialized flag.  Why do we need to get rid of that flag?
> 
> thanks,
> 
> greg k-h
> 


=====
Jon Smirl
jonsmirl@yahoo.com


		
_______________________________
Do you Yahoo!?
Win 1 of 4,000 free domain names from Yahoo! Enter now.
http://promotions.yahoo.com/goldrush

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-25 18:06                       ` Jon Smirl
  2004-08-25 18:19                         ` Greg KH
@ 2004-08-25 18:29                         ` Matthew Wilcox
  1 sibling, 0 replies; 110+ messages in thread
From: Matthew Wilcox @ 2004-08-25 18:29 UTC (permalink / raw)
  To: Jon Smirl
  Cc: Greg KH, Jesse Barnes, Martin Mares, Pallipadi, Venkatesh,
	linux-pci, Alan Cox, Linux Kernel Mailing List, Petr Vandrovec,
	Benjamin Herrenschmidt

On Wed, Aug 25, 2004 at 11:06:06AM -0700, Jon Smirl wrote:
> Final version, I hope, includes short decription and Signed-off-by at
> top of patch.

+/**
+ * pci_remove_rom - disable the ROM and remove it's sysfs attribute
+ * @dev: pointer to pci device struct
+ * 
+ */

It's its, not it's, unless of course you mean it is, in which case it's
it's not its.

More helpfully, "its" is a pronoun like "his" or "hers".  "it's" is an
abbreviation for "it is".

Also, this is a mistake:

+/**
+ * pci_remove_rom - disable the ROM and remove it's sysfs attribute
+ * @dev: pointer to pci device struct
+ *
+ */
+void 
+pci_cleanup_rom(struct pci_dev *pdev) 

I'd like a little bit of description about the difference between
pci_cleanup_rom and pci_remove_rom in the docbook, please.

-- 
"Next the statesmen will invent cheap lies, putting the blame upon 
the nation that is attacked, and every man will be glad of those
conscience-soothing falsities, and will diligently study them, and refuse
to examine any refutations of them; and thus he will by and by convince 
himself that the war is just, and will thank God for the better sleep 
he enjoys after this process of grotesque self-deception." -- Mark Twain

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-25 18:06                       ` Jon Smirl
@ 2004-08-25 18:19                         ` Greg KH
  2004-08-25 18:45                           ` Jon Smirl
  2004-08-25 18:29                         ` Matthew Wilcox
  1 sibling, 1 reply; 110+ messages in thread
From: Greg KH @ 2004-08-25 18:19 UTC (permalink / raw)
  To: Jon Smirl
  Cc: Jesse Barnes, Martin Mares, Pallipadi, Venkatesh, linux-pci,
	Alan Cox, Linux Kernel Mailing List, Petr Vandrovec,
	Benjamin Herrenschmidt

On Wed, Aug 25, 2004 at 11:06:06AM -0700, Jon Smirl wrote:
> Final version, I hope, includes short decription and Signed-off-by at
> top of patch.

Hm, one comment.  I must have missed something in all of the different
versions of this patch, but why are you changing this code:

> diff -Nru a/drivers/pci/proc.c b/drivers/pci/proc.c
> --- a/drivers/pci/proc.c	Wed Aug 25 13:56:18 2004
> +++ b/drivers/pci/proc.c	Wed Aug 25 13:56:18 2004
> @@ -16,7 +16,6 @@
>  #include <asm/uaccess.h>
>  #include <asm/byteorder.h>
>  
> -static int proc_initialized;	/* = 0 */
>  
>  static loff_t
>  proc_bus_pci_lseek(struct file *file, loff_t off, int whence)
> @@ -387,9 +386,6 @@
>  	struct proc_dir_entry *de, *e;
>  	char name[16];
>  
> -	if (!proc_initialized)
> -		return -EACCES;
> -
>  	if (!(de = bus->procdir)) {
>  		if (pci_name_bus(name, bus))
>  			return -EEXIST;
> @@ -425,9 +421,6 @@
>  {
>  	struct proc_dir_entry *de = bus->procdir;
>  
> -	if (!proc_initialized)
> -		return -EACCES;
> -
>  	if (!de) {
>  		char name[16];
>  		sprintf(name, "%02x", bus->number);
> @@ -583,6 +576,7 @@
>  {
>  	return seq_open(file, &proc_bus_pci_devices_op);
>  }
> +
>  static struct file_operations proc_bus_pci_dev_operations = {
>  	.open		= proc_bus_pci_dev_open,
>  	.read		= seq_read,
> @@ -593,16 +587,20 @@
>  static int __init pci_proc_init(void)
>  {
>  	struct proc_dir_entry *entry;
> -	struct pci_dev *dev = NULL;
> +	struct pci_dev *pdev = NULL;
> +
>  	proc_bus_pci_dir = proc_mkdir("pci", proc_bus);
> +
>  	entry = create_proc_entry("devices", 0, proc_bus_pci_dir);
>  	if (entry)
>  		entry->proc_fops = &proc_bus_pci_dev_operations;
> -	proc_initialized = 1;
> -	while ((dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
> -		pci_proc_attach_device(dev);
> +
> +	while ((pdev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pdev)) != NULL) {
> +		pci_proc_attach_device(pdev);
>  	}
> +
>  	legacy_proc_init();
> +
>  	return 0;
>  }

I see some gratitous whitespace changes, and the removal of the
proc_initialized flag.  Why do we need to get rid of that flag?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-25 17:42                     ` Greg KH
@ 2004-08-25 18:06                       ` Jon Smirl
  2004-08-25 18:19                         ` Greg KH
  2004-08-25 18:29                         ` Matthew Wilcox
  0 siblings, 2 replies; 110+ messages in thread
From: Jon Smirl @ 2004-08-25 18:06 UTC (permalink / raw)
  To: Greg KH
  Cc: Jesse Barnes, Martin Mares, Pallipadi, Venkatesh, linux-pci,
	Alan Cox, Linux Kernel Mailing List, Petr Vandrovec,
	Benjamin Herrenschmidt

[-- Attachment #1: Type: text/plain, Size: 814 bytes --]

Final version, I hope, includes short decription and Signed-off-by at
top of patch.

--- Greg KH <greg@kroah.com> wrote:

> On Wed, Aug 25, 2004 at 10:32:41AM -0700, Jon Smirl wrote:
> > No functional changes from previous version. I fixed the braces and
> > diff'd it against 2.6.8.1-mm4. 
> > 
> > Where does "Signed-off-by:" go in a patch? Jesse and I added a
> > copyright  statement.
> 
> Please read Documentation/SubmittingPatches for details on this.  It
> should just go into the description of what the patch does, that I
> need
> in order to provide a good changelog entry for the patch.
> 
> thanks,
> 
> greg k-h
> 

=====
Jon Smirl
jonsmirl@yahoo.com


		
__________________________________
Do you Yahoo!?
Yahoo! Mail Address AutoComplete - You start. We finish.
http://promotions.yahoo.com/new_mail 

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: pci-sysfs-rom-17-mm.patch --]
[-- Type: text/x-patch; name="pci-sysfs-rom-17-mm.patch", Size: 16133 bytes --]

Exposes PCI ROMs via sysfs. Four new routine for drivers to use when 
accessing ROMs: pci_map_rom, pci_map_rom_copy, pci_unmap_rom, pci_remove_rom. 
Handles shadow ROMs for laptops that compress actual ROMs.

Signed-off-by: "Jon Smirl" <jonsmirl@yahoo.com>

diff -Nru a/arch/i386/pci/fixup.c b/arch/i386/pci/fixup.c
--- a/arch/i386/pci/fixup.c	Wed Aug 25 13:56:18 2004
+++ b/arch/i386/pci/fixup.c	Wed Aug 25 13:56:18 2004
@@ -255,3 +255,41 @@
 }
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2, pci_fixup_nforce2);
 
+/*
+ * Fixup to mark boot BIOS video selected by BIOS before it changes
+ *
+ * From information provided by "Jon Smirl" <jonsmirl@yahoo.com>
+ *
+ * The standard boot ROM sequence for an x86 machine uses the BIOS
+ * to select an initial video card for boot display. This boot video 
+ * card will have it's BIOS copied to C0000 in system RAM. 
+ * IORESOURCE_ROM_SHADOW is used to associate the boot video
+ * card with this copy. On laptops this copy has to be used since
+ * the main ROM may be compressed or combined with another image.
+ * See pci_map_rom() for use of this flag. IORESOURCE_ROM_SHADOW
+ * is marked here since the boot video device will be the only enabled
+ * video device at this point.
+ *
+ */static void __devinit pci_fixup_video(struct pci_dev *pdev)
+{
+	struct pci_dev *bridge;
+	struct pci_bus *bus;
+	u16 l;
+
+	if ((pdev->class >> 8) != PCI_CLASS_DISPLAY_VGA)
+		return;
+
+	/* Is VGA routed to us? */
+	bus = pdev->bus;
+	while (bus) {
+		bridge = bus->self;
+		if (bridge) {
+			pci_read_config_word(bridge, PCI_BRIDGE_CONTROL, &l);
+			if (!(l & PCI_BRIDGE_CTL_VGA))
+				return;
+		}
+		bus = bus->parent;
+	}
+	pdev->resource[PCI_ROM_RESOURCE].flags |= IORESOURCE_ROM_SHADOW;
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, pci_fixup_video);
diff -Nru a/drivers/pci/bus.c b/drivers/pci/bus.c
--- a/drivers/pci/bus.c	Wed Aug 25 13:56:18 2004
+++ b/drivers/pci/bus.c	Wed Aug 25 13:56:18 2004
@@ -97,10 +97,6 @@
 		spin_lock(&pci_bus_lock);
 		list_add_tail(&dev->global_list, &pci_devices);
 		spin_unlock(&pci_bus_lock);
-
-		pci_proc_attach_device(dev);
-		pci_create_sysfs_dev_files(dev);
-
 	}
 
 	list_for_each_entry(dev, &bus->devices, bus_list) {
diff -Nru a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
--- a/drivers/pci/pci-sysfs.c	Wed Aug 25 13:56:18 2004
+++ b/drivers/pci/pci-sysfs.c	Wed Aug 25 13:56:18 2004
@@ -5,6 +5,8 @@
  * (C) Copyright 2002-2004 IBM Corp.
  * (C) Copyright 2003 Matthew Wilcox
  * (C) Copyright 2003 Hewlett-Packard
+ * (C) Copyright 2004 Jon Smirl <jonsmirl@yahoo.com>
+ * (C) Copyright 2004 Silicon Graphics, Inc. Jesse Barnes <jbarnes@sgi.com>
  *
  * File attributes for PCI devices
  *
@@ -164,6 +166,220 @@
 	return count;
 }
 
+/**
+ * pci_enable_rom - enable ROM decoding for a PCI device
+ * @dev: PCI device to enable
+ *
+ * Enable ROM decoding on @dev.  This involves simply turning on the last
+ * bit of the PCI ROM BAR.  Note that some cards may share address decoders
+ * between the ROM and other resources, so enabling it may disable access
+ * to MMIO registers or other card memory.
+ */
+static void
+pci_enable_rom(struct pci_dev *pdev)
+{
+	u32 rom_addr;
+	
+	pci_read_config_dword(pdev, pdev->rom_base_reg, &rom_addr);
+	rom_addr |= PCI_ROM_ADDRESS_ENABLE;
+	pci_write_config_dword(pdev, pdev->rom_base_reg, rom_addr);
+}
+
+/**
+ * pci_disable_rom - disable ROM decoding for a PCI device
+ * @dev: PCI device to disable
+ *
+ * Disable ROM decoding on a PCI device by turning off the last bit in the
+ * ROM BAR.
+ */
+static void
+pci_disable_rom(struct pci_dev *pdev)
+{
+	u32 rom_addr;
+	pci_read_config_dword(pdev, pdev->rom_base_reg, &rom_addr);
+	rom_addr &= ~PCI_ROM_ADDRESS_ENABLE;
+	pci_write_config_dword(pdev, pdev->rom_base_reg, rom_addr);
+}
+
+/**
+ * pci_map_rom - map a PCI ROM to kernel space
+ * @dev: pointer to pci device struct
+ * @size: pointer to receive size of pci window over ROM
+ * @return: kernel virtual pointer to image of ROM
+ *
+ * Map a PCI ROM into kernel space. If ROM is boot video ROM,
+ * the shadow BIOS copy will be returned instead of the 
+ * actual ROM.
+ */
+unsigned char *
+pci_map_rom(struct pci_dev *pdev, size_t *size)
+{
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+	loff_t start;
+	unsigned char *rom;
+	
+	if (res->flags & IORESOURCE_ROM_SHADOW) {	/* IORESOURCE_ROM_SHADOW only set on x86 */
+		start = (loff_t)0xC0000; 	/* primary video rom always starts here */
+		*size = 0x20000;		/* cover C000:0 through E000:0 */
+	} else {
+		if (res->flags & IORESOURCE_ROM_COPY) {
+			*size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
+			return (unsigned char *)pci_resource_start(pdev, PCI_ROM_RESOURCE);
+		} else {
+			/* assign the ROM an address if it doesn't have one */
+			if (res->parent == NULL)
+				pci_assign_resource(pdev, PCI_ROM_RESOURCE);
+	
+			start = pci_resource_start(pdev, PCI_ROM_RESOURCE);
+			*size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
+			if (*size == 0)
+				return NULL;
+			
+			/* Enable ROM space decodes */
+			pci_enable_rom(pdev);
+		}
+	}
+	
+	rom = ioremap(start, *size);
+	if (!rom) {
+		/* restore enable if ioremap fails */
+		if (!(res->flags & (IORESOURCE_ROM_ENABLE | IORESOURCE_ROM_SHADOW | IORESOURCE_ROM_COPY)))
+			pci_disable_rom(pdev);
+		return NULL;
+	}		
+	/* Standard PCI ROMs start out with these three bytes 55 AA size/512 */
+	if ((*rom == 0x55) && (*(rom + 1) == 0xAA))
+		*size = *(rom + 2) * 512;	/* return true ROM size, not PCI window size */
+		
+	return rom;
+}
+
+/**
+ * pci_map_rom_copy - map a PCI ROM to kernel space, create a copy
+ * @dev: pointer to pci device struct
+ * @size: pointer to receive size of pci window over ROM
+ * @return: kernel virtual pointer to image of ROM
+ *
+ * Map a PCI ROM into kernel space. If ROM is boot video ROM,
+ * the shadow BIOS copy will be returned instead of the 
+ * actual ROM.
+ */
+unsigned char *
+pci_map_rom_copy(struct pci_dev *pdev, size_t *size)
+{
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+	unsigned char *rom;
+	
+	rom = pci_map_rom(pdev, size);
+	if (!rom)
+		return NULL;
+		
+	if (res->flags & (IORESOURCE_ROM_COPY | IORESOURCE_ROM_SHADOW))
+		return rom;
+		
+	res->start = (unsigned long)kmalloc(*size, GFP_KERNEL);
+	if (!res->start) 
+		return rom;
+
+	res->end = res->start + *size; 
+	memcpy((void*)res->start, rom, *size);
+	pci_unmap_rom(pdev, rom);
+	res->flags |= IORESOURCE_ROM_COPY;
+	
+	return (unsigned char *)res->start;
+}
+
+/**
+ * pci_unmap_rom - unmap the ROM from kernel space
+ * @dev: pointer to pci device struct
+ * @rom: virtual address of the previous mapping
+ *
+ * Remove a mapping of a previously mapped ROM
+ */
+void 
+pci_unmap_rom(struct pci_dev *pdev, unsigned char *rom)
+{
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+
+	if (res->flags & IORESOURCE_ROM_COPY)
+		return;
+		
+	iounmap(rom);
+		
+	/* Disable again before continuing, leave enabled if pci=rom */
+	if (!(res->flags & (IORESOURCE_ROM_ENABLE | IORESOURCE_ROM_SHADOW)))
+		pci_disable_rom(pdev);
+}
+
+static struct bin_attribute rom_attr;
+
+/**
+ * pci_remove_rom - disable the ROM and remove it's sysfs attribute
+ * @dev: pointer to pci device struct
+ *
+ */
+void 
+pci_remove_rom(struct pci_dev *pdev) 
+{
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+	
+	if (pci_resource_len(pdev, PCI_ROM_RESOURCE))
+		sysfs_remove_bin_file(&pdev->dev.kobj, &rom_attr);
+	if (!(res->flags & (IORESOURCE_ROM_ENABLE | IORESOURCE_ROM_SHADOW | IORESOURCE_ROM_COPY)))
+		pci_disable_rom(pdev);
+}
+
+/**
+ * pci_remove_rom - disable the ROM and remove it's sysfs attribute
+ * @dev: pointer to pci device struct
+ *
+ */
+void 
+pci_cleanup_rom(struct pci_dev *pdev) 
+{
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+	if (res->flags & IORESOURCE_ROM_COPY) {
+		kfree((void*)res->start);
+		res->flags &= ~IORESOURCE_ROM_COPY;
+		res->start = 0;
+		res->end = 0;
+	}
+}
+
+/**
+ * pci_read_rom - read a PCI ROM
+ * @kobj: kernel object handle
+ * @buf: where to put the data we read from the ROM
+ * @off: file offset
+ * @count: number of bytes to read
+ *
+ * Put @count bytes starting at @off into @buf from the ROM in the PCI
+ * device corresponding to @kobj.
+ */
+static ssize_t
+pci_read_rom(struct kobject *kobj, char *buf, loff_t off, size_t count)
+{
+	struct pci_dev *pdev = to_pci_dev(container_of(kobj, struct device, kobj));
+	unsigned char *rom;
+	size_t size;
+	
+	rom = pci_map_rom(pdev, &size);	/* size starts out as PCI window size */
+	if (!rom)
+		return 0;
+		
+	if (off >= size)
+		count = 0;
+	else {
+		if (off + count > size)
+			count = size - off;
+		
+		memcpy_fromio(buf, rom + off, count);
+	}
+	pci_unmap_rom(pdev, rom);
+		
+	return count;
+}
+
 static struct bin_attribute pci_config_attr = {
 	.attr =	{
 		.name = "config",
@@ -192,7 +408,60 @@
 		sysfs_create_bin_file(&pdev->dev.kobj, &pci_config_attr);
 	else
 		sysfs_create_bin_file(&pdev->dev.kobj, &pcie_config_attr);
-
+		
+	/* If the device has a ROM, try to expose it in sysfs. */
+	if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) {
+		struct bin_attribute *rom_attr;
+		
+		rom_attr = kmalloc(sizeof(*rom_attr), GFP_ATOMIC);
+		if (rom_attr) {
+			pdev->rom_attr = rom_attr;
+			rom_attr->size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
+			rom_attr->attr.name = "rom";
+			rom_attr->attr.mode = S_IRUSR;
+			rom_attr->attr.owner = THIS_MODULE;
+			rom_attr->read = pci_read_rom;
+			sysfs_create_bin_file(&pdev->dev.kobj, rom_attr);
+		}
+	}
 	/* add platform-specific attributes */
 	pcibios_add_platform_entries(pdev);
 }
+
+/**
+ * pci_remove_sysfs_dev_files - cleanup PCI specific sysfs files
+ * @pdev: device whose entries we should free
+ *
+ * Cleanup when @pdev is removed from sysfs.
+ */
+void pci_remove_sysfs_dev_files(struct pci_dev *pdev)
+{
+	if (pdev->cfg_size < 4096)
+		sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr);
+	else
+		sysfs_remove_bin_file(&pdev->dev.kobj, &pcie_config_attr);
+
+	if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) {
+		if (pdev->rom_attr) {
+			sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr);
+			kfree(pdev->rom_attr);
+		}
+	}
+}
+
+static int __init pci_sysfs_init(void)
+{
+	struct pci_dev *pdev = NULL;
+	
+	while ((pdev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pdev)) != NULL)
+		pci_create_sysfs_dev_files(pdev);
+
+	return 0;
+}
+
+__initcall(pci_sysfs_init);
+
+EXPORT_SYMBOL(pci_map_rom);
+EXPORT_SYMBOL(pci_map_rom_copy);
+EXPORT_SYMBOL(pci_unmap_rom);
+EXPORT_SYMBOL(pci_remove_rom);
diff -Nru a/drivers/pci/pci.h b/drivers/pci/pci.h
--- a/drivers/pci/pci.h	Wed Aug 25 13:56:18 2004
+++ b/drivers/pci/pci.h	Wed Aug 25 13:56:18 2004
@@ -3,6 +3,8 @@
 extern int pci_hotplug (struct device *dev, char **envp, int num_envp,
 			 char *buffer, int buffer_size);
 extern void pci_create_sysfs_dev_files(struct pci_dev *pdev);
+extern void pci_remove_sysfs_dev_files(struct pci_dev *pdev);
+void pci_cleanup_rom(struct pci_dev *dev);
 extern int pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
 				  unsigned long size, unsigned long align,
 				  unsigned long min, unsigned int type_mask,
diff -Nru a/drivers/pci/probe.c b/drivers/pci/probe.c
--- a/drivers/pci/probe.c	Wed Aug 25 13:56:18 2004
+++ b/drivers/pci/probe.c	Wed Aug 25 13:56:18 2004
@@ -170,7 +170,7 @@
 		if (sz && sz != 0xffffffff) {
 			sz = pci_size(l, sz, PCI_ROM_ADDRESS_MASK);
 			if (sz) {
-				res->flags = (l & PCI_ROM_ADDRESS_ENABLE) |
+				res->flags = (l & IORESOURCE_ROM_ENABLE) |
 				  IORESOURCE_MEM | IORESOURCE_PREFETCH |
 				  IORESOURCE_READONLY | IORESOURCE_CACHEABLE;
 				res->start = l & PCI_ROM_ADDRESS_MASK;
diff -Nru a/drivers/pci/proc.c b/drivers/pci/proc.c
--- a/drivers/pci/proc.c	Wed Aug 25 13:56:18 2004
+++ b/drivers/pci/proc.c	Wed Aug 25 13:56:18 2004
@@ -16,7 +16,6 @@
 #include <asm/uaccess.h>
 #include <asm/byteorder.h>
 
-static int proc_initialized;	/* = 0 */
 
 static loff_t
 proc_bus_pci_lseek(struct file *file, loff_t off, int whence)
@@ -387,9 +386,6 @@
 	struct proc_dir_entry *de, *e;
 	char name[16];
 
-	if (!proc_initialized)
-		return -EACCES;
-
 	if (!(de = bus->procdir)) {
 		if (pci_name_bus(name, bus))
 			return -EEXIST;
@@ -425,9 +421,6 @@
 {
 	struct proc_dir_entry *de = bus->procdir;
 
-	if (!proc_initialized)
-		return -EACCES;
-
 	if (!de) {
 		char name[16];
 		sprintf(name, "%02x", bus->number);
@@ -583,6 +576,7 @@
 {
 	return seq_open(file, &proc_bus_pci_devices_op);
 }
+
 static struct file_operations proc_bus_pci_dev_operations = {
 	.open		= proc_bus_pci_dev_open,
 	.read		= seq_read,
@@ -593,16 +587,20 @@
 static int __init pci_proc_init(void)
 {
 	struct proc_dir_entry *entry;
-	struct pci_dev *dev = NULL;
+	struct pci_dev *pdev = NULL;
+
 	proc_bus_pci_dir = proc_mkdir("pci", proc_bus);
+
 	entry = create_proc_entry("devices", 0, proc_bus_pci_dir);
 	if (entry)
 		entry->proc_fops = &proc_bus_pci_dev_operations;
-	proc_initialized = 1;
-	while ((dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
-		pci_proc_attach_device(dev);
+
+	while ((pdev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pdev)) != NULL) {
+		pci_proc_attach_device(pdev);
 	}
+
 	legacy_proc_init();
+
 	return 0;
 }
 
diff -Nru a/drivers/pci/remove.c b/drivers/pci/remove.c
--- a/drivers/pci/remove.c	Wed Aug 25 13:56:18 2004
+++ b/drivers/pci/remove.c	Wed Aug 25 13:56:18 2004
@@ -16,6 +16,7 @@
 
  	msi_remove_pci_irq_vectors(dev);
 
+	pci_cleanup_rom(dev);
 	for (i = 0; i < PCI_NUM_RESOURCES; i++) {
 		struct resource *res = dev->resource + i;
 		if (res->parent)
@@ -26,6 +27,7 @@
 static void pci_destroy_dev(struct pci_dev *dev)
 {
 	pci_proc_detach_device(dev);
+	pci_remove_sysfs_dev_files(dev);
 	device_unregister(&dev->dev);
 
 	/* Remove the device from the device lists, and prevent any further
diff -Nru a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c
--- a/drivers/pci/setup-res.c	Wed Aug 25 13:56:18 2004
+++ b/drivers/pci/setup-res.c	Wed Aug 25 13:56:18 2004
@@ -56,7 +56,7 @@
 	if (resno < 6) {
 		reg = PCI_BASE_ADDRESS_0 + 4 * resno;
 	} else if (resno == PCI_ROM_RESOURCE) {
-		new |= res->flags & PCI_ROM_ADDRESS_ENABLE;
+		new |= res->flags & IORESOURCE_ROM_ENABLE;
 		reg = dev->rom_base_reg;
 	} else {
 		/* Hmm, non-standard resource. */
diff -Nru a/include/linux/ioport.h b/include/linux/ioport.h
--- a/include/linux/ioport.h	Wed Aug 25 13:56:18 2004
+++ b/include/linux/ioport.h	Wed Aug 25 13:56:18 2004
@@ -82,6 +82,11 @@
 #define IORESOURCE_MEM_SHADOWABLE	(1<<5)	/* dup: IORESOURCE_SHADOWABLE */
 #define IORESOURCE_MEM_EXPANSIONROM	(1<<6)
 
+/* PCI ROM control bits (IORESOURCE_BITS) */
+#define IORESOURCE_ROM_ENABLE		(1<<0)	/* ROM is enabled, same as PCI_ROM_ADDRESS_ENABLE */
+#define IORESOURCE_ROM_SHADOW		(1<<1)	/* ROM is copy at C000:0 */
+#define IORESOURCE_ROM_COPY		(1<<2)	/* ROM is alloc'd copy, resource field overlaid */
+
 /* PC/ISA/whatever - the normal PC address spaces: IO and memory */
 extern struct resource ioport_resource;
 extern struct resource iomem_resource;
diff -Nru a/include/linux/pci.h b/include/linux/pci.h
--- a/include/linux/pci.h	Wed Aug 25 13:56:18 2004
+++ b/include/linux/pci.h	Wed Aug 25 13:56:18 2004
@@ -537,6 +537,7 @@
 	unsigned int	is_busmaster:1; /* device is busmaster */
 	
 	u32		saved_config_space[16]; /* config space saved at suspend time */
+	struct bin_attribute *rom_attr; /* attribute descriptor for sysfs ROM entry */
 #ifdef CONFIG_PCI_NAMES
 #define PCI_NAME_SIZE	96
 #define PCI_NAME_HALF	__stringify(43)	/* less than half to handle slop */
@@ -777,6 +778,12 @@
 int pci_dac_set_dma_mask(struct pci_dev *dev, u64 mask);
 int pci_set_consistent_dma_mask(struct pci_dev *dev, u64 mask);
 int pci_assign_resource(struct pci_dev *dev, int i);
+
+/* ROM control related routines */
+unsigned char *pci_map_rom(struct pci_dev *pdev, size_t *size);
+unsigned char *pci_map_rom_copy(struct pci_dev *pdev, size_t *size);
+void pci_unmap_rom(struct pci_dev *pdev, unsigned char *rom);
+void pci_remove_rom(struct pci_dev *pdev);
 
 /* Power management related routines */
 int pci_save_state(struct pci_dev *dev, u32 *buffer);

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-25 17:32                   ` Jon Smirl
@ 2004-08-25 17:42                     ` Greg KH
  2004-08-25 18:06                       ` Jon Smirl
  0 siblings, 1 reply; 110+ messages in thread
From: Greg KH @ 2004-08-25 17:42 UTC (permalink / raw)
  To: Jon Smirl
  Cc: Jesse Barnes, Martin Mares, Pallipadi, Venkatesh, linux-pci,
	Alan Cox, Linux Kernel Mailing List, Petr Vandrovec,
	Benjamin Herrenschmidt

On Wed, Aug 25, 2004 at 10:32:41AM -0700, Jon Smirl wrote:
> No functional changes from previous version. I fixed the braces and
> diff'd it against 2.6.8.1-mm4. 
> 
> Where does "Signed-off-by:" go in a patch? Jesse and I added a
> copyright  statement.

Please read Documentation/SubmittingPatches for details on this.  It
should just go into the description of what the patch does, that I need
in order to provide a good changelog entry for the patch.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-23 22:51                 ` Greg KH
@ 2004-08-25 17:32                   ` Jon Smirl
  2004-08-25 17:42                     ` Greg KH
  0 siblings, 1 reply; 110+ messages in thread
From: Jon Smirl @ 2004-08-25 17:32 UTC (permalink / raw)
  To: Greg KH, Jesse Barnes
  Cc: Martin Mares, Pallipadi, Venkatesh, linux-pci, Alan Cox,
	Linux Kernel Mailing List, Petr Vandrovec,
	Benjamin Herrenschmidt

[-- Attachment #1: Type: text/plain, Size: 1040 bytes --]

No functional changes from previous version. I fixed the braces and
diff'd it against 2.6.8.1-mm4. 

Where does "Signed-off-by:" go in a patch? Jesse and I added a
copyright  statement.

Grep for PCI_ROM_ADDRESS_ENABLE and you can see all the places where
existing code should be adjusted for the new API. I started looking
fixing them but I am not sure what some of them are doing. For example
drivers/ide/pci/xxx seems to enable ROMs and then never use them. 

--- Greg KH <greg@kroah.com> wrote:
> There are a few minor coding style issues (the initial '{' for a
> function needs to be on a new line) and I need a "Signed-off-by:"
> line, and it probably will not apply right now due to the pci quirks
> changes (if you rediff it against the latest -mm tree, that should 
> fix it.) 
> 
> Other than that, it looks good to me.
> 
> thanks,
> 
> greg k-h
> 

=====
Jon Smirl
jonsmirl@yahoo.com


		
__________________________________
Do you Yahoo!?
Yahoo! Mail - 50x more storage than other providers!
http://promotions.yahoo.com/new_mail

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: pci-sysfs-rom-17-mm.patch --]
[-- Type: text/x-patch; name="pci-sysfs-rom-17-mm.patch", Size: 15875 bytes --]

diff -Nru a/arch/i386/pci/fixup.c b/arch/i386/pci/fixup.c
--- a/arch/i386/pci/fixup.c	Wed Aug 25 13:19:41 2004
+++ b/arch/i386/pci/fixup.c	Wed Aug 25 13:19:41 2004
@@ -255,3 +255,41 @@
 }
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2, pci_fixup_nforce2);
 
+/*
+ * Fixup to mark boot BIOS video selected by BIOS before it changes
+ *
+ * From information provided by "Jon Smirl" <jonsmirl@yahoo.com>
+ *
+ * The standard boot ROM sequence for an x86 machine uses the BIOS
+ * to select an initial video card for boot display. This boot video 
+ * card will have it's BIOS copied to C0000 in system RAM. 
+ * IORESOURCE_ROM_SHADOW is used to associate the boot video
+ * card with this copy. On laptops this copy has to be used since
+ * the main ROM may be compressed or combined with another image.
+ * See pci_map_rom() for use of this flag. IORESOURCE_ROM_SHADOW
+ * is marked here since the boot video device will be the only enabled
+ * video device at this point.
+ *
+ */static void __devinit pci_fixup_video(struct pci_dev *pdev)
+{
+	struct pci_dev *bridge;
+	struct pci_bus *bus;
+	u16 l;
+
+	if ((pdev->class >> 8) != PCI_CLASS_DISPLAY_VGA)
+		return;
+
+	/* Is VGA routed to us? */
+	bus = pdev->bus;
+	while (bus) {
+		bridge = bus->self;
+		if (bridge) {
+			pci_read_config_word(bridge, PCI_BRIDGE_CONTROL, &l);
+			if (!(l & PCI_BRIDGE_CTL_VGA))
+				return;
+		}
+		bus = bus->parent;
+	}
+	pdev->resource[PCI_ROM_RESOURCE].flags |= IORESOURCE_ROM_SHADOW;
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, pci_fixup_video);
diff -Nru a/drivers/pci/bus.c b/drivers/pci/bus.c
--- a/drivers/pci/bus.c	Wed Aug 25 13:19:41 2004
+++ b/drivers/pci/bus.c	Wed Aug 25 13:19:41 2004
@@ -97,10 +97,6 @@
 		spin_lock(&pci_bus_lock);
 		list_add_tail(&dev->global_list, &pci_devices);
 		spin_unlock(&pci_bus_lock);
-
-		pci_proc_attach_device(dev);
-		pci_create_sysfs_dev_files(dev);
-
 	}
 
 	list_for_each_entry(dev, &bus->devices, bus_list) {
diff -Nru a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
--- a/drivers/pci/pci-sysfs.c	Wed Aug 25 13:19:41 2004
+++ b/drivers/pci/pci-sysfs.c	Wed Aug 25 13:19:41 2004
@@ -5,6 +5,8 @@
  * (C) Copyright 2002-2004 IBM Corp.
  * (C) Copyright 2003 Matthew Wilcox
  * (C) Copyright 2003 Hewlett-Packard
+ * (C) Copyright 2004 Jon Smirl <jonsmirl@yahoo.com>
+ * (C) Copyright 2004 Silicon Graphics, Inc. Jesse Barnes <jbarnes@sgi.com>
  *
  * File attributes for PCI devices
  *
@@ -164,6 +166,220 @@
 	return count;
 }
 
+/**
+ * pci_enable_rom - enable ROM decoding for a PCI device
+ * @dev: PCI device to enable
+ *
+ * Enable ROM decoding on @dev.  This involves simply turning on the last
+ * bit of the PCI ROM BAR.  Note that some cards may share address decoders
+ * between the ROM and other resources, so enabling it may disable access
+ * to MMIO registers or other card memory.
+ */
+static void
+pci_enable_rom(struct pci_dev *pdev)
+{
+	u32 rom_addr;
+	
+	pci_read_config_dword(pdev, pdev->rom_base_reg, &rom_addr);
+	rom_addr |= PCI_ROM_ADDRESS_ENABLE;
+	pci_write_config_dword(pdev, pdev->rom_base_reg, rom_addr);
+}
+
+/**
+ * pci_disable_rom - disable ROM decoding for a PCI device
+ * @dev: PCI device to disable
+ *
+ * Disable ROM decoding on a PCI device by turning off the last bit in the
+ * ROM BAR.
+ */
+static void
+pci_disable_rom(struct pci_dev *pdev)
+{
+	u32 rom_addr;
+	pci_read_config_dword(pdev, pdev->rom_base_reg, &rom_addr);
+	rom_addr &= ~PCI_ROM_ADDRESS_ENABLE;
+	pci_write_config_dword(pdev, pdev->rom_base_reg, rom_addr);
+}
+
+/**
+ * pci_map_rom - map a PCI ROM to kernel space
+ * @dev: pointer to pci device struct
+ * @size: pointer to receive size of pci window over ROM
+ * @return: kernel virtual pointer to image of ROM
+ *
+ * Map a PCI ROM into kernel space. If ROM is boot video ROM,
+ * the shadow BIOS copy will be returned instead of the 
+ * actual ROM.
+ */
+unsigned char *
+pci_map_rom(struct pci_dev *pdev, size_t *size)
+{
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+	loff_t start;
+	unsigned char *rom;
+	
+	if (res->flags & IORESOURCE_ROM_SHADOW) {	/* IORESOURCE_ROM_SHADOW only set on x86 */
+		start = (loff_t)0xC0000; 	/* primary video rom always starts here */
+		*size = 0x20000;		/* cover C000:0 through E000:0 */
+	} else {
+		if (res->flags & IORESOURCE_ROM_COPY) {
+			*size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
+			return (unsigned char *)pci_resource_start(pdev, PCI_ROM_RESOURCE);
+		} else {
+			/* assign the ROM an address if it doesn't have one */
+			if (res->parent == NULL)
+				pci_assign_resource(pdev, PCI_ROM_RESOURCE);
+	
+			start = pci_resource_start(pdev, PCI_ROM_RESOURCE);
+			*size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
+			if (*size == 0)
+				return NULL;
+			
+			/* Enable ROM space decodes */
+			pci_enable_rom(pdev);
+		}
+	}
+	
+	rom = ioremap(start, *size);
+	if (!rom) {
+		/* restore enable if ioremap fails */
+		if (!(res->flags & (IORESOURCE_ROM_ENABLE | IORESOURCE_ROM_SHADOW | IORESOURCE_ROM_COPY)))
+			pci_disable_rom(pdev);
+		return NULL;
+	}		
+	/* Standard PCI ROMs start out with these three bytes 55 AA size/512 */
+	if ((*rom == 0x55) && (*(rom + 1) == 0xAA))
+		*size = *(rom + 2) * 512;	/* return true ROM size, not PCI window size */
+		
+	return rom;
+}
+
+/**
+ * pci_map_rom_copy - map a PCI ROM to kernel space, create a copy
+ * @dev: pointer to pci device struct
+ * @size: pointer to receive size of pci window over ROM
+ * @return: kernel virtual pointer to image of ROM
+ *
+ * Map a PCI ROM into kernel space. If ROM is boot video ROM,
+ * the shadow BIOS copy will be returned instead of the 
+ * actual ROM.
+ */
+unsigned char *
+pci_map_rom_copy(struct pci_dev *pdev, size_t *size)
+{
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+	unsigned char *rom;
+	
+	rom = pci_map_rom(pdev, size);
+	if (!rom)
+		return NULL;
+		
+	if (res->flags & (IORESOURCE_ROM_COPY | IORESOURCE_ROM_SHADOW))
+		return rom;
+		
+	res->start = (unsigned long)kmalloc(*size, GFP_KERNEL);
+	if (!res->start) 
+		return rom;
+
+	res->end = res->start + *size; 
+	memcpy((void*)res->start, rom, *size);
+	pci_unmap_rom(pdev, rom);
+	res->flags |= IORESOURCE_ROM_COPY;
+	
+	return (unsigned char *)res->start;
+}
+
+/**
+ * pci_unmap_rom - unmap the ROM from kernel space
+ * @dev: pointer to pci device struct
+ * @rom: virtual address of the previous mapping
+ *
+ * Remove a mapping of a previously mapped ROM
+ */
+void 
+pci_unmap_rom(struct pci_dev *pdev, unsigned char *rom)
+{
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+
+	if (res->flags & IORESOURCE_ROM_COPY)
+		return;
+		
+	iounmap(rom);
+		
+	/* Disable again before continuing, leave enabled if pci=rom */
+	if (!(res->flags & (IORESOURCE_ROM_ENABLE | IORESOURCE_ROM_SHADOW)))
+		pci_disable_rom(pdev);
+}
+
+static struct bin_attribute rom_attr;
+
+/**
+ * pci_remove_rom - disable the ROM and remove it's sysfs attribute
+ * @dev: pointer to pci device struct
+ *
+ */
+void 
+pci_remove_rom(struct pci_dev *pdev) 
+{
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+	
+	if (pci_resource_len(pdev, PCI_ROM_RESOURCE))
+		sysfs_remove_bin_file(&pdev->dev.kobj, &rom_attr);
+	if (!(res->flags & (IORESOURCE_ROM_ENABLE | IORESOURCE_ROM_SHADOW | IORESOURCE_ROM_COPY)))
+		pci_disable_rom(pdev);
+}
+
+/**
+ * pci_remove_rom - disable the ROM and remove it's sysfs attribute
+ * @dev: pointer to pci device struct
+ *
+ */
+void 
+pci_cleanup_rom(struct pci_dev *pdev) 
+{
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+	if (res->flags & IORESOURCE_ROM_COPY) {
+		kfree((void*)res->start);
+		res->flags &= ~IORESOURCE_ROM_COPY;
+		res->start = 0;
+		res->end = 0;
+	}
+}
+
+/**
+ * pci_read_rom - read a PCI ROM
+ * @kobj: kernel object handle
+ * @buf: where to put the data we read from the ROM
+ * @off: file offset
+ * @count: number of bytes to read
+ *
+ * Put @count bytes starting at @off into @buf from the ROM in the PCI
+ * device corresponding to @kobj.
+ */
+static ssize_t
+pci_read_rom(struct kobject *kobj, char *buf, loff_t off, size_t count)
+{
+	struct pci_dev *pdev = to_pci_dev(container_of(kobj, struct device, kobj));
+	unsigned char *rom;
+	size_t size;
+	
+	rom = pci_map_rom(pdev, &size);	/* size starts out as PCI window size */
+	if (!rom)
+		return 0;
+		
+	if (off >= size)
+		count = 0;
+	else {
+		if (off + count > size)
+			count = size - off;
+		
+		memcpy_fromio(buf, rom + off, count);
+	}
+	pci_unmap_rom(pdev, rom);
+		
+	return count;
+}
+
 static struct bin_attribute pci_config_attr = {
 	.attr =	{
 		.name = "config",
@@ -192,7 +408,60 @@
 		sysfs_create_bin_file(&pdev->dev.kobj, &pci_config_attr);
 	else
 		sysfs_create_bin_file(&pdev->dev.kobj, &pcie_config_attr);
-
+		
+	/* If the device has a ROM, try to expose it in sysfs. */
+	if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) {
+		struct bin_attribute *rom_attr;
+		
+		rom_attr = kmalloc(sizeof(*rom_attr), GFP_ATOMIC);
+		if (rom_attr) {
+			pdev->rom_attr = rom_attr;
+			rom_attr->size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
+			rom_attr->attr.name = "rom";
+			rom_attr->attr.mode = S_IRUSR;
+			rom_attr->attr.owner = THIS_MODULE;
+			rom_attr->read = pci_read_rom;
+			sysfs_create_bin_file(&pdev->dev.kobj, rom_attr);
+		}
+	}
 	/* add platform-specific attributes */
 	pcibios_add_platform_entries(pdev);
 }
+
+/**
+ * pci_remove_sysfs_dev_files - cleanup PCI specific sysfs files
+ * @pdev: device whose entries we should free
+ *
+ * Cleanup when @pdev is removed from sysfs.
+ */
+void pci_remove_sysfs_dev_files(struct pci_dev *pdev)
+{
+	if (pdev->cfg_size < 4096)
+		sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr);
+	else
+		sysfs_remove_bin_file(&pdev->dev.kobj, &pcie_config_attr);
+
+	if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) {
+		if (pdev->rom_attr) {
+			sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr);
+			kfree(pdev->rom_attr);
+		}
+	}
+}
+
+static int __init pci_sysfs_init(void)
+{
+	struct pci_dev *pdev = NULL;
+	
+	while ((pdev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pdev)) != NULL)
+		pci_create_sysfs_dev_files(pdev);
+
+	return 0;
+}
+
+__initcall(pci_sysfs_init);
+
+EXPORT_SYMBOL(pci_map_rom);
+EXPORT_SYMBOL(pci_map_rom_copy);
+EXPORT_SYMBOL(pci_unmap_rom);
+EXPORT_SYMBOL(pci_remove_rom);
diff -Nru a/drivers/pci/pci.h b/drivers/pci/pci.h
--- a/drivers/pci/pci.h	Wed Aug 25 13:19:41 2004
+++ b/drivers/pci/pci.h	Wed Aug 25 13:19:41 2004
@@ -3,6 +3,8 @@
 extern int pci_hotplug (struct device *dev, char **envp, int num_envp,
 			 char *buffer, int buffer_size);
 extern void pci_create_sysfs_dev_files(struct pci_dev *pdev);
+extern void pci_remove_sysfs_dev_files(struct pci_dev *pdev);
+void pci_cleanup_rom(struct pci_dev *dev);
 extern int pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
 				  unsigned long size, unsigned long align,
 				  unsigned long min, unsigned int type_mask,
diff -Nru a/drivers/pci/probe.c b/drivers/pci/probe.c
--- a/drivers/pci/probe.c	Wed Aug 25 13:19:41 2004
+++ b/drivers/pci/probe.c	Wed Aug 25 13:19:41 2004
@@ -170,7 +170,7 @@
 		if (sz && sz != 0xffffffff) {
 			sz = pci_size(l, sz, PCI_ROM_ADDRESS_MASK);
 			if (sz) {
-				res->flags = (l & PCI_ROM_ADDRESS_ENABLE) |
+				res->flags = (l & IORESOURCE_ROM_ENABLE) |
 				  IORESOURCE_MEM | IORESOURCE_PREFETCH |
 				  IORESOURCE_READONLY | IORESOURCE_CACHEABLE;
 				res->start = l & PCI_ROM_ADDRESS_MASK;
diff -Nru a/drivers/pci/proc.c b/drivers/pci/proc.c
--- a/drivers/pci/proc.c	Wed Aug 25 13:19:41 2004
+++ b/drivers/pci/proc.c	Wed Aug 25 13:19:41 2004
@@ -16,7 +16,6 @@
 #include <asm/uaccess.h>
 #include <asm/byteorder.h>
 
-static int proc_initialized;	/* = 0 */
 
 static loff_t
 proc_bus_pci_lseek(struct file *file, loff_t off, int whence)
@@ -387,9 +386,6 @@
 	struct proc_dir_entry *de, *e;
 	char name[16];
 
-	if (!proc_initialized)
-		return -EACCES;
-
 	if (!(de = bus->procdir)) {
 		if (pci_name_bus(name, bus))
 			return -EEXIST;
@@ -425,9 +421,6 @@
 {
 	struct proc_dir_entry *de = bus->procdir;
 
-	if (!proc_initialized)
-		return -EACCES;
-
 	if (!de) {
 		char name[16];
 		sprintf(name, "%02x", bus->number);
@@ -583,6 +576,7 @@
 {
 	return seq_open(file, &proc_bus_pci_devices_op);
 }
+
 static struct file_operations proc_bus_pci_dev_operations = {
 	.open		= proc_bus_pci_dev_open,
 	.read		= seq_read,
@@ -593,16 +587,20 @@
 static int __init pci_proc_init(void)
 {
 	struct proc_dir_entry *entry;
-	struct pci_dev *dev = NULL;
+	struct pci_dev *pdev = NULL;
+
 	proc_bus_pci_dir = proc_mkdir("pci", proc_bus);
+
 	entry = create_proc_entry("devices", 0, proc_bus_pci_dir);
 	if (entry)
 		entry->proc_fops = &proc_bus_pci_dev_operations;
-	proc_initialized = 1;
-	while ((dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
-		pci_proc_attach_device(dev);
+
+	while ((pdev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pdev)) != NULL) {
+		pci_proc_attach_device(pdev);
 	}
+
 	legacy_proc_init();
+
 	return 0;
 }
 
diff -Nru a/drivers/pci/remove.c b/drivers/pci/remove.c
--- a/drivers/pci/remove.c	Wed Aug 25 13:19:41 2004
+++ b/drivers/pci/remove.c	Wed Aug 25 13:19:41 2004
@@ -16,6 +16,7 @@
 
  	msi_remove_pci_irq_vectors(dev);
 
+	pci_cleanup_rom(dev);
 	for (i = 0; i < PCI_NUM_RESOURCES; i++) {
 		struct resource *res = dev->resource + i;
 		if (res->parent)
@@ -26,6 +27,7 @@
 static void pci_destroy_dev(struct pci_dev *dev)
 {
 	pci_proc_detach_device(dev);
+	pci_remove_sysfs_dev_files(dev);
 	device_unregister(&dev->dev);
 
 	/* Remove the device from the device lists, and prevent any further
diff -Nru a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c
--- a/drivers/pci/setup-res.c	Wed Aug 25 13:19:41 2004
+++ b/drivers/pci/setup-res.c	Wed Aug 25 13:19:41 2004
@@ -56,7 +56,7 @@
 	if (resno < 6) {
 		reg = PCI_BASE_ADDRESS_0 + 4 * resno;
 	} else if (resno == PCI_ROM_RESOURCE) {
-		new |= res->flags & PCI_ROM_ADDRESS_ENABLE;
+		new |= res->flags & IORESOURCE_ROM_ENABLE;
 		reg = dev->rom_base_reg;
 	} else {
 		/* Hmm, non-standard resource. */
diff -Nru a/include/linux/ioport.h b/include/linux/ioport.h
--- a/include/linux/ioport.h	Wed Aug 25 13:19:41 2004
+++ b/include/linux/ioport.h	Wed Aug 25 13:19:41 2004
@@ -82,6 +82,11 @@
 #define IORESOURCE_MEM_SHADOWABLE	(1<<5)	/* dup: IORESOURCE_SHADOWABLE */
 #define IORESOURCE_MEM_EXPANSIONROM	(1<<6)
 
+/* PCI ROM control bits (IORESOURCE_BITS) */
+#define IORESOURCE_ROM_ENABLE		(1<<0)	/* ROM is enabled, same as PCI_ROM_ADDRESS_ENABLE */
+#define IORESOURCE_ROM_SHADOW		(1<<1)	/* ROM is copy at C000:0 */
+#define IORESOURCE_ROM_COPY		(1<<2)	/* ROM is alloc'd copy, resource field overlaid */
+
 /* PC/ISA/whatever - the normal PC address spaces: IO and memory */
 extern struct resource ioport_resource;
 extern struct resource iomem_resource;
diff -Nru a/include/linux/pci.h b/include/linux/pci.h
--- a/include/linux/pci.h	Wed Aug 25 13:19:41 2004
+++ b/include/linux/pci.h	Wed Aug 25 13:19:41 2004
@@ -537,6 +537,7 @@
 	unsigned int	is_busmaster:1; /* device is busmaster */
 	
 	u32		saved_config_space[16]; /* config space saved at suspend time */
+	struct bin_attribute *rom_attr; /* attribute descriptor for sysfs ROM entry */
 #ifdef CONFIG_PCI_NAMES
 #define PCI_NAME_SIZE	96
 #define PCI_NAME_HALF	__stringify(43)	/* less than half to handle slop */
@@ -777,6 +778,12 @@
 int pci_dac_set_dma_mask(struct pci_dev *dev, u64 mask);
 int pci_set_consistent_dma_mask(struct pci_dev *dev, u64 mask);
 int pci_assign_resource(struct pci_dev *dev, int i);
+
+/* ROM control related routines */
+unsigned char *pci_map_rom(struct pci_dev *pdev, size_t *size);
+unsigned char *pci_map_rom_copy(struct pci_dev *pdev, size_t *size);
+void pci_unmap_rom(struct pci_dev *pdev, unsigned char *rom);
+void pci_remove_rom(struct pci_dev *pdev);
 
 /* Power management related routines */
 int pci_save_state(struct pci_dev *dev, u32 *buffer);

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-25 15:36               ` Matthew Wilcox
@ 2004-08-25 15:50                 ` Jon Smirl
  0 siblings, 0 replies; 110+ messages in thread
From: Jon Smirl @ 2004-08-25 15:50 UTC (permalink / raw)
  To: Matthew Wilcox
  Cc: Martin Mares, Pallipadi, Venkatesh, Greg KH, Jesse Barnes,
	linux-pci, Alan Cox, Linux Kernel Mailing List, Petr Vandrovec,
	Benjamin Herrenschmidt

--- Matthew Wilcox <willy@debian.org> wrote:
> On Wed, Aug 18, 2004 at 11:13:10AM -0700, Jon Smirl wrote:
> > I haven't received any comments on pci-sysfs-rom-17.patch. Is
> everyone
> > asleep or is it ready to be pushed upstream? I'm still not sure
> that
> > anyone has tried it on a ppc machine.
> 
> I'm now working on something that could use the ROM mapping facility.
> However, I know that I only need the first 64k of the ROM.  Would it
> be reasonable to check the value passed in to pci_map_rom() in *size
> and only automatically set it if it's 0?

pci_map_rom doesn't really consume much in the way of system resources.
64K vs 128KB is just a little address space usage that is freed as soon
as you do pci_unmap_rom().

pci_map_rom_copy() is the only call that consumes significant resource.

=====
Jon Smirl
jonsmirl@yahoo.com


		
__________________________________
Do you Yahoo!?
Yahoo! Mail is new and improved - Check it out!
http://promotions.yahoo.com/new_mail

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-18 18:13             ` Jon Smirl
                                 ` (2 preceding siblings ...)
  2004-08-19 14:01               ` Martin Mares
@ 2004-08-25 15:36               ` Matthew Wilcox
  2004-08-25 15:50                 ` Jon Smirl
  3 siblings, 1 reply; 110+ messages in thread
From: Matthew Wilcox @ 2004-08-25 15:36 UTC (permalink / raw)
  To: Jon Smirl
  Cc: Martin Mares, Pallipadi, Venkatesh, Greg KH, Jesse Barnes,
	linux-pci, Alan Cox, Linux Kernel Mailing List, Petr Vandrovec,
	Benjamin Herrenschmidt

On Wed, Aug 18, 2004 at 11:13:10AM -0700, Jon Smirl wrote:
> I haven't received any comments on pci-sysfs-rom-17.patch. Is everyone
> asleep or is it ready to be pushed upstream? I'm still not sure that
> anyone has tried it on a ppc machine.

I'm now working on something that could use the ROM mapping facility.
However, I know that I only need the first 64k of the ROM.  Would it
be reasonable to check the value passed in to pci_map_rom() in *size
and only automatically set it if it's 0?

-- 
"Next the statesmen will invent cheap lies, putting the blame upon 
the nation that is attacked, and every man will be glad of those
conscience-soothing falsities, and will diligently study them, and refuse
to examine any refutations of them; and thus he will by and by convince 
himself that the war is just, and will thank God for the better sleep 
he enjoys after this process of grotesque self-deception." -- Mark Twain

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-18 18:37               ` Jesse Barnes
@ 2004-08-23 22:51                 ` Greg KH
  2004-08-25 17:32                   ` Jon Smirl
  0 siblings, 1 reply; 110+ messages in thread
From: Greg KH @ 2004-08-23 22:51 UTC (permalink / raw)
  To: Jesse Barnes
  Cc: Jon Smirl, Martin Mares, Pallipadi, Venkatesh, linux-pci,
	Alan Cox, Linux Kernel Mailing List, Petr Vandrovec,
	Benjamin Herrenschmidt

On Wed, Aug 18, 2004 at 02:37:06PM -0400, Jesse Barnes wrote:
> On Wednesday, August 18, 2004 2:13 pm, Jon Smirl wrote:
> > I haven't received any comments on pci-sysfs-rom-17.patch. Is everyone
> > asleep or is it ready to be pushed upstream? I'm still not sure that
> > anyone has tried it on a ppc machine.
> >
> > Since it's small I'll attach it again for your convenience.
> 
> Works for me on ia64.  Greg, is this one ready for your queue?

There are a few minor coding style issues (the initial '{' for a
function needs to be on a new line) and I need a "Signed-off-by:" line,
and it probably will not apply right now due to the pci quirks changes
(if you rediff it against the latest -mm tree, that should fix it.)

Other than that, it looks good to me.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-19 23:11                 ` Jon Smirl
@ 2004-08-20 10:26                   ` Martin Mares
  0 siblings, 0 replies; 110+ messages in thread
From: Martin Mares @ 2004-08-20 10:26 UTC (permalink / raw)
  To: Jon Smirl
  Cc: Pallipadi, Venkatesh, Greg KH, Jesse Barnes, linux-pci, Alan Cox,
	Linux Kernel Mailing List, Petr Vandrovec,
	Benjamin Herrenschmidt

Hello!
 
> The shadowing logic is a must have for laptops. BenH and I discovered
> this the hard way. Some laptops take the system and video ROM images,
> combine them and then compress them into a ROM. At boot time these ROM
> images are decompressed into RAM at C0000 and F0000. For these system
> the only place to get the ROM data for things like video timings is
> from the shadow area. Putting the shadow logic into the shared ROM code
> lets up remove it from all of the video drivers. This lets us write
> video drivers that don't care if the card is primary or secondary.

I understand this and I didn't want to remove the shadow ROM logic,
just to separate it from the real PCI ROM space, the reasons being (1) clarity,
(2) it might be useful to look at both.

				Have a nice fortnight
-- 
Martin `MJ' Mares   <mj@ucw.cz>   http://atrey.karlin.mff.cuni.cz/~mj/
Faculty of Math and Physics, Charles University, Prague, Czech Rep., Earth
Purchasing Windows is an Unrecoverable Application Error.

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-19 14:01               ` Martin Mares
@ 2004-08-19 23:11                 ` Jon Smirl
  2004-08-20 10:26                   ` Martin Mares
  0 siblings, 1 reply; 110+ messages in thread
From: Jon Smirl @ 2004-08-19 23:11 UTC (permalink / raw)
  To: Martin Mares
  Cc: Pallipadi, Venkatesh, Greg KH, Jesse Barnes, linux-pci, Alan Cox,
	Linux Kernel Mailing List, Petr Vandrovec,
	Benjamin Herrenschmidt

--- Martin Mares <mj@ucw.cz> wrote:
> I was thinking again about the VGA ROM's and I tend to believe that
> even if you happen to guess correctly where is the ROM shadowed, it
> would be better to show the _original_ ROM image and deliver the
> shadow copy as a separate file. (If somebody decides to initialize 
> the VGA manually by running the code in the ROM, is the shadow copy
> any better?)

The shadowing logic is a must have for laptops. BenH and I discovered
this the hard way. Some laptops take the system and video ROM images,
combine them and then compress them into a ROM. At boot time these ROM
images are decompressed into RAM at C0000 and F0000. For these system
the only place to get the ROM data for things like video timings is
from the shadow area. Putting the shadow logic into the shared ROM code
lets up remove it from all of the video drivers. This lets us write
video drivers that don't care if the card is primary or secondary.

Video cards that are using the shadow copy don't need to be initially
reset. Since they are your boot display the system BIOS must have
already initialized them. Instead the you need the shadow image since
it may contain the timing data needed to change modes on a laptop display.

=====
Jon Smirl
jonsmirl@yahoo.com


		
__________________________________
Do you Yahoo!?
Yahoo! Mail is new and improved - Check it out!
http://promotions.yahoo.com/new_mail

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-19 12:51               ` Alan Cox
@ 2004-08-19 23:00                 ` Jon Smirl
  0 siblings, 0 replies; 110+ messages in thread
From: Jon Smirl @ 2004-08-19 23:00 UTC (permalink / raw)
  To: Alan Cox
  Cc: Martin Mares, Pallipadi, Venkatesh, Greg KH, Jesse Barnes,
	linux-pci, Linux Kernel Mailing List, Petr Vandrovec,
	Benjamin Herrenschmidt

--- Alan Cox <alan@lxorguk.ukuu.org.uk> wrote:
> On Mer, 2004-08-18 at 19:13, Jon Smirl wrote:
> > I haven't received any comments on pci-sysfs-rom-17.patch. Is
> > everyone asleep or is it ready to be pushed upstream? I'm still 
> > not sure that anyone has tried it on a ppc machine.
> 
> Looks ok to me. Not sure we ever want to copy roms kernel side but
> thats a minor query only.
> 

Drivers for hardware with partial PCI decoding need to at least add a
call to pci_remove_rom() to remove the rom attribute from sysfs. I
don't know which hardware has this problem or even if any hardware with
a Linux driver has this problem. If anyone is maintainer for a driver
like this, please let me know and I can add the call to
pci_remove_rom().

Also, I believe the i386 quirk should also be applied to the x86_64
arch. I don't have the hardware and I don't know enough about the boot
aequence in 64 bits to do the right thing with a guess. The code is not
broken for x86_64, it just may not pick up the right ROM image on that
architecture.


=====
Jon Smirl
jonsmirl@yahoo.com


		
__________________________________
Do you Yahoo!?
Yahoo! Mail - 50x more storage than other providers!
http://promotions.yahoo.com/new_mail

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-18 18:13             ` Jon Smirl
  2004-08-18 18:37               ` Jesse Barnes
  2004-08-19 12:51               ` Alan Cox
@ 2004-08-19 14:01               ` Martin Mares
  2004-08-19 23:11                 ` Jon Smirl
  2004-08-25 15:36               ` Matthew Wilcox
  3 siblings, 1 reply; 110+ messages in thread
From: Martin Mares @ 2004-08-19 14:01 UTC (permalink / raw)
  To: Jon Smirl
  Cc: Pallipadi, Venkatesh, Greg KH, Jesse Barnes, linux-pci, Alan Cox,
	Linux Kernel Mailing List, Petr Vandrovec,
	Benjamin Herrenschmidt

Hello!

> I haven't received any comments on pci-sysfs-rom-17.patch. Is everyone
> asleep or is it ready to be pushed upstream? I'm still not sure that
> anyone has tried it on a ppc machine.

I was away, so sorry for the delay.  The patch looks OK to me now.

I was thinking again about the VGA ROM's and I tend to believe that even if
you happen to guess correctly where is the ROM shadowed, it would be better
to show the _original_ ROM image and deliver the shadow copy as a separate
file. (If somebody decides to initialize the VGA manually by running the
code in the ROM, is the shadow copy any better?)

I think that we can apply the current version of the patch (maybe except the
shadowing logic) and think about these issues later.

				Have a nice fortnight
-- 
Martin `MJ' Mares   <mj@ucw.cz>   http://atrey.karlin.mff.cuni.cz/~mj/
Faculty of Math and Physics, Charles University, Prague, Czech Rep., Earth
"In theory, practice and theory are the same, but in practice they are different." -- Larry McVoy

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-18 18:13             ` Jon Smirl
  2004-08-18 18:37               ` Jesse Barnes
@ 2004-08-19 12:51               ` Alan Cox
  2004-08-19 23:00                 ` Jon Smirl
  2004-08-19 14:01               ` Martin Mares
  2004-08-25 15:36               ` Matthew Wilcox
  3 siblings, 1 reply; 110+ messages in thread
From: Alan Cox @ 2004-08-19 12:51 UTC (permalink / raw)
  To: Jon Smirl
  Cc: Martin Mares, Pallipadi, Venkatesh, Greg KH, Jesse Barnes,
	linux-pci, Linux Kernel Mailing List, Petr Vandrovec,
	Benjamin Herrenschmidt

On Mer, 2004-08-18 at 19:13, Jon Smirl wrote:
> I haven't received any comments on pci-sysfs-rom-17.patch. Is everyone
> asleep or is it ready to be pushed upstream? I'm still not sure that
> anyone has tried it on a ppc machine.

Looks ok to me. Not sure we ever want to copy roms kernel side but thats
a minor query only.


^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-18 18:13             ` Jon Smirl
@ 2004-08-18 18:37               ` Jesse Barnes
  2004-08-23 22:51                 ` Greg KH
  2004-08-19 12:51               ` Alan Cox
                                 ` (2 subsequent siblings)
  3 siblings, 1 reply; 110+ messages in thread
From: Jesse Barnes @ 2004-08-18 18:37 UTC (permalink / raw)
  To: Jon Smirl
  Cc: Martin Mares, Pallipadi, Venkatesh, Greg KH, linux-pci, Alan Cox,
	Linux Kernel Mailing List, Petr Vandrovec,
	Benjamin Herrenschmidt

On Wednesday, August 18, 2004 2:13 pm, Jon Smirl wrote:
> I haven't received any comments on pci-sysfs-rom-17.patch. Is everyone
> asleep or is it ready to be pushed upstream? I'm still not sure that
> anyone has tried it on a ppc machine.
>
> Since it's small I'll attach it again for your convenience.

Works for me on ia64.  Greg, is this one ready for your queue?

Thanks,
Jesse

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-14 22:10           ` Martin Mares
  2004-08-14 23:39             ` Jon Smirl
@ 2004-08-18 18:13             ` Jon Smirl
  2004-08-18 18:37               ` Jesse Barnes
                                 ` (3 more replies)
  1 sibling, 4 replies; 110+ messages in thread
From: Jon Smirl @ 2004-08-18 18:13 UTC (permalink / raw)
  To: Martin Mares
  Cc: Pallipadi, Venkatesh, Greg KH, Jesse Barnes, linux-pci, Alan Cox,
	Linux Kernel Mailing List, Petr Vandrovec,
	Benjamin Herrenschmidt

[-- Attachment #1: Type: text/plain, Size: 420 bytes --]

I haven't received any comments on pci-sysfs-rom-17.patch. Is everyone
asleep or is it ready to be pushed upstream? I'm still not sure that
anyone has tried it on a ppc machine.

Since it's small I'll attach it again for your convenience.

=====
Jon Smirl
jonsmirl@yahoo.com


	
		
__________________________________
Do you Yahoo!?
New and Improved Yahoo! Mail - 100MB free storage!
http://promotions.yahoo.com/new_mail 

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: pci-sysfs-rom-17.patch --]
[-- Type: text/x-patch; name="pci-sysfs-rom-17.patch", Size: 15333 bytes --]

===== arch/i386/pci/fixup.c 1.19 vs edited =====
--- 1.19/arch/i386/pci/fixup.c	Thu Jun  3 10:58:17 2004
+++ edited/arch/i386/pci/fixup.c	Sat Aug 14 15:44:34 2004
@@ -237,6 +237,29 @@
 	}
 }
 
+static void __devinit pci_fixup_video(struct pci_dev *pdev)
+{
+	struct pci_dev *bridge;
+	struct pci_bus *bus;
+	u16 l;
+	
+	if ((pdev->class >> 8) != PCI_CLASS_DISPLAY_VGA)
+		return;
+	       
+	/* Is VGA routed to us? */
+	bus = pdev->bus;
+	while (bus) {
+		bridge = bus->self;
+		if (bridge) {
+			pci_read_config_word(bridge, PCI_BRIDGE_CONTROL, &l);
+			if (!(l & PCI_BRIDGE_CTL_VGA))
+				return;
+		}
+		bus = bus->parent;
+	}
+	pdev->resource[PCI_ROM_RESOURCE].flags |= IORESOURCE_ROM_SHADOW;
+}
+
 struct pci_fixup pcibios_fixups[] = {
 	{
 		.pass		= PCI_FIXUP_HEADER,
@@ -345,6 +368,12 @@
 		.vendor		= PCI_VENDOR_ID_NVIDIA,
 		.device		= PCI_DEVICE_ID_NVIDIA_NFORCE2,
 		.hook		= pci_fixup_nforce2
+	},
+	{
+		.pass		= PCI_FIXUP_HEADER,
+		.vendor		= PCI_ANY_ID,
+		.device		= PCI_ANY_ID,
+		.hook		= pci_fixup_video
 	},
 	{ .pass = 0 }
 };
===== drivers/pci/bus.c 1.9 vs edited =====
--- 1.9/drivers/pci/bus.c	Sat Apr 10 18:27:59 2004
+++ edited/drivers/pci/bus.c	Sat Aug 14 18:17:40 2004
@@ -97,10 +97,6 @@
 		spin_lock(&pci_bus_lock);
 		list_add_tail(&dev->global_list, &pci_devices);
 		spin_unlock(&pci_bus_lock);
-
-		pci_proc_attach_device(dev);
-		pci_create_sysfs_dev_files(dev);
-
 	}
 
 	list_for_each_entry(dev, &bus->devices, bus_list) {
===== drivers/pci/pci-sysfs.c 1.10 vs edited =====
--- 1.10/drivers/pci/pci-sysfs.c	Fri Jun  4 09:23:04 2004
+++ edited/drivers/pci/pci-sysfs.c	Sat Aug 14 19:19:17 2004
@@ -5,6 +5,8 @@
  * (C) Copyright 2002-2004 IBM Corp.
  * (C) Copyright 2003 Matthew Wilcox
  * (C) Copyright 2003 Hewlett-Packard
+ * (C) Copyright 2004 Jon Smirl <jonsmirl@yahoo.com>
+ * (C) Copyright 2004 Silicon Graphics, Inc. Jesse Barnes <jbarnes@sgi.com>
  *
  * File attributes for PCI devices
  *
@@ -164,6 +166,215 @@
 	return count;
 }
 
+/**
+ * pci_enable_rom - enable ROM decoding for a PCI device
+ * @dev: PCI device to enable
+ *
+ * Enable ROM decoding on @dev.  This involves simply turning on the last
+ * bit of the PCI ROM BAR.  Note that some cards may share address decoders
+ * between the ROM and other resources, so enabling it may disable access
+ * to MMIO registers or other card memory.
+ */
+static void
+pci_enable_rom(struct pci_dev *pdev)
+{
+	u32 rom_addr;
+	
+	pci_read_config_dword(pdev, pdev->rom_base_reg, &rom_addr);
+	rom_addr |= PCI_ROM_ADDRESS_ENABLE;
+	pci_write_config_dword(pdev, pdev->rom_base_reg, rom_addr);
+}
+
+/**
+ * pci_disable_rom - disable ROM decoding for a PCI device
+ * @dev: PCI device to disable
+ *
+ * Disable ROM decoding on a PCI device by turning off the last bit in the
+ * ROM BAR.
+ */
+static void
+pci_disable_rom(struct pci_dev *pdev)
+{
+	u32 rom_addr;
+	pci_read_config_dword(pdev, pdev->rom_base_reg, &rom_addr);
+	rom_addr &= ~PCI_ROM_ADDRESS_ENABLE;
+	pci_write_config_dword(pdev, pdev->rom_base_reg, rom_addr);
+}
+
+/**
+ * pci_map_rom - map a PCI ROM to kernel space
+ * @dev: pointer to pci device struct
+ * @size: pointer to receive size of pci window over ROM
+ * @return: kernel virtual pointer to image of ROM
+ *
+ * Map a PCI ROM into kernel space. If ROM is boot video ROM,
+ * the shadow BIOS copy will be returned instead of the 
+ * actual ROM.
+ */
+unsigned char *
+pci_map_rom(struct pci_dev *pdev, size_t *size) {
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+	loff_t start;
+	unsigned char *rom;
+	
+	if (res->flags & IORESOURCE_ROM_SHADOW) {	/* IORESOURCE_ROM_SHADOW only set on x86 */
+		start = (loff_t)0xC0000; 	/* primary video rom always starts here */
+		*size = 0x20000;		/* cover C000:0 through E000:0 */
+	} else if (res->flags & IORESOURCE_ROM_COPY) {
+		*size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
+		return (unsigned char *)pci_resource_start(pdev, PCI_ROM_RESOURCE);
+	} else {
+		/* assign the ROM an address if it doesn't have one */
+		if (res->parent == NULL)
+			pci_assign_resource(pdev, PCI_ROM_RESOURCE);
+
+		start = pci_resource_start(pdev, PCI_ROM_RESOURCE);
+		*size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
+		if (*size == 0)
+			return NULL;
+		
+		/* Enable ROM space decodes */
+		pci_enable_rom(pdev);
+	}
+	
+	rom = ioremap(start, *size);
+	if (!rom) {
+		/* restore enable if ioremap fails */
+		if (!(res->flags & (IORESOURCE_ROM_ENABLE | IORESOURCE_ROM_SHADOW | IORESOURCE_ROM_COPY)))
+			pci_disable_rom(pdev);
+		return NULL;
+	}		
+	/* Standard PCI ROMs start out with these three bytes 55 AA size/512 */
+	if ((*rom == 0x55) && (*(rom + 1) == 0xAA))
+		*size = *(rom + 2) * 512;	/* return true ROM size, not PCI window size */
+		
+	return rom;
+}
+
+/**
+ * pci_map_rom_copy - map a PCI ROM to kernel space, create a copy
+ * @dev: pointer to pci device struct
+ * @size: pointer to receive size of pci window over ROM
+ * @return: kernel virtual pointer to image of ROM
+ *
+ * Map a PCI ROM into kernel space. If ROM is boot video ROM,
+ * the shadow BIOS copy will be returned instead of the 
+ * actual ROM.
+ */
+unsigned char *
+pci_map_rom_copy(struct pci_dev *pdev, size_t *size) {
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+	unsigned char *rom;
+	
+	rom = pci_map_rom(pdev, size);
+	if (!rom)
+		return NULL;
+		
+	if (res->flags & (IORESOURCE_ROM_COPY | IORESOURCE_ROM_SHADOW))
+		return rom;
+		
+	res->start = (unsigned long)kmalloc(*size, GFP_KERNEL);
+	if (!res->start) 
+		return rom;
+
+	res->end = res->start + *size; 
+	memcpy((void*)res->start, rom, *size);
+	pci_unmap_rom(pdev, rom);
+	res->flags |= IORESOURCE_ROM_COPY;
+	
+	return (unsigned char *)res->start;
+}
+
+/**
+ * pci_unmap_rom - unmap the ROM from kernel space
+ * @dev: pointer to pci device struct
+ * @rom: virtual address of the previous mapping
+ *
+ * Remove a mapping of a previously mapped ROM
+ */
+void 
+pci_unmap_rom(struct pci_dev *pdev, unsigned char *rom) {
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+
+	if (res->flags & IORESOURCE_ROM_COPY)
+		return;
+		
+	iounmap(rom);
+		
+	/* Disable again before continuing, leave enabled if pci=rom */
+	if (!(res->flags & (IORESOURCE_ROM_ENABLE | IORESOURCE_ROM_SHADOW)))
+		pci_disable_rom(pdev);
+}
+
+static struct bin_attribute rom_attr;
+
+/**
+ * pci_remove_rom - disable the ROM and remove it's sysfs attribute
+ * @dev: pointer to pci device struct
+ *
+ */
+void 
+pci_remove_rom(struct pci_dev *pdev) 
+{
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+	
+	if (pci_resource_len(pdev, PCI_ROM_RESOURCE))
+		sysfs_remove_bin_file(&pdev->dev.kobj, &rom_attr);
+	if (!(res->flags & (IORESOURCE_ROM_ENABLE | IORESOURCE_ROM_SHADOW | IORESOURCE_ROM_COPY)))
+		pci_disable_rom(pdev);
+}
+
+/**
+ * pci_remove_rom - disable the ROM and remove it's sysfs attribute
+ * @dev: pointer to pci device struct
+ *
+ */
+void 
+pci_cleanup_rom(struct pci_dev *pdev) 
+{
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+	if (res->flags & IORESOURCE_ROM_COPY) {
+		kfree((void*)res->start);
+		res->flags &= ~IORESOURCE_ROM_COPY;
+		res->start = 0;
+		res->end = 0;
+	}
+}
+
+/**
+ * pci_read_rom - read a PCI ROM
+ * @kobj: kernel object handle
+ * @buf: where to put the data we read from the ROM
+ * @off: file offset
+ * @count: number of bytes to read
+ *
+ * Put @count bytes starting at @off into @buf from the ROM in the PCI
+ * device corresponding to @kobj.
+ */
+static ssize_t
+pci_read_rom(struct kobject *kobj, char *buf, loff_t off, size_t count)
+{
+	struct pci_dev *pdev = to_pci_dev(container_of(kobj, struct device, kobj));
+	unsigned char *rom;
+	size_t size;
+	
+	rom = pci_map_rom(pdev, &size);	/* size starts out as PCI window size */
+	if (!rom)
+		return 0;
+		
+	if (off >= size)
+		count = 0;
+	else {
+		if (off + count > size)
+			count = size - off;
+		
+		memcpy_fromio(buf, rom + off, count);
+	}
+	pci_unmap_rom(pdev, rom);
+		
+	return count;
+}
+
 static struct bin_attribute pci_config_attr = {
 	.attr =	{
 		.name = "config",
@@ -192,7 +403,60 @@
 		sysfs_create_bin_file(&pdev->dev.kobj, &pci_config_attr);
 	else
 		sysfs_create_bin_file(&pdev->dev.kobj, &pcie_config_attr);
-
+		
+	/* If the device has a ROM, try to expose it in sysfs. */
+	if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) {
+		struct bin_attribute *rom_attr;
+		
+		rom_attr = kmalloc(sizeof(*rom_attr), GFP_ATOMIC);
+		if (rom_attr) {
+			pdev->rom_attr = rom_attr;
+			rom_attr->size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
+			rom_attr->attr.name = "rom";
+			rom_attr->attr.mode = S_IRUSR;
+			rom_attr->attr.owner = THIS_MODULE;
+			rom_attr->read = pci_read_rom;
+			sysfs_create_bin_file(&pdev->dev.kobj, rom_attr);
+		}
+	}
 	/* add platform-specific attributes */
 	pcibios_add_platform_entries(pdev);
 }
+
+/**
+ * pci_remove_sysfs_dev_files - cleanup PCI specific sysfs files
+ * @pdev: device whose entries we should free
+ *
+ * Cleanup when @pdev is removed from sysfs.
+ */
+void pci_remove_sysfs_dev_files(struct pci_dev *pdev)
+{
+	if (pdev->cfg_size < 4096)
+		sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr);
+	else
+		sysfs_remove_bin_file(&pdev->dev.kobj, &pcie_config_attr);
+
+	if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) {
+		if (pdev->rom_attr) {
+			sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr);
+			kfree(pdev->rom_attr);
+		}
+	}
+}
+
+static int __init pci_sysfs_init(void)
+{
+	struct pci_dev *pdev = NULL;
+	
+	while ((pdev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pdev)) != NULL)
+		pci_create_sysfs_dev_files(pdev);
+
+	return 0;
+}
+
+__initcall(pci_sysfs_init);
+
+EXPORT_SYMBOL(pci_map_rom);
+EXPORT_SYMBOL(pci_map_rom_copy);
+EXPORT_SYMBOL(pci_unmap_rom);
+EXPORT_SYMBOL(pci_remove_rom);
===== drivers/pci/pci.h 1.12 vs edited =====
--- 1.12/drivers/pci/pci.h	Fri Jun  4 09:23:04 2004
+++ edited/drivers/pci/pci.h	Sat Aug 14 01:31:34 2004
@@ -3,6 +3,8 @@
 extern int pci_hotplug (struct device *dev, char **envp, int num_envp,
 			 char *buffer, int buffer_size);
 extern void pci_create_sysfs_dev_files(struct pci_dev *pdev);
+extern void pci_remove_sysfs_dev_files(struct pci_dev *pdev);
+void pci_cleanup_rom(struct pci_dev *dev);
 extern int pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
 				  unsigned long size, unsigned long align,
 				  unsigned long min, unsigned int type_mask,
===== drivers/pci/probe.c 1.65 vs edited =====
--- 1.65/drivers/pci/probe.c	Fri May 21 14:45:27 2004
+++ edited/drivers/pci/probe.c	Sat Aug 14 18:20:35 2004
@@ -170,7 +170,7 @@
 		if (sz && sz != 0xffffffff) {
 			sz = pci_size(l, sz, PCI_ROM_ADDRESS_MASK);
 			if (sz) {
-				res->flags = (l & PCI_ROM_ADDRESS_ENABLE) |
+				res->flags = (l & IORESOURCE_ROM_ENABLE) |
 				  IORESOURCE_MEM | IORESOURCE_PREFETCH |
 				  IORESOURCE_READONLY | IORESOURCE_CACHEABLE;
 				res->start = l & PCI_ROM_ADDRESS_MASK;
===== drivers/pci/proc.c 1.39 vs edited =====
--- 1.39/drivers/pci/proc.c	Sat May 29 05:12:56 2004
+++ edited/drivers/pci/proc.c	Sat Aug 14 19:25:17 2004
@@ -16,7 +16,6 @@
 #include <asm/uaccess.h>
 #include <asm/byteorder.h>
 
-static int proc_initialized;	/* = 0 */
 
 static loff_t
 proc_bus_pci_lseek(struct file *file, loff_t off, int whence)
@@ -387,9 +386,6 @@
 	struct proc_dir_entry *de, *e;
 	char name[16];
 
-	if (!proc_initialized)
-		return -EACCES;
-
 	if (!(de = bus->procdir)) {
 		if (pci_name_bus(name, bus))
 			return -EEXIST;
@@ -425,9 +421,6 @@
 {
 	struct proc_dir_entry *de = bus->procdir;
 
-	if (!proc_initialized)
-		return -EACCES;
-
 	if (!de) {
 		char name[16];
 		sprintf(name, "%02x", bus->number);
@@ -583,6 +576,7 @@
 {
 	return seq_open(file, &proc_bus_pci_devices_op);
 }
+
 static struct file_operations proc_bus_pci_dev_operations = {
 	.open		= proc_bus_pci_dev_open,
 	.read		= seq_read,
@@ -593,16 +587,20 @@
 static int __init pci_proc_init(void)
 {
 	struct proc_dir_entry *entry;
-	struct pci_dev *dev = NULL;
+	struct pci_dev *pdev = NULL;
+
 	proc_bus_pci_dir = proc_mkdir("pci", proc_bus);
+
 	entry = create_proc_entry("devices", 0, proc_bus_pci_dir);
 	if (entry)
 		entry->proc_fops = &proc_bus_pci_dev_operations;
-	proc_initialized = 1;
-	while ((dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
-		pci_proc_attach_device(dev);
+
+	while ((pdev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pdev)) != NULL) {
+		pci_proc_attach_device(pdev);
 	}
+
 	legacy_proc_init();
+
 	return 0;
 }
 
===== drivers/pci/remove.c 1.3 vs edited =====
--- 1.3/drivers/pci/remove.c	Tue Feb  3 12:17:30 2004
+++ edited/drivers/pci/remove.c	Sat Aug 14 18:18:57 2004
@@ -16,6 +16,7 @@
 
  	msi_remove_pci_irq_vectors(dev);
 
+	pci_cleanup_rom(dev);
 	for (i = 0; i < PCI_NUM_RESOURCES; i++) {
 		struct resource *res = dev->resource + i;
 		if (res->parent)
@@ -26,6 +27,7 @@
 static void pci_destroy_dev(struct pci_dev *dev)
 {
 	pci_proc_detach_device(dev);
+	pci_remove_sysfs_dev_files(dev);
 	device_unregister(&dev->dev);
 
 	/* Remove the device from the device lists, and prevent any further
===== drivers/pci/setup-res.c 1.25 vs edited =====
--- 1.25/drivers/pci/setup-res.c	Thu Mar 18 18:40:56 2004
+++ edited/drivers/pci/setup-res.c	Sat Aug 14 18:19:44 2004
@@ -56,7 +56,7 @@
 	if (resno < 6) {
 		reg = PCI_BASE_ADDRESS_0 + 4 * resno;
 	} else if (resno == PCI_ROM_RESOURCE) {
-		new |= res->flags & PCI_ROM_ADDRESS_ENABLE;
+		new |= res->flags & IORESOURCE_ROM_ENABLE;
 		reg = dev->rom_base_reg;
 	} else {
 		/* Hmm, non-standard resource. */
===== include/linux/ioport.h 1.14 vs edited =====
--- 1.14/include/linux/ioport.h	Thu Dec 18 00:48:57 2003
+++ edited/include/linux/ioport.h	Sat Aug 14 00:44:06 2004
@@ -82,6 +82,11 @@
 #define IORESOURCE_MEM_SHADOWABLE	(1<<5)	/* dup: IORESOURCE_SHADOWABLE */
 #define IORESOURCE_MEM_EXPANSIONROM	(1<<6)
 
+/* PCI ROM control bits (IORESOURCE_BITS) */
+#define IORESOURCE_ROM_ENABLE		(1<<0)	/* ROM is enabled, same as PCI_ROM_ADDRESS_ENABLE */
+#define IORESOURCE_ROM_SHADOW		(1<<1)	/* ROM is copy at C000:0 */
+#define IORESOURCE_ROM_COPY		(1<<2)	/* ROM is alloc'd copy, resource field overlaid */
+
 /* PC/ISA/whatever - the normal PC address spaces: IO and memory */
 extern struct resource ioport_resource;
 extern struct resource iomem_resource;
===== include/linux/pci.h 1.132 vs edited =====
--- 1.132/include/linux/pci.h	Mon Aug  2 04:00:43 2004
+++ edited/include/linux/pci.h	Sat Aug 14 00:56:35 2004
@@ -537,6 +537,7 @@
 	unsigned int	is_busmaster:1; /* device is busmaster */
 	
 	unsigned int 	saved_config_space[16]; /* config space saved at suspend time */
+	struct bin_attribute *rom_attr; /* attribute descriptor for sysfs ROM entry */
 #ifdef CONFIG_PCI_NAMES
 #define PCI_NAME_SIZE	96
 #define PCI_NAME_HALF	__stringify(43)	/* less than half to handle slop */
@@ -777,6 +778,12 @@
 int pci_dac_set_dma_mask(struct pci_dev *dev, u64 mask);
 int pci_set_consistent_dma_mask(struct pci_dev *dev, u64 mask);
 int pci_assign_resource(struct pci_dev *dev, int i);
+
+/* ROM control related routines */
+unsigned char *pci_map_rom(struct pci_dev *pdev, size_t *size);
+unsigned char *pci_map_rom_copy(struct pci_dev *pdev, size_t *size);
+void pci_unmap_rom(struct pci_dev *pdev, unsigned char *rom);
+void pci_remove_rom(struct pci_dev *pdev);
 
 /* Power management related routines */
 int pci_save_state(struct pci_dev *dev, u32 *buffer);

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-14 22:10           ` Martin Mares
@ 2004-08-14 23:39             ` Jon Smirl
  2004-08-18 18:13             ` Jon Smirl
  1 sibling, 0 replies; 110+ messages in thread
From: Jon Smirl @ 2004-08-14 23:39 UTC (permalink / raw)
  To: Martin Mares
  Cc: Pallipadi, Venkatesh, Greg KH, Jesse Barnes, linux-pci, Alan Cox,
	Linux Kernel Mailing List, Petr Vandrovec,
	Benjamin Herrenschmidt

[-- Attachment #1: Type: text/plain, Size: 1527 bytes --]

Version 16 was broken. I didn't use the rom_reg_base correctly.

Version 17.
fixes rom_reg_base usage.

Moves pci sysfs initialization to the same stage that /proc/bus/pci
initializes. PCI sysfs was originally initialized before the pci
subsystem was completely running.

While fixing sysfs initialization I noticed that /proc initialization
wasn't optimal so I fixed it too.

When initializing the ROM attribute I now just use the PCI window
length instead of reading the true length from the ROM. If the ROM is
copied it will use the true length. This avoids touching the ROM in
case of partial decoders.

> > The system shadow copy is one of the main motivators for this code.
> > On some laptops the system and video ROMs are compressed. On these
> > systems the boot ROM decompresses these ROM into the shadow memory
> > at  C0000-100000.
> 
> Is there any reliable way to find the address and size of such a
> shadow image or we need to stick with the 0xc0000 + 256KB range?

For the size use the (55 AA len) at the front of the ROM. This has to
work since this is how the PC system BIOS finds expansion ROMs.

For the address there are only two reliable addresses, C0000 for the
video ROM and F0000 for the system ROM. There may be other shadow ROMs
in the C0000-10000 range but I don't know of a way to tell what
hardware they belong to.

=====
Jon Smirl
jonsmirl@yahoo.com


	
		
__________________________________
Do you Yahoo!?
New and Improved Yahoo! Mail - 100MB free storage!
http://promotions.yahoo.com/new_mail 

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: pci-sysfs-rom-17.patch --]
[-- Type: text/x-patch; name="pci-sysfs-rom-17.patch", Size: 15333 bytes --]

===== arch/i386/pci/fixup.c 1.19 vs edited =====
--- 1.19/arch/i386/pci/fixup.c	Thu Jun  3 10:58:17 2004
+++ edited/arch/i386/pci/fixup.c	Sat Aug 14 15:44:34 2004
@@ -237,6 +237,29 @@
 	}
 }
 
+static void __devinit pci_fixup_video(struct pci_dev *pdev)
+{
+	struct pci_dev *bridge;
+	struct pci_bus *bus;
+	u16 l;
+	
+	if ((pdev->class >> 8) != PCI_CLASS_DISPLAY_VGA)
+		return;
+	       
+	/* Is VGA routed to us? */
+	bus = pdev->bus;
+	while (bus) {
+		bridge = bus->self;
+		if (bridge) {
+			pci_read_config_word(bridge, PCI_BRIDGE_CONTROL, &l);
+			if (!(l & PCI_BRIDGE_CTL_VGA))
+				return;
+		}
+		bus = bus->parent;
+	}
+	pdev->resource[PCI_ROM_RESOURCE].flags |= IORESOURCE_ROM_SHADOW;
+}
+
 struct pci_fixup pcibios_fixups[] = {
 	{
 		.pass		= PCI_FIXUP_HEADER,
@@ -345,6 +368,12 @@
 		.vendor		= PCI_VENDOR_ID_NVIDIA,
 		.device		= PCI_DEVICE_ID_NVIDIA_NFORCE2,
 		.hook		= pci_fixup_nforce2
+	},
+	{
+		.pass		= PCI_FIXUP_HEADER,
+		.vendor		= PCI_ANY_ID,
+		.device		= PCI_ANY_ID,
+		.hook		= pci_fixup_video
 	},
 	{ .pass = 0 }
 };
===== drivers/pci/bus.c 1.9 vs edited =====
--- 1.9/drivers/pci/bus.c	Sat Apr 10 18:27:59 2004
+++ edited/drivers/pci/bus.c	Sat Aug 14 18:17:40 2004
@@ -97,10 +97,6 @@
 		spin_lock(&pci_bus_lock);
 		list_add_tail(&dev->global_list, &pci_devices);
 		spin_unlock(&pci_bus_lock);
-
-		pci_proc_attach_device(dev);
-		pci_create_sysfs_dev_files(dev);
-
 	}
 
 	list_for_each_entry(dev, &bus->devices, bus_list) {
===== drivers/pci/pci-sysfs.c 1.10 vs edited =====
--- 1.10/drivers/pci/pci-sysfs.c	Fri Jun  4 09:23:04 2004
+++ edited/drivers/pci/pci-sysfs.c	Sat Aug 14 19:19:17 2004
@@ -5,6 +5,8 @@
  * (C) Copyright 2002-2004 IBM Corp.
  * (C) Copyright 2003 Matthew Wilcox
  * (C) Copyright 2003 Hewlett-Packard
+ * (C) Copyright 2004 Jon Smirl <jonsmirl@yahoo.com>
+ * (C) Copyright 2004 Silicon Graphics, Inc. Jesse Barnes <jbarnes@sgi.com>
  *
  * File attributes for PCI devices
  *
@@ -164,6 +166,215 @@
 	return count;
 }
 
+/**
+ * pci_enable_rom - enable ROM decoding for a PCI device
+ * @dev: PCI device to enable
+ *
+ * Enable ROM decoding on @dev.  This involves simply turning on the last
+ * bit of the PCI ROM BAR.  Note that some cards may share address decoders
+ * between the ROM and other resources, so enabling it may disable access
+ * to MMIO registers or other card memory.
+ */
+static void
+pci_enable_rom(struct pci_dev *pdev)
+{
+	u32 rom_addr;
+	
+	pci_read_config_dword(pdev, pdev->rom_base_reg, &rom_addr);
+	rom_addr |= PCI_ROM_ADDRESS_ENABLE;
+	pci_write_config_dword(pdev, pdev->rom_base_reg, rom_addr);
+}
+
+/**
+ * pci_disable_rom - disable ROM decoding for a PCI device
+ * @dev: PCI device to disable
+ *
+ * Disable ROM decoding on a PCI device by turning off the last bit in the
+ * ROM BAR.
+ */
+static void
+pci_disable_rom(struct pci_dev *pdev)
+{
+	u32 rom_addr;
+	pci_read_config_dword(pdev, pdev->rom_base_reg, &rom_addr);
+	rom_addr &= ~PCI_ROM_ADDRESS_ENABLE;
+	pci_write_config_dword(pdev, pdev->rom_base_reg, rom_addr);
+}
+
+/**
+ * pci_map_rom - map a PCI ROM to kernel space
+ * @dev: pointer to pci device struct
+ * @size: pointer to receive size of pci window over ROM
+ * @return: kernel virtual pointer to image of ROM
+ *
+ * Map a PCI ROM into kernel space. If ROM is boot video ROM,
+ * the shadow BIOS copy will be returned instead of the 
+ * actual ROM.
+ */
+unsigned char *
+pci_map_rom(struct pci_dev *pdev, size_t *size) {
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+	loff_t start;
+	unsigned char *rom;
+	
+	if (res->flags & IORESOURCE_ROM_SHADOW) {	/* IORESOURCE_ROM_SHADOW only set on x86 */
+		start = (loff_t)0xC0000; 	/* primary video rom always starts here */
+		*size = 0x20000;		/* cover C000:0 through E000:0 */
+	} else if (res->flags & IORESOURCE_ROM_COPY) {
+		*size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
+		return (unsigned char *)pci_resource_start(pdev, PCI_ROM_RESOURCE);
+	} else {
+		/* assign the ROM an address if it doesn't have one */
+		if (res->parent == NULL)
+			pci_assign_resource(pdev, PCI_ROM_RESOURCE);
+
+		start = pci_resource_start(pdev, PCI_ROM_RESOURCE);
+		*size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
+		if (*size == 0)
+			return NULL;
+		
+		/* Enable ROM space decodes */
+		pci_enable_rom(pdev);
+	}
+	
+	rom = ioremap(start, *size);
+	if (!rom) {
+		/* restore enable if ioremap fails */
+		if (!(res->flags & (IORESOURCE_ROM_ENABLE | IORESOURCE_ROM_SHADOW | IORESOURCE_ROM_COPY)))
+			pci_disable_rom(pdev);
+		return NULL;
+	}		
+	/* Standard PCI ROMs start out with these three bytes 55 AA size/512 */
+	if ((*rom == 0x55) && (*(rom + 1) == 0xAA))
+		*size = *(rom + 2) * 512;	/* return true ROM size, not PCI window size */
+		
+	return rom;
+}
+
+/**
+ * pci_map_rom_copy - map a PCI ROM to kernel space, create a copy
+ * @dev: pointer to pci device struct
+ * @size: pointer to receive size of pci window over ROM
+ * @return: kernel virtual pointer to image of ROM
+ *
+ * Map a PCI ROM into kernel space. If ROM is boot video ROM,
+ * the shadow BIOS copy will be returned instead of the 
+ * actual ROM.
+ */
+unsigned char *
+pci_map_rom_copy(struct pci_dev *pdev, size_t *size) {
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+	unsigned char *rom;
+	
+	rom = pci_map_rom(pdev, size);
+	if (!rom)
+		return NULL;
+		
+	if (res->flags & (IORESOURCE_ROM_COPY | IORESOURCE_ROM_SHADOW))
+		return rom;
+		
+	res->start = (unsigned long)kmalloc(*size, GFP_KERNEL);
+	if (!res->start) 
+		return rom;
+
+	res->end = res->start + *size; 
+	memcpy((void*)res->start, rom, *size);
+	pci_unmap_rom(pdev, rom);
+	res->flags |= IORESOURCE_ROM_COPY;
+	
+	return (unsigned char *)res->start;
+}
+
+/**
+ * pci_unmap_rom - unmap the ROM from kernel space
+ * @dev: pointer to pci device struct
+ * @rom: virtual address of the previous mapping
+ *
+ * Remove a mapping of a previously mapped ROM
+ */
+void 
+pci_unmap_rom(struct pci_dev *pdev, unsigned char *rom) {
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+
+	if (res->flags & IORESOURCE_ROM_COPY)
+		return;
+		
+	iounmap(rom);
+		
+	/* Disable again before continuing, leave enabled if pci=rom */
+	if (!(res->flags & (IORESOURCE_ROM_ENABLE | IORESOURCE_ROM_SHADOW)))
+		pci_disable_rom(pdev);
+}
+
+static struct bin_attribute rom_attr;
+
+/**
+ * pci_remove_rom - disable the ROM and remove it's sysfs attribute
+ * @dev: pointer to pci device struct
+ *
+ */
+void 
+pci_remove_rom(struct pci_dev *pdev) 
+{
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+	
+	if (pci_resource_len(pdev, PCI_ROM_RESOURCE))
+		sysfs_remove_bin_file(&pdev->dev.kobj, &rom_attr);
+	if (!(res->flags & (IORESOURCE_ROM_ENABLE | IORESOURCE_ROM_SHADOW | IORESOURCE_ROM_COPY)))
+		pci_disable_rom(pdev);
+}
+
+/**
+ * pci_remove_rom - disable the ROM and remove it's sysfs attribute
+ * @dev: pointer to pci device struct
+ *
+ */
+void 
+pci_cleanup_rom(struct pci_dev *pdev) 
+{
+	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+	if (res->flags & IORESOURCE_ROM_COPY) {
+		kfree((void*)res->start);
+		res->flags &= ~IORESOURCE_ROM_COPY;
+		res->start = 0;
+		res->end = 0;
+	}
+}
+
+/**
+ * pci_read_rom - read a PCI ROM
+ * @kobj: kernel object handle
+ * @buf: where to put the data we read from the ROM
+ * @off: file offset
+ * @count: number of bytes to read
+ *
+ * Put @count bytes starting at @off into @buf from the ROM in the PCI
+ * device corresponding to @kobj.
+ */
+static ssize_t
+pci_read_rom(struct kobject *kobj, char *buf, loff_t off, size_t count)
+{
+	struct pci_dev *pdev = to_pci_dev(container_of(kobj, struct device, kobj));
+	unsigned char *rom;
+	size_t size;
+	
+	rom = pci_map_rom(pdev, &size);	/* size starts out as PCI window size */
+	if (!rom)
+		return 0;
+		
+	if (off >= size)
+		count = 0;
+	else {
+		if (off + count > size)
+			count = size - off;
+		
+		memcpy_fromio(buf, rom + off, count);
+	}
+	pci_unmap_rom(pdev, rom);
+		
+	return count;
+}
+
 static struct bin_attribute pci_config_attr = {
 	.attr =	{
 		.name = "config",
@@ -192,7 +403,60 @@
 		sysfs_create_bin_file(&pdev->dev.kobj, &pci_config_attr);
 	else
 		sysfs_create_bin_file(&pdev->dev.kobj, &pcie_config_attr);
-
+		
+	/* If the device has a ROM, try to expose it in sysfs. */
+	if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) {
+		struct bin_attribute *rom_attr;
+		
+		rom_attr = kmalloc(sizeof(*rom_attr), GFP_ATOMIC);
+		if (rom_attr) {
+			pdev->rom_attr = rom_attr;
+			rom_attr->size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
+			rom_attr->attr.name = "rom";
+			rom_attr->attr.mode = S_IRUSR;
+			rom_attr->attr.owner = THIS_MODULE;
+			rom_attr->read = pci_read_rom;
+			sysfs_create_bin_file(&pdev->dev.kobj, rom_attr);
+		}
+	}
 	/* add platform-specific attributes */
 	pcibios_add_platform_entries(pdev);
 }
+
+/**
+ * pci_remove_sysfs_dev_files - cleanup PCI specific sysfs files
+ * @pdev: device whose entries we should free
+ *
+ * Cleanup when @pdev is removed from sysfs.
+ */
+void pci_remove_sysfs_dev_files(struct pci_dev *pdev)
+{
+	if (pdev->cfg_size < 4096)
+		sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr);
+	else
+		sysfs_remove_bin_file(&pdev->dev.kobj, &pcie_config_attr);
+
+	if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) {
+		if (pdev->rom_attr) {
+			sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr);
+			kfree(pdev->rom_attr);
+		}
+	}
+}
+
+static int __init pci_sysfs_init(void)
+{
+	struct pci_dev *pdev = NULL;
+	
+	while ((pdev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pdev)) != NULL)
+		pci_create_sysfs_dev_files(pdev);
+
+	return 0;
+}
+
+__initcall(pci_sysfs_init);
+
+EXPORT_SYMBOL(pci_map_rom);
+EXPORT_SYMBOL(pci_map_rom_copy);
+EXPORT_SYMBOL(pci_unmap_rom);
+EXPORT_SYMBOL(pci_remove_rom);
===== drivers/pci/pci.h 1.12 vs edited =====
--- 1.12/drivers/pci/pci.h	Fri Jun  4 09:23:04 2004
+++ edited/drivers/pci/pci.h	Sat Aug 14 01:31:34 2004
@@ -3,6 +3,8 @@
 extern int pci_hotplug (struct device *dev, char **envp, int num_envp,
 			 char *buffer, int buffer_size);
 extern void pci_create_sysfs_dev_files(struct pci_dev *pdev);
+extern void pci_remove_sysfs_dev_files(struct pci_dev *pdev);
+void pci_cleanup_rom(struct pci_dev *dev);
 extern int pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
 				  unsigned long size, unsigned long align,
 				  unsigned long min, unsigned int type_mask,
===== drivers/pci/probe.c 1.65 vs edited =====
--- 1.65/drivers/pci/probe.c	Fri May 21 14:45:27 2004
+++ edited/drivers/pci/probe.c	Sat Aug 14 18:20:35 2004
@@ -170,7 +170,7 @@
 		if (sz && sz != 0xffffffff) {
 			sz = pci_size(l, sz, PCI_ROM_ADDRESS_MASK);
 			if (sz) {
-				res->flags = (l & PCI_ROM_ADDRESS_ENABLE) |
+				res->flags = (l & IORESOURCE_ROM_ENABLE) |
 				  IORESOURCE_MEM | IORESOURCE_PREFETCH |
 				  IORESOURCE_READONLY | IORESOURCE_CACHEABLE;
 				res->start = l & PCI_ROM_ADDRESS_MASK;
===== drivers/pci/proc.c 1.39 vs edited =====
--- 1.39/drivers/pci/proc.c	Sat May 29 05:12:56 2004
+++ edited/drivers/pci/proc.c	Sat Aug 14 19:25:17 2004
@@ -16,7 +16,6 @@
 #include <asm/uaccess.h>
 #include <asm/byteorder.h>
 
-static int proc_initialized;	/* = 0 */
 
 static loff_t
 proc_bus_pci_lseek(struct file *file, loff_t off, int whence)
@@ -387,9 +386,6 @@
 	struct proc_dir_entry *de, *e;
 	char name[16];
 
-	if (!proc_initialized)
-		return -EACCES;
-
 	if (!(de = bus->procdir)) {
 		if (pci_name_bus(name, bus))
 			return -EEXIST;
@@ -425,9 +421,6 @@
 {
 	struct proc_dir_entry *de = bus->procdir;
 
-	if (!proc_initialized)
-		return -EACCES;
-
 	if (!de) {
 		char name[16];
 		sprintf(name, "%02x", bus->number);
@@ -583,6 +576,7 @@
 {
 	return seq_open(file, &proc_bus_pci_devices_op);
 }
+
 static struct file_operations proc_bus_pci_dev_operations = {
 	.open		= proc_bus_pci_dev_open,
 	.read		= seq_read,
@@ -593,16 +587,20 @@
 static int __init pci_proc_init(void)
 {
 	struct proc_dir_entry *entry;
-	struct pci_dev *dev = NULL;
+	struct pci_dev *pdev = NULL;
+
 	proc_bus_pci_dir = proc_mkdir("pci", proc_bus);
+
 	entry = create_proc_entry("devices", 0, proc_bus_pci_dir);
 	if (entry)
 		entry->proc_fops = &proc_bus_pci_dev_operations;
-	proc_initialized = 1;
-	while ((dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
-		pci_proc_attach_device(dev);
+
+	while ((pdev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pdev)) != NULL) {
+		pci_proc_attach_device(pdev);
 	}
+
 	legacy_proc_init();
+
 	return 0;
 }
 
===== drivers/pci/remove.c 1.3 vs edited =====
--- 1.3/drivers/pci/remove.c	Tue Feb  3 12:17:30 2004
+++ edited/drivers/pci/remove.c	Sat Aug 14 18:18:57 2004
@@ -16,6 +16,7 @@
 
  	msi_remove_pci_irq_vectors(dev);
 
+	pci_cleanup_rom(dev);
 	for (i = 0; i < PCI_NUM_RESOURCES; i++) {
 		struct resource *res = dev->resource + i;
 		if (res->parent)
@@ -26,6 +27,7 @@
 static void pci_destroy_dev(struct pci_dev *dev)
 {
 	pci_proc_detach_device(dev);
+	pci_remove_sysfs_dev_files(dev);
 	device_unregister(&dev->dev);
 
 	/* Remove the device from the device lists, and prevent any further
===== drivers/pci/setup-res.c 1.25 vs edited =====
--- 1.25/drivers/pci/setup-res.c	Thu Mar 18 18:40:56 2004
+++ edited/drivers/pci/setup-res.c	Sat Aug 14 18:19:44 2004
@@ -56,7 +56,7 @@
 	if (resno < 6) {
 		reg = PCI_BASE_ADDRESS_0 + 4 * resno;
 	} else if (resno == PCI_ROM_RESOURCE) {
-		new |= res->flags & PCI_ROM_ADDRESS_ENABLE;
+		new |= res->flags & IORESOURCE_ROM_ENABLE;
 		reg = dev->rom_base_reg;
 	} else {
 		/* Hmm, non-standard resource. */
===== include/linux/ioport.h 1.14 vs edited =====
--- 1.14/include/linux/ioport.h	Thu Dec 18 00:48:57 2003
+++ edited/include/linux/ioport.h	Sat Aug 14 00:44:06 2004
@@ -82,6 +82,11 @@
 #define IORESOURCE_MEM_SHADOWABLE	(1<<5)	/* dup: IORESOURCE_SHADOWABLE */
 #define IORESOURCE_MEM_EXPANSIONROM	(1<<6)
 
+/* PCI ROM control bits (IORESOURCE_BITS) */
+#define IORESOURCE_ROM_ENABLE		(1<<0)	/* ROM is enabled, same as PCI_ROM_ADDRESS_ENABLE */
+#define IORESOURCE_ROM_SHADOW		(1<<1)	/* ROM is copy at C000:0 */
+#define IORESOURCE_ROM_COPY		(1<<2)	/* ROM is alloc'd copy, resource field overlaid */
+
 /* PC/ISA/whatever - the normal PC address spaces: IO and memory */
 extern struct resource ioport_resource;
 extern struct resource iomem_resource;
===== include/linux/pci.h 1.132 vs edited =====
--- 1.132/include/linux/pci.h	Mon Aug  2 04:00:43 2004
+++ edited/include/linux/pci.h	Sat Aug 14 00:56:35 2004
@@ -537,6 +537,7 @@
 	unsigned int	is_busmaster:1; /* device is busmaster */
 	
 	unsigned int 	saved_config_space[16]; /* config space saved at suspend time */
+	struct bin_attribute *rom_attr; /* attribute descriptor for sysfs ROM entry */
 #ifdef CONFIG_PCI_NAMES
 #define PCI_NAME_SIZE	96
 #define PCI_NAME_HALF	__stringify(43)	/* less than half to handle slop */
@@ -777,6 +778,12 @@
 int pci_dac_set_dma_mask(struct pci_dev *dev, u64 mask);
 int pci_set_consistent_dma_mask(struct pci_dev *dev, u64 mask);
 int pci_assign_resource(struct pci_dev *dev, int i);
+
+/* ROM control related routines */
+unsigned char *pci_map_rom(struct pci_dev *pdev, size_t *size);
+unsigned char *pci_map_rom_copy(struct pci_dev *pdev, size_t *size);
+void pci_unmap_rom(struct pci_dev *pdev, unsigned char *rom);
+void pci_remove_rom(struct pci_dev *pdev);
 
 /* Power management related routines */
 int pci_save_state(struct pci_dev *dev, u32 *buffer);

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-14 14:17         ` Jon Smirl
@ 2004-08-14 22:10           ` Martin Mares
  2004-08-14 23:39             ` Jon Smirl
  2004-08-18 18:13             ` Jon Smirl
  0 siblings, 2 replies; 110+ messages in thread
From: Martin Mares @ 2004-08-14 22:10 UTC (permalink / raw)
  To: Jon Smirl
  Cc: Pallipadi, Venkatesh, Greg KH, Jesse Barnes, linux-pci, Alan Cox,
	Linux Kernel Mailing List, Petr Vandrovec,
	Benjamin Herrenschmidt

Hello!

> That's why it works right now without the assign_resource. The BIOS has
> already set everything up.

Yes, it happens on some machines, but by far not on all.

> The code that accesses the ROM runs very early, before anything else is
> loaded. The only conflict I can think of is from the on-board BIOS of a
> disk cotroller with partial decoding. But I'm not sure if a care like
> that exists.

No, conflict can happen whenever the ROM and the BAR's are enabled
at the same time. The BAR's are usually already enabled when your code runs.

You either should disable the BAR's for a while or, much better, avoid
touching the ROM base register until somebody explicitly requests it.

I know the devices with shared decoders are rare, but I still prefer
a kernel which, given such a device, works fine if you don't touch
the sysfs files, to a kernel, which crashes during boot under such
circumstances.

Hence it is much better to avoid scanning the ROM for the real size
unless you are copying it. Using the size of the resource for the
sysfs file doesn't hurt anybody as the file itself shouldn't eat up
any space.

> If a card like this exists, I need to make the code use the BIOS SHADOW
> copy of the ROM.

Beware, such a copy needn't exist, you need to create your own shadow copy.

> The system shadow copy is one of the main motivators for this code. On
> some laptop the system and video ROMs are compressed. On these systems
> the boot ROM decompresses these ROM into the shadow memory at
> C0000-100000.

Is there any reliable way to find the address and size of such a shadow
image or we need to stick with the 0xc0000 + 256KB range?

				Have a nice fortnight
-- 
Martin `MJ' Mares   <mj@ucw.cz>   http://atrey.karlin.mff.cuni.cz/~mj/
Faculty of Math and Physics, Charles University, Prague, Czech Rep., Earth
A Bash poem: time for echo in canyon; do echo $echo $echo; done

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-14  9:47       ` Martin Mares
@ 2004-08-14 14:17         ` Jon Smirl
  2004-08-14 22:10           ` Martin Mares
  0 siblings, 1 reply; 110+ messages in thread
From: Jon Smirl @ 2004-08-14 14:17 UTC (permalink / raw)
  To: Martin Mares
  Cc: Pallipadi, Venkatesh, Greg KH, Jesse Barnes, linux-pci, Alan Cox,
	Linux Kernel Mailing List, Petr Vandrovec,
	Benjamin Herrenschmidt

--- Martin Mares <mj@ucw.cz> wrote:
> Hello!
> 
> > I removed the call to pci_assign_resource(). The sysfs attribute
> code
> > builds the attributes before the pci subsystem is fully
> initialized.
> > specifically before arch pcibios_init() has been called. If
> > pci_assign_resource() is called for the ROM before pcibios_init()
> the
> > kernel's resource maps have not been built yet. This will result in
> the
> > ROM being assigned a location on top of the framebuffer; as soon as
> it
> > is enabled the system will lock. Right now the code relies on the
> BIOS
> > getting the ROM address set up right. If we can figure out how to
> > initialize the sysfs attributes after pcibios_init() then I can put
> the
> > assign call back.
> 
> If I understand correctly, you don't need that. Just don't map the
> ROM before somebody explicitly requests it -- be it either some 
> driver or sysfs. In both cases, the PCI subsystem is already 
> initialized.

That's why it works right now without the assign_resource. The BIOS has
already set everything up.

> > I think the decoder sharing problem is being blown out of
> proportion
> > there are very few boards with this problem.
> 
> Yes, but it's no excuse for writing code which triggers these
> problems if there is an easy way how to do that more safely.

The code that accesses the ROM runs very early, before anything else is
loaded. The only conflict I can think of is from the on-board BIOS of a
disk cotroller with partial decoding. But I'm not sure if a care like
that exists.

If a card like this exists, I need to make the code use the BIOS SHADOW
copy of the ROM.

> 
> > Also, one card I have has a 256MB PCI window over a 48KB ROM. If I
> use
> > the window size instead of true size for a copy I would waste a lot
> of
> > memory.
> 
> BTW, if the card doesn't share decoders, is there any need to create
> a copy of the ROM? If not, no memory gets wasted.

The copy is triggered manually by the device driver with partial
decoding. When the driver loads loads and it is safe to access the ROM,
call pci_map_rom_copy() this will trigger the copy. Alternatively it
can call pci_rom_remove() which will remove the rom sysfs attribute.

Normal hardware needs to do nothing. If a normal device driver wants to
access the ROM call pci_map_rom(). This will automatically return the
actual ROM or the BIOS shadow copy as needed.

There are two times the ROM can get copied:
1) manually when a driver knows it has partial PCI decoding and calls
pci_map_rom_copy()
2) By the system BIOS. PC system BIOS' automatically shadow all of the
roms located from C0000 to 1000000 into RAM. If this copy exists I use
it.

The system shadow copy is one of the main motivators for this code. On
some laptop the system and video ROMs are compressed. On these systems
the boot ROM decompresses these ROM into the shadow memory at
C0000-100000.


> 
> 				Have a nice fortnight
> -- 
> Martin `MJ' Mares   <mj@ucw.cz>  
> http://atrey.karlin.mff.cuni.cz/~mj/
> Faculty of Math and Physics, Charles University, Prague, Czech Rep.,
> Earth
> How an engineer writes a program: Start by debugging an empty file...
> 


=====
Jon Smirl
jonsmirl@yahoo.com


		
__________________________________
Do you Yahoo!?
Yahoo! Mail - 50x more storage than other providers!
http://promotions.yahoo.com/new_mail

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-14  5:34     ` Jon Smirl
@ 2004-08-14  9:47       ` Martin Mares
  2004-08-14 14:17         ` Jon Smirl
  0 siblings, 1 reply; 110+ messages in thread
From: Martin Mares @ 2004-08-14  9:47 UTC (permalink / raw)
  To: Jon Smirl
  Cc: Pallipadi, Venkatesh, Greg KH, Jesse Barnes, linux-pci, Alan Cox,
	Linux Kernel Mailing List, Petr Vandrovec,
	Benjamin Herrenschmidt

Hello!

> I removed the call to pci_assign_resource(). The sysfs attribute code
> builds the attributes before the pci subsystem is fully initialized.
> specifically before arch pcibios_init() has been called. If
> pci_assign_resource() is called for the ROM before pcibios_init() the
> kernel's resource maps have not been built yet. This will result in the
> ROM being assigned a location on top of the framebuffer; as soon as it
> is enabled the system will lock. Right now the code relies on the BIOS
> getting the ROM address set up right. If we can figure out how to
> initialize the sysfs attributes after pcibios_init() then I can put the
> assign call back.

If I understand correctly, you don't need that. Just don't map the ROM
before somebody explicitly requests it -- be it either some driver
or sysfs. In both cases, the PCI subsystem is already initialized.

> I think the decoder sharing problem is being blown out of proportion
> there are very few boards with this problem.

Yes, but it's no excuse for writing code which triggers these problems
if there is an easy way how to do that more safely.

> Also, one card I have has a 256MB PCI window over a 48KB ROM. If I use
> the window size instead of true size for a copy I would waste a lot of
> memory.

BTW, if the card doesn't share decoders, is there any need to create
a copy of the ROM? If not, no memory gets wasted.

				Have a nice fortnight
-- 
Martin `MJ' Mares   <mj@ucw.cz>   http://atrey.karlin.mff.cuni.cz/~mj/
Faculty of Math and Physics, Charles University, Prague, Czech Rep., Earth
How an engineer writes a program: Start by debugging an empty file...

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-13 18:17   ` Martin Mares
@ 2004-08-14  5:34     ` Jon Smirl
  2004-08-14  9:47       ` Martin Mares
  0 siblings, 1 reply; 110+ messages in thread
From: Jon Smirl @ 2004-08-14  5:34 UTC (permalink / raw)
  To: Martin Mares
  Cc: Pallipadi, Venkatesh, Greg KH, Jesse Barnes, linux-pci, Alan Cox,
	Linux Kernel Mailing List, Petr Vandrovec,
	Benjamin Herrenschmidt

[-- Attachment #1: Type: text/plain, Size: 5546 bytes --]

New version 16 addressing some of the issues below...

--- Martin Mares <mj@ucw.cz> wrote:
> Hello!
> 
> Just a couple of notes about your patch:
> 
> +unsigned char *
> +pci_map_rom(struct pci_dev *dev, size_t *size) {
> +	struct resource *res = &dev->resource[PCI_ROM_RESOURCE];
> +	loff_t start;
> +	unsigned char *rom;
> +	
> +	if (res->flags & PCI_ROM_SHADOW) {	/* PCI_ROM_SHADOW only set on
> x86 */
> +		start = (loff_t)0xC0000; 	/* primary video rom always starts here
> */
> +		*size = 0x20000;		/* cover C000:0 through E000:0 */
> 
> Shouldn't we do this only if we find that the device has a ROM
> resource?

pci_map_rom() is called from two places, a driver that wants to map
it's own ROM so it knows it has one or during startup. The startup call
has a check to see if there is a ROM resource before calling
pci_map_rom().

> 
> +	} else if (res->flags & PCI_ROM_COPY) {
> +		*size = pci_resource_len(dev, PCI_ROM_RESOURCE);
> +		return (unsigned char *)pci_resource_start(dev, PCI_ROM_RESOURCE);
> +	} else {
> +		start = pci_resource_start(dev, PCI_ROM_RESOURCE);
> +		*size = pci_resource_len(dev, PCI_ROM_RESOURCE);
> +		if (*size == 0)
> +			return NULL;
> +		
> +		/* Enable ROM space decodes */
> +		pci_enable_rom(dev);
> +	}
> 
> This seems to be wrong -- before enabling a resource, you must call
> request_resource() on it and make sure that the ROM (1) has an
> address allocated, and (2) the address is not used by anything 
> else.

I sent this out before and didn't get any replies....

Any ideas on how to solve this problem...

Originally I had this in the enable ROM routine
/* assign the ROM an address if it doesn't have one */
if (r->parent == NULL)
   pci_assign_resource(dev->pdev, PCI_ROM_RESOURCE);

I removed the call to pci_assign_resource(). The sysfs attribute code
builds the attributes before the pci subsystem is fully initialized.
specifically before arch pcibios_init() has been called. If
pci_assign_resource() is called for the ROM before pcibios_init() the
kernel's resource maps have not been built yet. This will result in the
ROM being assigned a location on top of the framebuffer; as soon as it
is enabled the system will lock. Right now the code relies on the BIOS
getting the ROM address set up right. If we can figure out how to
initialize the sysfs attributes after pcibios_init() then I can put the
assign call back.

> 
> +	/* If the device has a ROM, try to expose it in sysfs. */
> +	if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) {
> +		unsigned char *rom;
> +		struct bin_attribute *rom_attr;
> +		
> +		pdev->rom_attr = NULL;
> +		rom_attr = kmalloc(sizeof(*rom_attr), GFP_ATOMIC);
> +		if (rom_attr) {
> +			/* set default size */
> +			rom_attr->size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
> +			/* get true size if possible */
> +			rom = pci_map_rom(pdev, &rom_attr->size);
> 
> As we can never be sure about the decoder sharing, it is very wise
> not to touch the ROM BAR until somebody accesses the sysfs file.
Using
> size according to the PCI config space (i.e., the resource) does not
> harm anybody.
> 
This code is running before any device drivers are loaded so they can
be using the ROM. The other possibility is the boot video device but
for that ROM we use the shadow copy.

I think the decoder sharing problem is being blown out of proportion
there are very few boards with this problem. The only conceivable
problem I can see is booting off from a SCSI controller ROM where the
controller has this problem. In this case there will be a copy shadow
copy of the ROM. If anyone has hardware that fits this scenario, let me
know and I can adjust the shadow code for it.

Also, one card I have has a 256MB PCI window over a 48KB ROM. If I use
the window size instead of true size for a copy I would waste a lot of
memory.

> +	if (dev->hdr_type == PCI_HEADER_TYPE_NORMAL) {
> +		res = &dev->resource[PCI_ROM_RESOURCE];
> +		if (res->flags & PCI_ROM_COPY) {
> +			kfree((void*)res->start);
> +			res->flags &= !PCI_ROM_COPY;
> 
> ~, not !
You're right. I fixed it.
> 
> +			res->start = 0;
> +			res->end = 0;
> +		}
> +	}
> 
> This should better be handled in a separate function in the same
> source file as the rest of the ROM handling code.
I moved it.
> 
> Also, what about ROMs in the other header types? Wouldn't it be
> better to scan all resources for the COPY flag instead?

I adjusted everything to use dev->rom_base_reg instead is
PCI_ROM_RESOURCE.

> 
>  #define PCI_SUBSYSTEM_ID	0x2e  
>  #define PCI_ROM_ADDRESS		0x30	/* Bits 31..11 are address, 10..1
> reserved */
>  #define  PCI_ROM_ADDRESS_ENABLE	0x01
> +#define  PCI_ROM_SHADOW		0x02	/* resource flag, ROM is copy at
> C000:0 */
> +#define  PCI_ROM_COPY		0x04	/* resource flag, ROM is alloc'd copy */
>  #define PCI_ROM_ADDRESS_MASK	(~0x7ffUL)
> 
> This does not belong here! This part of pci.h describes the
> configuration space, not stuff internal to the kernel. Better 
> introduce a new resource flag in <linux/ioport.h>.

I added new flags in ioport.h. Historically PCI_ROM_ADDRESS_ENABLE has
been used instead of the new IORESOURCE_ROM_ENABLE define I just
created.
 
> 
> 				Have a nice fortnight
> -- 
> Martin `MJ' Mares   <mj@ucw.cz>  
> http://atrey.karlin.mff.cuni.cz/~mj/
> Faculty of Math and Physics, Charles University, Prague, Czech Rep.,
> Earth
> "Oh no, not again!"  -- The bowl of petunias
> 

=====
Jon Smirl
jonsmirl@yahoo.com


		
__________________________________
Do you Yahoo!?
New and Improved Yahoo! Mail - Send 10MB messages!
http://promotions.yahoo.com/new_mail 

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: pci-sysfs-rom-16.patch --]
[-- Type: text/x-patch; name="pci-sysfs-rom-16.patch", Size: 13206 bytes --]

===== arch/i386/pci/fixup.c 1.19 vs edited =====
--- 1.19/arch/i386/pci/fixup.c	Thu Jun  3 10:58:17 2004
+++ edited/arch/i386/pci/fixup.c	Sat Aug 14 01:27:01 2004
@@ -237,6 +237,29 @@
 	}
 }
 
+static void __devinit pci_fixup_video(struct pci_dev *pdev)
+{
+	struct pci_dev *bridge;
+	struct pci_bus *bus;
+	u16 l;
+	
+	if ((pdev->class >> 8) != PCI_CLASS_DISPLAY_VGA)
+		return;
+	       
+	/* Is VGA routed to us? */
+	bus = pdev->bus;
+	while (bus) {
+		bridge = bus->self;
+		if (bridge) {
+			pci_read_config_word(bridge, PCI_BRIDGE_CONTROL, &l);
+			if (!(l & PCI_BRIDGE_CTL_VGA))
+				return;
+		}
+		bus = bus->parent;
+	}
+	pdev->resource[pdev->rom_base_reg].flags |= IORESOURCE_ROM_SHADOW;
+}
+
 struct pci_fixup pcibios_fixups[] = {
 	{
 		.pass		= PCI_FIXUP_HEADER,
@@ -345,6 +368,12 @@
 		.vendor		= PCI_VENDOR_ID_NVIDIA,
 		.device		= PCI_DEVICE_ID_NVIDIA_NFORCE2,
 		.hook		= pci_fixup_nforce2
+	},
+	{
+		.pass		= PCI_FIXUP_HEADER,
+		.vendor		= PCI_ANY_ID,
+		.device		= PCI_ANY_ID,
+		.hook		= pci_fixup_video
 	},
 	{ .pass = 0 }
 };
===== drivers/pci/pci-sysfs.c 1.10 vs edited =====
--- 1.10/drivers/pci/pci-sysfs.c	Fri Jun  4 09:23:04 2004
+++ edited/drivers/pci/pci-sysfs.c	Sat Aug 14 01:31:14 2004
@@ -5,6 +5,8 @@
  * (C) Copyright 2002-2004 IBM Corp.
  * (C) Copyright 2003 Matthew Wilcox
  * (C) Copyright 2003 Hewlett-Packard
+ * (C) Copyright 2004 Jon Smirl <jonsmirl@yahoo.com>
+ * (C) Copyright 2004 Silicon Graphics, Inc. Jesse Barnes <jbarnes@sgi.com>
  *
  * File attributes for PCI devices
  *
@@ -164,6 +166,211 @@
 	return count;
 }
 
+/**
+ * pci_enable_rom - enable ROM decoding for a PCI device
+ * @dev: PCI device to enable
+ *
+ * Enable ROM decoding on @dev.  This involves simply turning on the last
+ * bit of the PCI ROM BAR.  Note that some cards may share address decoders
+ * between the ROM and other resources, so enabling it may disable access
+ * to MMIO registers or other card memory.
+ */
+static void
+pci_enable_rom(struct pci_dev *pdev)
+{
+	u32 rom_addr;
+	
+	pci_read_config_dword(pdev, pdev->rom_base_reg, &rom_addr);
+	rom_addr |= PCI_ROM_ADDRESS_ENABLE;
+	pci_write_config_dword(pdev, pdev->rom_base_reg, rom_addr);
+}
+
+/**
+ * pci_disable_rom - disable ROM decoding for a PCI device
+ * @dev: PCI device to disable
+ *
+ * Disable ROM decoding on a PCI device by turning off the last bit in the
+ * ROM BAR.
+ */
+static void
+pci_disable_rom(struct pci_dev *pdev)
+{
+	u32 rom_addr;
+	pci_read_config_dword(pdev, pdev->rom_base_reg, &rom_addr);
+	rom_addr &= ~PCI_ROM_ADDRESS_ENABLE;
+	pci_write_config_dword(pdev, pdev->rom_base_reg, rom_addr);
+}
+
+/**
+ * pci_map_rom - map a PCI ROM to kernel space
+ * @dev: pointer to pci device struct
+ * @size: pointer to receive size of pci window over ROM
+ * @return: kernel virtual pointer to image of ROM
+ *
+ * Map a PCI ROM into kernel space. If ROM is boot video ROM,
+ * the shadow BIOS copy will be returned instead of the 
+ * actual ROM.
+ */
+unsigned char *
+pci_map_rom(struct pci_dev *pdev, size_t *size) {
+	struct resource *res = &pdev->resource[pdev->rom_base_reg];
+	loff_t start;
+	unsigned char *rom;
+	
+	if (res->flags & IORESOURCE_ROM_SHADOW) {	/* IORESOURCE_ROM_SHADOW only set on x86 */
+		start = (loff_t)0xC0000; 	/* primary video rom always starts here */
+		*size = 0x20000;		/* cover C000:0 through E000:0 */
+	} else if (res->flags & IORESOURCE_ROM_COPY) {
+		*size = pci_resource_len(pdev, pdev->rom_base_reg);
+		return (unsigned char *)pci_resource_start(pdev, pdev->rom_base_reg);
+	} else {
+		start = pci_resource_start(pdev, pdev->rom_base_reg);
+		*size = pci_resource_len(pdev, pdev->rom_base_reg);
+		if (*size == 0)
+			return NULL;
+		
+		/* Enable ROM space decodes */
+		pci_enable_rom(pdev);
+	}
+	
+	rom = ioremap(start, *size);
+	if (!rom) {
+		/* restore enable if ioremap fails */
+		if (!(res->flags & (IORESOURCE_ROM_ENABLE | IORESOURCE_ROM_SHADOW | IORESOURCE_ROM_COPY)))
+			pci_disable_rom(pdev);
+		return NULL;
+	}		
+	/* Standard PCI ROMs start out with these three bytes 55 AA size/512 */
+	if ((*rom == 0x55) && (*(rom + 1) == 0xAA))
+		*size = *(rom + 2) * 512;	/* return true ROM size, not PCI window size */
+		
+	return rom;
+}
+
+/**
+ * pci_map_rom_copy - map a PCI ROM to kernel space, create a copy
+ * @dev: pointer to pci device struct
+ * @size: pointer to receive size of pci window over ROM
+ * @return: kernel virtual pointer to image of ROM
+ *
+ * Map a PCI ROM into kernel space. If ROM is boot video ROM,
+ * the shadow BIOS copy will be returned instead of the 
+ * actual ROM.
+ */
+unsigned char *
+pci_map_rom_copy(struct pci_dev *pdev, size_t *size) {
+	struct resource *res = &pdev->resource[pdev->rom_base_reg];
+	unsigned char *rom;
+	
+	rom = pci_map_rom(pdev, size);
+	if (!rom)
+		return NULL;
+		
+	if (res->flags & (IORESOURCE_ROM_COPY | IORESOURCE_ROM_SHADOW))
+		return rom;
+		
+	res->start = (unsigned long)kmalloc(*size, GFP_KERNEL);
+	if (!res->start) 
+		return rom;
+
+	res->end = res->start + *size; 
+	memcpy((void*)res->start, rom, *size);
+	pci_unmap_rom(pdev, rom);
+	res->flags |= IORESOURCE_ROM_COPY;
+	
+	return (unsigned char *)res->start;
+}
+
+/**
+ * pci_unmap_rom - unmap the ROM from kernel space
+ * @dev: pointer to pci device struct
+ * @rom: virtual address of the previous mapping
+ *
+ * Remove a mapping of a previously mapped ROM
+ */
+void 
+pci_unmap_rom(struct pci_dev *pdev, unsigned char *rom) {
+	struct resource *res = &pdev->resource[pdev->rom_base_reg];
+
+	if (res->flags & IORESOURCE_ROM_COPY)
+		return;
+		
+	iounmap(rom);
+		
+	/* Disable again before continuing, leave enabled if pci=rom */
+	if (!(res->flags & (IORESOURCE_ROM_ENABLE | IORESOURCE_ROM_SHADOW)))
+		pci_disable_rom(pdev);
+}
+
+static struct bin_attribute rom_attr;
+
+/**
+ * pci_remove_rom - disable the ROM and remove it's sysfs attribute
+ * @dev: pointer to pci device struct
+ *
+ */
+void 
+pci_remove_rom(struct pci_dev *pdev) 
+{
+	struct resource *res = &pdev->resource[pdev->rom_base_reg];
+	
+	if (pci_resource_len(pdev, pdev->rom_base_reg))
+		sysfs_remove_bin_file(&pdev->dev.kobj, &rom_attr);
+	if (!(res->flags & (IORESOURCE_ROM_ENABLE | IORESOURCE_ROM_SHADOW | IORESOURCE_ROM_COPY)))
+		pci_disable_rom(pdev);
+}
+
+/**
+ * pci_remove_rom - disable the ROM and remove it's sysfs attribute
+ * @dev: pointer to pci device struct
+ *
+ */
+void 
+pci_cleanup_rom(struct pci_dev *pdev) 
+{
+	struct resource *res = &pdev->resource[pdev->rom_base_reg];
+	if (res->flags & IORESOURCE_ROM_COPY) {
+		kfree((void*)res->start);
+		res->flags &= ~IORESOURCE_ROM_COPY;
+		res->start = 0;
+		res->end = 0;
+	}
+}
+
+/**
+ * pci_read_rom - read a PCI ROM
+ * @kobj: kernel object handle
+ * @buf: where to put the data we read from the ROM
+ * @off: file offset
+ * @count: number of bytes to read
+ *
+ * Put @count bytes starting at @off into @buf from the ROM in the PCI
+ * device corresponding to @kobj.
+ */
+static ssize_t
+pci_read_rom(struct kobject *kobj, char *buf, loff_t off, size_t count)
+{
+	struct pci_dev *pdev = to_pci_dev(container_of(kobj, struct device, kobj));
+	unsigned char *rom;
+	size_t size;
+	
+	rom = pci_map_rom(pdev, &size);	/* size starts out as PCI window size */
+	if (!rom)
+		return 0;
+		
+	if (off >= size)
+		count = 0;
+	else {
+		if (off + count > size)
+			count = size - off;
+		
+		memcpy_fromio(buf, rom + off, count);
+	}
+	pci_unmap_rom(pdev, rom);
+		
+	return count;
+}
+
 static struct bin_attribute pci_config_attr = {
 	.attr =	{
 		.name = "config",
@@ -193,6 +400,57 @@
 	else
 		sysfs_create_bin_file(&pdev->dev.kobj, &pcie_config_attr);
 
+	/* If the device has a ROM, try to expose it in sysfs. */
+	if (pci_resource_len(pdev, pdev->rom_base_reg)) {
+		unsigned char *rom;
+		struct bin_attribute *rom_attr;
+		
+		pdev->rom_attr = NULL;
+		rom_attr = kmalloc(sizeof(*rom_attr), GFP_ATOMIC);
+		if (rom_attr) {
+			/* set default size */
+			rom_attr->size = pci_resource_len(pdev, pdev->rom_base_reg);
+			/* get true size if possible */
+			rom = pci_map_rom(pdev, &rom_attr->size);
+			if (!rom)
+				kfree(rom_attr);
+			else {
+				pci_unmap_rom(pdev, rom);
+				pdev->rom_attr = rom_attr;
+				rom_attr->attr.name = "rom";
+				rom_attr->attr.mode = S_IRUSR;
+				rom_attr->attr.owner = THIS_MODULE;
+				rom_attr->read = pci_read_rom;
+				sysfs_create_bin_file(&pdev->dev.kobj, rom_attr);
+			}
+		}
+	}
 	/* add platform-specific attributes */
 	pcibios_add_platform_entries(pdev);
 }
+
+/**
+ * pci_remove_sysfs_dev_files - cleanup PCI specific sysfs files
+ * @pdev: device whose entries we should free
+ *
+ * Cleanup when @pdev is removed from sysfs.
+ */
+void pci_remove_sysfs_dev_files(struct pci_dev *pdev)
+{
+	if (pdev->cfg_size < 4096)
+		sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr);
+	else
+		sysfs_remove_bin_file(&pdev->dev.kobj, &pcie_config_attr);
+
+	if (pci_resource_len(pdev, pdev->rom_base_reg)) {
+		if (pdev->rom_attr) {
+			sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr);
+			kfree(pdev->rom_attr);
+		}
+	}
+}
+
+EXPORT_SYMBOL(pci_map_rom);
+EXPORT_SYMBOL(pci_map_rom_copy);
+EXPORT_SYMBOL(pci_unmap_rom);
+EXPORT_SYMBOL(pci_remove_rom);
===== drivers/pci/pci.h 1.12 vs edited =====
--- 1.12/drivers/pci/pci.h	Fri Jun  4 09:23:04 2004
+++ edited/drivers/pci/pci.h	Sat Aug 14 01:31:34 2004
@@ -3,6 +3,8 @@
 extern int pci_hotplug (struct device *dev, char **envp, int num_envp,
 			 char *buffer, int buffer_size);
 extern void pci_create_sysfs_dev_files(struct pci_dev *pdev);
+extern void pci_remove_sysfs_dev_files(struct pci_dev *pdev);
+void pci_cleanup_rom(struct pci_dev *dev);
 extern int pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
 				  unsigned long size, unsigned long align,
 				  unsigned long min, unsigned int type_mask,
===== drivers/pci/probe.c 1.65 vs edited =====
--- 1.65/drivers/pci/probe.c	Fri May 21 14:45:27 2004
+++ edited/drivers/pci/probe.c	Sat Aug 14 01:02:04 2004
@@ -170,7 +170,7 @@
 		if (sz && sz != 0xffffffff) {
 			sz = pci_size(l, sz, PCI_ROM_ADDRESS_MASK);
 			if (sz) {
-				res->flags = (l & PCI_ROM_ADDRESS_ENABLE) |
+				res->flags = (l & IORESOURCE_ROM_ENABLE) |
 				  IORESOURCE_MEM | IORESOURCE_PREFETCH |
 				  IORESOURCE_READONLY | IORESOURCE_CACHEABLE;
 				res->start = l & PCI_ROM_ADDRESS_MASK;
===== drivers/pci/remove.c 1.3 vs edited =====
--- 1.3/drivers/pci/remove.c	Tue Feb  3 12:17:30 2004
+++ edited/drivers/pci/remove.c	Sat Aug 14 01:29:05 2004
@@ -16,6 +16,7 @@
 
  	msi_remove_pci_irq_vectors(dev);
 
+	pci_cleanup_rom(dev);
 	for (i = 0; i < PCI_NUM_RESOURCES; i++) {
 		struct resource *res = dev->resource + i;
 		if (res->parent)
@@ -26,6 +27,7 @@
 static void pci_destroy_dev(struct pci_dev *dev)
 {
 	pci_proc_detach_device(dev);
+	pci_remove_sysfs_dev_files(dev);
 	device_unregister(&dev->dev);
 
 	/* Remove the device from the device lists, and prevent any further
===== drivers/pci/setup-res.c 1.25 vs edited =====
--- 1.25/drivers/pci/setup-res.c	Thu Mar 18 18:40:56 2004
+++ edited/drivers/pci/setup-res.c	Sat Aug 14 01:02:01 2004
@@ -56,7 +56,7 @@
 	if (resno < 6) {
 		reg = PCI_BASE_ADDRESS_0 + 4 * resno;
 	} else if (resno == PCI_ROM_RESOURCE) {
-		new |= res->flags & PCI_ROM_ADDRESS_ENABLE;
+		new |= res->flags & IORESOURCE_ROM_ENABLE;
 		reg = dev->rom_base_reg;
 	} else {
 		/* Hmm, non-standard resource. */
===== include/linux/ioport.h 1.14 vs edited =====
--- 1.14/include/linux/ioport.h	Thu Dec 18 00:48:57 2003
+++ edited/include/linux/ioport.h	Sat Aug 14 00:44:06 2004
@@ -82,6 +82,11 @@
 #define IORESOURCE_MEM_SHADOWABLE	(1<<5)	/* dup: IORESOURCE_SHADOWABLE */
 #define IORESOURCE_MEM_EXPANSIONROM	(1<<6)
 
+/* PCI ROM control bits (IORESOURCE_BITS) */
+#define IORESOURCE_ROM_ENABLE		(1<<0)	/* ROM is enabled, same as PCI_ROM_ADDRESS_ENABLE */
+#define IORESOURCE_ROM_SHADOW		(1<<1)	/* ROM is copy at C000:0 */
+#define IORESOURCE_ROM_COPY		(1<<2)	/* ROM is alloc'd copy, resource field overlaid */
+
 /* PC/ISA/whatever - the normal PC address spaces: IO and memory */
 extern struct resource ioport_resource;
 extern struct resource iomem_resource;
===== include/linux/pci.h 1.132 vs edited =====
--- 1.132/include/linux/pci.h	Mon Aug  2 04:00:43 2004
+++ edited/include/linux/pci.h	Sat Aug 14 00:56:35 2004
@@ -537,6 +537,7 @@
 	unsigned int	is_busmaster:1; /* device is busmaster */
 	
 	unsigned int 	saved_config_space[16]; /* config space saved at suspend time */
+	struct bin_attribute *rom_attr; /* attribute descriptor for sysfs ROM entry */
 #ifdef CONFIG_PCI_NAMES
 #define PCI_NAME_SIZE	96
 #define PCI_NAME_HALF	__stringify(43)	/* less than half to handle slop */
@@ -777,6 +778,12 @@
 int pci_dac_set_dma_mask(struct pci_dev *dev, u64 mask);
 int pci_set_consistent_dma_mask(struct pci_dev *dev, u64 mask);
 int pci_assign_resource(struct pci_dev *dev, int i);
+
+/* ROM control related routines */
+unsigned char *pci_map_rom(struct pci_dev *pdev, size_t *size);
+unsigned char *pci_map_rom_copy(struct pci_dev *pdev, size_t *size);
+void pci_unmap_rom(struct pci_dev *pdev, unsigned char *rom);
+void pci_remove_rom(struct pci_dev *pdev);
 
 /* Power management related routines */
 int pci_save_state(struct pci_dev *dev, u32 *buffer);

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-12  2:22 ` Jon Smirl
@ 2004-08-13 18:17   ` Martin Mares
  2004-08-14  5:34     ` Jon Smirl
  0 siblings, 1 reply; 110+ messages in thread
From: Martin Mares @ 2004-08-13 18:17 UTC (permalink / raw)
  To: Jon Smirl
  Cc: Pallipadi, Venkatesh, Greg KH, Jesse Barnes, linux-pci, Alan Cox,
	Linux Kernel Mailing List, Petr Vandrovec,
	Benjamin Herrenschmidt

Hello!

Just a couple of notes about your patch:

+unsigned char *
+pci_map_rom(struct pci_dev *dev, size_t *size) {
+	struct resource *res = &dev->resource[PCI_ROM_RESOURCE];
+	loff_t start;
+	unsigned char *rom;
+	
+	if (res->flags & PCI_ROM_SHADOW) {	/* PCI_ROM_SHADOW only set on x86 */
+		start = (loff_t)0xC0000; 	/* primary video rom always starts here */
+		*size = 0x20000;		/* cover C000:0 through E000:0 */

Shouldn't we do this only if we find that the device has a ROM resource?

+	} else if (res->flags & PCI_ROM_COPY) {
+		*size = pci_resource_len(dev, PCI_ROM_RESOURCE);
+		return (unsigned char *)pci_resource_start(dev, PCI_ROM_RESOURCE);
+	} else {
+		start = pci_resource_start(dev, PCI_ROM_RESOURCE);
+		*size = pci_resource_len(dev, PCI_ROM_RESOURCE);
+		if (*size == 0)
+			return NULL;
+		
+		/* Enable ROM space decodes */
+		pci_enable_rom(dev);
+	}

This seems to be wrong -- before enabling a resource, you must call
request_resource() on it and make sure that the ROM (1) has an address
allocated, and (2) the address is not used by anything else.

+	/* If the device has a ROM, try to expose it in sysfs. */
+	if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) {
+		unsigned char *rom;
+		struct bin_attribute *rom_attr;
+		
+		pdev->rom_attr = NULL;
+		rom_attr = kmalloc(sizeof(*rom_attr), GFP_ATOMIC);
+		if (rom_attr) {
+			/* set default size */
+			rom_attr->size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
+			/* get true size if possible */
+			rom = pci_map_rom(pdev, &rom_attr->size);

As we can never be sure about the decoder sharing, it is very wise not to
touch the ROM BAR until somebody accesses the sysfs file. Using size according
to the PCI config space (i.e., the resource) does not harm anybody.

+	if (dev->hdr_type == PCI_HEADER_TYPE_NORMAL) {
+		res = &dev->resource[PCI_ROM_RESOURCE];
+		if (res->flags & PCI_ROM_COPY) {
+			kfree((void*)res->start);
+			res->flags &= !PCI_ROM_COPY;

~, not !

+			res->start = 0;
+			res->end = 0;
+		}
+	}

This should better be handled in a separate function in the same source
file as the rest of the ROM handling code.

Also, what about ROMs in the other header types? Wouldn't it be better to
scan all resources for the COPY flag instead?

 #define PCI_SUBSYSTEM_ID	0x2e  
 #define PCI_ROM_ADDRESS		0x30	/* Bits 31..11 are address, 10..1 reserved */
 #define  PCI_ROM_ADDRESS_ENABLE	0x01
+#define  PCI_ROM_SHADOW		0x02	/* resource flag, ROM is copy at C000:0 */
+#define  PCI_ROM_COPY		0x04	/* resource flag, ROM is alloc'd copy */
 #define PCI_ROM_ADDRESS_MASK	(~0x7ffUL)

This does not belong here! This part of pci.h describes the configuration
space, not stuff internal to the kernel. Better introduce a new resource
flag in <linux/ioport.h>.

				Have a nice fortnight
-- 
Martin `MJ' Mares   <mj@ucw.cz>   http://atrey.karlin.mff.cuni.cz/~mj/
Faculty of Math and Physics, Charles University, Prague, Czech Rep., Earth
"Oh no, not again!"  -- The bowl of petunias

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
@ 2004-08-12  8:39 Thomas Winischhofer
  0 siblings, 0 replies; 110+ messages in thread
From: Thomas Winischhofer @ 2004-08-12  8:39 UTC (permalink / raw)
  To: Linux Kernel Mailing List




+	res->flags &= !PCI_ROM_COPY;

Erm....



-- 
Thomas Winischhofer
Vienna/Austria
thomas AT winischhofer DOT net	       *** http://www.winischhofer.net
twini AT xfree86 DOT org

^ permalink raw reply	[flat|nested] 110+ messages in thread

* RE: [PATCH] add PCI ROMs to sysfs
  2004-08-11 23:27 Pallipadi, Venkatesh
  2004-08-12  0:22 ` Jon Smirl
@ 2004-08-12  2:22 ` Jon Smirl
  2004-08-13 18:17   ` Martin Mares
  1 sibling, 1 reply; 110+ messages in thread
From: Jon Smirl @ 2004-08-12  2:22 UTC (permalink / raw)
  To: Pallipadi, Venkatesh, Greg KH, Jesse Barnes
  Cc: Martin Mares, linux-pci, Alan Cox, Linux Kernel Mailing List,
	Petr Vandrovec, Benjamin Herrenschmidt

[-- Attachment #1: Type: text/plain, Size: 704 bytes --]

pci-sysfs-rom-14.patch

switches back to allocating sysfs attr structure
adds jesse copyright
fixes ordering problem with get length out of shadow ROM

pending issues
Alan's concern about copying ROMs for partially decoded hardware
Does quirk need to be added to x86_64
Does this patch break on non-x86 architectures
My concern about relying on bios to set initial address for ROM
please check it on laptops

If a radeon ROM shows as FFFF it is a bug in the radeon hardware. Load
the new radeonfb driver and the ROM will appear.

=====
Jon Smirl
jonsmirl@yahoo.com


		
__________________________________
Do you Yahoo!?
Yahoo! Mail is new and improved - Check it out!
http://promotions.yahoo.com/new_mail

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: pci-sysfs-rom-14.patch --]
[-- Type: text/x-patch; name="pci-sysfs-rom-14.patch", Size: 13094 bytes --]

===== arch/i386/pci/fixup.c 1.19 vs edited =====
--- 1.19/arch/i386/pci/fixup.c	Thu Jun  3 10:58:17 2004
+++ edited/arch/i386/pci/fixup.c	Wed Aug 11 22:14:20 2004
@@ -237,6 +237,29 @@
 	}
 }
 
+static void __devinit pci_fixup_video(struct pci_dev *pdev)
+{
+	struct pci_dev *bridge;
+	struct pci_bus *bus;
+	u16 l;
+	
+	if ((pdev->class >> 8) != PCI_CLASS_DISPLAY_VGA)
+		return;
+	       
+	/* Is VGA routed to us? */
+	bus = pdev->bus;
+	while (bus) {
+		bridge = bus->self;
+		if (bridge) {
+			pci_read_config_word(bridge, PCI_BRIDGE_CONTROL, &l);
+			if (!(l & PCI_BRIDGE_CTL_VGA))
+				return;
+		}
+		bus = bus->parent;
+	}
+	pdev->resource[PCI_ROM_RESOURCE].flags |= PCI_ROM_SHADOW;
+}
+
 struct pci_fixup pcibios_fixups[] = {
 	{
 		.pass		= PCI_FIXUP_HEADER,
@@ -345,6 +368,12 @@
 		.vendor		= PCI_VENDOR_ID_NVIDIA,
 		.device		= PCI_DEVICE_ID_NVIDIA_NFORCE2,
 		.hook		= pci_fixup_nforce2
+	},
+	{
+		.pass		= PCI_FIXUP_HEADER,
+		.vendor		= PCI_ANY_ID,
+		.device		= PCI_ANY_ID,
+		.hook		= pci_fixup_video
 	},
 	{ .pass = 0 }
 };
===== drivers/pci/pci-sysfs.c 1.10 vs edited =====
--- 1.10/drivers/pci/pci-sysfs.c	Fri Jun  4 09:23:04 2004
+++ edited/drivers/pci/pci-sysfs.c	Wed Aug 11 22:13:46 2004
@@ -5,6 +5,8 @@
  * (C) Copyright 2002-2004 IBM Corp.
  * (C) Copyright 2003 Matthew Wilcox
  * (C) Copyright 2003 Hewlett-Packard
+ * (C) Copyright 2004 Jon Smirl <jonsmirl@yahoo.com>
+ * (C) Copyright 2004 Silicon Graphics, Inc. Jesse Barnes <jbarnes@sgi.com>
  *
  * File attributes for PCI devices
  *
@@ -164,6 +166,193 @@
 	return count;
 }
 
+/**
+ * pci_enable_rom - enable ROM decoding for a PCI device
+ * @dev: PCI device to enable
+ *
+ * Enable ROM decoding on @dev.  This involves simply turning on the last
+ * bit of the PCI ROM BAR.  Note that some cards may share address decoders
+ * between the ROM and other resources, so enabling it may disable access
+ * to MMIO registers or other card memory.
+ */
+static void
+pci_enable_rom(struct pci_dev *dev)
+{
+	u32 rom_addr;
+	
+	pci_read_config_dword(dev, dev->rom_base_reg, &rom_addr);
+	rom_addr |= PCI_ROM_ADDRESS_ENABLE;
+	pci_write_config_dword(dev, dev->rom_base_reg, rom_addr);
+}
+
+/**
+ * pci_disable_rom - disable ROM decoding for a PCI device
+ * @dev: PCI device to disable
+ *
+ * Disable ROM decoding on a PCI device by turning off the last bit in the
+ * ROM BAR.
+ */
+static void
+pci_disable_rom(struct pci_dev *dev)
+{
+	u32 rom_addr;
+	pci_read_config_dword(dev, dev->rom_base_reg, &rom_addr);
+	rom_addr &= ~PCI_ROM_ADDRESS_ENABLE;
+	pci_write_config_dword(dev, dev->rom_base_reg, rom_addr);
+}
+
+/**
+ * pci_map_rom - map a PCI ROM to kernel space
+ * @dev: pointer to pci device struct
+ * @size: pointer to receive size of pci window over ROM
+ * @return: kernel virtual pointer to image of ROM
+ *
+ * Map a PCI ROM into kernel space. If ROM is boot video ROM,
+ * the shadow BIOS copy will be returned instead of the 
+ * actual ROM.
+ */
+unsigned char *
+pci_map_rom(struct pci_dev *dev, size_t *size) {
+	struct resource *res = &dev->resource[PCI_ROM_RESOURCE];
+	loff_t start;
+	unsigned char *rom;
+	
+	if (res->flags & PCI_ROM_SHADOW) {	/* PCI_ROM_SHADOW only set on x86 */
+		start = (loff_t)0xC0000; 	/* primary video rom always starts here */
+		*size = 0x20000;		/* cover C000:0 through E000:0 */
+	} else if (res->flags & PCI_ROM_COPY) {
+		*size = pci_resource_len(dev, PCI_ROM_RESOURCE);
+		return (unsigned char *)pci_resource_start(dev, PCI_ROM_RESOURCE);
+	} else {
+		start = pci_resource_start(dev, PCI_ROM_RESOURCE);
+		*size = pci_resource_len(dev, PCI_ROM_RESOURCE);
+		if (*size == 0)
+			return NULL;
+		
+		/* Enable ROM space decodes */
+		pci_enable_rom(dev);
+	}
+	
+	rom = ioremap(start, *size);
+	if (!rom) {
+		/* restore enable if ioremap fails */
+		if (!(res->flags & (PCI_ROM_ADDRESS_ENABLE | PCI_ROM_SHADOW | PCI_ROM_COPY)))
+			pci_disable_rom(dev);
+		return NULL;
+	}		
+	/* Standard PCI ROMs start out with these three bytes 55 AA size/512 */
+	if ((*rom == 0x55) && (*(rom + 1) == 0xAA))
+		*size = *(rom + 2) * 512;	/* return true ROM size, not PCI window size */
+		
+	return rom;
+}
+
+/**
+ * pci_map_rom_copy - map a PCI ROM to kernel space, create a copy
+ * @dev: pointer to pci device struct
+ * @size: pointer to receive size of pci window over ROM
+ * @return: kernel virtual pointer to image of ROM
+ *
+ * Map a PCI ROM into kernel space. If ROM is boot video ROM,
+ * the shadow BIOS copy will be returned instead of the 
+ * actual ROM.
+ */
+unsigned char *
+pci_map_rom_copy(struct pci_dev *dev, size_t *size) {
+	struct resource *res = &dev->resource[PCI_ROM_RESOURCE];
+	unsigned char *rom;
+	
+	rom = pci_map_rom(dev, size);
+	if (!rom)
+		return NULL;
+		
+	if (res->flags & (PCI_ROM_COPY | PCI_ROM_SHADOW))
+		return rom;
+		
+	res->start = (unsigned long)kmalloc(*size, GFP_KERNEL);
+	if (!res->start) 
+		return rom;
+
+	res->end = res->start + *size; 
+	memcpy((void*)res->start, rom, *size);
+	pci_unmap_rom(dev, rom);
+	res->flags |= PCI_ROM_COPY;
+	
+	return (unsigned char *)res->start;
+}
+
+/**
+ * pci_unmap_rom - unmap the ROM from kernel space
+ * @dev: pointer to pci device struct
+ * @rom: virtual address of the previous mapping
+ *
+ * Remove a mapping of a previously mapped ROM
+ */
+void 
+pci_unmap_rom(struct pci_dev *dev, unsigned char *rom) {
+	struct resource *res = &dev->resource[PCI_ROM_RESOURCE];
+
+	if (res->flags & PCI_ROM_COPY)
+		return;
+		
+	iounmap(rom);
+		
+	/* Disable again before continuing, leave enabled if pci=rom */
+	if (!(res->flags & (PCI_ROM_ADDRESS_ENABLE | PCI_ROM_SHADOW)))
+		pci_disable_rom(dev);
+}
+
+static struct bin_attribute rom_attr;
+
+/**
+ * pci_remove_rom - disable the ROM and remove it's sysfs attribute
+ * @dev: pointer to pci device struct
+ *
+ */
+void 
+pci_remove_rom(struct pci_dev *dev) {
+	struct resource *res = &dev->resource[PCI_ROM_RESOURCE];
+	
+	if (pci_resource_len(dev, PCI_ROM_RESOURCE))
+		sysfs_remove_bin_file(&dev->dev.kobj, &rom_attr);
+	if (!(res->flags & (PCI_ROM_ADDRESS_ENABLE | PCI_ROM_SHADOW | PCI_ROM_COPY)))
+		pci_disable_rom(dev);
+}
+
+/**
+ * pci_read_rom - read a PCI ROM
+ * @kobj: kernel object handle
+ * @buf: where to put the data we read from the ROM
+ * @off: file offset
+ * @count: number of bytes to read
+ *
+ * Put @count bytes starting at @off into @buf from the ROM in the PCI
+ * device corresponding to @kobj.
+ */
+static ssize_t
+pci_read_rom(struct kobject *kobj, char *buf, loff_t off, size_t count)
+{
+	struct pci_dev *dev = to_pci_dev(container_of(kobj,struct device,kobj));
+	unsigned char *rom;
+	size_t size;
+	
+	rom = pci_map_rom(dev, &size);	/* size starts out as PCI window size */
+	if (!rom)
+		return 0;
+		
+	if (off >= size)
+		count = 0;
+	else {
+		if (off + count > size)
+			count = size - off;
+		
+		memcpy_fromio(buf, rom + off, count);
+	}
+	pci_unmap_rom(dev, rom);
+		
+	return count;
+}
+
 static struct bin_attribute pci_config_attr = {
 	.attr =	{
 		.name = "config",
@@ -186,13 +375,63 @@
 	.write = pci_write_config,
 };
 
-void pci_create_sysfs_dev_files (struct pci_dev *pdev)
+void pci_create_sysfs_dev_files(struct pci_dev *pdev)
 {
 	if (pdev->cfg_size < 4096)
 		sysfs_create_bin_file(&pdev->dev.kobj, &pci_config_attr);
 	else
 		sysfs_create_bin_file(&pdev->dev.kobj, &pcie_config_attr);
 
+	/* If the device has a ROM, try to expose it in sysfs. */
+	if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) {
+		unsigned char *rom;
+		struct bin_attribute *rom_attr;
+		
+		pdev->rom_attr = NULL;
+		rom_attr = kmalloc(sizeof(*rom_attr), GFP_ATOMIC);
+		if (rom_attr) {
+			/* set default size */
+			rom_attr->size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
+			/* get true size if possible */
+			rom = pci_map_rom(pdev, &rom_attr->size);
+			if (!rom)
+				kfree(rom_attr);
+			else {
+				pci_unmap_rom(pdev, rom);
+				pdev->rom_attr = rom_attr;
+				rom_attr->attr.name = "rom";
+				rom_attr->attr.mode = S_IRUSR;
+				rom_attr->attr.owner = THIS_MODULE;
+				rom_attr->read = pci_read_rom;
+				sysfs_create_bin_file(&pdev->dev.kobj, rom_attr);
+			}
+		}
+	}
 	/* add platform-specific attributes */
 	pcibios_add_platform_entries(pdev);
 }
+
+/**
+ * pci_remove_sysfs_dev_files - cleanup PCI specific sysfs files
+ * @pdev: device whose entries we should free
+ *
+ * Cleanup when @pdev is removed from sysfs.
+ */
+void pci_remove_sysfs_dev_files(struct pci_dev *pdev)
+{
+	if (pdev->cfg_size < 4096)
+		sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr);
+	else
+		sysfs_remove_bin_file(&pdev->dev.kobj, &pcie_config_attr);
+
+	if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) {
+		if (pdev->rom_attr) {
+			sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr);
+			kfree(pdev->rom_attr);
+		}
+	}
+}
+
+EXPORT_SYMBOL(pci_map_rom);
+EXPORT_SYMBOL(pci_map_rom_copy);
+EXPORT_SYMBOL(pci_unmap_rom);
===== drivers/pci/pci.h 1.12 vs edited =====
--- 1.12/drivers/pci/pci.h	Fri Jun  4 09:23:04 2004
+++ edited/drivers/pci/pci.h	Sun Aug  8 17:20:23 2004
@@ -3,6 +3,7 @@
 extern int pci_hotplug (struct device *dev, char **envp, int num_envp,
 			 char *buffer, int buffer_size);
 extern void pci_create_sysfs_dev_files(struct pci_dev *pdev);
+extern void pci_remove_sysfs_dev_files(struct pci_dev *pdev);
 extern int pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
 				  unsigned long size, unsigned long align,
 				  unsigned long min, unsigned int type_mask,
===== drivers/pci/remove.c 1.3 vs edited =====
--- 1.3/drivers/pci/remove.c	Tue Feb  3 12:17:30 2004
+++ edited/drivers/pci/remove.c	Sun Aug  8 17:20:23 2004
@@ -12,12 +12,22 @@
 
 static void pci_free_resources(struct pci_dev *dev)
 {
+	struct resource *res;
 	int i;
 
  	msi_remove_pci_irq_vectors(dev);
 
+	if (dev->hdr_type == PCI_HEADER_TYPE_NORMAL) {
+		res = &dev->resource[PCI_ROM_RESOURCE];
+		if (res->flags & PCI_ROM_COPY) {
+			kfree((void*)res->start);
+			res->flags &= !PCI_ROM_COPY;
+			res->start = 0;
+			res->end = 0;
+		}
+	}
 	for (i = 0; i < PCI_NUM_RESOURCES; i++) {
-		struct resource *res = dev->resource + i;
+		res = dev->resource + i;
 		if (res->parent)
 			release_resource(res);
 	}
@@ -26,6 +36,7 @@
 static void pci_destroy_dev(struct pci_dev *dev)
 {
 	pci_proc_detach_device(dev);
+	pci_remove_sysfs_dev_files(dev);
 	device_unregister(&dev->dev);
 
 	/* Remove the device from the device lists, and prevent any further
===== include/linux/pci.h 1.132 vs edited =====
--- 1.132/include/linux/pci.h	Mon Aug  2 04:00:43 2004
+++ edited/include/linux/pci.h	Wed Aug 11 21:57:09 2004
@@ -102,6 +102,8 @@
 #define PCI_SUBSYSTEM_ID	0x2e  
 #define PCI_ROM_ADDRESS		0x30	/* Bits 31..11 are address, 10..1 reserved */
 #define  PCI_ROM_ADDRESS_ENABLE	0x01
+#define  PCI_ROM_SHADOW		0x02	/* resource flag, ROM is copy at C000:0 */
+#define  PCI_ROM_COPY		0x04	/* resource flag, ROM is alloc'd copy */
 #define PCI_ROM_ADDRESS_MASK	(~0x7ffUL)
 
 #define PCI_CAPABILITY_LIST	0x34	/* Offset of first capability list entry */
@@ -537,6 +539,7 @@
 	unsigned int	is_busmaster:1; /* device is busmaster */
 	
 	unsigned int 	saved_config_space[16]; /* config space saved at suspend time */
+	struct bin_attribute *rom_attr; /* attribute descriptor for sysfs ROM entry */
 #ifdef CONFIG_PCI_NAMES
 #define PCI_NAME_SIZE	96
 #define PCI_NAME_HALF	__stringify(43)	/* less than half to handle slop */
@@ -777,6 +780,12 @@
 int pci_dac_set_dma_mask(struct pci_dev *dev, u64 mask);
 int pci_set_consistent_dma_mask(struct pci_dev *dev, u64 mask);
 int pci_assign_resource(struct pci_dev *dev, int i);
+
+/* ROM control related routines */
+unsigned char *pci_map_rom(struct pci_dev *dev, size_t *size);
+unsigned char *pci_map_rom_copy(struct pci_dev *dev, size_t *size);
+void pci_unmap_rom(struct pci_dev *dev, unsigned char *rom);
+void pci_remove_rom(struct pci_dev *dev);
 
 /* Power management related routines */
 int pci_save_state(struct pci_dev *dev, u32 *buffer);
===== kdb/gen-kdb_cmds.c 1.1 vs edited =====
--- 1.1/kdb/gen-kdb_cmds.c	Sun Aug  8 17:19:30 2004
+++ edited/kdb/gen-kdb_cmds.c	Sun Aug  8 17:22:49 2004
@@ -17,16 +17,6 @@
 static __initdata char kdb_cmd15[] = "  -archkdbcommon\n";
 static __initdata char kdb_cmd16[] = "  -bta\n";
 static __initdata char kdb_cmd17[] = "endefcmd\n";
-static __initdata char kdb_cmd18[] = "defcmd archkdbcommon \"\" \"Common arch debugging\"\n";
-static __initdata char kdb_cmd19[] = "  set LINES 2000000\n";
-static __initdata char kdb_cmd20[] = "  set BTAPROMPT 0\n";
-static __initdata char kdb_cmd21[] = "  -summary\n";
-static __initdata char kdb_cmd22[] = "  -id %eip-24\n";
-static __initdata char kdb_cmd23[] = "  -cpu\n";
-static __initdata char kdb_cmd24[] = "  -ps\n";
-static __initdata char kdb_cmd25[] = "  -dmesg 600\n";
-static __initdata char kdb_cmd26[] = "  -bt\n";
-static __initdata char kdb_cmd27[] = "endefcmd\n";
 char __initdata *kdb_cmds[] = {
   kdb_cmd0,
   kdb_cmd1,
@@ -46,15 +36,5 @@
   kdb_cmd15,
   kdb_cmd16,
   kdb_cmd17,
-  kdb_cmd18,
-  kdb_cmd19,
-  kdb_cmd20,
-  kdb_cmd21,
-  kdb_cmd22,
-  kdb_cmd23,
-  kdb_cmd24,
-  kdb_cmd25,
-  kdb_cmd26,
-  kdb_cmd27,
   0
 };

^ permalink raw reply	[flat|nested] 110+ messages in thread

* RE: [PATCH] add PCI ROMs to sysfs
  2004-08-11 23:27 Pallipadi, Venkatesh
@ 2004-08-12  0:22 ` Jon Smirl
  2004-08-12  2:22 ` Jon Smirl
  1 sibling, 0 replies; 110+ messages in thread
From: Jon Smirl @ 2004-08-12  0:22 UTC (permalink / raw)
  To: Pallipadi, Venkatesh, Greg KH, Jesse Barnes, Benjamin Herrenschmidt
  Cc: Martin Mares, linux-pci, Alan Cox, Linux Kernel Mailing List,
	Petr Vandrovec, Benjamin Herrenschmidt

Changing the quirk in fixup.c from FINAL to HEADER fixes the problem.
Only the length was coming from the wrong place, the ROM contents came
from the shadow memory.

Any ideas on how to solve this problem...

Originally I had this in the enable ROM routine
/* assign the ROM an address if it doesn't have one */
if (r->parent == NULL)
   pci_assign_resource(dev->pdev, PCI_ROM_RESOURCE);
                                                                       
                                 
I removed the call to pci_assign_resource(). The sysfs attribute code
builds the attributes before the pci subsystem is fully initialized.
specifically before arch pcibios_init() has been called. If
pci_assign_resource() is called for the ROM before pcibios_init() the
kernel's resource maps have not been built yet. This will result in the
ROM being located on top of the framebuffer; as soon as it is enabled
the system will lock. Right now the code relies on the BIOS getting the
ROM address set up right. If we can figure out how to initialize the
sysfs attributes after pcibios_init() then I can put the assign call
back.

--- "Pallipadi, Venkatesh" <venkatesh.pallipadi@intel.com> wrote:

> 
> One issue with x86 quirk in this patch.
> The actual sysfs entries are created during the PCI bus scan.
> But, pci_fixup_video() gets called later during device_initcalls.
> So, PCI_ROM_SHADOW is kind of ineffective now. 
> 
> Thanks,
> Venki

=====
Jon Smirl
jonsmirl@yahoo.com


		
__________________________________
Do you Yahoo!?
Yahoo! Mail Address AutoComplete - You start. We finish.
http://promotions.yahoo.com/new_mail 

^ permalink raw reply	[flat|nested] 110+ messages in thread

* RE: [PATCH] add PCI ROMs to sysfs
@ 2004-08-11 23:27 Pallipadi, Venkatesh
  2004-08-12  0:22 ` Jon Smirl
  2004-08-12  2:22 ` Jon Smirl
  0 siblings, 2 replies; 110+ messages in thread
From: Pallipadi, Venkatesh @ 2004-08-11 23:27 UTC (permalink / raw)
  To: Jon Smirl, Greg KH, Jesse Barnes, Benjamin Herrenschmidt
  Cc: Martin Mares, linux-pci, Alan Cox, Linux Kernel Mailing List,
	Petr Vandrovec, Benjamin Herrenschmidt


One issue with x86 quirk in this patch.
The actual sysfs entries are created during the PCI bus scan.
But, pci_fixup_video() gets called later during device_initcalls.
So, PCI_ROM_SHADOW is kind of ineffective now. 

Thanks,
Venki

>-----Original Message-----
>From: linux-kernel-owner@vger.kernel.org 
>[mailto:linux-kernel-owner@vger.kernel.org] On Behalf Of Jon Smirl
>Sent: Wednesday, August 11, 2004 12:24 PM
>To: Greg KH; Jesse Barnes; Benjamin Herrenschmidt
>Cc: Martin Mares; linux-pci@atrey.karlin.mff.cuni.cz; Alan 
>Cox; Linux Kernel Mailing List; Petr Vandrovec; Benjamin Herrenschmidt
>Subject: Re: [PATCH] add PCI ROMs to sysfs
>
>I can put together a new patch later tonight that reverts to the old
>size scheme. It was more complicated since you need to allocate each
>attribute.
>
>I'll attach a newer version that incorporates a little feedback about
>creating a function to remove the attribute for devices that don't want
>to expose the ROM.
>
>Alan Cox had concerns about copying the ROMs for those devices that
>don't implement full address decoding. I'm using kmalloc for 40-60KB.
>Would vmalloc be a better choice? Very few drivers will use the copy
>option, mostly old hardware.
>
>BenH said he would check it out on ppc but I haven't heard from him
>yet.
>
>Jesse, did you notice that the quirk for tracking the boot video device
>is x86 only? I believe this needs to run on ia64 and x86_64 too. How do
>we want to do that? It will do the wrong thing on architectures that
>don't shadow video ROMs to C0000.
>
>

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-04 17:06 ` Jesse Barnes
@ 2004-08-05  1:38   ` Jon Smirl
  0 siblings, 0 replies; 110+ messages in thread
From: Jon Smirl @ 2004-08-05  1:38 UTC (permalink / raw)
  To: Jesse Barnes, Petr Vandrovec; +Cc: linux-kernel

[-- Attachment #1: Type: text/plain, Size: 1754 bytes --]

New version, it's getting smaller.
add a lot of error checking 
uses memcpy_fromio
reuses rom_attr since sysfs copies the length into the dentry
if the ROM was enabled from pci=rom, it leaves it that way

We still need:
quick for radeon bug
quirk for recording boot graphics device

I need to do an x86 architecture specific thing in the boot device
quirk. In order to tell the length of the ROM I need to start with the
55 AA at C000:0. The next byte after the 55 AA is ROM length divided by
512. There is no direct way to query for this length since the ROM
image may have been decompressed and there is no real ROM. Not sure
what the other platforms will do. Does Open Firmware use the same 55 AA
len scheme? PCI report ROM length much larger than they really are, my
radeon ROM is 128KB from PCI but it really is 48KB.

Any ideas on the simplest way to identify the boot video card? I can
scan all of the PCI adapters looking for video ones and then check if
it is enabled since the only one enabled will be the boot device.

I can simplify the patch further if there is a free bit in
resource->flags. The bottom byte looks to be for bus specific use but I
am having trouble determining which bits PCI is using.

With a bit in resource->flags I can remove this structure from pci_dev
struct rom_info {
	char *rom; /* copy of the ROM if necessary */
	size_t size;
};
The bit would indicate that the ROM is being read from the BIOS shadow
copy making it ok to overlay the ROM resource slot. I'd then modify
pci_assign_resource, pci_release_resource to respect the bit. 


=====
Jon Smirl
jonsmirl@yahoo.com


	
		
__________________________________
Do you Yahoo!?
New and Improved Yahoo! Mail - 100MB free storage!
http://promotions.yahoo.com/new_mail 

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: pci-sysfs-rom-9.patch --]
[-- Type: text/x-patch; name="pci-sysfs-rom-9.patch", Size: 6387 bytes --]

===== drivers/pci/pci-sysfs.c 1.10 vs edited =====
--- 1.10/drivers/pci/pci-sysfs.c	Fri Jun  4 09:23:04 2004
+++ edited/drivers/pci/pci-sysfs.c	Wed Aug  4 20:39:45 2004
@@ -164,6 +164,103 @@
 	return count;
 }
 
+/**
+ * pci_enable_rom - enable ROM decoding for a PCI device
+ * @dev: PCI device to enable
+ *
+ * Enable ROM decoding on @dev.  This involves simply turning on the last
+ * bit of the PCI ROM BAR.  Note that some cards may share address decoders
+ * between the ROM and other resources, so enabling it may disable access
+ * to MMIO registers or other card memory.
+ */
+static void
+pci_enable_rom(struct pci_dev *dev)
+{
+	u32 rom_addr;
+	
+	pci_read_config_dword(dev, dev->rom_base_reg, &rom_addr);
+	rom_addr |= PCI_ROM_ADDRESS_ENABLE;
+	pci_write_config_dword(dev, dev->rom_base_reg, rom_addr);
+}
+
+/**
+ * pci_disable_rom - disable ROM decoding for a PCI device
+ * @dev: PCI device to disable
+ *
+ * Disable ROM decoding on a PCI device by turning off the last bit in the
+ * ROM BAR.
+ */
+static void
+pci_disable_rom(struct pci_dev *dev)
+{
+	u32 rom_addr;
+
+	pci_read_config_dword(dev, dev->rom_base_reg, &rom_addr);
+	rom_addr &= ~PCI_ROM_ADDRESS_ENABLE;
+	pci_write_config_dword(dev, dev->rom_base_reg, rom_addr);
+}
+
+/**
+ * pci_read_rom - read a PCI ROM
+ * @kobj: kernel object handle
+ * @buf: where to put the data we read from the ROM
+ * @off: file offset
+ * @count: number of bytes to read
+ *
+ * Put @count bytes starting at @off into @buf from the ROM in the PCI
+ * device corresponding to @kobj.
+ */
+static ssize_t
+pci_read_rom(struct kobject *kobj, char *buf, loff_t off, size_t count)
+{
+	struct pci_dev *dev = to_pci_dev(container_of(kobj,struct device,kobj));
+	loff_t start;
+	size_t size;
+	unsigned char *rom;
+
+	if (dev->rom_info.rom) {
+		size = dev->rom_info.size;
+		rom = dev->rom_info.rom;
+		
+		if (off >= size)
+			return 0;
+			
+		if (off + count > size)
+			count = size - off;
+			
+		memcpy(buf, rom + off, count);
+	} else {
+		/* assign the ROM an address if it doesn't have one */
+		struct resource *res = &dev->resource[PCI_ROM_RESOURCE];
+		if (res->parent == NULL)
+			pci_assign_resource(dev, PCI_ROM_RESOURCE);
+			
+		start = pci_resource_start(dev, PCI_ROM_RESOURCE);
+		size = pci_resource_len(dev, PCI_ROM_RESOURCE);
+		
+		if (off >= size)
+			return 0;
+			
+		if (off + count > size)
+			count = size - off;
+			
+		/* Enable ROM space decodes and do the reads */
+		pci_enable_rom(dev);
+		
+		rom = ioremap(start, size);
+		if (rom) {
+			memcpy_fromio(buf, rom + off, count);
+			iounmap(rom);
+		} else
+			count = 0;
+			
+		/* Disable again before continuing, leave enabled if pci=rom */
+		if (!(res->flags & PCI_ROM_ADDRESS_ENABLE))
+			pci_disable_rom(dev);
+	}
+	return count;
+}
+
 static struct bin_attribute pci_config_attr = {
 	.attr =	{
 		.name = "config",
@@ -186,13 +283,45 @@
 	.write = pci_write_config,
 };
 
-void pci_create_sysfs_dev_files (struct pci_dev *pdev)
+static struct bin_attribute rom_attr = {
+	.attr =	{
+		.name = "rom",
+		.mode = S_IRUSR,
+		.owner = THIS_MODULE,
+	},
+	/* .size is set individually for each device, sysfs copies it into dentry */
+	.read = pci_read_rom,
+};
+
+void pci_create_sysfs_dev_files(struct pci_dev *pdev)
 {
 	if (pdev->cfg_size < 4096)
 		sysfs_create_bin_file(&pdev->dev.kobj, &pci_config_attr);
 	else
 		sysfs_create_bin_file(&pdev->dev.kobj, &pcie_config_attr);
 
+	/* If the device has a ROM, try to expose it in sysfs. */
+	if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) {
+		rom_attr.size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
+		sysfs_create_bin_file(&pdev->dev.kobj, &rom_attr);
+	}
 	/* add platform-specific attributes */
 	pcibios_add_platform_entries(pdev);
+}
+
+/**
+ * pci_remove_sysfs_dev_files - cleanup PCI specific sysfs files
+ * @pdev: device whose entries we should free
+ *
+ * Cleanup when @pdev is removed from sysfs.
+ */
+void pci_remove_sysfs_dev_files(struct pci_dev *pdev)
+{
+	if (pdev->cfg_size < 4096)
+		sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr);
+	else
+		sysfs_remove_bin_file(&pdev->dev.kobj, &pcie_config_attr);
+
+	if (pci_resource_len(pdev, PCI_ROM_RESOURCE))
+		sysfs_remove_bin_file(&pdev->dev.kobj, &rom_attr);
 }
===== drivers/pci/pci.h 1.12 vs edited =====
--- 1.12/drivers/pci/pci.h	Fri Jun  4 09:23:04 2004
+++ edited/drivers/pci/pci.h	Tue Aug  3 17:05:19 2004
@@ -3,6 +3,7 @@
 extern int pci_hotplug (struct device *dev, char **envp, int num_envp,
 			 char *buffer, int buffer_size);
 extern void pci_create_sysfs_dev_files(struct pci_dev *pdev);
+extern void pci_remove_sysfs_dev_files(struct pci_dev *pdev);
 extern int pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
 				  unsigned long size, unsigned long align,
 				  unsigned long min, unsigned int type_mask,
===== drivers/pci/probe.c 1.65 vs edited =====
--- 1.65/drivers/pci/probe.c	Fri May 21 14:45:27 2004
+++ edited/drivers/pci/probe.c	Tue Aug  3 17:05:19 2004
@@ -157,6 +157,8 @@
 #endif
 		}
 	}
+
+	dev->rom_info.rom = NULL;
 	if (rom) {
 		dev->rom_base_reg = rom;
 		res = &dev->resource[PCI_ROM_RESOURCE];
===== drivers/pci/remove.c 1.3 vs edited =====
--- 1.3/drivers/pci/remove.c	Tue Feb  3 12:17:30 2004
+++ edited/drivers/pci/remove.c	Tue Aug  3 17:05:20 2004
@@ -26,6 +26,9 @@
 static void pci_destroy_dev(struct pci_dev *dev)
 {
 	pci_proc_detach_device(dev);
+	/* Free the copy of the ROM if we made one */
+	kfree(dev->rom_info.rom);
+	pci_remove_sysfs_dev_files(dev);
 	device_unregister(&dev->dev);
 
 	/* Remove the device from the device lists, and prevent any further
===== include/linux/pci.h 1.132 vs edited =====
--- 1.132/include/linux/pci.h	Mon Aug  2 04:00:43 2004
+++ edited/include/linux/pci.h	Wed Aug  4 19:53:26 2004
@@ -471,6 +471,11 @@
 	pci_mmap_mem
 };
 
+struct rom_info {
+	char *rom; /* copy of the ROM if necessary */
+	size_t size;
+};
+
 /* This defines the direction arg to the DMA mapping routines. */
 #define PCI_DMA_BIDIRECTIONAL	0
 #define PCI_DMA_TODEVICE	1
@@ -537,6 +542,7 @@
 	unsigned int	is_busmaster:1; /* device is busmaster */
 	
 	unsigned int 	saved_config_space[16]; /* config space saved at suspend time */
+	struct rom_info rom_info; /* How and where to get the ROM info for this device */
 #ifdef CONFIG_PCI_NAMES
 #define PCI_NAME_SIZE	96
 #define PCI_NAME_HALF	__stringify(43)	/* less than half to handle slop */

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
  2004-08-04 15:57 Petr Vandrovec
@ 2004-08-04 17:06 ` Jesse Barnes
  2004-08-05  1:38   ` Jon Smirl
  0 siblings, 1 reply; 110+ messages in thread
From: Jesse Barnes @ 2004-08-04 17:06 UTC (permalink / raw)
  To: Petr Vandrovec; +Cc: Jon Smirl, linux-kernel

On Wednesday, August 4, 2004 8:57 am, Petr Vandrovec wrote:
> Test for failure? Test for no ROM devices?

If we get here, we can be sure there's a ROM device otherwise the 'rom' file 
wouldn't have been created.

> Please read it with readl. At least on my Matrox G550 reading 64KB ROM with
> byte accesses takes 1334ms, with 16bit accesses 840ms and with
> 32bit (or 64bit MMX) accesses 551ms. Straight (non-IO aware) memcpy
> takes 535ms. And put some conditional_schedule()s here, 550ms (or even
> 34ms for 4KB chunk) is IMHO too long.

memcpy_from_io maybe?  That would probably make the most sense.

Thanks for the comments.  Jon, do you want to respin it?

Thanks,
Jesse

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH] add PCI ROMs to sysfs
@ 2004-08-04 15:57 Petr Vandrovec
  2004-08-04 17:06 ` Jesse Barnes
  0 siblings, 1 reply; 110+ messages in thread
From: Petr Vandrovec @ 2004-08-04 15:57 UTC (permalink / raw)
  To: Jon Smirl; +Cc: linux-kernel

On  3 Aug 04 at 23:08, Jon Smirl wrote:

> +   unsigned long start;
> +   loff_t i, size;
> +   char direct_access = dev->rom_info.rom ? 0 : 1;
> +   unsigned char *rom = NULL;

What about using 'rom' variable for non-direct access too?

      unsigned char *rom = dev->rom_info.rom;
      char direct_access = rom == NULL;
      
> +   struct resource *r = &dev->resource[PCI_ROM_RESOURCE];
> +
> +   if (off > size)

You can do  'if (off >= size) ...' And well, where is size assigned at
all?

> +       return 0;
> +       
> +   if (direct_access) {
> +       /* assign the ROM an address if it doesn't have one */
> +       if (r->parent == NULL)
> +           pci_assign_resource(dev, PCI_ROM_RESOURCE);
> +       /* Enable ROM space decodes and do the reads */
> +       pci_enable_rom(dev);
> +       start = pci_resource_start(dev, PCI_ROM_RESOURCE);
> +       size = pci_resource_len(dev, PCI_ROM_RESOURCE);
> +       rom = ioremap(start, size);

Test for failure? Test for no ROM devices?

> +       
> +       printk("read_rom start %lx size %x\n", start, size);
> +       printk("rom bytes %02x %02x\n", rom, rom + 1);

readb(rom), readb(rom+1)?

> +   }
> +   if (off + count > size) {

Is off + count guaranteed to not overflow?

> +       size -= off;
> +       count = size;
> +   } else
> +       size = count;
> +
> +   i = 0;
> +   while (size > 0) {
> +       unsigned char val;
> +       if (direct_access)
> +           val = readb(rom + off);

Please read it with readl. At least on my Matrox G550 reading 64KB ROM with
byte accesses takes 1334ms, with 16bit accesses 840ms and with
32bit (or 64bit MMX) accesses 551ms. Straight (non-IO aware) memcpy 
takes 535ms. And put some conditional_schedule()s here, 550ms (or even
34ms for 4KB chunk) is IMHO too long.

> +       else
> +           val = *(dev->rom_info.rom + off);

If you made changes suggested at the beginning, you can do this instead:

              val = rom[off];

                                                Best regards,
                                                    Petr Vandrovec


^ permalink raw reply	[flat|nested] 110+ messages in thread

end of thread, other threads:[~2004-11-05 23:20 UTC | newest]

Thread overview: 110+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2004-07-30 21:09 [PATCH] add PCI ROMs to sysfs Jesse Barnes
2004-07-30 21:29 ` Greg KH
2004-07-30 21:34   ` Jesse Barnes
2004-07-30 21:39     ` Greg KH
2004-07-30 21:48     ` Jesse Barnes
2004-07-30 22:15     ` Jon Smirl
2004-07-31 15:59       ` Jesse Barnes
2004-08-02 17:02         ` Jesse Barnes
2004-08-02 17:29           ` Jon Smirl
2004-08-02 21:00           ` Jon Smirl
2004-08-02 21:05             ` Jesse Barnes
2004-08-02 23:32               ` Alan Cox
2004-08-02 23:30           ` Alan Cox
2004-08-03  2:03             ` Jesse Barnes
2004-08-03  2:32               ` Jon Smirl
2004-08-03 17:07                 ` Jesse Barnes
2004-08-03 21:19               ` Jon Smirl
2004-08-03 21:28                 ` Jesse Barnes
2004-08-03 21:30                   ` Jesse Barnes
2004-08-03 21:31                   ` Martin Mares
2004-08-03 21:36                     ` Jon Smirl
2004-08-03 21:39                       ` Martin Mares
2004-08-04  6:00                         ` lspci and ROM on bridge, was: " Jon Smirl
2004-08-04 16:04                           ` Martin Mares
2004-08-05  5:05                         ` Jon Smirl
2004-08-05  5:41                           ` Benjamin Herrenschmidt
2004-08-05 11:53                             ` Jon Smirl
2004-08-05 15:54                           ` Jesse Barnes
2004-08-05 16:25                             ` Jesse Barnes
2004-08-05 20:45                               ` Jon Smirl
2004-08-05 21:12                                 ` Jesse Barnes
2004-08-06 21:14                                   ` Jon Smirl
2004-08-06 22:33                                     ` Jesse Barnes
2004-08-11 17:04                                     ` Jesse Barnes
2004-08-11 17:28                                       ` Greg KH
2004-08-11 18:02                                         ` Jesse Barnes
2004-08-11 18:12                                           ` Greg KH
2004-08-12  1:28                                             ` Marcelo Tosatti
2004-08-12 14:38                                               ` Jesse Barnes
2004-08-12 17:29                                               ` Greg KH
2004-08-12  2:25                                             ` Miles Bader
2004-08-12  4:38                                               ` Greg KH
2004-08-12  9:18                                                 ` Geert Uytterhoeven
2004-08-12 22:01                                                 ` Matthew Wilcox
2004-08-11 19:24                                         ` Jon Smirl
2004-08-11 19:44                                           ` Jesse Barnes
2004-08-11 20:11                                           ` Alan Cox
2004-08-11 23:31                                             ` Jon Smirl
2004-08-12 11:51                                               ` Alan Cox
2004-08-12 20:28                                                 ` Jon Smirl
2004-08-12  0:45                                         ` Jon Smirl
2004-08-12  4:37                                           ` Greg KH
2004-08-04  6:08           ` Jon Smirl
2004-08-04 15:56             ` Jesse Barnes
2004-07-30 21:53   ` Jon Smirl
2004-07-31 10:03     ` Vojtech Pavlik
2004-07-31 13:28       ` Jon Smirl
2004-07-31 15:42         ` Greg KH
2004-08-04 15:57 Petr Vandrovec
2004-08-04 17:06 ` Jesse Barnes
2004-08-05  1:38   ` Jon Smirl
2004-08-11 23:27 Pallipadi, Venkatesh
2004-08-12  0:22 ` Jon Smirl
2004-08-12  2:22 ` Jon Smirl
2004-08-13 18:17   ` Martin Mares
2004-08-14  5:34     ` Jon Smirl
2004-08-14  9:47       ` Martin Mares
2004-08-14 14:17         ` Jon Smirl
2004-08-14 22:10           ` Martin Mares
2004-08-14 23:39             ` Jon Smirl
2004-08-18 18:13             ` Jon Smirl
2004-08-18 18:37               ` Jesse Barnes
2004-08-23 22:51                 ` Greg KH
2004-08-25 17:32                   ` Jon Smirl
2004-08-25 17:42                     ` Greg KH
2004-08-25 18:06                       ` Jon Smirl
2004-08-25 18:19                         ` Greg KH
2004-08-25 18:45                           ` Jon Smirl
2004-08-25 18:55                             ` Greg KH
2004-08-25 20:06                               ` Jon Smirl
2004-08-26 13:13                                 ` Matthew Wilcox
2004-08-26 15:40                                   ` Jon Smirl
2004-08-26 15:58                                     ` Matthew Wilcox
2004-08-26 19:54                                       ` Jon Smirl
2004-08-28 16:15                                         ` Matthew Wilcox
2004-08-28 17:33                                           ` Jon Smirl
2004-08-28 17:38                                           ` Jon Smirl
2004-08-27 16:43                                       ` Matthew Wilcox
2004-08-27 22:29                                         ` Jon Smirl
2004-08-28 16:35                                           ` Matthew Wilcox
2004-08-28 21:53                                             ` Grant Grundler
2004-08-25 18:29                         ` Matthew Wilcox
2004-08-19 12:51               ` Alan Cox
2004-08-19 23:00                 ` Jon Smirl
2004-08-19 14:01               ` Martin Mares
2004-08-19 23:11                 ` Jon Smirl
2004-08-20 10:26                   ` Martin Mares
2004-08-25 15:36               ` Matthew Wilcox
2004-08-25 15:50                 ` Jon Smirl
2004-08-12  8:39 Thomas Winischhofer
2004-08-29  4:58 Jon Smirl
2004-09-03  1:40 Jon Smirl
2004-09-03 17:27 ` Jesse Barnes
2004-09-03 17:45   ` Jesse Barnes
2004-09-03 18:06     ` Jesse Barnes
2004-09-08  3:15 Jon Smirl
2004-09-08  6:07 ` Greg KH
2004-09-08 23:50 ` Greg KH
2004-10-08  2:20   ` Jon Smirl
2004-11-05 23:06     ` Greg KH

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.