PS:要转载请注明出处,本人版权所有。

PS: 这个只是基于《我自己》的理解,

如果和你的原则及想法相冲突,请谅解,勿喷。

环境说明

  无

前言


  稍微接触过嵌入式板卡的,基本都知道嵌入式板卡里面有个功能叫做RTC。在Linux里面,有几个概念比较重要,它们分别是系统时间和硬件时钟。对于系统时间的话,大家都了解的比较多,我们在各种界面上看到的时间都是系统时间,我们在CLI里面,用date命令操作的时间就是系统时间,我们的系统时间可以通过网络同步或者说RTC同步。

  此外,还有一个硬件时钟功能,这个功能就是RealTimeClock,它主要是用纽扣电池来维护一个硬件时钟,主要是用来同步时钟用的。例如,我们的设备怎么在没有网络的情况下,每次上电开机都能够得到真实时间,就需要靠RTC功能。对于RTC来说,在Linux里面,我们一般使用hwclock命令来对他做相关的操作,例如设置硬件时钟,例如从硬件时钟中得到真实时间,并设置到系统时钟等等。

一个奇怪的错误


  上面我们提到了hwclock,最近,我们常用的一个板卡,其是Android系统,我们在交付给客户的时候,发现了一个奇怪的问题,我们执行hwclock失败了,而且错误还是Permission Deniend,熟悉linux的朋友可能都经常看到这个错误,它就是权限错误。下面是我们执行此命令的环境和错误:

  有人立马就会想到是不是我们没有用root用户来执行的原因,从上图可知,并不是这样的,这个错误远远没有想象的那么简单,但是也没有那么复杂。

深入分析相关源码


  首先,在Android里面,hwclock是来至于一个叫做toybox的库,大概在external/toybox/toys/other/hwclock.c,其中关于设置硬件时钟时的核心代码段如下:

if (toys.optflags & FLAG_w) {
/* The value of tm_isdst is positive if daylight saving time is in effect,
* zero if it is not and negative if the information is not available.
* todo: so why isn't this negative...? */
tm.tm_isdst = 0;
xioctl(fd, RTC_SET_TIME, &tm);
}

  看到这里,我们明白,必须要到内核源码才能够得到我们想要的答案。

  通过RTC_SET_TIME,在内核代码的drivers/rtc/rtc-dev.c里面,我们找到了第一处可能导致出现Permission Deniend的地方,代码如下:

static long rtc_dev_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
int err = 0;
struct rtc_device *rtc = file->private_data;
const struct rtc_class_ops *ops = rtc->ops;
struct rtc_time tm;
struct rtc_wkalrm alarm;
void __user *uarg = (void __user *) arg; err = mutex_lock_interruptible(&rtc->ops_lock);
if (err)
return err; /* check that the calling task has appropriate permissions
* for certain ioctls. doing this check here is useful
* to avoid duplicate code in each driver.
*/
switch (cmd) {
case RTC_EPOCH_SET:
case RTC_SET_TIME:
if (!capable(CAP_SYS_TIME))
err = -EACCES;
break; case RTC_IRQP_SET:
if (arg > rtc->max_user_freq && !capable(CAP_SYS_RESOURCE))
err = -EACCES;
break; case RTC_PIE_ON:
if (rtc->irq_freq > rtc->max_user_freq &&
!capable(CAP_SYS_RESOURCE))
err = -EACCES;
break;
} //......
}

  这里产生权限拒绝的原因是由于Linux Capability,因此我们首要的是检查我们的hwclock有没有CAP_SYS_TIME能力,我们通过prctl + PR_CAPBSET_READ 来获取hwclock有没有这个能力(当然,这里可以有很多的其他方式来确定,我这里用最简单的方法来确定),在hwclock源码中添加如下代码段:

  int ret = prctl(PR_CAPBSET_READ, CAP_SYS_TIME);
if (ret < 0)
perror("sky: prctl PR_CAPBSET_READ"); printf("has CAP_SYS_TIME %d\n", ret);

  运行结果如下图:

  从这里我们可以知道,我们的hwclock程序是具备CAP_SYS_TIME的,因此,报权限拒绝的地方并不在这里。

  我们接着通过RTC_SET_TIME,在内核代码的drivers/rtc/rtc-dev.c里面,有如下片段:

static long rtc_dev_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
// ... ...
case RTC_SET_TIME:
mutex_unlock(&rtc->ops_lock); if (copy_from_user(&tm, uarg, sizeof(tm)))
return -EFAULT; return rtc_set_time(rtc, &tm); // ... ...
}

  我们从这里可以看到,通过RTC_SET_TIME,我们执行rtc_set_time此方法,然后得到了返回值,我们有理由怀疑,我们得到的权限拒绝,来自于这个地方。

  在内核代码的drivers/rtc/interface.c里面,有rtc_set_time的定义,函数定义如下:

int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm)
{
int err; err = rtc_valid_tm(tm);
if (err != 0)
return err; err = rtc_valid_range(rtc, tm);
if (err)
return err; rtc_subtract_offset(rtc, tm); err = mutex_lock_interruptible(&rtc->ops_lock);
if (err)
return err; if (!rtc->ops)
err = -ENODEV;
else if (rtc->ops->set_time)
err = rtc->ops->set_time(rtc->dev.parent, tm);
else if (rtc->ops->set_mmss64) {
time64_t secs64 = rtc_tm_to_time64(tm); err = rtc->ops->set_mmss64(rtc->dev.parent, secs64);
} else if (rtc->ops->set_mmss) {
time64_t secs64 = rtc_tm_to_time64(tm);
err = rtc->ops->set_mmss(rtc->dev.parent, secs64);
} else
err = -EINVAL; pm_stay_awake(rtc->dev.parent);
mutex_unlock(&rtc->ops_lock);
/* A timer might have just expired */
schedule_work(&rtc->irqwork); trace_rtc_set_time(rtc_tm_to_time64(tm), err);
return err;
}
EXPORT_SYMBOL_GPL(rtc_set_time);

  从这段代码可知,真正的错误还是来自于rtc 具体驱动里面的xxx_set_time 函数里面。在Linux内核里面,有许多的RTC驱动,因此,我们需要了解到我们的当前的RTC硬件是什么,我们通过logcat -b kernel |grep rtc命令得到了如下的信息:

  因此,我们当前的rtc驱动就是rtc-pm8xxx,我们去查找相关的驱动源码drivers/rtc/rtc-pm8xxx.c,发现了一点端倪,其set_time源码重点片段如下:

static int pm8xxx_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
// ... ...
struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev);
// ... ... // ... ...
if (!rtc_dd->allow_set_time)
return -EACCES;
// ... ...
}

  从这里,我们可以知道,这个驱动有一个allow_set_time的参数,如果不允许,就会返回权限拒绝,那到底可不可以呢?我们尝试将这个属性设置一下,或者直接修改当前的源码。我们全局查找一下这个属性,发现其来自于这里:

static int pm8xxx_rtc_probe(struct platform_device *pdev)
{
// ... ...
struct pm8xxx_rtc *rtc_dd; // ... ...
rtc_dd = devm_kzalloc(&pdev->dev, sizeof(*rtc_dd), GFP_KERNEL);
if (rtc_dd == NULL)
return -ENOMEM;
// ... ...
rtc_dd->allow_set_time = of_property_read_bool(pdev->dev.of_node,
"allow-set-time");
// ... ...
}

  从这里,我们可以知道当前这个rtc_dd->allow_set_time来自于dts里面,一个叫做allow-set-time的属性。

  我们通过pm8150_rtc全局搜索,在某dtsi文件里面发现了此驱动的相关定义,我们修改这个字段,添加allow-set-time属性,示例如下:


pm8150_rtc: qcom,pm8150_rtc {
// ... ...
allow-set-time;
};

  然后我们通过重新编译android源码,重新生成dtbo.img镜像,然后刷入我们的系统,然后我们惊奇的发现,我们解决了这个奇怪的permission denied问题。

后记


  虽然,我们解决了permission denied问题,但是后续还是出现了rtc无法正常设置的问题,通过内核日志查看,是pm8150驱动出了问题,最后只能交给上游板卡厂家去适配解决。

参考文献

  • 无

打赏、订阅、收藏、丢香蕉、硬币,请关注公众号(攻城狮的搬砖之路)

PS: 请尊重原创,不喜勿喷。

PS: 要转载请注明出处,本人版权所有。

PS: 有问题请留言,看到后我会第一时间回复。

