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
24 changes: 24 additions & 0 deletions docs/reference/mpremote.rst
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ The full list of supported commands are:
- `mip <mpremote_command_mip>`
- `mount <mpremote_command_mount>`
- `unmount <mpremote_command_unmount>`
- `romfs <mpremote_command_romfs>`
- `rtc <mpremote_command_rtc>`
- `sleep <mpremote_command_sleep>`
- `reset <mpremote_command_reset>`
Expand Down Expand Up @@ -347,6 +348,29 @@ The full list of supported commands are:
This happens automatically when ``mpremote`` terminates, but it can be used
in a sequence to unmount an earlier mount before subsequent command are run.

.. _mpremote_command_romfs:

- **romfs** -- manage ROMFS partitions on the device:

.. code-block:: bash

$ mpremote romfs <sub-command>

``<sub-command>`` may be:

- ``romfs query`` to list all the available ROMFS partitions and their size
- ``romfs [-o <output>] build <source>`` to create a ROMFS image from the given
source directory; the default output file is the source appended by ``.romfs``
- ``romfs [-p <partition>] deploy <source>`` to deploy a ROMFS image to the device;
will also create a temporary ROMFS image if the source is a directory

The ``build`` and ``deploy`` sub-commands both support the ``-m``/``--mpy`` option
to automatically compile ``.py`` files to ``.mpy`` when creating the ROMFS image.
This option is enabled by default, but only works if the ``mpy_cross`` Python
package has been installed (eg via ``pip install mpy_cross``). If the package is
not installed then a warning is printed and ``.py`` files remain as is. Compiling
of ``.py`` files can be disabled with the ``--no-mpy`` option.

.. _mpremote_command_rtc:

- **rtc** -- set/get the device clock (RTC):
Expand Down
7 changes: 7 additions & 0 deletions extmod/modvfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,18 @@
#error "MICROPY_PY_VFS requires MICROPY_VFS"
#endif

#if MICROPY_VFS_ROM_IOCTL
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_vfs_rom_ioctl_obj, 1, 4, mp_vfs_rom_ioctl);
#endif

static const mp_rom_map_elem_t vfs_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_vfs) },

{ MP_ROM_QSTR(MP_QSTR_mount), MP_ROM_PTR(&mp_vfs_mount_obj) },
{ MP_ROM_QSTR(MP_QSTR_umount), MP_ROM_PTR(&mp_vfs_umount_obj) },
#if MICROPY_VFS_ROM_IOCTL
{ MP_ROM_QSTR(MP_QSTR_rom_ioctl), MP_ROM_PTR(&mp_vfs_rom_ioctl_obj) },
#endif
#if MICROPY_VFS_FAT
{ MP_ROM_QSTR(MP_QSTR_VfsFat), MP_ROM_PTR(&mp_fat_vfs_type) },
#endif
Expand Down
30 changes: 30 additions & 0 deletions extmod/vfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@
#include "extmod/vfs_posix.h"
#endif

#if MICROPY_VFS_ROM && MICROPY_VFS_ROM_IOCTL
#include "extmod/vfs_rom.h"
#endif

// For mp_vfs_proxy_call, the maximum number of additional args that can be passed.
// A fixed maximum size is used to avoid the need for a costly variable array.
#define PROXY_MAX_ARGS (2)
Expand Down Expand Up @@ -552,6 +556,32 @@ int mp_vfs_mount_and_chdir_protected(mp_obj_t bdev, mp_obj_t mount_point) {
return ret;
}

#if MICROPY_VFS_ROM && MICROPY_VFS_ROM_IOCTL

int mp_vfs_mount_romfs_protected(void) {
int ret;
nlr_buf_t nlr;
if (nlr_push(&nlr) == 0) {
mp_obj_t args[2] = { MP_OBJ_NEW_SMALL_INT(MP_VFS_ROM_IOCTL_GET_SEGMENT), MP_OBJ_NEW_SMALL_INT(0) };
mp_obj_t rom = mp_vfs_rom_ioctl(2, args);
mp_obj_t romfs = mp_call_function_1(MP_OBJ_FROM_PTR(&mp_type_vfs_rom), rom);
mp_obj_t mount_point = MP_OBJ_NEW_QSTR(MP_QSTR__slash_rom);
mp_call_function_2(MP_OBJ_FROM_PTR(&mp_vfs_mount_obj), romfs, mount_point);
#if MICROPY_PY_SYS_PATH_ARGV_DEFAULTS
// Add "/rom" and "/rom/lib" to `sys.path`.
mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR__slash_rom));
mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR__slash_rom_slash_lib));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I got caught out that I couldn't import a Python module from the top-level of the rootfs without doing sys.path.append('/rom'). How about adding both /rom and /rom/lib to sys.path by default?

(I'm thinking along the lines that the top-level is the application code either as .py files or package directories, and /rom/lib is managed by some future variant of 'mip install'.)

I don't really see a downside - I guess someone might decide to put a lot of non-Python assets in the top level and slow down import a little, but that seems like a much less common pattern than putting a Python file there.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding to the search path, ie sys.path, really does slow down imports. Because you need to stat .py and .mpy for each import (assuming the first fails), and then for packages you also need to stat __init__.py and __init__.mpy. This is something we need to optimise in general.

So, I really wanted to keep sys.path down to a minimum. I did start out adding just /rom to the path, but after discussion with you and Jim, changed that to /rom/lib.

The user can easily change the path themselves.

Note that neither boot.py nor main.py will execute from the ROMFS, because those boot up files are searched in the current directory.

I'd really like to improve all this in MicroPython 2.0. But for now... maybe just keep it as /rom/lib and document it? Or change it to '/rom`? Or just omit altogether and make the user add it as they need?

Side note: mip install won't work with /rom/lib in the path before /flash/lib, because it tries to install into the ROMFS...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess a key question is what behaviour we want for (eventually) installing MIP packages into the ROMFS. I'd figured that it's desirable to keep them separated somewhere, so it's easy for the developer to see the difference between "my application" and "libraries I've installed from elsewhere". So my thinking was that /rom/lib would be the natural place to put the externa llibraries, as it mirrors the way it works now.

I don't think there's any way to have this distinction and only add one entry to the path, though...

Or just omit altogether and make the user add it as they need?

On balance I think it's preferable to have helpful/usable defaults. Then the developer who wants to optimise their package import speed can be the one who tweaks sys.path to remove entries they don't need.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(But if we're going with one default entry only, probably /rom is less surprising for people building ROMFS from a directory and then using it on their board.)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On balance I think it's preferable to have helpful/usable defaults. Then the developer who wants to optimise their package import speed can be the one who tweaks sys.path to remove entries they don't need

Yes, that's a good point. So maybe then the defaults can be /rom and /rom/lib, which is how we recommend to layout a project. Then users can tweak as needed.

I'll do a few benchmarks to see if adding a second path makes much of a difference.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both /rom and /rom/lib are now added automatically to sys.path.

#endif
ret = 0; // success
nlr_pop();
} else {
ret = -MP_EIO;
}
return ret;
}

#endif

MP_REGISTER_ROOT_POINTER(struct _mp_vfs_mount_t *vfs_cur);
MP_REGISTER_ROOT_POINTER(struct _mp_vfs_mount_t *vfs_mount_table);

Expand Down
18 changes: 18 additions & 0 deletions extmod/vfs.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,13 @@
#define MP_BLOCKDEV_IOCTL_BLOCK_SIZE (5)
#define MP_BLOCKDEV_IOCTL_BLOCK_ERASE (6)

// Constants for vfs.rom_ioctl() function.
#define MP_VFS_ROM_IOCTL_GET_NUMBER_OF_SEGMENTS (1) // rom_ioctl(1)
#define MP_VFS_ROM_IOCTL_GET_SEGMENT (2) // rom_ioctl(2, <id>)
#define MP_VFS_ROM_IOCTL_WRITE_PREPARE (3) // rom_ioctl(3, <id>, <len>)
#define MP_VFS_ROM_IOCTL_WRITE (4) // rom_ioctl(4, <id>, <offset>, <buf>)
#define MP_VFS_ROM_IOCTL_WRITE_COMPLETE (5) // rom_ioctl(5, <id>)

// At the moment the VFS protocol just has import_stat, but could be extended to other methods
typedef struct _mp_vfs_proto_t {
mp_import_stat_t (*import_stat)(void *self, const char *path);
Expand Down Expand Up @@ -105,6 +112,9 @@ mp_obj_t mp_vfs_stat(mp_obj_t path_in);
mp_obj_t mp_vfs_statvfs(mp_obj_t path_in);

int mp_vfs_mount_and_chdir_protected(mp_obj_t bdev, mp_obj_t mount_point);
#if MICROPY_VFS_ROM && MICROPY_VFS_ROM_IOCTL
int mp_vfs_mount_romfs_protected(void);
#endif

MP_DECLARE_CONST_FUN_OBJ_KW(mp_vfs_mount_obj);
MP_DECLARE_CONST_FUN_OBJ_1(mp_vfs_umount_obj);
Expand All @@ -122,4 +132,12 @@ MP_DECLARE_CONST_FUN_OBJ_1(mp_vfs_rmdir_obj);
MP_DECLARE_CONST_FUN_OBJ_1(mp_vfs_stat_obj);
MP_DECLARE_CONST_FUN_OBJ_1(mp_vfs_statvfs_obj);

#if MICROPY_VFS_ROM_IOCTL
// When MICROPY_VFS_ROM_IOCTL is enabled a port must define the following function.
// This is a generic interface to allow querying and modifying the user-accessible,
// read-only memory area of a device, if it is configured with such an area.
// Supported ioctl commands are given by MP_VFS_ROM_IOCTL_xxx.
mp_obj_t mp_vfs_rom_ioctl(size_t n_args, const mp_obj_t *args);
#endif

#endif // MICROPY_INCLUDED_EXTMOD_VFS_H
76 changes: 76 additions & 0 deletions ports/esp32/esp32_partition.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,20 @@ typedef struct _esp32_partition_obj_t {
uint16_t block_size;
} esp32_partition_obj_t;

#if MICROPY_VFS_ROM_IOCTL

static esp32_partition_obj_t esp32_partition_romfs_obj = {
.base = { .type = NULL },
.part = NULL,
.cache = NULL,
.block_size = NATIVE_BLOCK_SIZE_BYTES,
};

static const void *esp32_partition_romfs_ptr = NULL;
static esp_partition_mmap_handle_t esp32_partition_romfs_handle;

#endif

static esp32_partition_obj_t *esp32_partition_new(const esp_partition_t *part, uint16_t block_size) {
if (part == NULL) {
mp_raise_OSError(MP_ENOENT);
Expand Down Expand Up @@ -114,6 +128,24 @@ static mp_obj_t esp32_partition_make_new(const mp_obj_type_t *type, size_t n_arg
return MP_OBJ_FROM_PTR(esp32_partition_new(part, block_size));
}

#if MICROPY_VFS_ROM_IOCTL
static mp_int_t esp32_partition_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) {
esp32_partition_obj_t *self = MP_OBJ_TO_PTR(self_in);
if (self == &esp32_partition_romfs_obj && flags == MP_BUFFER_READ) {
if (esp32_partition_romfs_ptr == NULL) {
check_esp_err(esp_partition_mmap(self->part, 0, self->part->size, ESP_PARTITION_MMAP_DATA, &esp32_partition_romfs_ptr, &esp32_partition_romfs_handle));
}
bufinfo->buf = (void *)esp32_partition_romfs_ptr;
bufinfo->len = self->part->size;
bufinfo->typecode = 'B';
return 0;
} else {
// Unsupported.
return 1;
}
}
#endif

static mp_obj_t esp32_partition_find(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
// Parse args
enum { ARG_type, ARG_subtype, ARG_label, ARG_block_size };
Expand Down Expand Up @@ -284,11 +316,55 @@ static const mp_rom_map_elem_t esp32_partition_locals_dict_table[] = {
};
static MP_DEFINE_CONST_DICT(esp32_partition_locals_dict, esp32_partition_locals_dict_table);

#if MICROPY_VFS_ROM_IOCTL
#define ESP32_PARTITION_GET_BUFFER buffer, esp32_partition_get_buffer,
#else
#define ESP32_PARTITION_GET_BUFFER
#endif

MP_DEFINE_CONST_OBJ_TYPE(
esp32_partition_type,
MP_QSTR_Partition,
MP_TYPE_FLAG_NONE,
make_new, esp32_partition_make_new,
print, esp32_partition_print,
ESP32_PARTITION_GET_BUFFER
locals_dict, &esp32_partition_locals_dict
);

/******************************************************************************/
// romfs partition

#if MICROPY_VFS_ROM_IOCTL

mp_obj_t mp_vfs_rom_ioctl(size_t n_args, const mp_obj_t *args) {
if (esp32_partition_romfs_obj.base.type == NULL) {
esp32_partition_romfs_obj.base.type = &esp32_partition_type;
// Get the romfs partition.
// TODO: number of segments ioctl can be used if there is more than one romfs.
esp_partition_iterator_t iter = esp_partition_find(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "romfs");
if (iter != NULL) {
esp32_partition_romfs_obj.part = esp_partition_get(iter);
}
esp_partition_iterator_release(iter);
}

switch (mp_obj_get_int(args[0])) {
case MP_VFS_ROM_IOCTL_GET_NUMBER_OF_SEGMENTS:
if (esp32_partition_romfs_obj.part == NULL) {
return MP_OBJ_NEW_SMALL_INT(0);
} else {
return MP_OBJ_NEW_SMALL_INT(1);
}
case MP_VFS_ROM_IOCTL_GET_SEGMENT:
if (esp32_partition_romfs_obj.part == NULL) {
return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL);
} else {
return MP_OBJ_FROM_PTR(&esp32_partition_romfs_obj);
}
default:
return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL);
}
}

#endif // MICROPY_VFS_ROM_IOCTL
8 changes: 8 additions & 0 deletions ports/esp32/partitions-4MiB-romfs.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Notes: the offset of the partition table itself is set in
# $IDF_PATH/components/partition_table/Kconfig.projbuild.
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x6000,
phy_init, data, phy, 0xf000, 0x1000,
factory, app, factory, 0x10000, 0x1D0000,
romfs, data, 0x8f, 0x1E0000, 0x20000,
vfs, data, fat, 0x200000, 0x200000,
1 change: 1 addition & 0 deletions ports/esp8266/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ SRC_C = \
fatfs_port.c \
posix_helpers.c \
hspi.c \
vfs_rom_ioctl.c \
$(wildcard $(BOARD_DIR)/*.c) \

ifeq ($(MICROPY_PY_ESPNOW),1)
Expand Down
3 changes: 2 additions & 1 deletion ports/esp8266/boards/ESP8266_GENERIC/board.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
"variants": {
"OTA": "OTA compatible",
"FLASH_1M": "1MiB flash",
"FLASH_512K": "512kiB flash"
"FLASH_512K": "512kiB flash",
"FLASH_2M_ROMFS": "2MiB flash with ROMFS"
},
"vendor": "Espressif"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
LD_FILES = boards/esp8266_2MiB_ROMFS.ld

MICROPY_PY_ESPNOW ?= 1
MICROPY_PY_BTREE ?= 1
MICROPY_VFS_FAT ?= 1
MICROPY_VFS_LFS2 ?= 1

# Add asyncio and extra micropython-lib packages (in addition to the port manifest).
FROZEN_MANIFEST ?= $(BOARD_DIR)/manifest_2MiB.py

# Configure mpconfigboard.h.
CFLAGS += -DMICROPY_ESP8266_2M -DMICROPY_VFS_ROM=1
24 changes: 24 additions & 0 deletions ports/esp8266/boards/esp8266_2MiB_ROMFS.ld
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/* GNU linker script for ESP8266 with 2M or more flash, and includes a ROMFS partition

Flash layout:
0x40200000 36k header + iram/dram init
0x40209000 668k firmware (irom0)
0x402c0000 320k ROMFS
0x40300000 1M+ filesystem (not memory mapped)
*/

MEMORY
{
dport0_0_seg : org = 0x3ff00000, len = 16
dram0_0_seg : org = 0x3ffe8000, len = 80K
iram1_0_seg : org = 0x40100000, len = 32K
irom0_0_seg : org = 0x40209000, len = 1M - 36K - 320K
FLASH_ROMFS : org = 0x402b0000, len = 320K
}

/* define ROMFS extents */
_micropy_hw_romfs_start = ORIGIN(FLASH_ROMFS);
_micropy_hw_romfs_size = LENGTH(FLASH_ROMFS);

/* define common sections and symbols */
INCLUDE boards/esp8266_common.ld
1 change: 1 addition & 0 deletions ports/esp8266/boards/esp8266_common.ld
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ SECTIONS
*modonewire.o(.literal* .text*)
*network_wlan.o(.literal* .text*)
*esp_mphal.o(.literal* .text*)
*vfs_rom_ioctl.o(.literal* .text*)

/* we put as much rodata as possible in this section */
/* note that only rodata accessed as a machine word is allowed here */
Expand Down
9 changes: 8 additions & 1 deletion ports/esp8266/modesp.c
Original file line number Diff line number Diff line change
Expand Up @@ -164,9 +164,16 @@ static MP_DEFINE_CONST_FUN_OBJ_0(esp_flash_size_obj, esp_flash_size);
#define IS_OTA_FIRMWARE() ((*(uint32_t *)0x40200000 & 0xff00) == 0x100)

extern byte _firmware_size[];
#if MICROPY_VFS_ROM_IOCTL
extern uint8_t _micropy_hw_romfs_size;
#endif

static mp_obj_t esp_flash_user_start(void) {
return MP_OBJ_NEW_SMALL_INT((uint32_t)_firmware_size);
uint32_t flash_user_start = (uint32_t)_firmware_size;
#if MICROPY_VFS_ROM_IOCTL
flash_user_start += (uint32_t)&_micropy_hw_romfs_size;
#endif
return MP_OBJ_NEW_SMALL_INT(flash_user_start);
}
static MP_DEFINE_CONST_FUN_OBJ_0(esp_flash_user_start_obj, esp_flash_user_start);

Expand Down
Loading
Loading