gpu/intel: add platform service for display handling

The platform services is intented to be used by dde_linux's intel_fb_drv
in order to initlialize displays.

* implement and announce platform session
* limit accessible GTT and aperture of client to 64 MB
* forward display engine IRQs to platform client
* move all PCI resources to 'Igd::Resources' class in order to make them
  accessible by the platform service and the GPU driver
* fix fence register allocation for id zero (return true)

issue #4233
This commit is contained in:
Sebastian Sumpf 2021-07-16 11:47:26 +02:00 committed by Christian Helmuth
parent 11192b18e6
commit 6c003a13d2
4 changed files with 717 additions and 275 deletions

View File

@ -40,6 +40,8 @@
#include <ggtt.h>
#include <context.h>
#include <context_descriptor.h>
#include <resources.h>
#include <platform_session.h>
#include <ring_buffer.h>
@ -76,7 +78,6 @@ static Igd::Device_info _supported_devices[] = {
struct Igd::Device
{
struct Initialization_failed : Genode::Exception { };
struct Unsupported_device : Genode::Exception { };
struct Out_of_ram : Genode::Exception { };
struct Could_not_map_buffer : Genode::Exception { };
@ -105,54 +106,8 @@ struct Igd::Device
** PCI **
*********/
/*
* Config space utility methods
*/
template <typename T>
static Platform::Device::Access_size _access_size(T)
{
switch (sizeof(T)) {
case 1: return Platform::Device::ACCESS_8BIT;
case 2: return Platform::Device::ACCESS_16BIT;
default: return Platform::Device::ACCESS_32BIT;
}
}
template <typename FUNC>
void _retry_func(FUNC const &func)
{
Genode::size_t donate = PAGE_SIZE;
Genode::retry<Platform::Out_of_ram>(
func,
[&] () {
_pci.upgrade_ram(donate);
donate *= 2;
}, 2);
}
template <typename T>
T _config_read(unsigned int const devfn)
{
T val = 0;
_retry_func([&] () {
val = _device.config_read(devfn, _access_size(val));
});
return val;
}
template <typename T>
void _config_write(unsigned int const devfn, T val)
{
_retry_func([&] () {
_device.config_write(devfn, val, _access_size(val));
});
}
Platform::Connection &_pci;
Platform::Device_capability _pci_cap;
Platform::Device_client _device { _pci_cap };
Resources &_resources;
Platform::Device_client &_device { _resources.gpu_client() };
struct Pci_backend_alloc : Utils::Backend_alloc
{
@ -176,58 +131,8 @@ struct Igd::Device
_pci.free_dma_buffer(cap);
}
} _pci_backend_alloc { _pci };
} _pci_backend_alloc { _resources.platform() };
enum {
PCI_NUM_RES = 6,
PCI_CMD_REG = 4,
PCI_BUS_MASTER = 1<<2,
GTTMMADR = 0,
GMADR = 2,
};
Genode::Constructible<Genode::Io_mem_connection> _res[PCI_NUM_RES];
addr_t _res_base[PCI_NUM_RES];
size_t _res_size[PCI_NUM_RES];
Genode::Io_mem_dataspace_capability _res_ds[PCI_NUM_RES];
Genode::Constructible<Genode::Irq_session_client> _irq { };
void _poke_pci_resource(unsigned const id)
{
if (id >= PCI_NUM_RES) { throw -1; }
if (_res[id].constructed()) { throw -2; }
Platform::Device::Resource const res = _device.resource(id);
_res_base[id] = res.base();
_res_size[id] = res.size();
}
addr_t _map_pci_resource(unsigned const id)
{
_poke_pci_resource(id);
_res[id].construct(_env, _res_base[id], _res_size[id]);
_res_ds[id] = _res[id]->dataspace();
if (!_res_ds[id].valid()) { throw Initialization_failed(); }
addr_t addr = (addr_t)(_env.rm().attach(_res_ds[id], _res_size[id]));
using namespace Genode;
log("Map res:", id,
" base:", Hex(_res_base[id]),
" size:", Hex(_res_size[id]),
" vaddr:", Hex(addr));
return addr;
}
void _enable_pci_bus_master()
{
uint16_t cmd = _config_read<uint16_t>(PCI_CMD_REG);
cmd |= PCI_BUS_MASTER;
_config_write(PCI_CMD_REG, cmd);
}
Device_info _info { };
@ -247,6 +152,7 @@ struct Igd::Device
Hex(dev, Hex::OMIT_PREFIX), ".",
Hex(fun, Hex::OMIT_PREFIX), ")");
enum { PCI_NUM_RES = 6 };
for (int i = 0; i < PCI_NUM_RES; i++) {
using Resource = Platform::Device::Resource;
@ -342,7 +248,7 @@ struct Igd::Device
* so we do not have to rollback when the allocation failes.
*/
addr_t const base = _res_base[GMADR] + _ggtt->addr(offset);
addr_t const base = _resources.gmadr_base() + _ggtt->addr(offset);
Genode::Registered<Ggtt_mmio_mapping> *mem = new (&alloc)
Genode::Registered<Ggtt_mmio_mapping>(_ggtt_mmio_mapping_registry,
_env, base, size, offset);
@ -913,51 +819,7 @@ struct Igd::Device
return true;
}
void _handle_irq()
{
_mmio->disable_master_irq();
Mmio::GT_0_INTERRUPT_IIR::access_t const v = _mmio->read<Mmio::GT_0_INTERRUPT_IIR>();
bool const ctx_switch = Mmio::GT_0_INTERRUPT_IIR::Cs_ctx_switch_interrupt::get(v);
(void)ctx_switch;
bool const user_complete = Mmio::GT_0_INTERRUPT_IIR::Cs_mi_user_interrupt::get(v);
if (v) { _clear_rcs_iir(v); }
Vgpu *notify_gpu = nullptr;
if (user_complete) {
notify_gpu = _current_vgpu();
if (notify_gpu)
notify_gpu->user_complete();
}
bool const fault_valid = _mmio->fault_regs_valid();
if (fault_valid) { Genode::error("FAULT_REG valid"); }
_mmio->update_context_status_pointer();
if (user_complete) {
_unschedule_current_vgpu();
_active_vgpu = nullptr;
if (notify_gpu) {
if (!_notify_complete(notify_gpu))
_vgpu_list.enqueue(*notify_gpu);
}
/* keep the ball rolling... */
if (_current_vgpu()) {
_schedule_current_vgpu();
}
}
_mmio->enable_master_irq();
_irq->ack_irq();
}
Genode::Signal_handler<Device> _irq_dispatcher {
_env.ep(), *this, &Device::_handle_irq };
/************
** FENCES **
@ -1033,19 +895,14 @@ struct Igd::Device
*/
Device(Genode::Env &env,
Genode::Allocator &alloc,
Platform::Connection &pci,
Platform::Device_capability cap,
Genode::Xml_node config)
Resources &resources)
:
_env(env), _md_alloc(alloc), _pci(pci), _pci_cap(cap)
_env(env), _md_alloc(alloc), _resources(resources)
{
using namespace Genode;
if (!_supported()) { throw Unsupported_device(); }
/* trigger device_pd assignment */
_enable_pci_bus_master();
/*
* IHD-OS-BDW-Vol 2c-11.15 p. 1068
*/
@ -1058,7 +915,7 @@ struct Igd::Device
struct Ggc_lock : Bitfield<0, 1> { };
};
enum { PCI_GMCH_CTL = 0x50, };
MGGC_0_2_0_PCI::access_t v = _config_read<uint16_t>(PCI_GMCH_CTL);
MGGC_0_2_0_PCI::access_t v = _resources.config_read<uint16_t>(PCI_GMCH_CTL);
{
log("MGGC_0_2_0_PCI");
@ -1070,32 +927,26 @@ struct Igd::Device
}
/* map PCI resources */
_poke_pci_resource(GMADR);
addr_t gttmmadr_base = _map_pci_resource(GTTMMADR);
addr_t gttmmadr_base = _resources.map_gttmmadr();
_mmio.construct(_delayer, gttmmadr_base);
/* GGTT */
Number_of_bytes const fb_size =
config.attribute_value("fb_size", 32u<<20);
log("Reserve beginning ", fb_size, " in GGTT for framebuffer");
Ram_dataspace_capability scratch_page_ds = _pci_backend_alloc.alloc(PAGE_SIZE);
addr_t const scratch_page = Dataspace_client(scratch_page_ds).phys_addr();
/* reserverd size for framebuffer */
size_t const aperture_reserved = resources.gmadr_platform_size();
size_t const ggtt_size = (1u << MGGC_0_2_0_PCI::Gtt_graphics_memory_size::get(v)) << 20;
addr_t const ggtt_base = gttmmadr_base + (_res_size[GTTMMADR] / 2);
size_t const gmadr_size = _res_size[GMADR];
_ggtt.construct(*_mmio, ggtt_base, ggtt_size, gmadr_size, scratch_page, fb_size);
addr_t const ggtt_base = gttmmadr_base + (_resources.gttmmadr_size() / 2);
size_t const gmadr_size = _resources.gmadr_size();
_ggtt.construct(*_mmio, ggtt_base, ggtt_size, gmadr_size, scratch_page, aperture_reserved);
_ggtt->dump();
_vgpu_avail = (gmadr_size - fb_size) / Vgpu::APERTURE_SIZE;
_vgpu_avail = (gmadr_size - aperture_reserved) / Vgpu::APERTURE_SIZE;
_device_reset_and_init();
_irq.construct(_device.irq(0));
_irq->sigh(_irq_dispatcher);
_irq->ack_irq();
_mmio->dump();
_mmio->context_status_pointer_dump();
@ -1257,9 +1108,8 @@ struct Igd::Device
uint32_t const id = _mmio->find_free_fence();
if (id == INVALID_FENCE) {
Genode::warning("could not find free FENCE");
return false;
return id;
}
addr_t const lower = start * PAGE_SIZE;
addr_t const upper = lower + size;
uint32_t const pitch = ((mode & 0xffff0000) >> 16) / 128 - 1;
@ -1278,6 +1128,56 @@ struct Igd::Device
_clear_fence(id);
}
unsigned handle_irq()
{
Mmio::MASTER_INT_CTL::access_t master = _mmio->read<Mmio::MASTER_INT_CTL>();
/* handle render interrupts only */
if (Mmio::MASTER_INT_CTL::Render_interrupts_pending::get(master) == 0)
return master;
_mmio->disable_master_irq();
Mmio::GT_0_INTERRUPT_IIR::access_t const v = _mmio->read<Mmio::GT_0_INTERRUPT_IIR>();
bool const ctx_switch = Mmio::GT_0_INTERRUPT_IIR::Cs_ctx_switch_interrupt::get(v);
(void)ctx_switch;
bool const user_complete = Mmio::GT_0_INTERRUPT_IIR::Cs_mi_user_interrupt::get(v);
if (v) { _clear_rcs_iir(v); }
Vgpu *notify_gpu = nullptr;
if (user_complete) {
notify_gpu = _current_vgpu();
if (notify_gpu)
notify_gpu->user_complete();
}
bool const fault_valid = _mmio->fault_regs_valid();
if (fault_valid) { Genode::error("FAULT_REG valid"); }
_mmio->update_context_status_pointer();
if (user_complete) {
_unschedule_current_vgpu();
_active_vgpu = nullptr;
if (notify_gpu) {
if (!_notify_complete(notify_gpu))
_vgpu_list.enqueue(*notify_gpu);
}
/* keep the ball rolling... */
if (_current_vgpu()) {
_schedule_current_vgpu();
}
}
return master;
}
void enable_master_irq() { _mmio->enable_master_irq(); }
private:
/*
@ -1630,9 +1530,10 @@ class Gpu::Session_component : public Genode::Session_object<Gpu::Session>
Igd::size_t const size = Genode::Dataspace_client(b->cap).size();
Genode::uint32_t const fenced = _device.set_tiling(b->map.offset, size, mode);
b->fenced = fenced;
if (fenced != Buffer::INVALID_FENCE) { _vgpu.active_fences++; }
return fenced;
return fenced != Buffer::INVALID_FENCE;
}
};
@ -1715,97 +1616,6 @@ struct Main
{
Genode::Env &_env;
/*********
** Pci **
*********/
Platform::Connection _pci;
Platform::Device_capability _pci_cap { };
Platform::Device_capability _find_gpu_device()
{
using namespace Platform;
auto _scan_pci = [&] (Platform::Connection &pci,
Device_capability const &prev) {
Device_capability cap = Genode::retry<Platform::Out_of_ram>(
[&] () { return pci.next_device(prev, 0, 0); },
[&] () { pci.upgrade_ram(4096); }, 8);
if (prev.valid()) { pci.release_device(prev); }
return cap;
};
Device_capability cap;
while ((cap = _scan_pci(_pci, cap)).valid()) {
Device_client device(cap);
enum { BDW_DEVICE_ID = 0x1600, };
if ((device.class_code() >> 8) == 0x0300
&& (device.device_id() & 0xff00) == BDW_DEVICE_ID) {
return cap;
}
}
return Device_capability();
}
Platform::Device_capability _find_bridge()
{
using namespace Platform;
auto _scan_pci = [&] (Platform::Connection &pci,
Device_capability const &prev) {
Device_capability cap;
cap = Genode::retry<Platform::Out_of_ram>(
[&] () { return pci.next_device(prev, 0, 0); },
[&] () { pci.upgrade_ram(4096); }, 8);
if (prev.valid()) { pci.release_device(prev); }
return cap;
};
Device_capability cap;
unsigned char bus = 0xff, dev = 0xff, func = 0xff;
while ((cap = _scan_pci(_pci, cap)).valid()) {
Device_client device(cap);
device.bus_address(&bus, &dev, &func);
if (bus == 0 && dev == 0 && func == 0) {
return cap;
}
}
return Device_capability();
}
bool _mch_enabled()
{
using namespace Platform;
Device_capability cap = _find_bridge();
if (!cap.valid()) { return false; }
Device_client device(cap);
/*
* 5th Gen Core Processor datasheet vol 2 p. 48
*/
enum { MCHBAR_OFFSET = 0x48, };
struct MCHBAR : Genode::Register<64>
{
struct Mchbaren : Bitfield<0, 1> { };
};
MCHBAR::access_t const v = device.config_read(MCHBAR_OFFSET,
Device::ACCESS_32BIT);
return MCHBAR::Mchbaren::get(v);
}
/*********
** Gpu **
*********/
@ -1813,28 +1623,31 @@ struct Main
Genode::Sliced_heap _root_heap { _env.ram(), _env.rm() };
Gpu::Root _gpu_root { _env, _root_heap };
Genode::Attached_rom_dataspace _config_rom { _env, "config" };
Genode::Heap _device_md_alloc;
Genode::Constructible<Igd::Device> _device { };
Igd::Resources _gpu_resources { _env, *this, &Main::ack_irq };
Genode::Irq_session_client _irq { _gpu_resources.gpu_client().irq(0) };
Genode::Signal_handler<Main> _irq_dispatcher {
_env.ep(), *this, &Main::handle_irq };
Constructible<Platform::Root> _platform_root { };
Main(Genode::Env &env)
:
_env(env), _pci(env), _device_md_alloc(_env.ram(), _env.rm())
_env(env), _device_md_alloc(_env.ram(), _env.rm())
{
/* initial donation for device pd */
_pci.upgrade_ram(1024*1024);
/* IRQ */
_irq.sigh(_irq_dispatcher);
_irq.ack_irq();
_pci_cap = _find_gpu_device();
if (!_pci_cap.valid() || !_mch_enabled()) {
throw Igd::Device::Initialization_failed();
}
/* platform service */
_platform_root.construct(_env, _device_md_alloc, _gpu_resources);
/* GPU */
try {
_device.construct(_env, _device_md_alloc, _pci, _pci_cap,
_config_rom.xml());
_device.construct(_env, _device_md_alloc, _gpu_resources);
} catch (...) {
_env.parent().exit(1);
return;
}
@ -1842,7 +1655,37 @@ struct Main
_env.parent().announce(_env.ep().manage(_gpu_root));
}
~Main() { _pci.release_device(_pci_cap); }
void handle_irq()
{
unsigned master = 0;
if (_device.constructed())
master = _device->handle_irq();
/* GPU not present forward all IRQs to platform client */
else {
_platform_root->handle_irq();
return;
}
/*
* GPU present check for display engine related IRQs before calling platform
* client
*/
using Master = Igd::Mmio::MASTER_INT_CTL;
if (Master::De_interrupts_pending::get(master) &&
(_platform_root->handle_irq()))
return;
ack_irq();
}
void ack_irq()
{
if (_device.constructed()) {
_device->enable_master_irq();
}
_irq.ack_irq();
}
};

