bsps/shared/dev/flash: Add a flashdev simulation backend

This adds a backend for flashdev that simulates a flash device with
adjustable delays to simulate real flash devices and offers
configurable flash parameters. This also migrates the JFFS2
filesystem tests to the flashdev framework and eliminates the
purpose-built simulated flash in the testsuite in favor of a more
generic and widely usable solution.
This commit is contained in:
Kinsey Moore
2025-09-04 17:26:23 -05:00
parent 710a6e124e
commit 20faa90860
6 changed files with 701 additions and 284 deletions

View File

@@ -36,213 +36,77 @@
#include <rtems/jffs2.h>
#include <rtems/libio.h>
#include <rtems/libcsupport.h>
#include "fstest.h"
#include "fstest_support.h"
#include <dev/flash/flash_sim_flashdev.h>
#include <dev/flash/jffs2_flashdev.h>
#define FLASH_PAGE_SIZE (1024UL)
#define FLASH_PAGE_SIZE ( 1024UL )
/* Out of Band/Spare area size is per-page */
#define FLASH_PAGE_OOB_SIZE (32UL)
#define FLASH_PAGE_OOB_SIZE ( 32UL )
#define PAGES_PER_BLOCK (16UL)
#define BLOCKS_PER_DEVICE (8UL)
#define PAGES_PER_BLOCK ( 16UL )
#define BLOCKS_PER_DEVICE ( 8UL )
#define FLASH_OOB_SIZE (BLOCKS_PER_DEVICE * PAGES_PER_BLOCK * FLASH_PAGE_OOB_SIZE)
#define FLASH_BLOCK_SIZE (PAGES_PER_BLOCK * FLASH_PAGE_SIZE)
#define FLASH_SIZE (BLOCKS_PER_DEVICE * FLASH_BLOCK_SIZE)
typedef struct {
rtems_jffs2_flash_control super;
unsigned char area[FLASH_SIZE];
unsigned char oob[FLASH_OOB_SIZE];
} flash_control;
static flash_control *get_flash_control(rtems_jffs2_flash_control *super)
{
return (flash_control *) super;
}
static int flash_read(
rtems_jffs2_flash_control *super,
uint32_t offset,
unsigned char *buffer,
size_t size_of_buffer
)
{
flash_control *self = get_flash_control(super);
unsigned char *chunk = &self->area[offset];
memcpy(buffer, chunk, size_of_buffer);
return 0;
}
static int flash_write(
rtems_jffs2_flash_control *super,
uint32_t offset,
const unsigned char *buffer,
size_t size_of_buffer
)
{
flash_control *self = get_flash_control(super);
unsigned char *chunk = &self->area[offset];
size_t i;
for (i = 0; i < size_of_buffer; ++i) {
chunk[i] &= buffer[i];
}
return 0;
}
static int flash_erase(
rtems_jffs2_flash_control *super,
uint32_t offset
)
{
flash_control *self = get_flash_control(super);
uint32_t page_index = offset / FLASH_PAGE_SIZE;
uint32_t oob_offset = page_index * FLASH_PAGE_OOB_SIZE;
unsigned char *chunk = &self->area[offset];
unsigned char *oobchunk = &self->oob[oob_offset];
memset(chunk, 0xff, FLASH_BLOCK_SIZE);
memset(oobchunk, 0xff, PAGES_PER_BLOCK * FLASH_PAGE_OOB_SIZE);
return 0;
}
static int flash_read_oob(
rtems_jffs2_flash_control *super,
uint32_t offset,
uint8_t *buffer,
uint32_t size_of_buffer
)
{
flash_control *self = get_flash_control(super);
uint32_t page_index = offset / FLASH_PAGE_SIZE;
uint32_t oob_offset = page_index * FLASH_PAGE_OOB_SIZE;
unsigned char *chunk = &self->oob[oob_offset];
memcpy(buffer, chunk, size_of_buffer);
return 0;
}
static int flash_write_oob(
rtems_jffs2_flash_control *super,
uint32_t offset,
uint8_t *buffer,
uint32_t size_of_buffer
)
{
flash_control *self = get_flash_control(super);
uint32_t page_index = offset / FLASH_PAGE_SIZE;
uint32_t oob_offset = page_index * FLASH_PAGE_OOB_SIZE;
unsigned char *chunk = &self->oob[oob_offset];
size_t i;
for (i = 0; i < size_of_buffer; ++i) {
chunk[i] &= buffer[i];
}
return 0;
}
static int flash_block_is_bad(
rtems_jffs2_flash_control *super,
uint32_t orig_offset,
bool *bad
)
{
(void) super;
(void) orig_offset;
*bad = false;
return 0;
}
static int flash_block_mark_bad(
rtems_jffs2_flash_control *super,
uint32_t orig_offset
)
{
(void) super;
(void) orig_offset;
return 0;
}
static uint32_t flash_get_oob_size(
rtems_jffs2_flash_control *super
)
{
(void) super;
return FLASH_PAGE_OOB_SIZE;
}
static flash_control flash_instance = {
.super = {
.block_size = FLASH_BLOCK_SIZE,
.flash_size = FLASH_SIZE,
.read = flash_read,
.write = flash_write,
.erase = flash_erase,
.block_is_bad = flash_block_is_bad,
.block_mark_bad = flash_block_mark_bad,
.oob_read = flash_read_oob,
.oob_write = flash_write_oob,
.get_oob_size = flash_get_oob_size,
.write_size = FLASH_PAGE_SIZE
}
};
#define FLASHDEV_PATH "dev/flashdev0"
static rtems_jffs2_compressor_control compressor_instance = {
.compress = rtems_jffs2_compressor_rtime_compress,
.decompress = rtems_jffs2_compressor_rtime_decompress
};
static const rtems_jffs2_mount_data mount_data = {
.flash_control = &flash_instance.super,
.compressor_control = &compressor_instance
};
static rtems_flashdev *flash = NULL;
static void erase_all(void)
{
memset(&flash_instance.area[0], 0xff, FLASH_SIZE);
memset(&flash_instance.oob[0], 0xff, FLASH_OOB_SIZE);
}
static rtems_resource_snapshot before_mount;
void test_initialize_filesystem(void)
void test_initialize_filesystem( void )
{
int rv;
int status;
rtems_status_code sc;
rtems_flashdev_region region;
struct flash_sim_flashdev_attributes attr;
erase_all();
attr.read_delay_ns = 0;
attr.write_delay_ns = 0;
attr.erase_delay_ns = 0;
attr.page_size_bytes = FLASH_PAGE_SIZE;
attr.page_oob_bytes = FLASH_PAGE_OOB_SIZE;
attr.pages_per_sector = PAGES_PER_BLOCK;
attr.total_sectors = BLOCKS_PER_DEVICE;
attr.type = RTEMS_FLASHDEV_NAND;
attr.alloc = NULL;
attr.free = NULL;
rv = mkdir(BASE_FOR_TEST, S_IRWXU | S_IRWXG | S_IRWXO);
rtems_test_assert(rv == 0);
flash = flash_sim_flashdev_init( &attr );
rtems_test_assert( flash != NULL );
rtems_resource_snapshot_take(&before_mount);
/* Register the flashdev as a device */
status = rtems_flashdev_register( flash, FLASHDEV_PATH );
rtems_test_assert( !status );
rv = mount(
NULL,
rv = mkdir( BASE_FOR_TEST, S_IRWXU | S_IRWXG | S_IRWXO );
rtems_test_assert( rv == 0 );
region.offset = 0;
region.size = FLASH_PAGE_SIZE * PAGES_PER_BLOCK * BLOCKS_PER_DEVICE;
sc = jffs2_flashdev_mount(
FLASHDEV_PATH,
BASE_FOR_TEST,
RTEMS_FILESYSTEM_TYPE_JFFS2,
RTEMS_FILESYSTEM_READ_WRITE,
&mount_data
&region,
&compressor_instance,
false
);
rtems_test_assert(rv == 0);
rtems_test_assert( sc == RTEMS_SUCCESSFUL );
}
void test_shutdown_filesystem(void)
void test_shutdown_filesystem( void )
{
int rv = unmount(BASE_FOR_TEST);
rtems_test_assert(rv == 0);
rtems_test_assert(rtems_resource_snapshot_check(&before_mount));
int rv = unmount( BASE_FOR_TEST );
rtems_test_assert( rv == 0 );
rv = rtems_flashdev_unregister( FLASHDEV_PATH );
rtems_test_assert( rv == 0 );
}
#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER

View File

@@ -30,130 +30,79 @@
#endif
#include <tmacros.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <string.h>
#include <dev/flash/flash_sim_flashdev.h>
#include <dev/flash/jffs2_flashdev.h>
#include <rtems/jffs2.h>
#include <rtems/libio.h>
#include <rtems/libcsupport.h>
#include "fstest.h"
#include "fstest_support.h"
#define BLOCK_SIZE (16UL * 1024UL)
#define FLASH_PAGE_SIZE ( 1024UL )
#define PAGES_PER_SECTOR ( 16UL )
#define SECTORS_PER_DEVICE ( 8UL )
#define FLASH_SIZE (8UL * BLOCK_SIZE)
typedef struct {
rtems_jffs2_flash_control super;
unsigned char area[FLASH_SIZE];
} flash_control;
static flash_control *get_flash_control(rtems_jffs2_flash_control *super)
{
return (flash_control *) super;
}
static int flash_read(
rtems_jffs2_flash_control *super,
uint32_t offset,
unsigned char *buffer,
size_t size_of_buffer
)
{
flash_control *self = get_flash_control(super);
unsigned char *chunk = &self->area[offset];
memcpy(buffer, chunk, size_of_buffer);
return 0;
}
static int flash_write(
rtems_jffs2_flash_control *super,
uint32_t offset,
const unsigned char *buffer,
size_t size_of_buffer
)
{
flash_control *self = get_flash_control(super);
unsigned char *chunk = &self->area[offset];
size_t i;
for (i = 0; i < size_of_buffer; ++i) {
chunk[i] &= buffer[i];
}
return 0;
}
static int flash_erase(
rtems_jffs2_flash_control *super,
uint32_t offset
)
{
flash_control *self = get_flash_control(super);
unsigned char *chunk = &self->area[offset];
memset(chunk, 0xff, BLOCK_SIZE);
return 0;
}
static flash_control flash_instance = {
.super = {
.block_size = BLOCK_SIZE,
.flash_size = FLASH_SIZE,
.read = flash_read,
.write = flash_write,
.erase = flash_erase
}
};
#define FLASHDEV_PATH "dev/flashdev0"
static rtems_jffs2_compressor_control compressor_instance = {
.compress = rtems_jffs2_compressor_rtime_compress,
.decompress = rtems_jffs2_compressor_rtime_decompress
};
static const rtems_jffs2_mount_data mount_data = {
.flash_control = &flash_instance.super,
.compressor_control = &compressor_instance
};
static rtems_flashdev *flash = NULL;
static void erase_all(void)
{
memset(&flash_instance.area[0], 0xff, FLASH_SIZE);
}
static rtems_resource_snapshot before_mount;
void test_initialize_filesystem(void)
void test_initialize_filesystem( void )
{
int rv;
int status;
rtems_status_code sc;
rtems_flashdev_region region;
struct flash_sim_flashdev_attributes attr;
erase_all();
attr.read_delay_ns = 0;
attr.write_delay_ns = 0;
attr.erase_delay_ns = 0;
attr.page_size_bytes = FLASH_PAGE_SIZE;
attr.pages_per_sector = PAGES_PER_SECTOR;
attr.total_sectors = SECTORS_PER_DEVICE;
attr.type = RTEMS_FLASHDEV_NOR;
attr.alloc = NULL;
attr.free = NULL;
rv = mkdir(BASE_FOR_TEST, S_IRWXU | S_IRWXG | S_IRWXO);
rtems_test_assert(rv == 0);
flash = flash_sim_flashdev_init( &attr );
rtems_test_assert( flash != NULL );
rtems_resource_snapshot_take(&before_mount);
/* Register the flashdev as a device */
status = rtems_flashdev_register( flash, FLASHDEV_PATH );
rtems_test_assert( !status );
rv = mount(
NULL,
rv = mkdir( BASE_FOR_TEST, S_IRWXU | S_IRWXG | S_IRWXO );
rtems_test_assert( rv == 0 );
region.offset = 0;
region.size = FLASH_PAGE_SIZE * PAGES_PER_SECTOR * SECTORS_PER_DEVICE;
sc = jffs2_flashdev_mount(
FLASHDEV_PATH,
BASE_FOR_TEST,
RTEMS_FILESYSTEM_TYPE_JFFS2,
RTEMS_FILESYSTEM_READ_WRITE,
&mount_data
&region,
&compressor_instance,
false
);
rtems_test_assert(rv == 0);
rtems_test_assert( sc == RTEMS_SUCCESSFUL );
}
void test_shutdown_filesystem(void)
void test_shutdown_filesystem( void )
{
int rv = unmount(BASE_FOR_TEST);
rtems_test_assert(rv == 0);
rtems_test_assert(rtems_resource_snapshot_check(&before_mount));
int rv = unmount( BASE_FOR_TEST );
rtems_test_assert( rv == 0 );
rv = rtems_flashdev_unregister( FLASHDEV_PATH );
rtems_test_assert( rv == 0 );
}
#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER

View File

@@ -29,25 +29,24 @@
#include "test_flashdev.h"
#include <stdio.h>
#include <stdlib.h>
#include <dev/flash/flash_sim_flashdev.h>
#include <fcntl.h>
#include <rtems/libcsupport.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#define TEST_NAME_LENGTH 10
#define TEST_DATA_SIZE (PAGE_SIZE * PAGE_COUNT)
#define TEST_DATA_SIZE ( PAGE_SIZE * PAGE_COUNT )
#define PAGE_COUNT 16
#define PAGE_SIZE 128
#define SECTOR_COUNT 4
#define SECTOR_SIZE (TEST_DATA_SIZE / SECTOR_COUNT)
#define SECTOR_SIZE ( TEST_DATA_SIZE / SECTOR_COUNT )
#define WB_SIZE 1
const char rtems_test_name[] = "FLASHDEV 1";
static void run_test(void);
static void run_test(void) {
char buff[TEST_DATA_SIZE] = {0};
@@ -219,6 +218,47 @@ static void run_test(void) {
rtems_test_assert(!status);
}
static void run_flash_sim_test( void )
{
FILE *file;
rtems_flashdev* flash;
int status;
rtems_resource_snapshot snapshot;
/* Check resource usage on creation and deletion */
rtems_resource_snapshot_take( &snapshot );
flash = flash_sim_flashdev_init( NULL );
rtems_test_assert( flash != NULL );
rtems_flashdev_destroy( flash );
flash = NULL;
rtems_test_assert( rtems_resource_snapshot_check( &snapshot ) );
/* Initialize the flash device driver and flashdev */
rtems_resource_snapshot_take( &snapshot );
flash = flash_sim_flashdev_init( NULL );
rtems_test_assert( flash != NULL );
/* Register the flashdev as a device */
status = rtems_flashdev_register( flash, "dev/flashdev0" );
rtems_test_assert( !status );
/* Open the flashdev */
file = fopen( "dev/flashdev0", "r+" );
rtems_test_assert( file != NULL );
status = fclose( file );
rtems_test_assert( !status );
status = rtems_flashdev_unregister( "dev/flashdev0" );
rtems_test_assert( !status );
rtems_test_assert( rtems_resource_snapshot_check( &snapshot ) );
}
static void Init(rtems_task_argument arg)
{
(void) arg;
@@ -226,6 +266,7 @@ static void Init(rtems_task_argument arg)
TEST_BEGIN();
run_test();
run_flash_sim_test();
TEST_END();
rtems_test_exit(0);