mirror of
https://github.com/apache/nuttx.git
synced 2026-08-01 20:28:58 +00:00
Summary Permissions (Part 3) 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 Impact See https://github.com/apache/nuttx/issues/19410 Testing Compiles ok. Signed-off-by: Catalin Visinescu <catalin_visinescu@yahoo.com>
813 lines
20 KiB
C
813 lines
20 KiB
C
/****************************************************************************
|
|
* drivers/mtd/dhara.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 <errno.h>
|
|
#include <nuttx/debug.h>
|
|
#include <stdio.h>
|
|
|
|
#include <nuttx/nuttx.h>
|
|
#include <nuttx/kmalloc.h>
|
|
#include <nuttx/mtd/mtd.h>
|
|
#include <nuttx/lib/lib.h>
|
|
|
|
#include <dhara/map.h>
|
|
#include <dhara/nand.h>
|
|
|
|
/****************************************************************************
|
|
* Pre-processor Definitions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Private Types
|
|
****************************************************************************/
|
|
|
|
struct dhara_pagecache_s
|
|
{
|
|
dq_entry_t node;
|
|
dhara_page_t page;
|
|
FAR uint8_t *buffer;
|
|
};
|
|
|
|
typedef struct dhara_pagecache_s dhara_pagecache_t;
|
|
|
|
struct dhara_dev_s
|
|
{
|
|
struct dhara_nand nand;
|
|
struct dhara_map map;
|
|
mutex_t lock;
|
|
|
|
FAR struct mtd_dev_s *mtd; /* Contained MTD interface */
|
|
struct mtd_geometry_s geo; /* Device geometry */
|
|
uint16_t blkper; /* R/W blocks per erase block */
|
|
uint16_t refs; /* Number of references */
|
|
bool unlinked; /* The driver has been unlinked */
|
|
|
|
/* Two pagesize buffer first is for working temp buffer
|
|
* second is for journel use
|
|
*/
|
|
|
|
FAR uint8_t *pagebuf;
|
|
|
|
/* Read cache for accelerate read dhara meta data */
|
|
|
|
struct dq_queue_s readcache;
|
|
dhara_pagecache_t readpage[CONFIG_DHARA_READ_NCACHES];
|
|
};
|
|
|
|
typedef struct dhara_dev_s dhara_dev_t;
|
|
|
|
/****************************************************************************
|
|
* Private Function Prototypes
|
|
****************************************************************************/
|
|
|
|
static int dhara_open(FAR struct inode *inode);
|
|
static int dhara_close(FAR struct inode *inode);
|
|
static ssize_t dhara_read(FAR struct inode *inode,
|
|
FAR unsigned char *buffer,
|
|
blkcnt_t start_sector,
|
|
unsigned int nsectors);
|
|
static ssize_t dhara_write(FAR struct inode *inode,
|
|
FAR const unsigned char *buffer,
|
|
blkcnt_t start_sector,
|
|
unsigned int nsectors);
|
|
static int dhara_geometry(FAR struct inode *inode,
|
|
FAR struct geometry *geometry);
|
|
static int dhara_ioctl(FAR struct inode *inode,
|
|
int cmd,
|
|
unsigned long arg);
|
|
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
|
|
static int dhara_unlink(FAR struct inode *inode);
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Private Data
|
|
****************************************************************************/
|
|
|
|
static const struct block_operations g_dhara_bops =
|
|
{
|
|
dhara_open, /* open */
|
|
dhara_close, /* close */
|
|
dhara_read, /* read */
|
|
dhara_write, /* write */
|
|
dhara_geometry, /* geometry */
|
|
dhara_ioctl /* ioctl */
|
|
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
|
|
, dhara_unlink /* unlink */
|
|
#endif
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
static int dhara_convert_result(dhara_error_t err)
|
|
{
|
|
switch (err)
|
|
{
|
|
case DHARA_E_NONE:
|
|
return 0;
|
|
|
|
case DHARA_E_BAD_BLOCK:
|
|
case DHARA_E_ECC:
|
|
case DHARA_E_TOO_BAD:
|
|
case DHARA_E_CORRUPT_MAP:
|
|
return -EBADMSG;
|
|
|
|
case DHARA_E_JOURNAL_FULL:
|
|
case DHARA_E_MAP_FULL:
|
|
return -ENOSPC;
|
|
|
|
case DHARA_E_NOT_FOUND:
|
|
return -ENOENT;
|
|
|
|
default:
|
|
return -EFAULT;
|
|
}
|
|
}
|
|
|
|
static bool dhara_check_ff(FAR const uint8_t *buf, size_t size)
|
|
{
|
|
FAR const uint8_t *p = buf;
|
|
size_t len;
|
|
|
|
for (len = 0; len < 16; len++)
|
|
{
|
|
if (!size)
|
|
return true;
|
|
|
|
if (*p != 0xff)
|
|
return false;
|
|
|
|
p++;
|
|
size--;
|
|
}
|
|
|
|
return memcmp(buf, p, size) == 0;
|
|
}
|
|
|
|
static int dhara_init_readcache(FAR dhara_dev_t *dev)
|
|
{
|
|
FAR dq_queue_t *q = &dev->readcache;
|
|
int i = 0;
|
|
|
|
dq_init(q);
|
|
|
|
do
|
|
{
|
|
dev->readpage[i].page = DHARA_PAGE_NONE;
|
|
dev->readpage[i].buffer = kmm_malloc(dev->geo.blocksize);
|
|
dq_addfirst(&dev->readpage[i].node, q);
|
|
}
|
|
while (dev->readpage[i++].buffer && i < CONFIG_DHARA_READ_NCACHES);
|
|
|
|
return i == CONFIG_DHARA_READ_NCACHES ? 0 : -ENOMEM;
|
|
}
|
|
|
|
static void dhara_deinit_readcache(FAR dhara_dev_t *dev)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < CONFIG_DHARA_READ_NCACHES; i++)
|
|
{
|
|
if (dev->readpage[i].buffer)
|
|
{
|
|
kmm_free(dev->readpage[i].buffer);
|
|
}
|
|
}
|
|
}
|
|
|
|
static uint8_t *dhara_find_readcache(FAR dhara_dev_t *dev,
|
|
dhara_page_t page)
|
|
{
|
|
FAR dq_queue_t *q = &dev->readcache;
|
|
FAR dhara_pagecache_t *cache;
|
|
FAR dq_entry_t *c;
|
|
|
|
for (c = dq_peek(q); c; c = dq_next(c))
|
|
{
|
|
cache = (FAR dhara_pagecache_t *)c;
|
|
if (cache->page == page)
|
|
{
|
|
dq_rem(c, q);
|
|
dq_addfirst(c, q);
|
|
return cache->buffer;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static dhara_pagecache_t *dhara_grab_readcache(FAR dhara_dev_t *dev)
|
|
{
|
|
FAR dq_queue_t *q = &dev->readcache;
|
|
FAR dhara_pagecache_t *cache;
|
|
FAR dq_entry_t *c;
|
|
|
|
c = dq_tail(q);
|
|
dq_rem(c, q);
|
|
cache = (FAR dhara_pagecache_t *)c;
|
|
cache->page = DHARA_PAGE_NONE;
|
|
return cache;
|
|
}
|
|
|
|
static void dhara_insert_readcache(FAR dhara_dev_t *dev,
|
|
dhara_pagecache_t *cache)
|
|
{
|
|
FAR dq_queue_t *q = &dev->readcache;
|
|
|
|
if (cache->page != DHARA_PAGE_NONE)
|
|
dq_addfirst((dq_entry_t *)cache, q);
|
|
else
|
|
dq_addlast((dq_entry_t *)cache, q);
|
|
}
|
|
|
|
static void dhara_discard_readcache(FAR dhara_dev_t *dev,
|
|
dhara_page_t page)
|
|
{
|
|
FAR dhara_pagecache_t *cache;
|
|
FAR dq_queue_t *q = &dev->readcache;
|
|
FAR dq_entry_t *c;
|
|
|
|
for (c = dq_peek(q); c; c = dq_next(c))
|
|
{
|
|
cache = (FAR dhara_pagecache_t *)c;
|
|
if (cache->page == page)
|
|
{
|
|
cache->page = DHARA_PAGE_NONE;
|
|
dq_rem(c, q);
|
|
dq_addlast(c, q);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void dhara_update_readcache(FAR dhara_dev_t *dev,
|
|
dhara_page_t page,
|
|
FAR const uint8_t *data)
|
|
{
|
|
FAR dhara_pagecache_t *cache;
|
|
FAR dq_queue_t *q = &dev->readcache;
|
|
FAR dq_entry_t *c;
|
|
|
|
for (c = dq_peek(q); c; c = dq_next(c))
|
|
{
|
|
cache = (FAR dhara_pagecache_t *)c;
|
|
if (cache->page == page)
|
|
{
|
|
cache->page = page;
|
|
memcpy(cache->buffer, data, dev->geo.blocksize);
|
|
dq_rem(c, q);
|
|
dq_addfirst(c, q);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: dhara_open
|
|
*
|
|
* Description: Open the block device
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int dhara_open(FAR struct inode *inode)
|
|
{
|
|
FAR dhara_dev_t *dev;
|
|
|
|
DEBUGASSERT(inode->i_private);
|
|
dev = inode->i_private;
|
|
nxmutex_lock(&dev->lock);
|
|
dev->refs++;
|
|
nxmutex_unlock(&dev->lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: dhara_close
|
|
*
|
|
* Description: close the block device
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int dhara_close(FAR struct inode *inode)
|
|
{
|
|
FAR dhara_dev_t *dev;
|
|
|
|
DEBUGASSERT(inode->i_private);
|
|
dev = inode->i_private;
|
|
nxmutex_lock(&dev->lock);
|
|
dev->refs--;
|
|
nxmutex_unlock(&dev->lock);
|
|
|
|
if (dev->refs == 0 && dev->unlinked)
|
|
{
|
|
nxmutex_destroy(&dev->lock);
|
|
dhara_deinit_readcache(dev);
|
|
kmm_free(dev->pagebuf);
|
|
kmm_free(dev);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: dhara_read
|
|
*
|
|
* Description: Read the specified number of sectors
|
|
*
|
|
****************************************************************************/
|
|
|
|
static ssize_t dhara_read(FAR struct inode *inode,
|
|
FAR unsigned char *buffer,
|
|
blkcnt_t start_sector,
|
|
unsigned int nsectors)
|
|
{
|
|
FAR dhara_dev_t *dev;
|
|
size_t nread = 0;
|
|
int ret = 0;
|
|
|
|
DEBUGASSERT(inode->i_private);
|
|
dev = inode->i_private;
|
|
|
|
nxmutex_lock(&dev->lock);
|
|
while (nsectors-- > 0)
|
|
{
|
|
dhara_error_t err;
|
|
ret = dhara_map_read(&dev->map,
|
|
start_sector,
|
|
buffer,
|
|
&err);
|
|
if (ret < 0)
|
|
{
|
|
ret = dhara_convert_result(err);
|
|
ferr("Read startblock %lld failed nread %zd err: %s\n",
|
|
(long long)start_sector, nread, dhara_strerror(err));
|
|
break;
|
|
}
|
|
|
|
nread++;
|
|
start_sector++;
|
|
buffer += dev->geo.blocksize;
|
|
}
|
|
|
|
nxmutex_unlock(&dev->lock);
|
|
return nread ? nread : ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: dhara_write
|
|
*
|
|
* Description: Write (or buffer) the specified number of sectors
|
|
*
|
|
****************************************************************************/
|
|
|
|
static ssize_t dhara_write(FAR struct inode *inode,
|
|
FAR const unsigned char *buffer,
|
|
blkcnt_t start_sector,
|
|
unsigned int nsectors)
|
|
{
|
|
FAR dhara_dev_t *dev;
|
|
size_t nwrite = 0;
|
|
int ret = 0;
|
|
|
|
DEBUGASSERT(inode->i_private);
|
|
dev = inode->i_private;
|
|
|
|
nxmutex_lock(&dev->lock);
|
|
while (nsectors-- > 0)
|
|
{
|
|
dhara_error_t err;
|
|
ret = dhara_map_write(&dev->map,
|
|
start_sector,
|
|
buffer,
|
|
&err);
|
|
if (ret < 0)
|
|
{
|
|
ret = dhara_convert_result(err);
|
|
ferr("Write starting at block %lld failed nwrite %zu err %s\n",
|
|
(long long)start_sector, nwrite, dhara_strerror(err));
|
|
break;
|
|
}
|
|
|
|
nwrite++;
|
|
start_sector++;
|
|
buffer += dev->geo.blocksize;
|
|
}
|
|
|
|
nxmutex_unlock(&dev->lock);
|
|
return nwrite ? nwrite : ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: dhara_geometry
|
|
*
|
|
* Description: Return device geometry
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int dhara_geometry(FAR struct inode *inode,
|
|
FAR struct geometry *geometry)
|
|
{
|
|
FAR dhara_dev_t *dev;
|
|
|
|
DEBUGASSERT(inode->i_private);
|
|
dev = inode->i_private;
|
|
|
|
if (geometry)
|
|
{
|
|
geometry->geo_available = true;
|
|
geometry->geo_mediachanged = false;
|
|
geometry->geo_writeenabled = true;
|
|
geometry->geo_nsectors = dev->geo.neraseblocks * dev->blkper;
|
|
geometry->geo_sectorsize = dev->geo.blocksize;
|
|
|
|
strcpy(geometry->geo_model, dev->geo.model);
|
|
nxmutex_unlock(&dev->lock);
|
|
return 0;
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: dhara_ioctl
|
|
*
|
|
* Description: Return device geometry
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int dhara_ioctl(FAR struct inode *inode,
|
|
int cmd,
|
|
unsigned long arg)
|
|
{
|
|
FAR dhara_dev_t *dev;
|
|
int ret;
|
|
|
|
DEBUGASSERT(inode->i_private);
|
|
dev = inode->i_private;
|
|
|
|
/* No other block driver ioctl commands are not recognized by this
|
|
* driver. Other possible MTD driver ioctl commands are passed through
|
|
* to the MTD driver (unchanged).
|
|
*/
|
|
|
|
ret = MTD_IOCTL(dev->mtd, cmd, arg);
|
|
if (ret < 0 && ret != -ENOTTY)
|
|
{
|
|
ferr("MTD ioctl(%04x) failed: %d\n", cmd, ret);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: dhara_unlink
|
|
*
|
|
* Description: Unlink the device
|
|
*
|
|
****************************************************************************/
|
|
|
|
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
|
|
static int dhara_unlink(FAR struct inode *inode)
|
|
{
|
|
FAR dhara_dev_t *dev;
|
|
|
|
DEBUGASSERT(inode->i_private);
|
|
dev = inode->i_private;
|
|
nxmutex_lock(&dev->lock);
|
|
dev->unlinked = true;
|
|
nxmutex_unlock(&dev->lock);
|
|
|
|
if (dev->refs == 0)
|
|
{
|
|
nxmutex_destroy(&dev->lock);
|
|
dhara_deinit_readcache(dev);
|
|
kmm_free(dev->pagebuf);
|
|
kmm_free(dev);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/* dhara nand interface implement */
|
|
|
|
int dhara_nand_is_bad(FAR const struct dhara_nand *n,
|
|
dhara_block_t bno)
|
|
{
|
|
FAR dhara_dev_t *dev = (FAR dhara_dev_t *)n;
|
|
return MTD_ISBAD(dev->mtd, bno);
|
|
}
|
|
|
|
void dhara_nand_mark_bad(FAR const struct dhara_nand *n,
|
|
dhara_block_t bno)
|
|
{
|
|
FAR dhara_dev_t *dev = (FAR dhara_dev_t *)n;
|
|
MTD_MARKBAD(dev->mtd, bno);
|
|
}
|
|
|
|
int dhara_nand_erase(FAR const struct dhara_nand *n,
|
|
dhara_block_t bno,
|
|
FAR dhara_error_t *err)
|
|
{
|
|
FAR dhara_dev_t *dev = (FAR dhara_dev_t *)n;
|
|
dhara_page_t pno = bno << n->log2_ppb;
|
|
int ret;
|
|
int i;
|
|
|
|
ret = MTD_ERASE(dev->mtd, bno, 1);
|
|
if (ret < 0)
|
|
{
|
|
dhara_set_error(err, DHARA_E_BAD_BLOCK);
|
|
return ret;
|
|
}
|
|
|
|
for (i = 0; i < dev->blkper; i++)
|
|
{
|
|
dhara_discard_readcache(dev, pno + i);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int dhara_nand_prog(FAR const struct dhara_nand *n,
|
|
dhara_page_t p,
|
|
FAR const uint8_t *data,
|
|
FAR dhara_error_t *err)
|
|
{
|
|
FAR dhara_dev_t *dev = (FAR dhara_dev_t *)n;
|
|
int ret;
|
|
|
|
ret = MTD_BWRITE(dev->mtd, p, 1, data);
|
|
if (ret < 0)
|
|
{
|
|
dhara_set_error(err, DHARA_E_BAD_BLOCK);
|
|
return ret;
|
|
}
|
|
|
|
dhara_update_readcache(dev, p, data);
|
|
return 0;
|
|
}
|
|
|
|
int dhara_nand_is_free(FAR const struct dhara_nand *n,
|
|
dhara_page_t p)
|
|
{
|
|
FAR dhara_dev_t *dev = (FAR dhara_dev_t *)n;
|
|
size_t page_size = 1 << n->log2_page_size;
|
|
FAR uint8_t *buf = dev->pagebuf;
|
|
dhara_error_t err;
|
|
|
|
if (dhara_nand_read(n, p, 0, page_size, buf, &err) < 0)
|
|
{
|
|
ferr("Fail to read page for free check err %s\n",
|
|
dhara_strerror(err));
|
|
return 0;
|
|
}
|
|
|
|
return dhara_check_ff(buf, page_size);
|
|
}
|
|
|
|
int dhara_nand_read(FAR const struct dhara_nand *n,
|
|
dhara_page_t p,
|
|
size_t offset,
|
|
size_t length,
|
|
FAR uint8_t *data,
|
|
dhara_error_t *err)
|
|
{
|
|
FAR dhara_dev_t *dev = (FAR dhara_dev_t *)n;
|
|
FAR dhara_pagecache_t *cache;
|
|
FAR uint8_t *buf;
|
|
int ret;
|
|
|
|
buf = dhara_find_readcache(dev, p);
|
|
if (buf)
|
|
{
|
|
memcpy(data, buf + offset, length);
|
|
return 0;
|
|
}
|
|
|
|
cache = dhara_grab_readcache(dev);
|
|
ret = MTD_BREAD(dev->mtd, p, 1, cache->buffer);
|
|
if (ret == -EUCLEAN)
|
|
{
|
|
ret = 0; /* Ignore the correctable ECC error */
|
|
}
|
|
|
|
if (ret < 0)
|
|
{
|
|
dhara_insert_readcache(dev, cache);
|
|
dhara_set_error(err, DHARA_E_ECC);
|
|
return ret;
|
|
}
|
|
|
|
memcpy(data, cache->buffer + offset, length);
|
|
cache->page = p;
|
|
dhara_insert_readcache(dev, cache);
|
|
return ret;
|
|
}
|
|
|
|
int dhara_nand_copy(FAR const struct dhara_nand *n,
|
|
dhara_page_t src,
|
|
dhara_page_t dst,
|
|
FAR dhara_error_t *err)
|
|
{
|
|
FAR dhara_dev_t *dev = (FAR dhara_dev_t *)n;
|
|
size_t page_size = 1 << n->log2_page_size;
|
|
FAR uint8_t *buf = dev->pagebuf;
|
|
int ret;
|
|
|
|
ret = dhara_nand_read(n, src, 0, page_size, buf, err);
|
|
if (ret < 0)
|
|
{
|
|
ferr("src_page %d, ret %d failed: %s\n",
|
|
src, ret, dhara_strerror(*err));
|
|
return ret;
|
|
}
|
|
|
|
ret = dhara_nand_prog(n, dst, buf, err);
|
|
if (ret < 0)
|
|
{
|
|
ferr("dst_page %d, ret %d failed: %s\n",
|
|
dst, ret, dhara_strerror(*err));
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: dhara_initialize_by_path
|
|
*
|
|
* Description:
|
|
* Initialize to provide a block driver wrapper around an MTD interface
|
|
*
|
|
* Input Parameters:
|
|
* path - The block device path.
|
|
* mtd - The MTD device that supports the FLASH interface.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int dhara_initialize_by_path(FAR const char *path,
|
|
FAR struct mtd_dev_s *mtd)
|
|
{
|
|
FAR dhara_dev_t *dev;
|
|
int ret;
|
|
|
|
/* Sanity check */
|
|
|
|
if (path == NULL || mtd == NULL)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Allocate a DHARA_MTDBLOCK device structure */
|
|
|
|
dev = (FAR dhara_dev_t *)
|
|
kmm_zalloc(sizeof(dhara_dev_t));
|
|
if (dev == NULL)
|
|
{
|
|
return -ENOMEM;
|
|
}
|
|
|
|
nxmutex_init(&dev->lock);
|
|
|
|
/* Initialize the DHARA_MTDBLOCK device structure */
|
|
|
|
dev->mtd = mtd;
|
|
|
|
/* Get the device geometry. (casting to uintptr_t first
|
|
* eliminates complaints on some architectures where the
|
|
* sizeof long is different
|
|
* from the size of a pointer).
|
|
*/
|
|
|
|
ret = MTD_IOCTL(mtd,
|
|
MTDIOC_GEOMETRY,
|
|
(unsigned long)((uintptr_t)&dev->geo));
|
|
if (ret < 0)
|
|
{
|
|
ferr("MTD ioctl(MTDIOC_GEOMETRY) failed: %d\n", ret);
|
|
goto err;
|
|
}
|
|
|
|
/* Get the number of R/W blocks per erase block */
|
|
|
|
dev->blkper = dev->geo.erasesize / dev->geo.blocksize;
|
|
DEBUGASSERT(dev->blkper * dev->geo.blocksize == dev->geo.erasesize);
|
|
|
|
/* Init dhara */
|
|
|
|
dev->nand.log2_page_size = fls(dev->geo.blocksize) - 1;
|
|
dev->nand.log2_ppb = fls(dev->blkper) - 1;
|
|
dev->nand.num_blocks = dev->geo.neraseblocks;
|
|
|
|
dev->pagebuf = kmm_zalloc(dev->geo.blocksize * 2);
|
|
if (!dev->pagebuf)
|
|
{
|
|
ret = -ENOMEM;
|
|
goto err;
|
|
}
|
|
|
|
ret = dhara_init_readcache(dev);
|
|
if (ret != 0)
|
|
{
|
|
goto err;
|
|
}
|
|
|
|
dhara_map_init(&dev->map, &dev->nand,
|
|
dev->pagebuf + dev->geo.blocksize,
|
|
CONFIG_DHARA_GC_RATIO);
|
|
|
|
dhara_map_resume(&dev->map, NULL);
|
|
|
|
/* Inode private data is a reference to the
|
|
* DHARA_MTDBLOCK device structure
|
|
*/
|
|
|
|
ret = register_blockdriver(path, &g_dhara_bops, 0600, dev);
|
|
if (ret < 0)
|
|
{
|
|
ferr("register_blockdriver failed: %d\n", ret);
|
|
goto err;
|
|
}
|
|
|
|
return ret;
|
|
|
|
err:
|
|
nxmutex_destroy(&dev->lock);
|
|
dhara_deinit_readcache(dev);
|
|
kmm_free(dev->pagebuf);
|
|
kmm_free(dev);
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: dhara_initialize
|
|
*
|
|
* Description:
|
|
* Initialize to provide a block driver wrapper around an MTD interface
|
|
*
|
|
* Input Parameters:
|
|
* minor - The minor device number. The MTD block device will be
|
|
* registered as as /dev/mtdblockN where N is the minor number.
|
|
* mtd - The MTD device that supports the FLASH interface.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int dhara_initialize(int minor, FAR struct mtd_dev_s *mtd)
|
|
{
|
|
FAR char *path;
|
|
int ret;
|
|
|
|
#ifdef CONFIG_DEBUG_FEATURES
|
|
/* Sanity check */
|
|
|
|
if (minor < 0 || minor > 255)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
#endif
|
|
|
|
path = lib_get_pathbuffer();
|
|
if (path == NULL)
|
|
{
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/* Do the real work by dhara_mtdblock_initialize_by_path */
|
|
|
|
snprintf(path, PATH_MAX, "/dev/mtdblock%d", minor);
|
|
ret = dhara_initialize_by_path(path, mtd);
|
|
lib_put_pathbuffer(path);
|
|
return ret;
|
|
}
|