Yeah so the idea was to have two volumes, one system volume that is read-write for epicardium but only exposed on USB from the bootloader and a user volume that can be exposed from both bootloader and epicardium that is accessible by l0dables and pycardium. Since "USB Storage" and running an app is mutually exclusive, we would not have to lock access to that second volume.
To sum up:
bootloader exposes both volumes
epicardium either runs user code or exposes the second volume
since system volume is never exposed on USB by epicardium, no access control is needed there.
since user volume is only exposed when no user application is running, no access control is needed there.
but
I've been looking at our options. libff does support multiple volumes either as separate partitions on a single physical volume, or as multiple physical volumes with only one partition on each of them. For us it sounds best to go the first route, but the caveat is: windows only supports one partition per volume on USB mass storage. This means, for the bootloader to be able to expose system and user volumes, we would have to provide two mass storage devices via USB, which would, on first glance, incur a non-trivial amount of changes.
So back to global locking: I'm not sure it is the best solution and I'm also not sure if it is needed at all... Looking at the program flow, we see that, in epicardium, the "USB storage" mode will only be left by pressing the reset button. The reset button causes the actions "stop current app" and "deinit peripherals". Both of these actions, coming from "USB storage", are mostly no-ops AFAICT.
So the question is: could we just have "USB storage" enter the bootloader in a mode where it unconditionally exposes the USB mass storage (place marker in non-volatile memory, then reset)?
While I'm been typing this, I keep coming back to this idea: why not have a bootloader option in non-volatile memory, that does either:
normal boot procedure (no valid option present in NV memory)
expose USB mass storage
enter sleep mode
shut down completely ?
shortcut to main.py / menu.py
To prevent boot-loops, the first order of action for the bootloader is to load the option from NV memory and clear it directly afterwards. This will ensure the normal boot procedure will trigger and a debugger can be attached.
Regarding NV memory: I'm not sure if there are RAM regions or maybe even registers that can survive a soft reset but if so, I'd say that would be the way to go.
So according to MAX32665-MAX32668 User Guide chapter 4.3, all of peripheral reset, soft reset and system reset keep RAM unaffected so we could in theory place some sort of argc+argv+integrity check thereof into RAM and select boot modes that way.
So the question is: could we just have "USB storage" enter the bootloader in a mode where it unconditionally exposes the USB mass storage (place marker in non-volatile memory, then reset)?
I would prefer keeping the bootloader out of this for two main reasons: First of all, this would mean part of the normal badge operation is dependent on the bootloader. If we find a bug in the USB Mass-Storage code, we would have to instruct everyone to update the bootloader. This is something we need to avoid as much as possible and unfortunately bugs in the MAXUSB stack seem to be quite likely.
The other issue I see is that opening USB this way would kill everything running in Epicardium. That is, any BLE connections and associated state is dropped, l0dables/apps which have volatile state will restart from scratch, and sensors cannot continue running measurements in the background.
I feel like reboots should be reserved to reset the entire badge's state and be a last-resort method if all else fails. They are also comparatively slow because we need to reinitialize all peripherals from scratch every time.
I'm not sure it is the best solution and I'm also not sure if it is needed at all... Looking at the program flow, we see that, in epicardium, the "USB storage" mode will only be left by pressing the reset button.
I think the incompleteness of the flow-diagram is hiding some things here. You are correct that there will most likely not be a user-app running while USB-Storage is opened. But we have other sources of file-system activity which might interfere. One example would be BLE file-transfers. While we can probably manually disable all these when starting USB-Storage, I am afraid we will accidentally introduce some regression related to this at some point. I feel like an explicit lock is the only way to be sure all misuse will result in an explicit error.
At the very beginning you mention
Yeah so the idea was to have two volumes, one system volume that is read-write for epicardium but only exposed on USB from the bootloader and a user volume that can be exposed from both bootloader and epicardium that is accessible by l0dables and pycardium.
I am not sure I am following what the intention of this split is. What kind of data would be stored on the system volume?
Welp, the more I think about the topic, the more I am unsure where to split... the original idea is to forego the need for locking by having files that need to be accessible from epicardium on the system volume and others that users would want to exchange via USB be on the user volume. An extension of this would be to have system files (card10.bin update) inaccessible from user code, since only the user volume would be exposed during run time.
But yeah, which files go where and does that make sense? It seemed like a great idea a few days ago... ;)
I feel like an explicit lock is the only way to be sure all misuse will result in an explicit error.
I've done the first steps towards this in !67 (merged) next step is to make this thread-safe and then to add a FreeRTOS task for the MSC part.
Don't spend too much effort on the Epicardium API side of this, as it is closely related to the Lifecycle API (#61 (closed)). I think it makes more sense to integrate it into there once that comes to life.