Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions common/src/lx_nor_flash_block_reclaim.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
* SPDX-License-Identifier: MIT
**************************************************************************/

/* Portions of this file were generated with AI assistance. */


/**************************************************************************/
/**************************************************************************/
Expand Down Expand Up @@ -294,7 +296,8 @@ UINT status;
_lx_nor_flash_physical_sector_allocate(nor_flash, logical_sector, &new_mapping_address, &new_sector_address);

/* Check to see if the new sector is also in the erase block. */
if ((new_sector_address >= block_word_ptr) && (new_sector_address < (block_word_ptr + nor_flash -> lx_nor_flash_words_per_block)))
if ((new_mapping_address != LX_NULL) && (new_sector_address >= block_word_ptr) &&
(new_sector_address < (block_word_ptr + nor_flash -> lx_nor_flash_words_per_block)))
{

/* Yes, the new sector was found in the block to be erased. Simply move the search pointer
Expand All @@ -311,7 +314,8 @@ UINT status;
/* Check again for the new sector inside of the block to erase. This should be impossible, since
we check previously if there are enough free sectors outside of this block needed to reclaim
this block. */
if ((new_sector_address >= block_word_ptr) && (new_sector_address < (block_word_ptr + LX_NOR_SECTOR_SIZE)))
if ((new_mapping_address != LX_NULL) && (new_sector_address >= block_word_ptr) &&
(new_sector_address < (block_word_ptr + LX_NOR_SECTOR_SIZE)))
{

/* System error, a new sector is not available outside of the erase block.
Expand Down Expand Up @@ -453,7 +457,7 @@ UINT status;
_lx_nor_flash_system_error(nor_flash, LX_SYSTEM_ALLOCATION_FAILED);

/* Return the error. */
return(status);
return(LX_SYSTEM_ALLOCATION_FAILED);
}

/* Decrement the number of mapped sectors. */
Expand Down
6 changes: 6 additions & 0 deletions test/cmake/regression/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,9 @@ foreach(test_case ${regression_test_cases} ${regression_test_cases_exfat})
target_compile_definitions(${test_name} PRIVATE BATCH_TEST)
add_test(${CMAKE_BUILD_TYPE}::${test_name} ${test_name})
endforeach()

add_executable(levelx_nor_reclaim_failure_test
${SOURCE_DIR}/levelx_nor_reclaim_failure_test.c)
target_link_libraries(levelx_nor_reclaim_failure_test PRIVATE azrtos::levelx)
add_test(${CMAKE_BUILD_TYPE}::levelx_nor_reclaim_failure_test
levelx_nor_reclaim_failure_test)
160 changes: 160 additions & 0 deletions test/regression/levelx_nor_reclaim_failure_test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
/***************************************************************************
* Copyright (c) 2026 Eclipse ThreadX contributors
*
* This program and the accompanying materials are made available under the
* terms of the MIT License which is available at
* https://opensource.org/licenses/MIT.
*
* AI Disclosure: This file was largely AI-generated by Codex (GPT-6).
* The AI-generated portions may be considered public domain (CC0-1.0)
* and not subject to the project's licence. The human contributor has
* reviewed and verified that the code is correct.
*
* SPDX-License-Identifier: MIT and CC0-1.0
**************************************************************************/

#include <string.h>
#include "lx_api.h"

static LX_NOR_FLASH nor_flash;
static ULONG flash_words[256];
static UINT allocation_calls;
static UINT invalidation_calls;
static UINT unexpected_driver_calls;
static UINT fail_on_retry;


#ifndef LX_STANDALONE_ENABLE
/* Provide the ThreadX application entry point required by the host library. */
VOID tx_application_define(VOID *first_unused_memory)
{
LX_PARAMETER_NOT_USED(first_unused_memory);
}
#endif


/* Select a block with one mapped sector for reclamation. */
UINT _lx_nor_flash_next_block_to_erase_find(LX_NOR_FLASH *flash, ULONG *erase_block,
ULONG *erase_count, ULONG *mapped_sectors,
ULONG *obsolete_sectors)
{
LX_PARAMETER_NOT_USED(flash);
*erase_block = 0;
*erase_count = 0;
*mapped_sectors = 1;
*obsolete_sectors = 0;
return(LX_SUCCESS);
}


/* Fail either the first allocation or the retry after selecting the erase block. */
UINT _lx_nor_flash_physical_sector_allocate(LX_NOR_FLASH *flash, ULONG logical_sector,
ULONG **mapping_address, ULONG **sector_address)
{
LX_PARAMETER_NOT_USED(flash);
LX_PARAMETER_NOT_USED(logical_sector);
allocation_calls++;

if ((fail_on_retry != 0U) && (allocation_calls == 1U))
{
*mapping_address = &flash_words[2];
*sector_address = &flash_words[8];
return(LX_SUCCESS);
}

*mapping_address = LX_NULL;
*sector_address = LX_NULL;
return(LX_NO_SECTORS);
}


/* Count mapping-cache invalidations before allocation. */
VOID _lx_nor_flash_sector_mapping_cache_invalidate(LX_NOR_FLASH *flash, ULONG logical_sector)
{
LX_PARAMETER_NOT_USED(flash);
LX_PARAMETER_NOT_USED(logical_sector);
invalidation_calls++;
}


/* Supply mapped-list words when direct reading is disabled. */
UINT _lx_nor_flash_driver_read(LX_NOR_FLASH *flash, ULONG *address, ULONG *destination, ULONG words)
{
ULONG i;

LX_PARAMETER_NOT_USED(flash);
for (i = 0; i < words; i++)
{
destination[i] = address[i];
}
return(LX_SUCCESS);
}


/* Record writes that must not occur after allocation failure. */
UINT _lx_nor_flash_driver_write(LX_NOR_FLASH *flash, ULONG *address, ULONG *source, ULONG words)
{
LX_PARAMETER_NOT_USED(flash);
LX_PARAMETER_NOT_USED(address);
LX_PARAMETER_NOT_USED(source);
LX_PARAMETER_NOT_USED(words);
unexpected_driver_calls++;
return(LX_SUCCESS);
}


/* Record erases that must not occur after allocation failure. */
UINT _lx_nor_flash_driver_block_erase(LX_NOR_FLASH *flash, ULONG block, ULONG erase_count)
{
LX_PARAMETER_NOT_USED(flash);
LX_PARAMETER_NOT_USED(block);
LX_PARAMETER_NOT_USED(erase_count);
unexpected_driver_calls++;
return(LX_SUCCESS);
}


/* Check both allocation-failure points in block reclaim. */
static UINT test_reclaim_allocation_failure(UINT retry)
{
UINT status;

(void) memset(&nor_flash, 0, sizeof(nor_flash));
(void) memset(flash_words, 0, sizeof(flash_words));
allocation_calls = 0;
invalidation_calls = 0;
unexpected_driver_calls = 0;
fail_on_retry = retry;

nor_flash.lx_nor_flash_base_address = flash_words;
nor_flash.lx_nor_flash_words_per_block = 128;
nor_flash.lx_nor_flash_total_blocks = 2;
nor_flash.lx_nor_flash_physical_sectors_per_block = 2;
nor_flash.lx_nor_flash_free_physical_sectors = 2;
nor_flash.lx_nor_flash_block_physical_sector_mapping_offset = 1;
flash_words[1] = LX_NOR_PHYSICAL_SECTOR_VALID | 7U;

status = _lx_nor_flash_block_reclaim(&nor_flash);
if ((status != LX_SYSTEM_ALLOCATION_FAILED) ||
(nor_flash.lx_nor_flash_diagnostic_system_error != LX_SYSTEM_ALLOCATION_FAILED) ||
(nor_flash.lx_nor_flash_diagnostic_system_errors != 1U) ||
(allocation_calls != (retry + 1U)) || (invalidation_calls != 1U) ||
(unexpected_driver_calls != 0U))
{
return(LX_ERROR);
}
return(LX_SUCCESS);
}


/* Run failure checks for the first allocation and its retry. */
int main(void)
{
if ((test_reclaim_allocation_failure(0U) != LX_SUCCESS) ||
(test_reclaim_allocation_failure(1U) != LX_SUCCESS))
{
return(1);
}

return(0);
}