File: examples.rst

package info (click to toggle)
rauc 1.15-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 6,336 kB
  • sloc: ansic: 36,989; python: 3,354; sh: 1,391; xml: 53; makefile: 41
file content (430 lines) | stat: -rw-r--r-- 11,609 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
Examples
========

Full System Example
-------------------

This chapter aims to explain the basic concepts needed for RAUC using a simple
but realistic scenario.

The system is x86-based with 1GiB of disk space and 1GiB of RAM. GRUB_ was
selected as the bootloader and we want to have two symmetric installations.
Each installation consists of an ext4 root file system only (which contains the
matching kernel image).

We want to provide update bundles using a USB memory stick. We don't have a
hardware watchdog, so we need to explicitly tell GRUB_ whether a boot was
successful.

This scenario can be easily reproduced using a QEMU_ virtual machine.

.. _GRUB: https://www.gnu.org/software/grub/
.. _QEMU: http://wiki.qemu.org/

PKI Setup
~~~~~~~~~

RAUC uses an x.509 PKI (public key infrastructure) to sign and verify updates.
To create a simple key pair for testing, we can use ``openssl``::

  > openssl req -x509 -newkey rsa:4096 -nodes -keyout demo.key.pem -out demo.cert.pem -subj "/O=rauc Inc./CN=rauc-demo"

For actual usage, setting up a real PKI (with a CA separate from the signing
keys and a revocation infrastructure) is *strongly* recommended. OpenVPN's
easy-rsa_ is a good first step. See :ref:`sec-security` for more details.

.. _easy-rsa: https://github.com/OpenVPN/easy-rsa

RAUC Configuration
~~~~~~~~~~~~~~~~~~

We need a RAUC system configuration file to describe the slots which can be
updated

.. code-block:: cfg

  [system]
  compatible=rauc-demo-x86
  bootloader=grub
  mountprefix=/mnt/rauc
  bundle-formats=-plain

  [keyring]
  path=demo.cert.pem

  [slot.rootfs.0]
  device=/dev/sda2
  type=ext4
  bootname=A

  [slot.rootfs.1]
  device=/dev/sda3
  type=ext4
  bootname=B

In this case, we need to place the signing certificate into the same
directory as the ``system.conf``, so that it is used by RAUC for verification.

GRUB Configuration
~~~~~~~~~~~~~~~~~~

GRUB itself is stored on ``/dev/sda1``, separate from the root file system. To
access GRUB's environment file, this partition should be mounted to ``/boot``
(which means that the environment file is found at ``/boot/grub/grubenv``).

GRUB does not provide the boot target selection logic as needed by RAUC
out of the box. Instead we use a script to implement it

.. code-block:: sh

  # set default menuentry (Slot A) and timeout (3s)
  default=0
  timeout=3

  any_ok=0

  set ORDER="A B"
  set A_OK=0
  set B_OK=0
  set A_TRY=0
  set B_TRY=0
  load_env

  # select bootable slot
  for SLOT in $ORDER; do
      if [ "$SLOT" == "A" ]; then
          INDEX=0
          OK=$A_OK
          TRY=$A_TRY
          A_TRY=1
      fi
      if [ "$SLOT" == "B" ]; then
          INDEX=1
          OK=$B_OK
          TRY=$B_TRY
          B_TRY=1
      fi
      if [ "$OK" -eq 1 -a "$TRY" -eq 0 ]; then
          default=$INDEX
          any_ok=1
          break
      fi
  done

  # reset booted flags in case both sides have failed to boot
  if [ "$any_ok" -eq 0 ]; then
      if [ "$A_OK" -eq 1 -a "$A_TRY" -eq 1 ]; then
          A_TRY=0
      fi
      if [ "$B_OK" -eq 1 -a "$B_TRY" -eq 1 ]; then
          B_TRY=0
      fi
  fi

  save_env A_TRY B_TRY

  CMDLINE="panic=60 quiet"

  menuentry "Slot A (OK=$A_OK TRY=$A_TRY)" {
      linux (hd0,2)/kernel root=/dev/sda2 $CMDLINE rauc.slot=A
  }

  menuentry "Slot B (OK=$B_OK TRY=$B_TRY)" {
      linux (hd0,3)/kernel root=/dev/sda3 $CMDLINE rauc.slot=B
  }

