diff mbox series

thunderbolt: thunderbolt: add vendor's NVM formats

Message ID 20220815041145.35629-1-chensiying21@gmail.com (mailing list archive)
State Superseded
Headers show
Series thunderbolt: thunderbolt: add vendor's NVM formats | expand

Commit Message

Szuying Chen Aug. 15, 2022, 4:11 a.m. UTC
From: Szuying Chen <Chloe_Chen@asmedia.com.tw>

The patch add tb_nvm_validate() contain an array that has functions
pointers to asmedia_nvm_validate().
And asmedia_nvm_validate() that recognize supported vendor works in one
of the following cases:
Case nvm_upgrade: enable nvm's attribute by setting no_nvm_upgrade
flag to create nvm_authenticate file node.
Case nvm_add:add active/non-active NVM devices.
Case nvm_write:update firmware to non-ative NVM device.

Our patches were through checkpatch.pl. But the file(switch.c.)
have existed 13 warning before we patch it.

Signed-off-by: Szuying Chen <Chloe_Chen@asmedia.com.tw>
---
 drivers/thunderbolt/nvm.c    | 147 +++++++++++++++++++++++++++++++++++
 drivers/thunderbolt/switch.c |  17 ++++
 drivers/thunderbolt/tb.h     |  18 +++++
 3 files changed, 182 insertions(+)

--
2.34.1

Comments

Greg KH Aug. 15, 2022, 6:38 a.m. UTC | #1
On Mon, Aug 15, 2022 at 12:11:45PM +0800, Szuying Chen wrote:
> From: Szuying Chen <Chloe_Chen@asmedia.com.tw>
> 
> The patch add tb_nvm_validate() contain an array that has functions
> pointers to asmedia_nvm_validate().
> And asmedia_nvm_validate() that recognize supported vendor works in one
> of the following cases:
> Case nvm_upgrade: enable nvm's attribute by setting no_nvm_upgrade
> flag to create nvm_authenticate file node.
> Case nvm_add:add active/non-active NVM devices.
> Case nvm_write:update firmware to non-ative NVM device.
> 
> Our patches were through checkpatch.pl. But the file(switch.c.)
> have existed 13 warning before we patch it.
> 
> Signed-off-by: Szuying Chen <Chloe_Chen@asmedia.com.tw>
> ---
>  drivers/thunderbolt/nvm.c    | 147 +++++++++++++++++++++++++++++++++++
>  drivers/thunderbolt/switch.c |  17 ++++
>  drivers/thunderbolt/tb.h     |  18 +++++
>  3 files changed, 182 insertions(+)

Hi,

This is the friendly patch-bot of Greg Kroah-Hartman.  You have sent him
a patch that has triggered this response.  He used to manually respond
to these common problems, but in order to save his sanity (he kept
writing the same thing over and over, yet to different people), I was
created.  Hopefully you will not take offence and will fix the problem
in your patch and resubmit it so that it can be accepted into the Linux
kernel tree.

You are receiving this message because of the following common error(s)
as indicated below:

- This looks like a new version of a previously submitted patch, but you
  did not list below the --- line any changes from the previous version.
  Please read the section entitled "The canonical patch format" in the
  kernel file, Documentation/SubmittingPatches for what needs to be done
  here to properly describe this.

If you wish to discuss this problem further, or you have questions about
how to resolve this issue, please feel free to respond to this email and
Greg will reply once he has dug out from the pending patches received
from other developers.

thanks,

greg k-h's patch email bot
Greg KH Aug. 15, 2022, 6:40 a.m. UTC | #2
On Mon, Aug 15, 2022 at 12:11:45PM +0800, Szuying Chen wrote:
> +/* ASMedia specific validation mode */
> +#define nvm_upgrade 0
> +#define nvm_add 1
> +#define nvm_write 2

Why is this not an enum?

And why is this specific to a single vendor, yet that vendor name is not
in the #define?

> +struct nvm_asmedia {
> +	u32 date;

__le32?

> +	u32 customerID:16;
> +	u32 version:8;
> +	u32 reserved:8;

Are you sure these bitfields are correct on all platforms?

thanks,

greg k-h
kernel test robot Aug. 15, 2022, 1:12 p.m. UTC | #3
Hi Szuying,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on linus/master]
[also build test WARNING on v6.0-rc1 next-20220815]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Szuying-Chen/thunderbolt-thunderbolt-add-vendor-s-NVM-formats/20220815-121330
base:   https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git 568035b01cfb107af8d2e4bd2fb9aea22cf5b868
config: arc-allyesconfig (https://download.01.org/0day-ci/archive/20220815/202208152107.P6jLM6te-lkp@intel.com/config)
compiler: arceb-elf-gcc (GCC) 12.1.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/intel-lab-lkp/linux/commit/de39c1897bc8c08ddfa4d3019c95314e697b7256
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Szuying-Chen/thunderbolt-thunderbolt-add-vendor-s-NVM-formats/20220815-121330
        git checkout de39c1897bc8c08ddfa4d3019c95314e697b7256
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=arc SHELL=/bin/bash drivers/thunderbolt/

If you fix the issue, kindly add following tag where applicable
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

   drivers/thunderbolt/nvm.c:150: warning: Function parameter or member 'mode' not described in 'tb_nvm_validate'
>> drivers/thunderbolt/nvm.c:150: warning: expecting prototype for tb_nvm_vendor_handle(). Prototype was for tb_nvm_validate() instead


vim +150 drivers/thunderbolt/nvm.c

   143	
   144	/**
   145	 * tb_nvm_vendor_handle() - support vendor's NVM format
   146	 * @sw: Thunderbolt switch
   147	 * @handle: 0:NvmUpgradeSuppport, 1:NvmAdd, 2:NvmWrite
   148	 */
   149	int tb_nvm_validate(struct tb_switch *sw, unsigned int mode)
 > 150	{
   151		int res, i;
   152	
   153		for (i = 0; i < ARRAY_SIZE(tb_nvm_vendors); i++) {
   154			const struct tb_nvm_id *id = &tb_nvm_vendors[i];
   155	
   156			if (id->hw_vendor_id && id->hw_vendor_id != sw->config.vendor_id)
   157				continue;
   158	
   159			 res = id->validate(sw, mode);
   160		}
   161		return res;
   162	}
   163
Mario Limonciello Aug. 15, 2022, 5:28 p.m. UTC | #4
+hughsie for additional comments

Various inline comments below.

On 8/14/2022 23:11, Szuying Chen wrote:
> From: Szuying Chen <Chloe_Chen@asmedia.com.tw>
> 
> The patch add tb_nvm_validate() contain an array that has functions
> pointers to asmedia_nvm_validate().
> And asmedia_nvm_validate() that recognize supported vendor works in one
> of the following cases:
> Case nvm_upgrade: enable nvm's attribute by setting no_nvm_upgrade
> flag to create nvm_authenticate file node.
> Case nvm_add:add active/non-active NVM devices.
> Case nvm_write:update firmware to non-ative NVM device.
> 
> Our patches were through checkpatch.pl. But the file(switch.c.)
> have existed 13 warning before we patch it.

Please feel free to add other patches to the series to clean up 
warnings.  Just because you didn't introduce them doesn't mean you can't 
fix them =)