View File

@ -75,16 +75,20 @@ class Igd::Mmio : public Genode::Mmio
struct Audio_codec_interrupts_pending : Bitfield<24, 1> { };
struct De_pch_interrupts_pending : Bitfield<23, 1> { };
struct De_misc_interrupts_pending : Bitfield<22, 1> { };
struct De_pch_misc : Bitfield<22, 2> { };
struct De_port_interrupts_pending : Bitfield<20, 1> { };
struct De_pipe_c_interrupts_pending : Bitfield<18, 1> { };
struct De_pipe_b_interrupts_pending : Bitfield<17, 1> { };
struct De_pipe_a_interrupts_pending : Bitfield<16, 1> { };
struct De_pipe : Bitfield<16, 3> { };
struct Vebox_interrupts_pending : Bitfield< 6, 1> { };
struct Gtpm_interrupts_pending : Bitfield< 4, 1> { };
struct Vcs2_interrupts_pending : Bitfield< 3, 1> { };
struct Vcs1_interrupts_pending : Bitfield< 2, 1> { };
struct Blitter_interrupts_pending : Bitfield< 1, 1> { };
struct Render_interrupts_pending : Bitfield< 0, 1> { };
struct De_interrupts_pending :
Genode::Bitset_3<De_pipe, De_port_interrupts_pending, De_pch_misc> { };
};
/*
@ -1213,7 +1217,7 @@ class Igd::Mmio : public Genode::Mmio
{
RCS_RING_CONTEXT_STATUS_PTR::access_t const wp = read<RCS_RING_CONTEXT_STATUS_PTR::Write_pointer>();
if (wp > 0x05) {
Genode::warning("ring context status write-pointer invalid");
Genode::warning("ring context status write-pointer invalid", Genode::Hex(wp));
return;
}

View File

@ -0,0 +1,315 @@
/*
* \brief Platform service implementation
* \author Sebastian Sumpf
* \author Josef Soentgen
* \date 2021-07-16
*/
/*
* Copyright (C) 2021 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.
*/
#include <platform_session/platform_session.h>
namespace Platform {
class Device_component;
class Session_component;
class Io_mem_session_component;
class Irq_session_component;
class Root;
}
class Platform::Irq_session_component : public Rpc_object<Irq_session>
{
private:
Igd::Resources &_resources;
Signal_context_capability _sigh { };
public:
Irq_session_component(Igd::Resources &resources)
:
_resources(resources)
{ }
void ack_irq() override
{
_resources.ack_irq();
}
void sigh(Signal_context_capability sigh) override
{
_sigh = sigh;
}
bool handle_irq()
{
if (!_sigh.valid()) return false;
Signal_transmitter(_sigh).submit();
return true;
}
Info info() override
{
return Info { Info::INVALID, 0, 0 };
}
};
class Platform::Io_mem_session_component : public Rpc_object<Io_mem_session>
{
private:
Igd::Resources &_resources;
public:
Io_mem_session_component(Igd::Resources &resources)
:
_resources(resources) { }
Io_mem_dataspace_capability dataspace() override
{
return _resources.gttmmadr_platform_ds();
}
};
class Platform::Device_component : public Rpc_object<Device>
{
private:
Env &_env;
Igd::Resources &_resources;
Device_client &_device { _resources.gpu_client() };
Io_mem_session_component _gttmmadr_io { _resources };
Constructible<Io_mem_connection> _gmadr_io { };
Irq_session_component _irq { _resources };
public:
Device_component(Env &env, Igd::Resources &resources)
:
_env(env), _resources(resources)
{
_env.ep().rpc_ep().manage(&_gttmmadr_io);
_env.ep().rpc_ep().manage(&_irq);
}
~Device_component()
{
_env.ep().rpc_ep().dissolve(&_gttmmadr_io);
_env.ep().rpc_ep().dissolve(&_irq);
}
Irq_session_capability irq(uint8_t) override
{
return _irq.cap();
}
Io_mem_session_capability io_mem(uint8_t v_id, Cache caching,
addr_t /* offset */,
size_t /* size */) override
{
if (v_id == 0)
return _gttmmadr_io.cap();
if (v_id == 1) {
if (!_gmadr_io.constructed()) {
bool write_combined = (caching == WRITE_COMBINED) ? true : false;
_gmadr_io.construct(_env, _resources.gmadr_base(),
_resources.gmadr_platform_size(), write_combined);
}
return _gmadr_io->cap();
}
return Io_mem_session_capability();
}
void bus_address(unsigned char *bus, unsigned char *dev,
unsigned char *fn) override
{
_device.bus_address(bus, dev, fn);
}
unsigned short vendor_id() override
{
return _device.vendor_id();
}
unsigned short device_id() override
{
return _device.device_id();
}
unsigned class_code() override
{
return _device.class_code();
}
Resource resource(int resource_id) override
{
/* bar 0 is io mem/gtt */
if (resource_id == 0)
return Resource(_resources.gttmmadr_base(), _resources.gttmmadr_size());
/* bar 2 is GMADR (i.e., aperture) */
if(resource_id == 2)
return Resource(_resources.gmadr_base(), _resources.gmadr_platform_size());
return Resource();
}
unsigned config_read(unsigned char address, Access_size size) override
{
return _device.config_read(address, size);
}
void config_write(unsigned char /* address */, unsigned /* value */,
Access_size/* size */) override
{
}
Io_port_session_capability io_port(uint8_t /* id */) override
{
Genode::error(__func__, " is not supported");
return Io_port_session_capability();
}
bool handle_irq() { return _irq.handle_irq(); }
};
class Platform::Session_component : public Rpc_object<Session>
{
private:
Env &_env;
Device_component _device_component;
Connection &_platform;
Device_capability _bridge;
public:
Session_component(Env &env, Igd::Resources &resources)
:
_env(env),
_device_component(env, resources),
_platform(resources.platform()),
_bridge(resources.host_bridge_cap())
{
_env.ep().rpc_ep().manage(&_device_component);
}
~Session_component()
{
_env.ep().rpc_ep().dissolve(&_device_component);
}
Device_capability first_device(unsigned device_class, unsigned class_mask) override
{
enum { ISA_BRIDGE = 0x601u << 8 };
if (device_class == ISA_BRIDGE)
return _platform.first_device(device_class, class_mask);
return _bridge;
}
Device_capability next_device(Device_capability prev_device,
unsigned, unsigned) override
{
if (prev_device == _bridge)
return _device_component.cap();
return Device_capability();
}
void release_device(Device_capability device) override
{
if (device.valid() == false) return;
if (_device_component.cap() == device || device == _bridge) {
return;
}
_platform.release_device(device);
}
Device_capability device(Device_name const & /* string */) override
{
Genode::error(__func__, " is not supported");
return Device_capability();
}
Ram_dataspace_capability alloc_dma_buffer(size_t size, Cache cache) override
{
return _platform.alloc_dma_buffer(size, cache);
}
void free_dma_buffer(Ram_dataspace_capability cap) override
{
_platform.free_dma_buffer(cap);
}
addr_t dma_addr(Ram_dataspace_capability cap) override
{
return _platform.dma_addr(cap);
}
bool handle_irq() { return _device_component.handle_irq(); }
};
class Platform::Root : public Root_component<Session_component, Genode::Single_client>
{
private:
Env &_env;
Constructible<Session_component> _session { };
Igd::Resources &_resources;
public:
Root(Env &env, Allocator &md_alloc, Igd::Resources &resources)
:
Root_component<Session_component, Genode::Single_client>(&env.ep().rpc_ep(), &md_alloc),
_env(env), _resources(resources)
{
env.parent().announce(env.ep().manage(*this));
}
Session_component *_create_session(char const * /* args */) override
{
_session.construct(_env, _resources);
return &*_session;
}
void _upgrade_session(Session_component *, const char *args) override
{
if (!_session.constructed()) return;
_resources.platform().upgrade({ ram_quota_from_args(args),
cap_quota_from_args(args) });
}
void _destroy_session(Session_component *) override
{
if (_session.constructed())
_session.destruct();
}
bool handle_irq()
{
if (_session.constructed())
return _session->handle_irq();
return false;
}
};

