summaryrefslogtreecommitdiff
path: root/src/libcamera/ipa_module.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libcamera/ipa_module.cpp')
-rw-r--r--src/libcamera/ipa_module.cpp296
1 files changed, 145 insertions, 151 deletions
diff --git a/src/libcamera/ipa_module.cpp b/src/libcamera/ipa_module.cpp
index a01d0757..9ca74be6 100644
--- a/src/libcamera/ipa_module.cpp
+++ b/src/libcamera/ipa_module.cpp
@@ -2,28 +2,29 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * ipa_module.cpp - Image Processing Algorithm module
+ * Image Processing Algorithm module
*/
-#include "ipa_module.h"
+#include "libcamera/internal/ipa_module.h"
#include <algorithm>
-#include <array>
+#include <ctype.h>
#include <dlfcn.h>
#include <elf.h>
#include <errno.h>
#include <fcntl.h>
#include <link.h>
#include <string.h>
-#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
-#include <tuple>
#include <unistd.h>
-#include "log.h"
-#include "pipeline_handler.h"
-#include "utils.h"
+#include <libcamera/base/file.h>
+#include <libcamera/base/log.h>
+#include <libcamera/base/span.h>
+#include <libcamera/base/utils.h>
+
+#include "libcamera/internal/pipeline_handler.h"
/**
* \file ipa_module.h
@@ -42,27 +43,27 @@ LOG_DEFINE_CATEGORY(IPAModule)
namespace {
template<typename T>
-typename std::remove_extent_t<T> *elfPointer(void *map, off_t offset,
- size_t fileSize, size_t objSize)
+typename std::remove_extent_t<T> *elfPointer(Span<const uint8_t> elf,
+ off_t offset, size_t objSize)
{
size_t size = offset + objSize;
- if (size > fileSize || size < objSize)
+ if (size > elf.size() || size < objSize)
return nullptr;
- return reinterpret_cast<typename std::remove_extent_t<T> *>
- (static_cast<char *>(map) + offset);
+ return reinterpret_cast<typename std::remove_extent_t<T> *>(
+ reinterpret_cast<const char *>(elf.data()) + offset);
}
template<typename T>
-typename std::remove_extent_t<T> *elfPointer(void *map, off_t offset,
- size_t fileSize)
+typename std::remove_extent_t<T> *elfPointer(Span<const uint8_t> elf,
+ off_t offset)
{
- return elfPointer<T>(map, offset, fileSize, sizeof(T));
+ return elfPointer<T>(elf, offset, sizeof(T));
}
-int elfVerifyIdent(void *map, size_t soSize)
+int elfVerifyIdent(Span<const uint8_t> elf)
{
- char *e_ident = elfPointer<char[EI_NIDENT]>(map, 0, soSize);
+ const char *e_ident = elfPointer<const char[EI_NIDENT]>(elf, 0);
if (!e_ident)
return -ENOEXEC;
@@ -86,40 +87,47 @@ int elfVerifyIdent(void *map, size_t soSize)
return 0;
}
+const ElfW(Shdr) *elfSection(Span<const uint8_t> elf, const ElfW(Ehdr) *eHdr,
+ ElfW(Half) idx)
+{
+ if (idx >= eHdr->e_shnum)
+ return nullptr;
+
+ off_t offset = eHdr->e_shoff + idx *
+ static_cast<uint32_t>(eHdr->e_shentsize);
+ return elfPointer<const ElfW(Shdr)>(elf, offset);
+}
+
/**
* \brief Retrieve address and size of a symbol from an mmap'ed ELF file
- * \param[in] map Address of mmap'ed ELF file
- * \param[in] soSize Size of mmap'ed ELF file (in bytes)
+ * \param[in] elf Address and size of mmap'ed ELF file
* \param[in] symbol Symbol name
*
- * \return zero or error code, address or nullptr, size of symbol or zero,
- * respectively
+ * \return The memory region storing the symbol on success, or an empty span
+ * otherwise
*/
-std::tuple<void *, size_t>
-elfLoadSymbol(void *map, size_t soSize, const char *symbol)
+Span<const uint8_t> elfLoadSymbol(Span<const uint8_t> elf, const char *symbol)
{
- ElfW(Ehdr) *eHdr = elfPointer<ElfW(Ehdr)>(map, 0, soSize);
+ const ElfW(Ehdr) *eHdr = elfPointer<const ElfW(Ehdr)>(elf, 0);
if (!eHdr)
- return std::make_tuple(nullptr, 0);
+ return {};
- off_t offset = eHdr->e_shoff + eHdr->e_shentsize * eHdr->e_shstrndx;
- ElfW(Shdr) *sHdr = elfPointer<ElfW(Shdr)>(map, offset, soSize);
+ const ElfW(Shdr) *sHdr = elfSection(elf, eHdr, eHdr->e_shstrndx);
if (!sHdr)
- return std::make_tuple(nullptr, 0);
+ return {};
off_t shnameoff = sHdr->sh_offset;
/* Locate .dynsym section header. */
- ElfW(Shdr) *dynsym = nullptr;
+ const ElfW(Shdr) *dynsym = nullptr;
for (unsigned int i = 0; i < eHdr->e_shnum; i++) {
- offset = eHdr->e_shoff + eHdr->e_shentsize * i;
- sHdr = elfPointer<ElfW(Shdr)>(map, offset, soSize);
+ sHdr = elfSection(elf, eHdr, i);
if (!sHdr)
- return std::make_tuple(nullptr, 0);
+ return {};
- offset = shnameoff + sHdr->sh_name;
- char *name = elfPointer<char[8]>(map, offset, soSize);
+ off_t offset = shnameoff + sHdr->sh_name;
+ const char *name = elfPointer<const char[8]>(elf, offset);
if (!name)
- return std::make_tuple(nullptr, 0);
+ return {};
if (sHdr->sh_type == SHT_DYNSYM && !strcmp(name, ".dynsym")) {
dynsym = sHdr;
@@ -129,29 +137,28 @@ elfLoadSymbol(void *map, size_t soSize, const char *symbol)
if (dynsym == nullptr) {
LOG(IPAModule, Error) << "ELF has no .dynsym section";
- return std::make_tuple(nullptr, 0);
+ return {};
}
- offset = eHdr->e_shoff + eHdr->e_shentsize * dynsym->sh_link;
- sHdr = elfPointer<ElfW(Shdr)>(map, offset, soSize);
+ sHdr = elfSection(elf, eHdr, dynsym->sh_link);
if (!sHdr)
- return std::make_tuple(nullptr, 0);
+ return {};
off_t dynsym_nameoff = sHdr->sh_offset;
/* Locate symbol in the .dynsym section. */
- ElfW(Sym) *targetSymbol = nullptr;
+ const ElfW(Sym) *targetSymbol = nullptr;
unsigned int dynsym_num = dynsym->sh_size / dynsym->sh_entsize;
for (unsigned int i = 0; i < dynsym_num; i++) {
- offset = dynsym->sh_offset + dynsym->sh_entsize * i;
- ElfW(Sym) *sym = elfPointer<ElfW(Sym)>(map, offset, soSize);
+ off_t offset = dynsym->sh_offset + dynsym->sh_entsize * i;
+ const ElfW(Sym) *sym = elfPointer<const ElfW(Sym)>(elf, offset);
if (!sym)
- return std::make_tuple(nullptr, 0);
+ return {};
offset = dynsym_nameoff + sym->st_name;
- char *name = elfPointer<char>(map, offset, soSize,
- strlen(symbol) + 1);
+ const char *name = elfPointer<const char>(elf, offset,
+ strlen(symbol) + 1);
if (!name)
- return std::make_tuple(nullptr, 0);
+ return {};
if (!strcmp(name, symbol) &&
sym->st_info & STB_GLOBAL) {
@@ -162,22 +169,20 @@ elfLoadSymbol(void *map, size_t soSize, const char *symbol)
if (targetSymbol == nullptr) {
LOG(IPAModule, Error) << "Symbol " << symbol << " not found";
- return std::make_tuple(nullptr, 0);
+ return {};
}
/* Locate and return data of symbol. */
- if (targetSymbol->st_shndx >= eHdr->e_shnum)
- return std::make_tuple(nullptr, 0);
- offset = eHdr->e_shoff + targetSymbol->st_shndx * eHdr->e_shentsize;
- sHdr = elfPointer<ElfW(Shdr)>(map, offset, soSize);
+ sHdr = elfSection(elf, eHdr, targetSymbol->st_shndx);
if (!sHdr)
- return std::make_tuple(nullptr, 0);
- offset = sHdr->sh_offset + (targetSymbol->st_value - sHdr->sh_addr);
- char *data = elfPointer<char>(map, offset, soSize, targetSymbol->st_size);
+ return {};
+ off_t offset = sHdr->sh_offset + (targetSymbol->st_value - sHdr->sh_addr);
+ const uint8_t *data = elfPointer<const uint8_t>(elf, offset,
+ targetSymbol->st_size);
if (!data)
- return std::make_tuple(nullptr, 0);
+ return {};
- return std::make_tuple(data, targetSymbol->st_size);
+ return { data, targetSymbol->st_size };
}
} /* namespace */
@@ -218,26 +223,10 @@ elfLoadSymbol(void *map, size_t soSize, const char *symbol)
* \var IPAModuleInfo::name
* \brief The name of the IPA module
*
- * \var IPAModuleInfo::license
- * \brief License of the IPA module
- *
- * This license is used to determine whether to force isolation of the IPA in
- * a separate process. If the license is "Proprietary", then the IPA will
- * be isolated. If the license is open-source, then the IPA will be allowed to
- * run without isolation if the user enables it. The license should be an
- * SPDX license string. The following licenses are currently available to
- * allow the IPA to run unisolated:
- *
- * - GPL-2.0-only
- * - GPL-2.0-or-later
- * - GPL-3.0-only
- * - GPL-3.0-or-later
- * - LGPL-2.1-only
- * - LGPL-2.1-or-later
- * - LGPL-3.0-only
- * - LGPL-3.0-or-later
- *
- * Any other license will cause the IPA to be run isolated.
+ * The name may be used to build file system paths to IPA-specific resources.
+ * It shall only contain printable characters, and may not contain '*', '?' or
+ * '\'. For IPA modules included in libcamera, it shall match the directory of
+ * the IPA module in the source tree.
*
* \todo Allow user to choose to isolate open source IPAs
*/
@@ -262,7 +251,7 @@ elfLoadSymbol(void *map, size_t soSize, const char *symbol)
* The IPA module shared object file must be of the same endianness and
* bitness as libcamera.
*
- * The caller shall call the isValid() method after constructing an
+ * The caller shall call the isValid() function after constructing an
* IPAModule instance to verify the validity of the IPAModule.
*/
IPAModule::IPAModule(const std::string &libPath)
@@ -283,55 +272,67 @@ IPAModule::~IPAModule()
int IPAModule::loadIPAModuleInfo()
{
- int fd = open(libPath_.c_str(), O_RDONLY);
- if (fd < 0) {
- int ret = -errno;
+ File file{ libPath_ };
+ if (!file.open(File::OpenModeFlag::ReadOnly)) {
LOG(IPAModule, Error) << "Failed to open IPA library: "
- << strerror(-ret);
- return ret;
+ << strerror(-file.error());
+ return file.error();
}
- void *data = nullptr;
- size_t dataSize;
- void *map;
- size_t soSize;
- struct stat st;
- int ret = fstat(fd, &st);
- if (ret < 0)
- goto close;
- soSize = st.st_size;
- map = mmap(NULL, soSize, PROT_READ, MAP_PRIVATE, fd, 0);
- if (map == MAP_FAILED) {
- ret = -errno;
- goto close;
+ Span<const uint8_t> data = file.map();
+ int ret = elfVerifyIdent(data);
+ if (ret) {
+ LOG(IPAModule, Error) << "IPA module is not an ELF file";
+ return ret;
}
- ret = elfVerifyIdent(map, soSize);
- if (ret)
- goto unmap;
-
- std::tie(data, dataSize) = elfLoadSymbol(map, soSize, "ipaModuleInfo");
-
- if (data && dataSize == sizeof(info_))
- memcpy(&info_, data, dataSize);
+ Span<const uint8_t> info = elfLoadSymbol(data, "ipaModuleInfo");
+ if (info.size() < sizeof(info_)) {
+ LOG(IPAModule, Error) << "IPA module has no valid info";
+ return -EINVAL;
+ }
- if (!data)
- goto unmap;
+ memcpy(&info_, info.data(), sizeof(info_));
if (info_.moduleAPIVersion != IPA_MODULE_API_VERSION) {
LOG(IPAModule, Error) << "IPA module API version mismatch";
- ret = -EINVAL;
+ return -EINVAL;
}
-unmap:
- munmap(map, soSize);
-close:
- if (ret || !data)
+ /*
+ * Validate the IPA module name.
+ *
+ * \todo Consider module naming restrictions to avoid escaping from a
+ * base directory. Forbidding ".." may be enough, but this may be best
+ * implemented in a different layer.
+ */
+ std::string ipaName = info_.name;
+ auto iter = std::find_if_not(ipaName.begin(), ipaName.end(),
+ [](unsigned char c) -> bool {
+ return isprint(c) && c != '?' &&
+ c != '*' && c != '\\';
+ });
+ if (iter != ipaName.end()) {
LOG(IPAModule, Error)
- << "Error loading IPA module info for " << libPath_;
+ << "Invalid IPA module name '" << ipaName << "'";
+ return -EINVAL;
+ }
- close(fd);
- return ret;
+ /* Load the signature. Failures are not fatal. */
+ File sign{ libPath_ + ".sign" };
+ if (!sign.open(File::OpenModeFlag::ReadOnly)) {
+ LOG(IPAModule, Debug)
+ << "IPA module " << libPath_ << " is not signed";
+ return 0;
+ }
+
+ data = sign.map(0, -1, File::MapFlag::Private);
+ signature_.resize(data.size());
+ memcpy(signature_.data(), data.data(), data.size());
+
+ LOG(IPAModule, Debug) << "IPA module " << libPath_ << " is signed";
+
+ return 0;
}
/**
@@ -363,6 +364,21 @@ const struct IPAModuleInfo &IPAModule::info() const
}
/**
+ * \brief Retrieve the IPA module signature
+ *
+ * The IPA module signature is stored alongside the IPA module in a file with a
+ * '.sign' suffix, and is loaded when the IPAModule instance is created. This
+ * function returns the signature without verifying it. If the signature is
+ * missing, the returned vector will be empty.
+ *
+ * \return The IPA module signature
+ */
+const std::vector<uint8_t> IPAModule::signature() const
+{
+ return signature_;
+}
+
+/**
* \brief Retrieve the IPA module path
*
* The IPA module path is the file name and path of the IPA module shared
@@ -378,13 +394,13 @@ const std::string &IPAModule::path() const
/**
* \brief Load the IPA implementation factory from the shared object
*
- * The IPA module shared object implements an ipa_context object to be used
- * by pipeline handlers. This method loads the factory function from the
- * shared object. Later, createContext() can be called to instantiate the
- * ipa_context.
+ * The IPA module shared object implements an IPAInterface object to be used
+ * by pipeline handlers. This function loads the factory function from the
+ * shared object. Later, createInterface() can be called to instantiate the
+ * IPAInterface.
*
- * This method only needs to be called successfully once, after which
- * createContext() can be called as many times as ipa_context instances are
+ * This function only needs to be called successfully once, after which
+ * createInterface() can be called as many times as IPAInterface instances are
* needed.
*
* Calling this function on an invalid module (as returned by isValid()) is
@@ -426,20 +442,18 @@ bool IPAModule::load()
}
/**
- * \brief Instantiate an IPA context
+ * \brief Instantiate an IPA interface
*
- * After loading the IPA module with load(), this method creates an instance of
- * the IPA module context. Ownership of the context is passed to the caller, and
- * the context shall be destroyed by calling the \ref ipa_context_ops::destroy
- * "ipa_context::ops::destroy()" function.
+ * After loading the IPA module with load(), this function creates an instance
+ * of the IPA module interface.
*
* Calling this function on a module that has not yet been loaded, or an
* invalid module (as returned by load() and isValid(), respectively) is
* an error.
*
- * \return The IPA context on success, or nullptr on error
+ * \return The IPA interface on success, or nullptr on error
*/
-struct ipa_context *IPAModule::createContext()
+IPAInterface *IPAModule::createInterface()
{
if (!valid_ || !loaded_)
return nullptr;
@@ -448,12 +462,12 @@ struct ipa_context *IPAModule::createContext()
}
/**
- * \brief Verify if the IPA module maches a given pipeline handler
+ * \brief Verify if the IPA module matches a given pipeline handler
* \param[in] pipe Pipeline handler to match with
* \param[in] minVersion Minimum acceptable version of IPA module
* \param[in] maxVersion Maximum acceptable version of IPA module
*
- * This method checks if this IPA module matches the \a pipe pipeline handler,
+ * This function checks if this IPA module matches the \a pipe pipeline handler,
* and the input version range.
*
* \return True if the pipeline handler matches the IPA module, or false otherwise
@@ -466,29 +480,9 @@ bool IPAModule::match(PipelineHandler *pipe,
!strcmp(info_.pipelineName, pipe->name());
}
-/**
- * \brief Verify if the IPA module is open source
- *
- * \sa IPAModuleInfo::license
- */
-bool IPAModule::isOpenSource() const
+std::string IPAModule::logPrefix() const
{
- static const char *osLicenses[] = {
- "GPL-2.0-only",
- "GPL-2.0-or-later",
- "GPL-3.0-only",
- "GPL-3.0-or-later",
- "LGPL-2.1-only",
- "LGPL-2.1-or-later",
- "LGPL-3.0-only",
- "LGPL-3.0-or-later",
- };
-
- for (unsigned int i = 0; i < ARRAY_SIZE(osLicenses); i++)
- if (!strcmp(osLicenses[i], info_.license))
- return true;
-
- return false;
+ return utils::basename(libPath_.c_str());
}
} /* namespace libcamera */