GRUB since 2.02-beta1 supports the ``eval`` command, which can be used
to express the logic above more concisely.

The ``grubenv`` file can be modified using ``grub-editenv``, which is shipped
by GRUB. It can also be used to inspect the current contents::

  > grub-editenv /boot/grub/grubenv list
  ORDER="A B"
  A_OK=0
  B_OK=0
  A_TRY=0
  B_TRY=0

The initial installation of the bootloader and rootfs on the system is out of
scope for RAUC. A common approach is to generate a complete disk image
(including the partition table) using a build system such as
OpenEmbedded/Yocto, PTXdist or buildroot.

.. _sec-example-bundle-generation:

Bundle Generation
~~~~~~~~~~~~~~~~~

To create a bundle, we need to collect the components which should become part
of the update in a directory (in this case only the root file system image)::

  > mkdir temp-dir/
  > cp …/rootfs.ext4.img temp-dir/

Next, to describe the bundle contents to RAUC, we create a *manifest* file.
This must be named  ``manifest.raucm``::

  > cat >> temp-dir/manifest.raucm << EOF
  [update]
  compatible=rauc-demo-x86
  version=2015.04-1

  [bundle]
  format=verity

  [image.rootfs]
  filename=rootfs.ext4.img
  EOF

Note that we can omit the ``sha256`` and ``size`` parameters for the image
here, as RAUC will fill them out automatically when creating the bundle.

Finally, we run RAUC to create the bundle::

  > rauc --cert demo.cert.pem --key demo.key.pem bundle temp-dir/ update-2015.04-1.raucb
  > rm -r temp-dir

We now have the ``update-2015.04-1.raucb`` bundle file, which can be copied onto the
target system, in this case using a USB memory stick.

Update Installation
~~~~~~~~~~~~~~~~~~~

Having copied ``update-2015.04-1.raucb`` onto the target, we only need to run RAUC::

  > rauc install /mnt/usb/update-2015.04-1.raucb

After cyptographically verifying the bundle, RAUC will now determine the
active slots by looking at the ``rauc.slot`` variable. Then, it can select the
target slot for the update image from the inactive slots.

When the update is installed completely, we just need to restart the system. GRUB
will then try to boot the newly installed rootfs. Finally, if the boot was
successful, we need to inform the bootloader::

  > rauc status mark-good

If systemd_ is available, it is useful to run this command late in the boot
process and declare dependencies on the main application(s).

.. _systemd: http://www.freedesktop.org/wiki/Software/systemd/

If the boot is not marked as successful, GRUB will try the other installation
on the next boot. By configuring the kernel and systemd to reboot on
critical errors and by using a (software) watchdog, hangs in a non-working
installation can be avoided.

Write Slots Without Update Mechanics
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Assuming an image has been copied to or exists on the target, a manual slot
write can be performed by::

  > rauc write-slot rootfs.0 rootfs.ext4

This will write the rootfs image ``rootfs.ext4`` to the slot ``rootfs.0``. Note
that this bypasses all update mechanics like hooks, slot status etc.

.. _sec-example-slot-configs:

Example Slot Configurations
---------------------------

This provides some common examples on how to configure slots in your
system.conf for different scenarios.

Symmetric A/B Setup
~~~~~~~~~~~~~~~~~~~

This is the default case when having a fully-redundant root file system

.. code-block:: cfg
  :emphasize-lines: 3, 6, 8, 11

  [...]

  [slot.rootfs.0]
  device=/dev/sda2
  type=ext4
  bootname=A

  [slot.rootfs.1]
  device=/dev/sda3
  type=ext4
  bootname=B


Asymmetric A/Recovery Setup
~~~~~~~~~~~~~~~~~~~~~~~~~~~

In case storage is too restricted for a full A/B redundancy setup, an
asymmetric setup with a dedicated update/recovery slot can be used.
The recovery slot can be way smaller than the rootfs one as it needs to contain
only the tools for updating the rootfs slot.
Because the recovery slot is not meant to be updated in most cases, we can
manifest this for RAUC by setting the ``readonly=true`` option.

.. code-block:: cfg
  :emphasize-lines: 3, 6, 7, 9, 12

  [...]

  [slot.recovery.0]
  device=/dev/sda2
  type=ext4
  bootname=R
  readonly=true

  [slot.rootfs.0]
  device=/dev/sda3
  type=ext4
  bootname=A

