 Hi, everyone. I'm Sterling. I'm with Runtime. Runtime is a company that I co-founded on the CTL of Runtime. And our goal is to make open source software for embedded microcontrollers. So when I say a bootloader for embedded, it's really kind of a class of devices that are sub-embedded devices. So think Cortex-M-style devices, constrained devices with tiny little flashes and very little memory. And Runtime was founded with the idea of making software develop modern and easy for these devices. And so just a little bit about my background before I get started. I'm going to talk about the Minute Bootloader here, which has recently been refactored into a project called MCU Boot. And so I'll give you a little bit of that history. But prior to that, just a little bit of my background. So I was an early, I guess I got started at 13. I got started making websites. I was found open source through the PHP project, was a core contributor to the PHP project. And as I said on my previous talk, then Java started to take over the world and I decided that it was a time to find a safe port in the storm. And so I started working on embedded with a company named SilverSpring Networks. And I started working very basically there and then ended up running the firmware team. At SilverSpring, we shipped 23, 24 million connected devices. We shipped those devices onto the utility grid and in cities. And every single one of those devices had a secure bootloader, which we developed and managed over time. And we did that over multiple iterations of chips from our first ones, which were just simple ATMEGAs, to ARM7s, to our own custom system on chips, which had secure elements within them and had keys and key protection. We developed that and we regularly did software updates to 24 million devices and we did that securely and we run large swaths of the power grid. After that, founded runtime, which I just gave a bit of an overview of. And so this project is about the bootloader in the Apache Minute project. And here's a little bit of background on it. So runtime was one of the original members and contributors to the Apache Minute project. The idea behind Apache Minute is it's really an operating system for connected devices and not an operating system that you would think of as a traditional, just a kernel like UCOS or something like that. But an encompassing operating system that includes bootloader is networking stacks, file systems, everything that you would need to develop a connected product and all open source and Apache licensed. And so a part of that was to make the system software upgradeable, right? Everybody who develops a connected device, the one thing that everybody wants to do is upgrade software on that device. And so we within the Apache Minute project, we actually include libraries to do software upgrades. And those run in the application image itself along with a secure bootloader. And so we were happily working along there and then Zephyr launched. And so we've been talking for a number of months with the Zephyr project and the members of the Zephyr project. Ricardo actually is here in Lunaro. And so we decided to break the bootloader itself out of minute because fundamentally a bootloader should work across multiple operating systems and create a separate project called MCU boot. So right now it's hosted by Runtime. It's github.com slash run time co slash MCU boot. And we now can, with MCU boot, boot both minute images and Zephyr images. So if you come to the Runtime boot, which is just upstairs, a very quick product plug, we can actually show you our cloud being able to upgrade a minute device and have it swap to a Zephyr device and back and forth. And so this talk is about, and Lunaro has actually demonstrated this same bootloader at Lunaro Connect where they did software upgrades on Zephyr as well. So both of us have actually used this bootloader from a Runtime perspective. We've been using this bootloader for about six months on every single check-in and every single pull request to all of our development. We rebuild our entire system and we use this bootloader to upgrade devices and run our unit tests suite on it. So it runs pretty reliably. And so I'm just going to talk a little bit about the bootloader. This is the Flash layout slide, but really the idea behind the bootloader is to make it very, very small and secure for small embedded systems. So the type of processors that we run on are things like the Nordic NRF 51, which have 16K of RAM and 128K of Flash. So they're very, very small embedded devices. They grow in size. So some of them will have four megs of RAM and eight megs of Flash, but that typically is where it tops out. If it gets any bigger, people tend to run Linux and other things on them. I think at Silver Spring we ran up to 32 megs of RAM and 64 megs of Flash, but that was mainly because we had an entire product suite that had much lower-end systems as well as higher-end systems and we wanted a single development tree for all of it. But it's really designed to fit within a small code footprint. So the bootloader itself fits within 12K of code. The significance here is you're kind of at this level really looking at either 8K or 16K that you want to fit into. If you're any bigger than that, then it's just craziness. And the reason why you want to fit within 16K is that typically a bootloader itself needs to be right protected. And so if you look at the Flash, you can adjust where the right protect is set on the Flash. If you go to 17K, then you have to write protect at 32K typically. If you go above 32K, you have to write protect typically at 64K. So fitting within 12K of code size is really good. We can grow up to about 16K. One of the reasons we keep the bootloader itself within 12K of code is we have a serial boot option with minute. So the bootloader plus serial boot fits within 16K of code on these systems, which is fairly important. So if you look at the Flash, in addition to the bootloader itself, what we've talked about with the members of Zephyr is that also defining the actual layout of Flash systems for these small embedded devices. If you looked at what we did or what I've done in previous companies, what it ends up happening is everybody ends up having their own Flash layout for these devices and their own bootloader. So SilverSpring had our own bootloader. We also had our own Flash layout. And when you do that, it becomes very hard to manage these devices in the field and upgrade them over time. How do you, for example, create a running image and know what sectors are on that device? There is no concept of a superblock for these embedded systems, right? So how do you define the layout of a system and how do you understand that is a part of the MCU boot project? And both operating systems are collaborating on this project to do that. So what we have here in the Flash layout is we have the bootloader itself, and I'm trying to, oh, perfect. We have the bootloader itself. That typically sits depending on how you configure your Flash. That can either be at the very end of Flash for systems that boot that way, or it can be at the very beginning of Flash. Typically, it's at the very beginning of Flash. There's then a superblock which defines the layout of the Flash itself. So what it says is essentially this Flash is a certain size, and here are the blocks on that Flash. We are going to allocate blocks 10 through 26 for image 1, 26 through 45 for image 2. There's going to be a file system at 45 through 60. And so the superblock basically gives you the layout of Flash so that any application or kernel image that runs can read that superblock information and understand how the Flash is laid out itself. You then have two image slots. These slots, you can only have one image slot if you only want to have one upgradable image, but there's always two slots. Essentially what you do is if you want to have only one slot, you just make the second slot really, really small. And so images, slot 0 is the primary image slot. So the initial design of the bootloader is for systems that actually have internal flashes and run out of that internal flash. And so when you actually run on one of these systems, you don't want to have to have compile your code in a position-independent fashion. So you always want your image to be able to run from the same location. So slot 0 in our bootloader is the primary image location. Code always runs from there. And then slot 1 is the upgrade slot that you copy into. And if an upgraded is needed, the bootloader swaps the slots. And it swaps those slots using the scratch sector. So the scratch sector needs to be the size of at least one Flash sector so that you can do a safe copy of the two swaps across power cycles, right? Because you can't assume that these devices will always be constantly powered. You may get interruptions while you're doing an image upgrade. So essentially what you have is slot 0 sits there. And when you tell the image to do an upgrade, the bootloader will look at that and it will start swapping slot 1 into slot 0. And it will use the scratch slot as it erases sectors of Flash in slot 0 and replaces them with the contents of slot 1. So the bootloader runs a swap. And an image trailer tracks actually the state of swapping and upgrading the device. The bootloader itself, when it looks at these images, it supports either RSA or ECCDSA for signatures on the images itself. And so if you look at the actual image, essentially in the bootloader itself, what we have is an image header. That image header contains the size and version information for that image. So it's a simple header. Typically there's also some room for padding. So if you want to be able to locate interrupt vectors in text and you want to locate them at a specific location in text, there is a padding in the image header itself. You then have the code that's associated with the running image, so the kernel. And then you have a set of TLVs that include things like the signature of the image data itself. So the image header does not contain that signature, but the signature itself is contained within the TLVs. And then you contain the image trailer, which keeps an ongoing record of how the swap is going when it swaps between slot 0 and slot 1. And that's what those image trailers end up doing. So the way the bootloader works and the boot process is. So the assumption here is that the bootloader is not actually managing the software upgrade itself. There's one caveat to that, but typically what you're going to have is you're going to have slot 0 and slot 1 on your system. And the kernel itself is going to be linked with an image upgrade process. So it's going to be a preemptive, in minute, it's a preemptive task that runs, it's the same in Zephyr. And what we've done is we've implemented an image upgrade protocol that runs over OIC in minute. And you can download a new image. When you download that image, you download it to slot 1. And you can then tell the system to boot to the new image, and you can have a confirmation system based upon that. And that's all done within the application running code itself. That's not done within the bootloader. The bootloader just gets information from the operating system kernel about what image it should be running, and it tells it to do the swap. So what happens is on boot, the bootloader checks both slots. It normally boots slot 0, it verifies the signature of the image. So it does a SHA-256 of the image, and then it verifies the cryptographic signature of that image prior to booting it. And then if a new image is marked in slot 1, it safely swaps both of those images. It tries to boot the image itself, so it then goes and boots the image. When that image boots, it's its job to mark the bootloader and tell the bootloader that it has successfully booted. If it does not mark to the bootloader that it has successfully booted, the bootloader will swap back to the previous image itself. And so you have a fallback mechanism. The way this typically works in the field and the way we've managed devices successfully before is you want to have kind of both options here. If you think about how you're doing an upgrade, and I had to upgrade all those millions of devices, you want to push out that upgrade and you want to push it out to a small percentage of your devices first. If you think about it, first it's going to a few devices in development, then it's going to 20 devices or 100 devices in QA that you want to test. And throughout that process, you're kind of expecting things to fail horribly. You tested it with just your system here and it worked great, but then you didn't test it with the 20 other versions of hardware that you were upgrading and you have some issue. So when you do that, you want to have some way of making sure that the device falls back to the working image so you don't have to manually flash all of those devices. And so what you do is you upgrade the device and you set a fallback. If it doesn't, once it reboots and the network interface has come up and the device has successfully booted, you tell it and you confirm the slot and you say, okay, you have successfully booted and you move on with your life. As you go to rolling this out to larger and larger numbers of devices, you probably don't want to have that kind of information in the process because you have high confidence that the device is working well and you potentially have, you don't want to have a scenario where you have 20 million devices you upgrade and half of them are running one version or half are running the other. So what you can do is you can basically tell the bootloader to swap and confirm. And this typically depends on your process. For some people, even at the 20 million device scale, you're going to always want to swap to the new image and directly tell it to confirm that image and assume you have some sort of end to end check. In other scenarios, you're just going to want to say, nope, swap and confirm, run the new image. I'd rather you run the new image and take that risk than I would run two incompatible versions in a network. And this was, for example, at Silver Spring, we did large-scale mesh networks. So we had five million devices and continuous networks with 10,000 devices all meshing for each other. When we did a large-scale upgrade, we did not want to have to test compatibility of networking stacks at that scale with half of the devices running one image and half the devices running another. So we ran our confirmation process up to about 10,000 or 20,000 devices where we would actually confirm on every device. And then when we pushed it out to the full network, we would just switch and confirm. If you're running a connected wearable over Bluetooth and the phone is right there, you might always want it to fall back. So it really depends on what you want to do. So a little bit about source layout. So I mean, the best thing to do with MCU boot is it works on pretty much any platform that Minute supports, as well as more and more Zephyr platforms, at least the K64F. But I think Lenaro has tested it on more. Okay. So it runs on those same platforms on Minute. It also runs on Zephyr. So if you download Zephyr Minute, there are instructions in the bootloader for building for each of those platforms. And so you can actually play around with it. I'm going to talk mostly about Minute because that's just what I'm more comfortable with. But Zephyr works equally well. So don't be shy about trying it with Zephyr either. So if you go and you download Minute, you'll be able to build the bootloader. On every device that Minute runs on, you always have a bootloader. So the bootloader is located on the Nordic series, address zero. And you have to flash the bootloader to the device before you actually have the image. So we always go through the bootloader on the Minute system so you're able to download it. If you go to the GitHub address in the slide, so github.com slash runtime co slash MCU boot. What you'll see is a number of directories. And this is just to give you a little bit of an overview on those directories. So there's apps boot. Apps boot is a sample boot application for Minute. So it's what the bootloader itself is actually a library that you can compile in and link to specific locations in the core of the bootloader. In Minute, what we do to create our bootloader that runs on most devices is we have an application called apps boot, which simply brings in the bootloader library and defines a main function and calls into the bootloader. And that's pretty much all it does. Zephyr I think does something very similar. There's boot serial support. So we do support serial boot builder upgrades. That is the one exception to the rule when it comes to the application providing the software upgrade. And this is done for a lot for maker platforms actually was why we originally implemented it. So a lot of maker platforms don't have built-in debugger support, but they ship those devices. So this would be an example of this would be some of the Arduinos as well as the Adafruit has a Feather series where they don't want to spend the extra money on the Jlink chip or their users don't. And so the way they upgrade new software to these devices is over the bootloader. The bootloader is located in a right protect sector. And then you can use boot serial to load new firmware onto a device. You know, typical bootloaders like UBoot and other bootloaders have support for upgrade within them as well. I think over time we'd be happy to add Bluetooth support to the bootloader or USB or ethernet or any of those things. It's not really a high priority for us because most of our upgrade paths end up going through the running kernel image itself, but it's something that we certainly welcome as a contribution and can be any optional thing that is compiled into the bootloader. If you're looking for the core source of the bootloader, that's in boot.util. The Zephyr-based abstraction, so the way that Zephyr has implemented support for this bootloader is the bootloader itself leverages the minute APIs when running on minute for accessing the flash and things like that. So we have a HAL flash API. What Lenaro has done is they've gone ahead and they've created a mapping for the HAL flash APIs to Zephyr's driver interfaces. And so when you look at boot Zephyr, that is the Zephyr port of the bootloader. And there are instructions in the bootloader directory that describe how to build the system for Zephyr. I think Zephyr builds it about the same size as minute. So the abstraction itself doesn't end up costing a lot of code space. Image tool. So there's two ways of creating signed images that can be downloaded or creating manufacturing images that can be downloaded. You can either use the new tool if you're working within the minute framework. And so that tool is newt. There was just a talk on it. And that tool will allow you to create an image out of a minute system. It'll allow you to have a version with that image. It'll allow you to sign that image. It'll also allow you to create a manufacturing image which contains the bootloader itself, the super block, a default image to run, and an empty slot, so a full flash layout that you can load. And that's tied in with the minute system. Obviously, our goal is to make this bootloader work across all operating system interfaces. So we want Riot to use this. We want this to work with freeRTOS. It's our goal to make this a bootloader that everybody can use and that we all share effort on. So having that be the newt system is probably not the right way of doing that. So we've created a separate tool called image tool, which is image signing and generation for the MCU boot project. So if you're using this with other operating systems, you can use that tool. It actually shares source code with the minute system. So minute is written in, newt's package manager is newt. It's written in go. In go, the images, when you distribute a product, it's statically linked and compiled. And that works across Windows, Linux, and Mac. So essentially what image tool does is it just brings in the same signing and generation libraries that are in minute. And it uses those to create images in an operating system independent fashion. So there's no downside to using image tool and it'll work on pretty much any platform you run. And then there's sim, which is a great project done by David Brown from Lunaro, which essentially takes the boot util package from minute, and it runs it through its paces on Unix and Linux systems. So essentially it links in the C file that is boot util, which is just a library that is brought into other systems, and it simulates a local flash system. And so on every commit we can actually run the boot util simulator and run through a full set of test cases on the minute boot loader to actually ensure these things work well. And actually that was that slide. So I think that we covered all of that. So the Apache minute boot loader is fairly, go ahead. Yeah, so the Zephyr source abstraction is, it's essentially, so for example with the boot loader itself, we need some way of accessing flash, to load the images, to erase flash, to write flash. The boot loader itself uses an interface in minute, which is our how, which is how flash, and how flash defines the flashes that are in the system, as well as APIs for accessing and reading those flashes. We have a flash map, which defines is the super block with the information about what's on the flash and defines access mechanisms for that super block. Those are all minute specific APIs. So in boot Zephyr, there are implementations of those minute specific APIs, and there's not a ton of them. There's a handful that map into the Zephyr implementations itself. So for example Zephyr has a driver's flash. And so there's a file there that basically has how flash, which is the minute API mappings into Zephyr's mappings. And it doesn't add too much code. As I said, they end up being about the same size. So Zephyr fits pretty much in everything that minute fits in when it comes to these. But it allows us both to use our own underlying hardware implementations. So when you compile MCU boot for the MCU boot system, you pretty much support every system that Zephyr supports. When you compile MCU boot with the minute system, you pretty much support every system minute supports. And very similarly, we hope to do the same with Riot and other Oasis. So it's a hall area? It's just a hall area. That was more succinct. So Apache minute boot loader is approaching 1.0 now. So it's been, we've done, we've been in active development on it for over a year. That development has died down about three months ago. So the Apache minute boot loader is still within the Apache minute project. This is, that is not MCU boot, although we've maintained compatibility between the two projects. Apache minute is a project that is independent of runtime. We are obviously big contributors to that project and big supporters. But it is the Apache minute project's decision as to which boot loader to use. As runtime, though, we've, we've broken out MCU boot. I think our goal now is to, is to work towards the first release of MCU boot that supports both Zephyr and minute. And that will be, it will essentially be the Apache minute boot loader at 1.0 with full Zephyr support. And that's kind of what we consider our starting point, right? Because what we really want is to get everybody collaborating on the same system. And there are definitely features we need to add to this boot loader and improvements we need to make. But we want to make them as a community and we want to make them across multiple operating systems. And so the boot loader itself for the platforms it supports is, is very stable. We run, we have probably run hundreds of thousands of upgrades with it reliably on these devices, obviously not one at a time. So it's fairly stable for where it's run. But MCU boot does not yet have its first release. We're working towards that very rapidly. I don't think we're about too far off. So I think you should see that within the next three months. And then there's a whole bunch of future work we need to do. So the, and we would love, by the way, contributors and people to join in the effort. We're recruiting you. Please feel free to join. It's an open project. But some of the things we want to do that we haven't done is we have no support currently for hardware APIs for a lot of things. Like, so for example, a lot of the more modern hardware for secure boot will have multiple keys actually written into the hardware and those keys are fusible. So if you want to invalidate a key, you can no longer use that key and that's fusible within the hardware. We don't support any of that. We want to be able to support multi-stage boot loaders. So a lot of times what you'll see in devices is you'll have a very, very simple, rommable portion of the boot loader which literally just verifies the portion of the boot loader that gets written to flash. So it will just perform the signing and verification on that device. You'll often see this especially when a boot loader is located in an external flash as an example. On internal flashes like you have on the Nordic system, it's not that big a deal. But in a lot of cases, one of the potential attacks on a boot loader is if you have a flash that's external, if the boot loader itself is not signed, even if it's a secure boot loader, somebody can just desolder the flash and solder on a new flash and then they can ship an invalid product and the customer has no idea until everything goes wrong. So having a section of the boot loader that is rommable that can sign the actual boot loader and then having the boot loader itself which does the swaps and manages the images is something that we'd like to do in making it very clear what's rommable and what isn't. Support for loading images into RAM, this is a very common one. So often, you know, one architecture is, and this is the architecture in STMs as well as in Minute in the K64, is that you run directly from flash and you run in a fixed location in flash. Another very common architecture is that you have an external spy flash, that's where you locate the image, you have say a meg of RAM on the chip itself, you divide that RAM into where you want to run your image and what your data and BSS and heap is, and then you run your image directly out of the RAM on the chip. We don't support that currently, we obviously need to add that and we'll be adding that. And then supporting, support for replace instead of swap. This has been, this is not a feature we've released a lot but we've definitely heard it from people which is right now we swap the two images so that you can always fall back to the replacement image. There is a, there has been, there have been requests to essentially just erase the old image and replace the new one on top of it to reduce the complexity of the swap. That's an option that we're looking at adding and supporting. And then the major one and I think our real, our real goal here is support for other OS's besides Apache Minute and Zephyr. So we're looking to broaden the support for this bootloader as widely as possible. It's not very hard to do because you know there's a, there's a few things that we had to do to get it to work with Zephyr. It was implementing the HAL. It was, and then it was looking at being flexible about where interrupt vectors were located. So in Minute we located interrupt vectors in RAM because we believe life is too short to have interrupt vectors in text. Zephyr locates them in text. And so if you locate the interrupt vectors in text you need to be able to start the image header offset within the image itself because often interrupt vectors will start as zero or at some fixed location in the image. And so you need to have the image header pass the interrupt vectors themselves. So that was some of the work we had to do. We, I imagine there'll be other little things that we have to implement when we go and we add riot support and free RTOS support and all the other OSs. But I think our primary goal is really to get as many operating systems supported as possible. Because I think if we can, the goal here is really to drive everybody to share a secure bootloader and share the effort. It's not that much code. It's 12K compiled code for these systems. And it really makes sense to all share efforts on certification and security testing and really just make this a very simple and solid bootloader. So that's going to be where the majority of runtime's efforts are focused over the next couple months. And that's pretty much it. So that's the talk about the bootloader. Are there any questions that folks have? Well, so there's a couple things. It's really a configuration option. So there's a couple of layouts that you can have for this. One is you just have to size the slots and basically size your flash to ensure that you have enough room for growth. So our rule of thumb was we basically, when we would select flash, we would basically take double whatever our initial code size estimates were and made sure we had enough flash to do that. And you have to size the slots appropriately. The other option we support in minute is essentially what we do is we don't have a concept really. We have the concept of an application and a kernel. And so what we end up doing is slot zero becomes the kernel itself and it's just the kernel. And then slot one becomes the application. And we end up basically essentially what happens is if you want to do a new upgrade, you erase the application, you load the kernel into the application slot, then you copy the kernel down into slot zero, and then you upload a new application into slot one and you can size those appropriately. So those are the options we've supported. Exactly. Well, they're housed and making sure that they boot appropriately. So just going through and making sure on a regular basis that we test to make sure that we are booting Zephyr. I'm booting Riot on every platform that Riot boots on and similarly for Zephyr, right? Trying to get some alignment there. There is an effort that we're looking to start that's separate from this, which is to share efforts also on the software upgrade schemes themselves. We think that that should be a separate project from the bootloader because we think it should be a part of the running kernel itself and that there's going to be some diversity there, right? For Bluetooth, the way we do upgrades for Bluetooth is over OIC, which is Iotivity, and then we have a Seaborr-based upgrade protocol that runs over Bluetooth. When you're running in a Wi-Fi scenario, you're behind a NAT and you're probably going to want to dial into the cloud and talk to the cloud. So the upgrade protocol and the software that it does upgrades is going to change based upon the transports in the application. The bootloader should remain the same for all of those. So we are looking at a separate effort, which is to, at least for Bluetooth, all converge on an upgrade scheme and start those, but those won't be part of the MCU boot project. There's a standard interface. There's basically a flash interface that you have to tell the bootloader to boot to a new image or to do something. There's a few set of TLVs that you have to basically put it through its paces. And so that'll be standardized for whatever software upgrade protocol you end up using. I saw a hand over there. Sure. So basically every image is signed. So the way typically a secure boot scheme works is that you have a private key that you, as a company, will keep very secret. We, I think, used HSMs to sign our firmware images. The actual key material itself was printed on paper and put in like 30 different locations. And you all had to take it back together. And it was, it was painful. And we actually, these HSMs also give you policies, right? So what you can do is you can require that a product manager, an engineer, and a QA person all have to like, you know, give their thumbprint and sign away their firstborn to sign every firmware image. So those images get signed with the private key, which you keep as a developer or as a company very safe. On every single device that gets manufactured, you put the public component of that private key. And so what the bootloader does is when it comes up and it looks at the image to boot, it goes through and it computes a SHA-256 of the image, which is what actually gets signed by the tool. And then it uses the public key to verify that the private component of that image gets booted. And so that's how you ensure that the image you're booting is an authentic image that you should have booted. The public key is, as I mentioned, it depends, it's up to you. It could be stored on your laptop. I wouldn't recommend it. The public key or the private key? The private key you're using to sign the image is stored by the company that's signing it. So when you create a downloadable software image, you have that private key, at Silver Spring, we do new create image and you provide it with the private key or you provide it with the address of an HSM that can perform the signing operation. Yes, so that's stored right at the end of the bootloader itself. So it's in the right protect portion of the flash itself. Exactly. Well, you can locate it anywhere you wanted flash. Typically, you would locate it within the right protect portion of your flash. Put that image as well. So we locate it at the end of the bootloader. The bootloader itself is not signed today. That would be the thing that we would need to do to make it romable. Right. So essentially, when you have a romable bootloader, what you want to have is you want to have some portion that's located within the chip itself that is preloaded and romped in the chip. That comes out. You have a signed bootloader or a signed image and that gets verified by the rom portion of the chip, but that has no complexity beyond signing, beyond verifying the signature of the bootloader itself. Then the bootloader boots up and it goes and operates the upgrade. Exactly. Typically, those are managed by the chip vendors themselves. So when you start to do kind of higher volume manufacturing, you can actually work with the chip vendors to provide custom rom masks for your security certificates. Well, so we want to get down, I mean, we have to kind of, at least from a runtime perspective, support 16K of RAM and 128K of flash. I think if you look at kind of MCUs that are out there, that's the bottom limit for most of them. You certainly have smaller ones. I think 8K of RAM would be fine as well. We typically don't see many MCUs below that these days. 128. Yeah. It's rare. I mean, you will see 32K of flash on some of these devices. It's very rare. It would probably work on those devices, but although I don't think it would be very useful. Yeah, exactly right. Because we're now 12K of that for the bootloader and then how big is your image actually and do you want it to be secure? So I think for us, kind of where we're targeting is 8 to 16K of RAM and I think 8 is fine, but 128K of flash, which is what you see on most chips today, even the previous generation chips. And then on the upper end, sky is the limit, but I'd say it'd be rare to find anything beyond 128 megs of flash because typically at that point people run Linux. I mean, you do see spy flashes that get up to 256 megs just because there's a cost point that the chips are heading. But it doesn't really matter. I mean, we have enough address space for all of those chips. Yeah. So this is one other thing. Since you're looking at the standard rolling out there, is the superblock going to be some sort of managed somewhere? Is the way that that's defined? Yes. So there is a documentation on the image format itself, as well as on the superblock format that's maintained as part of the project. Everything is documented within the project and maintained as a part of the project. And we want to grow that and really have both the specification of the flash layout and of the superblock and the bootloader and how that works tied in with the bootloader itself. And Zephyr and all of the projects, I think it really benefits us all to just agree on the flash format itself and have a consistent way of understanding how the flash has been programmed. And starting at the bootloader just seems like a good place to do that. One sector of flash. Yeah. So it depends what your sector ends up being. So if you have 2K sectors, 2K, 8K sectors, 8K. Or rather, it has to be the size of the largest sector that you're going to erase within the slots themselves. Today, no, but that would be certainly something that's easy to add to the project and we'd be happy to add. Oh, you can certainly define more than, oh, sorry, these are just image slots. Yeah. So the flash map itself, you can define areas that are for file systems. So in minute, we have a file system, which is a log structure file system called NFFS. So you can say, I want this area to be for NFFS. We have a configuration variable storage mechanism as well that is just purely TLV config variables. And you can define those as an additional one. These are just the image slots. So there's a bunch of other flash that is defined there as well. Any other questions? Awesome. Well, on that note, thank you very much and cheers.