> 
> Signed-off-by: Szuying Chen <Chloe_Chen@asmedia.com.tw>
> ---
>   drivers/thunderbolt/nvm.c    | 147 +++++++++++++++++++++++++++++++++++
>   drivers/thunderbolt/switch.c |  17 ++++
>   drivers/thunderbolt/tb.h     |  18 +++++
>   3 files changed, 182 insertions(+)
> 
> diff --git a/drivers/thunderbolt/nvm.c b/drivers/thunderbolt/nvm.c
> index b3f310389378..6db2034ec8e5 100644
> --- a/drivers/thunderbolt/nvm.c
> +++ b/drivers/thunderbolt/nvm.c
> @@ -9,11 +9,158 @@
>   #include <linux/idr.h>
>   #include <linux/slab.h>
>   #include <linux/vmalloc.h>
> +#include <linux/pm_runtime.h>
> 
>   #include "tb.h"
> 
>   static DEFINE_IDA(nvm_ida);
> 
> +static int tb_switch_nvm_read(void *priv, unsigned int offset, void *val,
> +			      size_t bytes)
> +{
> +	struct tb_nvm *nvm = priv;
> +	struct tb_switch *sw = tb_to_switch(nvm->dev);
> +	int ret;
> +
> +	pm_runtime_get_sync(&sw->dev);
> +	if (!mutex_trylock(&sw->tb->lock)) {
> +		ret = restart_syscall();
> +		goto out;
> +	}
> +	ret = usb4_switch_nvm_read(sw, offset, val, bytes);
> +	mutex_unlock(&sw->tb->lock);
> +
> +out:
> +	pm_runtime_mark_last_busy(&sw->dev);
> +	pm_runtime_put_autosuspend(&sw->dev);
> +
> +	return ret;
> +}
> +
> +static int tb_switch_nvm_write(void *priv, unsigned int offset, void *val,
> +			       size_t bytes)
> +{
> +	struct tb_nvm *nvm = priv;
> +	struct tb_switch *sw = tb_to_switch(nvm->dev);
> +	int ret;
> +
> +	if (!mutex_trylock(&sw->tb->lock))
> +		return restart_syscall();
> +
> +	/*
> +	 * Since writing the NVM image might require some special steps,
> +	 * for example when CSS headers are written, we cache the image
> +	 * locally here and handle the special cases when the user asks
> +	 * us to authenticate the image.
> +	 */
> +	ret = tb_nvm_write_buf(nvm, offset, val, bytes);
> +	mutex_unlock(&sw->tb->lock);
> +
> +	return ret;
> +}
> +
> +static int asmedia_nvm_validate(struct tb_switch *sw, unsigned int mode)
> +{
> +	struct tb_nvm *nvm;
> +	u32 val;
> +	u32 nvm_size;
> +	int ret = 0;
> +	unsigned int image_size;
> +	const u8 *buf = sw->nvm->buf;
> +
> +	switch (mode) {
> +	case nvm_upgrade:
> +		if (sw->no_nvm_upgrade)
> +			sw->no_nvm_upgrade = false;
> +
> +		break;
> +
> +	case nvm_add:
> +		nvm = tb_nvm_alloc(&sw->dev);
> +		if (IS_ERR(nvm)) {
> +			ret = PTR_ERR(nvm);
> +			break;
> +		}
> +
> +		ret = usb4_switch_nvm_read(sw, NVM_Date, &val, sizeof(val));
> +		if (ret)
> +			break;
> +
> +		nvm->nvm_asm.date = (((u8)val) << 0x10 | ((u8)(val >> 0x8)) << 0x8 | (u8)(val >> 0x10));
> +		ret = usb4_switch_nvm_read(sw, NVM_CUSTOMER_ID, &val, sizeof(val));
> +		if (ret)
> +			break;
> +
> +		nvm->nvm_asm.customerID = (((u8)val) << 0x8 | ((u8)(val >> 0x8)));
> +		nvm->nvm_asm.version = (u8)(val >> 0x10);
> +		nvm_size = SZ_512K;
> +		ret = tb_nvm_add_active(nvm, nvm_size, tb_switch_nvm_read);
> +		if (ret)
> +			break;
> +
> +		ret = tb_nvm_add_non_active(nvm, NVM_MAX_SIZE, tb_switch_nvm_write);
> +		if (ret)
> +			break;
> +
> +		sw->nvm = nvm;
> +		break;
> +
> +	case nvm_write:
> +		if (!buf) {
> +			ret = -EINVAL;
> +			break;
> +		}
> +		image_size = sw->nvm->buf_data_size;
> +		if (image_size < NVM_MIN_SIZE || image_size > NVM_MAX_SIZE) {
> +			ret = -EINVAL;
> +			break;
> +		}
> +		ret = usb4_switch_nvm_write(sw, 0, buf, image_size);
> +		if (!ret)
> +			sw->nvm->flushed = true;
> +
> +		break;
> +
> +	default:
> +		break;
> +	}
> +
> +	if ((mode == nvm_add) && (ret != 0))
> +		tb_nvm_free(sw->nvm);
> +
> +	return ret;
> +}
> +
> +struct tb_nvm_id {
> +	u16 hw_vendor_id;
> +	int (*validate)(struct tb_switch *sw, unsigned int handle);
> +};
> +
> +static const struct tb_nvm_id tb_nvm_vendors[] = {
> +	/* ASMedia software CM firmware upgrade */
> +	{ 0x174c, asmedia_nvm_validate },
> +};
> +
> +/**
> + * tb_nvm_vendor_handle() - support vendor's NVM format
> + * @sw: Thunderbolt switch
> + * @handle: 0:NvmUpgradeSuppport, 1:NvmAdd, 2:NvmWrite
> + */
> +int tb_nvm_validate(struct tb_switch *sw, unsigned int mode)
> +{
> +	int res, i;
> +
> +	for (i = 0; i < ARRAY_SIZE(tb_nvm_vendors); i++) {
> +		const struct tb_nvm_id *id = &tb_nvm_vendors[i];
> +
> +		if (id->hw_vendor_id && id->hw_vendor_id != sw->config.vendor_id)
> +			continue;
> +
> +		 res = id->validate(sw, mode);
> +	}
> +	return res;
> +}
> +
>   /**
>    * tb_nvm_alloc() - Allocate new NVM structure
>    * @dev: Device owning the NVM
> diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c
> index 244f8cd38b25..352e64f3dc92 100644
> --- a/drivers/thunderbolt/switch.c
> +++ b/drivers/thunderbolt/switch.c
> @@ -114,6 +114,14 @@ static int nvm_validate_and_write(struct tb_switch *sw)
>   	if (image_size < NVM_MIN_SIZE || image_size > NVM_MAX_SIZE)
>   		return -EINVAL;
> 
> +	/*
> +	 * Vendor's nvm write. If the image has been flushed to the
> +	 * storage are, nvm write is complete.
> +	 */
> +	ret = tb_nvm_validate(sw, nvm_write);
> +	if (sw->nvm->flushed)
> +		return ret;
> +
>   	/*
>   	 * FARB pointer must point inside the image and must at least
>   	 * contain parts of the digital section we will be reading here.
> @@ -390,6 +398,11 @@ static int tb_switch_nvm_add(struct tb_switch *sw)
>   	if (!nvm_readable(sw))
>   		return 0;
> 
> +	/* Vendor's NVM formats add */
> +	ret = tb_nvm_validate(sw, nvm_add);
> +	if (ret)
> +		return ret;
> +
>   	/*
>   	 * The NVM format of non-Intel hardware is not known so
>   	 * currently restrict NVM upgrade for Intel hardware. We may

This comment should be adjusted as after your patch lands both Intel and 
ASMedia formats are known and included.

> @@ -1953,6 +1966,9 @@ static ssize_t nvm_version_show(struct device *dev,
>   		ret = -ENODATA;
>   	else if (!sw->nvm)
>   		ret = -EAGAIN;
> +	/*ASMedia NVM version show format xxxxxx_xxxx_xx */
> +	else if (sw->config.vendor_id == 0x174C)
> +		ret = sprintf(buf, "%06x_%04x_%02x\n", sw->nvm->nvm_asm.date, sw->nvm->nvm_asm.customerID, sw->nvm->nvm_asm.version);