Separate Application Partition
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

RAUC allows to have a separate redundant set of slots for the application (or
other purpose) that have a fixed relation to their corresponding rootfs slots.
RAUC assures that an update of the entire slot group (rootfs + appfs) is
atomic.

When defining appfs slots, be sure to set the correct `parent` relation to the
associated bootable slot.

.. code-block:: cfg
  :emphasize-lines: 14, 19

  [...]

  [slot.rootfs.0]
  device=/dev/sda2
  type=ext4
  bootname=A

  [slot.rootfs.1]
  device=/dev/sda3
  type=ext4
  bootname=B

  [slot.appfs.0]
  parent=rootfs.0
  device=/dev/sda4
  type=ext4

  [slot.appfs.1]
  parent=rootfs.1
  device=/dev/sda5
  type=ext4

Atomic Bootloader Updates (eMMC)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Updating the Bootloader is also possible with RAUC, despite this is a bit more
critical than updating the rootfs, as there is no fallback mechanism.

However, depending on the ROM loader it can at least be possible to perform the
bootloader update atomically.
The most common example for this is using the two boot partitions of an eMMC
for atomic bootloader updates which RAUC supports out-of-the-box
(refer :ref:`sec-emmc-boot`).

.. code-block:: cfg
  :emphasize-lines: 3, 5

  [...]

  [slot.bootloader.0]
  device=/dev/mmcblk0
  type=boot-emmc

  [slot.rootfs.0]
  device=/dev/mmcblk0p1
  type=ext4
  bootname=A

  [slot.rootfs.1]
  device=/dev/mmcblk0p2
  type=ext4
  bootname=B

Symmetric A/B Setup + Recovery
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Booting into the recovery slot should normally be handled by the bootloader
if it fails to load the symmetric slots.

Thus from the RAUC perspective this setup is identical to the default A/B
setup.

Anyway, you can still define it as a slot if you need to be able to provide
an update for this, too.

Symmetric A/B Setup + Shared (Single-File) Artifacts Repository
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Configuring a ``file`` *artifact repository* in addition to the A/B *system
slots* can be useful for systems which use one or several larger data files
(e.g.  map data) that should be updated more frequently and/or independently of
the main A/B rootfs.

.. code-block:: cfg
  :emphasize-lines: 13-15

  [...]

  [slot.rootfs.0]
  device=/dev/sda2
  type=ext4
  bootname=A

  [slot.rootfs.1]
  device=/dev/sda3
  type=ext4
  bootname=B

  [artifacts.map-data]
  path=/srv/maps
  type=file

The artifacts directory ``maps`` from this example must be located on a separate
(shared) partition that is mounted to the respective active rootfs slot under
``/srv``.

Example Integrations
--------------------

There are a couple of community projects that can serve as a base or blueprint
for integrating RAUC into projects or products.

OpenEmbedded / Yocto Project
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. rubric:: meta-rauc-community

The `meta-rauc-community repository
<https://github.com/rauc/meta-rauc-community>`_ contains layers for some
platforms, demonstrating different ways to use RAUC.

Currently supported platforms are:

* `qemux86-64 <https://github.com/rauc/meta-rauc-community/tree/master/meta-rauc-qemux86>`_
* `RaspberryPi <https://github.com/rauc/meta-rauc-community/tree/master/meta-rauc-raspberrypi>`_
* `CuBox-i <https://github.com/rauc/meta-rauc-community/tree/master/meta-rauc-nxp>`_
* `Allwinner SunXi <https://github.com/rauc/meta-rauc-community/tree/master/meta-rauc-sunxi>`_
* `NVIDIA Tegra <https://github.com/rauc/meta-rauc-community/tree/master/meta-rauc-tegra>`_

.. rubric:: Eclipse Leda

Leda, the Eclipse project for software-defined vehicles, provides an example
RAUC integration:

https://eclipse-leda.github.io/leda/docs/device-provisioning/self-update/rauc-integration/

Buildroot
~~~~~~~~~

.. rubric:: Buildroot + RAUC (br2rauc)

The `br2rauc <https://github.com/cdsteinkuehler/br2rauc>`_ project provides
an example Buildroot integration for the Raspberry PI CM4.