From 789c1093f02c436b320d78a739f9610c8271cb73 Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Mon, 11 Oct 2021 21:21:14 +0800 Subject: rtc: class: don't call cdev_device_del() when cdev_device_add() failed I got a null-ptr-deref report when doing fault injection test: general protection fault, probably for non-canonical address 0xdffffc0000000022: 0000 [#1] SMP KASAN PTI KASAN: null-ptr-deref in range [0x0000000000000110-0x0000000000000117] RIP: 0010:device_del+0x132/0xdc0 Call Trace: cdev_device_del+0x1a/0x80 devm_rtc_unregister_device+0x37/0x80 release_nodes+0xc3/0x3b0 If cdev_device_add() fails, 'dev->p' is not set, it causes null-ptr-deref when calling cdev_device_del(). Registering character device is optional, we don't return error code here, so introduce a new flag 'RTC_NO_CDEV' to indicate if it has character device, cdev_device_del() is called when this bit is not set. Reported-by: Hulk Robot Signed-off-by: Yang Yingliang Signed-off-by: Alexandre Belloni Link: https://lore.kernel.org/r/20211011132114.3663509-1-yangyingliang@huawei.com --- include/linux/rtc.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/rtc.h b/include/linux/rtc.h index bd611e26291d..354e0843ab17 100644 --- a/include/linux/rtc.h +++ b/include/linux/rtc.h @@ -80,6 +80,7 @@ struct rtc_timer { /* flags */ #define RTC_DEV_BUSY 0 +#define RTC_NO_CDEV 1 struct rtc_device { struct device dev; -- cgit v1.2.3 From 917425f71f36ce6f61841497040e10d0166106d8 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Mon, 18 Oct 2021 17:19:27 +0200 Subject: rtc: add alarm related features Add more alarm related features to be declared by drivers. Signed-off-by: Alexandre Belloni Link: https://lore.kernel.org/r/20211018151933.76865-2-alexandre.belloni@bootlin.com --- include/uapi/linux/rtc.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/uapi/linux/rtc.h b/include/uapi/linux/rtc.h index f950bff75e97..f4037c541925 100644 --- a/include/uapi/linux/rtc.h +++ b/include/uapi/linux/rtc.h @@ -114,7 +114,9 @@ struct rtc_pll_info { #define RTC_FEATURE_ALARM 0 #define RTC_FEATURE_ALARM_RES_MINUTE 1 #define RTC_FEATURE_NEED_WEEK_DAY 2 -#define RTC_FEATURE_CNT 3 +#define RTC_FEATURE_ALARM_RES_2S 3 +#define RTC_FEATURE_UPDATE_INTERRUPT 4 +#define RTC_FEATURE_CNT 5 #define RTC_MAX_FREQ 8192 -- cgit v1.2.3 From 6a8af1b6568ad9ee08a419fb12c793f7992cf8a4 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Mon, 18 Oct 2021 17:19:28 +0200 Subject: rtc: add parameter ioctl Add an ioctl allowing to get and set extra parameters for an RTC. For now, only handle getting available features. Signed-off-by: Alexandre Belloni Link: https://lore.kernel.org/r/20211018151933.76865-3-alexandre.belloni@bootlin.com --- drivers/rtc/dev.c | 40 ++++++++++++++++++++++++++++++++++++++++ include/uapi/linux/rtc.h | 18 ++++++++++++++++++ 2 files changed, 58 insertions(+) (limited to 'include') diff --git a/drivers/rtc/dev.c b/drivers/rtc/dev.c index 5b8ebe86124a..143c097eff0f 100644 --- a/drivers/rtc/dev.c +++ b/drivers/rtc/dev.c @@ -208,6 +208,7 @@ static long rtc_dev_ioctl(struct file *file, const struct rtc_class_ops *ops = rtc->ops; struct rtc_time tm; struct rtc_wkalrm alarm; + struct rtc_param param; void __user *uarg = (void __user *)arg; err = mutex_lock_interruptible(&rtc->ops_lock); @@ -221,6 +222,7 @@ static long rtc_dev_ioctl(struct file *file, switch (cmd) { case RTC_EPOCH_SET: case RTC_SET_TIME: + case RTC_PARAM_SET: if (!capable(CAP_SYS_TIME)) err = -EACCES; break; @@ -382,6 +384,44 @@ static long rtc_dev_ioctl(struct file *file, err = -EFAULT; return err; + case RTC_PARAM_GET: + if (copy_from_user(¶m, uarg, sizeof(param))) { + mutex_unlock(&rtc->ops_lock); + return -EFAULT; + } + + switch(param.param) { + long offset; + case RTC_PARAM_FEATURES: + if (param.index != 0) + err = -EINVAL; + param.uvalue = rtc->features[0]; + break; + + default: + err = -EINVAL; + } + + if (!err) + if (copy_to_user(uarg, ¶m, sizeof(param))) + err = -EFAULT; + + break; + + case RTC_PARAM_SET: + if (copy_from_user(¶m, uarg, sizeof(param))) { + mutex_unlock(&rtc->ops_lock); + return -EFAULT; + } + + switch(param.param) { + case RTC_PARAM_FEATURES: + default: + err = -EINVAL; + } + + break; + default: /* Finally try the driver's ioctl interface */ if (ops->ioctl) { diff --git a/include/uapi/linux/rtc.h b/include/uapi/linux/rtc.h index f4037c541925..3241f9ecc639 100644 --- a/include/uapi/linux/rtc.h +++ b/include/uapi/linux/rtc.h @@ -14,6 +14,7 @@ #include #include +#include /* * The struct used to pass data via the following ioctl. Similar to the @@ -66,6 +67,17 @@ struct rtc_pll_info { long pll_clock; /* base PLL frequency */ }; +struct rtc_param { + __u64 param; + union { + __u64 uvalue; + __s64 svalue; + __u64 ptr; + }; + __u32 index; + __u32 __pad; +}; + /* * ioctl calls that are permitted to the /dev/rtc interface, if * any of the RTC drivers are enabled. @@ -95,6 +107,9 @@ struct rtc_pll_info { #define RTC_PLL_GET _IOR('p', 0x11, struct rtc_pll_info) /* Get PLL correction */ #define RTC_PLL_SET _IOW('p', 0x12, struct rtc_pll_info) /* Set PLL correction */ +#define RTC_PARAM_GET _IOW('p', 0x13, struct rtc_param) /* Get parameter */ +#define RTC_PARAM_SET _IOW('p', 0x14, struct rtc_param) /* Set parameter */ + #define RTC_VL_DATA_INVALID _BITUL(0) /* Voltage too low, RTC data is invalid */ #define RTC_VL_BACKUP_LOW _BITUL(1) /* Backup voltage is low */ #define RTC_VL_BACKUP_EMPTY _BITUL(2) /* Backup empty or not present */ @@ -118,6 +133,9 @@ struct rtc_pll_info { #define RTC_FEATURE_UPDATE_INTERRUPT 4 #define RTC_FEATURE_CNT 5 +/* parameter list */ +#define RTC_PARAM_FEATURES 0 + #define RTC_MAX_FREQ 8192 -- cgit v1.2.3 From 2268551935dbf1abcbb4d4fb7b1ad74dbe0d1be0 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Mon, 18 Oct 2021 17:19:29 +0200 Subject: rtc: expose correction feature Add a new feature for RTCs able to correct the oscillator imprecision. This is also called offset or trimming. Such drivers have a .set_offset callback, use that to set the feature bit from the core. Signed-off-by: Alexandre Belloni Link: https://lore.kernel.org/r/20211018151933.76865-4-alexandre.belloni@bootlin.com --- drivers/rtc/class.c | 3 +++ include/uapi/linux/rtc.h | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c index dbccd71589b9..2e0cbc190a8a 100644 --- a/drivers/rtc/class.c +++ b/drivers/rtc/class.c @@ -389,6 +389,9 @@ int __devm_rtc_register_device(struct module *owner, struct rtc_device *rtc) if (!rtc->ops->set_alarm) clear_bit(RTC_FEATURE_ALARM, rtc->features); + if (rtc->ops->set_offset) + set_bit(RTC_FEATURE_CORRECTION, rtc->features); + rtc->owner = owner; rtc_device_get_offset(rtc); diff --git a/include/uapi/linux/rtc.h b/include/uapi/linux/rtc.h index 3241f9ecc639..c83bb9a4fa4f 100644 --- a/include/uapi/linux/rtc.h +++ b/include/uapi/linux/rtc.h @@ -131,7 +131,8 @@ struct rtc_param { #define RTC_FEATURE_NEED_WEEK_DAY 2 #define RTC_FEATURE_ALARM_RES_2S 3 #define RTC_FEATURE_UPDATE_INTERRUPT 4 -#define RTC_FEATURE_CNT 5 +#define RTC_FEATURE_CORRECTION 5 +#define RTC_FEATURE_CNT 6 /* parameter list */ #define RTC_PARAM_FEATURES 0 -- cgit v1.2.3 From a6d8c6e1a5c6fb982964861dc84c0c7cb0151c7c Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Mon, 18 Oct 2021 17:19:30 +0200 Subject: rtc: add correction parameter Add a new parameter allowing the get and set the correction using ioctls instead of just sysfs. Signed-off-by: Alexandre Belloni Link: https://lore.kernel.org/r/20211018151933.76865-5-alexandre.belloni@bootlin.com --- drivers/rtc/dev.c | 19 +++++++++++++++++++ include/uapi/linux/rtc.h | 1 + 2 files changed, 20 insertions(+) (limited to 'include') diff --git a/drivers/rtc/dev.c b/drivers/rtc/dev.c index 143c097eff0f..abee1fc4705e 100644 --- a/drivers/rtc/dev.c +++ b/drivers/rtc/dev.c @@ -398,6 +398,16 @@ static long rtc_dev_ioctl(struct file *file, param.uvalue = rtc->features[0]; break; + case RTC_PARAM_CORRECTION: + mutex_unlock(&rtc->ops_lock); + if (param.index != 0) + return -EINVAL; + err = rtc_read_offset(rtc, &offset); + mutex_lock(&rtc->ops_lock); + if (err == 0) + param.svalue = offset; + break; + default: err = -EINVAL; } @@ -416,6 +426,15 @@ static long rtc_dev_ioctl(struct file *file, switch(param.param) { case RTC_PARAM_FEATURES: + err = -EINVAL; + break; + + case RTC_PARAM_CORRECTION: + mutex_unlock(&rtc->ops_lock); + if (param.index != 0) + return -EINVAL; + return rtc_set_offset(rtc, param.svalue); + default: err = -EINVAL; } diff --git a/include/uapi/linux/rtc.h b/include/uapi/linux/rtc.h index c83bb9a4fa4f..5debe82439c2 100644 --- a/include/uapi/linux/rtc.h +++ b/include/uapi/linux/rtc.h @@ -136,6 +136,7 @@ struct rtc_param { /* parameter list */ #define RTC_PARAM_FEATURES 0 +#define RTC_PARAM_CORRECTION 1 #define RTC_MAX_FREQ 8192 -- cgit v1.2.3 From 0d20e9fb1262b1f9ac895b287db892bc75b05b84 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Mon, 18 Oct 2021 17:19:31 +0200 Subject: rtc: add BSM parameter BSM or Backup Switch Mode is a common feature on RTCs, allowing to select how the RTC will decide when to switch from its primary power supply to the backup power supply. It is necessary to be able to set it from userspace as there are uses cases where it has to be done dynamically. Supported values are: RTC_BSM_DISABLED: disabled RTC_BSM_DIRECT: switching will happen as soon as Vbackup > Vdd RTC_BSM_LEVEL: switching will happen around a threshold, usually with an hysteresis RTC_BSM_STANDBY: switching will not happen until Vdd > Vbackup, this is useful to ensure the RTC doesn't draw any power until the device is first powered on. Signed-off-by: Alexandre Belloni Link: https://lore.kernel.org/r/20211018151933.76865-6-alexandre.belloni@bootlin.com --- drivers/rtc/dev.c | 10 ++++++++-- include/linux/rtc.h | 2 ++ include/uapi/linux/rtc.h | 9 ++++++++- 3 files changed, 18 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/rtc/dev.c b/drivers/rtc/dev.c index abee1fc4705e..e104972a28fd 100644 --- a/drivers/rtc/dev.c +++ b/drivers/rtc/dev.c @@ -409,7 +409,10 @@ static long rtc_dev_ioctl(struct file *file, break; default: - err = -EINVAL; + if (rtc->ops->param_get) + err = rtc->ops->param_get(rtc->dev.parent, ¶m); + else + err = -EINVAL; } if (!err) @@ -436,7 +439,10 @@ static long rtc_dev_ioctl(struct file *file, return rtc_set_offset(rtc, param.svalue); default: - err = -EINVAL; + if (rtc->ops->param_set) + err = rtc->ops->param_set(rtc->dev.parent, ¶m); + else + err = -EINVAL; } break; diff --git a/include/linux/rtc.h b/include/linux/rtc.h index 354e0843ab17..47fd1c2d3a57 100644 --- a/include/linux/rtc.h +++ b/include/linux/rtc.h @@ -66,6 +66,8 @@ struct rtc_class_ops { int (*alarm_irq_enable)(struct device *, unsigned int enabled); int (*read_offset)(struct device *, long *offset); int (*set_offset)(struct device *, long offset); + int (*param_get)(struct device *, struct rtc_param *param); + int (*param_set)(struct device *, struct rtc_param *param); }; struct rtc_device; diff --git a/include/uapi/linux/rtc.h b/include/uapi/linux/rtc.h index 5debe82439c2..03e5b776e597 100644 --- a/include/uapi/linux/rtc.h +++ b/include/uapi/linux/rtc.h @@ -132,11 +132,18 @@ struct rtc_param { #define RTC_FEATURE_ALARM_RES_2S 3 #define RTC_FEATURE_UPDATE_INTERRUPT 4 #define RTC_FEATURE_CORRECTION 5 -#define RTC_FEATURE_CNT 6 +#define RTC_FEATURE_BACKUP_SWITCH_MODE 6 +#define RTC_FEATURE_CNT 7 /* parameter list */ #define RTC_PARAM_FEATURES 0 #define RTC_PARAM_CORRECTION 1 +#define RTC_PARAM_BACKUP_SWITCH_MODE 2 + +#define RTC_BSM_DISABLED 0 +#define RTC_BSM_DIRECT 1 +#define RTC_BSM_LEVEL 2 +#define RTC_BSM_STANDBY 3 #define RTC_MAX_FREQ 8192 -- cgit v1.2.3