tock icon indicating copy to clipboard operation
tock copied to clipboard

Suitability for a smartwatch environment

Open philpax opened this issue 3 years ago • 14 comments

Hi there!

I've been thinking about the viability of porting Tock to the PineTime as it would enable a variety of interesting features (notably not having to link applications into the firmware).

I've had a brief look through the repository and have read the original paper, and Tock seems like it'd be an ideal fit (once the appropriate hardware bringup is done). However, I have a few concerns:

  1. Can applications be started at runtime by other applications? e.g. can a smartwatch launcher app launch other applications? It doesn't look like there has been any movement on the tracking issue #1138.
  2. Can applications be loaded from external flash? The PineTime has 4MB of Flash where we would ideally be keeping all non-OS data.
  3. Can the firmware and/or applications be updated wirelessly? I believe the former may be a function of the chipset (the nRF52), so I'm not too concerned about that. It's more the latter I'm interested in; if 1) and 2) are possible, then it seems like application updating should be possible through a service that accepts incoming applications and writes them to the filesystem.

Even if 2 or 3) aren't possible, I might attempt a port just to see what it's like (and because the nRF52-DK support should greatly ease the bringup process). Not being able to start applications without tockloader would be a problem, though... 😅

Thanks in advance! 🙂

philpax avatar Sep 25 '21 23:09 philpax

Hi @philpax!

I think that, without knowing too much of the specifics of the PineTime hardware, however it being based on the nRF52832, it does sound very feasible to be able to run Tock on this device. It'd also be a subjectively cool application, given the Tock model of mutually distrustful applications plus the automatic low-power modes might be a reasonable fit for a smartwatch. There appears to be an effort in this direction already, though judging from the Git revision it's aged a bit: https://github.com/kompass/tock-pinetime

To answer your questions:

Can applications be started at runtime by other applications? e.g. can a smartwatch launcher app launch other applications?

and

Can applications be loaded from external flash? The PineTime has 4MB of Flash where we would ideally be keeping all non-OS data.

To my knowledge, with only upstream code it's not yet possible to do these things, however we'd likely want to support them to some extend. I suggest you have a look at the load_processes function and how it interacts with the kernel to get a good understanding of Tock's process model, plus the documentation (Userland, Memory Layout, Startup).

Specifically, it might be a good start to look at the process console capsule, which currently allows users to exercise some basic control over the Tock kernel and applications from a console. A similar system call driver capsule could expose some of these features to other privileged userspace processes.

All upstream boards currently work by executing apps directly from flash (i.e. flash mapped into the MCUs address space as read-execute). To comply with this model, some boards which don't even have flash (such as the LiteX Arty-A7 FPGA platform) simply dedicate a section of their RAM to flash. Something like this might work for loading processes from an external flash as well: depending on how much RAM is available for this purpose, before the kernel starts or before a context switch to userspace, load a process binary from external flash into RAM. Getting this to work would likely require some modifications to the Tock kernel.

Can the firmware and/or applications be updated wirelessly?

I believe that is something which @bradjc is working on. Maybe he can elaborate further.


I suggest that, before looking into these more tricky issues, it'd be a good start to get Tock running on the PineTime in the first place. Also note that while the nRFs do have Bluetooth LE, they do rely on a proprietary Nordic SoftDevice for implementing a non-trivial part of that stack. Tock currently supports passive BLE scanning and advertising on these chips.

lschuermann avatar Sep 27 '21 15:09 lschuermann

Hi Leon!

Thanks for the very in-depth answer, it's given me much to think about, and much to investigate (hence the late reply). Much appreciated!

I agree about Tock being a neat fit for the application - with the name, it's almost as if it were meant to be 😄 I had also seen that repository, but it doesn't appear like any work has been done in the year and a half since. It seems like it would be easier to start from scratch, especially with Tock 2.0 having been released in the meantime.

I agree that a port of Tock to the PineTime should be done before tackling any of the big-ticket issues, but I wanted to make sure that the features I desired were at least theoretically possible before committing time to a port 😅 Luckily, the PineTime is not too dissimilar from a NRF52DK, so initial bringup shouldn't be too painful once the requisite drivers are written.


Your pointers to load_processes, the process model documentation, and especially the process console capsule are much appreciated[^1]. It looks like processes are loaded into a static, fixed-size array, and each of these processes can be individually toggled on and off. That'll work for an initial proof of concept, but dynamic loading from flash is likely necessary as the current OS for the PineTime already has around a dozen "applications" (integrated into the firmware, so overhead is limited.)

I have a few ideas around how dynamic loading might be done, but it's definitely something that I would need to consult with the Tock team on. My understanding of load_processes is that it scans through the flash it's given, and then tries to construct a ProcessStandard for each process it encounters, which involves manually allocating space from the remaining heap to contain the ProcessStandard and its associated state. As such, in order to unload an existing process and load in a new one, you would need to:

  • disable the process
  • remove it from the processes array used by the board/kernel
  • "destroy" the associated ProcessStandard, so that the space claimed by it is available for use
  • if the new process can fit within the newly-freed space, use that; otherwise, attempt to allocate a new ProcessStandard at the current heap end
  • add the process to the processes array and update the index associated with each ProcessId (or remove the index entirely - hard to speculate without testing, but I feel like a O(N) linear search to match ProcessIds is probably a non-issue with single-digit N)

The big problem I can see here is that there may not be enough space to load in the new application due to fragmentation. I could be mistaken, but my understanding is that userland applications use physical memory addresses, which means that relocating them would be impossible. I'm not sure how best to tackle this, but given a PineTime Tock port would already have to page applications in and out, it's possible that I could just get away with reloading applications on the fly to reduce fragmentation.


The process console capsule shows that it's very possible to enable and disable processes at runtime, so that's a great place to start for a blessed launcher application of some kind, as you suggest. Will definitely explore when I get around to it!

Your suggestion to load app binaries from external flash into RAM is exactly what I was considering doing. We don't have a tremendous amount of RAM at 64kB, but that should be enough to page in and out applications on demand if dynamic loading's possible. I suspect loading applications from RAM may not involve too much kernel hacking from looking at load_processes; instead of reading through a region of flash and scanning for applications, each individual application can be copied from flash into RAM and the resulting pointer could be given to ProcessStandard::create (with the lifetimes amended, of course 🙂).


I'm not too fussed about the wireless updating of the firmware for now, as firmware isn't updated too often (especially with as much functionality externalised to applications as possible) and support can always be added later. Wireless updating of applications should be doable with a capsule that has access to the flash and BLE, so I'm content to mark that as "possible until proven otherwise" 😄

As for the Nordic SoftDevice, thanks for the call-out. The existing PineTime firmware uses Apache NimBLE, which is an alternate OSS implementation of the BLE stack of Nordic devices. Has NimBLE already been investigated by the Tock project?


Sorry for the wall of text; I'm quite enthused about the prospect of bringing something like Tock to the PineTime as it seems like a great domain fit and would let people concentrate on writing applications (and hopefully serve as a good demo of Tock at the same time!)

I'm waiting on some hardware to come in, so I can't get started with the work quite yet, but these are the steps I intend on following:

  1. Build Tock for the NRF52DK and confirm all functionality works as expected.
  2. Clone the NRF52DK board definition for the PineTime, and remove everything specific to the DK, and ensure what remains does not conflict with the PineTime. Flash this build to a PineTime dev kit and confirm that it can be interacted with over UART.
  3. Implement any missing drivers for PineTime/Tock. At a glance: a. XT25F32B SPI-NOR flash b. CST816S touch panel c. BMA421/425 accelerometer d. TianYiHeXin HRS3300 heart rate sensor
  4. Write a clock application that reads from the RTC and renders the time, potentially through LVGL[^2], to the screen.
  5. Ensure the New Platform Checklist from the porting document is complete.

At this point, the PineTime platform should be ready to be upstreamed into Tock proper, and I can return to exploring the problem space associated with the issues discussed above. Does this plan of attack sound feasible to you/the team?

Thanks once again - looking forward to your response 🙂

[^1]: Tock has terrific documentation, and the code is very well commented. I'm very impressed, and without a doubt appreciative. [^2]: I noticed the existing LVGL example allocates an entire framebuffer within userspace memory. That's not viable for us as our screen's framebuffer actually exceeds our RAM capacity by some amount; I believe the existing PineTime firmware renders in chunks, but I'll need to verify this. Multiplexing LVGL between several applications seems like its own can of worms, and one I'm trying to avoid looking at right now 😂

philpax avatar Sep 30 '21 20:09 philpax

I have a few ideas around how dynamic loading might be done, but it's definitely something that I would need to consult with the Tock team on. My understanding of load_processes is that it scans through the flash it's given, and then tries to construct a ProcessStandard for each process it encounters, which involves manually allocating space from the remaining heap to contain the ProcessStandard and its associated state. As such, in order to unload an existing process and load in a new one, you would need to:

  • disable the process
  • remove it from the processes array used by the board/kernel
  • "destroy" the associated ProcessStandard, so that the space claimed by it is available for use
  • if the new process can fit within the newly-freed space, use that; otherwise, attempt to allocate a new ProcessStandard at the current heap end
  • add the process to the processes array and update the index associated with each ProcessId (or remove the index entirely - hard to speculate without testing, but I feel like a O(N) linear search to match ProcessIds is probably a non-issue with single-digit N)

The big problem I can see here is that there may not be enough space to load in the new application due to fragmentation. I could be mistaken, but my understanding is that userland applications use physical memory addresses, which means that relocating them would be impossible. I'm not sure how best to tackle this, but given a PineTime Tock port would already have to page applications in and out, it's possible that I could just get away with reloading applications on the fly to reduce fragmentation.

The process console capsule shows that it's very possible to enable and disable processes at runtime, so that's a great place to start for a blessed launcher application of some kind, as you suggest. Will definitely explore when I get around to it!

Your suggestion to load app binaries from external flash into RAM is exactly what I was considering doing. We don't have a tremendous amount of RAM at 64kB, but that should be enough to page in and out applications on demand if dynamic loading's possible. I suspect loading applications from RAM may not involve too much kernel hacking from looking at load_processes; instead of reading through a region of flash and scanning for applications, each individual application can be copied from flash into RAM and the resulting pointer could be given to ProcessStandard::create (with the lifetimes amended, of course slightly_smiling_face).

This description sounds right! a few challenges I expect you will encounter:

  • The PROCESSES array cannot currently be mutated
  • not having static lifetimes for processes may be pretty tricky and require a lot of refactoring
  • executing from RAM will probably require some updates to MPU configuration
  • we will need a good permissions system for loading/removing apps, though I agree that process_console is a good template to follow there

I'm not too fussed about the wireless updating of the firmware for now, as firmware isn't updated too often (especially with as much functionality externalised to applications as possible) and support can always be added later. Wireless updating of applications should be doable with a capsule that has access to the flash and BLE, so I'm content to mark that as "possible until proven otherwise" smile

Agreed that if you get dynamic app loading working, extending this to wireless updates is mostly a question of having a sufficiently mature network stack.

As for the Nordic SoftDevice, thanks for the call-out. The existing PineTime firmware uses Apache NimBLE, which is an alternate OSS implementation of the BLE stack of Nordic devices. Has NimBLE already been investigated by the Tock project?

It has not been -- we have tried to avoid including C code in the Tock kernel. There is precedent for running a C bluetooth stack on another MCU, and using C code in an application to interface with this other MCU (see the nrf serialization capsule).

I'm waiting on some hardware to come in, so I can't get started with the work quite yet, but these are the steps I intend on following:

  1. Build Tock for the NRF52DK and confirm all functionality works as expected.
  2. Clone the NRF52DK board definition for the PineTime, and remove everything specific to the DK, and ensure what remains does not conflict with the PineTime. Flash this build to a PineTime dev kit and confirm that it can be interacted with over UART.
  3. Implement any missing drivers for PineTime/Tock. At a glance: a. XT25F32B SPI-NOR flash b. CST816S touch panel c. BMA421/425 accelerometer d. TianYiHeXin HRS3300 heart rate sensor
  4. Write a clock application that reads from the RTC and renders the time, potentially through LVGL2, to the screen.
  5. Ensure the New Platform Checklist from the porting document is complete.

At this point, the PineTime platform should be ready to be upstreamed into Tock proper, and I can return to exploring the problem space associated with the issues discussed above. Does this plan of attack sound feasible to you/the team?

This sounds feasible, we would certainly be excited to accept all these drivers and a pinetime board definition! Feel free to submit a board before all of those drivers are finished, they can always be added incrementally afterwards.

hudson-ayers avatar Oct 01 '21 19:10 hudson-ayers

Has NimBLE already been investigated by the Tock project?

It has not been -- we have tried to avoid including C code in the Tock kernel.

Is there anything preventing NimBLE from being wrapped inside an application? I was under the impression that the current Bluetooth stack was also based on some library written in C.

dcz-self avatar Feb 08 '22 14:02 dcz-self

Is there anything preventing NimBLE from being wrapped inside an application? I was under the impression that the current Bluetooth stack was also based on some library written in C.

Tock applications generally do not have access to memory-mapped peripherals, that is they do not have access to the Bluetooth hardware directly. I'm certainly no expert on the nRF radio or NimBLE, there might be two approaches to get this working:

  • either NimBLE has an abstraction layer which allows routing accesses to the radio peripheral through the kernel,
  • or the MPU is configured in a way to allow access to the radio peripheral from an application.

An approach like this might work, but only if NimBLE does not otherwise require direct access to any memory-mapped peripheral which the Tock kernel requires access to. Also, depending on the types and features of peripherals exposed in such a way they may provide channels to perform denial of service attacks, or read and write to arbitrary memory (DMA). This would undermine Tock's security model and thus likely wouldn't be supported upstream. It might be still reasonable for downstream developers, at least until Tock has a more advanced BLE stack in the kernel and accessible by apps.

