mirror of
https://github.com/apache/nuttx.git
synced 2026-08-01 20:28:58 +00:00
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. This is part of https://github.com/apache/nuttx/issues/19410 Compiles ok. Signed-off-by: Catalin Visinescu <catalin_visinescu@yahoo.com>
366 lines
11 KiB
C
366 lines
11 KiB
C
/****************************************************************************
|
|
* drivers/efuse/efuse.c
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* 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 <nuttx/config.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <string.h>
|
|
#include <fcntl.h>
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <nuttx/debug.h>
|
|
|
|
#include <nuttx/fs/fs.h>
|
|
#include <nuttx/irq.h>
|
|
#include <nuttx/lib/lib.h>
|
|
#include <nuttx/mutex.h>
|
|
#include <nuttx/efuse/efuse.h>
|
|
|
|
#ifdef CONFIG_EFUSE
|
|
|
|
/****************************************************************************
|
|
* Pre-processor Definitions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Private Types
|
|
****************************************************************************/
|
|
|
|
/* This structure describes the state of the upper half driver */
|
|
|
|
struct efuse_upperhalf_s
|
|
{
|
|
mutex_t lock; /* Supports mutual exclusion */
|
|
FAR char *path; /* Registration path */
|
|
|
|
/* The contained lower-half driver */
|
|
|
|
FAR struct efuse_lowerhalf_s *lower;
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Function Prototypes
|
|
****************************************************************************/
|
|
|
|
static ssize_t efuse_read(FAR struct file *filep, FAR char *buffer,
|
|
size_t buflen);
|
|
static ssize_t efuse_write(FAR struct file *filep, FAR const char *buffer,
|
|
size_t buflen);
|
|
static int efuse_ioctl(FAR struct file *filep, int cmd,
|
|
unsigned long arg);
|
|
|
|
/****************************************************************************
|
|
* Private Data
|
|
****************************************************************************/
|
|
|
|
static const struct file_operations g_efuseops =
|
|
{
|
|
NULL, /* open */
|
|
NULL, /* close */
|
|
efuse_read, /* read */
|
|
efuse_write, /* write */
|
|
NULL, /* seek */
|
|
efuse_ioctl, /* ioctl */
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: efuse_read
|
|
*
|
|
* Description:
|
|
* A dummy read method. This is provided only to satisfy the VFS layer.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static ssize_t efuse_read(FAR struct file *filep, FAR char *buffer,
|
|
size_t buflen)
|
|
{
|
|
/* Return zero -- usually meaning end-of-file */
|
|
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: efuse_write
|
|
*
|
|
* Description:
|
|
* A dummy write method. This is provided only to satisfy the VFS layer.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static ssize_t efuse_write(FAR struct file *filep, FAR const char *buffer,
|
|
size_t buflen)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: efuse_ioctl
|
|
*
|
|
* Description:
|
|
* The standard ioctl method. This is where ALL of the efuse timer
|
|
* work is done.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int efuse_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
|
|
{
|
|
FAR struct inode *inode = filep->f_inode;
|
|
FAR struct efuse_upperhalf_s *upper;
|
|
FAR struct efuse_lowerhalf_s *lower;
|
|
int ret;
|
|
|
|
minfo("cmd: %d arg: %lu\n", cmd, arg);
|
|
upper = inode->i_private;
|
|
DEBUGASSERT(upper != NULL);
|
|
lower = upper->lower;
|
|
DEBUGASSERT(lower != NULL);
|
|
|
|
/* Get exclusive access to the device structures */
|
|
|
|
ret = nxmutex_lock(&upper->lock);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
/* Handle built-in ioctl commands */
|
|
|
|
switch (cmd)
|
|
{
|
|
/* cmd: EFUSEIOC_READ_FIELD
|
|
* Description: Read a field
|
|
* Argument: A pointer to a struct efuse_param.
|
|
* Where the field is an array.
|
|
* Each item in the array is a pointer to an efuse_desc_t
|
|
* variable.
|
|
* An efuse_desc_t variable contains an offset to a field
|
|
* or subfield and its size in bits.
|
|
* The size param is a redundancy, and it is the sum
|
|
* of all subfields sizes from efuse_desc_t in bits.
|
|
* The data is a pointer to a pre-allocated space
|
|
* where the driver will load the data read from efuse.
|
|
*/
|
|
|
|
case EFUSEIOC_READ_FIELD:
|
|
{
|
|
FAR struct efuse_param_s *param =
|
|
(FAR struct efuse_param_s *)((uintptr_t)arg);
|
|
|
|
/* Read the efuse */
|
|
|
|
DEBUGASSERT(lower->ops->read_field); /* Required */
|
|
ret = lower->ops->read_field(lower,
|
|
param->field,
|
|
param->data,
|
|
param->size);
|
|
}
|
|
break;
|
|
|
|
/* cmd: EFUSEIOC_WRITE_FIELD
|
|
* Description: Write a field
|
|
* Argument: A pointer to a struct efuse_param.
|
|
* Where the field is an array.
|
|
* Each item in the array is a pointer to an efuse_desc_t
|
|
* variable.
|
|
* An efuse_desc_t variable contains an offset to a field
|
|
* or subfield and its size in bits.
|
|
* The size param is a redundancy, and it is the sum
|
|
* of all subfields sizes from efuse_desc_t in bits.
|
|
* The data is a pointer to a pre-allocated space
|
|
* where the user wrote the value that he wants
|
|
* to write in a field or subfield.
|
|
*/
|
|
|
|
case EFUSEIOC_WRITE_FIELD:
|
|
{
|
|
FAR struct efuse_param_s *param =
|
|
(FAR struct efuse_param_s *)((uintptr_t)arg);
|
|
|
|
/* Write the efuse */
|
|
|
|
DEBUGASSERT(lower->ops->write_field); /* Required */
|
|
ret = lower->ops->write_field(lower,
|
|
param->field,
|
|
param->data,
|
|
param->size);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
minfo("Forwarding unrecognized cmd: %d arg: %lu\n", cmd, arg);
|
|
|
|
/* Ioctls commands that are not recognized by the "upper-half"
|
|
* driver are forwarded to the lower half driver through this
|
|
* method.
|
|
*/
|
|
|
|
if (lower->ops->ioctl) /* Optional */
|
|
{
|
|
ret = lower->ops->ioctl(lower, cmd, arg);
|
|
}
|
|
else
|
|
{
|
|
ret = -ENOTTY;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
nxmutex_unlock(&upper->lock);
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: efuse_register
|
|
*
|
|
* Description:
|
|
* This function binds an instance of a "lower half" efuse driver with
|
|
* the "upper half" efuse device and registers that device so that can
|
|
* be used by application code.
|
|
*
|
|
* Input Parameters:
|
|
* dev path - The full path to the driver to be registered in the NuttX
|
|
* pseudo-filesystem. The recommended convention is to name all
|
|
* efuse drivers as "/dev/efuse".
|
|
* lower - A pointer to an instance of lower half efuse driver. This
|
|
* instance is bound to the efuse driver and must persists as long as
|
|
* the driver persists.
|
|
*
|
|
* Returned Value:
|
|
* On success, a non-NULL handle is returned to the caller. In the event
|
|
* of any failure, a NULL value is returned.
|
|
*
|
|
****************************************************************************/
|
|
|
|
FAR void *efuse_register(FAR const char *path,
|
|
FAR struct efuse_lowerhalf_s *lower)
|
|
{
|
|
FAR struct efuse_upperhalf_s *upper;
|
|
int ret;
|
|
|
|
DEBUGASSERT(path && lower);
|
|
minfo("Entry: path=%s\n", path);
|
|
|
|
/* Allocate the upper-half data structure */
|
|
|
|
upper = (FAR struct efuse_upperhalf_s *)
|
|
kmm_zalloc(sizeof(struct efuse_upperhalf_s));
|
|
if (!upper)
|
|
{
|
|
merr("Upper half allocation failed\n");
|
|
goto errout;
|
|
}
|
|
|
|
/* Initialize the efuse timer device structure (it was already zeroed
|
|
* by kmm_zalloc()).
|
|
*/
|
|
|
|
nxmutex_init(&upper->lock);
|
|
upper->lower = lower;
|
|
|
|
/* Copy the registration path */
|
|
|
|
upper->path = strdup(path);
|
|
if (!upper->path)
|
|
{
|
|
merr("Path allocation failed\n");
|
|
goto errout_with_upper;
|
|
}
|
|
|
|
/* Register the efuse timer device */
|
|
|
|
ret = register_driver(path, &g_efuseops, 0600, upper);
|
|
if (ret < 0)
|
|
{
|
|
merr("register_driver failed: %d\n", ret);
|
|
goto errout_with_path;
|
|
}
|
|
|
|
return (FAR void *)upper;
|
|
|
|
errout_with_path:
|
|
lib_free(upper->path);
|
|
|
|
errout_with_upper:
|
|
nxmutex_destroy(&upper->lock);
|
|
kmm_free(upper);
|
|
|
|
errout:
|
|
return NULL;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: efuse_unregister
|
|
*
|
|
* Description:
|
|
* This function can be called to disable and unregister the efuse
|
|
* device driver.
|
|
*
|
|
* Input Parameters:
|
|
* handle - This is the handle that was returned by efuse_register()
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
****************************************************************************/
|
|
|
|
void efuse_unregister(FAR void *handle)
|
|
{
|
|
FAR struct efuse_upperhalf_s *upper;
|
|
FAR struct efuse_lowerhalf_s *lower;
|
|
|
|
/* Recover the pointer to the upper-half driver state */
|
|
|
|
upper = (FAR struct efuse_upperhalf_s *)handle;
|
|
DEBUGASSERT(upper != NULL);
|
|
lower = upper->lower;
|
|
DEBUGASSERT(lower != NULL);
|
|
|
|
minfo("Unregistering: %s\n", upper->path);
|
|
|
|
/* Unregister the efuse timer device */
|
|
|
|
unregister_driver(upper->path);
|
|
|
|
/* Then free all of the driver resources */
|
|
|
|
lib_free(upper->path);
|
|
nxmutex_destroy(&upper->lock);
|
|
kmm_free(upper);
|
|
}
|
|
|
|
#endif /* CONFIG_EFUSE */
|