Hi all, In this post, we’ll look forward to setting up flash file systems namely JFFS2 and YAFFS2 on RTEMS with flash simulated on a RAM-disk. For file system benchmarking purposes, we didn’t actually use them on a real flash(as to eliminate the effect of flash driver’s performance) but implementing them on a real flash won’t be very different from the process given in the later sections of this post. So, let’s begin with JFFS2 first:
JFFS2 on RTEMS
First reference you will see is of testsuites/fstests/jffs2_support/fs_support.c . Configuration here works up to a large extent unless the size of FLASH(configured via #define FLASH_SIZE (8UL * BLOCK_SIZE) ) increases beyond a certain limit.
typedef struct { rtems_jffs2_flash_control super; unsigned char area[FLASH_SIZE]; } flash_control;
Notice that in the above structure, the size of array statistically increases as FLASH_SIZE increases. Due to this, the final size of the binary also increases, and thus when loading it via u-boot, one can have the following error:
## Booting kernel from Legacy Image at 81500000 ... Image Name: RTEMS Created: 2018-08-05 3:48:17 UTC Image Type: ARM Linux Kernel Image (gzip compressed) Data Size: 1119071 Bytes = 1.1 MiB Load Address: 80000000 Entry Point: 80000000 Verifying Checksum ... OK ## Flattened Device Tree blob at 88000000 Booting using the fdt blob at 0x88000000 Uncompressing Kernel Image ... Error: inflate() returned -5 Image too large: increase CONFIG_SYS_BOOTM_LEN Must RESET board to recover
In order to avoid it we can dynamically allocate the `area` array-like with :
flash_instance.area = (char *)malloc(FLASH_SIZE + 1024);
Here, 1k extra space is allocated to compensate for the start alignment issues. So, below is my jffs2_support/fs_support.c file after modification:
/* * Copyright (c) 2013 embedded brains GmbH. All rights reserved. * * embedded brains GmbH * Dornierstr. 4 * 82178 Puchheim * Germany * <info@embedded-brains.de> * * The license and distribution terms for this file may be * found in the file LICENSE in this distribution or at * http://www.rtems.org/license/LICENSE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "pmacros.h" #include <sys/stat.h> #include <sys/types.h> #include <string.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_SIZE (8UL * BLOCK_SIZE) typedef struct { rtems_jffs2_flash_control super; unsigned char *area; } 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 } }; 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 void erase_all(void) { memset(&flash_instance.area[0], 0xff, FLASH_SIZE); } static rtems_resource_snapshot before_mount; void test_initialize_filesystem(void) { int rv; flash_instance.area = (char *)malloc(FLASH_SIZE + 1024); erase_all(); rv = mkdir(BASE_FOR_TEST, S_IRWXU | S_IRWXG | S_IRWXO); rtems_test_assert(rv == 0); rtems_resource_snapshot_take(&before_mount); rv = mount( NULL, BASE_FOR_TEST, RTEMS_FILESYSTEM_TYPE_JFFS2, RTEMS_FILESYSTEM_READ_WRITE, &mount_data ); rtems_test_assert(rv == 0); } 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)); } #define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER #define CONFIGURE_APPLICATION_NEEDS_SIMPLE_CONSOLE_DRIVER #define CONFIGURE_FILESYSTEM_JFFS2 #define CONFIGURE_LIBIO_MAXIMUM_FILE_DESCRIPTORS 40 #define CONFIGURE_MAXIMUM_TASKS 1 #define CONFIGURE_INIT_TASK_STACK_SIZE (32 * 1024) #define CONFIGURE_INIT_TASK_ATTRIBUTES RTEMS_FLOATING_POINT #define CONFIGURE_INITIAL_EXTENSIONS RTEMS_TEST_INITIAL_EXTENSION #define CONFIGURE_MAXIMUM_POSIX_KEY_VALUE_PAIRS 1 #define CONFIGURE_RTEMS_INIT_TASKS_TABLE #define CONFIGURE_INIT #include <rtems/confdefs.h>
Here, notice that flash_write(), flash_read, and flash_erase routines, which can be altered in case of reading/writing on an actual flash.
Setting up YAFFS2 on RTEMS
YAFFS configuration for RTEMS is a bit involved as it’s source files are kept in a separate repository https://github.com/m-labs/rtems-yaffs2
In order to use this port with RTEMS, we have to first build and install this library using the RTEMS toolchain. So, the first step will be:
- Setup an RTEMS toolchain along with the target BSP package: This step is pretty straight forward for anyone familiar with RTEMS. You just have to use RSB and RTEMS to have the BSP(let’s say, BeagleBone Black).
- Clone the YAFFS port, build, install it: Grab the port and then setup the RTEMS_MAKEFILE_PATH variable like:
export RTEMS_MAKEFILE_PATH=$sandbox/5/arm-rtems5/beagleboneblack
where `$sandbox/5` is your toolchain install path. then build the port using Makefile.rtems
cd rtems-yaffs2 make -f Makefile.rtems install
After this step, we have the YAFFS library ready to be used. This can be used by using `-lyaffs2` the flag in the linker call.
The next step is to write an RTEMS configuration to use YAFFS2. Here’s the configuration( Christian Mauderer’s work) that can be used:
/* * Copyright (c) 2017 Christian Mauderer <oss@c-mauderer.de> * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <unistd.h> #include <assert.h> #include <rtems.h> #include <rtems/console.h> #include <rtems/shell.h> #include "yaffs2.h" #define RTEMS_FILESYSTEM_TYPE_YAFFS "yaffs" #define MOUNT_PATH "/mnt" static void Init(rtems_task_argument arg) { rtems_status_code sc; int rv; (void)arg; rv = mount_and_make_target_path( NULL, MOUNT_PATH, RTEMS_FILESYSTEM_TYPE_YAFFS, RTEMS_FILESYSTEM_READ_WRITE, NULL ); assert(rv == 0); sc = rtems_shell_init( "SHLL", 32*1024, RTEMS_MAXIMUM_PRIORITY - 10, CONSOLE_DEVICE_NAME, false, true, NULL ); assert(sc == RTEMS_SUCCESSFUL); } /* * Configure RTEMS. */ #define CONFIGURE_MICROSECONDS_PER_TICK 1000 #define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER #define CONFIGURE_APPLICATION_NEEDS_CONSOLE_DRIVER #define CONFIGURE_APPLICATION_NEEDS_STUB_DRIVER #define CONFIGURE_APPLICATION_NEEDS_ZERO_DRIVER #define CONFIGURE_APPLICATION_NEEDS_LIBBLOCK #define CONFIGURE_FILESYSTEM_IMFS #define CONFIGURE_USE_IMFS_AS_BASE_FILESYSTEM #define CONFIGURE_HAS_OWN_FILESYSTEM_TABLE #include <rtems/imfs.h> const rtems_filesystem_table_t rtems_filesystem_table[] = { { "/", IMFS_initialize_support }, { RTEMS_FILESYSTEM_TYPE_IMFS, IMFS_initialize }, { RTEMS_FILESYSTEM_TYPE_YAFFS, yaffs_initialize }, { NULL, NULL } }; #define CONFIGURE_LIBIO_MAXIMUM_FILE_DESCRIPTORS 32 #define CONFIGURE_UNLIMITED_OBJECTS #define CONFIGURE_UNIFIED_WORK_AREAS #define CONFIGURE_MAXIMUM_USER_EXTENSIONS 1 #define CONFIGURE_INIT_TASK_STACK_SIZE (64*1024) #define CONFIGURE_INIT_TASK_INITIAL_MODES RTEMS_DEFAULT_MODES #define CONFIGURE_INIT_TASK_ATTRIBUTES RTEMS_FLOATING_POINT #define CONFIGURE_BDBUF_BUFFER_MAX_SIZE (32 * 1024) #define CONFIGURE_BDBUF_MAX_READ_AHEAD_BLOCKS 4 #define CONFIGURE_BDBUF_CACHE_MEMORY_SIZE (1 * 1024 * 1024) #define CONFIGURE_BDBUF_READ_AHEAD_TASK_PRIORITY 97 #define CONFIGURE_SWAPOUT_TASK_PRIORITY 97 //#define CONFIGURE_STACK_CHECKER_ENABLED #define CONFIGURE_RTEMS_INIT_TASKS_TABLE #define CONFIGURE_INIT #include <rtems/confdefs.h> /* * Configure Shell. */ #include <rtems/netcmds-config.h> #include <bsp/irq-info.h> #define CONFIGURE_SHELL_COMMANDS_INIT #define CONFIGURE_SHELL_COMMANDS_ALL #include <rtems/shellconfig.h>
and few supporting file:
/* * Based on code from Flickernoise * Copyright (C) 2010, 2011 Sebastien Bourdeauducq * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include <assert.h> #include <rtems.h> #include <rtems/libio.h> #include <rtems/seterr.h> #include <rtems/userenv.h> #include <errno.h> #include <limits.h> #include <stdlib.h> #include <yaffs/rtems_yaffs.h> #include <yaffs/yaffs_packedtags2.h> #include "yaffs2.h" #define MAXIMUM_YAFFS_MOUNTS 1 #define SIMFLASH_BLOCKSIZE (1024) #define SIMFLASH_NR_BLOCKS (110*1024) #define SIMFLASH_SIZE (SIMFLASH_BLOCKSIZE * SIMFLASH_NR_BLOCKS) #define SIMFLASH_CHUNK_TAGS_SIZE (16) #define SIMFLASH_CHUNK_DATA_SIZE (512-SIMFLASH_CHUNK_TAGS_SIZE) #define SIMFLASH_CHUNK_WHOLE_SIZE (SIMFLASH_CHUNK_DATA_SIZE + SIMFLASH_CHUNK_TAGS_SIZE) struct yaffs_softc { struct yaffs_dev *dev; uint8_t *simflash; unsigned int size; unsigned int blocksize; rtems_yaffs_os_handler free_os_context; }; static rtems_status_code my_read(uint8_t *simflash, void *buffer, size_t len, size_t offset) { memcpy(buffer, &simflash[offset], len); return RTEMS_SUCCESSFUL; } static rtems_status_code my_write(uint8_t *simflash, const void *buffer, size_t len, size_t offset) { memcpy(&simflash[offset], buffer, len); return RTEMS_SUCCESSFUL; } /* Flash access functions */ static unsigned int chunk_address(struct yaffs_dev *dev, int c) { struct yaffs_softc *sc = (struct yaffs_softc *)dev->driver_context; unsigned int chunks_per_block = (unsigned int) dev->param.chunks_per_block; return sc->blocksize*(c/chunks_per_block) + SIMFLASH_CHUNK_WHOLE_SIZE*(c%chunks_per_block); } static int write_chunk_tags(struct yaffs_dev *dev, int nand_chunk, const u8 *data, const struct yaffs_ext_tags *tags) { struct yaffs_softc *sc = (struct yaffs_softc *)dev->driver_context; unsigned int address; //printf("%s %d (data=%p tags=%p)\n", __func__, nand_chunk, data, tags); address = chunk_address(dev, nand_chunk); if(data) my_write(sc->simflash, data, SIMFLASH_CHUNK_DATA_SIZE, address); if(tags) { struct yaffs_packed_tags2_tags_only x; yaffs_pack_tags2_tags_only(&x, tags); my_write(sc->simflash, &x, SIMFLASH_CHUNK_TAGS_SIZE, address+SIMFLASH_CHUNK_DATA_SIZE); } return YAFFS_OK; } static int read_chunk_tags(struct yaffs_dev *dev, int nand_chunk, u8 *data, struct yaffs_ext_tags *tags) { struct yaffs_softc *sc = (struct yaffs_softc *)dev->driver_context; unsigned int address; //printf("%s %d (data=%p tags=%p)\n", __func__, nand_chunk, data, tags); address = chunk_address(dev, nand_chunk); if(data) my_read(sc->simflash, data, SIMFLASH_CHUNK_DATA_SIZE, address); if(tags) { struct yaffs_packed_tags2_tags_only x; my_read(sc->simflash, &x, SIMFLASH_CHUNK_TAGS_SIZE, address+SIMFLASH_CHUNK_DATA_SIZE); yaffs_unpack_tags2_tags_only(tags, &x); } return YAFFS_OK; } static int bad_block(struct yaffs_dev *dev, int blockId) { struct yaffs_ext_tags tags; int chunk_nr; chunk_nr = blockId * dev->param.chunks_per_block; read_chunk_tags(dev, chunk_nr, NULL, &tags); tags.block_bad = 1; write_chunk_tags(dev, chunk_nr, NULL, &tags); return YAFFS_OK; } static int query_block(struct yaffs_dev *dev, int blockId, enum yaffs_block_state *state, u32 *seq_number) { struct yaffs_ext_tags tags; int chunk_nr; *seq_number = 0; chunk_nr = blockId * dev->param.chunks_per_block; read_chunk_tags(dev, chunk_nr, NULL, &tags); if(tags.block_bad) *state = YAFFS_BLOCK_STATE_DEAD; else if(!tags.chunk_used) *state = YAFFS_BLOCK_STATE_EMPTY; else if(tags.chunk_used) { *state = YAFFS_BLOCK_STATE_NEEDS_SCAN; *seq_number = tags.seq_number; } return YAFFS_OK; } static int erase(struct yaffs_dev *dev, int blockId) { struct yaffs_softc *sc = dev->driver_context; memset(&(sc->simflash[blockId * SIMFLASH_BLOCKSIZE]), 0xff, SIMFLASH_BLOCKSIZE); return YAFFS_OK; } static int initialise(struct yaffs_dev *dev) { struct yaffs_softc *sc = dev->driver_context; sc->simflash = malloc(SIMFLASH_SIZE); if (sc->simflash != 0) { memset(sc->simflash, 0xff, SIMFLASH_SIZE); return YAFFS_OK; } else { return YAFFS_FAIL; } } static int mount_sema_created; static rtems_id mount_sema; static struct yaffs_softc *current_mounts[MAXIMUM_YAFFS_MOUNTS]; static void unmount_handler(struct yaffs_dev *dev, void *os_context) { struct yaffs_softc *softc = dev->driver_context; int i; rtems_semaphore_obtain(mount_sema, RTEMS_WAIT, RTEMS_NO_TIMEOUT); for(i=0;i<MAXIMUM_YAFFS_MOUNTS;i++) { if(current_mounts[i] == softc) { current_mounts[i] = NULL; break; } } softc->free_os_context(dev, os_context); free(softc); free(dev); rtems_semaphore_release(mount_sema); } static int flush_task_running; static rtems_id flush_task_id; static rtems_task flush_task(rtems_task_argument argument) { int i; struct yaffs_softc *sc; rtems_yaffs_default_os_context *os_context; (void) argument; while(1) { rtems_task_wake_after(10*100); rtems_semaphore_obtain(mount_sema, RTEMS_WAIT, RTEMS_NO_TIMEOUT); for(i=0;i<MAXIMUM_YAFFS_MOUNTS;i++) { sc = current_mounts[i]; if(sc != NULL) { os_context = sc->dev->os_context; os_context->os_context.lock(sc->dev, os_context); yaffs_flush_whole_cache(sc->dev); os_context->os_context.unlock(sc->dev, os_context); } } rtems_semaphore_release(mount_sema); } } int yaffs_initialize(rtems_filesystem_mount_table_entry_t *mt_entry, const void *data) { int i; int index; struct yaffs_dev *dev; struct yaffs_param *param; struct yaffs_softc *softc; rtems_yaffs_default_os_context *os_context; rtems_yaffs_mount_data md; rtems_status_code sc1, sc2; int r; if(!mount_sema_created) { sc1 = rtems_semaphore_create( rtems_build_name('Y', 'A', 'F', 'M'), 1, RTEMS_LOCAL | RTEMS_BINARY_SEMAPHORE | RTEMS_INHERIT_PRIORITY | RTEMS_PRIORITY, 0, &mount_sema ); if(sc1 != RTEMS_SUCCESSFUL) { errno = ENOMEM; return -1; } mount_sema_created = 1; } rtems_semaphore_obtain(mount_sema, RTEMS_WAIT, RTEMS_NO_TIMEOUT); index = -1; for(i=0;i<MAXIMUM_YAFFS_MOUNTS;i++) { if(current_mounts[i] == NULL) { index = i; break; } } rtems_semaphore_release(mount_sema); if(index == -1) { errno = ENOMEM; return -1; } dev = malloc(sizeof(struct yaffs_dev)); if(dev == NULL) { errno = ENOMEM; return -1; } memset(dev, 0, sizeof(struct yaffs_dev)); softc = malloc(sizeof(struct yaffs_softc)); if(softc == NULL) { errno = ENOMEM; free(dev); return -1; } softc->dev = dev; softc->size = SIMFLASH_SIZE; softc->blocksize = SIMFLASH_BLOCKSIZE; if((sc1 != RTEMS_SUCCESSFUL)||(sc2 != RTEMS_SUCCESSFUL)) { errno = EIO; free(softc); free(dev); return -1; } os_context = malloc(sizeof(rtems_yaffs_default_os_context)); if(os_context == NULL) { errno = ENOMEM; free(softc); free(dev); return -1; } r = rtems_yaffs_initialize_default_os_context(os_context); if(r == -1) { free(os_context); free(softc); free(dev); return -1; } softc->free_os_context = os_context->os_context.unmount; os_context->os_context.unmount = unmount_handler; /* set parameters */ dev->driver_context = softc; dev->os_context = os_context; dev->read_only = 0; param = &(dev->param); param->name = "simyaffs2"; param->start_block = 0; param->end_block = softc->size/softc->blocksize - 1; param->chunks_per_block = softc->blocksize/SIMFLASH_CHUNK_WHOLE_SIZE; param->total_bytes_per_chunk = SIMFLASH_CHUNK_WHOLE_SIZE; param->n_reserved_blocks = 5; param->n_caches = 15; param->inband_tags = 1; param->is_yaffs2 = 1; param->no_tags_ecc = 1; /* set callbacks */ param->write_chunk_tags_fn = write_chunk_tags; param->read_chunk_tags_fn = read_chunk_tags; param->bad_block_fn = bad_block; param->query_block_fn = query_block; param->erase_fn = erase; param->initialise_flash_fn = initialise; md.dev = dev; r = rtems_yaffs_mount_handler(mt_entry, &md); if(r == -1) { errno = ENOMEM; softc->free_os_context(dev, os_context); free(softc); free(dev); return -1; } current_mounts[index] = softc; if(!flush_task_running) { sc1 = rtems_task_create(rtems_build_name('F', 'L', 'S', 'H'), 220, 256*1024, RTEMS_PREEMPT | RTEMS_NO_TIMESLICE | RTEMS_NO_ASR, 0, &flush_task_id); assert(sc1 == RTEMS_SUCCESSFUL); sc1 = rtems_task_start(flush_task_id, flush_task, 0); assert(sc1 == RTEMS_SUCCESSFUL); flush_task_running = 1; } return 0; }
/* * Copyright (c) 2018 embedded brains GmbH. All rights reserved. * * embedded brains GmbH * Dornierstr. 4 * 82178 Puchheim * Germany * <rtems@embedded-brains.de> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef APP_YAFFS2_H #define APP_YAFFS2_H #include <rtems.h> #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ int yaffs_initialize(rtems_filesystem_mount_table_entry_t *mt_entry, const void *data); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* APP_YAFFS2_H */
Please notice that in yaffs.c file: there are two things Chunks and Blocks. Block is made up of smaller chunks. A Blocks is the smallest unit that can be erased while, chunks are the smallest units that can be read/written at a time. Also, one block is divided into two sections one for the flag and another for data.