 Hello and welcome to my talk about the barebox bootloader and some of its nifty features that I would like to share with you. My name is Ahmad Fatoum. I am an embedded Linux developer with Pingotronics, a German Linux consulting company where I have been using barebox for the last two years. As for the structure of my talk, I will start with an obligatory what's a bootloader, then we will talk a bit about the complexity set arise, how barebox came to be, then we will look at how to port barebox to a new board, how to customize barebox for a simple booting mode, then how to do it for redundant boot, then we will look at using barebox for bring up and along the way we will discuss these abstractions that have evolved in barebox to keep the complexity of it all at bay. So to start off, what's a bootloader? So on a modern SOC you have some sort of bootrom that's masked into the ship. This does the bare minimum needed low-level initialization to load our bootloader into an on-ship SRAM and from there on our bootloader has to do all the other initialization that bootrom didn't know how to do. It needs to set up the SRAM controller, it needs to configure the clocks and the PLLs and then it needs to access the boot medium to fetch the kernel and any other binaries that it might need, for example an init.rd or a device tree and at the end it will start the kernel with these parameters and images, but we have come to expect more of the bootloader. So there might be other firmware besides the main processors that need firmware loaded for us, so there might be a secure monitor binary that we need to load. The boot process should also be fast, so we need to use the caches, which on some architecture means that we need to have a driver for the memory management unit and fetching the kernel from the boot medium isn't always the same, we might have different boot media and we need to try them in order. We might have multiple partitions on the same boot medium, so we need to have some decision making to decide which partition to use. We might need to enable a boot splash because the boot process takes a bit longer than anticipated. We might need to kick off a core processor or even communicate with it out of the bootloader because it has access to some peripherals that we don't, for example some clocks or reset lines and when we have the kernel image, we might not be able to boot it directly. We may need to check a cryptographic signature to be sure that it's a kernel image that needs to be booted. There might be device tree fixups that we need to do on the device tree binary. For example, we can have the same board once with a display and once without and the bootloader needs to detect which kind of board it is and then it will fix up the device tree to either have a display or not. Then we might need to enable the watchdog so we can monitor the boot process and then at the end we can do which is what the bootloader is ultimately made for to start the kernel. But this gets complex real fast, but we want to maintain a scalable and maintainable code base so we would prefer to have some abstraction to keep the complexity lower. Arguably useful abstractions are driver model. So an SD card controller, host controller, they do basically all the same thing. They interact with some hardware and then they allow you to set IO settings and they allow you to send MMC commands over the MMC protocol. So it would be nice that all MMC host drivers implement the same interface. And then you can handle them all the same from the same MMC core. And you could have, for example, this MMC core provide you a block device operations. So at the end someone using this will just interact with read write of blocks and doesn't have to worry about the specifics that it is an SD card, for example. Type tightly connected to that is having some separate hardware description. So a device specific language like device three is uniquely positioned to represent these complex hardware setups that we have nowadays where you have SOC families that share some IP cores and share and don't share some others where you have system on modules that can be fit on different baseboards. So you would appreciate having some sort of code reuse or inheritance, which can be done nicely with device three. There are some other abstractions like a virtual file system. So you would appreciate having the ability to just mount a file system at a location. And then you can interact with it like any other file system. And you don't have to keep track for, okay, I have this partition on the MMC and that has an X4 on it. So I need to use read and write operations that are meant for X4. You can just use a generic read and write. Having a block layer so you can do caching of blocks having character devices. So you can abstract away the differences between let's say a registry map that's just memory mapped IO or a registry map that's connected over SPI. And you will want to interact with these character devices and with the drivers interactively. So you need to have a shell prompt and you need to be able to write scripts for your shell. And you would like to have the ability to persist these scripts that you have written. And all this is for having the ability to introspect your system so you can do rapid debugging and rapid debugging cycles. You might be inclined to think that this is basically what Linux is already capable of doing, which is right. You could compile a Linux with a built-in Initram FS with a built-in device tree. And that would check all these above boxes. But the boot process is very much size constrained because you need at first to fit into an SRAM. And then you chain loads your second stage, which also should be small so you don't spend too much time in it. And Linux would still need to be extended to support stuff like SDRAM initialization and so on. And clock setup and this low-level clock setup. And here comes the idea that why shouldn't we take an existing bootloader and extend it with this useful part of Linux for bootstrapping. That's basically how Barebox came to be. So it started as a Uboot fork called Uboot version 2 in 2007. It was renamed to Barebox in 2009. It's licensed under the GPL2. And it has a monthly release cycle. And now it supports a couple of architectures, ARM, MIPS, X86, EFI, RISC5. And its main selling points back then were a Linux-like driver API. So you have a driver model inspired by Linux and you have drivers that implement the same API. Or an API that's very similar to the one used within Linux. So this makes porting easier. The coding style is set of which Linux it has to. It doesn't use header files for configuration. Instead it uses the kernel config system and it uses cables for building the kernel. And for the user API, it has a POSIX-like file descriptor-based API. So you can just open a file in the virtual file system, read and write to it and from it. And it can also be accessed over a Unix-like interactive shell where you have your usual utilities. So that's basically what Barebox was made for. And over the years there have been some more features that are more about booting, which we will look like during this talk. So let's see how we can port Barebox to your board. So you can split a traditional booting process into a first and the second stage. The first stage does this low-level initialization that's needed to load the second stage, that would be on your personal computer, the BIOS. And the second stage is what runs from the SDRAM, which can a bit bigger than the limited SDRAM or flash you have. And this one loads the kernel, which is our operating system, which we should load. And Barebox can be used as BOSS. So on some SOCs it can be used as second stage. On some others it can be used as first stage as well. We will look in this talk how to use Barebox as a second stage for your board. So if you have worked with a Linux multi-platform kernel, you might think the same as me, that's a pretty great thing, that you can have just this one generic image and use it on dozens of different SOCs, on hundreds of different boards. This all works because you have a separate device tree description. And this device tree describes an actual hardware you have on your board. And then you start the kernel, which is a multi-platform ARM kernel, for example. And then when it has done the low-level initialization, it uses a device tree to discover what kind of devices are there. So you can have multiple devices, drivers, but only the drivers for the devices that are actually there are activated. And for the second stage bootloader, we would like to have some similar setup. We can't depend on another bootloader to pass us a device tree, usually, as the kernel does. So what we do is that we define a new pre-bootloader, and this pre-bootloader passes us a device tree. And pre-bootloader we will call PBL in this talk. And normal Barebox binary we will call Barebox proper. So how that looks like? We see to the right. We have three examples. Once we have Barebox prefixed with pre-bootloader and a device tree, and the pre-bootloader passes a device tree to Barebox. Another time we have two device trees, and Barebox can decide which device tree to use in the PBL and pass that. And in the middle we see another example where we have just one device tree but some firmware, which we need to early load. So that's something we can also use the PBL for. One short slide about the first-stage bootloader. How would that look like with the image format that we have just shown? So the best way would be to do the clock setup and the SDRAM in the PBL. We see that on the IMX. So the PBL runs. It will see that it's not running from SDRAM. So it loads, so it does a low-level setup. And then it shale loads Barebox again with the same PBL. But this time it loads a full image, not a small image. That's only the FSBL part into our SDRAM. And then Barebox is run again. And it will see, okay, now I am running from the SDRAM. So now let's do the normal bootup where I extract Barebox to the end of the SDRAM and branch to it. This is very nice for development because you have good compile time coverage. You can just build, for example, the IMX version 7 DevConfig and it builds images for over 100 bots at once. And it's nice for integration too. So you will probably not use a DevConfig for your bots, but you might have a few different IMX bots. So you can use the same config for all of them, the same recipe in your BSP. And that will generate you more than one image for each of your bots. And an alternative that might be easier is to build Barebox twice and then override the first stage bootloader in it so that it just chain loads the SSBL. You can do that, but this comes at the cost that the FSBL is no longer multi-image capable. So multi-image is that what you see to the right, that you have just Barebox separately and each time for each bot it's prefixed with a bot-specific pre-bootloader. Now that we have talked about this, let's look how you can make this work on your bot. So we will assume you are using device tree and further it would be nice if you are a good citizen and have already upstreamed your device tree into the kernel. If that's the case, you just need to wait a bit. And Barebox will import all Linux device trees into slash DTS. This is done on a regular manner. And as soon as that happens, you can define a Barebox DTS. We see that here. What the Barebox DTS is doing is it's including the upstream device tree. Then it's including a SOC device tree that's specific to Barebox. Then it sets all you need. You can also add some extra nodes. Here we add a node for having an persistent environment on the EMMC in a partition called Barebox Environment. And at the time of writing the upstream device tree didn't have a reset GPIOs property. So this is fixed up in the Barebox device tree. Now we need to pass this device tree to Barebox proper. So you need to have a pre-bootloader entry point. We see here how that looks like. You do some low-level initialization. This may set up the stack or the caches if needed. Then we print some symbol to make debugging easier in future. So you can see if the pre-bootloader entry has been entered. Which is the very first code in Barebox. So this is always a nice debugging aid. If something doesn't work, turn on low-level debugging and see if anything is printed. And then we pass the device tree and the memory base and size address to the Barebox entry point. And we don't see an explicit base and size here because we have a macro that passes the correct base address for the SoC family and that asks our SDRAM controller about the size. So we need not specify this here. So we have no duplication. After that we need to add a board driver. Or we don't need to. It's optional. So as soon as you have called Barebox entry function this will link in Barebox proper. But this Barebox proper is not board specific in any way. But you might want to have a board driver that matches against your board's compatible to do stuff like handling hardware quirks or registering device tree fix ups or even modify the live, unflattened device tree that's within Barebox. We see an example here. So we have a system on module and we match against the compatible of it. And then we change the model name. The model name usually comes from device tree. It's a bit too long for the slide. So it's made shorter here. And then we register a Barebox update handler that way Barebox knows how to update itself. And that looks just like any other board driver. You can have multiple and they are chosen according to the device tree that's passed to Barebox. Then you need to tell Kconfig and Cable about the device tree, the preboot loader and maybe our board driver if we had at one. How that looks like. We see here low level dot C was the name of the low level codes as a preboot loader. It's compiled to a low level dot O. So we add that to the low level Y variables. Same goes for the board driver. We add that to opt, which are the objects for the normal Barebox. Then we have a Kconfig menu entry. So you can select it in the menu config. And then we tell Barebox to descend in the directory and to build the device tree. By default, everything is linked into Barebox and that link time stuff is discarded. That's not needed. So there is no problem in having multiple device trees built. We can just pick and choose what we actually want to use. And now that we have done this, we have a need for an image to contain all of this. And that's how it looks like. It looks a bit like boilerplate. And indeed it is. You are best served by checking what other boards are doing for your SOCs and just copying that. But to explain succinctly what we are doing here, we are adding a new entry point to the list of PBLs. Then we give the new image a name and we give it a format to use. The format here is a dot STM32. There is a rule above that's called external tool that will format our Barebox binary by prefixing it with header and that's what the FSBL is expecting of us. And now that you have done all of this, you can restart the Barebox build process by typing make with appropriate variable set. Barebox will prompt you for the new board, which you can accept. Then it will build a new Barebox proper because you have changed Barebox itself by adding a board driver. And then it will take that Barebox proper and link it once with every enabled board. And that resulting image, you can put on your boot medium and have the FSBL run it. And when you do that, you should see something like that. So you have here a Barebox boot log. You will see that it by default fails because it will try to network boot if nothing else is configured. It will try here to boot username, which is none-linux-hostname of the device which comes from the device tree. And understandably, this is not available over TFTP yet. So it will just fail until our assets nothing bootable has been found, which brings us to the next part. How can I customize Barebox? So for example, the username is not none, but my initials. This is done in Barebox via the environment. We have already seen Kconfig, which can be used for configuration, but the problem with Kconfig is that it's global in scope and you can't do bot-specific configuration in it because that would break multi-image. So if you have your 100 bots, they surely don't all boot from the same MMC, for example. So you can just put this into the Kconfig and the solution in Barebox for that is built in environment. You have a default environment. We see that to the right. So the default environment is a directory structure. Indeed, every environment is that can contain boot scripts or binaries that can be loaded, some data, some init scripts that are automatically loaded and some default non-volatile variables, which we see at the very end. Then there is a feature-specific environment that's overlaid on top. For example, if you have device firmware upgrade enabled, it can add you some scripts that are specific to that USB feature. You can have bot-specific environment. So each bot can in the make file for in the bot directory can say BBN and add a new directory that's overlay that can then be overlaid on top. And you can have an external environment, which you will usually use if you have BSP and you are using the same environment for different bots. So Barebox can be just told in the Kconfig, use this directory for an environment that's overlaid at the very end. And this environment is built-in. It's built-in into Barebox. And at runtime, Barebox will mount it under slash nth. And from there on bot code and init scripts can read-write it, but that's just a runtime. Persistence is done separately. How does that look like? We can see here. We have these two variables, autoboot and user. And we set autoboot to abort, so it doesn't continue booting because we are debugging. And we set user to my initials. Barebox will tell you NV variables are modified and that they will on shutdown be saved automatically. How that looks like? We can do a recursive listing of the slash nth directory. And we will see at the very end that we now have an autoboot and a user file. And these will have our new values. So a variable like user, that's called a magic variable. They are so-called because they are evaluated at different places in the Barebox execution flow. You can list them with a magic var command. We see that below. So for example, there's opt-arc. If you do option parsing in the shell, it will have option argument. There is boot source, which tells you where Barebox came from. For example, from an MMC or from a USB. And there are also some nifty stuff like boot and provide machine ID, which will have Barebox fix up the global machine ID, which for example comes from your SOC serial into the kernel command line. So system D can use it. Most of these magic variables we see here are global variables. And these global variables will be initialized at startup from the corresponding non-volatile NV variables. So if I set global user, it will be just active for runtime. If I set NV user, this will be saved to the nonvolatile environment. And on the next startup, Barebox will load the environment and check okay, I have an NV user, so it will initialize global user with that value. These global variables or global parameters are so-called because they are associated with an abstract global device. You have many more devices in Barebox and every device driver can associate these device parameters with the devices. Yeah, and that's the way you do runtime configuration in Barebox. So how that looks like, we can see to the left below. We have a device that's called Temp Reboot Mode. This device driver, the driver for this device is Cisco Reboot Mode, which was basically just ported over from Linux. It does parsing of device tree nodes that we see here. This device tree node has some modes, for example normal, loader, fast boot, and each has a value. And if you set this reboot mode, it writes that value at the identified offset in register. And that way, the operating system can return some value to the boot loader, so it customizes the next boot. The driver is, as I said, just ported from Linux, and it adds some Barebox-specific stuff like this death-add-param enums that we see to the right. This registers a parameter of type enumeration, with the name next. That should call a callback whenever it's set, and it has these possible values. We see that in the depth info input, so next has current value of normal. It has the type enumeration, and it has these possible values. And then, as soon as we have registered this parameter, we can interact with it from the shell. So we can echo it, which we are doing here, and we see, okay, the previous value was normal. We can set it, which will call our callback, and then we can do a reset on the next reset Barebox should see, okay, I am now having the reboot mode of fastboot. Let's see how we can tie that all together. You can, so far, have seen that we can do that on the shell. We can, on the shell, set variables, read the value out of them, but often you will want to write scripts. You can do that with hash, which is the default shell in Barebox. You can add hash scripts to slash m slash bin, and from there on the aren't the path, so you can just tap complete to get some, and they can interact with the variables that we have seen. There are also some core shell utilities, like cat, echo, memory display, memory write, so you can modify the variables and also the character devices that we see to the right. So you have different character devices, for example, for eProms, or hardware random number generators that you can interact with. And you can have init scripts that are run automatically, for example, slash m slash init, everything run in that directory will be automatically, everything located in that directory will be run automatically. We have seen that reboot mode before, how that works is that it will source slash m slash b mode, which is what is written in that register on startup, so that way we can define a fast boot boot mode if we would like. And at the end, we can also have boot scripts which customize how Barebox should boot. We see here an example boot script that's called MMC. It's opened in Barebox Code Editor, and we see what it's doing. It's checking if NV boot default is set. If so, there has been an override, so just exit, and then it checks if the boot source is from MMC, then it sets global boot default to be MMC with boot source instance .root concatenated. So what this is basically doing is that it says if I am coming from an SD card, boot, try to boot the root partition on that SD card, the partition that's called boot, and if I come from another SD card or from an MMC, use that one root partition instead. So these are just a few lines of code which you can do a bit more complex setup easily with. So let's see how we can use that all for boot. So Barebox has boot command which is the default entry point for booting. And as you see, it has a big help text because it's a very versatile tool. So instead of using a boot script like we have just seen before, let's take a step back and ask ourselves what is it, what we want from the booting process, how should it look like and then we can see how we can use boot best. So if you have a root FS partition, and that root FS partition has a slash boot directory and that slash boot directory also already has a kernel, a device tree, an init.rd, device tree overlays, boot arguments, it has all this stuff in single place, you shouldn't need any bootloader configuration now because it's all there. At most what you need is a file that describes which kernel to choose, which device tree, which boot arguments and that file you can place on the boot partition itself and then the bootloader doesn't need any extra info, it should detect everything else as soon as it knows which partition it is. So memory layout, you don't need to tell the bootloader where should the kernel be located or where should the device tree be located. You just need these image buffers to be correctly aligned to be non-overlapping and the bootloader can already dynamically determine that, so no need to hard-code it. You shouldn't need to tell the bootloader what kind of file system it is. It can just check some magic signature and just mount it on the fly on first access, so you don't need to do this manual step in front, same goes for the image format. So you could also check the magic signature of the image and call the appropriate handler and you don't want to repeat yourself. Your bootloader already has board-specific information. For example, it needs to know which root partition to use, so it could generate a root equals argument for the kernel and it knows which console to use for output, so it can pass along that exact same console to the kernel, so you don't want to write this again and you still need the ability to exactly specify these if needed. As for the first point Babox uses the bootloader specification, which you can read the full text of on systemd.io we see here an example at a well-known location slash loader slash entries, we have a couple of files and these boot spec files, they have a title for using a menu, they have options that are to be passed to the kernel, they identify a Linux binary, a device tree binary and that's all the information you need, so you can just tell Babox to boot this partition Babox will mount it, it will look at that directory and then it will iterate over the files there, we have one file that references a device tree with a compatible that doesn't match the compatible that the Babox device tree has, so this is ignored then it finds the correct device tree it will say that it will attempt to boot it it will fix up a root option for the kernel to use it will load this into memory and it shows you the command line at the end it will abort because here we have minus default dry run and we see here that this root argument was never explicitly stated in the example that we have shown, this is because Linux append root, which is written in the boot spec, is a Babox extension for appending the correct root automatically this allows us to have a boot spec that's storage agnostic so you could just extract what's in this partition to a network share, makes this available of a network file system, point Babox at it and you can boot it all the same Babox would fix up the correct root equals NFS and IP addresses and so on and that means that you just need one configuration in Babox for such a simple booting scenario which is where to get this partition from and we see that at the very bottom we have mmc. 0.4 which is our fifth partition we start counting from 0 and we just need to put this into boot default and then we are good to go Babox will do everything we have seen here because it starts boot by default after the countdown and then it will just mount check the boot specs and then in the end boot the kernel with the specified parameters this is a very simplified boot we want in this talk to go a bit beyond just booting so let's look about a more complex booting scenarios having two root FS partitions why would you like to have two root FS partitions so we can run one boot partition and then on and so on updates other partition while we are running then we can reset and before that tell the bootloader to try the other partition next time then we can boot into the next system and if that boot process failed for whatever reason we need to have a fallback so AB partitions are uniquely are very suitable for that and how that looks like in Babox it's we see to the left with boot chooser can define different boot targets these two boot targets can have attempts how many times they are retries and there is automatic fallback between them for all of this to work we will need to detect this update failure so it can revert back to the other partition and the user space needs an ability of an update to say well let's try the Neo partition and for that we need mutable variable storage we have the environment which could be used for that which we are already using for that during development for saving say NV variables but it's inadequate for use in production because it lacks redundancy it's not atomic and we would appreciate some other things like authorization by HMAG well-evelling access control so the Babox solution for that is Babox state which is described in the device tree like the reboot mode we have seen before this has a backend this backend can be any device Babox knows how to write to and it defines some set of variables that Buchu can then use which can also be used for some other stuff we have a Babox state utility that allows you to access it from within the operating system so Babox fixes up this device tree snippet into the kernels and from there on we have this Babox state utility that can pass the device tree pass to the kernel and use UDF to find the correct Linux device and that's also configurations that you need then you can just start Babox state and you can use it to get or set variables so the Babox state implementation maintains three copies for redundancy and atomicity and the CRC32 for corruption detection and it says optional support for HMAG well-evelling by cyclically writing and it's strictly for variable storage so you don't need a mutable Babox environment in the field you can leave the Babox environment built in indeed you should do that and you should just use the Babox state for the variables that you need to share with the user space so that way user space can't change your boot scripts for example and it works great with Rauch so there has been a talk about Rauch last year here at the ELCE I have linked another talk here as well so this talk talks about using Babox boot chooser with Rauch specifically so if that's interesting to you you might want to check that out so how does that all look like in the end to boot a system with Babox boot chooser it's basically just configuration in the non-volatile environment you put for example in your BSP so it's compiled into Babox it has some stuff like auto-boot timeout which is set to 0 here you can still abort Babox but this doesn't do a countdown it has a boot default which is boot chooser it sets up the watchdog and then boot chooser is configured to have this number of default attempts what went to reset attempts for example on a power on then it says which boot targets we have and how to boot each target and everything else so whole logic it's already contained in Babox boot chooser you don't need to write any scripts on your own boot chooser can just take care of it that's the normal case sometimes you need the ability to do it another way and for that you can call bootM directly so boot collects these boot entries or runs these boot scripts or passes these boot specs and then it passes it in the end to bootM which is for booting images and you can just write your own boot scripts and specify these variables that bootM is using or call bootM yourself we see here an example bootM is used to boot a file over tftp slash mount slash tftp and then you have that file name how that works is that tftp is an auto mount so whenever it's accessed the first time this command that we see here is run if up all is done so all interfaces are set up then it mounts the tftp file system at that location and this means that you can just handle this file like any other file you can copy it you can flash it on some device or you can use bootM on it which are we doing here so that's all you need to chain load barebox over the network for example this is sometimes a bit more is needed for example because you don't have an image that's just executable but it has some formats that needs to be parsed for example a u-image, a fit image, an elf and these can also be generically handled in barebox by defining a new bootM handler we see that here for this barebox image the barebox image we talked at start that it has an stm32 header and we need to skip that header to reach the executable code and that's what barebox stm32 image handler is doing so it measures against a file type we have a file type function in barebox that checks the magic and returns a file type like the file command under Linux and then it associates that file type with bootM callback that will then get a buffer and do all the necessary things so you can boot set that includes the parts that I wanted to talk in about booting now comes a short part about bring up we have assumed so far that there are already drivers for the functionality you need they implement the bindings that you are using in the device tree, this is not always the case sometimes you need to do port drivers to barebox most subsystem APIs were imported from Linux and are occasionally kept up to date so often drivers are fairly easy to port you just need to adjust them a bit you copy them and then you fix the compiling errors till it works and you test it this is sometimes possible for example by the reboot mode driver because it's mostly generic code it was very easy to port but sometimes you have more involved devices and the kernel uses interrupts for those barebox stm32 interrupts another bootloader that differs in api but it already interacts with the hardware in the way you want if you do that you will need to give special attention to some multi-image incompatibilities that you don't have when you port kernel code for example if devs in the code depending on an SOC type are a big no-no because they break multi-image same goes for attribute weak which can be defined for some SOCs and for some other SOCs different you will want both of them there so it needs to be done generically clashing the finds or as we talked before configuration that's globally hardcoded for example in a k-config or in a makefile this should all be configurable at runtime for example by having a p-handle in the device tree to a node that you can use or by checking what's the compatible offset device tree node and choosing one or the other callback and if all fails you can try to roll your own and if you need it in the kernel you can port it later when you have your driver ready you will usually get the shell access out of the box because the driver course that you are programming against already do have character device that they allocate for IO memory, eProms, block device partitions, OTPs, net consoles, regmaps, files so you just need to implement the same API that the driver is already implementing under Linux to have it integrate with barebox you also have commands for device tree manipulation here is a nice example imagine you have a board in your remote lab and the lever got broken for the SD card you can just run this snippet it will create a new init script and persist it and that says for the multimedia cards 0 that's an alias so it looks alias in the device tree and says for MMC0 set a new property as a fix up that's broken CD what this line is doing is that every time you boot a kernel with this init script active it will fix up a broken CD property into the identified device tree node and then the kernel won't just wait for an interrupt that will never come because your card detects lever on the SD card is broken there are also useful defaults for netboot as we have seen before that factor in your username and the hostname of the board and that's very nice if you share the same board between different developers usually all you need to do for netbooting an unconfigured board is just setting your username and if you have the file at the correct location you can just boot you don't need to do any further modification to your bootloader if you are doing bring up and you don't have yet network because network for example hasn't been ported yet to barebox the driver for it you can also do it over serial RedFS allows reliable communication over serial there is an RFC for that and barebox implements it as a file system and it allows you to mount host directories over serial so you can use that for netbooting there are many more commands we don't have enough time to talk about all commands so there is some image to the left to which I hope is some sort of raw check image which you will look at and see the commands that you most desire if that doesn't work for you you can run the help command that will show you which commands are in barebox and call help with an argument to see what the commands are doing this help text is also mechanically extracted into swing stocks that you can review online if you don't find the command you are looking for it might be not there yet barebox has a nice API for defining your own commands which is basically a bosics like programming environment which means you have device files you can open them you get file descriptors out of them you can read and write to them this code for example out of busybox easier to port you also have a kernel API available so you can control GPIO central SPI transfers you have dedicated commands for each of them but for example if you have protocols that does SPI and uses GPIO for flow control and you would like to interact with it for debugging you could just do that normally we see an example here that's a true command which doesn't do much it has an alias so it can be invoked by two different names it has an empty completion you can have completion for file names for device tree nodes for device names but true needs nothing of that so it defines it has an empty completion and it doesn't do much does so successfully there are many more example in the barebox commands directory that you can check out this concludes my talk this is the last slide where I will talk a bit about recent developments in the last two years so barebox has gained some architecture support for example for the imx8mm q and plus the arm64 layerscape is also supported both our socs from nxp risk5 support initial support for that was merged upstream the stm32 mp1 which we have looked at a bit here in the talk has also made it into barebox the calry mppa was merged and a bit late to the party barebox now has raspberry pi 3 support we don't yet have raspberry pi 4 support if that's something that interests you and you would like to learn more about barebox that could be something you could try to get acquainted with barebox we also now have the ability to read uboot variables the uboot environment this was last possible 10 years ago now there is a proper barebox driver for that that allows you to mount the environment as a file system and interact with it we can now load opti and also do early loading of opti for example if you need to load opti out of the pbl to reduce your attack surface this can now be done on some systems the kernel address sanitizers and address sanitizers are now supported so gcc and clang support in compile time instrumentation of your code to catch memory safety issues and this expects your code to cooperate with it in that it's the allocator poisons memory that is freed and unpoisons memory when it allocates it so you can detect stuff like use of the freeze this is now possible on arm32 arm64 it was possible on sandbox before that because there is already lib address sanitizer this can be readily used undefined behavior sanitizer is now also supported and you can compile large parts of barebox on the sandbox for compile testing and for running static analysis of a barebox device 3 overlays are supported deprope is not yet in barebox but it's doing its final laps on the mailing list so it might very well be by the time you see this talk it's a feature I'm very excited about because it allows us to get rid of this init call shuffling and e-prope differs that we have in barebox kernel driver if that can't get a resource returns e-prope differ and then it's retried at a later time what deprope does is that it recursively on demand will probe the devices that offer the resources that you are needing work queues and slices also makes our way into barebox so far you could have polys these polys can for example flash a heartbeat LED or feed a watchdog and they are called whenever you do a delay in barebox this works nicely for these simple stuff but for example if you need to pull for UDP packets you can't reliably do that in Apollo because if your device driver for the network interface for the network chip does itself use a delay you have a nasty recursion going on so what happens with slices is that you can say I would like to have the network slices network slice in the network stack and then if Apollo needs a network slice it will try to claim it and if it can't it will just return and will be retried at a later time at which it can do stuff or the network work queues are a way to schedule work items that are run in the context of the shell and in the context of the shell you can basically do everything and everything is currently that barebox answers ICMP echo requests so now you can quite quickly reply to pings in barebox and fast boot over UDP is now also supported via this work queue mechanism so if that all got you interested you can check the barebox project home at barebox.org we are doing the collaboration of a mailing list hosted by InfraDead so if you have patches or questions you can ask there a public service announcement that had an outage so if you were subscribed or unsubscribed this might have been reset so you might want to check that out we are also an IRC if you prefer that and if you don't have a Raspberry Pi 4 handy you can compile for sandbox and run barebox under Linux and play around with it a bit so thank you for listening and do you have any questions