RE: [patch v22 1/4] drivers: jtag: Add JTAG core driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Greg.

Thanks for your review.
Please see my comments inline.

Best Regards,
Oleksandr Shamray

> -----Original Message-----
> From: Greg KH [mailto:gregkh@xxxxxxxxxxxxxxxxxxx]
> Sent: 28 мая 2018 г. 15:35
> To: Oleksandr Shamray <oleksandrs@xxxxxxxxxxxx>
> Cc: arnd@xxxxxxxx; linux-kernel@xxxxxxxxxxxxxxx; linux-arm-
> kernel@xxxxxxxxxxxxxxxxxxx; devicetree@xxxxxxxxxxxxxxx;
> openbmc@xxxxxxxxxxxxxxxx; joel@xxxxxxxxx; jiri@xxxxxxxxxxx;
> tklauser@xxxxxxxxxx; linux-serial@xxxxxxxxxxxxxxx; Vadim Pasternak
> <vadimp@xxxxxxxxxxxx>; system-sw-low-level <system-sw-low-
> level@xxxxxxxxxxxx>; robh+dt@xxxxxxxxxx; openocd-devel-
> owner@xxxxxxxxxxxxxxxxxxxxx; linux-api@xxxxxxxxxxxxxxx;
> davem@xxxxxxxxxxxxx; mchehab@xxxxxxxxxx
> Subject: Re: [patch v22 1/4] drivers: jtag: Add JTAG core driver
> 
> On Mon, May 28, 2018 at 01:34:09PM +0300, Oleksandr Shamray wrote:
> > Initial patch for JTAG driver
> > JTAG class driver provide infrastructure to support hardware/software
> > JTAG platform drivers. It provide user layer API interface for
> > flashing and debugging external devices which equipped with JTAG
> > interface using standard transactions.
> >
> > Driver exposes set of IOCTL to user space for:
> > - XFER:
> > - SIR (Scan Instruction Register, IEEE 1149.1 Data Register scan);
> > - SDR (Scan Data Register, IEEE 1149.1 Instruction Register scan);
> > - RUNTEST (Forces the IEEE 1149.1 bus to a run state for a specified
> >   number of clocks).
> > - SIOCFREQ/GIOCFREQ for setting and reading JTAG frequency.
> >
> > Driver core provides set of internal APIs for allocation and
> > registration:
> > - jtag_register;
> > - jtag_unregister;
> > - jtag_alloc;
> > - jtag_free;
> >
> > Platform driver on registration with jtag-core creates the next entry
> > in dev folder:
> > /dev/jtagX
> >
> > Signed-off-by: Oleksandr Shamray <oleksandrs@xxxxxxxxxxxx>
> > Signed-off-by: Jiri Pirko <jiri@xxxxxxxxxxxx>
> > Acked-by: Philippe Ombredanne <pombredanne@xxxxxxxx>
> > ---
> > v21->v22
> 
> 22 versions, way to stick with this...
> 
> Anyway, time to review it again.  I feel it's really close, but I don't want to
> apply it yet as the api feels odd in places.  If others have asked you to make
> these changes to look like this, I'm sorry, then please push back on me:
> 
> > +/*
> > + * JTAG core driver supports up to 256 devices
> > + * JTAG device name will be in range jtag0-jtag255
> > + * Set maximum len of jtag id to 3
> > + */
> > +
> > +#define MAX_JTAG_DEVS	255
> 
> Why have a max at all?  You should be able to just dynamically allocate them.
> Anyway, if you do want a max, you need to be able to properly keep the max
> number, and right now you have a race when registering (you check the
> number, and then much later do you increment it, a pointless use of an
> atomic value if I've ever seen one...)
> 

You are right. It's not good idea to have restriction of max dev number.
I will remove all regarding It.

> > +#define MAX_JTAG_NAME_LEN (sizeof("jtag") + 3)
> > +
> > +struct jtag {
> > +	struct miscdevice miscdev;
> 
> If you are a miscdev, why even have a max number?  Just let the max
> number of miscdev devices rule things.
> 
> > +	const struct jtag_ops *ops;
> > +	int id;
> > +	bool opened;
> 
> You shouldn't care about this, but ok...

Jtag HW not support to using with multiple requests from different users. So we prohibit this.

> 
> > +	struct mutex open_lock;
> > +	unsigned long priv[0];
> > +};
> > +
> > +static DEFINE_IDA(jtag_ida);
> > +
> > +static atomic_t jtag_refcount = ATOMIC_INIT(0);
> 
> Either use it as an atomic properly (test_and_set), or just leave it as an int, or
> better yet, don't use it at all.

It will be removed.

> 
> > +void *jtag_priv(struct jtag *jtag)
> > +{
> > +	return jtag->priv;
> > +}
> > +EXPORT_SYMBOL_GPL(jtag_priv);
> 
> Api naming issue here.  Traditionally this is a "get/set_drvdata" type function,
> as in dev_get_drvdata(), or dev_set_drvdata.  But, you are right in that the
> network stack has a netdev_priv() call, where you probably copied this idea
> from.
> 
> I don't know, I guess it's ok as-is, as it's the way networking does it, it's your
> call though.
> 

Yes,  I did as in networking. 

> > +static long jtag_ioctl(struct file *file, unsigned int cmd, unsigned
> > +long arg) {
> > +	struct jtag *jtag = file->private_data;
> > +	struct jtag_run_test_idle idle;
> > +	struct jtag_xfer xfer;
> > +	u8 *xfer_data;
> > +	u32 data_size;
> > +	u32 value;
> > +	int err;
> > +
> > +	if (!arg)
> > +		return -EINVAL;
> > +
> > +	switch (cmd) {
> > +	case JTAG_GIOCFREQ:
> > +		if (!jtag->ops->freq_get)
> > +			return -EOPNOTSUPP;
> > +
> > +		err = jtag->ops->freq_get(jtag, &value);
> > +		if (err)
> > +			break;
> > +
> > +		if (put_user(value, (__u32 __user *)arg))
> > +			err = -EFAULT;
> > +		break;
> > +
> > +	case JTAG_SIOCFREQ:
> > +		if (!jtag->ops->freq_set)
> > +			return -EOPNOTSUPP;
> > +
> > +		if (get_user(value, (__u32 __user *)arg))
> > +			return -EFAULT;
> > +		if (value == 0)
> > +			return -EINVAL;
> > +
> > +		err = jtag->ops->freq_set(jtag, value);
> > +		break;
> > +
> > +	case JTAG_IOCRUNTEST:
> > +		if (copy_from_user(&idle, (const void __user *)arg,
> > +				   sizeof(struct jtag_run_test_idle)))
> > +			return -EFAULT;
> > +
> > +		if (idle.endstate > JTAG_STATE_PAUSEDR)
> > +			return -EINVAL;
> 
> No validation that idle contains sane values?  Don't make every jtag driver
> have to do this type of validation please.
> 

I have partially validation of idle structure  (if (idle.endstate > JTAG_STATE_PAUSEDR)).
But I will add more validation.

> 
> > +
> > +		err = jtag->ops->idle(jtag, &idle);
> > +		break;
> > +
> > +	case JTAG_IOCXFER:
> > +		if (copy_from_user(&xfer, (const void __user *)arg,
> > +				   sizeof(struct jtag_xfer)))
> > +			return -EFAULT;
> > +
> > +		if (xfer.length >= JTAG_MAX_XFER_DATA_LEN)
> > +			return -EINVAL;
> > +
> > +		if (xfer.type > JTAG_SDR_XFER)
> > +			return -EINVAL;
> > +
> > +		if (xfer.direction > JTAG_WRITE_XFER)
> > +			return -EINVAL;
> > +
> > +		if (xfer.endstate > JTAG_STATE_PAUSEDR)
> > +			return -EINVAL;
> > +
> 
> See, you do good error checking here, why not for the previous ioctl?
> 
> 
> > +		data_size = DIV_ROUND_UP(xfer.length, BITS_PER_BYTE);
> > +		xfer_data = memdup_user(u64_to_user_ptr(xfer.tdio),
> data_size);
> > +
> 
> No blank line.
> 

Will remove

> > +		if (IS_ERR(xfer_data))
> > +			return -EFAULT;
> > +
> > +		err = jtag->ops->xfer(jtag, &xfer, xfer_data);
> > +		if (err) {
> > +			kfree(xfer_data);
> > +			return -EFAULT;
> 
> Why not return the error given to you?  -EFAULT is only if there is a memory
> error in a copy_to/from_user() call.
> 

Will change return code to err

> > +		}
> > +
> > +		err = copy_to_user(u64_to_user_ptr(xfer.tdio),
> > +				   (void *)xfer_data, data_size);
> > +		kfree(xfer_data);
> > +		if (err)
> > +			return -EFAULT;
> 
> Like here, that's correct.
> 
> > +
> > +		if (copy_to_user((void __user *)arg, (void *)&xfer,
> > +				 sizeof(struct jtag_xfer)))
> > +			return -EFAULT;
> > +		break;
> > +
> > +	case JTAG_GIOCSTATUS:
> > +		err = jtag->ops->status_get(jtag, &value);
> > +		if (err)
> > +			break;
> > +
> > +		err = put_user(value, (__u32 __user *)arg);
> > +		break;
> > +	case JTAG_SIOCMODE:
> > +		if (!jtag->ops->mode_set)
> > +			return -EOPNOTSUPP;
> > +
> > +		if (get_user(value, (__u32 __user *)arg))
> > +			return -EFAULT;
> > +		if (value == 0)
> > +			return -EINVAL;
> > +
> > +		err = jtag->ops->mode_set(jtag, value);
> > +		break;
> > +
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +	return err;
> > +}
> > +
> > +static int jtag_open(struct inode *inode, struct file *file) {
> > +	struct jtag *jtag = container_of(file->private_data, struct jtag,
> > +miscdev);
> > +
> > +	if (mutex_lock_interruptible(&jtag->open_lock))
> > +		return -ERESTARTSYS;
> 
> Why restart?  Just grab the lock, you don't want to have to do restartable
> logic in userspace, right?

Will change like:

ret = mutex_lock_interruptible(&jtag->open_lock);
if (ret)
	return ret;

> 
> > +
> > +	if (jtag->opened) {
> > +		mutex_unlock(&jtag->open_lock);
> > +		return -EBUSY;
> 
> Why do you care about only 1 userspace open call?  What does that buy you?
> Userspace can always pass around that file descriptor and use it in multiple
> places, so you are not really "protecting" yourself from anything.
> 

Accessing to JTAG HW from multiple processes will make confusion in the work of the JTAG protocol.
And I want to prohibit this.

> And better yet, if you get rid of this, your open/release functions get very
> tiny and simple.
> 
> > +	}
> > +	jtag->opened = true;
> > +	mutex_unlock(&jtag->open_lock);
> > +
> > +	nonseekable_open(inode, file);
> 
> No error checking of the return value?  Not good :(

Will add error handling

> 
> > +	file->private_data = jtag;
> > +	return 0;
> > +}
> > +
> > +static int jtag_release(struct inode *inode, struct file *file) {
> > +	struct jtag *jtag = file->private_data;
> > +
> > +	mutex_lock(&jtag->open_lock);
> > +	jtag->opened = false;
> > +	mutex_unlock(&jtag->open_lock);
> > +	return 0;
> > +}
> > +
> > +static const struct file_operations jtag_fops = {
> > +	.owner		= THIS_MODULE,
> > +	.open		= jtag_open,
> > +	.release	= jtag_release,
> > +	.llseek		= noop_llseek,
> > +	.unlocked_ioctl = jtag_ioctl,
> > +};
> > +
> > +struct jtag *jtag_alloc(struct device *host, size_t priv_size,
> > +			const struct jtag_ops *ops)
> > +{
> > +	struct jtag *jtag;
> > +
> > +	if (!host)
> > +		return NULL;
> > +
> > +	if (!ops)
> > +		return NULL;
> > +
> > +	if (!ops->idle || !ops->status_get || !ops->xfer)
> > +		return NULL;
> > +
> > +	jtag = kzalloc(sizeof(*jtag) + priv_size, GFP_KERNEL);
> > +	if (!jtag)
> > +		return NULL;
> > +
> > +	jtag->ops = ops;
> > +	jtag->miscdev.parent = host;
> > +
> > +	return jtag;
> > +}
> > +EXPORT_SYMBOL_GPL(jtag_alloc);
> > +
> > +void jtag_free(struct jtag *jtag)
> > +{
> > +	kfree(jtag);
> > +}
> > +EXPORT_SYMBOL_GPL(jtag_free);
> > +
> > +static int jtag_register(struct jtag *jtag) {
> > +	struct device *dev = jtag->miscdev.parent;
> > +	char *name;
> > +	int err;
> > +	int id;
> > +
> > +	if (!dev)
> > +		return -ENODEV;
> > +
> > +	if (atomic_read(&jtag_refcount) >= MAX_JTAG_DEVS)
> > +		return -ENOSPC;
> 
> Here, you are reading the value, and then setting it a long ways away.
> What happens if two people call this at the same time.  Not good.
> Either do it correctly, or better yet, don't do it at all.
> 

Removed.

> > +
> > +	id = ida_simple_get(&jtag_ida, 0, 0, GFP_KERNEL);
> > +	if (id < 0)
> > +		return id;
> > +
> > +	jtag->id = id;
> > +	jtag->opened = false;
> > +
> > +	name = kzalloc(MAX_JTAG_NAME_LEN, GFP_KERNEL);
> > +	if (!name) {
> > +		err = -ENOMEM;
> > +		goto err_jtag_alloc;
> > +	}
> > +
> > +	err = snprintf(name, MAX_JTAG_NAME_LEN, "jtag%d", id);
> > +	if (err < 0)
> > +		goto err_jtag_name;
> > +
> > +	mutex_init(&jtag->open_lock);
> > +	jtag->miscdev.fops =  &jtag_fops;
> > +	jtag->miscdev.minor = MISC_DYNAMIC_MINOR;
> > +	jtag->miscdev.name = name;
> > +
> > +	err = misc_register(&jtag->miscdev);
> > +	if (err) {
> > +		dev_err(jtag->miscdev.parent, "Unable to register
> device\n");
> > +		goto err_jtag_name;
> > +	}
> > +	pr_info("%s %d\n", __func__, __LINE__);
> > +	atomic_inc(&jtag_refcount);
> > +	return 0;
> > +
> > +err_jtag_name:
> > +	kfree(name);
> > +err_jtag_alloc:
> > +	ida_simple_remove(&jtag_ida, id);
> > +	return err;
> > +}
> > +
> > +static void jtag_unregister(struct jtag *jtag) {
> > +	misc_deregister(&jtag->miscdev);
> > +	kfree(jtag->miscdev.name);
> > +	ida_simple_remove(&jtag_ida, jtag->id);
> > +	atomic_dec(&jtag_refcount);
> > +}
> > +
> > +static void devm_jtag_unregister(struct device *dev, void *res) {
> > +	jtag_unregister(*(struct jtag **)res); }
> > +
> > +int devm_jtag_register(struct device *dev, struct jtag *jtag) {
> > +	struct jtag **ptr;
> > +	int ret;
> > +
> > +	ptr = devres_alloc(devm_jtag_unregister, sizeof(*ptr),
> GFP_KERNEL);
> > +	if (!ptr)
> > +		return -ENOMEM;
> > +
> > +	ret = jtag_register(jtag);
> > +	if (!ret) {
> > +		*ptr = jtag;
> > +		devres_add(dev, ptr);
> > +	} else {
> > +		devres_free(ptr);
> > +	}
> > +	return ret;
> > +}
> > +EXPORT_SYMBOL_GPL(devm_jtag_register);
> > +
> > +static void __exit jtag_exit(void)
> > +{
> > +	ida_destroy(&jtag_ida);
> > +}
> > +
> > +module_exit(jtag_exit);
> > +
> > +MODULE_AUTHOR("Oleksandr Shamray <oleksandrs@xxxxxxxxxxxx>");
> > +MODULE_DESCRIPTION("Generic jtag support"); MODULE_LICENSE("GPL
> v2");
> > diff --git a/include/linux/jtag.h b/include/linux/jtag.h new file mode
> > 100644 index 0000000..56d784d
> > --- /dev/null
> > +++ b/include/linux/jtag.h
> > @@ -0,0 +1,43 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +// include/linux/jtag.h - JTAG class driver // // Copyright (c) 2018
> > +Mellanox Technologies. All rights reserved.
> > +// Copyright (c) 2018 Oleksandr Shamray <oleksandrs@xxxxxxxxxxxx>
> > +
> > +#ifndef __JTAG_H
> > +#define __JTAG_H
> > +
> > +#include <uapi/linux/jtag.h>
> > +
> > +#define jtag_u64_to_ptr(arg) ((void *)(uintptr_t)arg)
> 
> Why do you need such a horrid cast?  What is this for?  And why use uintptr_t
> at all?  It's not a "normal" kernel type.
> 

Not needed - will be removed.

> > +
> > +#define JTAG_MAX_XFER_DATA_LEN 65535
> > +
> > +struct jtag;
> > +/**
> > + * struct jtag_ops - callbacks for JTAG control functions:
> > + *
> > + * @freq_get: get frequency function. Filled by dev driver
> > + * @freq_set: set frequency function. Filled by dev driver
> > + * @status_get: set status function. Mandatory func. Filled by dev
> > +driver
> > + * @idle: set JTAG to idle state function. Mandatory func. Filled by
> > +dev driver
> > + * @xfer: send JTAG xfer function. Mandatory func. Filled by dev
> > +driver
> > + * @mode_set: set specific work mode for JTAG. Filled by dev driver
> > +*/ struct jtag_ops {
> > +	int (*freq_get)(struct jtag *jtag, u32 *freq);
> > +	int (*freq_set)(struct jtag *jtag, u32 freq);
> > +	int (*status_get)(struct jtag *jtag, u32 *state);
> > +	int (*idle)(struct jtag *jtag, struct jtag_run_test_idle *idle);
> > +	int (*xfer)(struct jtag *jtag, struct jtag_xfer *xfer, u8 *xfer_data);
> > +	int (*mode_set)(struct jtag *jtag, u32 mode_mask); };
> > +
> > +void *jtag_priv(struct jtag *jtag);
> > +int devm_jtag_register(struct device *dev, struct jtag *jtag); void
> > +devm_jtag_unregister(struct device *dev, void *res); struct jtag
> > +*jtag_alloc(struct device *host, size_t priv_size,
> > +			const struct jtag_ops *ops);
> > +void jtag_free(struct jtag *jtag);
> > +
> > +#endif /* __JTAG_H */
> > diff --git a/include/uapi/linux/jtag.h b/include/uapi/linux/jtag.h new
> > file mode 100644 index 0000000..8bbf1a7
> > --- /dev/null
> > +++ b/include/uapi/linux/jtag.h
> > @@ -0,0 +1,102 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +// include/uapi/linux/jtag.h - JTAG class driver uapi // // Copyright
> > +(c) 2018 Mellanox Technologies. All rights reserved.
> > +// Copyright (c) 2018 Oleksandr Shamray <oleksandrs@xxxxxxxxxxxx>
> > +
> > +#ifndef __UAPI_LINUX_JTAG_H
> > +#define __UAPI_LINUX_JTAG_H
> > +
> > +/*
> > + * JTAG_XFER_HW_MODE: JTAG hardware mode. Used to set HW drived
> or
> > +bitbang
> > + * mode. This is bitmask param of ioctl JTAG_SIOCMODE command  */
> > +#define  JTAG_XFER_HW_MODE 1
> > +
> > +/**
> > + * enum jtag_endstate:
> > + *
> > + * @JTAG_STATE_IDLE: JTAG state machine IDLE state
> > + * @JTAG_STATE_PAUSEIR: JTAG state machine PAUSE_IR state
> > + * @JTAG_STATE_PAUSEDR: JTAG state machine PAUSE_DR state  */
> enum
> > +jtag_endstate {
> > +	JTAG_STATE_IDLE,
> > +	JTAG_STATE_PAUSEIR,
> > +	JTAG_STATE_PAUSEDR,
> > +};
> > +
> > +/**
> > + * enum jtag_xfer_type:
> > + *
> > + * @JTAG_SIR_XFER: SIR transfer
> > + * @JTAG_SDR_XFER: SDR transfer
> > + */
> > +enum jtag_xfer_type {
> > +	JTAG_SIR_XFER,
> > +	JTAG_SDR_XFER,
> > +};
> > +
> > +/**
> > + * enum jtag_xfer_direction:
> > + *
> > + * @JTAG_READ_XFER: read transfer
> > + * @JTAG_WRITE_XFER: write transfer
> > + */
> > +enum jtag_xfer_direction {
> > +	JTAG_READ_XFER,
> > +	JTAG_WRITE_XFER,
> > +};
> > +
> > +/**
> > + * struct jtag_run_test_idle - forces JTAG state machine to
> > + * RUN_TEST/IDLE state
> > + *
> > + * @reset: 0 - run IDLE/PAUSE from current state
> > + *         1 - go through TEST_LOGIC/RESET state before  IDLE/PAUSE
> > + * @end: completion flag
> > + * @tck: clock counter
> > + *
> > + * Structure provide interface to JTAG device for  JTAG IDLE execution.
> > + */
> > +struct jtag_run_test_idle {
> > +	__u8	reset;
> > +	__u8	endstate;
> > +	__u8	tck;
> > +};
> > +
> > +/**
> > + * struct jtag_xfer - jtag xfer:
> > + *
> > + * @type: transfer type
> > + * @direction: xfer direction
> > + * @length: xfer bits len
> > + * @tdio : xfer data array
> > + * @endir: xfer end state
> > + *
> > + * Structure provide interface to JTAG device for JTAG SDR/SIR xfer
> execution.
> > + */
> > +struct jtag_xfer {
> > +	__u8	type;
> > +	__u8	direction;
> > +	__u8	endstate;
> > +	__u8	padding;
> > +	__u32	length;
> > +	__u64	tdio;
> > +};
> > +
> > +/* ioctl interface */
> > +#define __JTAG_IOCTL_MAGIC	0xb2
> > +
> > +#define JTAG_IOCRUNTEST	_IOW(__JTAG_IOCTL_MAGIC, 0,\
> > +			     struct jtag_run_test_idle)
> 
> No need for 2 lines here, fits just fine on one.

Ok

> 
> > +#define JTAG_SIOCFREQ	_IOW(__JTAG_IOCTL_MAGIC, 1, unsigned
> int)
> > +#define JTAG_GIOCFREQ	_IOR(__JTAG_IOCTL_MAGIC, 2, unsigned int)
> > +#define JTAG_IOCXFER	_IOWR(__JTAG_IOCTL_MAGIC, 3, struct
> jtag_xfer)
> > +#define JTAG_GIOCSTATUS _IOWR(__JTAG_IOCTL_MAGIC, 4, enum
> jtag_endstate)
> > +#define JTAG_SIOCMODE	_IOW(__JTAG_IOCTL_MAGIC, 5, unsigned
> int)
> > +
> > +#define JTAG_FIRST_MINOR 0
> 
> Why is this in a uapi file?

Not needed - will be removed.

> 
> > +#define JTAG_MAX_DEVICES 32
> 
> This is never used, why is it in a uapi file?
> 

Not needed - will be removed.

> So, almost there, with the above mentioned things, I think you can make the
> code even simpler, which is always better, right?
> 
> thanks,
> 
> greg k-h

Thanks.

BR,
Oleksandr Shamray
--
To unsubscribe from this list: send the line "unsubscribe linux-serial" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Kernel Newbies]     [Security]     [Netfilter]     [Bugtraq]     [Linux PPP]     [Linux FS]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Linmodem]     [Device Mapper]     [Linux Kernel for ARM]

  Powered by Linux