lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <1165651931.7443.377.camel@gullible>
Date:	Sat, 09 Dec 2006 03:12:11 -0500
From:	Ben Collins <ben.collins@...ntu.com>
To:	Alan <alan@...rguk.ukuu.org.uk>
Cc:	Ralf Baechle <ralf@...ux-mips.org>, Andrew Morton <akpm@...l.org>,
	linux-kernel@...r.kernel.org
Subject: Re: [PATCH] PCI legacy resource fix

On Sat, 2006-12-09 at 02:46 +0000, Alan wrote:
> > Checking the patch, my problem is that the old way, all BAR's were being
> > set at start = end = flags = 0. The patch makes it set all the BAR's to
> 
> Yes the old quirk used to blank the resources as the values on the chip
> are undefined and random. This gives you corrupt resource trees and needs
> hacks in the drivers as well
> 
> > the normal values. This is what it looks like in lspci, pre this patch:
> > 
> >         Region 0: I/O ports at <unassigned>
> >         Region 1: I/O ports at <unassigned>
> >         Region 2: I/O ports at <unassigned>
> >         Region 3: I/O ports at <unassigned>
> 
> Then your device is in legacy mode, or was disabled
>  
> > So my device is not running in compatibility mode, and should not have
> 
> The paste you have their shows that it almost certainly is in legacy mode.
> 
> > the BAR's set, as Alan's patch does.
> 
> Dump the class code and other bits during boot check how your device is
> seen (native v legacy/compatibility) and whether the fixup logic
> triggers. It should only trigger for legacy devices.

You're right, I was reading this backwards.

Thing is, if I disable the quirk, ata_piix works correctly.

So I think maybe the problem is the logic here. Having the PIIX quirk
pre-reserve SATA ports that are on legacy BAR's just doesn't seem to
make sense unless libata is going to recognize that. I think it's just
an accident that it worked before.

