mirror of
https://github.com/apache/nuttx.git
synced 2026-08-01 20:28:58 +00:00
Permissions (Part 2) Description: In kernel builds, any unprivileged process running on the NuttX device can open /dev/efuse and attempt to read/write fuse content. Reading the fuses may provide valuable information to an attacker controlling the user process. The write operation, in extreme cases where the fuse blocks are not locked, may brick the device. DISCLAIMER: I tried to be strict with the settings, better to relax them later if it's needed. This is part of https://github.com/apache/nuttx/issues/19410 See https://github.com/apache/nuttx/issues/19410 Compiles ok. Signed-off-by: Catalin Visinescu <catalin_visinescu@yahoo.com>
483 lines
13 KiB
C
483 lines
13 KiB
C
/****************************************************************************
|
|
* drivers/timers/ptp_clock.c
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership. The
|
|
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance with the
|
|
* License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
* License for the specific language governing permissions and limitations
|
|
* under the License.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Included Files
|
|
****************************************************************************/
|
|
|
|
#include <sys/types.h>
|
|
#include <stdio.h>
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <nuttx/debug.h>
|
|
|
|
#include <nuttx/fs/fs.h>
|
|
#include <nuttx/kmalloc.h>
|
|
#include <nuttx/mutex.h>
|
|
#include <nuttx/timers/ptp_clock.h>
|
|
|
|
/****************************************************************************
|
|
* Pre-processor Definitions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Private Types
|
|
****************************************************************************/
|
|
|
|
/* This structure describes the state of the upper half driver */
|
|
|
|
struct ptp_upperhalf_s
|
|
{
|
|
FAR struct ptp_lowerhalf_s *lower; /* The handle of lower half driver */
|
|
mutex_t lock; /* Manages exclusive access to file operations */
|
|
long max_adj; /* The maximum frequency adjustment */
|
|
long adj_freq; /* remembers the frequency adjustment */
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Function Prototypes
|
|
****************************************************************************/
|
|
|
|
static ssize_t ptp_clock_read(FAR struct file *filep, FAR char *buffer,
|
|
size_t buflen);
|
|
static int ptp_clock_ioctl(FAR struct file *filep, int cmd,
|
|
unsigned long arg);
|
|
|
|
/****************************************************************************
|
|
* Private Data
|
|
****************************************************************************/
|
|
|
|
static const struct file_operations g_ptp_clock_file_ops =
|
|
{
|
|
NULL, /* open */
|
|
NULL, /* close */
|
|
ptp_clock_read, /* read */
|
|
NULL, /* write */
|
|
NULL, /* seek */
|
|
ptp_clock_ioctl, /* ioctl */
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
static ssize_t ptp_clock_read(FAR struct file *filep, FAR char *buffer,
|
|
size_t buflen)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static inline long ptp_clock_scaled_ppm_to_ppb(long ppm)
|
|
{
|
|
/* The 'freq' field in the 'struct timex' is in parts per
|
|
* million, but with a 16 bit binary fractional field.
|
|
*
|
|
* We want to calculate
|
|
*
|
|
* ppb = scaled_ppm * 1000 / 2^16
|
|
*
|
|
* which simplifies to
|
|
*
|
|
* ppb = scaled_ppm * 125 / 2^13
|
|
*/
|
|
|
|
int64_t ppb = 1 + ppm;
|
|
|
|
ppb *= 125;
|
|
ppb >>= 13;
|
|
return (long)ppb;
|
|
}
|
|
|
|
static int ptp_clock_adjtime(FAR struct ptp_lowerhalf_s *lower,
|
|
FAR struct timex *tx)
|
|
{
|
|
FAR struct ptp_upperhalf_s *upper = lower->upper;
|
|
int ret = -ENOTSUP;
|
|
|
|
if (tx->modes & ADJ_SETOFFSET)
|
|
{
|
|
struct timespec ts;
|
|
int64_t delta;
|
|
|
|
ts.tv_sec = tx->time.tv_sec;
|
|
ts.tv_nsec = tx->time.tv_usec;
|
|
if (!(tx->modes & ADJ_NANO))
|
|
{
|
|
ts.tv_nsec *= 1000;
|
|
}
|
|
|
|
if ((unsigned long)ts.tv_nsec >= NSEC_PER_SEC)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
|
|
delta = ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec;
|
|
ret = lower->ops->adjtime(lower, delta);
|
|
}
|
|
else if ((tx->modes & ADJ_FREQUENCY) && lower->ops->adjfine != NULL)
|
|
{
|
|
long ppb = ptp_clock_scaled_ppm_to_ppb(tx->freq);
|
|
|
|
if (ppb > upper->max_adj || ppb < -upper->max_adj)
|
|
{
|
|
return -ERANGE;
|
|
}
|
|
|
|
ret = lower->ops->adjfine(lower, ppb);
|
|
upper->adj_freq = tx->freq;
|
|
}
|
|
else if ((tx->modes & ADJ_OFFSET) &&
|
|
lower->ops->adjphase != NULL)
|
|
{
|
|
int32_t offset = tx->offset;
|
|
|
|
if (!(tx->modes & ADJ_NANO))
|
|
{
|
|
offset *= NSEC_PER_USEC;
|
|
}
|
|
|
|
ret = lower->ops->adjphase(lower, offset);
|
|
}
|
|
else if (tx->modes == 0)
|
|
{
|
|
tx->freq = upper->adj_freq;
|
|
ret = 0;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int ptp_clock_ioctl(FAR struct file *filep, int cmd,
|
|
unsigned long arg)
|
|
{
|
|
FAR struct ptp_upperhalf_s *upper = filep->f_inode->i_private;
|
|
FAR struct ptp_lowerhalf_s *lower = upper->lower;
|
|
int ret = -ENOTTY;
|
|
int i;
|
|
|
|
nxmutex_lock(&upper->lock);
|
|
switch (cmd)
|
|
{
|
|
case PTP_CLOCK_SETTIME:
|
|
{
|
|
ret = -ENOTSUP;
|
|
if (lower->ops->settime)
|
|
{
|
|
ret = lower->ops->settime(lower,
|
|
(FAR const struct timespec *)(uintptr_t)arg);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case PTP_CLOCK_GETTIME:
|
|
{
|
|
ret = -ENOTSUP;
|
|
if (lower->ops->gettime)
|
|
{
|
|
ret = lower->ops->gettime(lower,
|
|
(FAR struct timespec *)(uintptr_t)arg, NULL);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case PTP_CLOCK_GETRES:
|
|
{
|
|
ret = -ENOTSUP;
|
|
if (lower->ops->getres)
|
|
{
|
|
ret = lower->ops->getres(lower,
|
|
(FAR struct timespec *)(uintptr_t)arg);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case PTP_CLOCK_ADJTIME:
|
|
{
|
|
ret = ptp_clock_adjtime(lower, (FAR struct timex *)(uintptr_t)arg);
|
|
}
|
|
break;
|
|
|
|
case PTP_CLOCK_GETCAPS:
|
|
case PTP_CLOCK_GETCAPS2:
|
|
{
|
|
FAR struct ptp_clock_caps *caps = (FAR struct ptp_clock_caps *)
|
|
(uintptr_t)arg;
|
|
|
|
memset(caps, 0, sizeof(*caps));
|
|
caps->max_adj = upper->max_adj;
|
|
caps->cross_timestamping = lower->ops->getcrosststamp != NULL;
|
|
caps->adjust_phase = lower->ops->adjphase != NULL;
|
|
ret = OK;
|
|
}
|
|
break;
|
|
|
|
case PTP_SYS_OFFSET_PRECISE:
|
|
case PTP_SYS_OFFSET_PRECISE2:
|
|
{
|
|
FAR struct ptp_sys_offset_precise *preoff =
|
|
(FAR struct ptp_sys_offset_precise *)(uintptr_t)arg;
|
|
struct system_device_crosststamp xtstamp;
|
|
|
|
if (!lower->ops->getcrosststamp)
|
|
{
|
|
ret = -ENOTSUP;
|
|
break;
|
|
}
|
|
|
|
ret = lower->ops->getcrosststamp(lower, &xtstamp);
|
|
if (ret != 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
memset(preoff, 0, sizeof(*preoff));
|
|
preoff->device.sec = xtstamp.device.tv_sec;
|
|
preoff->device.nsec = xtstamp.device.tv_nsec;
|
|
preoff->sys_realtime.sec = xtstamp.realtime.tv_sec;
|
|
preoff->sys_realtime.nsec = xtstamp.realtime.tv_nsec;
|
|
preoff->sys_monoraw.sec = xtstamp.monoraw.tv_sec;
|
|
preoff->sys_monoraw.nsec = xtstamp.monoraw.tv_nsec;
|
|
}
|
|
break;
|
|
|
|
case PTP_SYS_OFFSET_EXTENDED:
|
|
case PTP_SYS_OFFSET_EXTENDED2:
|
|
{
|
|
FAR struct ptp_sys_offset_extended *extoff =
|
|
(FAR struct ptp_sys_offset_extended *)(uintptr_t)arg;
|
|
struct ptp_system_timestamp sts;
|
|
struct timespec ts;
|
|
|
|
if (!lower->ops->gettime)
|
|
{
|
|
ret = -ENOTSUP;
|
|
break;
|
|
}
|
|
|
|
if (extoff->n_samples > PTP_MAX_SAMPLES ||
|
|
extoff->rsv[0] || extoff->rsv[1] || extoff->rsv[2])
|
|
{
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
for (i = 0; i < extoff->n_samples; i++)
|
|
{
|
|
ret = lower->ops->gettime(lower, &ts, &sts);
|
|
if (ret < 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
extoff->ts[i][0].sec = sts.pre_ts.tv_sec;
|
|
extoff->ts[i][0].nsec = sts.pre_ts.tv_nsec;
|
|
extoff->ts[i][1].sec = ts.tv_sec;
|
|
extoff->ts[i][1].nsec = ts.tv_nsec;
|
|
extoff->ts[i][2].sec = sts.post_ts.tv_sec;
|
|
extoff->ts[i][2].nsec = sts.post_ts.tv_nsec;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case PTP_SYS_OFFSET:
|
|
case PTP_SYS_OFFSET2:
|
|
{
|
|
FAR struct ptp_sys_offset *sysoff =
|
|
(FAR struct ptp_sys_offset *)(uintptr_t)arg;
|
|
FAR struct ptp_clock_time *pct;
|
|
struct timespec ts;
|
|
|
|
if (lower->ops->gettime == NULL)
|
|
{
|
|
ret = -ENOTSUP;
|
|
break;
|
|
}
|
|
|
|
if (sysoff->n_samples > PTP_MAX_SAMPLES)
|
|
{
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
pct = &sysoff->ts[0];
|
|
for (i = 0; i < sysoff->n_samples; i++)
|
|
{
|
|
nxclock_gettime(CLOCK_REALTIME, &ts);
|
|
pct->sec = ts.tv_sec;
|
|
pct->nsec = ts.tv_nsec;
|
|
pct++;
|
|
ret = lower->ops->gettime(lower, &ts, NULL);
|
|
if (ret < 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
pct->sec = ts.tv_sec;
|
|
pct->nsec = ts.tv_nsec;
|
|
pct++;
|
|
}
|
|
|
|
nxclock_gettime(CLOCK_REALTIME, &ts);
|
|
pct->sec = ts.tv_sec;
|
|
pct->nsec = ts.tv_nsec;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
if (lower->ops->control)
|
|
{
|
|
ret = lower->ops->control(lower, cmd, arg);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
nxmutex_unlock(&upper->lock);
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: ptp_clockid_to_filep
|
|
*
|
|
* Description:
|
|
* Convert clockid to struct filep.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int ptp_clockid_to_filep(clockid_t clock_id, FAR struct file **filep)
|
|
{
|
|
FAR const struct file_operations *ops;
|
|
int ret;
|
|
|
|
if ((clock_id & CLOCK_MASK) != CLOCK_FD)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = clock_id >> CLOCK_SHIFT;
|
|
if (ret >= 0)
|
|
{
|
|
ret = file_get(ret, filep);
|
|
}
|
|
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
ops = (*filep)->f_inode->u.i_ops;
|
|
if (ops != &g_ptp_clock_file_ops)
|
|
{
|
|
file_put(*filep);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ptp_clock_register
|
|
*
|
|
* Description:
|
|
* This function binds an instance of a "lower half" ptp driver with the
|
|
* "upper half" ptp device and registers that device so that can be used
|
|
* by application code.
|
|
*
|
|
* Input Parameters:
|
|
* lower - A pointer to an instance of lower half ptp driver. This
|
|
* instance is bound to the ptp driver and must persists as long
|
|
* as the driver persists.
|
|
* mxa_adj - The maximum frequency adjustment in parts per billion.
|
|
* devno - The user specifies number of device. ex: /dev/ptpX.
|
|
*
|
|
* Returned Value:
|
|
* OK if the driver was successfully registered; A negated errno value is
|
|
* returned on any failure.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int ptp_clock_register(FAR struct ptp_lowerhalf_s *lower, int32_t max_adj,
|
|
int devno)
|
|
{
|
|
FAR struct ptp_upperhalf_s *upper;
|
|
char path[16];
|
|
int ret;
|
|
|
|
DEBUGASSERT(lower != NULL);
|
|
|
|
/* Allocate the upper-half data structure */
|
|
|
|
upper = kmm_zalloc(sizeof(struct ptp_upperhalf_s));
|
|
if (!upper)
|
|
{
|
|
ptperr("ERROR: Allocation failed\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
upper->lower = lower;
|
|
upper->max_adj = max_adj;
|
|
lower->upper = upper;
|
|
nxmutex_init(&upper->lock);
|
|
|
|
snprintf(path, sizeof(path), "/dev/ptp%d", devno);
|
|
ptpinfo("Registering %s\n", path);
|
|
ret = register_driver(path, &g_ptp_clock_file_ops, 0600, upper);
|
|
if (ret < 0)
|
|
{
|
|
nxmutex_destroy(&upper->lock);
|
|
kmm_free(upper);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ptp_clock_unregister
|
|
*
|
|
* Description:
|
|
* This function unregister character node and release all resource about
|
|
* upper half driver.
|
|
*
|
|
* Input Parameters:
|
|
* lower - A pointer to an instance of lower half ptp driver. This
|
|
* instance is bound to the ptp driver and must persists as long
|
|
* as the driver persists.
|
|
* devno - The user specifies which device of this type, from 0.
|
|
****************************************************************************/
|
|
|
|
void ptp_clock_unregister(FAR struct ptp_lowerhalf_s *lower, int devno)
|
|
{
|
|
FAR struct ptp_upperhalf_s *upper = lower->upper;
|
|
|
|
if (upper != NULL)
|
|
{
|
|
char path[16];
|
|
|
|
snprintf(path, sizeof(path), "/dev/ptp%d", devno);
|
|
unregister_driver(path);
|
|
nxmutex_destroy(&upper->lock);
|
|
kmm_free(upper);
|
|
}
|
|
}
|