Are you hard-pressed to use this specific format for the string?  I feel 
like it's overloading the definition of a version string quite a bit.

I also worry that userspace has come to expect "major.minor" for this 
and making your string use 2 decimals may mean more deviations for 
userspace too.

Perhaps should you just export it instead as:

"%02x.%06x", sw->nvm->nvm_asm.version, sw->nvm->nvm_asm.date

And move the customer ID into another sysfs file?  I would think this 
fits pretty well with the existing "vendor" or "device" sysfs file 
depending upon it's meaning.

If you do end up having a strong reason for deviating the version string 
format, then I think you should document both what the Intel format is 
(major.minor) and your format explicitly in 
Documentation/admin-guide/thunderbolt.rst.

>   	else
>   		ret = sprintf(buf, "%x.%x\n", sw->nvm->major, sw->nvm->minor);
> 
> @@ -2860,6 +2876,7 @@ int tb_switch_add(struct tb_switch *sw)
>   		tb_sw_dbg(sw, "uid: %#llx\n", sw->uid);
> 
>   		tb_check_quirks(sw);
> +		tb_nvm_validate(sw, nvm_upgrade);
> 
>   		ret = tb_switch_set_uuid(sw);
>   		if (ret) {
> diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
> index 5db76de40cc1..7f20f10352d9 100644
> --- a/drivers/thunderbolt/tb.h
> +++ b/drivers/thunderbolt/tb.h
> @@ -28,6 +28,22 @@
>   #define NVM_VERSION		0x08
>   #define NVM_FLASH_SIZE		0x45
> 
> +/* ASMedia specific NVM offsets */
> +#define NVM_Date	0x1c
> +#define NVM_CUSTOMER_ID	0x28
> +
> +/* ASMedia specific validation mode */
> +#define nvm_upgrade 0
> +#define nvm_add 1
> +#define nvm_write 2

As all of these values are ASMedia specific, I think the #defines should 
have ASMEDIA in the name.  I know Greg mentioned to roll into an enum, 
and I think you still can but make it something like:

#define ASMEDIA_NVM_DATE	0x1c
#define ASMEDIA_NVM_CUSTOMER_ID	0x28
enum asmeda_nvm_ops {
	ASMEDIA_NVM_UPGRADE = 0,
	ASMEDIA_NVM_ADD = 1,
	ASMEDIA_NVM_WRITE = 2,
};

> +
> +struct nvm_asmedia {
> +	u32 date;
> +	u32 customerID:16;
> +	u32 version:8;
> +	u32 reserved:8;
> +};
> +
>   /**
>    * struct tb_nvm - Structure holding NVM information
>    * @dev: Owner of the NVM
> @@ -57,6 +73,7 @@ struct tb_nvm {
>   	size_t buf_data_size;
>   	bool authenticating;
>   	bool flushed;
> +	struct nvm_asmedia nvm_asm;

Furthermore if you follow my suggestion on how to encode the version you 
can re-use the 'major' and 'minor' members from this struct and don't 
need to deviate in any way from it for your data.

* Major would map to your "version".
* Minor would map to "date".

You could instead then store the customer ID into the switches vendor ID 
or device ID member (whichever makes more sense).

>   };
> 
>   enum tb_nvm_write_ops {
> @@ -736,6 +753,7 @@ static inline void tb_domain_put(struct tb *tb)
>   	put_device(&tb->dev);
>   }
> 
> +int tb_nvm_validate(struct tb_switch *sw, unsigned int mode);
>   struct tb_nvm *tb_nvm_alloc(struct device *dev);
>   int tb_nvm_add_active(struct tb_nvm *nvm, size_t size, nvmem_reg_read_t reg_read);
>   int tb_nvm_write_buf(struct tb_nvm *nvm, unsigned int offset, void *val,
> --
> 2.34.1
> 
>
Mario Limonciello Aug. 15, 2022, 7:20 p.m. UTC | #5
On 8/15/2022 12:28, Limonciello, Mario wrote:
> +hughsie for additional comments
> 
> Various inline comments below.
> 
> On 8/14/2022 23:11, Szuying Chen wrote:
>> From: Szuying Chen <Chloe_Chen@asmedia.com.tw>
>>
>> The patch add tb_nvm_validate() contain an array that has functions
>> pointers to asmedia_nvm_validate().
>> And asmedia_nvm_validate() that recognize supported vendor works in one
>> of the following cases:
>> Case nvm_upgrade: enable nvm's attribute by setting no_nvm_upgrade
>> flag to create nvm_authenticate file node.
>> Case nvm_add:add active/non-active NVM devices.
>> Case nvm_write:update firmware to non-ative NVM device.
>>
>> Our patches were through checkpatch.pl. But the file(switch.c.)
>> have existed 13 warning before we patch it.
> 
> Please feel free to add other patches to the series to clean up 
> warnings.  Just because you didn't introduce them doesn't mean you can't 
> fix them =)
> 
>>
>> Signed-off-by: Szuying Chen <Chloe_Chen@asmedia.com.tw>
>> ---
>>   drivers/thunderbolt/nvm.c    | 147 +++++++++++++++++++++++++++++++++++
>>   drivers/thunderbolt/switch.c |  17 ++++
>>   drivers/thunderbolt/tb.h     |  18 +++++
>>   3 files changed, 182 insertions(+)

When you submit patches to the mailing list, you'll want to do two 
things I haven't noticed you doing:

1) When you create the patch with git format-patch use the 
"--subject-prefix" option to set your subject prefix.  I see that your 
patch has been submitted at least 3 times but Lore groups all 3 
submissions together.

The first should have been [PATCH], next should have been [PATCH v2], 
next [PATCH v3].

So I think technically your next submission SHOULD be [PATCH v4]

2) You should add below the --- comments about what changed from last 
submission.  This helps people have reviewed it in the past be able to 
better focus on what they should look most closely at.
It should be something like this:

```
.
.
.
Signed-off-by: User Name <user@name.com>
---

changes from v3->v4:
- Foo the bar

Note: The three previous submissions accidentally used the same subject 
prefix.  This changelog is relative to the most recent submission at $URL.

    drivers/thunderbolt/nvm.c    | 147
.
.
.

```

>>
>> diff --git a/drivers/thunderbolt/nvm.c b/drivers/thunderbolt/nvm.c
>> index b3f310389378..6db2034ec8e5 100644
>> --- a/drivers/thunderbolt/nvm.c
>> +++ b/drivers/thunderbolt/nvm.c
>> @@ -9,11 +9,158 @@
>>   #include <linux/idr.h>
>>   #include <linux/slab.h>
>>   #include <linux/vmalloc.h>
>> +#include <linux/pm_runtime.h>
>>
>>   #include "tb.h"
>>
>>   static DEFINE_IDA(nvm_ida);
>>
>> +static int tb_switch_nvm_read(void *priv, unsigned int offset, void 
>> *val,
>> +                  size_t bytes)
>> +{
>> +    struct tb_nvm *nvm = priv;
>> +    struct tb_switch *sw = tb_to_switch(nvm->dev);
>> +    int ret;
>> +
>> +    pm_runtime_get_sync(&sw->dev);
>> +    if (!mutex_trylock(&sw->tb->lock)) {
>> +        ret = restart_syscall();
>> +        goto out;
>> +    }
>> +    ret = usb4_switch_nvm_read(sw, offset, val, bytes);
>> +    mutex_unlock(&sw->tb->lock);
>> +
>> +out:
>> +    pm_runtime_mark_last_busy(&sw->dev);
>> +    pm_runtime_put_autosuspend(&sw->dev);
>> +
>> +    return ret;
>> +}
>> +
>> +static int tb_switch_nvm_write(void *priv, unsigned int offset, void 
>> *val,
>> +                   size_t bytes)
>> +{
>> +    struct tb_nvm *nvm = priv;
>> +    struct tb_switch *sw = tb_to_switch(nvm->dev);
>> +    int ret;
>> +
>> +    if (!mutex_trylock(&sw->tb->lock))
>> +        return restart_syscall();
>> +
>> +    /*
>> +     * Since writing the NVM image might require some special steps,
>> +     * for example when CSS headers are written, we cache the image
>> +     * locally here and handle the special cases when the user asks
>> +     * us to authenticate the image.
>> +     */
>> +    ret = tb_nvm_write_buf(nvm, offset, val, bytes);
>> +    mutex_unlock(&sw->tb->lock);
>> +
>> +    return ret;
>> +}
>> +
>> +static int asmedia_nvm_validate(struct tb_switch *sw, unsigned int mode)
>> +{
>> +    struct tb_nvm *nvm;
>> +    u32 val;
>> +    u32 nvm_size;
>> +    int ret = 0;
>> +    unsigned int image_size;
>> +    const u8 *buf = sw->nvm->buf;
>> +
>> +    switch (mode) {
>> +    case nvm_upgrade:
>> +        if (sw->no_nvm_upgrade)
>> +            sw->no_nvm_upgrade = false;
>> +
>> +        break;
>> +
>> +    case nvm_add:
>> +        nvm = tb_nvm_alloc(&sw->dev);
>> +        if (IS_ERR(nvm)) {
>> +            ret = PTR_ERR(nvm);
>> +            break;
>> +        }
>> +
>> +        ret = usb4_switch_nvm_read(sw, NVM_Date, &val, sizeof(val));
>> +        if (ret)
>> +            break;
>> +
>> +        nvm->nvm_asm.date = (((u8)val) << 0x10 | ((u8)(val >> 0x8)) 
>> << 0x8 | (u8)(val >> 0x10));
>> +        ret = usb4_switch_nvm_read(sw, NVM_CUSTOMER_ID, &val, 
>> sizeof(val));
>> +        if (ret)
>> +            break;
>> +
>> +        nvm->nvm_asm.customerID = (((u8)val) << 0x8 | ((u8)(val >> 
>> 0x8)));
>> +        nvm->nvm_asm.version = (u8)(val >> 0x10);
>> +        nvm_size = SZ_512K;
>> +        ret = tb_nvm_add_active(nvm, nvm_size, tb_switch_nvm_read);
>> +        if (ret)
>> +            break;
>> +
>> +        ret = tb_nvm_add_non_active(nvm, NVM_MAX_SIZE, 
>> tb_switch_nvm_write);
>> +        if (ret)
>> +            break;
>> +
>> +        sw->nvm = nvm;
>> +        break;
>> +
>> +    case nvm_write:
>> +        if (!buf) {
>> +            ret = -EINVAL;
>> +            break;
>> +        }
>> +        image_size = sw->nvm->buf_data_size;
>> +        if (image_size < NVM_MIN_SIZE || image_size > NVM_MAX_SIZE) {
>> +            ret = -EINVAL;
>> +            break;
>> +        }
>> +        ret = usb4_switch_nvm_write(sw, 0, buf, image_size);
>> +        if (!ret)
>> +            sw->nvm->flushed = true;
>> +
>> +        break;
>> +
>> +    default:
>> +        break;
>> +    }
>> +
>> +    if ((mode == nvm_add) && (ret != 0))
>> +        tb_nvm_free(sw->nvm);
>> +
>> +    return ret;
>> +}
>> +
>> +struct tb_nvm_id {
>> +    u16 hw_vendor_id;
>> +    int (*validate)(struct tb_switch *sw, unsigned int handle);
>> +};
>> +
>> +static const struct tb_nvm_id tb_nvm_vendors[] = {
>> +    /* ASMedia software CM firmware upgrade */
>> +    { 0x174c, asmedia_nvm_validate },
>> +};
>> +
>> +/**
>> + * tb_nvm_vendor_handle() - support vendor's NVM format
>> + * @sw: Thunderbolt switch
>> + * @handle: 0:NvmUpgradeSuppport, 1:NvmAdd, 2:NvmWrite
>> + */
>> +int tb_nvm_validate(struct tb_switch *sw, unsigned int mode)
>> +{
>> +    int res, i;
>> +
>> +    for (i = 0; i < ARRAY_SIZE(tb_nvm_vendors); i++) {
>> +        const struct tb_nvm_id *id = &tb_nvm_vendors[i];
>> +
>> +        if (id->hw_vendor_id && id->hw_vendor_id != 
>> sw->config.vendor_id)
>> +            continue;
>> +
>> +         res = id->validate(sw, mode);
>> +    }
>> +    return res;
>> +}
>> +
>>   /**
>>    * tb_nvm_alloc() - Allocate new NVM structure
>>    * @dev: Device owning the NVM
>> diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c
>> index 244f8cd38b25..352e64f3dc92 100644
>> --- a/drivers/thunderbolt/switch.c
>> +++ b/drivers/thunderbolt/switch.c
>> @@ -114,6 +114,14 @@ static int nvm_validate_and_write(struct 
>> tb_switch *sw)
>>       if (image_size < NVM_MIN_SIZE || image_size > NVM_MAX_SIZE)
>>           return -EINVAL;
>>
>> +    /*
>> +     * Vendor's nvm write. If the image has been flushed to the
>> +     * storage are, nvm write is complete.
>> +     */
>> +    ret = tb_nvm_validate(sw, nvm_write);
>> +    if (sw->nvm->flushed)
>> +        return ret;
>> +
>>       /*
>>        * FARB pointer must point inside the image and must at least
>>        * contain parts of the digital section we will be reading here.
>> @@ -390,6 +398,11 @@ static int tb_switch_nvm_add(struct tb_switch *sw)
>>       if (!nvm_readable(sw))
>>           return 0;
>>
>> +    /* Vendor's NVM formats add */
>> +    ret = tb_nvm_validate(sw, nvm_add);
>> +    if (ret)
>> +        return ret;
>> +
>>       /*
>>        * The NVM format of non-Intel hardware is not known so
>>        * currently restrict NVM upgrade for Intel hardware. We may
> 
> This comment should be adjusted as after your patch lands both Intel and 
> ASMedia formats are known and included.
> 
>> @@ -1953,6 +1966,9 @@ static ssize_t nvm_version_show(struct device *dev,
>>           ret = -ENODATA;
>>       else if (!sw->nvm)
>>           ret = -EAGAIN;
>> +    /*ASMedia NVM version show format xxxxxx_xxxx_xx */
>> +    else if (sw->config.vendor_id == 0x174C)
>> +        ret = sprintf(buf, "%06x_%04x_%02x\n", sw->nvm->nvm_asm.date, 
>> sw->nvm->nvm_asm.customerID, sw->nvm->nvm_asm.version);
> 
> Are you hard-pressed to use this specific format for the string?  I feel 
> like it's overloading the definition of a version string quite a bit.
> 
> I also worry that userspace has come to expect "major.minor" for this 
> and making your string use 2 decimals may mean more deviations for 
> userspace too.
> 
> Perhaps should you just export it instead as:
> 
> "%02x.%06x", sw->nvm->nvm_asm.version, sw->nvm->nvm_asm.date
> 
> And move the customer ID into another sysfs file?  I would think this 
> fits pretty well with the existing "vendor" or "device" sysfs file 
> depending upon it's meaning.
> 
> If you do end up having a strong reason for deviating the version string 
> format, then I think you should document both what the Intel format is 
> (major.minor) and your format explicitly in 
> Documentation/admin-guide/thunderbolt.rst.
> 
>>       else
>>           ret = sprintf(buf, "%x.%x\n", sw->nvm->major, sw->nvm->minor);
>>
>> @@ -2860,6 +2876,7 @@ int tb_switch_add(struct tb_switch *sw)
>>           tb_sw_dbg(sw, "uid: %#llx\n", sw->uid);
>>
>>           tb_check_quirks(sw);
>> +        tb_nvm_validate(sw, nvm_upgrade);
>>
>>           ret = tb_switch_set_uuid(sw);
>>           if (ret) {
>> diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
>> index 5db76de40cc1..7f20f10352d9 100644
>> --- a/drivers/thunderbolt/tb.h
>> +++ b/drivers/thunderbolt/tb.h
>> @@ -28,6 +28,22 @@
>>   #define NVM_VERSION        0x08
>>   #define NVM_FLASH_SIZE        0x45
>>
>> +/* ASMedia specific NVM offsets */
>> +#define NVM_Date    0x1c
>> +#define NVM_CUSTOMER_ID    0x28
>> +
>> +/* ASMedia specific validation mode */
>> +#define nvm_upgrade 0
>> +#define nvm_add 1
>> +#define nvm_write 2
> 
> As all of these values are ASMedia specific, I think the #defines should 
> have ASMEDIA in the name.  I know Greg mentioned to roll into an enum, 
> and I think you still can but make it something like:
> 
> #define ASMEDIA_NVM_DATE    0x1c
> #define ASMEDIA_NVM_CUSTOMER_ID    0x28
> enum asmeda_nvm_ops {
>      ASMEDIA_NVM_UPGRADE = 0,
>      ASMEDIA_NVM_ADD = 1,
>      ASMEDIA_NVM_WRITE = 2,
> };
> 
>> +
>> +struct nvm_asmedia {
>> +    u32 date;
>> +    u32 customerID:16;
>> +    u32 version:8;
>> +    u32 reserved:8;
>> +};
>> +
>>   /**
>>    * struct tb_nvm - Structure holding NVM information
>>    * @dev: Owner of the NVM
>> @@ -57,6 +73,7 @@ struct tb_nvm {
>>       size_t buf_data_size;
>>       bool authenticating;
>>       bool flushed;
>> +    struct nvm_asmedia nvm_asm;
> 
> Furthermore if you follow my suggestion on how to encode the version you 
> can re-use the 'major' and 'minor' members from this struct and don't 
> need to deviate in any way from it for your data.
> 
> * Major would map to your "version".
> * Minor would map to "date".
> 
> You could instead then store the customer ID into the switches vendor ID 
> or device ID member (whichever makes more sense).
> 
>>   };
>>
>>   enum tb_nvm_write_ops {
>> @@ -736,6 +753,7 @@ static inline void tb_domain_put(struct tb *tb)
>>       put_device(&tb->dev);
>>   }
>>
>> +int tb_nvm_validate(struct tb_switch *sw, unsigned int mode);
>>   struct tb_nvm *tb_nvm_alloc(struct device *dev);
>>   int tb_nvm_add_active(struct tb_nvm *nvm, size_t size, 
>> nvmem_reg_read_t reg_read);
>>   int tb_nvm_write_buf(struct tb_nvm *nvm, unsigned int offset, void 
>> *val,
>> -- 
>> 2.34.1
>>
>>
>
kernel test robot Aug. 16, 2022, 4:50 a.m. UTC | #6
Hi Szuying,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on linus/master]
[also build test WARNING on v6.0-rc1 next-20220815]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Szuying-Chen/thunderbolt-thunderbolt-add-vendor-s-NVM-formats/20220815-121330
base:   https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git 568035b01cfb107af8d2e4bd2fb9aea22cf5b868
config: i386-randconfig-m021
compiler: gcc-11 (Debian 11.3.0-3) 11.3.0

If you fix the issue, kindly add following tag where applicable
Reported-by: kernel test robot <lkp@intel.com>

smatch warnings:
drivers/thunderbolt/nvm.c:159 tb_nvm_validate() warn: inconsistent indenting
drivers/thunderbolt/nvm.c:161 tb_nvm_validate() error: uninitialized symbol 'res'.

vim +159 drivers/thunderbolt/nvm.c

   143	
   144	/**
   145	 * tb_nvm_vendor_handle() - support vendor's NVM format
   146	 * @sw: Thunderbolt switch
   147	 * @handle: 0:NvmUpgradeSuppport, 1:NvmAdd, 2:NvmWrite
   148	 */
   149	int tb_nvm_validate(struct tb_switch *sw, unsigned int mode)
   150	{
   151		int res, i;
   152	
   153		for (i = 0; i < ARRAY_SIZE(tb_nvm_vendors); i++) {
   154			const struct tb_nvm_id *id = &tb_nvm_vendors[i];
   155	
   156			if (id->hw_vendor_id && id->hw_vendor_id != sw->config.vendor_id)
   157				continue;
   158	
 > 159			 res = id->validate(sw, mode);
   160		}
 > 161		return res;
   162	}
   163
diff mbox series

Patch

diff --git a/drivers/thunderbolt/nvm.c b/drivers/thunderbolt/nvm.c
index b3f310389378..6db2034ec8e5 100644
--- a/drivers/thunderbolt/nvm.c
+++ b/drivers/thunderbolt/nvm.c
@@ -9,11 +9,158 @@ 
 #include <linux/idr.h>
 #include <linux/slab.h>
 #include <linux/vmalloc.h>
+#include <linux/pm_runtime.h>

 #include "tb.h"

 static DEFINE_IDA(nvm_ida);

+static int tb_switch_nvm_read(void *priv, unsigned int offset, void *val,
+			      size_t bytes)
+{
+	struct tb_nvm *nvm = priv;
+	struct tb_switch *sw = tb_to_switch(nvm->dev);
+	int ret;
+
+	pm_runtime_get_sync(&sw->dev);
+	if (!mutex_trylock(&sw->tb->lock)) {
+		ret = restart_syscall();
+		goto out;
+	}
+	ret = usb4_switch_nvm_read(sw, offset, val, bytes);
+	mutex_unlock(&sw->tb->lock);
+
+out:
+	pm_runtime_mark_last_busy(&sw->dev);
+	pm_runtime_put_autosuspend(&sw->dev);
+
+	return ret;
+}
+
+static int tb_switch_nvm_write(void *priv, unsigned int offset, void *val,
+			       size_t bytes)
+{
+	struct tb_nvm *nvm = priv;
+	struct tb_switch *sw = tb_to_switch(nvm->dev);
+	int ret;
+
+	if (!mutex_trylock(&sw->tb->lock))
+		return restart_syscall();
+
+	/*
+	 * Since writing the NVM image might require some special steps,
+	 * for example when CSS headers are written, we cache the image
+	 * locally here and handle the special cases when the user asks
+	 * us to authenticate the image.
+	 */
+	ret = tb_nvm_write_buf(nvm, offset, val, bytes);
+	mutex_unlock(&sw->tb->lock);
+
+	return ret;
+}
+
+static int asmedia_nvm_validate(struct tb_switch *sw, unsigned int mode)
+{
+	struct tb_nvm *nvm;
+	u32 val;
+	u32 nvm_size;
+	int ret = 0;
+	unsigned int image_size;
+	const u8 *buf = sw->nvm->buf;
+
+	switch (mode) {
+	case nvm_upgrade:
+		if (sw->no_nvm_upgrade)
+			sw->no_nvm_upgrade = false;
+
+		break;
+
+	case nvm_add:
+		nvm = tb_nvm_alloc(&sw->dev);
+		if (IS_ERR(nvm)) {
+			ret = PTR_ERR(nvm);
+			break;
+		}
+
+		ret = usb4_switch_nvm_read(sw, NVM_Date, &val, sizeof(val));
+		if (ret)
+			break;
+
+		nvm->nvm_asm.date = (((u8)val) << 0x10 | ((u8)(val >> 0x8)) << 0x8 | (u8)(val >> 0x10));
+		ret = usb4_switch_nvm_read(sw, NVM_CUSTOMER_ID, &val, sizeof(val));
+		if (ret)
+			break;
+
+		nvm->nvm_asm.customerID = (((u8)val) << 0x8 | ((u8)(val >> 0x8)));
+		nvm->nvm_asm.version = (u8)(val >> 0x10);
+		nvm_size = SZ_512K;
+		ret = tb_nvm_add_active(nvm, nvm_size, tb_switch_nvm_read);
+		if (ret)
+			break;
+
+		ret = tb_nvm_add_non_active(nvm, NVM_MAX_SIZE, tb_switch_nvm_write);
+		if (ret)
+			break;
+
+		sw->nvm = nvm;
+		break;
+
+	case nvm_write:
+		if (!buf) {
+			ret = -EINVAL;
+			break;
+		}
+		image_size = sw->nvm->buf_data_size;
+		if (image_size < NVM_MIN_SIZE || image_size > NVM_MAX_SIZE) {
+			ret = -EINVAL;
+			break;
+		}
+		ret = usb4_switch_nvm_write(sw, 0, buf, image_size);
+		if (!ret)
+			sw->nvm->flushed = true;
+
+		break;
+
+	default:
+		break;
+	}
+
+	if ((mode == nvm_add) && (ret != 0))
+		tb_nvm_free(sw->nvm);
+
+	return ret;
+}
+
+struct tb_nvm_id {
+	u16 hw_vendor_id;
+	int (*validate)(struct tb_switch *sw, unsigned int handle);
+};
+
+static const struct tb_nvm_id tb_nvm_vendors[] = {
+	/* ASMedia software CM firmware upgrade */
+	{ 0x174c, asmedia_nvm_validate },
+};
+
+/**
+ * tb_nvm_vendor_handle() - support vendor's NVM format
+ * @sw: Thunderbolt switch
+ * @handle: 0:NvmUpgradeSuppport, 1:NvmAdd, 2:NvmWrite
+ */
+int tb_nvm_validate(struct tb_switch *sw, unsigned int mode)
+{
+	int res, i;
+
+	for (i = 0; i < ARRAY_SIZE(tb_nvm_vendors); i++) {
+		const struct tb_nvm_id *id = &tb_nvm_vendors[i];
+
+		if (id->hw_vendor_id && id->hw_vendor_id != sw->config.vendor_id)
+			continue;
+
+		 res = id->validate(sw, mode);
+	}
+	return res;
+}
+
 /**
  * tb_nvm_alloc() - Allocate new NVM structure
  * @dev: Device owning the NVM
diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c
index 244f8cd38b25..352e64f3dc92 100644
--- a/drivers/thunderbolt/switch.c
+++ b/drivers/thunderbolt/switch.c
@@ -114,6 +114,14 @@  static int nvm_validate_and_write(struct tb_switch *sw)
 	if (image_size < NVM_MIN_SIZE || image_size > NVM_MAX_SIZE)
 		return -EINVAL;

+	/*
+	 * Vendor's nvm write. If the image has been flushed to the
+	 * storage are, nvm write is complete.
+	 */
+	ret = tb_nvm_validate(sw, nvm_write);
+	if (sw->nvm->flushed)
+		return ret;
+
 	/*
 	 * FARB pointer must point inside the image and must at least
 	 * contain parts of the digital section we will be reading here.
@@ -390,6 +398,11 @@  static int tb_switch_nvm_add(struct tb_switch *sw)
 	if (!nvm_readable(sw))
 		return 0;

+	/* Vendor's NVM formats add */
+	ret = tb_nvm_validate(sw, nvm_add);
+	if (ret)
+		return ret;
+
 	/*
 	 * The NVM format of non-Intel hardware is not known so
 	 * currently restrict NVM upgrade for Intel hardware. We may
@@ -1953,6 +1966,9 @@  static ssize_t nvm_version_show(struct device *dev,
 		ret = -ENODATA;
 	else if (!sw->nvm)
 		ret = -EAGAIN;
+	/*ASMedia NVM version show format xxxxxx_xxxx_xx */
+	else if (sw->config.vendor_id == 0x174C)
+		ret = sprintf(buf, "%06x_%04x_%02x\n", sw->nvm->nvm_asm.date, sw->nvm->nvm_asm.customerID, sw->nvm->nvm_asm.version);
 	else
 		ret = sprintf(buf, "%x.%x\n", sw->nvm->major, sw->nvm->minor);

@@ -2860,6 +2876,7 @@  int tb_switch_add(struct tb_switch *sw)
 		tb_sw_dbg(sw, "uid: %#llx\n", sw->uid);

 		tb_check_quirks(sw);
+		tb_nvm_validate(sw, nvm_upgrade);

 		ret = tb_switch_set_uuid(sw);
 		if (ret) {
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
index 5db76de40cc1..7f20f10352d9 100644
--- a/drivers/thunderbolt/tb.h
+++ b/drivers/thunderbolt/tb.h
@@ -28,6 +28,22 @@ 
 #define NVM_VERSION		0x08
 #define NVM_FLASH_SIZE		0x45

+/* ASMedia specific NVM offsets */
+#define NVM_Date	0x1c
+#define NVM_CUSTOMER_ID	0x28
+
+/* ASMedia specific validation mode */
+#define nvm_upgrade 0
+#define nvm_add 1
+#define nvm_write 2
+
+struct nvm_asmedia {
+	u32 date;
+	u32 customerID:16;
+	u32 version:8;
+	u32 reserved:8;
+};
+
 /**
  * struct tb_nvm - Structure holding NVM information
  * @dev: Owner of the NVM
@@ -57,6 +73,7 @@  struct tb_nvm {
 	size_t buf_data_size;
 	bool authenticating;
 	bool flushed;
+	struct nvm_asmedia nvm_asm;
 };

 enum tb_nvm_write_ops {
@@ -736,6 +753,7 @@  static inline void tb_domain_put(struct tb *tb)
 	put_device(&tb->dev);
 }

+int tb_nvm_validate(struct tb_switch *sw, unsigned int mode);
 struct tb_nvm *tb_nvm_alloc(struct device *dev);
 int tb_nvm_add_active(struct tb_nvm *nvm, size_t size, nvmem_reg_read_t reg_read);
 int tb_nvm_write_buf(struct tb_nvm *nvm, unsigned int offset, void *val,