View File

@ -0,0 +1,280 @@
/*
* \brief GPU resource handling
* \author Sebastian Sumpf
* \author Josef Soentgen
* \date 2021-07-16
*/
/*
* Copyright (C) 2021 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.
*/
#include <region_map/client.h>
#include <rm_session/connection.h>
namespace Igd {
class Resources;
}
struct Main;
class Igd::Resources : Genode::Noncopyable
{
public:
struct Initialization_failed : Genode::Exception { };
private:
using Io_mem_connection = Genode::Io_mem_connection;
using Io_mem_dataspace_capability = Genode::Io_mem_dataspace_capability;
Genode::Env &_env;
/* irq callback */
Main &_obj;
void (Main::*_ack_irq) ();
/* platform session */
Platform::Connection _platform { _env };
Platform::Device_capability _gpu_cap { };
Platform::Device_capability _host_bridge_cap { };
Genode::Constructible<Platform::Device_client> _gpu_client { };
/* mmio + gtt */
Platform::Device::Resource _gttmmadr { };
Io_mem_dataspace_capability _gttmmadr_ds { };
Genode::Constructible<Io_mem_connection> _gttmmadr_io { };
/* aperture */
Platform::Device::Resource _gmadr { };
enum {
/* reserved aperture for platform service */
APERTURE_RESERVED = 64u<<20,
/* reserved GTT for platform service, GTT entry is 8 byte */
GTT_RESERVED = (APERTURE_RESERVED/PAGE_SIZE) * 8,
};
/* managed dataspace for local platform service */
Genode::Rm_connection _rm_connection { _env };
Genode::Constructible<Genode::Region_map_client> _gttmmadr_rm { };
/**********
** Mmio **
**********/
void _create_gttmmadr_rm()
{
using off_t = Genode::off_t;
_gttmmadr_rm.construct(_rm_connection.create((gttmmadr_size())));
/* GTT starts at half of the mmio memory, assumed size is 8 MB */
off_t const gtt_offset = gttmmadr_size() / 2;
size_t const gtt_size = 8ul << 20;
/* attach actual iomem + reserved = 8 MB */
_gttmmadr_rm->attach_at(_gttmmadr_ds, 0, gtt_offset);
/* attach beginning of GTT */
_gttmmadr_rm->attach_at(_gttmmadr_ds, gtt_offset, GTT_RESERVED, gtt_offset);
/* attach the rest of the GTT as dummy RAM */
Genode::Ram_dataspace_capability dummmy_gtt_ds { _env.ram().alloc(PAGE_SIZE) };
size_t remainder = gtt_size - GTT_RESERVED;
for (off_t offset = gtt_offset + GTT_RESERVED;
remainder > 0;
offset += PAGE_SIZE, remainder -= PAGE_SIZE) {
_rm_connection.retry_with_upgrade(Genode::Ram_quota{4096},
Genode::Cap_quota{8}, [&]() {
_gttmmadr_rm->attach_at(dummmy_gtt_ds, offset, PAGE_SIZE); });
}
}
/*********
** Pci **
*********/
void _find_devices()
{
using namespace Platform;
auto _scan_pci = [&] (Platform::Connection &pci,
Device_capability const &prev,
bool release) {
Device_capability cap = pci.with_upgrade([&]() {
return pci.next_device(prev, 0, 0); });
if (prev.valid() && release) { pci.release_device(prev); }
return cap;
};
Device_capability cap;
bool release = false;
while ((cap = _scan_pci(_platform, cap, release)).valid()) {
Device_client device(cap);
unsigned char bus = 0xff, dev = 0xff, func = 0xff;
device.bus_address(&bus, &dev, &func);
/* host pci bridge */
if (bus == 0 && dev == 0 && func == 0) {
_host_bridge_cap = cap;
release = false;
continue;
}
/* gpu */
if ((device.class_code() >> 8) == 0x0300) {
_gpu_cap = cap;
release = false;
continue;
}
release = true;
}
}
bool _mch_enabled()
{
using namespace Platform;
if (!_host_bridge_cap.valid()) { return false; }
Device_client device(_host_bridge_cap);
/*
* 5th Gen Core Processor datasheet vol 2 p. 48
*/
enum { MCHBAR_OFFSET = 0x48, };
struct MCHBAR : Genode::Register<64>
{
struct Mchbaren : Bitfield<0, 1> { };
};
MCHBAR::access_t const v = device.config_read(MCHBAR_OFFSET,
Platform::Device::ACCESS_32BIT);
return MCHBAR::Mchbaren::get(v);
}
template <typename T>
static Platform::Device::Access_size _access_size(T)
{
switch (sizeof(T)) {
case 1: return Platform::Device::ACCESS_8BIT;
case 2: return Platform::Device::ACCESS_16BIT;
default: return Platform::Device::ACCESS_32BIT;
}
}
void _enable_pci_bus_master()
{
enum {
PCI_CMD_REG = 4,
PCI_BUS_MASTER = 1<<2,
};
uint16_t cmd = config_read<uint16_t>(PCI_CMD_REG);
cmd |= PCI_BUS_MASTER;
config_write(PCI_CMD_REG, cmd);
}
public:
Resources(Genode::Env &env, Main &obj, void (Main::*ack_irq) ())
:
_env(env), _obj(obj), _ack_irq(ack_irq)
{
/* initial donation for device pd */
_platform.upgrade_ram(1024*1024);
_find_devices();
if (!_gpu_cap.valid() || !_mch_enabled()) {
throw Initialization_failed();
}
_gpu_client.construct(_gpu_cap);
_gttmmadr = _gpu_client->resource(0);
_gmadr = _gpu_client->resource(2);
_gttmmadr_io.construct(_env, _gttmmadr.base(), _gttmmadr.size());
_gttmmadr_ds = _gttmmadr_io->dataspace();
_enable_pci_bus_master();
Genode::log("Reserved beginning ",
Genode::Number_of_bytes(APERTURE_RESERVED),
" of aperture for platform service");
}
~Resources()
{
_platform.release_device(_gpu_cap);
_platform.release_device(_host_bridge_cap);
}
addr_t map_gttmmadr()
{
if (!_gttmmadr_ds.valid())
throw Initialization_failed();
addr_t addr = (addr_t)(_env.rm().attach(_gttmmadr_ds, _gttmmadr.size()));
log("Map res:", 0,
" base:", Genode::Hex(_gttmmadr.base()),
" size:", Genode::Hex(_gttmmadr.size()),
" vaddr:", Genode::Hex(addr));
return addr;
}
template <typename T>
T config_read(unsigned int const devfn)
{
T val = 0;
_platform.with_upgrade([&] () {
val = _gpu_client->config_read(devfn, _access_size(val));
});
return val;
}
template <typename T>
void config_write(unsigned int const devfn, T val)
{
_platform.with_upgrade([&] () {
_gpu_client->config_write(devfn, val, _access_size(val));
});
}
void ack_irq() { (_obj.*_ack_irq)(); }
Platform::Connection &platform() { return _platform; }
Platform::Device_client &gpu_client() { return *_gpu_client; }
Platform::Device_capability host_bridge_cap() { return _host_bridge_cap; }
addr_t gmadr_base() const { return _gmadr.base(); }
size_t gmadr_size() const { return _gmadr.size(); }
addr_t gttmmadr_base() const { return _gttmmadr.base(); }
addr_t gttmmadr_size() const { return _gttmmadr.size(); }
size_t gmadr_platform_size() const { return APERTURE_RESERVED; }
size_t gttmmadr_platform_size() const { return GTT_RESERVED; }
Io_mem_dataspace_capability gttmmadr_platform_ds()
{
using namespace Genode;
if (!_gttmmadr_rm.constructed())
_create_gttmmadr_rm();
return static_cap_cast<Io_mem_dataspace>(_gttmmadr_rm->dataspace());
}
};