mirror of
https://github.com/openembedded/meta-openembedded.git
synced 2026-07-15 16:07:26 +00:00
image_types_verity.bbclass: add FEC support
dm-verity can use Reed-Solomon forward error correction (FEC) data to transparently recover a limited number of corrupted blocks instead of only detecting them. This can be useful for mitigating error-prone storage. We generate this FEC data as an option, controlled by VERITY_FEC (default off, same behavior as before) and VERITY_FEC_ROOTS (number of Reed-Solomon roots, default 2). The FEC data is appended to the hash tree by default. If VERITY_IMAGE_FECDEV_SUFFIX is set, a separate file with the given suffix will be used. When enabled, the parameter file gains additional variables necessary for correct DM table generation. Recipes that do not set VERITY_FEC = "1" behave the same as before. Signed-off-by: Andreas Zdziarstek <andreas.zdziarstek@gmail.com> Signed-off-by: Khem Raj <khem.raj@oss.qualcomm.com>
This commit is contained in:
committed by
Khem Raj
parent
549ad825ea
commit
3755354fb2
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user