diff mbox

[v5,18/34] cxlflash: Fix AFU version access/storage and add check

Message ID 1443715012-18236-1-git-send-email-mrochs@linux.vnet.ibm.com (mailing list archive)
State Superseded, archived
Headers show

Commit Message

Matthew R. Ochs Oct. 1, 2015, 3:56 p.m. UTC
The AFU version is stored as a non-terminated string of bytes within
a 64-bit little-endian register. Presently the value is read directly
(no MMIO accessor) and is stored in a buffer that is not big enough
to contain a NULL terminator. Additionally the version obtained is not
evaluated against a known value to prevent usage with unsupported AFUs.
All of these deficiencies can lead to a variety of problems.

To remedy, use the correct MMIO accessor to read the version value into
a null-terminated buffer and add a check to prevent an incompatible AFU
from being used with this driver.

Signed-off-by: Matthew R. Ochs <mrochs@linux.vnet.ibm.com>
Signed-off-by: Manoj N. Kumar <manoj@linux.vnet.ibm.com>
Reviewed-by: Brian King <brking@linux.vnet.ibm.com>
---
 drivers/scsi/cxlflash/common.h  |  2 +-
 drivers/scsi/cxlflash/main.c    | 18 ++++++++++++------
 drivers/scsi/cxlflash/sislite.h |  2 +-
 3 files changed, 14 insertions(+), 8 deletions(-)

Comments

Daniel Axtens Oct. 2, 2015, 12:56 a.m. UTC | #1
"Matthew R. Ochs" <mrochs@linux.vnet.ibm.com> writes:

> The AFU version is stored as a non-terminated string of bytes within
> a 64-bit little-endian register. Presently the value is read directly
> (no MMIO accessor) and is stored in a buffer that is not big enough
> to contain a NULL terminator. Additionally the version obtained is not
> evaluated against a known value to prevent usage with unsupported AFUs.
> All of these deficiencies can lead to a variety of problems.
>
> +	if ((afu->interface_version + 1) == 0) {
> +		pr_err("Back level AFU, please upgrade. AFU version %s "
> +		       "interface version 0x%llx\n", afu->version,
> +		       afu->interface_version);
> +		rc = -EINVAL;
> +		goto err1;

I'm confused by this if statement. If afu->interface_version + 1 == 0,
and interface_version is a 64bit unsigned int, that would mean that
afu->interface_version was 0xFFFF FFFF FFFF FFFF.

Are you trying to check against all Fs? Is that value significant in the
hardware?

Regards,
Daniel



> +	} else
> +		pr_debug("%s: afu version %s, interface version 0x%llX\n",
> +			 __func__, afu->version, afu->interface_version);
>  
>  	rc = start_afu(cfg);
>  	if (rc) {
> diff --git a/drivers/scsi/cxlflash/sislite.h b/drivers/scsi/cxlflash/sislite.h
> index 63bf394..8425d1a 100644
> --- a/drivers/scsi/cxlflash/sislite.h
> +++ b/drivers/scsi/cxlflash/sislite.h
> @@ -340,7 +340,7 @@ struct sisl_global_regs {
>  #define SISL_AFUCONF_MBOX_CLR_READ     0x0010ULL
>  	__be64 afu_config;
>  	__be64 rsvd[0xf8];
> -	__be64 afu_version;
> +	__le64 afu_version;
>  	__be64 interface_version;
>  };
>  
> -- 
> 2.1.0
>
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/linuxppc-dev
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Matthew R. Ochs Oct. 2, 2015, 1:18 a.m. UTC | #2
> On Oct 1, 2015, at 7:56 PM, Daniel Axtens <dja@axtens.net> wrote:
> 
> "Matthew R. Ochs" <mrochs@linux.vnet.ibm.com> writes:
> 
>> The AFU version is stored as a non-terminated string of bytes within
>> a 64-bit little-endian register. Presently the value is read directly
>> (no MMIO accessor) and is stored in a buffer that is not big enough
>> to contain a NULL terminator. Additionally the version obtained is not
>> evaluated against a known value to prevent usage with unsupported AFUs.
>> All of these deficiencies can lead to a variety of problems.
>> 
>> +	if ((afu->interface_version + 1) == 0) {
>> +		pr_err("Back level AFU, please upgrade. AFU version %s "
>> +		       "interface version 0x%llx\n", afu->version,
>> +		       afu->interface_version);
>> +		rc = -EINVAL;
>> +		goto err1;
> 
> I'm confused by this if statement. If afu->interface_version + 1 == 0,
> and interface_version is a 64bit unsigned int, that would mean that
> afu->interface_version was 0xFFFF FFFF FFFF FFFF.
> 
> Are you trying to check against all Fs? Is that value significant in the
> hardware?

Correct, downlevel (unsupported) AFUs don't implement the interface_version
register and thus will return -1 at that offset.


--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Daniel Axtens Oct. 2, 2015, 4:17 a.m. UTC | #3
"Matthew R. Ochs" <mrochs@linux.vnet.ibm.com> writes:

> Correct, downlevel (unsupported) AFUs don't implement the interface_version
> register and thus will return -1 at that offset.
Cool. It would be good to document that somewhere, either the comment or
the commit message, but it's not worth holding up the series for it.

I also tend to use the form (var == ~0ULL) for tests like this but
that's an aesthetic thing.

Reviewed-by: Daniel Axtens <dja@axtens.net>

Regards,
Daniel
diff mbox

Patch

diff --git a/drivers/scsi/cxlflash/common.h b/drivers/scsi/cxlflash/common.h
index faf7f56..3be5754 100644
--- a/drivers/scsi/cxlflash/common.h
+++ b/drivers/scsi/cxlflash/common.h
@@ -179,7 +179,7 @@  struct afu {
 	u32 cmd_couts;		/* Number of command checkouts */
 	u32 internal_lun;	/* User-desired LUN mode for this AFU */
 
-	char version[8];
+	char version[16];
 	u64 interface_version;
 
 	struct cxlflash_cfg *parent; /* Pointer back to parent cxlflash_cfg */
diff --git a/drivers/scsi/cxlflash/main.c b/drivers/scsi/cxlflash/main.c
index c25efc3..c1d5c88 100644
--- a/drivers/scsi/cxlflash/main.c
+++ b/drivers/scsi/cxlflash/main.c
@@ -1751,14 +1751,20 @@  static int init_afu(struct cxlflash_cfg *cfg)
 		goto err1;
 	}
 
-	/* don't byte reverse on reading afu_version, else the string form */
-	/*     will be backwards */
-	reg = afu->afu_map->global.regs.afu_version;
-	memcpy(afu->version, &reg, 8);
+	/* No byte reverse on reading afu_version or string will be backwards */
+	reg = readq(&afu->afu_map->global.regs.afu_version);
+	memcpy(afu->version, &reg, sizeof(reg));
 	afu->interface_version =
 	    readq_be(&afu->afu_map->global.regs.interface_version);
-	pr_debug("%s: afu version %s, interface version 0x%llX\n",
-		 __func__, afu->version, afu->interface_version);
+	if ((afu->interface_version + 1) == 0) {
+		pr_err("Back level AFU, please upgrade. AFU version %s "
+		       "interface version 0x%llx\n", afu->version,
+		       afu->interface_version);
+		rc = -EINVAL;
+		goto err1;
+	} else
+		pr_debug("%s: afu version %s, interface version 0x%llX\n",
+			 __func__, afu->version, afu->interface_version);
 
 	rc = start_afu(cfg);
 	if (rc) {
diff --git a/drivers/scsi/cxlflash/sislite.h b/drivers/scsi/cxlflash/sislite.h
index 63bf394..8425d1a 100644
--- a/drivers/scsi/cxlflash/sislite.h
+++ b/drivers/scsi/cxlflash/sislite.h
@@ -340,7 +340,7 @@  struct sisl_global_regs {
 #define SISL_AFUCONF_MBOX_CLR_READ     0x0010ULL
 	__be64 afu_config;
 	__be64 rsvd[0xf8];
-	__be64 afu_version;
+	__le64 afu_version;
 	__be64 interface_version;
 };