lschuermann avatar Feb 08 '22 15:02 lschuermann

Thanks for the answer. Now I'm seeing that BLE is present as a capsule, so it's in kernel space, here: https://github.com/tock/tock/blob/master/capsules/src/ble_advertising_driver.rs . But at the same time, the book says:

The first step a BLE serialization app must do is initialize the BLE stack on the co-processor. This can be done with Nordic's SDK, but to simplify things Tock supports the Simple BLE library. The goal of simple_ble.c [...]

simple_ble.c seems to be based on the Nordic library, but it also seems to be in the userspace. How is it possible? Or is the book just outdated, and the BLE tutorial won't work as described?

dcz-self avatar Feb 08 '22 15:02 dcz-self

I think the book is outdated. If I'm not mistaking, this example was used with two chips: one chip that was running Tock that was connected to another external chip that runs the Nordic BLE stack. The actual example uses a driver that controls the BLE chip via UART.

To provide BLE connectivity, several Tock boards use the Nordic nRF51822 
as a BLE co-processor. In this configuration, the nRF51822 runs all of the BLE operations 
and exposes a command interface over a UART bus.

All that Tock can natively do is to advertise and passively scan for advertisements.

#2233 tries to implement the Rubble library as a Tock driver, but I think the actual Rubble library is not an actual complete stack.

alexandruradovici avatar Feb 08 '22 15:02 alexandruradovici

To anyone who comes across this issue in the near future, including @philpax :

I want to cover the smartwatch use case, and I'm willing to give it a honest shot, at least in terms of BLE (nRF52840) and runtime apps (drivers will be device-specific). Please get in touch with me over email if interested: fosico [dot] dcz [at] porcupinefactory [dot] org

Perhaps we could establish some sort of a working group.

dcz-self avatar Feb 08 '22 16:02 dcz-self

I want to cover the smartwatch use case

I'm really interested in that use case as well. In fact, I have a PineTime dev kit on my desk right now. Unfortunately I'm going to be busy for the next few months. I think working on that could be a good excuse to revisit the BLE story on Tock.

lschuermann avatar Feb 08 '22 18:02 lschuermann

I think in th short term I'm going to try to get NimBLE working, because that's fully featured, and relatively popular. That brings me to the question in relation to allowing direct hardware access from userspace.

Also, depending on the types and features of peripherals exposed in such a way they may provide channels to perform denial of service attacks, or read and write to arbitrary memory (DMA). This would undermine Tock's security model and thus likely wouldn't be supported upstream.

As far as I understand, applications aren't inherently any less secure than capsules, apart from an assumption that application code is less trusted. Allowing untrusted code to access hardware is clearly asking for trouble.

But if an application and a capsule expose equivalent interface, and are of the same trust level, wouldn't that fit into the current security model?

The only challenge is defining how the trust works. As far as I understand, capsules are simply compiled into the kernel. An application that has hardware access and can't be replaced would be as trusted and functional as a capsule.

Or am I wrong about something here?

dcz-self avatar Feb 08 '22 19:02 dcz-self

One big difference between capsules and applications is that capsules are written in Rust and are not allowed to use unsafe code. These capsules cannot directly access hardware resource, they have to go through a HIL (Hardware Interface Layer) and ask a low level driver to do the job. This is not enforced by hardware, but by the Rust compiler, as the capsule can only use the API it has access to.

When writing applications, this compiler enforcement cannot be done. If applications are written in C, there is no way to enforce this as the compiler does not provide such means (safe vs unsafe code). When writing Rust applications, these still have to use unsafe at bootstrapping (start) and for performing system calls. Due to this, application memory access is enforced by the hardware.

alexandruradovici avatar Feb 08 '22 20:02 alexandruradovici

Thanks. So my current understanding is that capsules do not only have memory (-mapped devices) access limited by the language, but also relies on the language to enforce how the memory is used. If it was just that, it would have been the same as hardware protection.

Which makes sense, it's not common in Rust to have a resource representing an unrestrained "memory area", without some extra invariants.

dcz-self avatar Feb 08 '22 20:02 dcz-self

For the record, I've started adding support for the pre-release Jazda project in my personal repo: https://github.com/dcz-self/tock/tree/sma_q3/boards/nordic/sma_q3

Right now it's in various states of disarray, but I'll try to gradually upstream drivers in no particular order.

dcz-self avatar Feb 15 '22 17:02 dcz-self

Just FYI, this is the hardware for Jazda running my branch of Tock: 1_1090239

dcz-self avatar Apr 01 '22 17:04 dcz-self