genode/repos/os/src/drivers/platform/spec/x86/device_pd/main.cc

185 lines
4.8 KiB
C++

/*
* \brief Pci device protection domain service for platform driver
* \author Alexander Boettcher
* \date 2013-02-10
*/
/*
* Copyright (C) 2013-2015 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#include <os/static_root.h>
#include <base/printf.h>
#include <base/sleep.h>
#include <cap_session/connection.h>
#include <dataspace/client.h>
#include <rm_session/client.h>
#include <pd_session/client.h>
#include <util/flex_iterator.h>
#include <util/retry.h>
#include "../pci_device_pd_ipc.h"
/**
*
*/
struct Expanding_rm_session_client : Genode::Rm_session_client
{
Genode::Rm_session_capability _cap;
Expanding_rm_session_client(Genode::Rm_session_capability cap)
: Rm_session_client(cap), _cap(cap) { }
Local_addr attach(Genode::Dataspace_capability ds,
Genode::size_t size, Genode::off_t offset,
bool use_local_addr,
Local_addr local_addr,
bool executable) override
{
return Genode::retry<Rm_session::Out_of_metadata>(
[&] () {
return Rm_session_client::attach(ds, size, offset,
use_local_addr,
local_addr,
executable); },
[&] () {
enum { UPGRADE_QUOTA = 4096 };
if (Genode::env()->ram_session()->avail() < UPGRADE_QUOTA)
throw;
char buf[32];
Genode::snprintf(buf, sizeof(buf), "ram_quota=%u",
UPGRADE_QUOTA);
Genode::env()->parent()->upgrade(_cap, buf);
});
}
};
static Genode::Rm_session *rm_session() {
using namespace Genode;
static Expanding_rm_session_client rm (static_cap_cast<Rm_session>(env()->parent()->session("Env::rm_session", "")));
return &rm;
}
static bool map_eager(Genode::addr_t const page, unsigned log2_order)
{
using Genode::addr_t;
Genode::Thread_base * myself = Genode::Thread_base::myself();
Nova::Utcb * utcb = reinterpret_cast<Nova::Utcb *>(myself->utcb());
Nova::Rights const mapping_rw(true, true, false);
addr_t const page_fault_portal = myself->tid().exc_pt_sel + 14;
/* setup faked page fault information */
utcb->set_msg_word(((addr_t)&utcb->qual[2] - (addr_t)utcb->msg) / sizeof(addr_t));
utcb->ip = reinterpret_cast<addr_t>(map_eager);
utcb->qual[1] = page;
utcb->crd_rcv = Nova::Mem_crd(page >> 12, log2_order - 12, mapping_rw);
/* trigger faked page fault */
Genode::uint8_t res = Nova::call(page_fault_portal);
return res == Nova::NOVA_OK;
}
void Platform::Device_pd_component::attach_dma_mem(Genode::Dataspace_capability ds_cap)
{
using namespace Genode;
Dataspace_client ds_client(ds_cap);
addr_t const phys = ds_client.phys_addr();
size_t const size = ds_client.size();
addr_t page = ~0UL;
try {
page = rm_session()->attach_at(ds_cap, phys);
} catch (Rm_session::Out_of_metadata) {
throw;
} catch (...) { }
/* sanity check */
if ((page == ~0UL) || (page != phys)) {
if (page != ~0UL)
rm_session()->detach(page);
PERR("attachment of DMA memory @ %lx+%zx failed", phys, size);
return;
}
Genode::Flexpage_iterator it(page, size, page, size, 0);
for (Genode::Flexpage flex = it.page(); flex.valid(); flex = it.page()) {
if (map_eager(flex.addr, flex.log2_order))
continue;
PERR("attachment of DMA memory @ %lx+%zx failed at %lx", phys, size,
flex.addr);
return;
}
}
void Platform::Device_pd_component::assign_pci(Genode::Io_mem_dataspace_capability io_mem_cap, Genode::uint16_t rid)
{
using namespace Genode;
Dataspace_client ds_client(io_mem_cap);
addr_t page = rm_session()->attach(io_mem_cap);
/* sanity check */
if (!page)
throw Rm_session::Region_conflict();
/* trigger mapping of whole memory area */
if (!map_eager(page, 12))
PERR("assignment of PCI device failed - %lx", page);
/* try to assign pci device to this protection domain */
if (!env()->pd_session()->assign_pci(page, rid))
PERR("assignment of PCI device %x:%x.%x failed phys=%lx virt=%lx",
rid >> 8, (rid >> 3) & 0x1f, rid & 0x7,
ds_client.phys_addr(), page);
/* we don't need the mapping anymore */
rm_session()->detach(page);
}
int main(int argc, char **argv)
{
using namespace Genode;
Genode::printf("Device protection domain starting ...\n");
/*
* Initialize server entry point
*/
enum {
STACK_SIZE = 1024*sizeof(Genode::addr_t)
};
static Cap_connection cap;
static Rpc_entrypoint ep(&cap, STACK_SIZE, "device_pd_ep");
static Platform::Device_pd_component pd_component;
/*
* Attach input root interface to the entry point
*/
static Static_root<Platform::Device_pd> root(ep.manage(&pd_component));
env()->parent()->announce(ep.manage(&root));
printf("Device protection domain started\n");
Genode::sleep_forever();
return 0;
}