// -*- Mode: C++ -*- // vim:ft=cpp /** * \file * \brief Region mapper interface */ /* * (c) 2008-2009 Adam Lackorzynski , * Alexander Warg , * Björn Döbel , * Torsten Frenzel * economic rights: Technische Universität Dresden (Germany) * * This file is part of TUD:OS and distributed under the terms of the * GNU General Public License 2. * Please see the COPYING-GPL-2 file for details. * * As a special exception, you may use this file as part of a free software * library without restriction. Specifically, if other files instantiate * templates or use macros or inline functions from this file, or you compile * this file and link it with other files to produce an executable, this * file does not by itself cause the resulting executable to be covered by * the GNU General Public License. This exception does not however * invalidate any other reasons why the executable file might be covered by * the GNU General Public License. */ #pragma once #include #include #include #include #include #include #include #include #include #include namespace L4Re { /** * \defgroup api_l4re_rm Region map API * \ingroup api_l4re * \brief Virtual address-space management. * * A region map object implements two protocols. The first protocol is the * kernel page-fault protocol, to resolve page faults for threads running in an * L4 task. The second protocol is the region map protocol itself, which allows * managing the virtual memory address space of an L4 task. * * There are two basic concepts provided by the region map abstraction: * - **Areas** are reserved ranges in the virtual memory address space. * - **Regions** are areas that are backed by (part of) a dataspace, i.e. * accessing them results in access to the physical memory the dataspace * manages. * * Areas can be reserved for special use or for attaching a dataspace at a later * time. When attaching a dataspace, the user can instruct the region map to * search for an appropriate range to attach to. Regions are skipped in this * search since they already have dataspaces attached to them, and areas are * skipped because they are reserved. * * When a region map receives a page fault IPC, the region map will check if the * faulting virtual address lies in a region. If yes, it will answer the page * fault IPC with a mapping from the backing dataspace. If not, an error is * returned. * * \see L4Re::Dataspace, L4Re::Rm, * \see \ref l4re_concepts_ds_rm */ /** * \brief Region map * \headerfile rm l4/re/rm * \ingroup api_l4re_rm * * \see \link api_l4re_rm Region map API \endlink. */ class L4_EXPORT Rm : public L4::Kobject_t > { public: typedef L4Re::Dataspace::Offset Offset; /// Result values for detach operation. enum Detach_result { Detached_ds = 0, ///< Detached data sapce. Kept_ds = 1, ///< Kept data space. Split_ds = 2, ///< Splitted data space, and done. Detach_result_mask = 3, Detach_again = 4, ///< Detached data space, more to do. }; enum Region_flag_shifts { /// Start of Rm cache bits Caching_shift = Dataspace::F::Caching_shift, }; /** Rm flags definitions. */ struct F { /// Flags for attach operation. enum Attach_flags : l4_uint32_t { /// Search for a suitable address range. Search_addr = 0x20000, /// Search only in area, or map into area. In_area = 0x40000, /// Eagerly map the attached data space in. Eager_map = 0x80000, /// Mask of all attach flags. Attach_mask = 0xf0000, }; L4_TYPES_FLAGS_OPS_DEF(Attach_flags); enum Region_flags : l4_uint16_t { /// Region rights Rights_mask = 0x0f, R = Dataspace::F::R, W = Dataspace::F::W, X = Dataspace::F::X, RW = Dataspace::F::RW, RX = Dataspace::F::RX, RWX = Dataspace::F::RWX, /// Free the portion of the data space after detach Detach_free = 0x200, /// Region has a pager Pager = 0x400, /// Region is reserved (blocked) Reserved = 0x800, /// Mask of all Rm cache bits Caching_mask = Dataspace::F::Caching_mask, /// Cache bits for normal cacheable memory Cache_normal = Dataspace::F::Normal, /// Cache bits for buffered (write combining) memory Cache_buffered = Dataspace::F::Bufferable, /// Cache bits for uncached memory Cache_uncached = Dataspace::F::Uncacheable, /// Mask for all bits for cache options and rights. Ds_map_mask = 0xff, /// Mask of all region flags Region_flags_mask = 0xffff, }; L4_TYPES_FLAGS_OPS_DEF(Region_flags); friend constexpr Dataspace::Flags map_flags(Region_flags rf) { return Dataspace::Flags((l4_uint16_t)rf & Ds_map_mask); } struct Flags : L4::Types::Flags_ops_t { l4_uint32_t raw; Flags() = default; explicit constexpr Flags(l4_uint32_t f) : raw(f) {} constexpr Flags(Attach_flags rf) : raw((l4_uint32_t)rf) {} constexpr Flags(Region_flags rf) : raw((l4_uint32_t)rf) {} friend constexpr Dataspace::Flags map_flags(Flags f) { return Dataspace::Flags(f.raw & Ds_map_mask); } constexpr Region_flags region_flags() const { return Region_flags(raw & Region_flags_mask); } constexpr Attach_flags attach_flags() const { return Attach_flags(raw & Attach_mask); } constexpr bool r() const { return raw & L4_FPAGE_RO; } constexpr bool w() const { return raw & L4_FPAGE_W; } constexpr bool x() const { return raw & L4_FPAGE_X; } constexpr unsigned cap_rights() const { return w() ? L4_CAP_FPAGE_RW : L4_CAP_FPAGE_RO; } }; friend constexpr Flags operator | (Region_flags l, Attach_flags r) { return Flags(l) | Flags(r); } friend constexpr Flags operator | (Attach_flags l, Region_flags r) { return Flags(l) | Flags(r); } }; using Attach_flags = F::Attach_flags; using Region_flags = F::Region_flags; using Flags = F::Flags; /// Flags for detach operation enum Detach_flags { /** * \brief Do an unmap of the exact region given. * \internal * * This flag is useful for _detach(). * * Using this mode for detach, unmaps the exact region given. * This has the effect that parts of regions may stay in the address space. */ Detach_exact = 1, /** * \brief Do an unmap of all overlapping regions. * \internal * * This flag is useful for _detach(). * * Using this mode for detach, unmaps all regions that overlap with * the given region. */ Detach_overlap = 2, /** * \brief Do not free the detached data space, ignore the F::Detach_free * \internal * * This flag is useful for _detach(). * */ Detach_keep = 4, }; /** * \brief Reserve the given area in the region map. * \param[in,out] start The virtual start address of the area to reserve. * Returns the start address of the area. * \param size The size of the area to reserve (in bytes). * \param flags Flags for the reserved area (see * #L4Re::Rm::F::Region_flags and * #L4Re::Rm::F::Attach_flags). * \param align Alignment of area if searched as bits (log2 value). * \retval 0 Success * \retval -L4_EADDRNOTAVAIL The given area cannot be reserved. * \retval <0 IPC errors * * This function reserves an area within the virtual address space managed * by the region map. There are two kinds of areas available: * - Reserved areas (\a flags = F::Reserved), where no data spaces can be * attached * - Special purpose areas (\a flags = 0), where data spaces can be attached * to the area via the F::In_area flag and a start address within the area * itself. * * \note When searching for a free place in the virtual address space * (with \a flags = F::Search_addr), the space between \a start and the end of * the virtual address space is searched. */ long reserve_area(l4_addr_t *start, unsigned long size, Flags flags = Flags(0), unsigned char align = L4_PAGESHIFT) const noexcept { return reserve_area_t::call(c(), start, size, flags, align); } L4_RPC_NF(long, reserve_area, (L4::Ipc::In_out start, unsigned long size, Flags flags, unsigned char align)); /** * \brief Reserve the given area in the region map. * \param[in,out] start The virtual start address of the area to reserve. * Returns the start address of the area. * \param size The size of the area to reserve (in bytes). * \param flags Flags for the reserved area (see F::Region_flags and * F::Attach_flags). * \param align Alignment of area if searched as bits (log2 value). * \retval 0 Success * \retval -L4_EADDRNOTAVAIL The given area cannot be reserved. * \retval <0 IPC errors * * For more information, please refer to the analogous function * \see L4Re::Rm::reserve_area. */ template< typename T > long reserve_area(T **start, unsigned long size, Flags flags = Flags(0), unsigned char align = L4_PAGESHIFT) const noexcept { return reserve_area_t::call(c(), (l4_addr_t*)start, size, flags, align); } /** * \brief Free an area from the region map. * * \param addr An address within the area to free. * \retval 0 Success * \retval -L4_ENOENT No area found. * \retval <0 IPC errors * * \note The data spaces that are attached to that area are not detached by * this operation. * \see reserve_area() for more information about areas. */ L4_RPC(long, free_area, (l4_addr_t addr)); L4_RPC_NF(long, attach, (L4::Ipc::In_out start, unsigned long size, Flags flags, L4::Ipc::Opt > mem, Offset offs, unsigned char align, L4::Ipc::Opt client_cap)); L4_RPC_NF(long, detach, (l4_addr_t addr, unsigned long size, unsigned flags, l4_addr_t &start, l4_addr_t &rsize, l4_cap_idx_t &mem_cap)); /** * \brief Attach a data space to a region. * * \param[in,out] start Virtual start address where the region manager * shall attach the data space. Will be rounded down to * the nearest start of a page. * If #L4Re::Rm::F::Search_addr is given this value is * used as the start address to search for a free * virtual memory region and the resulting address is * returned here. * If #L4Re::Rm::F::In_area is given the value is used * as a selector for the area (see * #L4Re::Rm::reserve_area) to attach the data space * to. * \param size Size of the data space to attach (in bytes). Will be * rounded up to the nearest multiple of the page size. * \param flags The flags control how and with which rights the * dataspace is attached to the region. See * #L4Re::Rm::F::Attach_flags and * #L4Re::Rm::F::Region_flags. The caller must specify * the desired rights of the attached region * explicitly. The default set of rights is empty. If * the `F::Eager_map` flag is set this function may * also return L4Re::Dataspace::map error codes if the * mapping fails. * \param mem Data space. * \param offs Offset into the data space to use. * \param align Alignment of the virtual region, log2-size, default: * a page (#L4_PAGESHIFT). This is only meaningful if * the #L4Re::Rm::F::Search_addr flag is used. * * \retval 0 Success * \retval -L4_ENOENT No area could be found (see * #L4Re::Rm::F::In_area) * \retval -L4_EPERM Operation not allowed. * \retval -L4_EINVAL * \retval -L4_EADDRNOTAVAIL The given address is not available. * \retval <0 IPC errors * * Makes the whole or parts of a data space visible in the virtual memory * of the corresponding task. The corresponding region in the virtual * address space is backed with the contents of the dataspace. * * \note When searching for a free place in the virtual address space, * the space between \a start and the end of the virtual address space is * searched. * * \note There is no region object created, instead the region is * defined by a virtual address within this range (see #L4Re::Rm::find). */ long attach(l4_addr_t *start, unsigned long size, Flags flags, L4::Ipc::Cap mem, Offset offs = 0, unsigned char align = L4_PAGESHIFT) const noexcept; /** * \copydoc L4Re::Rm::attach */ template< typename T > long attach(T **start, unsigned long size, Flags flags, L4::Ipc::Cap mem, Offset offs = 0, unsigned char align = L4_PAGESHIFT) const noexcept { union X { l4_addr_t a; T* t; }; X *x = reinterpret_cast(start); return attach(&x->a, size, flags, mem, offs, align); } #if __cplusplus >= 201103L template< typename T > class Unique_region { private: T _addr; L4::Cap _rm; public: Unique_region(Unique_region const &) = delete; Unique_region &operator = (Unique_region const &) = delete; Unique_region() noexcept : _addr(0), _rm(L4::Cap::Invalid) {} explicit Unique_region(T addr) noexcept : _addr(addr), _rm(L4::Cap::Invalid) {} Unique_region(T addr, L4::Cap const &rm) noexcept : _addr(addr), _rm(rm) {} Unique_region(Unique_region &&o) noexcept : _addr(o.get()), _rm(o._rm) { o.release(); } Unique_region &operator = (Unique_region &&o) noexcept { if (&o != this) { if (_rm.is_valid()) _rm->detach(l4_addr_t(_addr), 0); _rm = o._rm; _addr = o.release(); } return *this; } ~Unique_region() noexcept { if (_rm.is_valid()) _rm->detach(l4_addr_t(_addr), 0); } T get() const noexcept { return _addr; } T release() noexcept { _rm = L4::Cap::Invalid; return _addr; } void reset(T addr, L4::Cap const &rm) noexcept { if (_rm.is_valid()) _rm->detach(l4_addr_t(_addr), 0); _rm = rm; _addr = addr; } void reset() noexcept { reset(0, L4::Cap::Invalid); } bool is_valid() const noexcept { return _rm.is_valid(); } /** \brief Dereference the pointer. */ T operator * () const noexcept { return _addr; } /** \brief Member access for the object. */ T operator -> () const noexcept { return _addr; } }; template< typename T > long attach(Unique_region *start, unsigned long size, Flags flags, L4::Ipc::Cap mem, Offset offs = 0, unsigned char align = L4_PAGESHIFT) const noexcept { l4_addr_t addr = (l4_addr_t)start->get(); long res = attach(&addr, size, flags, mem, offs, align); if (res < 0) return res; start->reset((T)addr, L4::Cap(cap())); return res; } #endif /** * \brief Detach a region from the address space. * * \param addr Virtual address of region, any address within the * region is valid. * \param[out] mem Dataspace that is affected. Give 0 if not interested. * \param task This argument specifies the task where the pages are * unmapped. Provide L4::Cap::Invalid for none. * The default is the current task. * * \retval #Detach_result On success. * \retval -L4_ENOENT No region found. * \retval <0 IPC errors * * Frees a region in the virtual address space given by addr (address type). * The corresponding part of the address space is now available again. */ int detach(l4_addr_t addr, L4::Cap *mem, L4::Cap const &task = This_task) const noexcept; /** * \copydoc L4Re::Rm::detach */ int detach(void *addr, L4::Cap *mem, L4::Cap const &task = This_task) const noexcept; /** * \brief Detach all regions of the specified interval. * * \param start Start of area to detach, must be within region. * \param size Size of of area to detach (in bytes). * \param[out] mem Dataspace that is affected. Give 0 if not interested. * \param task This argument specifies the task where the pages are * unmapped. Provide L4::Cap::Invalid for none. * The default is the current task. * * \retval #Detach_result On success. * \retval -L4_ENOENT No region found. * \retval <0 IPC errors * * Frees all regions within the interval given by start and size. If a * region overlaps the start or the end of the interval this region is only * detached partly. If the interval is within one region the original region * is split up into two separate regions. */ int detach(l4_addr_t start, unsigned long size, L4::Cap *mem, L4::Cap const &task) const noexcept; /** * \brief Find a region given an address and size. * * \param[in,out] addr Address to look for. Returns the start address of the * found region. * \param[in,out] size Size of the area to look for (in bytes). Returns the * size of the found region (in bytes). * \param[out] offset Offset at the beginning of the region within the * associated dataspace. * \param[out] flags Region flags, see F::Region_flags (and F::In_area). * \param[out] m Associated dataspace or paging service. * * \retval 0 Success * \retval -L4_EPERM Operation not allowed. * \retval -L4_ENOENT No region found. * \retval <0 IPC errors * * This function returns the properties of the region that contains the area * described by the addr and size parameter. If no such region is found but * a reserved area, the area is returned and F::In_area is set in `flags`. * Note, in the case of an area the `offset` and `m` return values are * invalid. * * \verbatim size-out / \ / \ addr-out \ ^________________\ ------------|----------------|------------------ | | Region | Dataspace | | |_______|___|____| | ------------|-------|---|----------------------- \ / |\ / \ / | |> size-in offset-out | |> addr-in \endverbatim * * * \note The value of the size input parameter should be 1 to assure that a * region can be determined unambiguously. * */ int find(l4_addr_t *addr, unsigned long *size, Offset *offset, L4Re::Rm::Flags *flags, L4::Cap *m) noexcept { return find_t::call(c(), addr, size, flags, offset, m); } L4_RPC_NF(int, find, (L4::Ipc::In_out addr, L4::Ipc::In_out size, L4Re::Rm::Flags *flags, Offset *offset, L4::Ipc::As_value > *m)); /** * A range of virtual addresses. */ struct Range { /// First address of the range. l4_addr_t start; /// Last address of the range. l4_addr_t end; }; /** * A region is a range of virtual addresses which is backed by a dataspace. * * \see api_l4re_rm */ using Region = Range; /** * An area is a range of virtual addresses which is reserved, * see L4Re::Rm::reserve_area(). * * \see api_l4re_rm */ using Area = Range; /** * Return the list of regions whose starting addresses are higher or equal to * `start` in the address space managed by this region map. * * \param start Virtual address from where to start searching. * \param[out] regions List of regions found in this region map. * * \retval >=0 Number of returned regions in the `regions` array. * \retval <0 IPC errors * * \note The returned list of regions might not be complete and the caller * shall use the function repeatedly with a start address one larger * that the end address of the last region from the previous call. */ L4_RPC(long, get_regions, (l4_addr_t start, L4::Ipc::Ret_array regions)); /** * Return the list of areas whose starting addresses are higher or equal to * `start` in the address space managed by this region map. * * \param start Virtual address from where to start searching. * \param[out] areas List of areas found in this region map. * * \retval >=0 Number of returned areas in the `areas` array. * \retval <0 IPC errors * * \note The returned list of areas might not be complete and the caller * shall use the function repeatedly with a start address one larger * than the end address of the last area from the previous call. */ L4_RPC(long, get_areas, (l4_addr_t start, L4::Ipc::Ret_array areas)); int detach(l4_addr_t start, unsigned long size, L4::Cap *mem, L4::Cap task, unsigned flags) const noexcept; typedef L4::Typeid::Rpcs Rpcs; }; inline int Rm::detach(l4_addr_t addr, L4::Cap *mem, L4::Cap const &task) const noexcept { return detach(addr, 1, mem, task, Detach_overlap); } inline int Rm::detach(void *addr, L4::Cap *mem, L4::Cap const &task) const noexcept { return detach((l4_addr_t)addr, 1, mem, task, Detach_overlap); } inline int Rm::detach(l4_addr_t addr, unsigned long size, L4::Cap *mem, L4::Cap const &task) const noexcept { return detach(addr, size, mem, task, Detach_exact); } };