genode/repos/ports/src/virtualbox6/sup_gmm.h
2021-04-20 12:03:03 +02:00

205 lines
4.4 KiB
C++

/*
* \brief Suplib GMM implementation
* \author Norman Feske
* \author Christian Helmuth
* \date 2020-11-09
*/
/*
* Copyright (C) 2020-2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#ifndef _SUP_GMM_H_
#define _SUP_GMM_H_
/* Genode includes */
#include <base/allocator_avl.h>
#include <base/attached_dataspace.h>
#include <base/mutex.h>
#include <rm_session/connection.h>
#include <vm_session/connection.h>
#include <region_map/client.h>
#include <libc/allocator.h>
/* local includes */
#include <sup.h>
namespace Sup { struct Gmm; }
/**
* Global (guest-memory) manager.(GMM)
*
* Layers in this pool are (top-down)
*
* - Page-sized allocation from Allocator_avl
* - Linear map of 32 GiB in VMM address space (Vmm_addr)
* - VirtualBox GMM chunks of GMM_CHUNK_SIZE (2 MiB) filled with pages and
* referenced with PAGEIDs (offset in linear area)
* - Slices of 128 MiB RAM dataspaces as backing store
*
* Notes
*
* - expect that guest-physical address not required here
* - NIL_GMM_CHUNKID kept unused - so offset 0 is chunk ID 1
* - we have to allocate from pool - so we need an allocator
*/
class Sup::Gmm
{
public:
struct Vmm_addr { addr_t value; };
struct Guest_addr { addr_t value; };
struct Offset { addr_t value; };
struct Bytes { size_t value; };
struct Pages { size_t value; };
struct Page_id { addr_t value; }; /* CHUNKID | PAGEIDX */
struct Protection
{
bool readable;
bool writeable;
bool executable;
bool none() const
{
return !readable && ! writeable && !executable;
}
bool operator == (Protection const &other) const
{
return readable == other.readable
&& writeable == other.writeable
&& executable == other.executable;
}
void print(Output &o) const
{
Genode::print(o, readable ? "r" : "-",
writeable ? "w" : "-",
executable ? "x" : "-");
}
};
private:
Env &_env;
Vm_connection &_vm_connection;
static constexpr Bytes _slice_size { 128*1024*1024ul };
static constexpr Bytes _map_size { 32*1024*1024*1024ul };
static constexpr auto _num_slices { _map_size.value / _slice_size.value };
Mutex _mutex { };
Dataspace_capability _slices[_num_slices];
Pages _size_pages { 0 }; /* current backing-store allocations */
Pages _reservation_pages { 0 }; /* current VMMR0_DO_GMM_UPDATE_RESERVATION pages */
Pages _alloc_ex_pages { 0 }; /* current SUPPAGEALLOCEX pages */
struct Map
{
Env &env;
Bytes const size;
Rm_connection connection { env };
Region_map_client rm { connection.create(size.value) };
Vmm_addr const base { (addr_t)env.rm().attach(rm.dataspace()) };
Vmm_addr const end { base.value + size.value - 1 };
Map(Env &env, Bytes size) : env(env), size(size) { }
} _map { _env, _map_size };
void _add_one_slice();
void _update_pool_size();
Vmm_addr _alloc_pages(Pages);
Offset _offset(Vmm_addr addr) const
{
if (addr.value < _map.base.value || addr.value > _map.end.value)
throw Out_of_range();
return Offset { addr.value - _map.base.value };
}
unsigned _slice_index(Offset offset) const
{
unsigned const index = offset.value / _slice_size.value;
if (index > _num_slices)
throw Out_of_range();
return index;
}
unsigned _slice_index(Vmm_addr addr) const
{
return _slice_index(_offset(addr));
}
Libc::Allocator _md_alloc;
Allocator_avl _alloc { &_md_alloc };
public:
struct Out_of_range : Exception { };
struct Allocation_failed : Exception { };
Gmm(Env &env, Vm_connection &);
/**
* Extend pool according to VMMR0_DO_GMM_UPDATE_RESERVATION
*/
void reservation_pages(Pages);
/**
* Allocate pages from reservation pool
*/
Vmm_addr alloc_from_reservation(Pages);
/**
* Extend pool and allocate pages
*/
Vmm_addr alloc_ex(Pages);
/**
* Free pages in pool
*/
void free(Vmm_addr, Pages);
/**
* Get page ID for VMM address inside linear area
*/
Page_id page_id(Vmm_addr);
/**
* Get 32-bit PAGEID from page ID
*/
static uint32_t page_id_as_uint32(Page_id);
/**
* Get VMM address inside linear area for page ID
*/
Vmm_addr vmm_addr(Page_id);
/**
* Make VMM memory available to the guest-physical address space
*
* \throw Out_of_range
*/
void map_to_guest(Vmm_addr, Guest_addr, Pages, Protection);
};
#endif /* _SUP_GMM_H_ */