Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

subsys: settings: nvs: provisioning of settings images #79130

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
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
3 changes: 3 additions & 0 deletions boards/qemu/x86/Kconfig.defconfig
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ config QEMU_TARGET
config HAS_COVERAGE_SUPPORT
default y

config X86_EXTRA_PAGE_TABLE_PAGES
default 1 if FLASH_SIMULATOR_SIZE > 0

endif # BOARD_QEMU_X86 || BOARD_QEMU_X86_64 || BOARD_QEMU_X86_LAKEMONT || BOARD_QEMU_X86_TINY


Expand Down
22 changes: 16 additions & 6 deletions boards/qemu/x86/board.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,20 @@ endif()
# debugserver: QEMU_EXTRA_FLAGS += -s -S
# debugserver: qemu

if(CONFIG_BOARD_QEMU_X86_TINY AND CONFIG_DEMAND_PAGING
AND NOT CONFIG_LINKER_GENERIC_SECTIONS_PRESENT_AT_BOOT)
# This is to map the flash so it is accessible.
math(EXPR QEMU_FLASH_SIZE_KB "${CONFIG_FLASH_SIZE} * 1024")
set(X86_EXTRA_GEN_MMU_ARGUMENTS
--map ${CONFIG_FLASH_BASE_ADDRESS},${QEMU_FLASH_SIZE_KB},W)
if(CONFIG_MMU)
if(CONFIG_BOARD_QEMU_X86_TINY AND CONFIG_DEMAND_PAGING
AND NOT CONFIG_LINKER_GENERIC_SECTIONS_PRESENT_AT_BOOT)
# This is to map the flash so it is accessible.
math(EXPR QEMU_FLASH_SIZE_KB "${CONFIG_FLASH_SIZE} * 1024")
set(X86_EXTRA_GEN_MMU_ARGUMENTS
--map ${CONFIG_FLASH_BASE_ADDRESS},${QEMU_FLASH_SIZE_KB},W)
endif()

if(DEFINED CONFIG_FLASH_SIMULATOR_BASE_ADDRESS
AND DEFINED CONFIG_FLASH_SIMULATOR_SIZE)

# Map the simulated flash to a memory file on QEMU targets.
set(X86_EXTRA_GEN_MMU_ARGUMENTS
--map ${CONFIG_FLASH_SIMULATOR_BASE_ADDRESS},${CONFIG_FLASH_SIMULATOR_SIZE},W)
endif()
endif()
35 changes: 26 additions & 9 deletions boards/qemu/x86/qemu_x86.dts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@
#endif
#define DT_FLASH_SIZE DT_SIZE_K(4096)

/* Simulated Flash */
#define DT_SIM_FLASH_BASE (DT_DRAM_BASE + DT_DRAM_SIZE - DT_SIZE_M(1))
#define DT_SIM_FLASH_SIZE DT_SIZE_K(256)

#include <intel/atom.dtsi>
#include <zephyr/dt-bindings/pcie/pcie.h>

Expand Down Expand Up @@ -80,16 +84,29 @@
};
};

reserved-memory {
#address-cells = <1>;
#size-cells = <1>;

sim_flash_mem: memory@1f00000 {
compatible = "zephyr,memory-region";
reg = <DT_SIM_FLASH_BASE DT_SIM_FLASH_SIZE>;
zephyr,memory-region = "SOC_NV_FLASH";
};
};

