summaryrefslogtreecommitdiff
path: root/src/commonlib/bsd/include
diff options
context:
space:
mode:
authorJulius Werner <jwerner@chromium.org>2019-12-11 16:19:48 -0800
committerPhilipp Deppenwiese <zaolin.daisuki@gmail.com>2020-10-30 11:13:35 +0000
commit0655f78041ef617844f436306fa5431e211f4431 (patch)
tree90e3382ce42dbdb6636b52f028f34403a0c4f56b /src/commonlib/bsd/include
parent0a9eea0f5bb192a7e92115c87480836fb3395471 (diff)
commonlib/bsd: Add new CBFS core implementation
This patch adds a new CBFS implementation that is intended to replace the existing commonlib/cbfs.c. The new implementation is designed to meet a bunch of current and future goals that in aggregate make it easier to start from scratch than to adapt the exisiting implementation: 1. Be BSD-licensed so it can evetually be shared with libpayload. 2. Allow generating/verifying a metadata hash for future CBFS per-file verification (see [1][2]). 3. Be very careful about reading (not mmaping) all data only once, to be suitable for eventual TOCTOU-safe verification. 4. Make it possible to efficiently implement all current and future firmware use cases (both with and without verification). The main primitive is the cbfs_walk() function which will traverse a CBFS and call a callback for every file. cbfs_lookup() uses this to implement the most common use case of finding a file so that it can be read. A host application using this code (e.g. coreboot, libpayload, cbfstool) will need to provide a <cbfs_glue.h> header to provide the glue to access the respective CBFS storage backend implementation. This patch merely adds the code, the next patch will integrate it into coreboot. [1]: https://www.youtube.com/watch?v=Hs_EhewBgtM [2]: https://osfc.io/uploads/talk/paper/47/The_future_of_firmware_verification_in_coreboot.pdf (Note: In early discussions the metadata hash was called "master hash".) Change-Id: Ica64c1751fa37686814c0247460c399261d5814c Signed-off-by: Julius Werner <jwerner@chromium.org> Reviewed-on: https://review.coreboot.org/c/coreboot/+/38421 Reviewed-by: Aaron Durbin <adurbin@chromium.org> Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Diffstat (limited to 'src/commonlib/bsd/include')
-rw-r--r--src/commonlib/bsd/include/commonlib/bsd/cb_err.h5
-rw-r--r--src/commonlib/bsd/include/commonlib/bsd/cbfs_private.h116
-rw-r--r--src/commonlib/bsd/include/commonlib/bsd/cbfs_serialized.h6
3 files changed, 124 insertions, 3 deletions
diff --git a/src/commonlib/bsd/include/commonlib/bsd/cb_err.h b/src/commonlib/bsd/include/commonlib/bsd/cb_err.h
index ab419a7709..e5aa852617 100644
--- a/src/commonlib/bsd/include/commonlib/bsd/cb_err.h
+++ b/src/commonlib/bsd/include/commonlib/bsd/cb_err.h
@@ -34,6 +34,11 @@ enum cb_err {
CB_I2C_PROTOCOL_ERROR = -302, /**< Data lost or spurious slave
device response, try again? */
CB_I2C_TIMEOUT = -303, /**< Transmission timed out */
+
+ /* CBFS errors */
+ CB_CBFS_IO = -400, /**< Underlying I/O error */
+ CB_CBFS_NOT_FOUND = -401, /**< File not found in directory */
+ CB_CBFS_HASH_MISMATCH = -402, /**< Master hash validation failed */
};
/* Don't typedef the enum directly, so the size is unambiguous for serialization. */
diff --git a/src/commonlib/bsd/include/commonlib/bsd/cbfs_private.h b/src/commonlib/bsd/include/commonlib/bsd/cbfs_private.h
new file mode 100644
index 0000000000..aaee62f4c3
--- /dev/null
+++ b/src/commonlib/bsd/include/commonlib/bsd/cbfs_private.h
@@ -0,0 +1,116 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-or-later */
+
+#ifndef _COMMONLIB_BSD_CBFS_PRIVATE_H_
+#define _COMMONLIB_BSD_CBFS_PRIVATE_H_
+
+
+#include <commonlib/bsd/cb_err.h>
+#include <commonlib/bsd/cbfs_serialized.h>
+#include <endian.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <vb2_sha.h>
+
+/*
+ * This header implements low-level CBFS access APIs that can be shared across different
+ * host applications (e.g. coreboot, libpayload, cbfstool). For verification purposes it
+ * implements the metadata hashing part but not the file hashing part, so the host application
+ * will need to verify file hashes itself after loading each file. Host applications that use
+ * verification should implement wrapper APIs that combine the lookup, loading and hashing steps
+ * into a single, safe function call and outside of the code implementing those APIs should not
+ * be accessing the low-level APIs in this file directly (e.g. coreboot SoC/driver code should
+ * never directly #include this file, and always use the higher level APIs in src/lib/cbfs.c).
+ *
+ * <cbfs_glue.h> needs to be provided by the host application using this CBFS library. It must
+ * define the following type, macros and functions:
+ *
+ * cbfs_dev_t An opaque type representing a CBFS storage backend.
+ * CBFS_ENABLE_HASHING Should be 0 to avoid linking hashing features, 1 otherwise. (Only for
+ * metadata hashing. Host application needs to check file hashes itself.)
+ * ERROR(...) printf-style macro to print errors.
+ * LOG(...) printf-style macro to print normal-operation log messages.
+ * DEBUG(...) printf-style macro to print detailed debug output.
+ *
+ * ssize_t cbfs_dev_read(cbfs_dev_t dev, void *buffer, size_t offset, size_t size);
+ * Read |size| bytes starting at |offset| from |dev| into |buffer|.
+ * Returns amount of bytes read on success and < 0 on error.
+ * This function *MUST* sanity-check offset/size on its own.
+ *
+ * size_t cbfs_dev_size(cbfs_dev_t dev);
+ * Return the total size in bytes of the CBFS storage (actual CBFS area).
+ */
+#include <cbfs_glue.h>
+
+/*
+ * Helper structure to allocate space for a blob of metadata on the stack.
+ * NOTE: The fields in any union cbfs_mdata or any of its substructures from cbfs_serialized.h
+ * should always remain in the same byte order as they are stored on flash (= big endian). To
+ * avoid byte-order confusion, fields should always and only be converted to host byte order at
+ * exactly the time they are read from one of these structures into their own separate variable.
+ */
+#define CBFS_METADATA_MAX_SIZE 256
+union cbfs_mdata {
+ struct {
+ struct cbfs_file h;
+ char filename[];
+ };
+ uint8_t raw[CBFS_METADATA_MAX_SIZE];
+};
+
+/* Flags that modify behavior of cbfs_walk(). */
+enum cbfs_walk_flags {
+ /* Write the calculated hash back out to |metadata_hash->hash| rather than comparing it.
+ |metadata_hash->algo| must still have been initialized by the caller. */
+ CBFS_WALK_WRITEBACK_HASH = (1 << 0),
+ /* Call |walker| for empty file entries (i.e. entries with one of the CBFS_TYPE_DELETED
+ types that mark free space in the CBFS). Otherwise, those entries will be skipped.
+ Either way, these entries are never included in the metadata_hash calculation. */
+ CBFS_WALK_INCLUDE_EMPTY = (1 << 1),
+};
+
+/*
+ * Traverse a CBFS and call a |walker| callback function for every file. Can additionally
+ * calculate a hash over the metadata of all files in the CBFS. If |metadata_hash| is NULL,
+ * hashing is disabled. If |walker| is NULL, will just traverse and hash the CBFS without
+ * invoking any callbacks (and always return CB_CBFS_NOT_FOUND unless there was another error).
+ *
+ * |arg| and |dev| will be passed through to |walker| unmodified. |offset| is the absolute
+ * offset in |dev| at which the current file metadata starts. |mdata| is a temporary buffer
+ * (only valid for the duration of this call to |walker|) containing already read metadata from
+ * the current file, up to |already_read| bytes. This will always at least contain the header
+ * fields and filename, but may contain more (i.e. attributes), depending on whether hashing is
+ * enabled. |walker| should call into cbfs_copy_fill_medadata() to copy the metadata of a file
+ * to a persistent buffer and automatically load remaining metadata from |dev| as needed based
+ * on the value of |already_read|.
+ *
+ * |walker| should return CB_CBFS_NOT_FOUND if it wants to continue being called for further
+ * files. Any other return code will be used as the final return code for cbfs_walk(). It will
+ * return immediately unless it needs to calculate a hash in which case it will still traverse
+ * the remaining CBFS (but not call |walker| anymore).
+ *
+ * Returns, from highest to lowest priority:
+ * CB_CBFS_IO - There was an IO error with the CBFS device (always considered fatal)
+ * CB_CBFS_HASH_MISMATCH - |metadata_hash| was provided and did not match the CBFS
+ * CB_SUCCESS/<other> - First non-CB_CBFS_NOT_FOUND code returned by walker()
+ * CB_CBFS_NOT_FOUND - walker() returned CB_CBFS_NOT_FOUND for every file in the CBFS
+ */
+cb_err_t cbfs_walk(cbfs_dev_t dev, cb_err_t (*walker)(cbfs_dev_t dev, size_t offset,
+ const union cbfs_mdata *mdata,
+ size_t already_read, void *arg),
+ void *arg, struct vb2_hash *metadata_hash, enum cbfs_walk_flags);
+
+/*
+ * Helper function that can be used by a |walker| callback to cbfs_walk() to copy the metadata
+ * of a file into a permanent buffer. Will copy the |already_read| metadata from |src| into
+ * |dst| and load remaining metadata from |dev| as required.
+ */
+cb_err_t cbfs_copy_fill_metadata(union cbfs_mdata *dst, const union cbfs_mdata *src,
+ size_t already_read, cbfs_dev_t dev, size_t offset);
+
+/* Find a file named |name| in the CBFS on |dev|. Copy its metadata (including attributes)
+ * into |mdata_out| and pass out the offset to the file data on the CBFS device.
+ * Verify the metadata with |metadata_hash| if provided. */
+cb_err_t cbfs_lookup(cbfs_dev_t dev, const char *name, union cbfs_mdata *mdata_out,
+ size_t *data_offset_out, struct vb2_hash *metadata_hash);
+
+#endif /* _COMMONLIB_BSD_CBFS_PRIVATE_H_ */
diff --git a/src/commonlib/bsd/include/commonlib/bsd/cbfs_serialized.h b/src/commonlib/bsd/include/commonlib/bsd/cbfs_serialized.h
index 3c76a49f55..7171634c8e 100644
--- a/src/commonlib/bsd/include/commonlib/bsd/cbfs_serialized.h
+++ b/src/commonlib/bsd/include/commonlib/bsd/cbfs_serialized.h
@@ -4,6 +4,7 @@
#define _CBFS_SERIALIZED_H_
#include <stdint.h>
+#include <vb2_sha.h>
/** These are standard values for the known compression
algorithms that coreboot knows about for stages and
@@ -124,12 +125,11 @@ struct cbfs_file_attr_compression {
uint32_t decompressed_size;
} __packed;
+/* Actual size in CBFS may be larger/smaller than struct size! */
struct cbfs_file_attr_hash {
uint32_t tag;
uint32_t len;
- uint32_t hash_type;
- /* hash_data is len - sizeof(struct) bytes */
- uint8_t hash_data[];
+ struct vb2_hash hash;
} __packed;
struct cbfs_file_attr_position {