9

I am working on a project where upgrades to the Raspberry PI will over HTTP, and the Raspberry PI will not be directly accessible (cannot just swap cards).

I would like to have a partition setup like so:

  • Partition 1- /boot (contains kernels for both partitions)
  • Partition 2- / (recovery partition)
  • Partition 3- / (primary partition)

When an upgrade goes bad and the Raspberry PI gets into a reboot loop, or hangs on boot, I'd like the user to be able to press a button, which triggers a GPIO line, which would cause the boot loader to boot into the recovery partition instead of the primary partition.

The recovery partition would never be upgraded, so this would be safe.

I see a couple of options:

  1. Always boot into the recovery partition, check GPIO, then boot into primary partition of no button pressed
  2. GPIO is checked by the boot loader directly

I'm basically trying to do something similar to what routers do, where if you hold in reset while it boots you can TFTP over a new image or something.

Is this possible with the Raspberry PI? If so, is there any documentation for doing this sort of thing?

Edit:

I found this answer to this related question: Is it possible to dual boot from the SD card?

A comment on the above question led me here: http://www.berryterminal.com/doku.php/berryboot. This looks promising, but I'll have to research it more to see if I can get a GPIO read from it. If anyone has any experience with it, I'd be very interested.

beatgammit
  • 91
  • 7
  • Let us know how you get on with that, looks interesting :) – Jivings Dec 04 '12 at 08:12
  • 1
    I think I found a way to accomplish this by writing a simple 2nd stage bootloader, so I'll make sure to document it here if I get around to it. Still hoping for a simple solution though... – beatgammit Dec 04 '12 at 18:29
  • Any success with 2nd stage bootloader? – avra Nov 27 '13 at 12:39
  • 1
    Unfortunately no, but I significantly reduced the chance of corruption using a RO filesystem: /boot (RO), / (RO), /var (RW), /home (RW). The initial problem was filesystem corruption when power is cut during boot. I'd still like like to write/find a 2nd stage bootloader though. – beatgammit Nov 30 '13 at 20:45
  • 1
    You could look at what the NOOBS (http://www.raspberrypi.org/archives/4100) bootloader for the Pi does. It might actually suit your purposes just to use it, as it provides for recovery partitions and installation. – Fred Mar 07 '14 at 08:11
  • @AwesomeUser - I wish I could say I have, but I'll make sure to do that when I find a solution. – beatgammit Mar 17 '14 at 03:19
  • @AwesomeUser Don't suggest that users post some of their hard earned bounty on a question. They should do that on their own. I, at least, would feel that as pushy and I wouldn't be very happy and definitely wouldn't follow those instructions. Just some helpful criticism. :) – RPiAwesomeness Mar 20 '14 at 17:56
  • 1
    I am suggesting ''against'' using a custom bootloader, but use hacks that runs after the system is started. It will be easier to implement and safer to debug. A faulty bootloader can fry your Pi. – Maxthon Chan Mar 24 '14 at 05:19

3 Answers3

5

If the error can happen any time after the system started, you can use a watchdog timer and some boot script.

The principle of this method is that whenever the system reboots without properly shut down, it goes to the recovery partition.

You need to write a script that runs every time the system starts and properly shuts down. Change /boot/cmdline.txt to recovery partition when the system starts, and changes back before properly shut down.

Then set up the watchdog timer. You can use the built-in one in the BCM2835 chip, or (if using a revision 2 board) construct your own using two GPIO pins, the reset header P6 and a 555 chip. When the critical program is started start the watchdog timer, and kick the dog periodically if the system is working properly. When the system fails the watchdog timer will be tripped and resets the processor, sends it to the recovery partition. This requires no user interaction as well and if using the built-in timer, no GPIO.

Using this method, you can also implement a reset button that will guarantee to send the system to recovery manually on a Rev. 2 board by installing a button to P6 header.

Maxthon Chan
  • 1,051
  • 7
  • 14
  • Your first answer is a bit closer to what I'd probably do, but I like the idea of using GPIOs in userspace and a watchdog. While I probably won't use any of the ideas wholesale, you've given me some good ideas. Thanks! – beatgammit Mar 24 '14 at 05:54
1

I have a way of doing this without hacking the kernel, that involves protecting the system from an untimely reboot:

  1. Download the upgrade image, checksum it and expand it into scratch space.
  2. Change /boot/cmdline.txt so that the next time the system boots it uses recovery partition as the root block device.
  3. Install the upgrade from the scratch space and verify that it is working.
  4. If the upgrade is working, change /boot/cmdline.txt back.
  5. If required, reboot.

A failed upgrade will cause the system to be booted into the recovery automatically. No GPIO needed.

Maxthon Chan
  • 1,051
  • 7
  • 14
  • The only issue here is (depending on the system) a fault may be detected hours after the update completes, at which point the user has no option to revert to the recovery partition. –  Mar 18 '14 at 09:12
  • 1
    @DanNixon I am posting another answer to resolve this new issue. – Maxthon Chan Mar 18 '14 at 09:28
1

Also, there is a third possible solution, but it will require you to dissect initrd of some PC version of Linux distribution to figure out how the syscall pivot_init() works. I am not sure if the kernel of Pi have this syscall. If it does, this method is possible, no kernel hack is required as well, and it uses one GPIO.

To do that, you will need to write a custom init program in the production system. check if the GPIO is on. If so, pivot_root() to recovery. Then exec() the original init so that the system continues to boot. You can, in the production system, figure out a way to keep this GPIO-watching constructed init running side-by-side (PID=2) with the original init (PID=1) and keep an eye on the GPIO and reboot to recovery if the button is pressed.

Maxthon Chan
  • 1,051
  • 7
  • 14