sim_flash: sim_flash {
compatible = "zephyr,sim-flash";

#address-cells = <1>;
#size-cells = <1>;
erase-value = <0xff>;

memory-region = <&sim_flash_mem>;

flash_sim0: flash_sim@0 {
compatible = "soc-nv-flash";
reg = <0x00000000 DT_SIZE_K(1024)>;
reg = <DT_SIM_FLASH_BASE DT_SIM_FLASH_SIZE>;

erase-block-size = <1024>;
write-block-size = <4>;
Expand Down Expand Up @@ -147,22 +164,22 @@
* Storage partition will be used by FCB/LittleFS/NVS
* if enabled.
*/
storage_partition: partition@1000 {
storage_partition: partition@0 {
label = "storage";
reg = <0x00001000 0x00010000>;
reg = <0x00000000 0x00010000>;
};

slot0_partition: partition@11000 {
slot0_partition: partition@10000 {
label = "image-0";
reg = <0x00011000 0x00010000>;
reg = <0x00010000 0x00010000>;
};
slot1_partition: partition@21000 {
slot1_partition: partition@20000 {
label = "image-1";
reg = <0x00021000 0x00010000>;
reg = <0x00020000 0x00010000>;
};
eepromemu_partition: partition@31000 {
eepromemu_partition: partition@30000 {
label = "eeprom-emu";
reg = <0x00031000 0x00010000>;
reg = <0x00030000 0x00010000>;
};
};
};
6 changes: 6 additions & 0 deletions cmake/emu/qemu.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,12 @@ else()
add_custom_target(qemu_nvme_disk)
endif()

if(CONFIG_FLASH_SIMULATOR_PROVISION)
list(APPEND QEMU_EXTRA_FLAGS
-device loader,file=${CMAKE_CURRENT_BINARY_DIR}/soc-nv-flash-image.hex
)
endif()

if(NOT QEMU_PIPE)
set(QEMU_PIPE_COMMENT "\nTo exit from QEMU enter: 'CTRL+a, x'\n")
endif()
Expand Down
20 changes: 20 additions & 0 deletions drivers/flash/Kconfig.simulator
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,26 @@ config FLASH_SIMULATOR_DOUBLE_WRITES
In case when FLASH_SIMULATOR_EXPLICIT_ERASE is selected multiple writes to the same bit
but only change from erase-value to opposite will be registered.

config FLASH_SIMULATOR_PROVISION
bool "Initialize flash in QEMU from an image file to simulate provisioning."
help
This option maps simulated flash to a memory file on QEMU targets. This
file can be a pre-built binary image so that flash will be pre-populated.

DT_COMPAT_Z_SIM_FLASH:=zephyr,sim-flash
DT_NODELABEL_ENABLED_WITH_Z_SIM_FLASH:=$(dt_nodelabel_enabled_with_compat,sim_flash,$(DT_COMPAT_Z_SIM_FLASH))
DT_NODELABEL_SIM_FLASH_MEMORY_REGION:=$(dt_node_ph_prop_path,$(dt_nodelabel_path,sim_flash),memory-region)

config FLASH_SIMULATOR_BASE_ADDRESS
hex
depends on $(DT_NODELABEL_ENABLED_WITH_Z_SIM_FLASH)
default $(dt_node_reg_addr_hex,$(DT_NODELABEL_SIM_FLASH_MEMORY_REGION))

config FLASH_SIMULATOR_SIZE
int
depends on $(DT_NODELABEL_ENABLED_WITH_Z_SIM_FLASH)
default $(dt_node_reg_size_int,$(DT_NODELABEL_SIM_FLASH_MEMORY_REGION))

config FLASH_SIMULATOR_SIMULATE_TIMING
bool "Hardware timing simulation"

Expand Down
22 changes: 13 additions & 9 deletions drivers/flash/flash_simulator.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
#error "Erase unit must be a multiple of program unit"
#endif

#define MOCK_FLASH(addr) (mock_flash + (addr) - FLASH_SIMULATOR_BASE_OFFSET)
#define MOCK_FLASH(offset) (mock_flash + (offset))

/* maximum number of pages that can be tracked by the stats module */
#define STATS_PAGE_COUNT_THRESHOLD 256
Expand Down Expand Up @@ -148,6 +148,9 @@ static bool flash_erase_at_start;
static bool flash_rm_at_exit;
static bool flash_in_ram;
#else
#if CONFIG_FLASH_SIMULATOR_PROVISION
static uint8_t *mock_flash = (uint8_t *)FLASH_SIMULATOR_BASE_OFFSET;
#else
#if DT_NODE_HAS_PROP(DT_PARENT(SOC_NV_FLASH_NODE), memory_region)
#define FLASH_SIMULATOR_MREGION \
LINKER_DT_NODE_REGION_NAME( \
Expand All @@ -156,6 +159,7 @@ static uint8_t mock_flash[FLASH_SIMULATOR_FLASH_SIZE] Z_GENERIC_SECTION(FLASH_SI
#else
static uint8_t mock_flash[FLASH_SIMULATOR_FLASH_SIZE];
#endif
#endif /* CONFIG_FLASH_SIMULATOR_PROVISION */
#endif /* CONFIG_ARCH_POSIX */

static const struct flash_driver_api flash_sim_api;
Expand All @@ -174,9 +178,9 @@ static int flash_range_is_valid(const struct device *dev, off_t offset,
size_t len)
{
ARG_UNUSED(dev);
if ((offset + len > FLASH_SIMULATOR_FLASH_SIZE +
FLASH_SIMULATOR_BASE_OFFSET) ||
(offset < FLASH_SIMULATOR_BASE_OFFSET)) {

if ((offset < 0 || offset >= FLASH_SIMULATOR_FLASH_SIZE ||
(FLASH_SIMULATOR_FLASH_SIZE - offset) < len)) {
return 0;
}

Expand Down Expand Up @@ -299,8 +303,7 @@ static int flash_sim_write(const struct device *dev, const off_t offset,

static void unit_erase(const uint32_t unit)
{
const off_t unit_addr = FLASH_SIMULATOR_BASE_OFFSET +
(unit * FLASH_SIMULATOR_ERASE_UNIT);
const off_t unit_addr = unit * FLASH_SIMULATOR_ERASE_UNIT;

/* erase the memory unit by setting it to erase value */
memset(MOCK_FLASH(unit_addr), FLASH_SIMULATOR_ERASE_VALUE,
Expand Down Expand Up @@ -332,8 +335,7 @@ static int flash_sim_erase(const struct device *dev, const off_t offset,
}
#endif
/* the first unit to be erased */
uint32_t unit_start = (offset - FLASH_SIMULATOR_BASE_OFFSET) /
FLASH_SIMULATOR_ERASE_UNIT;
uint32_t unit_start = offset / FLASH_SIMULATOR_ERASE_UNIT;

/* erase as many units as necessary and increase their erase counter */
for (uint32_t i = 0; i < len / FLASH_SIMULATOR_ERASE_UNIT; i++) {
Expand Down Expand Up @@ -417,7 +419,9 @@ static int flash_mock_init(const struct device *dev)
static int flash_mock_init(const struct device *dev)
{
ARG_UNUSED(dev);
memset(mock_flash, FLASH_SIMULATOR_ERASE_VALUE, ARRAY_SIZE(mock_flash));
if (!IS_ENABLED(CONFIG_FLASH_SIMULATOR_PROVISION)) {
memset(mock_flash, FLASH_SIMULATOR_ERASE_VALUE, FLASH_SIMULATOR_FLASH_SIZE);
}
return 0;
}
#endif /* DT_NODE_HAS_PROP(DT_PARENT(SOC_NV_FLASH_NODE), memory_region) */
Expand Down
5 changes: 4 additions & 1 deletion include/zephyr/arch/x86/ia32/linker.ld
Original file line number Diff line number Diff line change
Expand Up @@ -573,7 +573,10 @@ SECTIONS
.strtab 0 : { *(.strtab) }
.shstrtab 0 : { *(.shstrtab) }
#endif
}

/* Sections generated from 'zephyr,memory-region' nodes */
LINKER_DT_SECTIONS()
}

#ifdef CONFIG_XIP
/*
Expand Down
3 changes: 3 additions & 0 deletions include/zephyr/kernel/internal/mm.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@
#ifdef CONFIG_XIP
#error "XIP and a virtual memory kernel are not allowed"
#endif
#ifdef CONFIG_FLASH_SIMULATOR_PROVISION
#error "Simulated flash memory cannot be provisioned for a virtual memory kernel."
#endif
#endif

#ifndef _ASMLANGUAGE
Expand Down
57 changes: 57 additions & 0 deletions samples/subsys/settings/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,60 @@ Sample Output
<gamma> = 0 (default)
save <gamma> key directly: OK.
...
Provisioning of Settings
************************

Settings will often have to be provisioned separately from the application
itself, e.g. because the settings are specific to a particular device or because
the settings are sensitive and should not be included in the application binary.

The settings subsystem supports this use case by allowing settings to be
provisioned from a separate image.

To demonstrate this feature, the sample can be built with the
``CONFIG_FLASH_SIMULATOR_PROVISION`` option on QEMU. This will simulate a flash
partition with a fixed memory layout and load the settings from a separate image
at runtime into QEMU memory.

To build and run the sample on QEMU, follow these steps:

1. Build the sample for the qemu_x86 board with the default settings from
qemu_x86.conf (i.e. with the NVS settings backend enabled.) and this
overlay:

.. zephyr-app-commands::
:zephyr-app: samples/subsys/settings
:board: qemu_x86
:goals: run
:conf: overlay-provisioning.conf
:compact:

2. Place the sample soc-nv-flash-image.hex file in the build directory:

.. code-block:: shell
cp samples/subsys/settings/soc-nv-flash-image.hex build/zephyr
3. Start the QEMU emulator with the following command:

.. zephyr-app-commands::
:zephyr-app: samples/subsys/settings
:goals: run
:compact:

To achieve the same with a real device, a separate flash image can be prepared
that conforms to the selected memory backend and is flashed separately to the
appropriate flash memory location.

A tool to generate arbitrary NVS partition content is currently in preparation
and will be contributed soon. In the meantime you can extract the flash content
from a test device, store it to a binary file and convert it to a hex file using
the ``objcpy`` tool:

.. code-block:: shell
objcopy -O ihex --change-section-address SOC_NV_FLASH=<your flash base address> \
<extracted-flash-mem-region>.bin build/zephyr/soc-nv-flash-image.hex
And then flash the hex file to the device without erasing existing flash content.
7 changes: 7 additions & 0 deletions samples/subsys/settings/overlay-provisioning.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# See the sample's README for more information on how
# to provision settings from a separate flash image.
CONFIG_FLASH_SIMULATOR_PROVISION=y

CONFIG_DEBUG=y
CONFIG_DEBUG_OPTIMIZATIONS=y
CONFIG_DEBUG_THREAD_INFO=y
Loading
Loading