记一次有趣的hwclock写RTC的PermissionDenied错误的更多相关文章

  1. 使用hwclock同步RTC(硬件时钟)和OS Clock(操作系统时钟)

    Linux下面有个命令叫做hwclock,其主要的功能是用来在RTC和操作系统之间进行时钟同步.既可以将RTC的时钟同步到操作系统,也可以在通过hwclock将当前系统时间.Internet时间.本地 ...

  2. 记一次有趣的tp5代码执行

    0x00 前言 朋友之前给了个站,拿了很久终于拿下,简单记录一下. 0x01 基础信息 漏洞点:tp 5 method 代码执行,payload如下 POST /?s=captcha _method= ...

  3. 记一次有趣的thinkphp代码执行

    0x00 前言 朋友之前给了个站,拿了很久终于拿下,简单记录一下. 0x01 基础信息 漏洞点:tp 5 method 代码执行,payload如下 POST /?s=captcha _method= ...

  4. 记一次有趣的互联网事件及console.log~

    寂寞的中国互联网又一次瘫痪了. 说是顶级域的根挂了,不知道是黑客还是某个实习生干挂的. 反正到现在还没有人来解释这件事. 先普及一下,为什么顶级域的根挂了全中国都挂了. 那是因为dns解析的特点是递归 ...

  5. 记一次有趣的 Netty 源码问题

    背景 起因是一个朋友问我的一个关于 ServerBootstrap 启动的问题. 相关 issue 他的问题我复述一下: ServerBootstrap 的绑定流程如下: ServerBootstra ...

  6. 使用hwclock读取rtc中的时间时报错"hwclock: ioctl(RTC_RD_TIME) to /dev/rtc0 to read the time failed: No such device or address"如何处理?

    1. No such device or address 这一句表明当前的板子上没有这样的外设,检查设备树和硬件连接情况 2. 笔者是这样解决的 由于设备树中为rtc所指定的总线与硬件上的连接rtc的 ...

  7. 记一次用express手写博客

    1.req.session时一直是undefined 解决方法: // sesssion应用的配置 app.use(session({ secret:'blog', cookie: ('name', ...

  8. 记一次有趣的JsonFormat不生效问题

    dto中使用了JsonFormat注解,如图 然后再序列化时 objectMapper.writeValueAsString(printReceBillVO) 始终值是一个Long,最后发现是包引用错 ...

  9. sort函数居然能改变元素值?记一次有趣的Bug——四数之和

    坐标leetcode: 我想都不想直接深度优先搜索暴力求解: class Solution { public: vector<vector<int>> res; //答案 in ...

  10. windows下安装phpcms html/ 文件夹不可写的一种错误以及解决方法

    朋友安装phpcms时遇到奇葩问题,环境搭建在windows7中,竟然出现 html/ 和 phpsso_server/caches/文件夹不可写问题(如图) 在windows下出现这种权限的问题真不 ...

随机推荐

  1. Unity的IFilterBuildAssemblies:深入解析与实用案例

    Unity IFilterBuildAssemblies Unity IFilterBuildAssemblies是Unity引擎中的一个非常有用的功能,它可以让开发者在构建项目时自定义哪些程序集需要 ...

  2. ChatGPT 助力开发人员改进代码的5个方式

    近年来,在软件开发中使用人工智能和机器学习变得越来越普遍.因此,开发人员开始转向像 OpenAI 的 ChatGPT 这样的工具来简化他们的工作,提高他们的工作效率.ChatGPT是一个由 OpenA ...

  3. 后缀数组C++详解

    后缀定义 "后缀i"代表以第i个字符开头的后缀,存储是用i代表字符串s的后缀s[i...n] 后缀数组是什么? 后缀数组(Suffix Array)主要关系到两个数组:sa 和 r ...

  4. 一文了解 history 和 react-router 的实现原理

    我们是袋鼠云数栈 UED 团队,致力于打造优秀的一站式数据中台产品.我们始终保持工匠精神,探索前端道路,为社区积累并传播经验价值. 本文作者:霜序 前言 在前一篇文章中,我们详细的说了 react-r ...

  5. 实在智能TARS-RPA-Agent,业界首发的产品级大模型Agent有何非凡之处?

    融合LLM的RPA进化到什么程度? AIGC如何借AI Agent落地? 像生成文本一样生成流程的ChatRPA,能够提升RPA新体验? 边探索边创建的ChatRPA,能否破解RPA与LLM融合难题? ...

  6. AI绘画:StableDiffusion制作AI赛博机车图保姆级教程

    本教程收集于:AIGC从入门到精通教程汇总 如果你具备Stable Diffusion的基础知识,那制作AI赛博机车图会更为顺畅.即便没有这个基础,只要严格按照教程的步骤执行,也能成功制作出来. 本教 ...

  7. 快手根据关键词取商品列表 API 返回值说明

    item_search-根据关键词取商品列表 注册开通 ks.item_search 公共参数 名称 类型 必须 描述 key String 是 调用key(必须以GET方式拼接在URL中) secr ...

  8. pycurl库使用详解

    要使用pycurl库 要初始化一个curl对象 c = pycurl.Curl() 设置选项 c.setopt

  9. 5.2 磁盘CRC32完整性检测

    CRC校验技术是用于检测数据传输或存储过程中是否出现了错误的一种方法,校验算法可以通过计算应用与数据的循环冗余校验(CRC)检验值来检测任何数据损坏.通过运用本校验技术我们可以实现对特定内存区域以及磁 ...

  10. 3 分钟把高质量 AI 知识库 FastGPT 装进企业微信

    FastGPT V4 已经上线,直接冲上 GitHub Trending. 如果你还不知道 FastGPT 是什么,可以先去看看作者的介绍 使用 FastGPT 构建高质量 AI 知识库 非常多的企业 ...