Saturday, 5 July 2014

BootJacker: The Amazing AVR Bootloader Hack!

There's an old adage that says if you don't know it's impossible you could end up achieving it. BootJacker is that kind of hack: a way for ordinary firmware on an AVR to reprogram its bootloader. It's something Atmel's manual for AVR microcontrollers with Bootloaders says is impossible (Note the italics):

27.3.1 Application Section.
The Application section is the section of the Flash that is used for storing the application code. The protection level for the Application section can be selected by the application Boot Lock bits (Boot Lock bits 0), see Table 27-2 on page 284. The Application section can never store any Boot Loader code since the SPM instruction is disabled when executed from the Application section.

Here's the background: I'm the designer of FIGnition, the definitive DIY 8-bit computer. It's not cobbled together from hackware from around the web, instead three years of sweat and bare-metal development has gone into this tiny 8-bitter. I've been working on firmware version 1.0.0 for a few months; the culmination of claiming that I'll put audio data transfer on the machine (along with a fast, tiny Floating point library about 60% of the size of the AVR libc one).

Firmware 1.0.0 uses the space previously occupied by its 2Kb USB bootloader and so, needs its own migration firmware image to copy the V1.0.0 firmware to external flash. The last stage is to reprogram the bootloader with a tiny 128b bootloader which reads the new image from external flash. Just as I got to the last stage I came across section 27.3.1, which let me know in no uncertain terms that I was wasting my time.

I sat around dumbstruck for a while ("How could I have not read that?") before wondering whether[1], crazies of crazy, imagining that a solution to the impossible might actually lead me there. And it turns out it does.

The solution is actually conceptually fairly simple. A bootloader, by its very nature is designed to download new firmware to the device. Therefore it will contain at least one spm instruction. Because the spm configuration register must be written no more than 4 cycles before the spm instruction it means there are very few sequences that practically occur: just sts, spm or out, spm sequences. So, all you need to is find the sequence in the bootloader section; set up the right registers and call it.

However, it turned out there was a major problem with that too. The V-USB self-programming bootloader's spm instructions aren't a neat little routine, but are inlined into the main code; so calling it would just cause the AVR to crash as it tried to execute the rest of the V-USB bootloader.

Nasty, but again there's a solution. By using a timer clocked at the CPU frequency (which is easy on an AVR), you can create a routine in assembler which sets up the registers for the Bootloader's out, spm sequence; calls it and just at the moment when it's executed the first cycle of the spm itself, the timer interrupt goes off and the AVR should jump to your interrupt routine (in Application space). The interrupt routine pops the bootloader address and then returns to the previous code - which is the routine that sets up the outspm sequence. This should work, because when you apply spm instructions to the bootloader section the CPU is halted until it's complete.

Here's the key part of BootJacker:

The code uses the Bootloader's spm to first write a page of flash which also contains a usable outspm sequence and then uses that routine to write the rest (because of course you might end up overwriting the bootloader with your own new bootloader!)

BootJacker involves cycle counting, I used a test routine to figure out the actual number of instructions executed after you set the timer for x cycles in the future (it's x-2). In addition I found there was one other oddity: erase and writes always have a 1 cycle latency after the SPM in a bootloader. I fixed this with a nop instruction in my mini bootloader.

This algorithm, I think is pretty amazing. It means that most bootloaders can in fact be overwritten using application firmware containing a version of BootJacker!

[1] As a Christian, I also have to fess' up that I prayed about it too. Not some kind of desperation thing, but some pretty calm prayer, trusting it'll get sorted out :-) 


Snowcamo said...

[1] As a Christian, I also have to fess' up that I prayed about it too. Not some kind of desperation thing, but some pretty calm prayer, trusting it'll get sorted out :-)

This never fails, and has a habit of transforming impossible things into possible (Luke 1:37).

My most memorable case was a time critical piece of assembler 20 years ago, where I had to multiply a variable with a constant and there really was neither time nor fast multiply available. The constant was from a complex formula and when I finally got it calculated, it turned out to be 256.000xxxxxx. The precision was plenty enough.

Bubz said...

As an atheist, I must admit I don't believe prayer is what what made this awsome hack reality, but rather the unique skillset of a very talented hacker. I don't want to start any religious thing going, I just wanted to acknowledge how awesome this trick is.
I have had times when I needed to use a modified optiboot, as the braindead reset trickery to get to bootloader uses rts/dtr to do a hw reset, and optiboot makes this worse by ignoring soft reset. But by modifying the bootloader, you eliminate most casual Arduino users, as they would never get into that.
This is a very sweet trick, big ups!!!

quibelez said...

reaction = current_mood ? yeah_sure : no_brainer;

ptdecker said...

Nice hack!

Snial said...

Hi guys,

Thanks for the comments! @Snowcamo - good one!

@Bubz, @quibelez, @ptdecker! Thanks for the encouragements and your takes on how insight comes about, it all comes into play! I wasn't trying to offer much in the way of theology, it's just the blogging equivalent of e.g. Daniel Sturridge giving thanks for his equaliser when playing against Italy :-)

Have a great week all of you: may your talents, moods and algorithmic precision bring you success!

-cheers from Julz

Ralf said...

You didn't need to do any of this to solve your original problem though!

There's a very straightforward serial programming interface (hold RESET low and program with pins PB1,PB2 and PB3. It's even documented in the manual! You can do a full chip erase and from there program any bootloader / application code you like.

Snial said...

Hi Ralf,

Yes, that's called an ICSP (or just ISP), an in-circuit serial programmer. I use a home-brew ICSP to program AVRs from scratch, but when I sell FIGnition computers, I can't expect them to have an ICSP so instead I provide a bootloader on the AVR so that users can upgrade their FIGnition firmware just using a FIGnition and no other hardware. This is the way arduinos are normally programmed, and if you mess up the bootloader there, you will brick your arduino!

Up until now I've used the excellent V-USB software USB bootloader from Objective Development. To provide complete flexibility without incurring USB licensing fees I've switched to audio data transfer and an audio bootloader. This means I really do need to supply a migration program which can update customer's bootloaders.

-cheers from Julz

witnessdigital said...

really cool, I was looking at fuse settings with the AVRdragon to make a self erasing application on AVR including the bootloader.. but this trick allows me to overwrite pretty much everything on the chip... Thanx
its a REALLY neat hack...


Matthijs Kooijman said...

Cool! Nicely thought outside the box :-)