mirror of
https://github.com/apache/nuttx-apps.git
synced 2026-08-01 20:29:00 +00:00
examples/nxflatxip: Run an NXFLAT module in place out of xipfs.
The point of a writable execute-in-place file system is that a module can arrive after the firmware was built and still run without being copied into RAM. This demonstrates exactly that, end to end: it writes an NXFLAT module into xipfs the way a download would, declaring the size up front so the extent is exact, checks the file system can hand out a real flash pointer for it, and runs two instances concurrently. Both instances report the address of their own text and of their stack. The text address is the same in both and is inside the flash window -- it is the address the file occupies on the media -- while the stacks differ. While they run, the extent carries one pin per instance, which is what stops the defragmenter relocating code that is executing. Two subcommands cover the file system rather than the module. bench times a bulk read out of the mapped flash before and after a write, which on the RP2350 is the cost of whichever path the driver used to restore XIP. defrag fills the volume, deletes every other file until an allocation genuinely cannot be satisfied, compacts, retries the same allocation, then verifies every relocated file byte for byte and again after a remount, printing a block map at each step. The module has neither static data nor string constants, and reports through a callback into the firmware instead of formatting its own output. That is not stylistic: the ldnxflat in circulation resolves the GOT entries for .bss objects into the import table, and drops the addend when it resolves the PC-relative references to .rodata, so a module using either quietly computes wrong addresses. The comment in module/xipmod.c records both. The callback also exercises the other direction of the interface, firmware code entered with the module's data base still live in r10. Assisted-by: Claude Code:claude-opus-5 Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
This commit is contained in:
parent
3a03552c6a
commit
2492317f15
9 changed files with 1257 additions and 0 deletions
2
examples/nxflatxip/.gitignore
vendored
Normal file
2
examples/nxflatxip/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
/symtab.c
|
||||
/xipmod_bin.h
|
||||
29
examples/nxflatxip/CMakeLists.txt
Normal file
29
examples/nxflatxip/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
# ##############################################################################
|
||||
# apps/examples/nxflatxip/CMakeLists.txt
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# ##############################################################################
|
||||
|
||||
# The module and the symbol table it needs are built by module/Makefile, which
|
||||
# the CMake build does not drive. As with apps/examples/nxflat, use the make
|
||||
# build for this example.
|
||||
|
||||
if(CONFIG_EXAMPLES_NXFLATXIP)
|
||||
nuttx_add_application(NAME nxflatxip SRCS nxflatxip_main.c symtab.c)
|
||||
endif()
|
||||
38
examples/nxflatxip/Kconfig
Normal file
38
examples/nxflatxip/Kconfig
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
#
|
||||
# For a description of the syntax of this configuration file,
|
||||
# see the file kconfig-language.txt in the NuttX tools repository.
|
||||
#
|
||||
|
||||
config EXAMPLES_NXFLATXIP
|
||||
tristate "NXFLAT execute-in-place from xipfs demo"
|
||||
depends on FS_XIPFS && NXFLAT && LIBC_EXECFUNCS
|
||||
default n
|
||||
---help---
|
||||
Write an NXFLAT module into xipfs at run time, the way a download
|
||||
would, then run two instances of it concurrently. The module's
|
||||
text is executed directly out of flash and shared between the
|
||||
instances, while each instance gets its own data.
|
||||
|
||||
Subcommands:
|
||||
|
||||
bench XIP read throughput before and after a flash write
|
||||
defrag fragment the volume, compact it, with a block map
|
||||
|
||||
Building the module needs the mknxflat and ldnxflat host tools
|
||||
from the NuttX toolchain, and the board's Make.defs must name
|
||||
them, exactly as apps/examples/nxflat requires.
|
||||
|
||||
if EXAMPLES_NXFLATXIP
|
||||
|
||||
config EXAMPLES_NXFLATXIP_MOUNTPT
|
||||
string "xipfs mountpoint"
|
||||
default "/mnt/xipfs"
|
||||
|
||||
config EXAMPLES_NXFLATXIP_MTD
|
||||
string "MTD device backing the volume"
|
||||
default "/dev/rpflash"
|
||||
---help---
|
||||
Used only by the defrag subcommand, which remounts the volume to
|
||||
show that what it did survives.
|
||||
|
||||
endif # EXAMPLES_NXFLATXIP
|
||||
25
examples/nxflatxip/Make.defs
Normal file
25
examples/nxflatxip/Make.defs
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
############################################################################
|
||||
# apps/examples/nxflatxip/Make.defs
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
############################################################################
|
||||
|
||||
ifneq ($(CONFIG_EXAMPLES_NXFLATXIP),)
|
||||
CONFIGURED_APPS += $(APPDIR)/examples/nxflatxip
|
||||
endif
|
||||
54
examples/nxflatxip/Makefile
Normal file
54
examples/nxflatxip/Makefile
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
############################################################################
|
||||
# apps/examples/nxflatxip/Makefile
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
############################################################################
|
||||
|
||||
include $(APPDIR)/Make.defs
|
||||
|
||||
# NXFLAT execute-in-place from xipfs
|
||||
|
||||
CSRCS = symtab.c
|
||||
MAINSRC = nxflatxip_main.c
|
||||
|
||||
# The generated symbol table declares every imported symbol as void *, which
|
||||
# the compiler objects to for the ones it knows as builtins.
|
||||
|
||||
symtab.c_CFLAGS = -fno-builtin
|
||||
|
||||
PROGNAME = nxflatxip
|
||||
PRIORITY = SCHED_PRIORITY_DEFAULT
|
||||
STACKSIZE = $(CONFIG_DEFAULT_TASK_STACKSIZE)
|
||||
MODULE = $(CONFIG_EXAMPLES_NXFLATXIP)
|
||||
|
||||
# Both are generated by the module build below
|
||||
|
||||
symtab.c: build
|
||||
xipmod_bin.h: build
|
||||
|
||||
$(MAINSRC): xipmod_bin.h
|
||||
|
||||
.PHONY: build
|
||||
build:
|
||||
+$(Q) $(MAKE) -C module TOPDIR="$(TOPDIR)" APPDIR="$(APPDIR)" CROSSDEV=$(CROSSDEV)
|
||||
|
||||
clean::
|
||||
+$(Q) $(MAKE) -C module TOPDIR="$(TOPDIR)" APPDIR="$(APPDIR)" CROSSDEV=$(CROSSDEV) clean
|
||||
|
||||
include $(APPDIR)/Application.mk
|
||||
4
examples/nxflatxip/module/.gitignore
vendored
Normal file
4
examples/nxflatxip/module/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
/xipmod
|
||||
/xipmod-thunk.S
|
||||
/*.r1
|
||||
/*.r2
|
||||
94
examples/nxflatxip/module/Makefile
Normal file
94
examples/nxflatxip/module/Makefile
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
############################################################################
|
||||
# apps/examples/nxflatxip/module/Makefile
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
############################################################################
|
||||
|
||||
include $(APPDIR)/Make.defs
|
||||
|
||||
# The NXFLAT module the demo downloads into xipfs. Built exactly the way
|
||||
# apps/examples/nxflat builds its test programs, so it needs the same two
|
||||
# host tools from the NuttX toolchain: mknxflat and ldnxflat.
|
||||
|
||||
BIN = xipmod
|
||||
|
||||
R1SRCS = $(BIN).c
|
||||
R1OBJS = $(R1SRCS:.c=.o)
|
||||
|
||||
R2SRC = $(BIN)-thunk.S
|
||||
R2OBJ = $(R2SRC:.S=.o)
|
||||
|
||||
EXAMPLDIR = $(APPDIR)$(DELIM)examples$(DELIM)nxflatxip
|
||||
BINHDR = $(EXAMPLDIR)$(DELIM)$(BIN)_bin.h
|
||||
SYMTAB = $(EXAMPLDIR)$(DELIM)symtab.c
|
||||
|
||||
all: $(BINHDR) $(SYMTAB)
|
||||
.PHONY: all clean
|
||||
|
||||
$(R1OBJS): %.o: %.c
|
||||
@echo "CC: $<"
|
||||
$(Q) $(CC) -c $(CPICFLAGS) $< -o $@
|
||||
|
||||
$(R2OBJ): %.o: %.S
|
||||
@echo "AS: $<"
|
||||
$(Q) $(CC) -c $(CPICFLAGS) $< -o $@
|
||||
|
||||
$(BIN).r1: $(R1OBJS)
|
||||
@echo "LD: $<"
|
||||
$(Q) $(LD) $(NXFLATLDFLAGS1) -o $@ $^
|
||||
|
||||
$(R2SRC): $(BIN).r1
|
||||
@echo "MK: $<"
|
||||
$(Q) $(MKNXFLAT) -o $@ $^
|
||||
|
||||
$(BIN).r2: $(R2OBJ)
|
||||
@echo "LD: $<"
|
||||
$(Q) $(LD) $(NXFLATLDFLAGS2) -o $@ $(R1OBJS) $(R2OBJ)
|
||||
|
||||
$(BIN): $(BIN).r2
|
||||
@echo "LD: $<"
|
||||
$(Q) $(LDNXFLAT) $(LDNXFLATFLAGS) -o $@ $^
|
||||
|
||||
# Embed the module in the demo. It is not the demo's own text, it is a
|
||||
# payload the demo writes into the filesystem, so a plain byte array is what
|
||||
# is wanted.
|
||||
|
||||
$(BINHDR): $(BIN)
|
||||
@echo "GEN: $@"
|
||||
$(Q) xxd -i -n g_xipmod $(BIN) | \
|
||||
sed -e "s/^unsigned char/static const unsigned char/" \
|
||||
-e "s/^unsigned int/static const unsigned int/" >$@.tmp
|
||||
$(Q) $(call TESTANDREPLACEFILE, $@.tmp, $@)
|
||||
|
||||
# The symbols the module imports from the firmware. mknxflat left the list
|
||||
# in the thunk file it generated, so that is what is scanned.
|
||||
|
||||
$(SYMTAB): $(R2SRC)
|
||||
@echo "GEN: $@"
|
||||
$(Q) $(APPDIR)$(DELIM)tools$(DELIM)mksymtab.sh . g_nxflat >$@.tmp
|
||||
$(Q) $(call TESTANDREPLACEFILE, $@.tmp, $@)
|
||||
|
||||
clean:
|
||||
$(call DELFILE, $(BIN))
|
||||
$(call DELFILE, $(R2SRC))
|
||||
$(call DELFILE, *.r1)
|
||||
$(call DELFILE, *.r2)
|
||||
$(call DELFILE, $(BINHDR))
|
||||
$(call DELFILE, $(SYMTAB))
|
||||
$(call CLEAN)
|
||||
116
examples/nxflatxip/module/xipmod.c
Normal file
116
examples/nxflatxip/module/xipmod.c
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
/****************************************************************************
|
||||
* apps/examples/nxflatxip/module/xipmod.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.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* The module the demo downloads into xipfs and runs.
|
||||
*
|
||||
* Given a seed on the command line it fills an array derived from it,
|
||||
* sleeps long enough for a second instance to do the same with a different
|
||||
* seed, checks its own values are untouched, and hands the result to the
|
||||
* firmware. It reports the address of its own text, which is the point of
|
||||
* the exercise: that address is inside the memory mapped flash window, it
|
||||
* is the same in both instances, and the module is executing from it rather
|
||||
* than from a copy in RAM.
|
||||
*
|
||||
* Two things this module deliberately does not have, both because of what
|
||||
* the ldnxflat in circulation does with them:
|
||||
*
|
||||
* - No global or static variables. Those are reached through the GOT,
|
||||
* and the GOT entries come out pointing into the import table rather
|
||||
* than into .bss, so the module overwrites the addresses of the
|
||||
* functions it imports and jumps into nothing on its next call out.
|
||||
* - No string constants. With this linker script .rodata lives in the
|
||||
* text segment and is reached PC-relatively, and the addend already
|
||||
* in the instruction is dropped when that relocation is resolved, so
|
||||
* the pointer lands somewhere in the middle of the code.
|
||||
*
|
||||
* Hence the reporting callback: the module passes numbers to the firmware
|
||||
* and the firmware, which has a working printf and a format string, does
|
||||
* the printing. It also exercises the other direction of the interface --
|
||||
* firmware code entered with the module's data base still in r10.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/****************************************************************************
|
||||
* Pre-processor Definitions
|
||||
****************************************************************************/
|
||||
|
||||
#define NVALUES 64
|
||||
|
||||
/****************************************************************************
|
||||
* Public Function Prototypes
|
||||
****************************************************************************/
|
||||
|
||||
/* Exported by the demo in the firmware and imported by this module */
|
||||
|
||||
void xipmod_report(int seed, const void *text, const void *stack,
|
||||
long sum, int errors);
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int values[NVALUES];
|
||||
int errors = 0;
|
||||
int seed = 1;
|
||||
long sum = 0;
|
||||
int i;
|
||||
|
||||
if (argc > 1)
|
||||
{
|
||||
seed = atoi(argv[1]);
|
||||
}
|
||||
|
||||
for (i = 0; i < NVALUES; i++)
|
||||
{
|
||||
values[i] = seed * (i + 1);
|
||||
}
|
||||
|
||||
/* Long enough that the other instance has certainly filled its own copy
|
||||
* by the time this one checks.
|
||||
*/
|
||||
|
||||
usleep(200000);
|
||||
|
||||
for (i = 0; i < NVALUES; i++)
|
||||
{
|
||||
if (values[i] != seed * (i + 1))
|
||||
{
|
||||
errors++;
|
||||
}
|
||||
|
||||
sum += values[i];
|
||||
}
|
||||
|
||||
xipmod_report(seed, (const void *)main, (const void *)values, sum, errors);
|
||||
|
||||
return errors == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
895
examples/nxflatxip/nxflatxip_main.c
Normal file
895
examples/nxflatxip/nxflatxip_main.c
Normal file
|
|
@ -0,0 +1,895 @@
|
|||
/****************************************************************************
|
||||
* apps/examples/nxflatxip/nxflatxip_main.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.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* End to end demonstration of what a writable execute-in-place file system
|
||||
* is for:
|
||||
*
|
||||
* 1. write an NXFLAT module into xipfs at run time, the way a download
|
||||
* would,
|
||||
* 2. confirm the file system can hand out a direct flash pointer for it,
|
||||
* 3. run two instances of it concurrently,
|
||||
* 4. confirm both ran correctly, that their data did not interfere, and
|
||||
* that the shared text was pinned in place while they ran.
|
||||
*
|
||||
* The same module in a ROMFS image would give the same execute-in-place
|
||||
* behaviour; what it could not do is arrive after the firmware was built.
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <nuttx/binfmt/binfmt.h>
|
||||
#include <nuttx/fs/ioctl.h>
|
||||
#include <nuttx/fs/xipfs.h>
|
||||
#include <nuttx/symtab.h>
|
||||
|
||||
#include "xipmod_bin.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Pre-processor Definitions
|
||||
****************************************************************************/
|
||||
|
||||
#define MOUNTPT CONFIG_EXAMPLES_NXFLATXIP_MOUNTPT
|
||||
#define MTDDEV CONFIG_EXAMPLES_NXFLATXIP_MTD
|
||||
#define MODPATH MOUNTPT "/xipmod"
|
||||
#define BENCH_PATH MOUNTPT "/big"
|
||||
#define BENCH_SIZE (256 * 1024)
|
||||
#define DF_FILES 55
|
||||
#define DF_MAPCOLS 50
|
||||
|
||||
/****************************************************************************
|
||||
* External Symbols
|
||||
****************************************************************************/
|
||||
|
||||
/* Generated by module/Makefile from the import list mknxflat left in the
|
||||
* module's thunk file. The NXFLAT loader has no other way to resolve the
|
||||
* functions the module calls.
|
||||
*/
|
||||
|
||||
extern const struct symtab_s g_nxflat_exports[];
|
||||
extern const int g_nxflat_nexports;
|
||||
|
||||
/****************************************************************************
|
||||
* Private Data
|
||||
****************************************************************************/
|
||||
|
||||
static char g_map[512];
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: xipmod_report
|
||||
*
|
||||
* Description:
|
||||
* Called by the module, running in place from flash, to report what it
|
||||
* found. It passes numbers rather than formatting its own output; see
|
||||
* module/xipmod.c for why.
|
||||
*
|
||||
* This is also the direction of the interface that needs r10 to survive:
|
||||
* the module reaches its own data through that register, and this
|
||||
* function runs with the module's data base still in it. That is what
|
||||
* the --fixed-r10 the ARM toolchain adds under CONFIG_PIC is for.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
void xipmod_report(int seed, FAR const void *text, FAR const void *stack,
|
||||
long sum, int errors)
|
||||
{
|
||||
syslog(LOG_INFO, " instance seed %d: text %p, stack %p, sum %ld -- %s\n",
|
||||
seed, text, stack, sum,
|
||||
errors == 0 ? "data private and intact" : "DATA CORRUPTED");
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: stage_module
|
||||
*
|
||||
* Description:
|
||||
* Write the module into the file system exactly as a download would:
|
||||
* declare the size up front so xipfs reserves an exactly sized
|
||||
* contiguous extent, then write it once.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int stage_module(void)
|
||||
{
|
||||
ssize_t nwritten;
|
||||
int fd;
|
||||
|
||||
unlink(MODPATH);
|
||||
|
||||
fd = open(MODPATH, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0755);
|
||||
if (fd < 0)
|
||||
{
|
||||
syslog(LOG_INFO, "ERROR: open %s failed: %d\n", MODPATH, errno);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (ftruncate(fd, g_xipmod_len) < 0)
|
||||
{
|
||||
syslog(LOG_INFO, "ERROR: ftruncate failed: %d\n", errno);
|
||||
close(fd);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
nwritten = write(fd, g_xipmod, g_xipmod_len);
|
||||
close(fd);
|
||||
|
||||
if (nwritten != (ssize_t)g_xipmod_len)
|
||||
{
|
||||
syslog(LOG_INFO, "ERROR: short write: %zd of %u\n", nwritten,
|
||||
g_xipmod_len);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
syslog(LOG_INFO, "staged %u bytes to %s\n", g_xipmod_len, MODPATH);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: extent_info_path
|
||||
*
|
||||
* Description:
|
||||
* Where a file physically lies: its extent, its flash address, and how
|
||||
* many live mappings are pinning it in place.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int extent_info_path(FAR const char *path,
|
||||
FAR struct xipfs_extent_info_s *info)
|
||||
{
|
||||
int fd;
|
||||
int ret;
|
||||
|
||||
fd = open(path, O_RDONLY | O_CLOEXEC);
|
||||
if (fd < 0)
|
||||
{
|
||||
return -errno;
|
||||
}
|
||||
|
||||
ret = ioctl(fd, XIPFSIOC_EXTENTINFO, (unsigned long)(uintptr_t)info);
|
||||
close(fd);
|
||||
|
||||
return ret < 0 ? -errno : 0;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: bench_read
|
||||
*
|
||||
* Description:
|
||||
* Time a bulk read straight out of the memory mapped flash, big enough to
|
||||
* miss the XIP cache. Run once on a freshly booted system, where the
|
||||
* bootrom's fast read mode is still in effect, and again after a flash
|
||||
* write has forced the driver to restore XIP itself. The difference is
|
||||
* the cost of whichever restore path the driver used.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static long bench_read(void)
|
||||
{
|
||||
struct timespec t0;
|
||||
struct timespec t1;
|
||||
FAR uint8_t *buf;
|
||||
ssize_t n;
|
||||
long total = 0;
|
||||
int fd;
|
||||
|
||||
buf = malloc(4096);
|
||||
if (buf == NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
fd = open(BENCH_PATH, O_RDONLY | O_CLOEXEC);
|
||||
if (fd < 0)
|
||||
{
|
||||
free(buf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &t0);
|
||||
|
||||
while ((n = read(fd, buf, 4096)) > 0)
|
||||
{
|
||||
total += n;
|
||||
}
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &t1);
|
||||
|
||||
close(fd);
|
||||
free(buf);
|
||||
|
||||
if (total <= 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return (t1.tv_sec - t0.tv_sec) * 1000 +
|
||||
(t1.tv_nsec - t0.tv_nsec) / 1000000;
|
||||
}
|
||||
|
||||
static int bench_main(void)
|
||||
{
|
||||
long before;
|
||||
long after;
|
||||
int fd;
|
||||
|
||||
syslog(LOG_INFO, "\nXIP read benchmark (%d KB)\n", BENCH_SIZE / 1024);
|
||||
|
||||
before = bench_read();
|
||||
if (before < 0)
|
||||
{
|
||||
syslog(LOG_INFO, " create %s first: "
|
||||
"dd if=/dev/zero of=%s bs=512 count=512\n",
|
||||
BENCH_PATH, BENCH_PATH);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
syslog(LOG_INFO, " before any flash write : %ld ms\n", before);
|
||||
|
||||
/* Any write forces the driver to tear XIP down and put it back */
|
||||
|
||||
fd = open(MOUNTPT "/benchw",
|
||||
O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644);
|
||||
if (fd >= 0)
|
||||
{
|
||||
write(fd, "x", 1);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
after = bench_read();
|
||||
syslog(LOG_INFO, " after a flash write : %ld ms\n", after);
|
||||
|
||||
if (before > 0)
|
||||
{
|
||||
syslog(LOG_INFO, " ratio : %ld.%02ldx\n",
|
||||
after / before, ((after * 100) / before) % 100);
|
||||
}
|
||||
|
||||
unlink(MOUNTPT "/benchw");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
static int remount(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = umount(MOUNTPT);
|
||||
if (ret < 0 && errno != ENOENT && errno != EINVAL)
|
||||
{
|
||||
return -errno;
|
||||
}
|
||||
|
||||
ret = mount(MTDDEV, MOUNTPT, "xipfs", 0, NULL);
|
||||
return ret < 0 ? -errno : 0;
|
||||
}
|
||||
|
||||
static void fill_pattern(FAR uint8_t *buf, size_t len, uint8_t seed)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
buf[i] = (uint8_t)(seed + (i * 7));
|
||||
}
|
||||
}
|
||||
|
||||
static int create_file(FAR const char *path, size_t len, uint8_t seed)
|
||||
{
|
||||
FAR uint8_t *buf;
|
||||
ssize_t nw;
|
||||
int fd;
|
||||
int ret = 0;
|
||||
|
||||
buf = malloc(len);
|
||||
if (buf == NULL)
|
||||
{
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
fill_pattern(buf, len, seed);
|
||||
|
||||
fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644);
|
||||
if (fd < 0)
|
||||
{
|
||||
free(buf);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (ftruncate(fd, len) < 0)
|
||||
{
|
||||
ret = -errno;
|
||||
}
|
||||
else
|
||||
{
|
||||
nw = write(fd, buf, len);
|
||||
if (nw != (ssize_t)len)
|
||||
{
|
||||
ret = nw < 0 ? -errno : -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
close(fd);
|
||||
free(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool verify_file(FAR const char *path, size_t len, uint8_t seed)
|
||||
{
|
||||
FAR uint8_t *got;
|
||||
FAR uint8_t *want;
|
||||
bool ok = false;
|
||||
ssize_t n;
|
||||
int fd;
|
||||
|
||||
got = malloc(len);
|
||||
want = malloc(len);
|
||||
if (got == NULL || want == NULL)
|
||||
{
|
||||
free(got);
|
||||
free(want);
|
||||
return false;
|
||||
}
|
||||
|
||||
fill_pattern(want, len, seed);
|
||||
|
||||
fd = open(path, O_RDONLY | O_CLOEXEC);
|
||||
if (fd >= 0)
|
||||
{
|
||||
n = read(fd, got, len);
|
||||
ok = (n == (ssize_t)len) && (memcmp(got, want, len) == 0);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
free(got);
|
||||
free(want);
|
||||
return ok;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: run_defrag
|
||||
*
|
||||
* Description:
|
||||
* Compact the volume. The command acts on the volume, so it goes through
|
||||
* a descriptor for the mountpoint directory: a descriptor for a file
|
||||
* inside it would hold that file open, and an open extent cannot be
|
||||
* relocated.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int run_defrag(uint32_t max_ms,
|
||||
FAR struct xipfs_defrag_result_s *result)
|
||||
{
|
||||
struct xipfs_defrag_arg_s arg;
|
||||
int fd;
|
||||
int ret;
|
||||
|
||||
memset(&arg, 0, sizeof(arg));
|
||||
arg.max_ms = max_ms;
|
||||
|
||||
fd = open(MOUNTPT, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
|
||||
if (fd < 0)
|
||||
{
|
||||
return -errno;
|
||||
}
|
||||
|
||||
ret = ioctl(fd, XIPFSIOC_DEFRAG, (unsigned long)(uintptr_t)&arg);
|
||||
close(fd);
|
||||
|
||||
if (ret < 0)
|
||||
{
|
||||
return -errno;
|
||||
}
|
||||
|
||||
*result = arg.result;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Defragmentation showcase
|
||||
*
|
||||
* Fragments the volume until an allocation genuinely cannot be satisfied --
|
||||
* plenty of free space, but none of it contiguous -- then compacts and
|
||||
* retries the same allocation. On a real board everything below runs
|
||||
* against real NOR: each move is a page-at-a-time copy, a metadata commit,
|
||||
* and an erase of the vacated blocks.
|
||||
****************************************************************************/
|
||||
|
||||
static int df_mapfill(FAR uint32_t *dstart, FAR uint32_t *dblocks)
|
||||
{
|
||||
struct xipfs_extent_info_s info;
|
||||
FAR struct dirent *de;
|
||||
FAR DIR *dirp;
|
||||
char path[80];
|
||||
int files = 0;
|
||||
uint32_t i;
|
||||
|
||||
memset(g_map, '.', sizeof(g_map));
|
||||
*dstart = 0;
|
||||
*dblocks = 0;
|
||||
|
||||
dirp = opendir(MOUNTPT);
|
||||
if (dirp == NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
while ((de = readdir(dirp)) != NULL)
|
||||
{
|
||||
snprintf(path, sizeof(path), "%s/%s", MOUNTPT, de->d_name);
|
||||
if (extent_info_path(path, &info) < 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
*dstart = info.data_start;
|
||||
*dblocks = info.data_nblocks;
|
||||
files++;
|
||||
|
||||
for (i = 0; i < info.nblocks; i++)
|
||||
{
|
||||
uint32_t idx = info.start_block - info.data_start + i;
|
||||
if (idx < sizeof(g_map))
|
||||
{
|
||||
g_map[idx] = '#';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dirp);
|
||||
return files;
|
||||
}
|
||||
|
||||
static void df_showmap(FAR const char *title, uint32_t dblocks,
|
||||
FAR uint32_t *freeout, FAR uint32_t *largestout)
|
||||
{
|
||||
uint32_t largest = 0;
|
||||
uint32_t run = 0;
|
||||
uint32_t free = 0;
|
||||
uint32_t i;
|
||||
char row[DF_MAPCOLS + 1];
|
||||
|
||||
syslog(LOG_INFO, "\n %s\n", title);
|
||||
|
||||
for (i = 0; i < dblocks; i++)
|
||||
{
|
||||
if (g_map[i] == '.')
|
||||
{
|
||||
free++;
|
||||
if (++run > largest)
|
||||
{
|
||||
largest = run;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
run = 0;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < dblocks; i += DF_MAPCOLS)
|
||||
{
|
||||
uint32_t n = dblocks - i;
|
||||
if (n > DF_MAPCOLS)
|
||||
{
|
||||
n = DF_MAPCOLS;
|
||||
}
|
||||
|
||||
memcpy(row, &g_map[i], n);
|
||||
row[n] = '\0';
|
||||
syslog(LOG_INFO, " %3" PRIu32 " |%s|\n", i, row);
|
||||
}
|
||||
|
||||
syslog(LOG_INFO, " free %" PRIu32
|
||||
" blocks, largest contiguous run %" PRIu32 "\n", free, largest);
|
||||
|
||||
if (freeout != NULL)
|
||||
{
|
||||
*freeout = free;
|
||||
}
|
||||
|
||||
if (largestout != NULL)
|
||||
{
|
||||
*largestout = largest;
|
||||
}
|
||||
}
|
||||
|
||||
static int df_try_alloc(uint32_t nblocks, uint32_t erasesize)
|
||||
{
|
||||
int fd;
|
||||
int ret;
|
||||
|
||||
unlink(MOUNTPT "/bigalloc");
|
||||
|
||||
fd = open(MOUNTPT "/bigalloc",
|
||||
O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644);
|
||||
if (fd < 0)
|
||||
{
|
||||
return -errno;
|
||||
}
|
||||
|
||||
ret = ftruncate(fd, (off_t)nblocks * erasesize);
|
||||
close(fd);
|
||||
|
||||
if (ret < 0)
|
||||
{
|
||||
ret = -errno;
|
||||
unlink(MOUNTPT "/bigalloc");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: empty_dir
|
||||
*
|
||||
* Description:
|
||||
* Delete everything below 'dir', descending into subdirectories.
|
||||
*
|
||||
* One entry is taken per pass rather than a listing being collected up
|
||||
* front, because this recurses and the task stack is the default 2 KB: an
|
||||
* array of names big enough to be useful costs more than a kilobyte a
|
||||
* frame, and two levels of that overflows and returns through a smashed
|
||||
* frame. A pass costs an opendir instead, which no one is timing.
|
||||
*
|
||||
* The directory is closed before anything is removed, since unlinking
|
||||
* while iterating skips entries.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static void empty_dir(FAR const char *dir)
|
||||
{
|
||||
char path[80];
|
||||
bool more = true;
|
||||
|
||||
while (more)
|
||||
{
|
||||
FAR struct dirent *de;
|
||||
FAR DIR *dirp;
|
||||
bool isdir;
|
||||
|
||||
dirp = opendir(dir);
|
||||
if (dirp == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
de = readdir(dirp);
|
||||
if (de == NULL)
|
||||
{
|
||||
closedir(dirp);
|
||||
return;
|
||||
}
|
||||
|
||||
isdir = (de->d_type == DTYPE_DIRECTORY);
|
||||
snprintf(path, sizeof(path), "%s/%s", dir, de->d_name);
|
||||
closedir(dirp);
|
||||
|
||||
if (isdir)
|
||||
{
|
||||
empty_dir(path);
|
||||
more = rmdir(path) == 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
more = unlink(path) == 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int defrag_main(void)
|
||||
{
|
||||
struct xipfs_defrag_result_s res;
|
||||
struct xipfs_extent_info_s info;
|
||||
struct timespec t0;
|
||||
struct timespec t1;
|
||||
uint32_t dstart;
|
||||
uint32_t dblocks;
|
||||
uint32_t erasesize = 4096;
|
||||
uint32_t want;
|
||||
uint32_t freeblk = 0;
|
||||
uint32_t largest = 0;
|
||||
char path[80];
|
||||
long ms;
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
memset(&res, 0, sizeof(res));
|
||||
|
||||
syslog(LOG_INFO, "\n=== xipfs defragmentation ===\n");
|
||||
|
||||
/* Clean slate, so the map below accounts for every used block */
|
||||
|
||||
empty_dir(MOUNTPT);
|
||||
|
||||
syslog(LOG_INFO, "\n[1] writing %d files...\n", DF_FILES);
|
||||
|
||||
for (i = 0; i < DF_FILES; i++)
|
||||
{
|
||||
snprintf(path, sizeof(path), MOUNTPT "/m%02d", i);
|
||||
if (create_file(path, 2000, (uint8_t)(i + 1)) < 0)
|
||||
{
|
||||
syslog(LOG_INFO, " stopped at %d (volume full)\n", i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (extent_info_path(MOUNTPT "/m00", &info) == 0)
|
||||
{
|
||||
erasesize = info.erasesize;
|
||||
}
|
||||
|
||||
df_mapfill(&dstart, &dblocks);
|
||||
df_showmap("map, all files present:", dblocks, NULL, NULL);
|
||||
|
||||
syslog(LOG_INFO, "\n[2] deleting every other file to fragment it...\n");
|
||||
|
||||
for (i = 0; i < DF_FILES; i += 2)
|
||||
{
|
||||
snprintf(path, sizeof(path), MOUNTPT "/m%02d", i);
|
||||
unlink(path);
|
||||
}
|
||||
|
||||
df_mapfill(&dstart, &dblocks);
|
||||
df_showmap("map, fragmented (# used, . free):", dblocks,
|
||||
&freeblk, &largest);
|
||||
|
||||
/* Bigger than any single hole, but comfortably inside the total free
|
||||
* space -- so it can only be satisfied once the holes are coalesced.
|
||||
*/
|
||||
|
||||
want = largest + (freeblk - largest) / 2;
|
||||
|
||||
syslog(LOG_INFO, "\n[3] requesting a %" PRIu32
|
||||
"-block (%" PRIu32 " KB) contiguous file\n", want,
|
||||
want * erasesize / 1024);
|
||||
|
||||
ret = df_try_alloc(want, erasesize);
|
||||
syslog(LOG_INFO, " result: %s\n",
|
||||
ret < 0 ? "FAILED -ENOSPC (free space is not contiguous)" : "ok");
|
||||
|
||||
syslog(LOG_INFO, "\n[4] compacting...\n");
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &t0);
|
||||
ret = run_defrag(0, &res);
|
||||
clock_gettime(CLOCK_MONOTONIC, &t1);
|
||||
|
||||
ms = (t1.tv_sec - t0.tv_sec) * 1000 + (t1.tv_nsec - t0.tv_nsec) / 1000000;
|
||||
|
||||
if (ret < 0)
|
||||
{
|
||||
syslog(LOG_INFO, " defrag failed: %d\n", ret);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
syslog(LOG_INFO, " elapsed : %ld ms\n", ms);
|
||||
syslog(LOG_INFO, " extents relocated: %" PRIu32 "\n",
|
||||
res.extents_moved);
|
||||
syslog(LOG_INFO, " blocks reclaimed : %" PRIu32 "\n",
|
||||
res.blocks_reclaimed);
|
||||
syslog(LOG_INFO, " largest free run : %zu bytes (%zu blocks)\n",
|
||||
res.largest_free_run, res.largest_free_run / erasesize);
|
||||
syslog(LOG_INFO, " stop reason : %d (%s)\n", res.reason,
|
||||
res.reason == XIPFS_DEFRAG_DONE ? "done" :
|
||||
res.reason == XIPFS_DEFRAG_BLOCKED_OPEN ? "blocked by an open "
|
||||
"file" :
|
||||
res.reason == XIPFS_DEFRAG_BLOCKED_PINS ? "blocked by a live "
|
||||
"XIP mapping" :
|
||||
"other");
|
||||
|
||||
df_mapfill(&dstart, &dblocks);
|
||||
df_showmap("map after defrag:", dblocks, NULL, NULL);
|
||||
|
||||
want = res.largest_free_run / erasesize;
|
||||
|
||||
syslog(LOG_INFO, "\n[5] retrying, sized to the reported largest run"
|
||||
" (%" PRIu32 " blocks)\n", want);
|
||||
ret = df_try_alloc(want, erasesize);
|
||||
syslog(LOG_INFO, " result: %s\n",
|
||||
ret < 0 ? "still failed" : "OK -- allocation now fits");
|
||||
unlink(MOUNTPT "/bigalloc");
|
||||
|
||||
syslog(LOG_INFO, "\n[6] verifying every relocated file byte-for-byte\n");
|
||||
|
||||
ret = 0;
|
||||
for (i = 1; i < DF_FILES; i += 2)
|
||||
{
|
||||
snprintf(path, sizeof(path), MOUNTPT "/m%02d", i);
|
||||
if (!verify_file(path, 2000, (uint8_t)(i + 1)))
|
||||
{
|
||||
syslog(LOG_INFO, " MISMATCH in %s\n", path);
|
||||
ret = -1;
|
||||
}
|
||||
}
|
||||
|
||||
syslog(LOG_INFO, " %s\n", ret == 0 ? "all survivors intact" : "FAILED");
|
||||
|
||||
syslog(LOG_INFO, "\n[7] remounting to prove it is durable\n");
|
||||
if (remount() < 0)
|
||||
{
|
||||
syslog(LOG_INFO, " remount FAILED\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
for (i = 1; i < DF_FILES; i += 2)
|
||||
{
|
||||
snprintf(path, sizeof(path), MOUNTPT "/m%02d", i);
|
||||
if (!verify_file(path, 2000, (uint8_t)(i + 1)))
|
||||
{
|
||||
ret = -1;
|
||||
}
|
||||
}
|
||||
|
||||
syslog(LOG_INFO, " %s\n",
|
||||
ret == 0 ? "all files intact after remount" : "FAILED");
|
||||
|
||||
syslog(LOG_INFO, "\n=== defrag complete ===\n");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
int main(int argc, FAR char *argv[])
|
||||
{
|
||||
struct xipfs_extent_info_s info;
|
||||
FAR char *args[3];
|
||||
char seed[2][8];
|
||||
pid_t pid[2];
|
||||
int status;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
if (argc > 1 && strcmp(argv[1], "bench") == 0)
|
||||
{
|
||||
return bench_main();
|
||||
}
|
||||
|
||||
if (argc > 1 && strcmp(argv[1], "defrag") == 0)
|
||||
{
|
||||
return defrag_main();
|
||||
}
|
||||
|
||||
syslog(LOG_INFO,
|
||||
"\n=== NXFLAT module executed in place from xipfs ===\n\n");
|
||||
|
||||
ret = stage_module();
|
||||
if (ret < 0)
|
||||
{
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
ret = extent_info_path(MODPATH, &info);
|
||||
if (ret < 0)
|
||||
{
|
||||
syslog(LOG_INFO, "ERROR: cannot query extent: %d\n", ret);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
syslog(LOG_INFO, "extent: block %" PRIu32 " x%" PRIu32 ", size %" PRIu32
|
||||
", flash addr 0x%08" PRIxPTR "\n",
|
||||
info.start_block, info.nblocks, info.size, info.xipaddr);
|
||||
|
||||
if (info.xipaddr == 0)
|
||||
{
|
||||
syslog(LOG_INFO,
|
||||
"ERROR: the file system cannot expose a flash pointer, so the "
|
||||
"module would have to be copied to RAM. Does the MTD driver "
|
||||
"answer BIOC_XIPBASE?\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* Launch two instances. They overlap deliberately: each fills its array,
|
||||
* sleeps, then checks it. Shared data would show up as a mismatch.
|
||||
*/
|
||||
|
||||
syslog(LOG_INFO, "\nrunning two concurrent instances...\n\n");
|
||||
|
||||
for (i = 0; i < 2; i++)
|
||||
{
|
||||
snprintf(seed[i], sizeof(seed[i]), "%d", i + 1);
|
||||
|
||||
args[0] = (FAR char *)"xipmod";
|
||||
args[1] = seed[i];
|
||||
args[2] = NULL;
|
||||
|
||||
/* exec() rather than posix_spawn() because the symbol table the
|
||||
* module links against is the demo's, not one the OS holds.
|
||||
*/
|
||||
|
||||
pid[i] = exec(MODPATH, args, NULL, g_nxflat_exports,
|
||||
g_nxflat_nexports);
|
||||
if (pid[i] < 0)
|
||||
{
|
||||
syslog(LOG_INFO, "ERROR: exec of instance %d failed: %d\n", i + 1,
|
||||
pid[i]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
/* While both are alive the shared text must be pinned once per instance,
|
||||
* which is what stops the defragmenter relocating code that is executing.
|
||||
*/
|
||||
|
||||
usleep(100000);
|
||||
|
||||
if (extent_info_path(MODPATH, &info) == 0)
|
||||
{
|
||||
syslog(LOG_INFO,
|
||||
"\n[while running] pin count on the shared text = %" PRIu32
|
||||
"\n\n", info.pincount);
|
||||
}
|
||||
|
||||
for (i = 0; i < 2; i++)
|
||||
{
|
||||
if (waitpid(pid[i], &status, 0) >= 0 &&
|
||||
(!WIFEXITED(status) || WEXITSTATUS(status) != EXIT_SUCCESS))
|
||||
{
|
||||
syslog(LOG_INFO, "instance %d did not exit cleanly\n", i + 1);
|
||||
ret = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* The pins outlive the instances. binfmt maps the text in the context of
|
||||
* whoever called exec(), so the mappings belong to this task's address
|
||||
* space, not to the tasks that ran, and they are released when this task
|
||||
* exits. Run "xipfs -n" after this demo returns to see the count back at
|
||||
* zero and the extent movable again.
|
||||
*/
|
||||
|
||||
if (extent_info_path(MODPATH, &info) == 0)
|
||||
{
|
||||
syslog(LOG_INFO,
|
||||
"[after exit] pin count on the shared text = %" PRIu32
|
||||
" (released when this task exits)\n", info.pincount);
|
||||
}
|
||||
|
||||
syslog(LOG_INFO, "\n=== %s ===\n", ret == 0 ? "done" : "FAILED");
|
||||
return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue