diff --git a/meta-oe/classes/image_types_verity.bbclass b/meta-oe/classes/image_types_verity.bbclass
index 24462277af..efe94a2987 100644
--- a/meta-oe/classes/image_types_verity.bbclass
+++ b/meta-oe/classes/image_types_verity.bbclass
@@ -36,6 +36,60 @@
 #   dd if=/dev/random bs=1k count=1 | sha256sum
 #
 # and assign it to the parameter VERITY_SALT.
+#
+# Forward Error Correction (FEC) can optionally be generated by setting
+# VERITY_FEC to "1". dm-verity then transparently recovers a limited number of
+# corrupted blocks instead of only detecting them.
+#
+# The number of Reed-Solomon roots is set via VERITY_FEC_ROOTS (default 2, max
+# 24). Higher number of roots means higher error resilience but also more
+# overhead in terms of space.
+#
+# The FEC data is either appended after the hash tree or put in its own file,
+# using VERITY_IMAGE_FECDEV_SUFFIX.
+#
+# When FEC is enabled, additional values are appended to the parameter file:
+#
+#   VERITY_FEC_RS_ROOTS - number of Reed-Solomon roots
+#   VERITY_FEC_BLOCKS   - number of parity blocks generated on the FEC device.
+#                         This is the FEC overhead and is NOT the kernel table's
+#                         "fec_blocks" argument (see the example below).
+#   VERITY_FEC_START    - offset (in VERITY_DATA_BLOCK_SIZE blocks) of the FEC
+#                         data on the FEC device.
+#
+# extending the dmsetup example above (with <fec_dev> as the FEC blockdevice) to
+#
+#   . <IMAGE_LINK_NAME>.verity-params
+#   dmsetup create <dm_dev_name> --readonly --table "0 $VERITY_DATA_SECTORS \
+#       verity 1 <dev> <hash_dev> \
+#       $VERITY_DATA_BLOCK_SIZE  $VERITY_HASH_BLOCK_SIZE \
+#       $VERITY_DATA_BLOCKS  $VERITY_DATA_BLOCKS \
+#       $VERITY_HASH_ALGORITHM  $VERITY_ROOT_HASH  $VERITY_SALT \
+#       9 ignore_zero_blocks use_fec_from_device <fec_dev> \
+#       fec_roots $VERITY_FEC_RS_ROOTS \
+#       fec_blocks $((VERITY_DATA_BLOCKS + VERITY_HASH_BLOCKS)) \
+#       fec_start $VERITY_FEC_START"
+#
+# Note that the kernel's "fec_blocks" is the number of blocks covered by FEC,
+# i.e. the data and hash blocks together (VERITY_DATA_BLOCKS +
+# VERITY_HASH_BLOCKS), and differs from VERITY_FEC_BLOCKS, which counts the
+# generated parity blocks.
+#
+# The same device as above can be configured with veritysetup, using
+#
+#   . <IMAGE_LINK_NAME>.verity-params
+#   veritysetup open --no-superblock <dev> <dm_dev_name> <dev> $VERITY_ROOT_HASH \
+#       --data-block-size=$VERITY_DATA_BLOCK_SIZE \
+#       --hash-block-size=$VERITY_HASH_BLOCK_SIZE \
+#       --data-blocks=$VERITY_DATA_BLOCKS \
+#       --hash-offset=$((VERITY_DATA_BLOCKS * VERITY_DATA_BLOCK_SIZE)) \
+#       --salt=$VERITY_SALT \
+#       --fec-device=<dev> \
+#       --fec-offset=$((VERITY_FEC_START * VERITY_DATA_BLOCK_SIZE)) \
+#       --fec-roots=$VERITY_FEC_RS_ROOTS
+#
+# NOTE: To be able to use dm-verity with FEC, your kernel must have
+#       CONFIG_DM_VERITY_FEC enabled.
 
 inherit image-artifact-names
 
@@ -48,6 +102,9 @@ VERITY_IMAGE_FSTYPE ?= "ext4"
 VERITY_IMAGE_SUFFIX ?= ".verity"
 VERITY_IMAGE_HASHDEV_SUFFIX ?= "${VERITY_IMAGE_SUFFIX}"
 VERITY_INPUT_IMAGE ?= "${IMGDEPLOYDIR}/${IMAGE_LINK_NAME}.${VERITY_IMAGE_FSTYPE}"
+VERITY_FEC ?= ""
+VERITY_FEC_ROOTS ?= "2"
+VERITY_IMAGE_FECDEV_SUFFIX ?= "${VERITY_IMAGE_HASHDEV_SUFFIX}"
 
 IMAGE_TYPEDEP:verity = "${VERITY_IMAGE_FSTYPE}"
 IMAGE_TYPES_MASKED += "verity"
@@ -75,6 +132,10 @@ python do_image_verity () {
     verity_image_hashdev_suffix = d.getVar('VERITY_IMAGE_HASHDEV_SUFFIX')
     verity_hashdev = '{}{}'.format(image, verity_image_hashdev_suffix)
 
+    fec = bb.utils.to_boolean(d.getVar('VERITY_FEC'))
+    verity_image_fecdev_suffix = d.getVar('VERITY_IMAGE_FECDEV_SUFFIX')
+    verity_fecdev = '{}{}'.format(image, verity_image_fecdev_suffix)
+
     # For better readability the parameter VERITY_BLOCK_SIZE is specified in
     # bytes. It must be a multiple of the logical sector size which is 512 bytes
     # in Linux. Make sure that this is the case as otherwise the resulting
@@ -87,6 +148,11 @@ python do_image_verity () {
     if salt == d.getVar('CLASS_VERITY_SALT'):
         bb.warn("Please overwrite VERITY_SALT with an image specific one!")
 
+    if fec:
+        fec_roots = int(d.getVar('VERITY_FEC_ROOTS'))
+        if not 2 <= fec_roots <= 24:
+            bb.fatal("VERITY_FEC_ROOTS must be between 2 and 24 (got %d)!" % fec_roots)
+
     shutil.copyfile(image, verity)
 
     data_size_blocks, data_size_rest = divmod(os.stat(verity).st_size, block_size)
@@ -125,6 +191,26 @@ python do_image_verity () {
                 verityfile.seek(0, io.SEEK_END)
                 verityfile.write(b'\x00' * (block_size - data_size_rest))
 
+    # Optionally generate Reed-Solomon FEC data. If the FEC data shares a file
+    # with the hash tree, it must start past it. Do a first veritysetup pass to
+    # get the size of the hash tree and calculate the offset. With a dedicated
+    # FEC file the FEC data starts at offset 0 and a single pass suffices.
+    fec_offset = 0
+    if fec:
+        if verity_fecdev == verity_hashdev:
+            try:
+                subprocess.check_output(veritysetup_command)
+            except subprocess.CalledProcessError as err:
+                bb.fatal('%s returned with %s (%s)' % (err.cmd, err.returncode, err.output))
+            fec_offset = os.stat(verity_hashdev).st_size
+            if fec_offset % block_size != 0:
+                bb.fatal("hash tree end (%d) is not a multiple of the block size" % fec_offset)
+            veritysetup_command.append('--fec-offset={}'.format(fec_offset))
+        veritysetup_command += [
+            '--fec-device={}'.format(verity_fecdev),
+            '--fec-roots={}'.format(fec_roots),
+        ]
+
     # Create verity image
     try:
         output = subprocess.check_output(veritysetup_command)
@@ -151,6 +237,13 @@ python do_image_verity () {
 
     params.append('VERITY_DATA_SECTORS={}'.format(data_size//512))
 
+    if fec:
+        # Where the FEC data starts on its device, in blocks. It's not part
+        # of veritysetup's output, so emit it for `dmsetup create ...`.
+        # A byte offset, if needed (e.g. for `veritysetup open ...`), is
+        # VERITY_FEC_START * VERITY_DATA_BLOCK_SIZE.
+        params.append('VERITY_FEC_START={}'.format(fec_offset//block_size))
+
     try:
         with open(image + '.verity-params', 'w') as f:
             f.write('\n'.join(params))
@@ -161,6 +254,8 @@ python do_image_verity () {
     suffix_list = [ verity_image_suffix, '.verity-info', '.verity-params' ]
     if verity != verity_hashdev:
         suffix_list.append(verity_image_hashdev_suffix)
+    if fec and verity_fecdev not in (verity, verity_hashdev):
+        suffix_list.append(verity_image_fecdev_suffix)
 
     for suffix in suffix_list:
         try:
