mirror of
https://github.com/genodelabs/genode.git
synced 2025-04-08 11:55:24 +00:00
platform_drv: add IOMMU devices to common
With this change, platform-specific code is able to define factories that acquire IOMMU devices to be used by the platform driver. genodelabs/genode#4761
This commit is contained in:
parent
f98466430f
commit
f2e63bdd64
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* \brief Platform driver - compound object for all derivate implementations
|
||||
* \author Johannes Schlatow
|
||||
* \author Stefan Kalkowski
|
||||
* \date 2022-05-10
|
||||
*/
|
||||
@ -11,11 +12,19 @@
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _SRC__DRIVERS__PLATFORM__COMMON_H_
|
||||
#define _SRC__DRIVERS__PLATFORM__COMMON_H_
|
||||
|
||||
#include <base/registry.h>
|
||||
|
||||
#include <root.h>
|
||||
#include <device_owner.h>
|
||||
#include <io_mmu.h>
|
||||
|
||||
namespace Driver { class Common; };
|
||||
|
||||
class Driver::Common : Device_reporter
|
||||
class Driver::Common : Device_reporter,
|
||||
public Device_owner
|
||||
{
|
||||
private:
|
||||
|
||||
@ -25,9 +34,14 @@ class Driver::Common : Device_reporter
|
||||
Attached_rom_dataspace _platform_info { _env, "platform_info" };
|
||||
Heap _heap { _env.ram(), _env.rm() };
|
||||
Sliced_heap _sliced_heap { _env.ram(), _env.rm() };
|
||||
Device_model _devices { _env, _heap, *this };
|
||||
Device_model _devices { _env, _heap, *this, *this };
|
||||
Signal_handler<Common> _dev_handler { _env.ep(), *this,
|
||||
&Common::_handle_devices };
|
||||
Device::Owner _owner_id { *this };
|
||||
|
||||
Io_mmu_devices _io_mmu_devices { };
|
||||
Registry<Io_mmu_factory> _io_mmu_factories { };
|
||||
|
||||
Driver::Root _root;
|
||||
|
||||
Constructible<Expanding_reporter> _cfg_reporter { };
|
||||
@ -44,8 +58,12 @@ class Driver::Common : Device_reporter
|
||||
Heap & heap() { return _heap; }
|
||||
Device_model & devices() { return _devices; }
|
||||
|
||||
Registry<Io_mmu_factory> & io_mmu_factories() {
|
||||
return _io_mmu_factories; }
|
||||
|
||||
void announce_service();
|
||||
void handle_config(Xml_node config);
|
||||
void acquire_io_mmu_devices();
|
||||
|
||||
|
||||
/*********************
|
||||
@ -53,13 +71,38 @@ class Driver::Common : Device_reporter
|
||||
*********************/
|
||||
|
||||
void update_report() override;
|
||||
|
||||
/******************
|
||||
** Device_owner **
|
||||
******************/
|
||||
|
||||
void disable_device(Device const & device) override;
|
||||
};
|
||||
|
||||
|
||||
void Driver::Common::acquire_io_mmu_devices()
|
||||
{
|
||||
_io_mmu_factories.for_each([&] (Io_mmu_factory & factory) {
|
||||
|
||||
_devices.for_each([&] (Device & dev) {
|
||||
if (dev.owner().valid())
|
||||
return;
|
||||
|
||||
if (factory.matches(dev)) {
|
||||
dev.acquire(*this);
|
||||
factory.create(_heap, _io_mmu_devices, dev);
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
void Driver::Common::_handle_devices()
|
||||
{
|
||||
_devices_rom.update();
|
||||
_devices.update(_devices_rom.xml());
|
||||
acquire_io_mmu_devices();
|
||||
update_report();
|
||||
_root.update_policy();
|
||||
}
|
||||
@ -83,6 +126,15 @@ void Driver::Common::update_report()
|
||||
}
|
||||
|
||||
|
||||
void Driver::Common::disable_device(Device const & device)
|
||||
{
|
||||
_io_mmu_devices.for_each([&] (Io_mmu & io_mmu) {
|
||||
if (io_mmu.name() == device.name())
|
||||
destroy(_heap, &io_mmu);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
void Driver::Common::handle_config(Xml_node config)
|
||||
{
|
||||
config.for_each_sub_node("report", [&] (Xml_node const node) {
|
||||
@ -120,3 +172,5 @@ Driver::Common::Common(Genode::Env & env,
|
||||
_devices_rom.sigh(_dev_handler);
|
||||
_handle_devices();
|
||||
}
|
||||
|
||||
#endif /* _SRC__DRIVERS__PLATFORM__COMMON_H_ */
|
||||
|
@ -317,6 +317,7 @@ class Driver::Device_model :
|
||||
Env & _env;
|
||||
Heap & _heap;
|
||||
Device_reporter & _reporter;
|
||||
Device_owner & _owner;
|
||||
List_model<Device> _model { };
|
||||
Registry<Shared_interrupt> _shared_irqs { };
|
||||
Clocks _clocks { };
|
||||
@ -331,8 +332,9 @@ class Driver::Device_model :
|
||||
|
||||
Device_model(Env & env,
|
||||
Heap & heap,
|
||||
Device_reporter & reporter)
|
||||
: _env(env), _heap(heap), _reporter(reporter) { }
|
||||
Device_reporter & reporter,
|
||||
Device_owner & owner)
|
||||
: _env(env), _heap(heap), _reporter(reporter), _owner(owner) { }
|
||||
|
||||
~Device_model() {
|
||||
_model.destroy_all_elements(*this); }
|
||||
|
@ -63,6 +63,7 @@ void Device_model::destroy_element(Device & device)
|
||||
device._reserved_mem_list.destroy_all_elements(policy);
|
||||
}
|
||||
|
||||
device.release(_owner);
|
||||
Genode::destroy(_heap, &device);
|
||||
}
|
||||
|
||||
|
131
repos/os/src/drivers/platform/dma_allocator.cc
Normal file
131
repos/os/src/drivers/platform/dma_allocator.cc
Normal file
@ -0,0 +1,131 @@
|
||||
/*
|
||||
* \brief Platform driver - DMA allocator
|
||||
* \author Johannes Schlatow
|
||||
* \date 2023-03-23
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2023 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.
|
||||
*/
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/log.h>
|
||||
|
||||
/* local includes */
|
||||
#include <dma_allocator.h>
|
||||
|
||||
using namespace Driver;
|
||||
|
||||
addr_t Dma_allocator::_alloc_dma_addr(addr_t const phys_addr,
|
||||
size_t const size,
|
||||
bool const force_phys_addr)
|
||||
{
|
||||
using Alloc_error = Allocator::Alloc_error;
|
||||
|
||||
if (!_iommu) return phys_addr;
|
||||
|
||||
/*
|
||||
* 1:1 mapping (allocate at specified range from DMA memory allocator)
|
||||
*/
|
||||
if (force_phys_addr) {
|
||||
return _dma_alloc.alloc_addr(size, phys_addr).convert<addr_t>(
|
||||
[&] (void *) -> addr_t { return phys_addr; },
|
||||
[&] (Alloc_error err) -> addr_t {
|
||||
switch (err) {
|
||||
case Alloc_error::OUT_OF_RAM: throw Out_of_ram();
|
||||
case Alloc_error::OUT_OF_CAPS: throw Out_of_caps();
|
||||
case Alloc_error::DENIED:
|
||||
error("Could not attach DMA range at ",
|
||||
Hex_range(phys_addr, size), " (error: ", err, ")");
|
||||
break;
|
||||
}
|
||||
return 0UL;
|
||||
});
|
||||
}
|
||||
|
||||
/* natural size align (to some limit) for better IOMMU TLB usage */
|
||||
unsigned size_align_log2 = unsigned(log2(size));
|
||||
if (size_align_log2 < 12) /* 4 kB */
|
||||
size_align_log2 = 12;
|
||||
if (size_align_log2 > 24) /* 16 MB */
|
||||
size_align_log2 = 24;
|
||||
|
||||
return _dma_alloc.alloc_aligned(size, size_align_log2).convert<addr_t>(
|
||||
[&] (void *ptr) { return (addr_t)ptr; },
|
||||
[&] (Alloc_error err) -> addr_t {
|
||||
switch (err) {
|
||||
case Alloc_error::OUT_OF_RAM: throw Out_of_ram();
|
||||
case Alloc_error::OUT_OF_CAPS: throw Out_of_caps();
|
||||
case Alloc_error::DENIED:
|
||||
error("Could not allocate DMA area of size: ", size,
|
||||
" alignment: ", size_align_log2,
|
||||
" total avail: ", _dma_alloc.avail(),
|
||||
" (error: ", err, ")");
|
||||
break;
|
||||
};
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
bool Dma_allocator::reserve(addr_t phys_addr, size_t size)
|
||||
{
|
||||
return _alloc_dma_addr(phys_addr, size, true) == phys_addr;
|
||||
}
|
||||
|
||||
|
||||
void Dma_allocator::unreserve(addr_t phys_addr, size_t) { _free_dma_addr(phys_addr); }
|
||||
|
||||
|
||||
Dma_buffer & Dma_allocator::alloc_buffer(Ram_dataspace_capability cap,
|
||||
addr_t phys_addr,
|
||||
size_t size)
|
||||
{
|
||||
addr_t dma_addr = _alloc_dma_addr(phys_addr, size, false);
|
||||
|
||||
try {
|
||||
return * new (_md_alloc) Dma_buffer(_registry, *this, cap, dma_addr, size);
|
||||
} catch (Out_of_ram) {
|
||||
_free_dma_addr(dma_addr);
|
||||
throw;
|
||||
} catch (Out_of_caps) {
|
||||
_free_dma_addr(dma_addr);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Dma_allocator::_free_dma_addr(addr_t dma_addr)
|
||||
{
|
||||
if (_iommu)
|
||||
_dma_alloc.free((void *)dma_addr);
|
||||
}
|
||||
|
||||
|
||||
Dma_allocator::Dma_allocator(Allocator & md_alloc,
|
||||
bool const iommu)
|
||||
:
|
||||
_md_alloc(md_alloc), _iommu(iommu)
|
||||
{
|
||||
/* 0x1000 - 4GB */
|
||||
enum { DMA_SIZE = 0xffffe000 };
|
||||
_dma_alloc.add_range(0x1000, DMA_SIZE);
|
||||
|
||||
/*
|
||||
* Interrupt address range is special handled and in general not
|
||||
* usable for normal DMA translations, see chapter 3.15
|
||||
* of "Intel Virtualization Technology for Directed I/O"
|
||||
* (March 2023, Revision 4.1)
|
||||
*/
|
||||
enum { IRQ_RANGE_BASE = 0xfee00000u, IRQ_RANGE_SIZE = 0x100000 };
|
||||
_dma_alloc.remove_range(IRQ_RANGE_BASE, IRQ_RANGE_SIZE);
|
||||
}
|
||||
|
||||
|
||||
Dma_buffer::~Dma_buffer()
|
||||
{
|
||||
dma_alloc._free_dma_addr(dma_addr);
|
||||
}
|
76
repos/os/src/drivers/platform/dma_allocator.h
Normal file
76
repos/os/src/drivers/platform/dma_allocator.h
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* \brief Platform driver - DMA allocator
|
||||
* \author Johannes Schlatow
|
||||
* \date 2023-03-23
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2023 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 _SRC__DRIVERS__PLATFORM__DMA_ALLOCATOR_H_
|
||||
#define _SRC__DRIVERS__PLATFORM__DMA_ALLOCATOR_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/allocator_avl.h>
|
||||
#include <base/registry.h>
|
||||
|
||||
namespace Driver {
|
||||
using namespace Genode;
|
||||
|
||||
struct Dma_buffer;
|
||||
class Dma_allocator;
|
||||
}
|
||||
|
||||
|
||||
struct Driver::Dma_buffer : Registry<Dma_buffer>::Element
|
||||
{
|
||||
Ram_dataspace_capability const cap;
|
||||
addr_t dma_addr;
|
||||
size_t size;
|
||||
Dma_allocator & dma_alloc;
|
||||
|
||||
Dma_buffer(Registry<Dma_buffer> & registry,
|
||||
Dma_allocator & dma_alloc,
|
||||
Ram_dataspace_capability const cap,
|
||||
addr_t dma_addr,
|
||||
size_t size)
|
||||
: Registry<Dma_buffer>::Element(registry, *this),
|
||||
cap(cap), dma_addr(dma_addr), size(size), dma_alloc(dma_alloc)
|
||||
{ }
|
||||
|
||||
~Dma_buffer();
|
||||
};
|
||||
|
||||
|
||||
class Driver::Dma_allocator
|
||||
{
|
||||
private:
|
||||
|
||||
friend class Dma_buffer;
|
||||
|
||||
Allocator & _md_alloc;
|
||||
bool const _iommu;
|
||||
Allocator_avl _dma_alloc { &_md_alloc };
|
||||
Registry<Dma_buffer> _registry { };
|
||||
|
||||
addr_t _alloc_dma_addr(addr_t phys_addr, size_t size, bool const force_phys_addr);
|
||||
void _free_dma_addr(addr_t dma_addr);
|
||||
|
||||
public:
|
||||
|
||||
bool reserve(addr_t phys_addr, size_t size);
|
||||
void unreserve(addr_t phys_addr, size_t size);
|
||||
|
||||
Dma_buffer & alloc_buffer(Ram_dataspace_capability cap, addr_t phys_addr, size_t size);
|
||||
|
||||
Registry<Dma_buffer> & buffer_registry() { return _registry; }
|
||||
Registry<Dma_buffer> const & buffer_registry() const { return _registry; }
|
||||
|
||||
Dma_allocator(Allocator & md_alloc, bool const iommu);
|
||||
};
|
||||
|
||||
#endif /* _SRC__DRIVERS__PLATFORM__DMA_ALLOCATOR_H */
|
198
repos/os/src/drivers/platform/io_mmu.h
Normal file
198
repos/os/src/drivers/platform/io_mmu.h
Normal file
@ -0,0 +1,198 @@
|
||||
/*
|
||||
* \brief Platform driver - IO MMU interface
|
||||
* \author Johannes Schlatow
|
||||
* \date 2023-01-20
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2023 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 _SRC__DRIVERS__PLATFORM__IO_MMU_H_
|
||||
#define _SRC__DRIVERS__PLATFORM__IO_MMU_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/registry.h>
|
||||
#include <base/quota_guard.h>
|
||||
#include <pci/types.h>
|
||||
|
||||
/* local includes */
|
||||
#include <device.h>
|
||||
#include <dma_allocator.h>
|
||||
|
||||
namespace Driver
|
||||
{
|
||||
using namespace Genode;
|
||||
|
||||
class Io_mmu;
|
||||
class Io_mmu_factory;
|
||||
|
||||
using Io_mmu_devices = Registry<Io_mmu>;
|
||||
}
|
||||
|
||||
|
||||
class Driver::Io_mmu : private Io_mmu_devices::Element
|
||||
{
|
||||
public:
|
||||
|
||||
using Range = Platform::Device_interface::Range;
|
||||
|
||||
class Domain : private Registry<Domain>::Element
|
||||
{
|
||||
private:
|
||||
|
||||
friend class Io_mmu;
|
||||
|
||||
Io_mmu & _io_mmu;
|
||||
Allocator & _md_alloc;
|
||||
|
||||
unsigned _active_devices { 0 };
|
||||
|
||||
public:
|
||||
|
||||
Allocator & md_alloc() { return _md_alloc; }
|
||||
|
||||
Device::Name const & device_name() const { return _io_mmu.name(); }
|
||||
|
||||
void enable_device()
|
||||
{
|
||||
_active_devices++;
|
||||
|
||||
if (_active_devices == 1)
|
||||
_io_mmu._enable_domain();
|
||||
}
|
||||
|
||||
void disable_device()
|
||||
{
|
||||
if (_active_devices > 0) {
|
||||
_active_devices--;
|
||||
|
||||
if (_active_devices == 0)
|
||||
_io_mmu._disable_domain();
|
||||
}
|
||||
}
|
||||
|
||||
unsigned devices() const { return _active_devices; }
|
||||
|
||||
/* interface for (un)assigning a pci device */
|
||||
virtual void enable_pci_device(Io_mem_dataspace_capability const,
|
||||
Pci::Bdf const) = 0;
|
||||
virtual void disable_pci_device(Io_mem_dataspace_capability const,
|
||||
Pci::Bdf const) = 0;
|
||||
|
||||
/* interface for adding/removing DMA buffers */
|
||||
virtual void add_range(Range const &, Dataspace_capability const) = 0;
|
||||
virtual void remove_range(Range const &) = 0;
|
||||
|
||||
Domain(Io_mmu & io_mmu,
|
||||
Allocator & md_alloc,
|
||||
Registry<Dma_buffer> const & buffer_registry)
|
||||
: Registry<Domain>::Element(io_mmu._domains, *this),
|
||||
_io_mmu(io_mmu), _md_alloc(md_alloc)
|
||||
{
|
||||
/* we always need to add existing buffers when creating a new domain */
|
||||
buffer_registry.for_each([&] (Dma_buffer const & buf) {
|
||||
add_range({ buf.dma_addr, buf.size }, buf.cap); });
|
||||
}
|
||||
|
||||
virtual ~Domain() { }
|
||||
};
|
||||
|
||||
protected:
|
||||
|
||||
friend class Domain;
|
||||
|
||||
Device::Name _name;
|
||||
Registry<Domain> _domains { };
|
||||
|
||||
unsigned _active_domains { 0 };
|
||||
|
||||
virtual void _enable() { };
|
||||
virtual void _disable() { };
|
||||
|
||||
void _enable_domain()
|
||||
{
|
||||
if (!_active_domains)
|
||||
_enable();
|
||||
|
||||
_active_domains++;
|
||||
};
|
||||
|
||||
void _disable_domain()
|
||||
{
|
||||
if (_active_domains > 0)
|
||||
_active_domains--;
|
||||
|
||||
if (!_active_domains)
|
||||
_disable();
|
||||
};
|
||||
|
||||
void _destroy_domains()
|
||||
{
|
||||
_domains.for_each([&] (Domain & domain) {
|
||||
destroy(domain.md_alloc(), &domain); });
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Device::Name const & name() const { return _name; }
|
||||
|
||||
bool domain_owner(Domain const & domain) const {
|
||||
return &domain._io_mmu == this; }
|
||||
|
||||
/* Return true if device requires physical addressing */
|
||||
virtual bool mpu() { return false; };
|
||||
|
||||
/* Create a Io_mmu::Domain object */
|
||||
virtual Domain & create_domain(Allocator &,
|
||||
Registry<Dma_buffer> const &,
|
||||
Ram_quota_guard &,
|
||||
Cap_quota_guard &) = 0;
|
||||
|
||||
Io_mmu(Io_mmu_devices & io_mmu_devices,
|
||||
Device::Name const & name)
|
||||
: Io_mmu_devices::Element(io_mmu_devices, *this),
|
||||
_name(name)
|
||||
{ }
|
||||
|
||||
virtual ~Io_mmu()
|
||||
{
|
||||
/**
|
||||
* destroying domain objects
|
||||
* any derived class that overrides any virtual method must
|
||||
* call this at the very beginning of its destruction
|
||||
*/
|
||||
_destroy_domains();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class Driver::Io_mmu_factory : private Genode::Registry<Io_mmu_factory>::Element
|
||||
{
|
||||
protected:
|
||||
|
||||
Device::Type _type;
|
||||
|
||||
public:
|
||||
|
||||
Io_mmu_factory(Registry<Io_mmu_factory> & registry,
|
||||
Device::Type const & type)
|
||||
: Registry<Io_mmu_factory>::Element(registry, *this),
|
||||
_type(type)
|
||||
{ }
|
||||
|
||||
virtual ~Io_mmu_factory() { }
|
||||
|
||||
bool matches(Device const & dev) {
|
||||
return dev.type() == _type; }
|
||||
|
||||
virtual void create(Allocator &,
|
||||
Io_mmu_devices &,
|
||||
Device const &) = 0;
|
||||
};
|
||||
|
||||
|
||||
#endif /* _SRC__DRIVERS__PLATFORM__IO_MMU_H_ */
|
@ -7,6 +7,7 @@ SRC_CC += pci.cc
|
||||
SRC_CC += root.cc
|
||||
SRC_CC += session_component.cc
|
||||
SRC_CC += shared_irq.cc
|
||||
SRC_CC += dma_allocator.cc
|
||||
|
||||
GENERIC_DIR := $(dir $(call select_from_repositories,src/drivers/platform/target.inc))
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user