diff --git a/repos/os/src/drivers/gpu/intel/main.cc b/repos/os/src/drivers/gpu/intel/main.cc index 19b5c0cc6f..77e3afd9d5 100644 --- a/repos/os/src/drivers/gpu/intel/main.cc +++ b/repos/os/src/drivers/gpu/intel/main.cc @@ -40,6 +40,8 @@ #include #include #include +#include +#include #include @@ -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 - 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 - void _retry_func(FUNC const &func) - { - Genode::size_t donate = PAGE_SIZE; - Genode::retry( - func, - [&] () { - _pci.upgrade_ram(donate); - donate *= 2; - }, 2); - } - - template - T _config_read(unsigned int const devfn) - { - T val = 0; - _retry_func([&] () { - val = _device.config_read(devfn, _access_size(val)); - }); - - return val; - } - - template - 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 _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 _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(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 *mem = new (&alloc) Genode::Registered(_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(); - - 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 _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(PCI_GMCH_CTL); + MGGC_0_2_0_PCI::access_t v = _resources.config_read(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(); + + /* 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(); + + 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 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( - [&] () { 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( - [&] () { 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 _device { }; + Igd::Resources _gpu_resources { _env, *this, &Main::ack_irq }; + + Genode::Irq_session_client _irq { _gpu_resources.gpu_client().irq(0) }; + Genode::Signal_handler
_irq_dispatcher { + _env.ep(), *this, &Main::handle_irq }; + + Constructible _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(); + } }; diff --git a/repos/os/src/drivers/gpu/intel/mmio.h b/repos/os/src/drivers/gpu/intel/mmio.h index 7635d8a041..3f52cc8e47 100644 --- a/repos/os/src/drivers/gpu/intel/mmio.h +++ b/repos/os/src/drivers/gpu/intel/mmio.h @@ -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 { }; }; /* @@ -1213,7 +1217,7 @@ class Igd::Mmio : public Genode::Mmio { RCS_RING_CONTEXT_STATUS_PTR::access_t const wp = read(); if (wp > 0x05) { - Genode::warning("ring context status write-pointer invalid"); + Genode::warning("ring context status write-pointer invalid", Genode::Hex(wp)); return; } diff --git a/repos/os/src/drivers/gpu/intel/platform_session.h b/repos/os/src/drivers/gpu/intel/platform_session.h new file mode 100644 index 0000000000..ec98b2b90c --- /dev/null +++ b/repos/os/src/drivers/gpu/intel/platform_session.h @@ -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 + +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 +{ + 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 +{ + 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 +{ + private: + + Env &_env; + Igd::Resources &_resources; + Device_client &_device { _resources.gpu_client() }; + + Io_mem_session_component _gttmmadr_io { _resources }; + Constructible _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 +{ + 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 +{ + private: + + Env &_env; + Constructible _session { }; + Igd::Resources &_resources; + + public: + + Root(Env &env, Allocator &md_alloc, Igd::Resources &resources) + : + Root_component(&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; + } +}; diff --git a/repos/os/src/drivers/gpu/intel/resources.h b/repos/os/src/drivers/gpu/intel/resources.h new file mode 100644 index 0000000000..cd34f8f2bb --- /dev/null +++ b/repos/os/src/drivers/gpu/intel/resources.h @@ -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 +#include + +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 _gpu_client { }; + + /* mmio + gtt */ + Platform::Device::Resource _gttmmadr { }; + Io_mem_dataspace_capability _gttmmadr_ds { }; + Genode::Constructible _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 _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 + 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(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 + 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 + 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(_gttmmadr_rm->dataspace()); + } +}; +