Looking at ata_pci_init_one(), it does check for this:

        if (legacy_mode) {
                if (!request_region(ATA_PRIMARY_CMD, 8, "libata")) {
                        struct resource *conflict, res;
                        res.start = ATA_PRIMARY_CMD;
                        res.end = ATA_PRIMARY_CMD + 8 - 1;
                        conflict = ____request_resource(&ioport_resource, &res);
                        while (conflict->child)
                                conflict = ____request_resource(conflict, &res);
                        if (!strcmp(conflict->name, "libata"))
                                legacy_mode |= ATA_PORT_PRIMARY;

My controller is in legacy mode, however, it never gets to here because
of this call, just before this block of code:

        rc = pci_request_regions(pdev, DRV_NAME);
        if (rc) {
                disable_dev_on_err = 0;
                goto err_out;
        }

So, I wrote up this patch that made things work for me. It also should
make ata_pci_init_one() work with mixed legacy/native ports on the same
controller (if there are such things). My controller is port0=SATA,
port1=PATA.

diff --git a/drivers/ata/libata-sff.c b/drivers/ata/libata-sff.c
index 10ee22a..8897eba 100644
--- a/drivers/ata/libata-sff.c
+++ b/drivers/ata/libata-sff.c
@@ -852,11 +852,14 @@ #ifdef CONFIG_PCI
 struct ata_probe_ent *
 ata_pci_init_native_mode(struct pci_dev *pdev, struct ata_port_info **port, int ports)
 {
-	struct ata_probe_ent *probe_ent =
-		ata_probe_ent_alloc(pci_dev_to_dev(pdev), port[0]);
+	struct ata_probe_ent *probe_ent;
 	int p = 0;
 	unsigned long bmdma;
 
+	if (!(ports & (ATA_PORT_PRIMARY|ATA_PORT_SECONDARY)))
+		return NULL;
+
+	probe_ent = ata_probe_ent_alloc(pci_dev_to_dev(pdev), port[0]);
 	if (!probe_ent)
 		return NULL;
 
@@ -906,6 +909,9 @@ static struct ata_probe_ent *ata_pci_ini
 	struct ata_probe_ent *probe_ent;
 	unsigned long bmdma = pci_resource_start(pdev, 4);
 
+	if (!(port_mask & (ATA_PORT_PRIMARY|ATA_PORT_SECONDARY)))
+		return NULL;
+
 	probe_ent = ata_probe_ent_alloc(pci_dev_to_dev(pdev), port[0]);
 	if (!probe_ent)
 		return NULL;
@@ -979,10 +985,10 @@ static struct ata_probe_ent *ata_pci_ini
 int ata_pci_init_one (struct pci_dev *pdev, struct ata_port_info **port_info,
 		      unsigned int n_ports)
 {
-	struct ata_probe_ent *probe_ent = NULL;
+	struct ata_probe_ent *probe_ent_legacy = NULL;
+	struct ata_probe_ent *probe_ent_native = NULL;
 	struct ata_port_info *port[2];
-	u8 mask;
-	unsigned int legacy_mode = 0;
+	unsigned int legacy_mode = 0, legacy_probe = 0, native_probe = 0;
 	int disable_dev_on_err = 1;
 	int rc;
 
@@ -1011,29 +1017,32 @@ int ata_pci_init_one (struct pci_dev *pd
 	if ((pdev->class >> 8) == PCI_CLASS_STORAGE_IDE) {
 		u8 tmp8;
 
-		/* TODO: What if one channel is in native mode ... */
 		pci_read_config_byte(pdev, PCI_CLASS_PROG, &tmp8);
-		mask = (1 << 2) | (1 << 0);
-		if ((tmp8 & mask) != mask)
-			legacy_mode = (1 << 3);
+
+		if ((tmp8 & ATA_PORT_PRIMARY_LEGACY) == 0)
+			legacy_mode |= ATA_PORT_PRIMARY;
+		if ((tmp8 & ATA_PORT_SECONDARY_LEGACY) == 0)
+			legacy_mode |= ATA_PORT_SECONDARY;
+
 #if defined(CONFIG_NO_ATA_LEGACY)
 		/* Some platforms with PCI limits cannot address compat
 		   port space. In that case we punt if their firmware has
 		   left a device in compatibility mode */
 		if (legacy_mode) {
-			printk(KERN_ERR "ata: Compatibility mode ATA is not supported on this platform, skipping.\n");
+			printk(KERN_ERR "ata: Compatibility mode ATA is not " \
+					"supported on this platform, skipping.\n");
 			return -EOPNOTSUPP;
 		}
 #endif
 	}
 
-	rc = pci_request_regions(pdev, DRV_NAME);
+	rc = pci_request_region(pdev, 4, DRV_NAME);
 	if (rc) {
 		disable_dev_on_err = 0;
 		goto err_out;
 	}
 
-	if (legacy_mode) {
+	if (legacy_mode & ATA_PORT_PRIMARY) {
 		if (!request_region(ATA_PRIMARY_CMD, 8, "libata")) {
 			struct resource *conflict, res;
 			res.start = ATA_PRIMARY_CMD;
@@ -1042,7 +1051,7 @@ #endif
 			while (conflict->child)
 				conflict = ____request_resource(conflict, &res);
 			if (!strcmp(conflict->name, "libata"))
-				legacy_mode |= ATA_PORT_PRIMARY;
+				legacy_probe |= ATA_PORT_PRIMARY;
 			else {
 				disable_dev_on_err = 0;
 				printk(KERN_WARNING "ata: 0x%0X IDE port busy\n" \
@@ -1051,8 +1060,19 @@ #endif
 						    conflict->name);
 			}
 		} else
-			legacy_mode |= ATA_PORT_PRIMARY;
+			legacy_probe |= ATA_PORT_PRIMARY;
+	} else {
+		if (!pci_request_region(pdev, 0, DRV_NAME)) {
+			if (!pci_request_region(pdev, 1, DRV_NAME))
+				native_probe |= ATA_PORT_PRIMARY;
+			else {
+				disable_dev_on_err = 0;
+				pci_release_region(pdev, 0);
+			}
+		}
+	}
 
+	if (legacy_mode & ATA_PORT_SECONDARY) {
 		if (!request_region(ATA_SECONDARY_CMD, 8, "libata")) {
 			struct resource *conflict, res;
 			res.start = ATA_SECONDARY_CMD;
@@ -1061,7 +1081,7 @@ #endif
 			while (conflict->child)
 				conflict = ____request_resource(conflict, &res);
 			if (!strcmp(conflict->name, "libata"))
-				legacy_mode |= ATA_PORT_SECONDARY;
+				legacy_probe |= ATA_PORT_SECONDARY;
 			else {
 				disable_dev_on_err = 0;
 				printk(KERN_WARNING "ata: 0x%X IDE port busy\n" \
@@ -1070,11 +1090,20 @@ #endif
 						    conflict->name);
 			}
 		} else
-			legacy_mode |= ATA_PORT_SECONDARY;
-	}
+			legacy_probe |= ATA_PORT_SECONDARY;
+	} else if (n_ports > 1) {
+		if (!pci_request_region(pdev, 2, DRV_NAME)) {
+			if (!pci_request_region(pdev, 3, DRV_NAME))
+				native_probe = ATA_PORT_SECONDARY;
+			else {
+				disable_dev_on_err = 0;
+				pci_release_region(pdev, 2);
+			}
+                }
+        }
 
-	/* we have legacy mode, but all ports are unavailable */
-	if (legacy_mode == (1 << 3)) {
+	/* Didn't enable any ports */
+	if (!legacy_probe && !native_probe) {
 		rc = -EBUSY;
 		goto err_out_regions;
 	}
@@ -1087,38 +1116,58 @@ #endif
 	if (rc)
 		goto err_out_regions;
 
-	if (legacy_mode) {
-		probe_ent = ata_pci_init_legacy_port(pdev, port, legacy_mode);
-	} else {
-		if (n_ports == 2)
-			probe_ent = ata_pci_init_native_mode(pdev, port, ATA_PORT_PRIMARY | ATA_PORT_SECONDARY);
-		else
-			probe_ent = ata_pci_init_native_mode(pdev, port, ATA_PORT_PRIMARY);
-	}
-	if (!probe_ent) {
+	probe_ent_legacy = ata_pci_init_legacy_port(pdev, port, legacy_probe);
+	probe_ent_native = ata_pci_init_native_mode(pdev, port, native_probe);
+
+	if (!probe_ent_legacy && !probe_ent_native) {
 		rc = -ENOMEM;
 		goto err_out_regions;
 	}
 
 	pci_set_master(pdev);
 
-	if (!ata_device_add(probe_ent)) {
+	if (probe_ent_legacy) {
+		if (!ata_device_add(probe_ent_legacy)) {
+			printk(KERN_WARNING "ata: Failed to add legacy device(s)\n");
+			kfree(probe_ent_legacy);
+			probe_ent_legacy = NULL;
+		}
+	}
+	if (probe_ent_native) {
+		if (!ata_device_add(probe_ent_native)) {
+			printk(KERN_WARNING "ata: Failed to add native device(s)\n");
+			kfree(probe_ent_native);
+			probe_ent_native = NULL;
+		}
+	}
+
+	/* Did both fail? If so, no devices to see here. */
+	if (!probe_ent_native && !probe_ent_legacy) {
 		rc = -ENODEV;
-		goto err_out_ent;
+		goto err_out_regions;
 	}
 
-	kfree(probe_ent);
+	kfree(probe_ent_legacy);
+	kfree(probe_ent_native);
 
 	return 0;
 
-err_out_ent:
-	kfree(probe_ent);
 err_out_regions:
-	if (legacy_mode & ATA_PORT_PRIMARY)
+	if (legacy_probe & ATA_PORT_PRIMARY)
 		release_region(ATA_PRIMARY_CMD, 8);
-	if (legacy_mode & ATA_PORT_SECONDARY)
+	else if (native_probe & ATA_PORT_PRIMARY) {
+		pci_release_region(pdev, 0);
+		pci_release_region(pdev, 1);
+	}
+
+	if (legacy_probe & ATA_PORT_SECONDARY)
 		release_region(ATA_SECONDARY_CMD, 8);
-	pci_release_regions(pdev);
+	else if (native_probe & ATA_PORT_SECONDARY) {
+		pci_release_region(pdev, 2);
+		pci_release_region(pdev, 3);
+	}
+
+	pci_release_region(pdev, 4);
 err_out:
 	if (disable_dev_on_err)
 		pci_disable_device(pdev);
diff --git a/include/linux/libata.h b/include/linux/libata.h
index ab27548..1f0a200 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -260,6 +260,10 @@ enum {
 	ATA_PORT_PRIMARY	= (1 << 0),
 	ATA_PORT_SECONDARY	= (1 << 1),
 
+	/* masks for port legacy mode */
+	ATA_PORT_PRIMARY_LEGACY	= (1 << 0),
+	ATA_PORT_SECONDARY_LEGACY = (1 << 2),
+
 	/* ering size */
 	ATA_ERING_SIZE		= 32,
 

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