289 lines
8.8 KiB
C++
289 lines
8.8 KiB
C++
/*
|
|
* \brief NOVA-specific convenience functions
|
|
* \author Norman Feske
|
|
* \author Sebastian Sumpf
|
|
* \author Alexander Boettcher
|
|
* \date 2010-01-19
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2010-2017 Genode Labs GmbH
|
|
*
|
|
* This file is part of the Genode OS framework, which is distributed
|
|
* under the terms of the GNU Affero General Public License version 3.
|
|
*/
|
|
|
|
#ifndef _CORE__INCLUDE__NOVA_UTIL_H_
|
|
#define _CORE__INCLUDE__NOVA_UTIL_H_
|
|
|
|
/* Genode includes */
|
|
#include <base/log.h>
|
|
|
|
/* NOVA includes */
|
|
#include <nova/syscalls.h>
|
|
|
|
/* local includes */
|
|
#include <util.h>
|
|
|
|
/**
|
|
* Return boot CPU number. It is required if threads in core should be placed
|
|
* on the same CPU as the main thread.
|
|
*/
|
|
inline Genode::addr_t boot_cpu()
|
|
{
|
|
/**
|
|
* Initial value of ax and di register, saved by the crt0 startup code
|
|
* and SOLELY VALID in 'core' !!!
|
|
*
|
|
* For x86_32 - __initial_ax contains the number of the boot CPU.
|
|
* For x86_64 - __initial_di contains the number of the boot CPU.
|
|
*/
|
|
extern Genode::addr_t __initial_ax;
|
|
extern Genode::addr_t __initial_di;
|
|
|
|
return (sizeof(void *) > 4) ? __initial_di : __initial_ax;
|
|
}
|
|
|
|
/**
|
|
* Establish a mapping
|
|
*
|
|
* \param utcb UTCB of the calling EC
|
|
* \param src_crd capability range descriptor of source
|
|
* resource to map locally
|
|
* \param dst_crd capability range descriptor of mapping
|
|
* target
|
|
* \param kern_pd Whether to map the items from the kernel or from core
|
|
* \param dma_mem Whether the memory is usable for DMA or not
|
|
*/
|
|
static int map_local(Genode::addr_t const pd, Nova::Utcb &utcb,
|
|
Nova::Crd const src_crd, Nova::Crd const dst_crd,
|
|
bool const kern_pd = false, bool const dma_mem = false,
|
|
bool const write_combined = false)
|
|
{
|
|
/* asynchronously map capabilities */
|
|
utcb.set_msg_word(0);
|
|
|
|
/* ignore return value as one item always fits into the utcb */
|
|
bool const ok = utcb.append_item(src_crd, 0, kern_pd, false, false,
|
|
dma_mem, write_combined);
|
|
(void)ok;
|
|
|
|
Nova::uint8_t res = Nova::delegate(pd, pd, dst_crd);
|
|
if (res != Nova::NOVA_OK) {
|
|
|
|
typedef Genode::Hex Hex;
|
|
error("map_local failed ",
|
|
Hex(src_crd.addr()), ":", Hex(src_crd.order()), ":", Hex(src_crd.type()), "->",
|
|
Hex(dst_crd.addr()), ":", Hex(dst_crd.order()), ":", Hex(dst_crd.type()), " - ",
|
|
"result=", Hex(res), " "
|
|
"msg=", Hex(utcb.msg_items()), ":",
|
|
Hex(utcb.msg_words()), ":",
|
|
Hex(utcb.msg()[0]), " !!! "
|
|
"utcb=", &utcb, " "
|
|
"kern=", kern_pd);
|
|
return res > 0 ? res : -1;
|
|
}
|
|
/* clear receive window */
|
|
utcb.crd_rcv = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static inline int unmap_local(Nova::Crd crd, bool self = true) {
|
|
return Nova::revoke(crd, self); }
|
|
|
|
inline int map_local_phys_to_virt(Nova::Utcb &utcb, Nova::Crd const src,
|
|
Nova::Crd const dst, Genode::addr_t const pd)
|
|
{
|
|
return map_local(pd, utcb, src, dst, true);
|
|
}
|
|
|
|
inline int map_local_one_to_one(Nova::Utcb &utcb, Nova::Crd const crd,
|
|
Genode::addr_t const pd)
|
|
{
|
|
return map_local(pd, utcb, crd, crd, true);
|
|
}
|
|
|
|
|
|
/**
|
|
* Find least significant set bit in value
|
|
*/
|
|
inline unsigned char
|
|
lsb_bit(unsigned long const &value, unsigned char const shift = 0)
|
|
{
|
|
unsigned long const scan = value >> shift;
|
|
if (scan == 0) return 0;
|
|
|
|
unsigned char pos = __builtin_ctzl(scan);
|
|
unsigned char res = shift ? pos + shift : pos;
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* Remap pages in the local address space
|
|
*
|
|
* \param utcb UTCB of the main thread
|
|
* \param from_start physical source address
|
|
* \param to_start local virtual destination address
|
|
* \param num_pages number of pages to map
|
|
*/
|
|
inline int map_local(Genode::addr_t const pd, Nova::Utcb &utcb,
|
|
Genode::addr_t from_start, Genode::addr_t to_start,
|
|
Genode::size_t num_pages,
|
|
Nova::Rights const &permission,
|
|
bool kern_pd = false, bool dma_mem = false,
|
|
bool write_combined = false)
|
|
{
|
|
using namespace Nova;
|
|
using namespace Genode;
|
|
|
|
size_t const size = num_pages << get_page_size_log2();
|
|
|
|
addr_t const from_end = from_start + size;
|
|
addr_t const to_end = to_start + size;
|
|
|
|
for (addr_t offset = 0; offset < size; ) {
|
|
|
|
addr_t const from_curr = from_start + offset;
|
|
addr_t const to_curr = to_start + offset;
|
|
|
|
/*
|
|
* The common alignment corresponds to the number of least significant
|
|
* zero bits in both addresses.
|
|
*/
|
|
addr_t const common_bits = from_curr | to_curr;
|
|
|
|
/* find least set bit in common bits */
|
|
size_t order = lsb_bit(common_bits, get_page_size_log2());
|
|
|
|
/* look if flexpage fits into both 'from' and 'to' address range */
|
|
if ((from_end - from_curr) < (1UL << order))
|
|
order = log2(from_end - from_curr);
|
|
|
|
if ((to_end - to_curr) < (1UL << order))
|
|
order = log2(to_end - to_curr);
|
|
|
|
if (order >= sizeof(void *)*8)
|
|
return 1;
|
|
|
|
int const res = map_local(pd, utcb,
|
|
Mem_crd((from_curr >> 12), order - get_page_size_log2(), permission),
|
|
Mem_crd((to_curr >> 12), order - get_page_size_log2(), permission),
|
|
kern_pd, dma_mem, write_combined);
|
|
if (res) return res;
|
|
|
|
/* advance offset by current flexpage size */
|
|
offset += (1UL << order);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* Unmap pages from the local address space
|
|
*
|
|
* \param utcb UTCB of the main thread
|
|
* \param start local virtual address
|
|
* \param num_pages number of pages to unmap
|
|
* \param self unmap from this pd or solely from other pds
|
|
* \param self map from this pd or solely from other pds
|
|
* \param rights rights to be revoked, default: all rwx
|
|
*/
|
|
inline void unmap_local(Nova::Utcb &, Genode::addr_t start,
|
|
Genode::size_t num_pages,
|
|
bool const self = true,
|
|
Nova::Rights const rwx = Nova::Rights(true, true, true))
|
|
{
|
|
using namespace Nova;
|
|
using namespace Genode;
|
|
|
|
Genode::addr_t base = start >> get_page_size_log2();
|
|
|
|
if (start & (get_page_size() - 1)) {
|
|
error("unmap failed - unaligned address specified");
|
|
return;
|
|
}
|
|
|
|
while (num_pages) {
|
|
unsigned char const base_bit = lsb_bit(base);
|
|
unsigned char const order_bit = min(log2(num_pages), 31U);
|
|
unsigned char const order = min(order_bit, base_bit);
|
|
|
|
Mem_crd const crd(base, order, rwx);
|
|
|
|
unmap_local(crd, self);
|
|
|
|
num_pages -= 1UL << order;
|
|
base += 1UL << order;
|
|
}
|
|
}
|
|
|
|
|
|
template <typename FUNC>
|
|
inline Nova::uint8_t syscall_retry(Genode::Pager_object &pager, FUNC func)
|
|
{
|
|
Nova::uint8_t res;
|
|
do {
|
|
res = func();
|
|
} while (res == Nova::NOVA_PD_OOM && Nova::NOVA_OK == pager.handle_oom());
|
|
|
|
return res;
|
|
}
|
|
|
|
inline Nova::uint8_t async_map(Genode::Pager_object &pager,
|
|
Genode::addr_t const source_pd,
|
|
Genode::addr_t const target_pd,
|
|
Nova::Obj_crd const &source_initial_caps,
|
|
Nova::Obj_crd const &target_initial_caps,
|
|
Nova::Utcb &utcb)
|
|
{
|
|
/* asynchronously map capabilities */
|
|
utcb.set_msg_word(0);
|
|
|
|
/* ignore return value as one item always fits into the utcb */
|
|
bool const ok = utcb.append_item(source_initial_caps, 0);
|
|
(void)ok;
|
|
|
|
return syscall_retry(pager,
|
|
[&]() {
|
|
return Nova::delegate(source_pd, target_pd, target_initial_caps);
|
|
});
|
|
}
|
|
|
|
inline Nova::uint8_t map_vcpu_portals(Genode::Pager_object &pager,
|
|
Genode::addr_t const source_exc_base,
|
|
Genode::addr_t const target_exc_base,
|
|
Nova::Utcb &utcb,
|
|
Genode::addr_t const source_pd)
|
|
{
|
|
using Nova::Obj_crd;
|
|
using Nova::NUM_INITIAL_VCPU_PT_LOG2;
|
|
|
|
Obj_crd const source_initial_caps(source_exc_base, NUM_INITIAL_VCPU_PT_LOG2);
|
|
Obj_crd const target_initial_caps(target_exc_base, NUM_INITIAL_VCPU_PT_LOG2);
|
|
|
|
return async_map(pager, source_pd, pager.pd_sel(),
|
|
source_initial_caps, target_initial_caps, utcb);
|
|
}
|
|
|
|
inline Nova::uint8_t map_pagefault_portal(Genode::Pager_object &pager,
|
|
Genode::addr_t const source_exc_base,
|
|
Genode::addr_t const target_exc_base,
|
|
Genode::addr_t const target_pd,
|
|
Nova::Utcb &utcb)
|
|
{
|
|
using Nova::Obj_crd;
|
|
using Nova::PT_SEL_PAGE_FAULT;
|
|
|
|
Genode::addr_t const source_pd = Genode::platform_specific().core_pd_sel();
|
|
|
|
Obj_crd const source_initial_caps(source_exc_base + PT_SEL_PAGE_FAULT, 0);
|
|
Obj_crd const target_initial_caps(target_exc_base + PT_SEL_PAGE_FAULT, 0);
|
|
|
|
return async_map(pager, source_pd, target_pd,
|
|
source_initial_caps, target_initial_caps, utcb);
|
|
}
|
|
|
|
#endif /* _CORE__INCLUDE__NOVA_UTIL_H_ */
|