| // Copyright 2020 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef SRC_LIB_VMO_STORE_VMO_STORE_H_ |
| #define SRC_LIB_VMO_STORE_VMO_STORE_H_ |
| |
| #include <lib/fit/result.h> |
| #include <zircon/status.h> |
| |
| #include <memory> |
| |
| #include <fbl/alloc_checker.h> |
| |
| #include "storage_types.h" |
| |
| namespace vmo_store { |
| |
| // `VmoStore` pinning options. |
| struct PinOptions { |
| // The BTI used for pinning. |
| // Note that `VmoStore` does *not* take ownership of the BTI handle. It is the caller's |
| // responsibility to ensure the BTI handle is valid. |
| zx::unowned_bti bti; |
| // Options passed to zx_bti_pin. See `StoredVmo::Map` for more details. |
| uint32_t bti_pin_options; |
| // Index pinned pages for fast lookup. See `StoredVmo::Map` for more details. |
| bool index; |
| }; |
| |
| struct MapOptions { |
| // Options passed to `zx_vmar_map`. |
| zx_vm_option_t vm_option; |
| // Pointer to `VmarManager`. If null, the root `vmar` will be used. |
| fbl::RefPtr<fzl::VmarManager> vmar; |
| }; |
| |
| // `VmoStore` options controlling mapping and pinning behavior. |
| struct Options { |
| // If provided, `VmoStore` will attempt to map stored VMOs. |
| fit::optional<MapOptions> map; |
| // If provided, `VmoStore` will attempt to pin stored VMOs. |
| fit::optional<PinOptions> pin; |
| }; |
| |
| // A base class used to compose `VmoStore`s. |
| // |
| // Users should not use `VmoStoreBase` directly, use `VmoStore` or `OwnedVmoStore` instead. |
| // |
| // `Impl` is a base implementation that is either `AbstractStorage` or |
| // `VmoStoreBase<AbstractStorage>`. |
| template <typename Impl> |
| class VmoStoreBase { |
| public: |
| static_assert(internal::has_key_v<Impl>, "Backing must define a Key type"); |
| static_assert(internal::has_meta_v<Impl>, "Backing must define a Meta type"); |
| // The key type used to reference VMOs. |
| using Key = typename Impl::Key; |
| // User metadata associated with every registered VMO. |
| using Meta = typename Impl::Meta; |
| using StoredVmo = ::vmo_store::StoredVmo<Meta>; |
| |
| // Reserves `capacity` slots on the underlying store. |
| // Stores that grow automatically may chose to pre-allocate memory on `Reserve`. |
| // Stores that do not grow automatically will only increase their memory consumption upon |
| // `Reserve` being called. |
| zx_status_t Reserve(size_t capacity) { return impl_.Reserve(capacity); } |
| |
| // Returns the number of registered VMOs. |
| size_t count() const { return impl_.count(); } |
| // Returns `true` if the backing store is full. |
| // Stores that grow automatically will never report that they're full. |
| bool is_full() const { return impl_.is_full(); } |
| |
| protected: |
| template <typename... StoreArgs> |
| VmoStoreBase(StoreArgs... store_args) : impl_(std::forward<StoreArgs>(store_args)...) {} |
| Impl impl_; |
| }; |
| |
| // A data structure that keeps track of registered VMOs using a `Backing` storage type. |
| // `VmoStore` keeps track of registered VMOs and performs common mapping and pinning operations, |
| // providing common operations used in VMO pre-registration on Banjo and FIDL APIs. |
| // This structure is not thread-safe. Users must provide their own thread-safety accounting for the |
| // chosen `Backing` format. |
| // `Backing` is the data structure used to store the registered VMOs. It must implement |
| // `AbstractStorage`. |
| // |
| // Example usage: |
| // ``` |
| // using namespace vmo_store; |
| // // Declaring our types first. |
| // // `MyKey` is the key type that is used to register and retrieve VMOs from a VmoStore. |
| // using MyKey = size_t; |
| // // `MyMeta` is extra user metadata associated with every stored VMO (can be `void`). |
| // using MyMeta = std::string; |
| // // Declare our store alias, we're using `HashTableStorage` in this example. |
| // // See `storage_types.h` for other Backing storage types. |
| // using MyVmoStore = VmoStore<HashTableStorage<MyKey, MyMeta>>; |
| // MyVmoStore store(Options{...}); |
| // |
| // // Now let's register, retrieve, and unregister a `zx::vmo` obtained through `GetVmo()`. |
| // // The second argument to `Register` is our user metadata. |
| // fit::result<size_t, zx_status_t> result = store.Register(GetVmo(), "my first VMO"); |
| // size_t key = result.take_value(); |
| // auto * my_registered_vmo = store.GetVmo(key); |
| // |
| // // Print metadata associated with VMO. |
| // std::cout << "Got Vmo called " << my_registered_vmo->meta() << std::endl; |
| // // see `stored_vmo.h` for other `StoredVmo` methods, like retrieving mapped or pinned memory. |
| // |
| // // Finally, unregister the VMO, which will discard the VMO handle along with any mapping or |
| // // pinning. |
| // store.Unregister(key); |
| // ``` |
| // |
| // See `OwnedVmoStore` for an alternative API where registration happens through an ownership agent. |
| template <typename Backing> |
| class VmoStore : public VmoStoreBase<Backing> { |
| public: |
| using typename VmoStoreBase<Backing>::Key; |
| using typename VmoStoreBase<Backing>::Meta; |
| using typename VmoStoreBase<Backing>::StoredVmo; |
| |
| static_assert( |
| internal::is_abstract_storage<Backing>, |
| "Backing must implement AbstractStorage, see storage_types.h for build in storage classes"); |
| |
| template <typename... StoreArgs> |
| explicit VmoStore(Options options, StoreArgs... store_args) |
| : VmoStoreBase<Backing>(std::forward<StoreArgs>(store_args)...), |
| options_(std::move(options)){ |
| |
| } |
| |
| // Registers a VMO with this store, returning the key used to access that VMO on success. |
| template <typename... MetaArgs> |
| fit::result<Key, zx_status_t> Register(zx::vmo vmo, MetaArgs... vmo_args) { |
| return Register(StoredVmo(std::move(vmo), std::forward<MetaArgs>(vmo_args)...)); |
| } |
| |
| fit::result<Key, zx_status_t> Register(StoredVmo vmo) { |
| zx_status_t status = PrepareStore(&vmo); |
| if (status != ZX_OK) { |
| return fit::error(status); |
| } |
| auto key = this->impl_.Push(std::move(vmo)); |
| if (!key.has_value()) { |
| return fit::error(ZX_ERR_NO_RESOURCES); |
| } |
| return fit::ok(std::move(*key)); |
| } |
| |
| // Registers a VMO with this store using the provided `key`. |
| template <typename... MetaArgs> |
| zx_status_t RegisterWithKey(Key key, zx::vmo vmo, MetaArgs... vmo_args) { |
| return RegisterWithKey(std::move(key), |
| StoredVmo(std::move(vmo), std::forward<MetaArgs>(vmo_args)...)); |
| } |
| |
| zx_status_t RegisterWithKey(Key key, StoredVmo vmo) { |
| zx_status_t status = PrepareStore(&vmo); |
| if (status != ZX_OK) { |
| return status; |
| } |
| return this->impl_.Insert(std::move(key), std::move(vmo)); |
| } |
| |
| // Unregisters the VMO at `key`. |
| // The VMO handle will be dropped, alongside all the mapping and pinning handles. |
| // Returns `ZX_ERR_NOT_FOUND` if `key` does not point to a registered VMO. |
| zx_status_t Unregister(Key key) { |
| if (!this->impl_.Erase(key)) { |
| return ZX_ERR_NOT_FOUND; |
| } |
| return ZX_OK; |
| } |
| |
| // Gets an _unowned_ pointer to the `StoredVmo` referenced by `key`. |
| // Returns `nullptr` if `key` does not point to a register VMO. |
| StoredVmo* GetVmo(const Key& key) { return this->impl_.Get(key); } |
| |
| DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(VmoStore); |
| |
| private: |
| zx_status_t PrepareStore(StoredVmo* vmo) { |
| if (!vmo->vmo()->is_valid()) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| zx_status_t status; |
| if (options_.map) { |
| const auto& map_options = *options_.map; |
| status = vmo->Map(map_options.vm_option, map_options.vmar); |
| if (status != ZX_OK) { |
| return status; |
| } |
| } |
| if (options_.pin) { |
| const auto& pin_options = *options_.pin; |
| status = vmo->Pin(*pin_options.bti, pin_options.bti_pin_options, pin_options.index); |
| if (status != ZX_OK) { |
| return status; |
| } |
| } |
| return ZX_OK; |
| } |
| |
| Options options_; |
| }; |
| |
| } // namespace vmo_store |
| |
| #endif // SRC_LIB_VMO_STORE_VMO_STORE_H_ |