diff --git a/repos/os/include/gpu_session/capability.h b/repos/os/include/gpu_session/capability.h
new file mode 100644
index 000000000..a4ebe0a1f
--- /dev/null
+++ b/repos/os/include/gpu_session/capability.h
@@ -0,0 +1,22 @@
+/*
+ * \brief GPU session capability type
+ * \author Josef Soentgen
+ * \date 2017-04-28
+ */
+
+/*
+ * Copyright (C) 2017 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 _INCLUDE__GPU_SESSION__CAPABILITY_H_
+#define _INCLUDE__GPU_SESSION__CAPABILITY_H_
+
+#include
+#include
+
+namespace Gpu { typedef Genode::Capability Session_capability; }
+
+#endif /* _INCLUDE__GPU_SESSION__CAPABILITY_H_ */
diff --git a/repos/os/include/gpu_session/client.h b/repos/os/include/gpu_session/client.h
new file mode 100644
index 000000000..7fbaec96b
--- /dev/null
+++ b/repos/os/include/gpu_session/client.h
@@ -0,0 +1,73 @@
+/*
+ * \brief Client-side Gpu session interface
+ * \author Josef Soentgen
+ * \date 2017-04-28
+ */
+
+/*
+ * Copyright (C) 2017 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 _INCLUDE__GPU_SESSION__CLIENT_H_
+#define _INCLUDE__GPU_SESSION__CLIENT_H_
+
+/* Genode includes */
+#include
+#include
+
+namespace Gpu { class Session_client; }
+
+
+class Gpu::Session_client : public Genode::Rpc_client
+{
+ public:
+
+ /**
+ * Constructor
+ *
+ * \param session session capability
+ */
+ Session_client(Session_capability session)
+ : Genode::Rpc_client(session) { }
+
+ /***********************
+ ** Session interface **
+ ***********************/
+
+ Info info() const override {
+ return call(); }
+
+ void exec_buffer(Genode::Dataspace_capability cap, Genode::size_t size) override {
+ call(cap, size); }
+
+ void completion_sigh(Genode::Signal_context_capability sigh) override {
+ call(sigh); }
+
+ Genode::Dataspace_capability alloc_buffer(Genode::size_t size) override {
+ return call(size); }
+
+ void free_buffer(Genode::Dataspace_capability ds) override {
+ call(ds); }
+
+ Genode::Dataspace_capability map_buffer(Genode::Dataspace_capability ds,
+ bool aperture) override {
+ return call(ds, aperture); }
+
+ void unmap_buffer(Genode::Dataspace_capability ds) override {
+ call(ds); }
+
+ bool map_buffer_ppgtt(Genode::Dataspace_capability ds,
+ Gpu::addr_t va) override {
+ return call(ds, va); }
+
+ void unmap_buffer_ppgtt(Genode::Dataspace_capability ds, Gpu::addr_t va) override {
+ call(ds, va); }
+
+ bool set_tiling(Genode::Dataspace_capability ds, unsigned mode) override {
+ return call(ds, mode); }
+};
+
+#endif /* _INCLUDE__GPU_SESSION__CLIENT_H_ */
diff --git a/repos/os/include/gpu_session/connection.h b/repos/os/include/gpu_session/connection.h
new file mode 100644
index 000000000..425c44dac
--- /dev/null
+++ b/repos/os/include/gpu_session/connection.h
@@ -0,0 +1,51 @@
+/*
+ * \brief Connection to Gpu service
+ * \author Josef Soentgen
+ * \date 2017-04-28
+ */
+
+/*
+ * Copyright (C) 2017 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 _INCLUDE__GPU_SESSION__CONNECTION_H_
+#define _INCLUDE__GPU_SESSION__CONNECTION_H_
+
+#include
+#include
+#include
+
+namespace Gpu { struct Connection; }
+
+struct Gpu::Connection : Genode::Connection, Session_client
+{
+ /**
+ * Issue session request
+ *
+ * \noapi
+ */
+ Capability _session(Genode::Parent &parent,
+ char const *label, Genode::size_t quota)
+ {
+ return session(parent, "ram_quota=%ld, label=\"%s\"", quota, label);
+ }
+
+ /**
+ * Constructor
+ *
+ * \param quota initial amount of quota used for allocating Gpu
+ * memory
+ */
+ Connection(Genode::Env &env,
+ Genode::size_t quota = Session::REQUIRED_QUOTA,
+ const char *label = "")
+ :
+ Genode::Connection(env, _session(env.parent(), label, quota)),
+ Session_client(cap())
+ { }
+};
+
+#endif /* _INCLUDE__GPU_SESSION__CONNECTION_H_ */
diff --git a/repos/os/include/gpu_session/gpu_session.h b/repos/os/include/gpu_session/gpu_session.h
new file mode 100644
index 000000000..e4be24e55
--- /dev/null
+++ b/repos/os/include/gpu_session/gpu_session.h
@@ -0,0 +1,184 @@
+/*
+ * \brief Gpu session interface.
+ * \author Josef Soentgen
+ * \date 2017-04-28
+ */
+
+/*
+ * Copyright (C) 2017 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 _INCLUDE__GPU_SESSION__GPU_SESSION_H_
+#define _INCLUDE__GPU_SESSION__GPU_SESSION_H_
+
+#include
+
+namespace Gpu {
+
+ using addr_t = Genode::uint64_t;
+
+ struct Info;
+ struct Session;
+}
+
+
+/*
+ * Gpu information
+ *
+ * Used to query information in the DRM backend
+ */
+struct Gpu::Info
+{
+ using Chip_id = Genode::uint16_t;
+ using Features = Genode::uint32_t;
+ using size_t = Genode::size_t;
+ using Context_id = Genode::uint32_t;
+
+ Chip_id chip_id;
+ Features features;
+ size_t aperture_size;
+ Context_id ctx_id;
+
+ Info(Chip_id chip_id, Features features,
+ size_t aperture_size, Context_id ctx_id)
+ :
+ chip_id(chip_id), features(features),
+ aperture_size(aperture_size), ctx_id(ctx_id)
+ { }
+};
+
+
+/*
+ * Gpu session interface
+ */
+struct Gpu::Session : public Genode::Session
+{
+ struct Out_of_ram : Genode::Exception { };
+ struct Out_of_caps : Genode::Exception { };
+
+ enum { REQUIRED_QUOTA = 1024 * 1024, CAP_QUOTA = 8, };
+
+ static const char *service_name() { return "Gpu"; }
+
+ virtual ~Session() { }
+
+ /***********************
+ ** Session interface **
+ ***********************/
+
+ /**
+ * Query GPU information
+ */
+ virtual Info info() const = 0;
+
+ /**
+ * Execute commands from given buffer
+ *
+ * \param cap capability to buffer object containing the exec buffer
+ * \param size size of the batch buffer in bytes
+ */
+ virtual void exec_buffer(Genode::Dataspace_capability cap, Genode::size_t size) = 0;
+
+ /**
+ * Register completion signal handler
+ *
+ * \param sigh signal handler that is called when the execution
+ * has completed
+ */
+ virtual void completion_sigh(Genode::Signal_context_capability sigh) = 0;
+
+ /**
+ * Allocate buffer dataspace
+ *
+ * \param size size of buffer in bytes
+ *
+ * \throw Out_of_ram
+ * \throw Out_of_caps
+ */
+ virtual Genode::Dataspace_capability alloc_buffer(Genode::size_t size) = 0;
+
+ /**
+ * Free buffer dataspace
+ *
+ * \param ds dataspace capability for buffer
+ */
+ virtual void free_buffer(Genode::Dataspace_capability ds) = 0;
+
+ /**
+ * Map buffer
+ *
+ * \param ds dataspace capability for buffer
+ * \param aperture if true create CPU accessible mapping through
+ * GGTT window, otherwise create PPGTT mapping
+ */
+ virtual Genode::Dataspace_capability map_buffer(Genode::Dataspace_capability ds,
+ bool aperture) = 0;
+
+ /**
+ * Unmap buffer
+ *
+ * \param ds dataspace capability for buffer
+ */
+ virtual void unmap_buffer(Genode::Dataspace_capability ds) = 0;
+
+ /**
+ * Map buffer in PPGTT
+ *
+ * \param ds dataspace capability for buffer
+ * \param va virtual address
+ */
+ virtual bool map_buffer_ppgtt(Genode::Dataspace_capability ds,
+ Gpu::addr_t va) = 0;
+
+ /**
+ * Unmap buffer
+ *
+ * \param ds dataspace capability for buffer
+ */
+ virtual void unmap_buffer_ppgtt(Genode::Dataspace_capability ds, Gpu::addr_t) = 0;
+
+ /**
+ * Set tiling for buffer
+ *
+ * \param ds dataspace capability for buffer
+ * \param mode tiling mode
+ */
+ virtual bool set_tiling(Genode::Dataspace_capability ds, unsigned mode) = 0;
+
+ /*******************
+ ** RPC interface **
+ *******************/
+
+ GENODE_RPC(Rpc_info, Info, info);
+ GENODE_RPC(Rpc_exec_buffer, void, exec_buffer, Genode::Dataspace_capability,
+ Genode::size_t);
+ GENODE_RPC(Rpc_completion_sigh, void, completion_sigh,
+ Genode::Signal_context_capability);
+ GENODE_RPC_THROW(Rpc_alloc_buffer, Genode::Dataspace_capability, alloc_buffer,
+ GENODE_TYPE_LIST(Out_of_ram),
+ Genode::size_t);
+ GENODE_RPC(Rpc_free_buffer, void, free_buffer, Genode::Dataspace_capability);
+ GENODE_RPC_THROW(Rpc_map_buffer, Genode::Dataspace_capability, map_buffer,
+ GENODE_TYPE_LIST(Out_of_ram),
+ Genode::Dataspace_capability, bool);
+ GENODE_RPC(Rpc_unmap_buffer, void, unmap_buffer,
+ Genode::Dataspace_capability);
+ GENODE_RPC_THROW(Rpc_map_buffer_ppgtt, bool, map_buffer_ppgtt,
+ GENODE_TYPE_LIST(Out_of_ram),
+ Genode::Dataspace_capability, Gpu::addr_t);
+ GENODE_RPC(Rpc_unmap_buffer_ppgtt, void, unmap_buffer_ppgtt,
+ Genode::Dataspace_capability, Gpu::addr_t);
+ GENODE_RPC(Rpc_set_tiling, bool, set_tiling,
+ Genode::Dataspace_capability, unsigned);
+
+ GENODE_RPC_INTERFACE(Rpc_info, Rpc_exec_buffer,
+ Rpc_completion_sigh, Rpc_alloc_buffer,
+ Rpc_free_buffer, Rpc_map_buffer, Rpc_unmap_buffer,
+ Rpc_map_buffer_ppgtt, Rpc_unmap_buffer_ppgtt,
+ Rpc_set_tiling);
+};
+
+#endif /* _INCLUDE__GPU_SESSION__GPU_SESSION_H_ */
diff --git a/repos/os/src/drivers/gpu/intel/commands.h b/repos/os/src/drivers/gpu/intel/commands.h
new file mode 100644
index 000000000..e0e13c02a
--- /dev/null
+++ b/repos/os/src/drivers/gpu/intel/commands.h
@@ -0,0 +1,202 @@
+/*
+ * \brief Broadwell MI commands
+ * \author Josef Soentgen
+ * \date 2017-03-15
+ */
+
+/*
+ * Copyright (C) 2017 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 _COMMANDS_H_
+#define _COMMANDS_H_
+
+/* Genode includes */
+#include
+#include
+
+/* local includes */
+#include
+
+
+namespace Igd {
+
+ struct Cmd_header;
+ struct Mi_noop;
+ struct Mi_user_interrupt;
+ struct Mi_batch_buffer_start;
+ struct Pipe_control;
+
+ void cmd_dump(uint32_t cmd, uint32_t index = 0);
+}
+
+
+/*
+ * IHD-OS-BDW-Vol 6-11.15 p. 2
+ */
+struct Igd::Cmd_header : Genode::Register<32>
+{
+ struct Cmd_type : Bitfield<29, 3>
+ {
+ enum {
+ MI_COMMAND = 0b000,
+ MI_BCS = 0b010,
+ MI_RCS = 0b011,
+ };
+ };
+
+ struct Cmd_subtype : Bitfield<27, 2> { };
+ struct Cmd_opcode : Bitfield<24, 3> { };
+
+ /*
+ * Actually bit 23:x seems to be the sub-opcode but opcodes
+ * include bit 23 (see p. 5).
+ */
+ struct Mi_cmd_opcode : Bitfield<23, 6>
+ {
+ enum {
+ MI_NOOP = 0x00,
+ MI_USER_INTERRUPT = 0x02,
+ MI_WAIT_FOR_EVENT = 0x03,
+ MI_FLUSH = 0x04,
+ MI_REPORT_HEAD = 0x07,
+ MI_ARB_ON_OFF = 0x08,
+ MI_BATCH_BUFFER_END = 0x0A,
+ MI_SUSPEND_FLUSH = 0x0B,
+ MI_SET_APPID = 0x0E,
+ MI_OVERLAY_FLIP = 0x11,
+ MI_LOAD_SCAN_LINES_INCL = 0x12,
+ MI_DISPLAY_FLIP = 0x14,
+ MI_DISPLAY_FLIP_I915 = 0x14,
+ MI_SEMAPHORE_MBOX = 0x16,
+ MI_SET_CONTEXT = 0x18,
+ MI_SEMAPHORE_SIGNAL = 0x1b,
+ MI_SEMAPHORE_WAIT = 0x1c,
+ MI_STORE_DWORD_IMM = 0x20,
+ MI_STORE_DWORD_INDEX = 0x21,
+ MI_LOAD_REGISTER_IMM = 0x22,
+ MI_STORE_REGISTER_MEM = 0x24,
+ MI_FLUSH_DW = 0x26,
+ MI_LOAD_REGISTER_MEM = 0x29,
+ MI_BATCH_BUFFER = 0x30,
+ MI_BATCH_BUFFER_START = 0x31,
+ };
+ };
+
+ typename Cmd_header::access_t value;
+
+ Cmd_header() : value(0) { }
+
+ Cmd_header(Igd::uint32_t value) : value(value) { }
+};
+
+
+/*
+ * IHD-OS-BDW-Vol 2a-11.15 p. 870
+ */
+struct Igd::Mi_noop : Cmd_header
+{
+ Mi_noop()
+ {
+ Cmd_header::Cmd_type::set(Cmd_header::value,
+ Cmd_header::Cmd_type::MI_COMMAND);
+ Cmd_header::Mi_cmd_opcode::set(Cmd_header::value,
+ Cmd_header::Mi_cmd_opcode::MI_NOOP);
+ }
+};
+
+
+/*
+ * IHD-OS-BDW-Vol 2a-11.15 p. 948 ff.
+ */
+struct Igd::Mi_user_interrupt : Cmd_header
+{
+
+ Mi_user_interrupt()
+ {
+ Cmd_header::Cmd_type::set(Cmd_header::value,
+ Cmd_header::Cmd_type::MI_COMMAND);
+ Cmd_header::Mi_cmd_opcode::set(Cmd_header::value,
+ Cmd_header::Mi_cmd_opcode::MI_USER_INTERRUPT);
+ }
+};
+
+
+/*
+ * IHD-OS-BDW-Vol 2a-11.15 p. 793 ff.
+ */
+struct Igd::Mi_batch_buffer_start : Cmd_header
+{
+ struct Address_space_indicator : Bitfield<8, 1>
+ {
+ enum { GTT = 0b0, PPGTT = 0b1, };
+ };
+
+ struct Dword_length : Bitfield<0, 8> { };
+
+ Mi_batch_buffer_start()
+ {
+ Cmd_header::Cmd_type::set(Cmd_header::value,
+ Cmd_header::Cmd_type::MI_COMMAND);
+ Cmd_header::Mi_cmd_opcode::set(Cmd_header::value,
+ Cmd_header::Mi_cmd_opcode::MI_BATCH_BUFFER_START);
+ Address_space_indicator::set(Cmd_header::value, Address_space_indicator::PPGTT);
+
+ Dword_length::set(Cmd_header::value, 1);
+ }
+};
+
+
+/*
+ * IHD-OS-BDW-Vol 2a-11.15 p. 983 ff.
+ */
+struct Igd::Pipe_control : Cmd_header
+{
+ struct Dword_length : Bitfield<0, 8> { };
+
+ enum {
+ GFX_PIPE_LINE = 0b11,
+ PIPE_CONTROL = 0b10,
+ };
+
+ enum {
+ FLUSH_L3 = (1 << 27),
+ GLOBAL_GTT_IVB = (1 << 24),
+ MMIO_WRITE = (1 << 23),
+ STORE_DATA_INDEX = (1 << 21),
+ CS_STALL = (1 << 20),
+ TLB_INVALIDATE = (1 << 18),
+ MEDIA_STATE_CLEAR = (1 << 16),
+ QW_WRITE = (1 << 14),
+ POST_SYNC_OP_MASK = (3 << 14),
+ DEPTH_STALL = (1 << 13),
+ WRITE_FLUSH = (1 << 12),
+ RENDER_TARGET_CACHE_FLUSH = (1 << 12),
+ INSTRUCTION_CACHE_INVALIDATE = (1 << 11),
+ TEXTURE_CACHE_INVALIDATE = (1 << 10),
+ INDIRECT_STATE_DISABLE = (1 << 9),
+ NOTIFY = (1 << 8),
+ FLUSH_ENABLE = (1 << 7),
+ DC_FLUSH_ENABLE = (1 << 5),
+ VF_CACHE_INVALIDATE = (1 << 4),
+ CONST_CACHE_INVALIDATE = (1 << 3),
+ STATE_CACHE_INVALIDATE = (1 << 2),
+ STALL_AT_SCOREBOARD = (1 << 1),
+ DEPTH_CACHE_FLUSH = (1 << 0),
+ };
+
+ Pipe_control(Genode::uint8_t length)
+ {
+ Cmd_header::Cmd_type::set(Cmd_header::value,
+ Cmd_header::Cmd_type::MI_RCS);
+ Cmd_header::Cmd_subtype::set(Cmd_header::value, GFX_PIPE_LINE);
+ Cmd_header::Cmd_opcode::set(Cmd_header::value, PIPE_CONTROL);
+
+ Dword_length::set(Cmd_header::value, (length-2));
+ }
+};
+
+#endif /* _COMMANDS_H_ */
diff --git a/repos/os/src/drivers/gpu/intel/context.h b/repos/os/src/drivers/gpu/intel/context.h
new file mode 100644
index 000000000..01618f5fa
--- /dev/null
+++ b/repos/os/src/drivers/gpu/intel/context.h
@@ -0,0 +1,885 @@
+/*
+ * \brief Broadwell logical ring context
+ * \author Josef Soentgen
+ * \date 2017-03-15
+ */
+
+/*
+ * Copyright (C) 2017 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 _LOGICAL_RING_CONTEXT_H_
+#define _LOGICAL_RING_CONTEXT_H_
+
+/* Genode includes */
+#include
+#include
+
+/* local includes */
+#include
+#include
+
+
+namespace Igd {
+
+ struct Context_status_qword;
+
+ struct Common_context_regs;
+
+ struct Hardware_status_page;
+ template class Execlist_context;
+ template class Ppgtt_context;
+ class Engine_context;
+ class Ext_engine_context;
+ class Urb_atomic_context;
+
+ class Rcs_context;
+}
+
+
+/*
+ * IHD-OS-BDW-Vol 6-11.15 p. 8
+ * IHD-OS-BDW-Vol 2d-11.15 p. 111
+ */
+struct Igd::Context_status_qword : Genode::Register<64>
+{
+ struct Context_id : Bitfield<32, 32> { };
+ /* only valid if Preempted set bit */
+ struct Lite_restore : Bitfield<15, 1> { };
+ struct Display_plane : Bitfield<12, 3>
+ {
+ enum {
+ DISPLAY_PLANE_A = 0b000,
+ DISPLAY_PLANE_B = 0b001,
+ DISPLAY_PLANE_C = 0b010,
+ DISPLAY_PLANE_SPRITE_A = 0b011,
+ DISPLAY_PLANE_SPRITE_B = 0b100,
+ DISPLAY_PLANE_SPRITE_C = 0b101,
+ };
+ };
+ /* only valid if Wait_on_semaphore bit set */
+ struct Semaphore_wait_mode : Bitfield<11, 1>
+ {
+ enum {
+ SIGNAL_MODE = 0b00,
+ POLL_MODE = 0b01,
+ };
+ };
+ struct Wait_on_scanline : Bitfield< 8, 1> { };
+ struct Wait_on_semaphore : Bitfield< 7, 1> { };
+ struct Wait_on_v_blank : Bitfield< 6, 1> { };
+ struct Wait_on_sync_flip : Bitfield< 5, 1> { };
+ struct Context_complete : Bitfield< 4, 1> { };
+ struct Active_to_idle : Bitfield< 3, 1> { };
+ struct Element_switch : Bitfield< 2, 1> { };
+ struct Preempted : Bitfield< 1, 1> { };
+ struct Idle_to_active : Bitfield< 0, 1> { };
+};
+
+
+struct Igd::Common_context_regs : public Genode::Mmio
+{
+ template
+ struct Common_register : Register { };
+
+ template
+ struct Common_register_array : Register_array { };
+
+ addr_t _base;
+
+ Common_context_regs(addr_t base) : Genode::Mmio(base), _base(base) { }
+
+ addr_t base() const { return _base; }
+
+ template
+ void write_offset(typename T::access_t const value)
+ {
+ write(value + T::OFFSET);
+ }
+};
+
+
+/*
+ * IHD-OS-BDW-Vol 3-11.15 p. 18 (for VCS)
+ * IHD-OS-BDW-Vol 3-11.15 p. 20 (for BCS)
+ * IHD-OS-BDW-Vol 3-11.15 p. 22 (for VECS)
+ * IHD-OS-BDW-Vol 7-11.15 p. 27 (for RCS)
+ *
+ * All engines use the same layout until offset 0x118.
+ */
+template
+class Igd::Execlist_context : public Igd::Common_context_regs
+{
+ public:
+
+ /* MI_NOOP */
+ struct Noop_1 : Common_register<0x0000> { };
+
+ /*
+ * IHD-OS-BDW-Vol 2a-11.15 p. 841 ff.
+ */
+ struct Load_immediate_header : Common_register<0x0001> { };
+
+ /*
+ * XXX see i915 intel_lrc.h
+ */
+ struct Context_control_mmio : Common_register<0x0002>
+ {
+ enum { OFFSET = 0x244, };
+ };
+ struct Context_control_value : Common_register<0x0003>
+ {
+ using R = Common_register<0x0003>;
+
+ struct Mask_bits : R::template Bitfield<16, 16> { };
+
+ struct Inhibit_syn_context_switch_mask : R::template Bitfield<18, 1> { };
+ struct Inhibit_syn_context_switch : R::template Bitfield< 3, 1> { };
+
+ struct Rs_context_enable_mask : R::template Bitfield<17, 1> { };
+ struct Rs_context_enable : R::template Bitfield< 1, 1> { };
+
+ struct Engine_context_restore_inhibit_mask : R::template Bitfield<16, 1> { };
+ struct Engine_context_restore_inhibit : R::template Bitfield< 0, 1> { };
+ };
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 1350 ff
+ */
+ struct Ring_buffer_head_mmio : Common_register<0x0004>
+ {
+ enum { OFFSET = 0x34, };
+ };
+ struct Ring_buffer_head_value : Common_register<0x0005>
+ {
+ using R = Common_register<0x0005>;
+
+ struct Wrap_count : R::template Bitfield<21,11> { };
+ struct Head_offset : R::template Bitfield< 2,19> { };
+ struct Reserved_mbz : R::template Bitfield< 0, 2> { };
+ };
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 1353 ff
+ */
+ struct Ring_buffer_tail_mmio : Common_register<0x0006>
+ {
+ enum { OFFSET = 0x30, };
+ };
+ struct Ring_buffer_tail_value : Common_register<0x0007>
+ {
+ using R = Common_register<0x0007>;
+
+ struct Reserved_mbz_1 : R::template Bitfield<21, 11> { };
+ struct Tail_offset : R::template Bitfield< 3, 18> { };
+ struct Reserved_mbz_2 : R::template Bitfield< 0, 3> { };
+ };
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 1352 ff
+ */
+ struct Ring_buffer_start_mmio : Common_register<0x0008>
+ {
+ enum { OFFSET = 0x38, };
+ };
+ struct Ring_buffer_start_value : Common_register<0x0009>
+ {
+ using R = Common_register<0x0009>;
+
+ struct Starting_address : R::template Bitfield<12, 20> { };
+ struct Reserved_mbz : R::template Bitfield< 0, 12> { };
+ };
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 1345 ff
+ */
+ struct Ring_buffer_control_mmio : Common_register<0x000A>
+ {
+ enum { OFFSET = 0x3c, };
+ };
+ struct Ring_buffer_control_value : Common_register<0x000B>
+ {
+ using R = Common_register<0x000B>;
+
+ struct Reserved_mbz_1 : R::template Bitfield<21, 11> { };
+ struct Buffer_length : R::template Bitfield<12, 9> { };
+ struct RBwait : R::template Bitfield<11, 1>
+ {
+ enum { CLEAR = 0b01, };
+ };
+
+ struct Semaphore_wait : R::template Bitfield<10, 1>
+ {
+ enum { CLEAR = 0b01, };
+ };
+
+ struct Reserved_mbz_2 : R::template Bitfield< 3, 7> { };
+ struct Arhp : R::template Bitfield< 1, 2>
+ {
+ enum {
+ MI_AUTOREPORT_OFF = 0,
+ MI_AUTOREPORT_64KB = 1,
+ MI_AUTOREPORT_4KB = 2,
+ MI_AUTOREPORT_128KB = 3
+ };
+ };
+
+ struct Ring_buffer_enable : R::template Bitfield< 0, 1> { };
+ };
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 142
+ */
+ struct Bb_addr_udw_mmio : Common_register<0x000C>
+ {
+ enum { OFFSET = 0x168, };
+ };
+ struct Bb_addr_udw_value : Common_register<0x000D> { };
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 129
+ */
+ struct Bb_addr_mmio : Common_register<0x000E>
+ {
+ enum { OFFSET = 0x140, };
+ };
+ struct Bb_addr_value : Common_register<0x000F> { };
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 138
+ */
+ struct Bb_state_mmio : Common_register<0x0010>
+ {
+ enum { OFFSET = 0x110, };
+ };
+ struct Bb_state_value : Common_register<0x0011>
+ {
+ struct Address_space_indicator : Bitfield<5, 1> { };
+ };
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 1402
+ */
+ struct Sbb_addr_udw_mmio : Common_register<0x0012>
+ {
+ enum { OFFSET = 0x11C, };
+ };
+ struct Sbb_addr_udw_value : Common_register<0x0013> { };
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 1397
+ */
+ struct Sbb_addr_mmio : Common_register<0x0014>
+ {
+ enum { OFFSET = 0x114, };
+ };
+ struct Sbb_addr_value : Common_register<0x0015> { };
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 1399
+ */
+ struct Sbb_state_mmio : Common_register<0x0016>
+ {
+ enum { OFFSET = 0x118, };
+ };
+ struct Sbb_state_value : Common_register<0x0017> { };
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 131 ff
+ *
+ * RCS only
+ */
+ struct Bb_per_ctx_ptr_mmio : Common_register<0x0018>
+ {
+ enum { OFFSET = 0x1C0, };
+ };
+ struct Bb_per_ctx_ptr_value : Common_register<0x0019>
+ {
+ using R = Common_register<0x0019>;
+
+ struct Address : R::template Bitfield<12, 20> { };
+ struct Reserved_mbz : R::template Bitfield< 2, 10> { };
+ struct Enable : R::template Bitfield< 1, 1> { };
+ struct Valid : R::template Bitfield< 0, 1> { };
+ };
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 821 ff
+ *
+ * RCS only
+ */
+ struct Rcs_indirect_ctx_mmio : Common_register<0x001A>
+ {
+ enum { OFFSET = 0x1C4, };
+ };
+ struct Rcs_indirect_ctx_value : Common_register<0x001B>
+ {
+ using R = Common_register<0x001B>;
+
+ struct Address : R::template Bitfield< 6, 26> { };
+ struct Size : R::template Bitfield< 0, 6> { };
+ };
+
+ /*
+ * XXX
+ *
+ * RCS only
+ */
+ struct Rcs_indirect_ctx_offset_mmio : Common_register<0x001C>
+ {
+ enum { OFFSET = 0x1C8, };
+ };
+ struct Rcs_indirect_ctx_offset_value : Common_register<0x001D>
+ {
+ using R = Common_register<0x001D>;
+
+ struct Reserved_mbz_1 : R::template Bitfield<16, 16> { };
+ struct Offset : R::template Bitfield< 6, 10> { };
+ struct Reserved_mbz_2 : R::template Bitfield< 0, 6> { };
+ };
+
+ /*
+ * XXX
+ *
+ * RCS only
+ */
+ struct Rcs_noop_1 : Common_register_array<0x001E, 2> { };
+
+ public:
+
+ Execlist_context(addr_t base,
+ addr_t ring_buffer_start,
+ size_t ring_buffer_length,
+ uint32_t immediate_header)
+ :
+ Common_context_regs(base)
+ {
+ write(immediate_header);
+ write_offset(RING_BASE);
+ {
+ typename Context_control_value::access_t v = read();
+ Context_control_value::Engine_context_restore_inhibit_mask::set(v, 1);
+ Context_control_value::Engine_context_restore_inhibit::set(v, 1);
+ Context_control_value::Inhibit_syn_context_switch_mask::set(v, 1);
+ Context_control_value::Inhibit_syn_context_switch::set(v, 1);
+ write(v);
+ }
+ write_offset(RING_BASE);
+ write_offset(RING_BASE);
+ write_offset(RING_BASE);
+ {
+ typename Ring_buffer_start_value::access_t v = read();
+ /* shift ring_buffer_start value accordingly */
+ typename Ring_buffer_start_value::access_t const addr = Ring_buffer_start_value::Starting_address::get(ring_buffer_start);
+ Ring_buffer_start_value::Starting_address::set(v, addr);
+ write(v);
+ }
+ write_offset(RING_BASE);
+ {
+ typename Ring_buffer_control_value::access_t v = read();
+ /* length is given in number of pages */
+ Ring_buffer_control_value::Buffer_length::set(v, ring_buffer_length / PAGE_SIZE);
+ /* according to the PRM it should be disable b/c of the amount of reports generated */
+ Ring_buffer_control_value::Arhp::set(v, Ring_buffer_control_value::Arhp::MI_AUTOREPORT_OFF);
+ Ring_buffer_control_value::Ring_buffer_enable::set(v, 1);
+ write(v);
+ }
+ write_offset(RING_BASE);
+ write_offset(RING_BASE);
+ write_offset(RING_BASE);
+ {
+ /* should actually not be written by software */
+ typename Bb_state_value::access_t v = 0;
+ Bb_state_value::Address_space_indicator::set(v, 1);
+ write(v);
+ }
+ write_offset(RING_BASE);
+ write_offset(RING_BASE);
+ write_offset(RING_BASE);
+ }
+
+ size_t tail_offset()
+ {
+ return read();
+ }
+
+ void tail_offset(size_t offset)
+ {
+ write(offset);
+ }
+
+ size_t head_offset()
+ {
+ return read();
+ }
+
+ /*********************
+ ** Debug interface **
+ *********************/
+
+ void dump()
+ {
+ using namespace Genode;
+
+ log("Execlist_context");
+ log(" Load_immediate_header: ", Hex(read(), Hex::PREFIX, Hex::PAD));
+ log(" Context_control: ", Hex(read(), Hex::PREFIX, Hex::PAD));
+ log(" Ring_buffer_head: ", Hex(read(), Hex::PREFIX, Hex::PAD));
+ log(" Wrap_count: ", Hex(read(), Hex::PREFIX, Hex::PAD));
+ log(" Ring_buffer_tail: ", Hex(read(), Hex::PREFIX, Hex::PAD));
+ log(" Ring_buffer_start: ", Hex(read(), Hex::PREFIX, Hex::PAD));
+ log(" Ring_buffer_control: ", Hex(read(), Hex::PREFIX, Hex::PAD));
+ log(" Bb_addr_udw: ", Hex(read(), Hex::PREFIX, Hex::PAD));
+ log(" Bb_addr: ", Hex(read(), Hex::PREFIX, Hex::PAD));
+ log(" Bb_state: ", Hex(read(), Hex::PREFIX, Hex::PAD));
+ log(" Sbb_addr_udw: ", Hex(read(), Hex::PREFIX, Hex::PAD));
+ log(" Sbb_addr: ", Hex(read(), Hex::PREFIX, Hex::PAD));
+ log(" Sbb_state: ", Hex(read(), Hex::PREFIX, Hex::PAD));
+ }
+};
+
+
+template
+class Igd::Ppgtt_context : public Igd::Common_context_regs
+{
+ public:
+
+ /* MI_NOOP */
+ struct Noop_1 : Common_register<0x0020> { };
+
+ /*
+ * IHD-OS-BDW-Vol 2a-11.15 p. 841 ff.
+ */
+ struct Load_immediate_header : Common_register<0x0021> { };
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 283
+ *
+ * RCS only
+ */
+ struct Cs_ctx_timestamp_mmio : Common_register<0x0022>
+ {
+ enum { OFFSET = 0x3A8, };
+ };
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 1143
+ */
+ struct Pdp_3_udw_mmio : Common_register<0x0024>
+ {
+ enum { OFFSET = 0x28C, };
+ };
+ struct Pdp_3_udw_value : Common_register<0x0025>
+ {
+ struct Value : Bitfield<0,32> { };
+ };
+
+ struct Pdp_3_ldw_mmio : Common_register<0x0026>
+ {
+ enum { OFFSET = 0x288, };
+ };
+ struct Pdp_3_ldw_value : Common_register<0x0027>
+ {
+ struct Value : Bitfield<0,32> { };
+ };
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 1142
+ */
+ struct Pdp_2_udw_mmio : Common_register<0x0028>
+ {
+ enum { OFFSET = 0x284, };
+ };
+ struct Pdp_2_udw_value : Common_register<0x0029>
+ {
+ struct Value : Bitfield<0,32> { };
+ };
+
+ struct Pdp_2_ldw_mmio : Common_register<0x002A>
+ {
+ enum { OFFSET = 0x280, };
+ };
+ struct Pdp_2_ldw_value : Common_register<0x002B>
+ {
+ struct Value : Bitfield<0,32> { };
+ };
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 1141
+ */
+ struct Pdp_1_udw_mmio : Common_register<0x002C>
+ {
+ enum { OFFSET = 0x27C, };
+ };
+ struct Pdp_1_udw_value : Common_register<0x002D>
+ {
+ struct Value : Bitfield<0,32> { };
+ };
+
+ struct Pdp_1_ldw_mmio : Common_register<0x002E>
+ {
+ enum { OFFSET = 0x278, };
+ };
+ struct Pdp_1_ldw_value : Common_register<0x002F>
+ {
+ struct Value : Bitfield<0,32> { };
+ };
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 1140
+ */
+ struct Pdp_0_udw_mmio : Common_register<0x0030>
+ {
+ enum { OFFSET = 0x274, };
+ };
+ struct Pdp_0_udw_value : Common_register<0x0031>
+ {
+ struct Value : Bitfield<0,32> { };
+ };
+
+ struct Pdp_0_ldw_mmio : Common_register<0x0032>
+ {
+ enum { OFFSET = 0x270, };
+ };
+ struct Pdp_0_ldw_value : Common_register<0x0033>
+ {
+ struct Value : Bitfield<0,32> { };
+ };
+
+ struct Noop_2 : Common_register_array<0x0034, 12> { };
+ struct Noop_3 : Common_register_array<0x0040, 1> { };
+
+ struct Load_immediate_header_2 : Common_register<0x0041> { };
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 1325
+ */
+ struct R_pwr_clk_state_mmio : Common_register<0x0042>
+ {
+ enum { OFFSET = 0x0C8, };
+ };
+ struct R_pwr_clk_state_value : Common_register<0x0043>
+ {
+ using R = Common_register<0x0043>;
+
+ struct Power_clock_state_enable : R::template Bitfield<31, 1> { };
+ struct Power_clock_state : R::template Bitfield< 0, 31> { };
+ };
+
+ /*
+ * IHD-OS-BDW-Vol 7-11.15 p. 659
+ */
+ struct Gpgpu_csr_base_Address : Common_register_array<0x00044, 3> { };
+
+ struct Noop_4 : Common_register_array<0x0047, 9> { };
+
+ public:
+
+ Ppgtt_context(addr_t base, uint64_t plm4_addr)
+ :
+ Common_context_regs(base)
+ {
+ write(0x11001011);
+ write_offset(RING_BASE);
+ {
+ write(0);
+ }
+ write_offset(RING_BASE);
+ write_offset(RING_BASE);
+ write_offset(RING_BASE);
+ write_offset(RING_BASE);
+ write_offset(RING_BASE);
+ write_offset(RING_BASE);
+ write_offset(RING_BASE);
+ {
+ Genode::uint32_t const udw = (Genode::uint32_t)((plm4_addr >> 16) >> 16);
+ write(udw);
+ }
+ write_offset(RING_BASE);
+ {
+ Genode::uint32_t const ldw = (Genode::uint32_t)plm4_addr;
+ write(ldw);
+ }
+
+ write_offset(RING_BASE);
+ }
+
+ /*********************
+ ** Debug interface **
+ *********************/
+
+ void dump()
+ {
+ using namespace Genode;
+
+ using C = Ppgtt_context;
+
+ log("Ppgtt_context");
+ log(" Pdp_0_udw: ", Hex(read(), Hex::PREFIX, Hex::PAD));
+ log(" Pdp_0_ldw: ", Hex(read(), Hex::PREFIX, Hex::PAD));
+ }
+};
+
+
+/*
+ * TODO
+ */
+class Igd::Engine_context
+{
+ public:
+
+ Engine_context() { }
+};
+
+
+/*
+ * TODO
+ */
+class Igd::Ext_engine_context
+{
+ public:
+
+ Ext_engine_context() { }
+};
+
+
+/*
+ * TODO
+ */
+class Igd::Urb_atomic_context
+{
+ public:
+
+ Urb_atomic_context() { }
+};
+
+
+/*
+ * IHD-OS-BDW-Vol 2d-11.15 p. 199
+ */
+class Igd::Hardware_status_page : public Igd::Common_context_regs
+{
+ public:
+
+ /*
+ * See ISR register definition
+ */
+ struct Interrupt_status_register_storage : Common_register<0> { };
+
+ /*
+ * See RING_BUFFER_HEAD_RCSUNIT definition
+ */
+ struct Ring_head_ptr_storage : Common_register<4> { };
+
+ /*
+ * See CTXT_ST_BUF
+ */
+ enum { CONTEXT_STATUS_DWORDS_NUM = 12, };
+ struct Context_status_dwords : Common_register_array<16, CONTEXT_STATUS_DWORDS_NUM> { };
+ struct Last_written_status_offset : Common_register<31> { };
+
+ Hardware_status_page(addr_t base)
+ : Common_context_regs(base) { }
+};
+
+
+/*
+ */
+class Igd::Rcs_context
+{
+ public:
+
+ enum {
+ HW_ID = 0,
+ CONTEXT_PAGES = 20 /* ctx */ + 1 /* GuC */,
+ RING_PAGES = 4,
+
+ RCS_RING_BASE = 0x2000,
+
+ HW_STATUS_PAGE_SIZE = PAGE_SIZE,
+
+ /*
+ * IHD-OS-BDW-Vol 7-11.15 p. 27 ff
+ */
+ EXECLIST_CTX_START = 0x0000,
+ EXECLIST_CTX_END = 0x0020,
+ EXECLIST_CTX_SIZE = (EXECLIST_CTX_END - EXECLIST_CTX_START) * sizeof(uint32_t),
+ EXECLIST_CTX_IH = 0x1100101B,
+
+ PPGTT_CTX_START = EXECLIST_CTX_END,
+ PPGTT_CTX_END = 0x0050,
+ PPGTT_CTX_SIZE = (PPGTT_CTX_END - PPGTT_CTX_START) * sizeof(uint32_t),
+ PPGTT_CTX_IH = 0x11000001,
+
+ ENGINE_CTX_START = PPGTT_CTX_END,
+ ENGINE_CTX_END = 0x0EC0,
+ ENGINE_CTX_SIZE = (ENGINE_CTX_END - ENGINE_CTX_START) * sizeof(uint32_t),
+
+ EXT_ENGINE_CTX_START = ENGINE_CTX_END,
+ EXT_ENGINE_CTX_END = 0x26B0,
+
+ URB_ATOMIC_STORE_START = EXT_ENGINE_CTX_END,
+ URB_ATOMIC_STORE_END = 0x46B0,
+ URB_ATOMIC_STORE_SIZE = (URB_ATOMIC_STORE_END - URB_ATOMIC_STORE_START) * sizeof(uint32_t),
+ };
+
+
+ private:
+
+ Hardware_status_page _hw_status_page;
+ Execlist_context _execlist_context;
+ Ppgtt_context _ppgtt_context;
+ Engine_context _engine_context;
+ Ext_engine_context _ext_engine_context;
+ Urb_atomic_context _urb_atomic_context;
+
+ public:
+
+ Rcs_context(addr_t map_base,
+ addr_t ring_buffer_start,
+ size_t ring_buffer_length,
+ uint64_t plm4_addr)
+ :
+ _hw_status_page(map_base),
+ _execlist_context((addr_t)(map_base + HW_STATUS_PAGE_SIZE),
+ ring_buffer_start, ring_buffer_length,
+ EXECLIST_CTX_IH),
+ _ppgtt_context((addr_t)(map_base + HW_STATUS_PAGE_SIZE), plm4_addr),
+ _engine_context(),
+ _ext_engine_context(),
+ _urb_atomic_context()
+ {
+ Genode::log(__func__, ":",
+ " map_base:", Genode::Hex(map_base),
+ " ring_buffer_start:", Genode::Hex(ring_buffer_start),
+ " ring_buffer_length:", Genode::Hex(ring_buffer_length),
+ " plm4_addr:", Genode::Hex(plm4_addr, Genode::Hex::PREFIX, Genode::Hex::PAD));
+
+ using C = Execlist_context;
+
+ _execlist_context.write_offset(RCS_RING_BASE);
+ {
+ using R = C::Bb_per_ctx_ptr_value;
+ typename R::access_t v = _execlist_context.read();
+ R::Address::set(v, 0);
+ R::Valid::set(v, 0);
+ _execlist_context.write(v);
+ }
+
+ _execlist_context.write_offset(RCS_RING_BASE);
+ {
+ using R = C::Rcs_indirect_ctx_value;
+ typename R::access_t v = _execlist_context.read();
+ R::Address::set(v, 0);
+ R::Size::set(v, 0);
+ _execlist_context.write(v);
+ }
+
+ _execlist_context.write_offset(RCS_RING_BASE);
+ {
+ using R = C::Rcs_indirect_ctx_offset_value;
+ typename R::access_t v = _execlist_context.read();
+ R::Offset::set(v, 0);
+ _execlist_context.write(v);
+ }
+
+ using P = Ppgtt_context;
+
+ _ppgtt_context.write(PPGTT_CTX_IH);
+ }
+
+ size_t head_offset()
+ {
+ return _execlist_context.head_offset();
+ }
+
+ void tail_offset(addr_t offset)
+ {
+ _execlist_context.tail_offset(offset);
+ }
+
+ /*********************
+ ** Debug interface **
+ *********************/
+
+ void dump()
+ {
+ using namespace Genode;
+
+ log("Rcs_context");
+ log(" HW status page: ", Hex(_hw_status_page.base(), Hex::PREFIX, Hex::PAD));
+ log(" Execlist_context: ", Hex(_execlist_context.base(), Hex::PREFIX, Hex::PAD));
+ log(" Ppgtt_context: ", Hex(_ppgtt_context.base(), Hex::PREFIX, Hex::PAD));
+
+ _execlist_context.dump();
+
+ using C = Execlist_context;
+
+ log(" Bb_per_ctx_ptr: ", Hex(_execlist_context.read(), Hex::PREFIX, Hex::PAD));
+ log(" Rcs_indirect_ctx: ", Hex(_execlist_context.read(), Hex::PREFIX, Hex::PAD));
+ log(" Rcs_indirect_ctx_offset: ", Hex(_execlist_context.read(), Hex::PREFIX, Hex::PAD));
+
+ _ppgtt_context.dump();
+ }
+
+ void dump_hw_status_page(bool raw = false)
+ {
+ using namespace Genode;
+
+ if (raw) {
+ uint32_t const *p = (uint32_t const *)_hw_status_page.base();
+ for (uint32_t i = 0; i < HW_STATUS_PAGE_SIZE / sizeof(uint32_t); i += 8) {
+ log(Hex(i, Hex::PREFIX, Hex::PAD), " ",
+ Hex(p[i ], Hex::PREFIX, Hex::PAD), " ",
+ Hex(p[i+1], Hex::PREFIX, Hex::PAD), " ",
+ Hex(p[i+2], Hex::PREFIX, Hex::PAD), " ",
+ Hex(p[i+3], Hex::PREFIX, Hex::PAD), " ",
+ Hex(p[i+4], Hex::PREFIX, Hex::PAD), " ",
+ Hex(p[i+5], Hex::PREFIX, Hex::PAD), " ",
+ Hex(p[i+6], Hex::PREFIX, Hex::PAD), " ",
+ Hex(p[i+7], Hex::PREFIX, Hex::PAD));
+ }
+ } else {
+ using H = Hardware_status_page;
+ log("Hardware_status_page");
+ log(" Interrupt_status_register_storage: ",
+ Hex(_hw_status_page.read(),
+ Hex::PREFIX, Hex::PAD));
+ log(" Ring_head_ptr_storage: ",
+ Hex(_hw_status_page.read(),
+ Hex::PREFIX, Hex::PAD));
+ for (int i = 0; i < H::CONTEXT_STATUS_DWORDS_NUM; i++) {
+ log(" Context_status_dwords: ",
+ Hex(_hw_status_page.read(i),
+ Hex::PREFIX, Hex::PAD));
+ }
+ log(" Last_written_status_offset: ",
+ Hex(_hw_status_page.read(),
+ Hex::PREFIX, Hex::PAD));
+ }
+ }
+
+ void dump_execlist_context()
+ {
+ using namespace Genode;
+
+ log("Execlist_context");
+ uint32_t const *p = (uint32_t const *)_execlist_context.base();
+ for (uint32_t i = 0; i < EXECLIST_CTX_SIZE / sizeof(uint32_t); i += 8) {
+ log(" ", Hex(i, Hex::PREFIX, Hex::PAD), " ",
+ Hex(p[i ], Hex::PREFIX, Hex::PAD), " ",
+ Hex(p[i+1], Hex::PREFIX, Hex::PAD), " ",
+ Hex(p[i+2], Hex::PREFIX, Hex::PAD), " ",
+ Hex(p[i+3], Hex::PREFIX, Hex::PAD), " ",
+ Hex(p[i+4], Hex::PREFIX, Hex::PAD), " ",
+ Hex(p[i+5], Hex::PREFIX, Hex::PAD), " ",
+ Hex(p[i+6], Hex::PREFIX, Hex::PAD), " ",
+ Hex(p[i+7], Hex::PREFIX, Hex::PAD));
+ }
+ }
+};
+
+#endif /* _LOGICAL_RING_CONTEXT_H_ */
diff --git a/repos/os/src/drivers/gpu/intel/context_descriptor.h b/repos/os/src/drivers/gpu/intel/context_descriptor.h
new file mode 100644
index 000000000..8b7e89f98
--- /dev/null
+++ b/repos/os/src/drivers/gpu/intel/context_descriptor.h
@@ -0,0 +1,117 @@
+/*
+ * \brief Broadwell context descriptor
+ * \author Josef Soentgen
+ * \date 2017-03-15
+ */
+
+/*
+ * Copyright (C) 2017 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 _CONTEXT_DESCRIPTOR_H_
+#define _CONTEXT_DESCRIPTOR_H_
+
+/* Genode includes */
+#include
+
+/* local includes */
+#include
+
+
+namespace Igd {
+
+ class Context_descriptor;
+}
+
+
+/*
+ * IHD-OS-BDW-Vol 2d-11.15 p. 107
+ */
+class Igd::Context_descriptor : Genode::Register<64>
+{
+ public:
+
+ /*
+ * Context ID covers 63:32 where we currently only
+ * care about the lowest 20 bits.
+ *
+ * 63:55 group ID
+ * 54 MBZ
+ * 53 MBZ
+ * 20:0 globally unique SW controlled ID
+ */
+ struct Context_id : Bitfield<32, 20> { };
+ struct Logical_ring_context_address : Bitfield<12, 20> { };
+ struct Privilege_access : Bitfield< 8, 1> { };
+ struct Fault_handling : Bitfield< 6, 2>
+ {
+ enum { FAULT_AND_HANG = 0b00, };
+ };
+ struct Addressing_mode : Bitfield< 3, 2>
+ {
+ enum {
+ ADVANCED_WO_AD = 0b00,
+ LEGACY_WO_64 = 0b01,
+ ADVANCED_WITH_AD = 0b10,
+ LEGACY_WITH_64 = 0b11,
+ };
+ };
+ struct Force_restore : Bitfield< 2, 1> { };
+ struct Force_pd_restore : Bitfield< 1, 1> { };
+ struct Valid : Bitfield< 0, 1> { };
+
+ private:
+
+ uint64_t _value;
+
+ public:
+
+ /**
+ * Constructor
+ *
+ * \param id context id
+ * \param lrca graphics memory address of the context
+ */
+ Context_descriptor(uint32_t id,
+ addr_t lrca)
+ {
+ /* shift lrca value accordingly */
+ typename Context_descriptor::access_t const addr = Context_descriptor::Logical_ring_context_address::get(lrca);
+ Logical_ring_context_address::set(_value, addr);
+
+ Privilege_access::set(_value, 1);
+ /* must be set to FAULT_AND_HANG according to PRM when legacy mode is used */
+ Fault_handling::set(_value, Fault_handling::FAULT_AND_HANG);
+ Addressing_mode::set(_value, Addressing_mode::LEGACY_WITH_64);
+ Context_id::set(_value, id);
+ Force_restore::set(_value, 1);
+ Force_pd_restore::set(_value, 1);
+ Valid::set(_value, 1);
+ }
+
+ /**
+ * Constructor
+ */
+ Context_descriptor() : _value(0) { }
+
+ uint32_t low() const { return (uint32_t)(_value & 0xffffffff); }
+ uint32_t high() const { return (uint32_t)(_value >> 32); }
+ bool valid() const { return Valid::get(_value) == 1; }
+ uint64_t value() const { return _value; }
+
+ /*********************
+ ** Debug interface **
+ *********************/
+
+ void dump()
+ {
+ using namespace Genode;
+
+ log("Context_descriptor: ", Hex(_value, Hex::PREFIX, Hex::PAD));
+ }
+};
+
+#endif /* _CONTEXT_DESCRIPTOR_H_ */
diff --git a/repos/os/src/drivers/gpu/intel/ggtt.h b/repos/os/src/drivers/gpu/intel/ggtt.h
new file mode 100644
index 000000000..85a36572b
--- /dev/null
+++ b/repos/os/src/drivers/gpu/intel/ggtt.h
@@ -0,0 +1,312 @@
+/*
+ * \brief Broadwell global graphics translation table
+ * \author Josef Soentgen
+ * \date 2017-03-15
+ */
+
+/*
+ * Copyright (C) 2017 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 _GLOBAL_GTT_H_
+#define _GLOBAL_GTT_H_
+
+/* Genode includes */
+#include
+
+/* local includes */
+#include
+
+
+namespace Igd {
+
+ struct Ggtt;
+}
+
+/*
+ * Global Graphics Translation Table
+ */
+class Igd::Ggtt
+{
+ public:
+
+ struct Could_not_find_free : Genode::Exception { };
+ struct Offset_out_of_range : Genode::Exception { };
+ struct Wrong_graphics_address : Genode::Exception { };
+
+ using Offset = Igd::uint32_t;
+
+ struct Mapping
+ {
+ Genode::Dataspace_capability cap;
+ Offset offset;
+ Igd::addr_t vaddr;
+
+ enum { INVALID_OFFSET = ~0u - 1, };
+
+ Mapping() : offset(INVALID_OFFSET) { }
+
+ Mapping(Genode::Dataspace_capability cap, Offset offset)
+ : cap(cap), offset(offset) { }
+
+ virtual ~Mapping() { }
+ };
+
+ private:
+
+ /*
+ * IHD-OS-BDW-Vol 5-11.15 p. 44
+ */
+ struct Page_table_entry : Genode::Register<64>
+ {
+ struct Physical_adress : Bitfield<12, 36> { };
+ struct Present : Bitfield< 0, 1> { };
+ };
+
+ struct Space_allocator
+ {
+ Genode::Bit_array<1024*1024> _array;
+ size_t _used;
+
+ bool allocated(addr_t const index) const
+ {
+ return _array.get(index, 1);
+ }
+
+ void set(addr_t const index)
+ {
+ _used++;
+ _array.set(index, 1);
+ }
+
+ void clear(addr_t const index)
+ {
+ _used--;
+ _array.clear(index, 1);
+ }
+
+ bool word_free(size_t const word) const
+ {
+ try {
+ size_t const bits = sizeof(size_t) * 8;
+ size_t const index = word * sizeof(size_t);
+ return !_array.get(index, bits);
+ } catch (...) { }
+
+ return false;
+ }
+
+ size_t used() const { return _used; }
+ } _space;
+
+ Igd::Mmio &_mmio;
+
+ addr_t const _base;
+ size_t const _size;
+ size_t const _num_entries;
+
+ uint64_t *_entries;
+
+ addr_t const _scratch_page;
+ size_t const _aperture_size;
+ size_t const _aperture_entries;
+
+ void _insert_pte(addr_t pa, Offset offset)
+ {
+ Page_table_entry::access_t pte = 0;
+ Page_table_entry::Physical_adress::set(pte, pa >> 12);
+ Page_table_entry::Present::set(pte, 1);
+
+ _entries[offset] = pte;
+ _mmio.flush_gfx_tlb();
+ Igd::wmb();
+ }
+
+ public:
+
+ /**
+ * Constructor
+ *
+ * \param mmio reference to Igd::Mmio object
+ * \param base virtual base address of GGTT start
+ * \param size size of GGTT in bytes
+ * \param aperture_size size of the aperture in bytes
+ * \param scratch_page physical address of the scratch page
+ * \param fb_size size of the framebuffer region in the GTT in bytes
+ */
+ Ggtt(Igd::Mmio &mmio, addr_t base, size_t size,
+ size_t aperture_size, addr_t scratch_page,
+ size_t fb_size)
+ :
+ _mmio(mmio),
+ _base(base),
+ _size(size),
+ /* make the last entry/page unavailable */
+ _num_entries((_size / 8) - 1),
+ _entries((uint64_t*)_base),
+ _scratch_page(scratch_page),
+ _aperture_size(aperture_size),
+ _aperture_entries(_aperture_size / PAGE_SIZE)
+ {
+ /* reserve GGTT region occupied by the framebuffer */
+ size_t const fb_entries = fb_size / PAGE_SIZE;
+ for (size_t i = 0; i < fb_entries; i++) {
+ _space.set(i);
+ }
+ for (size_t i = fb_entries; i < _num_entries; i++) {
+ _insert_pte(_scratch_page, i);
+ }
+ }
+
+ /**
+ * Insert page into GGTT
+ *
+ * \param pa physical address
+ * \param offset offset of GTT entry
+ *
+ * \throw Offset_out_of_range
+ */
+ void insert_pte(addr_t pa, Offset offset)
+ {
+ if (offset > _num_entries) { throw Offset_out_of_range(); }
+
+ if (_space.allocated(offset)) {
+ Genode::warning(__func__, " offset:", offset, " already used");
+ }
+
+ _space.set(offset);
+ _insert_pte(pa, offset);
+ }
+
+ /**
+ * Remove page from GTT
+ *
+ * \param offset offset of GTT entry
+ */
+ void remove_pte(Offset offset)
+ {
+ if (!_space.allocated(offset)) {
+ Genode::warning(__func__, " offset:", offset, " was not used");
+ return;
+ }
+
+ _space.clear(offset);
+ _insert_pte(_scratch_page, offset);
+ }
+
+ /**
+ * Remove range of pages from GTT
+ *
+ * \param start offset of the first page in the GGTT
+ * \param num number of pages
+ */
+ void remove_pte_range(Offset const start, Offset const num)
+ {
+ Offset const end = start + num;
+ for (Offset offset = start; offset < end; offset++) {
+ remove_pte(offset);
+ }
+ }
+
+ /**
+ * Find free GGTT entries
+ *
+ * \param num number of continuous entries
+ * \param aperture request entries mappable by CPU
+ *
+ * \return start offset of free entry range
+ *
+ * \throw Could_not_find_free
+ */
+ Offset find_free(size_t num, bool aperture = false) const
+ {
+ size_t const start_index = aperture ? 0 : _aperture_entries;
+ size_t const end_index = aperture ? _aperture_entries : _num_entries;
+
+ for (size_t i = start_index; i < end_index; i++) {
+ bool const f = _space.word_free(i);
+ if (f) { return i * (sizeof(size_t)); }
+ }
+
+ throw Could_not_find_free();
+ }
+
+ /**
+ * Get GGTT entry offset from GMADDR
+ *
+ * \param gmaddr graphics memory address
+ *
+ * \return GGTT offset of GMADDR
+ *
+ * \throw Wrong_graphics_address
+ */
+ Offset offset(addr_t const gmaddr) const
+ {
+ if (gmaddr & 0xfffu) { throw Wrong_graphics_address(); }
+ if (gmaddr > 0xffffffffu) { throw Wrong_graphics_address(); }
+ return gmaddr / PAGE_SIZE;
+ }
+
+ /**
+ * Get GMADDR for GGTT entry offset
+ *
+ * \param offset offset of GGTT entry
+ *
+ * \return graphics memory address
+ *
+ * \throw Offset_out_of_range
+ */
+ addr_t addr(Offset offset) const
+ {
+ if (offset > _num_entries) { throw Offset_out_of_range(); }
+ return offset * PAGE_SIZE;
+ }
+
+ /**
+ * Get total number of entries in GGTT
+ *
+ * \return number of entries
+ */
+ size_t entries() const { return _num_entries; }
+
+ /**
+ * Get scratch page address
+ *
+ * \return physical address of the scratch page
+ */
+ addr_t scratch_page() const { return _scratch_page; }
+
+ /*********************
+ ** Debug interface **
+ *********************/
+
+ void dump(bool const dump_entries = false,
+ size_t const limit = 0u,
+ size_t const start = 0u) const
+ {
+ using namespace Genode;
+
+ log("GGTT");
+ log(" vaddr:", Hex(_base), " size:", Hex(_size), " entries:", _num_entries,
+ " used:", _space.used(), " aperture_size:", Hex(_aperture_size));
+ log(" scratch_page:", Hex(_scratch_page), " (PA)");
+
+ if (!dump_entries) { return; }
+
+ log(" entries:");
+ uint64_t *ggtt = (uint64_t*)_base;
+ size_t const max = !limit ? _num_entries : limit > _num_entries ? _num_entries : limit;
+ for (size_t i = start; i < start + max; i += 8) {
+ log(" ", Hex(i, Hex::PREFIX, Hex::PAD), " ",
+ Hex(ggtt[i]), " ", Hex(ggtt[i + 1]), " ",
+ Hex(ggtt[i + 2]), " ", Hex(ggtt[i + 3]), " ",
+ Hex(ggtt[i + 4]), " ", Hex(ggtt[i + 5]), " ",
+ Hex(ggtt[i + 6]), " ", Hex(ggtt[i + 7]));
+ }
+ }
+};
+
+#endif /* _GLOBAL_GTT_H_ */
diff --git a/repos/os/src/drivers/gpu/intel/main.cc b/repos/os/src/drivers/gpu/intel/main.cc
new file mode 100644
index 000000000..3e7929173
--- /dev/null
+++ b/repos/os/src/drivers/gpu/intel/main.cc
@@ -0,0 +1,1873 @@
+/*
+ * \brief Broadwell multiplexer
+ * \author Josef Soentgen
+ * \data 2017-03-15
+ */
+
+/*
+ * Copyright (C) 2017 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
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* local includes */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+
+namespace Igd {
+
+ struct Device_info;
+ struct Device;
+}
+
+
+struct Igd::Device_info
+{
+ uint16_t id;
+ char const *descr;
+ uint64_t features;
+};
+
+
+/*
+ * IHD-OS-BDW-Vol 4-11.15 p. 9
+ */
+static Igd::Device_info _supported_devices[] = {
+ { 0x1606, "HD Graphics (BDW GT1 ULT)", 0ull },
+ { 0x1616, "HD Graphics 5500 (BDW GT2 ULT)", 0ull },
+ /* TODO proper eDRAM probing + caching */
+ { 0x1622, "Iris Pro Graphics 6200 (BDW GT3e)", 0ull },
+};
+
+#define ELEM_NUMBER(x) (sizeof((x)) / sizeof((x)[0]))
+
+
+struct Igd::Device
+{
+ struct Initialization_failed : Genode::Exception { };
+ struct Unsupported_device : Genode::Exception { };
+ struct Out_of_ram : Genode::Exception { };
+ struct Already_scheduled : Genode::Exception { };
+ struct Could_not_map_buffer : Genode::Exception { };
+
+ Genode::Env &_env;
+ Genode::Allocator_guard _md_alloc;
+
+ /***********
+ ** Timer **
+ ***********/
+
+ Timer::Connection _timer { _env };
+
+ enum { WATCHDOG_TIMEOUT = 1*1000*1000, };
+
+ struct Timer_delayer : Genode::Mmio::Delayer
+ {
+ Timer::Connection &_timer;
+ Timer_delayer(Timer::Connection &timer) : _timer(timer) { }
+
+ void usleep(unsigned us) { _timer.usleep(us); }
+ } _delayer { _timer };
+
+ /*********
+ ** PCI **
+ *********/
+
+ /*
+ * Config space utility methods
+ */
+
+ template
+ static Platform::Device::Access_size _access_size(T 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 };
+
+ struct Pci_backend_alloc : Utils::Backend_alloc
+ {
+ Platform::Connection &_pci;
+
+ Pci_backend_alloc(Platform::Connection &pci) : _pci(pci) { }
+
+ Genode::Ram_dataspace_capability alloc(Genode::Allocator_guard &guard,
+ Genode::size_t size)
+ {
+ if (!guard.withdraw(size)) { throw Out_of_ram(); }
+
+ /*
+ * XXX we do not account for any metadata the Platform
+ * driver might allocate on our behalf which will
+ * make the alloc_dma_buffer call fail.
+ */
+ _pci.upgrade_ram(size);
+ try {
+ return _pci.alloc_dma_buffer(size);
+ }
+ catch (Platform::Out_of_ram) {
+ throw Out_of_ram(); }
+ }
+
+ void free(Genode::Allocator_guard &guard, Genode::Ram_dataspace_capability cap)
+ {
+ if (!cap.valid()) {
+ Genode::error("could not free, capability invalid");
+ return;
+ }
+ size_t const size = Genode::Dataspace_client(cap).size();
+ /*
+ * XXX we will leak quota because the Platform session is not
+ * able to give us back any quota
+ */
+ guard.upgrade(size);
+ Genode::warning("leaking ", size, " bytes of RAM quota at ",
+ " platform_drv but upgrade guard anyway");
+
+ _pci.free_dma_buffer(cap);
+ }
+
+ } _pci_backend_alloc { _pci };
+
+ 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;
+
+ void _pci_info(char const *descr)
+ {
+ using namespace Genode;
+
+ uint16_t const vendor_id = _device.vendor_id();
+ uint16_t const device_id = _device.device_id();
+
+ uint8_t bus = 0, dev = 0, fun = 0;
+ _device.bus_address(&bus, &dev, &fun);
+
+ log("Found: '", descr, "' ",
+ "[", Hex(vendor_id), ":", Hex(device_id), "] (",
+ Hex(bus, Hex::OMIT_PREFIX), ":",
+ Hex(dev, Hex::OMIT_PREFIX), ".",
+ Hex(fun, Hex::OMIT_PREFIX), ")");
+
+ for (int i = 0; i < PCI_NUM_RES; i++) {
+
+ using Resource = Platform::Device::Resource;
+
+ Resource const resource = _device.resource(i);
+
+ if (resource.type() == Resource::INVALID) { continue; }
+
+ log(" Resource ", i, " "
+ "(", resource.type() == Resource::IO ? "I/O" : "MEM", "): "
+ "base=", Genode::Hex(resource.base()), " "
+ "size=", Genode::Hex(resource.size()), " ",
+ (resource.prefetchable() ? "prefetchable" : ""));
+ }
+ }
+
+ bool _supported()
+ {
+ uint16_t const id = _device.device_id();
+ for (size_t i = 0; i < ELEM_NUMBER(_supported_devices); i++) {
+ if (_supported_devices[i].id == id) {
+ _info = _supported_devices[i];
+ _pci_info(_supported_devices[i].descr);
+ return true;
+ }
+ }
+ _pci_info("");
+ return false;
+ }
+
+ /**********
+ ** GGTT **
+ **********/
+
+ Genode::Constructible _ggtt;
+
+ /**********
+ ** MMIO **
+ **********/
+
+ Genode::Constructible _mmio;
+
+ /************
+ ** MEMORY **
+ ************/
+
+ struct Unaligned_size : Genode::Exception { };
+
+ Genode::Ram_dataspace_capability _alloc_dataspace(Genode::Allocator_guard &guard,
+ size_t const size)
+ {
+ if (size & 0xfff) { throw Unaligned_size(); }
+
+ Genode::Ram_dataspace_capability ds = _pci_backend_alloc.alloc(guard, size);
+ if (!ds.valid()) { throw Out_of_ram(); }
+ return ds;
+ }
+
+ void _free_dataspace(Genode::Allocator_guard &guard,
+ Genode::Ram_dataspace_capability cap)
+ {
+ if (!cap.valid()) { return; }
+
+ _pci_backend_alloc.free(guard, cap);
+ }
+
+ struct Ggtt_mmio_mapping : Ggtt::Mapping
+ {
+ Genode::Io_mem_connection mmio;
+
+ Ggtt_mmio_mapping(Genode::Env &env, addr_t base, size_t size,
+ Ggtt::Offset offset)
+ :
+ mmio(env, base, size)
+ {
+ Ggtt::Mapping::cap = mmio.dataspace();
+ Ggtt::Mapping::offset = offset;
+ }
+
+ virtual ~Ggtt_mmio_mapping() { }
+ };
+
+ Genode::Registry> _ggtt_mmio_mapping_registry;
+
+ Ggtt_mmio_mapping const &_map_dataspace_ggtt(Genode::Allocator &alloc,
+ Genode::Dataspace_capability cap,
+ Ggtt::Offset offset)
+ {
+ Genode::Dataspace_client client(cap);
+ addr_t const phys_addr = client.phys_addr();
+ size_t const size = client.size();
+
+ /*
+ * Create the mapping first and insert the entries afterwards
+ * so we do not have to rollback when the allocation failes.
+ */
+
+ addr_t const base = _res_base[GMADR] + _ggtt->addr(offset);
+ Genode::Registered *mem = new (&alloc)
+ Genode::Registered(_ggtt_mmio_mapping_registry,
+ _env, base, size, offset);
+
+ for (size_t i = 0; i < size; i += PAGE_SIZE) {
+ addr_t const pa = phys_addr + i;
+ _ggtt->insert_pte(pa, offset + (i / PAGE_SIZE));
+ }
+
+ return *mem;
+ }
+
+ void _unmap_dataspace_ggtt(Genode::Allocator &alloc, Genode::Dataspace_capability cap)
+ {
+ size_t const num = Genode::Dataspace_client(cap).size() / PAGE_SIZE;
+
+ auto lookup_and_free = [&] (Ggtt_mmio_mapping &m) {
+ if (!(m.cap == cap)) { return; }
+
+ _ggtt->remove_pte_range(m.offset, num);
+ Genode::destroy(&alloc, &m);
+ };
+
+ _ggtt_mmio_mapping_registry.for_each(lookup_and_free);
+ }
+
+ struct Invalid_ppgtt : Genode::Exception { };
+
+ static addr_t _ppgtt_phys_addr(Igd::Ppgtt_allocator &alloc,
+ Igd::Ppgtt const * const ppgtt)
+ {
+ void * const p = alloc.phys_addr(const_cast(ppgtt));
+ if (p == nullptr) { throw Invalid_ppgtt(); }
+ return reinterpret_cast(p);
+ }
+
+ /**********
+ ** MISC **
+ **********/
+
+ uint32_t _id_alloc()
+ {
+ static uint32_t id = 1;
+
+ uint32_t const v = id++;
+ return v << 8;
+ }
+
+ /************
+ ** ENGINE **
+ ************/
+
+ struct Execlist : Genode::Noncopyable
+ {
+ Igd::Context_descriptor _elem0;
+ Igd::Context_descriptor _elem1;
+
+ Igd::Ring_buffer _ring;
+ bool _scheduled;
+
+ Execlist(uint32_t const id, addr_t const lrca,
+ addr_t const ring, size_t const ring_size)
+ :
+ _elem0(id, lrca), _elem1(),
+ _ring(ring, ring_size), _scheduled(false)
+ { }
+
+ Igd::Context_descriptor elem0() const { return _elem0; }
+ Igd::Context_descriptor elem1() const { return _elem1; }
+
+ void schedule(int port) { _scheduled = port; }
+ int scheduled() const { return _scheduled; }
+
+ /***************************
+ ** Ring buffer interface **
+ ***************************/
+
+ void ring_reset() { _ring.reset(); }
+ Ring_buffer::Index ring_tail() const { return _ring.tail(); }
+ Ring_buffer::Index ring_head() const { return _ring.head(); }
+ Ring_buffer::Index ring_append(Igd::Cmd_header cmd) { return _ring.append(cmd); }
+
+ bool ring_avail(Ring_buffer::Index num) const { return _ring.avail(num); }
+ Ring_buffer::Index ring_max() const { return _ring.max(); }
+ void ring_reset_and_fill_zero() { _ring.reset_and_fill_zero(); }
+ void ring_update_head(Ring_buffer::Index head) { _ring.update_head(head); }
+
+ void ring_flush(Ring_buffer::Index from, Ring_buffer::Index to)
+ {
+ _ring.flush(from, to);
+ }
+
+ void ring_dump(size_t limit = 0) const { _ring.dump(limit); }
+
+ /*********************
+ ** Debug interface **
+ *********************/
+
+ void dump() { _elem0.dump(); }
+ };
+
+ template
+ struct Engine
+ {
+ enum {
+ CONTEXT_PAGES = CONTEXT::CONTEXT_PAGES,
+ RING_PAGES = CONTEXT::RING_PAGES,
+ };
+
+ Genode::Ram_dataspace_capability ctx_ds;
+ Ggtt::Mapping const &ctx_map;
+ addr_t const ctx_vaddr;
+ addr_t const ctx_gmaddr;
+
+ Genode::Ram_dataspace_capability ring_ds;
+ Ggtt::Mapping const &ring_map;
+ addr_t const ring_vaddr;
+ addr_t const ring_gmaddr;
+
+ Ppgtt_allocator &ppgtt_allocator;
+ Ppgtt *ppgtt;
+ Ppgtt_scratch *ppgtt_scratch;
+
+ Genode::Constructible context;
+ Genode::Constructible execlist;
+
+ Engine(uint32_t id,
+ Ram ctx_ds,
+ Ggtt::Mapping const &ctx_map,
+ addr_t const ctx_vaddr,
+ addr_t const ctx_gmaddr,
+ Ram ring_ds,
+ Ggtt::Mapping const &ring_map,
+ addr_t const ring_vaddr,
+ addr_t const ring_gmaddr,
+ Ppgtt_allocator &ppgtt_allocator,
+ Ppgtt *ppgtt,
+ Ppgtt_scratch *ppgtt_scratch,
+ addr_t const pml4)
+ :
+ ctx_ds(ctx_ds),
+ ctx_map(ctx_map),
+ ctx_vaddr(ctx_vaddr),
+ ctx_gmaddr(ctx_gmaddr),
+ ring_ds(ring_ds),
+ ring_map(ring_map),
+ ring_vaddr(ring_vaddr),
+ ring_gmaddr(ring_gmaddr),
+ ppgtt_allocator(ppgtt_allocator),
+ ppgtt(ppgtt),
+ ppgtt_scratch(ppgtt_scratch)
+ {
+ size_t const ring_size = RING_PAGES * PAGE_SIZE;
+
+ /* setup context */
+ context.construct(ctx_vaddr, ring_gmaddr, ring_size, pml4);
+
+ /* setup execlist */
+ execlist.construct(id, ctx_gmaddr, ring_vaddr, ring_size);
+ execlist->ring_reset();
+ }
+
+ ~Engine()
+ {
+ execlist.destruct();
+ context.destruct();
+ }
+
+ size_t ring_size() const { return RING_PAGES * PAGE_SIZE; }
+
+ addr_t hw_status_page() const { return ctx_gmaddr; }
+
+ uint64_t seqno() const {
+ Utils::clflush((uint32_t*)(ctx_vaddr + 0xc0));
+ return *(uint32_t*)(ctx_vaddr + 0xc0); }
+ };
+
+ void _fill_page(Genode::Ram_dataspace_capability ds, addr_t v)
+ {
+ uint64_t * const p = _env.rm().attach(ds);
+ for (size_t i = 0; i < Ppgtt_scratch::MAX_ENTRIES; i++) {
+ p[i] = v;
+ }
+ _env.rm().detach(p);
+ }
+
+ void _populate_scratch(Ppgtt_scratch *scratch)
+ {
+ _fill_page(scratch->pt.ds, scratch->page.addr);
+ _fill_page(scratch->pd.ds, scratch->pt.addr);
+ _fill_page(scratch->pdp.ds, scratch->pd.addr);
+ }
+
+ template
+ Engine *_alloc_engine(Genode::Allocator_guard &md_alloc, uint32_t const id)
+ {
+ /* alloc context memory */
+ size_t const ctx_offset = _ggtt->find_free(CONTEXT::CONTEXT_PAGES, true);
+ size_t const ctx_size = CONTEXT::CONTEXT_PAGES * PAGE_SIZE;
+ Ram ctx_ds = _alloc_dataspace(md_alloc, ctx_size);
+ Ggtt::Mapping const &ctx_map = _map_dataspace_ggtt(md_alloc, ctx_ds, ctx_offset);
+ addr_t const ctx_vaddr = (addr_t)_env.rm().attach(ctx_map.cap) + PAGE_SIZE /* omit GuC page */;
+ addr_t const ctx_gmaddr = (ctx_offset + 1 /* omit GuC page */) * PAGE_SIZE;
+
+ /* alloc ring memory */
+ size_t const ring_offset = _ggtt->find_free(Rcs_context::RING_PAGES, true);
+ size_t const ring_size = CONTEXT::RING_PAGES * PAGE_SIZE;
+ Ram ring_ds = _alloc_dataspace(md_alloc, ring_size);
+ Ggtt::Mapping const &ring_map = _map_dataspace_ggtt(md_alloc, ring_ds, ring_offset);
+ addr_t const ring_vaddr = _env.rm().attach(ring_map.cap);
+ addr_t const ring_gmaddr = ring_offset * PAGE_SIZE;
+
+ /* PPGTT */
+ Igd::Ppgtt_allocator *ppgtt_allocator =
+ new (&md_alloc) Igd::Ppgtt_allocator(_env.rm(), md_alloc, _pci_backend_alloc);
+
+ Igd::Ppgtt_scratch *scratch =
+ new (&md_alloc) Igd::Ppgtt_scratch(md_alloc, _pci_backend_alloc);
+ _populate_scratch(scratch);
+
+ Igd::Ppgtt *ppgtt =
+ new (ppgtt_allocator) Igd::Ppgtt(&scratch->pdp);
+
+ /* get PML4 address */
+ addr_t const ppgtt_phys_addr = _ppgtt_phys_addr(*ppgtt_allocator, ppgtt);
+ addr_t const pml4 = ppgtt_phys_addr | 1;
+
+ return new (&md_alloc) Engine(id + CONTEXT::HW_ID,
+ ctx_ds, ctx_map, ctx_vaddr, ctx_gmaddr,
+ ring_ds, ring_map, ring_vaddr, ring_gmaddr,
+ *ppgtt_allocator, ppgtt, scratch, pml4);
+ }
+
+ template
+ void _free_engine(Genode::Allocator_guard &md_alloc, Engine *engine)
+ {
+ /* free PPGTT */
+ Genode::destroy(&md_alloc, engine->ppgtt_scratch);
+ Genode::destroy(&engine->ppgtt_allocator, engine->ppgtt);
+ Genode::destroy(&md_alloc, &engine->ppgtt_allocator);
+ /* free ring memory */
+ {
+ _env.rm().detach(engine->ring_vaddr);
+ _unmap_dataspace_ggtt(md_alloc, engine->ring_ds);
+ _free_dataspace(md_alloc, engine->ring_ds);
+ size_t const offset = (engine->ring_gmaddr / PAGE_SIZE) - 1;
+ _ggtt->remove_pte_range(offset, Engine::RING_PAGES);
+ }
+ /* free context memory */
+ {
+ _env.rm().detach(engine->ctx_vaddr - PAGE_SIZE);
+ _unmap_dataspace_ggtt(md_alloc, engine->ctx_ds);
+ _free_dataspace(md_alloc, engine->ctx_ds);
+ size_t const offset = (engine->ctx_gmaddr / PAGE_SIZE) - 1;
+ _ggtt->remove_pte_range(offset, Engine::CONTEXT_PAGES);
+ }
+ /* free engine */
+ Genode::destroy(&md_alloc, engine);
+ }
+
+ /**********
+ ** Vgpu **
+ **********/
+
+ uint32_t _vgpu_avail { 0 };
+
+ struct Vgpu : Genode::Fifo::Element
+ {
+ enum {
+ APERTURE_SIZE = 32u << 20,
+ MAX_FENCES = 4,
+ };
+
+ uint32_t active_fences { 0 };
+
+ Genode::Signal_context_capability _completion_sigh;
+
+ uint64_t _current_seqno { 0 };
+
+ uint32_t const _id;
+ Engine &rcs;
+
+ Vgpu(uint32_t const id, Engine &rcs)
+ : _id(id), rcs(rcs) { }
+
+ uint32_t id() const { return _id; }
+
+ void completion_sigh(Genode::Signal_context_capability sigh) {
+ _completion_sigh = sigh; }
+
+ Genode::Signal_context_capability completion_sigh() {
+ return _completion_sigh; }
+
+ uint64_t current_seqno() const { return _current_seqno; }
+
+ uint64_t complete_seqno() const { return rcs.seqno(); }
+
+ void setup_ring_buffer(Genode::addr_t const buffer_addr,
+ Genode::addr_t const scratch_addr)
+ {
+ _current_seqno++;
+
+ Execlist &el = *rcs.execlist;
+
+ Ring_buffer::Index advance = 0;
+
+ size_t const need = 4 /* batchbuffer cmd */ + 6 /* prolog */ + 16 /* epilog + w/a */;
+ if (!el.ring_avail(need)) { el.ring_reset_and_fill_zero(); }
+
+ /* save old tail */
+ Ring_buffer::Index const tail = el.ring_tail();
+
+ /* prolog */
+ if (1)
+ {
+ enum { CMD_NUM = 6, HWS_DATA = 0xc0, };
+ Genode::uint32_t cmd[CMD_NUM] = {};
+ Igd::Pipe_control pc(CMD_NUM);
+ cmd[0] = pc.value;
+ Genode::uint32_t tmp = 0;
+ tmp |= Igd::Pipe_control::CS_STALL;
+ tmp |= Igd::Pipe_control::TLB_INVALIDATE;
+ tmp |= Igd::Pipe_control::INSTRUCTION_CACHE_INVALIDATE;
+ tmp |= Igd::Pipe_control::TEXTURE_CACHE_INVALIDATE;
+ tmp |= Igd::Pipe_control::VF_CACHE_INVALIDATE;
+ tmp |= Igd::Pipe_control::CONST_CACHE_INVALIDATE;
+ tmp |= Igd::Pipe_control::STATE_CACHE_INVALIDATE;
+ tmp |= Igd::Pipe_control::QW_WRITE;
+ tmp |= Igd::Pipe_control::GLOBAL_GTT_IVB;
+ tmp |= Igd::Pipe_control::DC_FLUSH_ENABLE;
+ tmp |= Igd::Pipe_control::INDIRECT_STATE_DISABLE;
+ tmp |= Igd::Pipe_control::MEDIA_STATE_CLEAR;
+
+ cmd[1] = tmp;
+ cmd[2] = scratch_addr;
+ cmd[3] = 0;
+ cmd[4] = 0;
+ cmd[5] = 0;
+
+ for (size_t i = 0; i < CMD_NUM; i++) {
+ advance += el.ring_append(cmd[i]);
+ }
+ }
+
+ /* batch-buffer commands */
+ if (1)
+ {
+ enum { CMD_NUM = 4, };
+ Genode::uint32_t cmd[CMD_NUM] = {};
+ Igd::Mi_batch_buffer_start mi;
+
+ cmd[0] = mi.value;
+ cmd[1] = buffer_addr & 0xffffffff;
+ cmd[2] = (buffer_addr >> 32) & 0xffff;
+ cmd[3] = 0; /* MI_NOOP */
+
+ for (size_t i = 0; i < CMD_NUM; i++) {
+ advance += el.ring_append(cmd[i]);
+ }
+ }
+
+ /* epilog */
+ if (1)
+ {
+ enum { CMD_NUM = 6, HWS_DATA = 0xc0, };
+ Genode::uint32_t cmd[CMD_NUM] = {};
+ Igd::Pipe_control pc(CMD_NUM);
+ cmd[0] = pc.value;
+ Genode::uint32_t tmp = 0;
+ tmp |= Igd::Pipe_control::CS_STALL;
+ tmp |= Igd::Pipe_control::RENDER_TARGET_CACHE_FLUSH;
+ tmp |= Igd::Pipe_control::DEPTH_CACHE_FLUSH;
+ tmp |= Igd::Pipe_control::DC_FLUSH_ENABLE;
+ tmp |= Igd::Pipe_control::FLUSH_ENABLE;
+
+ cmd[1] = tmp;
+ cmd[2] = scratch_addr;
+ cmd[3] = 0;
+ cmd[4] = 0;
+ cmd[5] = 0;
+
+ for (size_t i = 0; i < CMD_NUM; i++) {
+ advance += el.ring_append(cmd[i]);
+ }
+ }
+
+ /*
+ * IHD-OS-BDW-Vol 2d-11.15 p. 199 ff.
+ *
+ * HWS page layout dword 48 - 1023 for driver usage
+ */
+ if (1)
+ {
+ enum { CMD_NUM = 8, HWS_DATA = 0xc0, };
+ Genode::uint32_t cmd[8] = {};
+ Igd::Pipe_control pc(6);
+ cmd[0] = pc.value;
+ Genode::uint32_t tmp = 0;
+ tmp |= Igd::Pipe_control::GLOBAL_GTT_IVB;
+ tmp |= Igd::Pipe_control::CS_STALL;
+ tmp |= Igd::Pipe_control::QW_WRITE;
+ cmd[1] = tmp;
+ cmd[2] = (rcs.hw_status_page() + HWS_DATA) & 0xffffffff;
+ cmd[3] = 0; /* upper addr 0 */
+ cmd[4] = _current_seqno & 0xffffffff;
+ cmd[5] = _current_seqno >> 32;
+ Igd::Mi_user_interrupt ui;
+ cmd[6] = ui.value;
+ cmd[7] = 0; /* MI_NOOP */
+
+ for (size_t i = 0; i < CMD_NUM; i++) {
+ advance += el.ring_append(cmd[i]);
+ }
+ }
+
+ /* w/a */
+ if (1)
+ {
+ for (size_t i = 0; i < 2; i++) {
+ advance += el.ring_append(0);
+ }
+ }
+
+ addr_t const offset = (((tail + advance) * sizeof(uint32_t)) >> 3) - 1;
+ rcs.context->tail_offset(offset % ((4*4096)>>3));
+ }
+
+ void rcs_map_ppgtt(addr_t vo, addr_t pa, size_t size)
+ {
+ Genode::Page_flags pf;
+ pf.writeable = Genode::Writeable::RW;
+
+ try {
+ rcs.ppgtt->insert_translation(vo, pa, size, pf,
+ &rcs.ppgtt_allocator,
+ &rcs.ppgtt_scratch->pdp);
+ } catch (Igd::Ppgtt_allocator::Out_of_memory) {
+ throw Igd::Device::Out_of_ram();
+ } catch (...) {
+ Genode::log(__func__, ": unknown exception");
+ throw;
+ }
+ }
+
+ void rcs_unmap_ppgtt(addr_t vo, size_t size)
+ {
+ rcs.ppgtt->remove_translation(vo, size,
+ &rcs.ppgtt_allocator,
+ &rcs.ppgtt_scratch->pdp);
+ }
+ };
+
+ Vgpu* _alloc_vgpu(Genode::Allocator_guard &md_alloc)
+ {
+ uint32_t const id = _id_alloc();
+
+ Engine *rcs = _alloc_engine(md_alloc, id);
+
+ Vgpu *gpu = new (&md_alloc) Vgpu(id, *rcs);
+ _vgpu_avail--;
+ return gpu;
+ }
+
+ void _free_vgpu(Genode::Allocator_guard &md_alloc, Vgpu *vgpu)
+ {
+ if (!vgpu) { return; }
+
+ Engine *rcs = &vgpu->rcs;
+ _free_engine(md_alloc, rcs);
+
+ Genode::destroy(&md_alloc, vgpu);
+ _vgpu_avail++;
+ }
+
+ /****************
+ ** SCHEDULING **
+ ****************/
+
+ Genode::Fifo _vgpu_list;
+ Vgpu *_active_vgpu { nullptr };
+
+ bool _vgpu_already_scheduled(Vgpu &vgpu) const
+ {
+ for (Vgpu *v = _vgpu_list.head(); v; v = v->next()) {
+ if (v == &vgpu) { return true; }
+ }
+ return false;
+ }
+
+ void _submit_execlist(Engine &engine)
+ {
+ Execlist &el = *engine.execlist;
+
+ int const port = _mmio->read();
+
+ el.schedule(port);
+
+ uint32_t desc[4];
+ desc[3] = el.elem1().high();
+ desc[2] = el.elem1().low();
+ desc[1] = el.elem0().high();
+ desc[0] = el.elem0().low();
+
+ _mmio->write(desc[3]);
+ _mmio->write(desc[2]);
+ _mmio->write(desc[1]);
+ _mmio->write(desc[0]);
+ }
+
+ Vgpu *_unschedule_current_vgpu()
+ {
+ Vgpu *gpu = _vgpu_list.dequeue();
+ return gpu;
+ }
+
+ Vgpu *_current_vgpu() { return _vgpu_list.head(); }
+
+ void _schedule_current_vgpu()
+ {
+ Vgpu *gpu = _current_vgpu();
+ if (!gpu) {
+ Genode::warning("no valid vGPU for scheduling found.");
+ return;
+ }
+
+ Engine &rcs = gpu->rcs;
+
+ _mmio->flush_gfx_tlb();
+
+ /*
+ * XXX check if HWSP is shared across contexts and if not when
+ * we actually need to write the register
+ */
+ Mmio::HWS_PGA_RCSUNIT::access_t const addr = rcs.hw_status_page();
+ _mmio->write_post(addr);
+
+ _submit_execlist(rcs);
+
+ _active_vgpu = gpu;
+ _timer.trigger_once(WATCHDOG_TIMEOUT);
+ }
+
+ /**********
+ ** INTR **
+ **********/
+
+ void _clear_rcs_iir(Mmio::GT_0_INTERRUPT_IIR::access_t const v)
+ {
+ _mmio->write_post(v);
+ }
+
+ Vgpu *_last_scheduled;
+
+ void _notify_complete(Vgpu *gpu)
+ {
+ if (!gpu) { return; }
+
+ uint64_t const curr_seqno = gpu->current_seqno();
+ uint64_t const comp_seqno = gpu->complete_seqno();
+
+ if (curr_seqno != comp_seqno) {
+ Genode::error(__func__, "sequence numbers (", curr_seqno, "/", comp_seqno, ") do not match");
+ _last_scheduled = gpu;
+ return;
+ }
+
+ Execlist &el = *gpu->rcs.execlist;
+ el.ring_update_head(gpu->rcs.context->head_offset());
+
+ Genode::Signal_transmitter(gpu->completion_sigh()).submit();
+ }
+
+ 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);
+
+ Vgpu *notify_gpu = nullptr;
+ if (user_complete) { notify_gpu = _current_vgpu(); }
+
+ if (v) { _clear_rcs_iir(v); }
+
+ bool const fault_valid = _mmio->fault_regs_valid();
+ if (fault_valid) { Genode::error("FAULT_REG valid"); }
+
+ bool const csb = _mmio->csb_unread();
+ (void)csb;
+
+ _mmio->update_context_status_pointer();
+
+ if (user_complete) {
+ _unschedule_current_vgpu();
+ _active_vgpu = nullptr;
+
+ if (notify_gpu) { _notify_complete(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 **
+ ************/
+
+ /* TODO introduce Fences object, Bit_allocator */
+ enum { INVALID_FENCE = 0xff, };
+
+ uint32_t _get_free_fence()
+ {
+ return _mmio->find_free_fence();
+ }
+
+ uint32_t _update_fence(uint32_t const id,
+ addr_t const lower,
+ addr_t const upper,
+ uint32_t const pitch,
+ bool const tile_x)
+ {
+ return _mmio->update_fence(id, lower, upper, pitch, tile_x);
+ }
+
+ void _clear_fence(uint32_t const id)
+ {
+ _mmio->clear_fence(id);
+ }
+
+ /**********************
+ ** watchdog timeout **
+ **********************/
+
+ void _handle_watchdog_timeout()
+ {
+ if (!_active_vgpu) { return; }
+
+ Genode::error("watchdog triggered: engine stuck");
+ _mmio->dump();
+ _mmio->error_dump();
+ _mmio->fault_dump();
+ _mmio->execlist_status_dump();
+ Vgpu *gpu = _current_vgpu();
+ gpu = gpu ? gpu : _last_scheduled;
+ gpu->rcs.context->dump();
+ gpu->rcs.context->dump_hw_status_page();
+ Execlist const &el = *gpu->rcs.execlist;
+ el.ring_dump(52);
+
+ _device_reset_and_init();
+
+ if (_active_vgpu == gpu) {
+ _unschedule_current_vgpu();
+ }
+
+ if (_current_vgpu()) {
+ _schedule_current_vgpu();
+ }
+ }
+
+ Genode::Signal_handler _watchdog_timeout_sigh {
+ _env.ep(), *this, &Device::_handle_watchdog_timeout };
+
+ void _device_reset_and_init()
+ {
+ _mmio->reset();
+ _mmio->clear_errors();
+ _mmio->init();
+ _mmio->enable_intr();
+ _mmio->forcewake_enable();
+ }
+
+ /**
+ * Constructor
+ */
+ Device(Genode::Env &env,
+ Genode::Allocator &alloc,
+ Platform::Connection &pci,
+ Platform::Device_capability cap,
+ Genode::Xml_node config)
+ :
+ _env(env), _md_alloc(&alloc, 8192), _pci(pci), _pci_cap(cap)
+ {
+ 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
+ */
+ struct MGGC_0_2_0_PCI : Genode::Register<16>
+ {
+ struct Graphics_mode_select : Bitfield<8, 8> { };
+ struct Gtt_graphics_memory_size : Bitfield<6, 2> { };
+ struct Versatile_acceleration_mode_enable : Bitfield<3, 1> { };
+ struct Igd_vga_disable : Bitfield<2, 1> { };
+ struct Ggc_lock : Bitfield<0, 1> { };
+ };
+ enum { PCI_GMCH_CTL = 0x50, };
+ MGGC_0_2_0_PCI::access_t v = _config_read(PCI_GMCH_CTL);
+
+ {
+ log("MGGC_0_2_0_PCI");
+ log(" Graphics_mode_select: ", Hex(MGGC_0_2_0_PCI::Graphics_mode_select::get(v)));
+ log(" Gtt_graphics_memory_size: ", Hex(MGGC_0_2_0_PCI::Gtt_graphics_memory_size::get(v)));
+ log(" Versatile_acceleration_mode_enable: ", Hex(MGGC_0_2_0_PCI::Versatile_acceleration_mode_enable::get(v)));
+ log(" Igd_vga_disable: ", Hex(MGGC_0_2_0_PCI::Igd_vga_disable::get(v)));
+ log(" Ggc_lock: ", Hex(MGGC_0_2_0_PCI::Ggc_lock::get(v)));
+ }
+
+ /* map PCI resources */
+ _poke_pci_resource(GMADR);
+
+ addr_t gttmmadr_base = _map_pci_resource(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(_md_alloc, PAGE_SIZE);
+ addr_t const scratch_page = Dataspace_client(scratch_page_ds).phys_addr();
+
+ 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);
+ _ggtt->dump();
+
+ _vgpu_avail = (gmadr_size - fb_size) / Vgpu::APERTURE_SIZE;
+
+ _device_reset_and_init();
+
+ _irq.construct(_device.irq(0));
+ _irq->sigh(_irq_dispatcher);
+ _irq->ack_irq();
+
+ _mmio->dump();
+
+ _timer.sigh(_watchdog_timeout_sigh);
+ }
+
+ /*********************
+ ** Device handling **
+ *********************/
+
+ /**
+ * Reset the physical device
+ */
+ void reset() { _device_reset_and_init(); }
+
+ /**
+ * Get chip id of the phyiscal device
+ */
+ uint16_t id() const { return _info.id; }
+
+ /**
+ * Get features of the phyiscal device
+ */
+ uint32_t features() const { return _info.features; }
+
+ /*******************
+ ** Vgpu handling **
+ *******************/
+
+ /**
+ * Allocate new vGPU
+ *
+ * \param alloc resource allocator and guard
+ *
+ * \return reference to new vGPU
+ *
+ * \throw Out_of_ram
+ * \throw Out_of_caps
+ */
+ Vgpu& alloc_vgpu(Genode::Allocator_guard &alloc)
+ {
+ return *_alloc_vgpu(alloc);
+ }
+
+ /**
+ * Free vGPU
+ *
+ * \param alloc reference to resource allocator
+ * \param vgpu reference to vGPU
+ */
+ void free_vgpu(Genode::Allocator_guard &alloc, Vgpu &vgpu)
+ {
+ _free_vgpu(alloc, &vgpu);
+ }
+
+ /**
+ * Add vGPU to scheduling list
+ *
+ * \param vgpu reference to vGPU
+ *
+ * \throw Already_scheduled
+ */
+ void vgpu_enqueue(Vgpu &vgpu)
+ {
+ if (_vgpu_already_scheduled(vgpu)) { throw Already_scheduled(); }
+
+ Vgpu const *pending = _current_vgpu();
+
+ _vgpu_list.enqueue(&vgpu);
+
+ if (pending) { return; }
+
+ /* none pending, kick-off execution */
+ _schedule_current_vgpu();
+ }
+
+ /**
+ * Check if there is a vGPU slot left
+ *
+ * \return true if slot is free, otherwise false is returned
+ */
+ bool vgpu_avail() const { return _vgpu_avail; }
+
+ /**
+ * Check if vGPU is currently scheduled
+ *
+ * \return true if vGPU is scheduled, otherwise false is returned
+ */
+ bool vgpu_active(Vgpu const &vgpu) const
+ {
+ Vgpu const *curr = _vgpu_list.head();
+ if (!curr) { return false; }
+ return &vgpu == curr ? true : false;
+ }
+
+ /*********************
+ ** Buffer handling **
+ *********************/
+
+ /**
+ * Allocate DMA buffer
+ *
+ * \param guard resource allocator and guard
+ * \param size size of the DMA buffer
+ *
+ * \return DMA buffer capability
+ *
+ * \throw Out_of_memory
+ */
+ Genode::Dataspace_capability alloc_buffer(Genode::Allocator_guard &guard,
+ size_t size)
+ {
+ return _pci_backend_alloc.alloc(guard, size);
+ }
+
+ /**
+ * Free DMA buffer
+ *
+ * \param guard resource allocator and guard
+ * \param cap DMA buffer capability
+ */
+ void free_buffer(Genode::Allocator_guard &guard,
+ Genode::Dataspace_capability cap)
+ {
+ if (!cap.valid()) { return; }
+
+ _pci_backend_alloc.free(guard,
+ Genode::static_cap_cast(cap));
+ }
+
+ /**
+ * Map DMA buffer in the GGTT
+ *
+ * \param guard resource allocator and guard
+ * \param cap DMA buffer capability
+ * \param aperture true if mapping should be accessible by CPU
+ *
+ * \return GGTT mapping
+ *
+ * \throw Could_not_map_buffer
+ */
+ Ggtt::Mapping const &map_buffer(Genode::Allocator &guard,
+ Genode::Dataspace_capability cap, bool aperture)
+ {
+ size_t const size = Genode::Dataspace_client(cap).size();
+ try {
+ size_t const num = size / PAGE_SIZE;
+ Ggtt::Offset const offset = _ggtt->find_free(num, aperture);
+ return _map_dataspace_ggtt(guard, cap, offset);
+ } catch (...) {
+ throw Could_not_map_buffer();
+ }
+ }
+
+ /**
+ * Unmap DMA buffer from GGTT
+ *
+ * \param guard resource allocator and guard
+ * \param mapping GGTT mapping
+ */
+ void unmap_buffer(Genode::Allocator &guard, Ggtt::Mapping mapping)
+ {
+ _unmap_dataspace_ggtt(guard, mapping.cap);
+ }
+
+ /**
+ * Set tiling mode for GGTT region
+ *
+ * \param start offset of the GGTT start entry
+ * \param size size of the region
+ * \param mode tiling mode for the region
+ *
+ * \return id of the used fence register
+ */
+ uint32_t set_tiling(Ggtt::Offset const start, size_t const size,
+ uint32_t const mode)
+ {
+ uint32_t const id = _mmio->find_free_fence();
+ if (id == INVALID_FENCE) {
+ Genode::warning("could not find free FENCE");
+ return false;
+ }
+
+ addr_t const lower = start * PAGE_SIZE;
+ addr_t const upper = lower + size;
+ uint32_t const pitch = ((mode & 0xffff0000) >> 16) / 128 - 1;
+ bool const tilex = (mode & 0x1);
+
+ return _update_fence(id, lower, upper, pitch, tilex);
+ }
+
+ /**
+ * Clear tiling for given fence
+ *
+ * \param id id of fence register
+ */
+ void clear_tiling(uint32_t const id)
+ {
+ _clear_fence(id);
+ }
+};
+
+
+namespace Gpu {
+
+ class Session_component;
+ class Root;
+
+ using Root_component = Genode::Root_component;
+}
+
+
+class Gpu::Session_component : public Genode::Session_object
+{
+ private:
+
+ Genode::Region_map &_rm;
+ Genode::Allocator_guard _guard;
+
+ Igd::Device &_device;
+ Igd::Device::Vgpu &_vgpu;
+
+ struct Buffer
+ {
+ Genode::Dataspace_capability cap;
+ Gpu::addr_t ppgtt_va;
+
+ enum { INVALID_FENCE = 0xff, };
+ Genode::uint32_t fenced;
+
+ Igd::Ggtt::Mapping map;
+
+ Buffer(Genode::Dataspace_capability cap)
+ : cap(cap), ppgtt_va(0), fenced(INVALID_FENCE) { }
+
+ virtual ~Buffer() { }
+ };
+
+ Genode::Registry> _buffer_registry;
+
+ Genode::uint64_t seqno { 0 };
+
+ void _free_buffers()
+ {
+ auto lookup_and_free = [&] (Buffer &buffer) {
+
+ if (buffer.map.offset != Igd::Ggtt::Mapping::INVALID_OFFSET) {
+ _device.unmap_buffer(_guard, buffer.map);
+ }
+
+ if (buffer.fenced != Buffer::INVALID_FENCE) {
+ _device.clear_tiling(buffer.fenced);
+ _vgpu.active_fences--;
+ }
+
+ Genode::Dataspace_client buf(buffer.cap);
+ Genode::size_t const actual_size = buf.size();
+ _vgpu.rcs_unmap_ppgtt(buffer.ppgtt_va, actual_size);
+
+ _device.free_buffer(_guard, buffer.cap);
+ Genode::destroy(&_guard, &buffer);
+ };
+ _buffer_registry.for_each(lookup_and_free);
+ }
+
+ public:
+
+ /**
+ * Constructor
+ *
+ * \param rm region map used for attaching GPU resources
+ * \param md_alloc meta-data allocator
+ * \param ram_quota initial ram quota
+ * \param device reference to the physical device
+ */
+ Session_component(Genode::Entrypoint &ep,
+ Resources resources,
+ Label const &label,
+ Diag diag,
+ Genode::Region_map &rm,
+ Genode::Allocator &md_alloc,
+ Genode::size_t ram_quota,
+ Igd::Device &device)
+ :
+ Session_object(ep, resources, label, diag),
+ _rm(rm), _guard(&md_alloc, ram_quota),
+ _device(device), _vgpu(_device.alloc_vgpu(_guard))
+ { }
+
+ ~Session_component()
+ {
+ _free_buffers();
+ _device.free_vgpu(_guard, _vgpu);
+ }
+
+ /*********************************
+ ** Session_component interface **
+ *********************************/
+
+ void upgrade_ram_quota(Genode::size_t quota)
+ {
+ _guard.upgrade(quota);
+ }
+
+ bool vgpu_active() const
+ {
+ return _device.vgpu_active(_vgpu);
+ }
+
+ /***************************
+ ** Gpu session interface **
+ ***************************/
+
+ Info info() const override
+ {
+ Genode::size_t const aperture_size = Igd::Device::Vgpu::APERTURE_SIZE;
+ return Info(_device.id(), _device.features(), aperture_size, _vgpu.id());
+ }
+
+ void exec_buffer(Genode::Dataspace_capability cap,
+ Genode::size_t size) override
+ {
+ Igd::addr_t ppgtt_va = 0;
+
+ auto lookup = [&] (Buffer &buffer) {
+ if (!(buffer.cap == cap)) { return; }
+ ppgtt_va = buffer.ppgtt_va;
+ };
+ _buffer_registry.for_each(lookup);
+
+ if (!ppgtt_va) {
+ Genode::error("Invalid execbuffer");
+ Genode::Signal_transmitter(_vgpu.completion_sigh()).submit();
+ return;
+ }
+
+ _vgpu.setup_ring_buffer(ppgtt_va, _device._ggtt->scratch_page());
+
+ try {
+ _device.vgpu_enqueue(_vgpu);
+ } catch (Igd::Device::Already_scheduled &e) {
+ Genode::error("vGPU already scheduled");
+ }
+ }
+
+ void completion_sigh(Genode::Signal_context_capability sigh) override
+ {
+ _vgpu.completion_sigh(sigh);
+ }
+
+ Genode::Dataspace_capability alloc_buffer(Genode::size_t size) override
+ {
+ /*
+ * XXX size might not be page aligned, allocator overhead is not
+ * included, mapping costs are not included and we throw at
+ * different locations...
+ *
+ * => better construct Buffer object as whole
+ */
+ Genode::size_t const need = size + sizeof(Genode::Registered);
+ Genode::size_t const avail = _guard.quota() - _guard.consumed();
+ if (need > avail) { throw Gpu::Session_component::Out_of_ram(); }
+
+ try {
+ Genode::Dataspace_capability cap = _device.alloc_buffer(_guard, size);
+
+ try {
+ new (&_guard) Genode::Registered(_buffer_registry, cap);
+ } catch(...) {
+ _device.free_buffer(_guard, cap);
+ throw Gpu::Session_component::Out_of_ram();
+ }
+ return cap;
+ } catch (Igd::Device::Out_of_ram) {
+ throw Gpu::Session_component::Out_of_ram();
+ }
+
+ return Genode::Dataspace_capability();
+ }
+
+ void free_buffer(Genode::Dataspace_capability cap) override
+ {
+ if (!cap.valid()) { return; }
+
+ auto lookup_and_free = [&] (Buffer &buffer) {
+ if (!(buffer.cap == cap)) { return; }
+
+ if (buffer.map.offset != Igd::Ggtt::Mapping::INVALID_OFFSET) {
+ Genode::error("cannot free mapped buffer");
+ /* XXX throw */
+ }
+
+ _device.free_buffer(_guard, cap);
+ Genode::destroy(&_guard, &buffer);
+ };
+ _buffer_registry.for_each(lookup_and_free);
+ }
+
+ Genode::Dataspace_capability map_buffer(Genode::Dataspace_capability cap,
+ bool aperture) override
+ {
+ if (!cap.valid()) { return Genode::Dataspace_capability(); }
+
+ Genode::Dataspace_capability map_cap;
+
+ auto lookup_and_map = [&] (Buffer &buffer) {
+ if (!(buffer.cap == cap)) { return; }
+
+ if (buffer.map.offset != Igd::Ggtt::Mapping::INVALID_OFFSET) {
+ Genode::error("buffer already mapped");
+ return;
+ }
+
+ try {
+ Igd::Ggtt::Mapping const &map = _device.map_buffer(_guard, cap, aperture);
+ buffer.map.cap = map.cap;
+ buffer.map.offset = map.offset;
+ map_cap = buffer.map.cap;
+ } catch (Igd::Device::Could_not_map_buffer) {
+ Genode::error("could not map buffer object");
+ throw Gpu::Session::Out_of_ram();
+ }
+ };
+ _buffer_registry.for_each(lookup_and_map);
+
+ return map_cap;
+ }
+
+ void unmap_buffer(Genode::Dataspace_capability cap) override
+ {
+ if (!cap.valid()) { return; }
+
+ bool unmapped = false;
+
+ auto lookup_and_unmap = [&] (Buffer &buffer) {
+ if (!(buffer.map.cap == cap)) { return; }
+
+ if (buffer.fenced != Buffer::INVALID_FENCE) {
+ _device.clear_tiling(buffer.fenced);
+ _vgpu.active_fences--;
+ }
+
+ _device.unmap_buffer(_guard, buffer.map);
+ buffer.map.offset = Igd::Ggtt::Mapping::INVALID_OFFSET;
+ unmapped = true;
+ };
+ _buffer_registry.for_each(lookup_and_unmap);
+
+ if (!unmapped) { Genode::error("buffer not mapped"); }
+ }
+
+ bool map_buffer_ppgtt(Genode::Dataspace_capability cap,
+ Gpu::addr_t va) override
+ {
+ if (!cap.valid()) { return false; }
+
+ bool result = false;
+ auto lookup_and_map = [&] (Buffer &buffer) {
+ if (!(buffer.cap == cap)) { return; }
+
+ if (buffer.ppgtt_va != 0) {
+ Genode::error("buffer already mapped");
+ return;
+ }
+
+ try {
+ Genode::Dataspace_client buf(cap);
+ /* XXX check that actual_size matches alloc_buffer size */
+ Genode::size_t const actual_size = buf.size();
+ Genode::addr_t const phys_addr = buf.phys_addr();
+ _vgpu.rcs_map_ppgtt(va, phys_addr, actual_size);
+ buffer.ppgtt_va = va;
+ result = true;
+ } catch (Igd::Device::Could_not_map_buffer) {
+ /* FIXME do not result in Out_of_ram */
+ Genode::error("could not map buffer object into PPGTT");
+ return;
+ }
+ /* will throw below */
+ catch (Igd::Device::Out_of_ram) { return; }
+ };
+ _buffer_registry.for_each(lookup_and_map);
+
+ if (!result) { throw Gpu::Session::Out_of_ram(); }
+
+ return result;
+ }
+
+ void unmap_buffer_ppgtt(Genode::Dataspace_capability cap,
+ Gpu::addr_t va) override
+ {
+ if (!cap.valid()) {
+ Genode::error("invalid buffer capability");
+ return;
+ }
+
+ auto lookup_and_unmap = [&] (Buffer &buffer) {
+ if (!(buffer.cap == cap)) { return; }
+
+ if (buffer.ppgtt_va == 0) {
+ Genode::error("buffer not mapped");
+ return;
+ }
+
+ if (buffer.ppgtt_va != va) {
+ Genode::error("buffer not mapped at ", Genode::Hex(va));
+ return;
+ }
+
+ Genode::Dataspace_client buf(cap);
+ Genode::size_t const actual_size = buf.size();
+ _vgpu.rcs_unmap_ppgtt(va, actual_size);
+ buffer.ppgtt_va = 0;
+ };
+ _buffer_registry.for_each(lookup_and_unmap);
+ }
+
+ bool set_tiling(Genode::Dataspace_capability cap,
+ Genode::uint32_t const mode) override
+ {
+ if (_vgpu.active_fences > Igd::Device::Vgpu::MAX_FENCES) {
+ Genode::error("no free fences left, already active: ", _vgpu.active_fences);
+ return false;
+ }
+
+ Buffer *b = nullptr;
+ auto lookup = [&] (Buffer &buffer) {
+ if (!(buffer.map.cap == cap)) { return; }
+ b = &buffer;
+ };
+ _buffer_registry.for_each(lookup);
+
+ if (b == nullptr) {
+ Genode::error("attempt to set tiling for non-mapped buffer");
+ return false;
+ }
+
+ 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;
+ }
+};
+
+
+class Gpu::Root : public Gpu::Root_component
+{
+ private:
+
+ Genode::Env &_env;
+ Igd::Device *_device;
+
+ Genode::size_t _ram_quota(char const *args)
+ {
+ return Genode::Arg_string::find_arg(args, "ram_quota").ulong_value(0);
+ }
+
+ protected:
+
+ Session_component *_create_session(char const *args)
+ {
+ if (!_device || !_device->vgpu_avail()) {
+ throw Genode::Service_denied();
+ }
+
+ /* at the moment we just need about ~160KiB for initial RCS bring-up */
+ Genode::size_t const required_quota = Gpu::Session::REQUIRED_QUOTA / 2;
+ Genode::size_t const ram_quota = _ram_quota(args);
+
+ if (ram_quota < required_quota) {
+ Genode::Session_label const label = Genode::label_from_args(args);
+ Genode::warning("insufficient dontated ram_quota (", ram_quota,
+ " bytes), require ", required_quota, " bytes ",
+ " by '", label, "'");
+ throw Session::Out_of_ram();
+ }
+
+ try {
+ using namespace Genode;
+
+ return new (md_alloc())
+ Session_component(_env.ep(),
+ session_resources_from_args(args),
+ session_label_from_args(args),
+ session_diag_from_args(args),
+ _env.rm(), *md_alloc(), ram_quota,
+ *_device);
+ } catch (...) { throw Genode::Service_denied(); }
+ }
+
+ void _upgrade_session(Session_component *s, char const *args)
+ {
+ s->upgrade_ram_quota(_ram_quota(args));
+
+ /*
+ s->Ram_quota_guard::upgrade(ram_quota_from_args(args));
+ s->Cap_quota_guard::upgrade(cap_quota_from_args(args));
+ */
+ }
+
+ void _destroy_session(Session_component *s)
+ {
+ if (s->vgpu_active()) {
+ Genode::warning("vGPU active, reset device and hope for the best");
+ _device->reset();
+ }
+ Genode::destroy(md_alloc(), s);
+ }
+
+ public:
+
+ Root(Genode::Env &env, Genode::Allocator &alloc)
+ : Root_component(env.ep(), alloc), _env(env), _device(nullptr) { }
+
+ void manage(Igd::Device &device) { _device = &device; }
+};
+
+
+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 **
+ *********/
+
+ 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;
+
+ Main(Genode::Env &env)
+ :
+ _env(env), _pci(env), _device_md_alloc(_env.ram(), _env.rm())
+ {
+ /* initial donation for device pd */
+ _pci.upgrade_ram(1024*1024);
+
+ _pci_cap = _find_gpu_device();
+ if (!_pci_cap.valid() || !_mch_enabled()) {
+ throw Igd::Device::Initialization_failed();
+ }
+
+ try {
+ _device.construct(_env, _device_md_alloc, _pci, _pci_cap,
+ _config_rom.xml());
+ } catch (...) {
+ _env.parent().exit(1);
+ return;
+ }
+
+ _gpu_root.manage(*_device);
+ _env.parent().announce(_env.ep().manage(_gpu_root));
+ }
+
+ ~Main() { _pci.release_device(_pci_cap); }
+};
+
+
+void Component::construct(Genode::Env &env) { static Main main(env); }
+
+
+Genode::size_t Component::stack_size() { return 32UL*1024*sizeof(long); }
diff --git a/repos/os/src/drivers/gpu/intel/mmio.h b/repos/os/src/drivers/gpu/intel/mmio.h
new file mode 100644
index 000000000..2bd3bc000
--- /dev/null
+++ b/repos/os/src/drivers/gpu/intel/mmio.h
@@ -0,0 +1,1310 @@
+/*
+ * \brief Broadwell MMIO definitions
+ * \author Josef Soentgen
+ * \data 2017-03-15
+ */
+
+/*
+ * Copyright (C) 2017 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 _MMIO_H_
+#define _MMIO_H_
+
+/* Genode includes */
+#include
+#include
+
+/* local includes */
+#include
+
+
+namespace Igd {
+
+ class Mmio;
+}
+
+
+class Igd::Mmio : public Genode::Mmio
+{
+ public:
+
+ enum {
+ /*
+ * XXX IDs are taken from Linux, still looking
+ * for a reference in the documentation
+ */
+ RCS_ID = 0,
+ VCS_ID = 1,
+ BCS_ID = 2,
+ VECS_ID = 3,
+ VCS2_ID = 4,
+
+ NUM_ENGINES = 5,
+ NUM_FENCES = 32,
+
+ SIZE = 2u<<20,
+ };
+
+ static char const *engine_name(int id)
+ {
+ switch (id) {
+ case RCS_ID: return "RCS";
+ case VCS_ID: return "VCS";
+ case BCS_ID: return "BCS";
+ case VECS_ID: return "VECS";
+ case VCS2_ID: return "VCS2";
+ }
+ return "";
+ }
+
+ /*************************
+ ** Interrupt registers **
+ *************************/
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 953 ff.
+ */
+ struct MASTER_INT_CTL : Register<0x44200, 32>
+ {
+ struct Master_interrupt_enable : Bitfield<31, 1> { };
+ struct Pcu_interrupts_pending : Bitfield<30, 1> { };
+ 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_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 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> { };
+ };
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 658 ff.
+ */
+ template
+ struct GT_0_INTERRUPT : Register
+ {
+ using B = Register;
+
+ struct Bcs_wait_on_semaphore : B::template Bitfield<27, 1> { };
+ struct Bcs_ctx_switch_interrupt : B::template Bitfield<24, 1> { };
+ struct Bcs_mi_flush_dw_notify : B::template Bitfield<20, 1> { };
+ struct Bcs_error_interrupt : B::template Bitfield<19, 1> { };
+ struct Bcs_mi_user_interrupt : B::template Bitfield<16, 1> { };
+
+ struct Cs_wait_on_semaphore : B::template Bitfield<11, 1> { };
+ struct Cs_l3_counter_slave : B::template Bitfield<10, 1> { };
+ struct Cs_ctx_switch_interrupt : B::template Bitfield< 8, 1> { };
+ struct Page_fault_error : B::template Bitfield< 7, 1> { };
+ struct Cs_watchdog_counter_expired : B::template Bitfield< 6, 1> { };
+ struct L3_parity_error : B::template Bitfield< 5, 1> { };
+ struct Cs_pipe_control_notify : B::template Bitfield< 4, 1> { };
+ struct Cs_error_interrupt : B::template Bitfield< 3, 1> { };
+ struct Cs_mi_user_interrupt : B::template Bitfield< 0, 1> { };
+ };
+
+ struct GT_0_INTERRUPT_ISR : GT_0_INTERRUPT<0x44300> { };
+ struct GT_0_INTERRUPT_IMR : GT_0_INTERRUPT<0x44304> { };
+ struct GT_0_INTERRUPT_IIR : GT_0_INTERRUPT<0x44308> { };
+ struct GT_0_INTERRUPT_IER : GT_0_INTERRUPT<0x4430C> { };
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 673 ff.
+ */
+ template
+ struct GT_1_INTERRUPT : Register
+ {
+ using B = Register;
+
+ struct Vcs2_wait_on_semaphore : B::template Bitfield<27, 1> { };
+ struct Vcs2_ctx_switch_interrupt : B::template Bitfield<24, 1> { };
+ struct Vcs2_watchdog_counter_expired : B::template Bitfield<22, 1> { };
+ struct Vcs2_mi_flush_dw_notify : B::template Bitfield<20, 1> { };
+ struct Vcs2_error_interrupt : B::template Bitfield<19, 1> { };
+ struct Vcs2_mi_user_interrupt : B::template Bitfield<16, 1> { };
+
+ struct Vcs1_wait_on_semaphore : B::template Bitfield<11, 1> { };
+ struct Vcs1_ctx_switch_interrupt : B::template Bitfield< 8, 1> { };
+ struct Vcs1_watchdog_counter_expired : B::template Bitfield< 6, 1> { };
+ struct Vcs1_pipe_control_notify : B::template Bitfield< 4, 1> { };
+ struct Vcs1_error_interrupt : B::template Bitfield< 3, 1> { };
+ struct Vcs1_mi_user_interrupt : B::template Bitfield< 0, 1> { };
+ };
+
+ struct GT_1_INTERRUPT_ISR : GT_1_INTERRUPT<0x44310> { };
+ struct GT_1_INTERRUPT_IMR : GT_1_INTERRUPT<0x44314> { };
+ struct GT_1_INTERRUPT_IIR : GT_1_INTERRUPT<0x44318> { };
+ struct GT_1_INTERRUPT_IER : GT_1_INTERRUPT<0x4431C> { };
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 687 ff.
+ */
+ template
+ struct GT_2_INTERRUPT : Register
+ {
+ using B = Register;
+
+ struct Unslice_frequency_control_up_interrupt : B::template Bitfield<13, 1> { };
+ struct Unslice_frequency_control_down_interrupt : B::template Bitfield<12, 1> { };
+ struct Nfafdl_frequency_up_interrupt : B::template Bitfield<11, 1> { };
+ struct Nfafdl_frequency_down_interrupt : B::template Bitfield<10, 1> { };
+ struct Gtpm_engines_idle_interrupt : B::template Bitfield< 8, 1> { };
+ struct Gtpm_uncore_to_core_trap_interrupt : B::template Bitfield< 7, 1> { };
+ struct Gtpm_render_frequency_downwards_timeout_during_rc6_interrupt : B::template Bitfield< 6, 1> { };
+ struct Gtpm_render_p_state_up_threshold_interrupt : B::template Bitfield< 5, 1> { };
+ struct Gtpm_render_p_state_down_threshold_interrupt : B::template Bitfield< 4, 1> { };
+ struct Gtpm_render_geyserville_up_evaluation_interval_interrupt : B::template Bitfield< 2, 1> { };
+ struct Gtpm_render_geyserville_down_evaluation_interval_interrupt : B::template Bitfield< 1, 1> { };
+ };
+
+ struct GT_2_INTERRUPT_ISR : GT_2_INTERRUPT<0x44320> { };
+ struct GT_2_INTERRUPT_IMR : GT_2_INTERRUPT<0x44324> { };
+ struct GT_2_INTERRUPT_IIR : GT_2_INTERRUPT<0x44328> { };
+ struct GT_2_INTERRUPT_IER : GT_2_INTERRUPT<0x4432C> { };
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 689 ff.
+ */
+ template
+ struct GT_3_INTERRUPT : Register
+ {
+ using B = Register;
+
+ struct Performance_monitoring_buffer_half_full_interrupt : B::template Bitfield<28, 1> { };
+ struct Vecs_wait_on_semaphore : B::template Bitfield<11, 1> { };
+ struct Vecs_ctx_switch_interrupt : B::template Bitfield< 8, 1> { };
+ struct Vecs_mi_flush_dw_notify : B::template Bitfield< 4, 1> { };
+ struct Vecs_error_interrupt : B::template Bitfield< 3, 1> { };
+ struct Vecs_mi_user_interrupt : B::template Bitfield< 0, 1> { };
+ };
+
+ struct GT_3_INTERRUPT_ISR : GT_3_INTERRUPT<0x44330> { };
+ struct GT_3_INTERRUPT_IMR : GT_3_INTERRUPT<0x44334> { };
+ struct GT_3_INTERRUPT_IIR : GT_3_INTERRUPT<0x44338> { };
+ struct GT_3_INTERRUPT_IER : GT_3_INTERRUPT<0x4433C> { };
+
+ /**************************
+ ** Page-table registers **
+ **************************/
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 1169
+ */
+ struct PAT_INDEX : Genode::Register<32>
+ {
+ struct Class_of_service : Bitfield<8, 2> { };
+ struct Lru_age : Bitfield<4, 2>
+ {
+ enum {
+ UCR = 0b00,
+ A0 = 0b01,
+ DNC = 0b10,
+ A3 = 0b11,
+ };
+ };
+ struct Target_cache : Bitfield<2, 2>
+ {
+ enum {
+ ELLC = 0b00,
+ LLC = 0b01,
+ LLCELLC = 0b10,
+ LLCeLLC = 0b11,
+ };
+ };
+ struct Mem_type : Bitfield<0, 2>
+ {
+ enum {
+ UC = 0b00,
+ WC = 0b01,
+ WT = 0b10,
+ WB = 0b11,
+ };
+ };
+ };
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 1170
+ */
+ struct PAT_INDEX_H : Register<0x040E0, 32> { };
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 1071
+ */
+ struct PAT_INDEX_L : Register<0x040E4, 32> { };
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 1516 ff.
+ */
+ struct TIMESTAMP_CTR : Register<0x44070, 32> { };
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 75
+ *
+ * TODO registers are not continuous
+ */
+ struct FAULT_REG : Register_array<0x04000, 32, NUM_ENGINES, 32>
+ {
+ struct Engine_ID : Bitfield<12,3>
+ {
+ enum EID {
+ GFX = 0,
+ MFX0 = 1,
+ MFX1 = 2,
+ VEBX = 3,
+ BLT = 4
+ };
+ };
+ struct SRCID : Bitfield< 3,8> { };
+ struct Fault_Type : Bitfield< 1,2>
+ {
+ enum GFX_FT {
+ INVALID_PTE = 0,
+ INVALID_PDE = 1,
+ INVALID_PDPE = 2,
+ INVALID_PML4E = 3
+ };
+ };
+ struct Valid_Bit : Bitfield<0,1> { };
+ };
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 446
+ */
+ struct FAULT_TLB_RB_DATA0 : Register<0x04B10, 32>
+ {
+ struct Fault_cycle_va : Bitfield<0, 32> { }; /* 43:12 */
+ };
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 447
+ */
+ struct FAULT_TLB_RB_DATA1 : Register<0x04B14, 32>
+ {
+ struct Cycle_gtt_sel : Bitfield<4, 1> { };
+ struct Fault_cycle_va : Bitfield<0, 4> { }; /* 47:44 */
+ };
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 1353 ff.
+ */
+ struct RING_BUFFER_TAIL_RCSUNIT : Register<0x02030, 32>
+ {
+ struct Tail_Offset : Bitfield< 3, 18> { };
+ };
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 1350 ff.
+ */
+ struct RING_BUFFER_HEAD_RCSUNIT : Register<0x02034, 32>
+ {
+ struct Wrap_Count : Bitfield<21, 11> { };
+ struct Head_Offset : Bitfield< 2, 19> { };
+ };
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 1352
+ */
+ struct RING_BUFFER_START_RCSUNIT : Register<0x02038, 32>
+ {
+ struct Starting_Address : Bitfield<12, 20> { };
+ };
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 1345 ff.
+ */
+ struct RING_BUFFER_CTL_RCSUNIT : Register<0x0203C, 32>
+ {
+ struct Buffer_length : Bitfield<12, 9> { };
+ struct RBWait : Bitfield<11, 1> { };
+ struct Semaphore_wait : Bitfield<10, 1> { };
+ struct Automatic_report_head_pointer : Bitfield< 1, 2>
+ {
+ enum {
+ MI_AUTOREPORT_OFF = 0,
+ MI_AUTOREPORT_64KB = 1,
+ MI_AUTOREPORT_4KB = 2,
+ MI_AUTO_REPORT_128KB = 3
+ };
+ };
+ struct Ring_buffer_enable : Bitfield< 0, 1> { };
+ };
+
+
+ /*********************
+ ** Debug registers **
+ *********************/
+
+ /*
+ * Ancient (2008) Volume 1: Graphics Core p. 228
+ */
+ struct IPEIR : Register<0x2064, 32>
+ {
+ struct Batch_buffer_error : Bitfield<3, 1> { };
+ };
+
+ /*
+ * Ancient (2008) Volume 1: Graphics Core p. 229
+ */
+ struct IPEHR : Register<0x2068, 32> { };
+
+ /*
+ * Ancient (2008) Volume 1: Graphics Core p. 229
+ */
+ template
+ struct INSTDONE_BASE : Register
+ {
+ enum { DEFAULT_VALUE = 0xffe7fffe, };
+ };
+
+ struct RCS_INSTDONE : INSTDONE_BASE<0x2000>
+ {
+ struct Row_0_eu_0_done : Bitfield<31, 1> { };
+ struct Row_0_eu_1_done : Bitfield<30, 1> { };
+ struct Row_0_eu_2_done : Bitfield<29, 1> { };
+ struct Row_0_eu_3_done : Bitfield<28, 1> { };
+ struct Row_1_eu_0_done : Bitfield<27, 1> { };
+ struct Row_1_eu_1_done : Bitfield<26, 1> { };
+ struct Row_1_eu_2_done : Bitfield<25, 1> { };
+ struct Row_1_eu_3_done : Bitfield<24, 1> { };
+ struct Sf_done : Bitfield<23, 1> { };
+ struct Se_done : Bitfield<22, 1> { };
+ struct Windower_done : Bitfield<21, 1> { };
+ struct Reserved1 : Bitfield<20, 1> { };
+ struct Reserved2 : Bitfield<19, 1> { };
+ struct Dip_done : Bitfield<18, 1> { };
+ struct Pl_done : Bitfield<17, 1> { };
+ struct Dg_done : Bitfield<16, 1> { };
+ struct Qc_done : Bitfield<15, 1> { };
+ struct Ft_done : Bitfield<14, 1> { };
+ struct Dm_done : Bitfield<13, 1> { };
+ struct Sc_done : Bitfield<12, 1> { };
+ struct Fl_done : Bitfield<11, 1> { };
+ struct By_done : Bitfield<10, 1> { };
+ struct Ps_done : Bitfield< 9, 1> { };
+ struct Cc_done : Bitfield< 8, 1> { };
+ struct Map_fl_done : Bitfield< 7, 1> { };
+ struct Map_l2_idle : Bitfield< 6, 1> { };
+ struct Msg_arb_0_done : Bitfield< 5, 1> { };
+ struct Msg_arb_1_done : Bitfield< 4, 1> { };
+ struct Ic_row_0_done : Bitfield< 3, 1> { };
+ struct Ic_row_1_done : Bitfield< 2, 1> { };
+ struct Cp_done : Bitfield< 1, 1> { };
+ struct Ring_0_enable : Bitfield< 0, 1> { };
+ };
+
+ /*
+ * Ancient (2008) Volume 1: Graphics Core p. 229
+ */
+ template
+ struct INSTDONE_1_BASE : Register
+ {
+ enum { DEFAULT_VALUE = 0x0, };
+ };
+
+ struct RCS_INSTDONE_1 : INSTDONE_1_BASE<0x2000> { };
+
+ /*
+ * Ancient (2008) Volume 1: Graphics Core p. 231
+ */
+ template
+ struct ACTHD_BASE : Register
+ {
+ using B = Register;
+
+ struct Head_pointer : B::template Bitfield<2, 30> { };
+ struct Reserved : B::template Bitfield<0, 2> { }; /* MBZ */
+ };
+
+ struct RCS_ACTHD : ACTHD_BASE<0x2000> { };
+
+ /*
+ * Ancient (2008) Volume 1: Graphics Core p. 205
+ */
+ struct PGTBL_ER : Register<0x2024, 32> { };
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 618 ff.
+ */
+ struct GFX_MODE : Register<0x0229C, 32>
+ {
+ struct Mask_bits : Bitfield<16, 16> { };
+
+ struct Execlist_enable_mask : Bitfield<31, 1> { };
+ struct Execlist_enable : Bitfield<15, 1> { };
+
+ struct Ppgtt_enable_mask : Bitfield<25, 1> { };
+ struct Ppgtt_enable : Bitfield< 9, 1> { };
+
+ struct Virtual_addressing_enable_mask : Bitfield<23, 1> { };
+ struct Virtual_addressing_enable : Bitfield< 7, 1> { };
+
+ struct Privilege_check_disable_mask : Bitfield<16, 1> { };
+ struct Privilege_check_disable : Bitfield< 0, 1> { };
+ };
+
+ /*
+ * IHD-OS-BDW-Vol 6-11.15 p. 19 ff.
+ * IHD-OS-BDW-Vol 7-11.15 p. 42
+ */
+ struct RCS_RING_CONTEXT_STATUS_PTR : Register<0x023A0, 32>
+ {
+ struct Read_pointer_mask : Bitfield<24, 8> { };
+ struct Write_pointer_mask : Bitfield<16, 8> { };
+ struct Read_pointer : Bitfield< 8, 8> { };
+ struct Write_pointer : Bitfield< 0, 8> { };
+ };
+
+ /*********************
+ ** Error registers **
+ *********************/
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 413
+ */
+ template
+ struct EIR_BASE : Register
+ {
+ using B = Register;
+
+ struct Reserved : B::template Bitfield<16, 16> { }; /* MBZ */
+ struct Error_identity_bits : B::template Bitfield< 0, 16> { };
+ };
+
+ struct RCS_EIR : EIR_BASE<0x2000> { };
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 414
+ */
+ template
+ struct EMR_BASE : Register
+ {
+ using B = Register;
+
+ struct Reserved : B::template Bitfield<8, 24> { }; /* MBO */
+ struct Error_mask_bits : B::template Bitfield<0, 8> { };
+
+ struct Error_page_table : B::template Bitfield<4, 1> { };
+ struct Error_mem_refresh : B::template Bitfield<1, 1> { };
+ };
+
+ struct RCS_EMR : EMR_BASE<0x2000> { };
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 415 ff.
+ */
+ /* struct ERR */
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 417
+ */
+ template
+ struct ESR_BASE : Register
+ {
+ using B = Register;
+
+ struct Reserved : B::template Bitfield<16, 16> { }; /* MBZ */
+ struct Error_status_bits : B::template Bitfield< 0, 16> { };
+ };
+
+ struct RCS_ESR : ESR_BASE<0x2000> { };
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 944 ff.
+ */
+ struct ERROR : Register<0x040A0, 32>
+ {
+ struct Ctx_fault_ctxt_not_prsnt_err : Bitfield<15, 1> { }; /* typo in PRM (prsmt) */
+ struct Ctx_fault_root_not_prsnt_err : Bitfield<14, 1> { }; /* " */
+ struct Ctx_fault_pasid_not_prsnt_err : Bitfield<13, 1> { };
+ struct Ctx_fault_pasid_ovflw_err : Bitfield<12, 1> { };
+ struct Ctx_fault_pasid_dis_err : Bitfield<11, 1> { };
+
+ struct Rstrm_fault_nowb_atomic_err : Bitfield<10, 1> { };
+ struct Unloaded_pd_error : Bitfield< 8, 1> { };
+ struct Hws_page_fault_error : Bitfield< 3, 1> { };
+ struct Invalid_page_directory_entry_error : Bitfield< 2, 1> { };
+ struct Ctx_page_fault_error : Bitfield< 1, 1> { };
+ struct Tlb_fault_error : Bitfield< 0, 1> { };
+ };
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 948
+ */
+ struct ERROR_2 : Register<0x040A4, 32>
+ {
+ struct Tlbpend_reg_faultcnt : Bitfield< 0, 6> { };
+ };
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 1237
+ */
+ struct PP_PFD : Register<0x04580, 32>
+ {
+ struct Fault_entry_page_address : Bitfield<12, 20> { };
+ };
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 1315 ff.
+ */
+ struct RC_CTRL0 : Register<0x0A090, 32> { };
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 1317 ff.
+ */
+ struct RC_CTRL1 : Register<0x0A094, 32>
+ {
+ struct Rc_state : Bitfield<18, 1> { };
+ };
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 1095
+ */
+ struct NDE_RSTWRN_OPT : Register<0x46408, 32>
+ {
+ struct Rst_pch_handshake_en : Bitfield<4, 1> { };
+ };
+
+ /*
+ * XXX documentation reference missing
+ */
+ struct PWR_WELL_CTL2 : Register<0x45404, 32>
+ {
+ struct Misc_io_power_state : Bitfield< 0, 1> { };
+ struct Misc_io_power_request : Bitfield< 1, 1> { };
+ struct Ddi_a_and_ddi_e_io_power_state : Bitfield< 2, 1> { };
+ struct Ddi_a_and_ddi_e_io_power_request : Bitfield< 3, 1> { };
+ struct Ddi_b_io_power_state : Bitfield< 4, 1> { };
+ struct Ddi_b_io_power_request : Bitfield< 5, 1> { };
+ struct Ddi_c_io_power_state : Bitfield< 6, 1> { };
+ struct Ddi_c_io_power_request : Bitfield< 7, 1> { };
+ struct Ddi_d_io_power_state : Bitfield< 8, 1> { };
+ struct Ddi_d_io_power_request : Bitfield< 9, 1> { };
+ struct Power_well_1_state : Bitfield<28, 1> { };
+ struct Power_well_1_request : Bitfield<29, 1> { };
+ struct Power_well_2_state : Bitfield<30, 1> { };
+ struct Power_well_2_request : Bitfield<31, 1> { };
+ };
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 853
+ */
+ struct L3_LRA_1_GPGPU : Register<0x04DD4, 32> { };
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 711
+ */
+ struct HWSTAM : Register<0x02098, 32> { };
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 712
+ */
+ struct HWS_PGA_RCSUNIT : Register<0x02080, 32> { };
+ struct HWS_PGA_VCSUNIT0 : Register<0x12080, 32> { };
+ struct HWS_PGA_VECSUNIT : Register<0x1A080, 32> { };
+ struct HWS_PGA_VCSUNIT1 : Register<0x1C080, 32> { };
+ struct HWS_PGA_BCSUNIT : Register<0x22080, 32> { };
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 1370
+ */
+ struct RP_FREQ_NORMAL : Register<0x0A008, 32>
+ {
+ struct Turbo_disable : Bitfield<31, 1> { };
+ };
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 1318
+ */
+ struct RP_CTRL : Register<0xA024, 32> { };
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 493
+ */
+ /*
+ reg_post is in the same cacheline as FORCEWAKE and is
+ read after FORCEWAKE was written to prevent reordering
+ when reading from FORCEWAKE_ACK_HSW [1] (older driver
+ version used a different register for posting reads but
+ that did not work on certain older (~2012) machines.
+
+ [1] https://lists.freedesktop.org/archives/intel-gfx/2015-January/058101.html
+ */
+ enum { FORCEWAKE_ID_RENDER = 0, };
+ struct FORCEWAKE_MT : Register<0x0A188, 32>
+ {
+ enum {
+ RESET = 0xffff0000u,
+ };
+
+ struct Multiple_force_wake_mask : Bitfield<16, 16> { };
+
+ struct Force_wake_request_for_thread_15 : Bitfield<15, 1> { };
+ struct Force_wake_request_for_thread_14 : Bitfield<14, 1> { };
+ struct Force_wake_request_for_thread_13 : Bitfield<13, 1> { };
+ struct Force_wake_request_for_thread_12 : Bitfield<12, 1> { };
+ struct Force_wake_request_for_thread_11 : Bitfield<11, 1> { };
+ struct Force_wake_request_for_thread_10 : Bitfield<10, 1> { };
+ struct Force_wake_request_for_thread_9 : Bitfield< 9, 1> { };
+ struct Force_wake_request_for_thread_8 : Bitfield< 8, 1> { };
+ struct Force_wake_request_for_thread_7 : Bitfield< 7, 1> { };
+ struct Force_wake_request_for_thread_6 : Bitfield< 6, 1> { };
+ struct Force_wake_request_for_thread_5 : Bitfield< 5, 1> { };
+ struct Force_wake_request_for_thread_4 : Bitfield< 4, 1> { };
+ struct Force_wake_request_for_thread_3 : Bitfield< 3, 1> { };
+ struct Force_wake_request_for_thread_2 : Bitfield< 2, 1> { };
+ struct Force_wake_request_for_thread_1 : Bitfield< 1, 1> { };
+ struct Force_wake_request_for_thread_0 : Bitfield< 0, 1> { };
+ };
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 703
+ *
+ * also known as FORCEWAKE_ACK_HSW in the Linux driver
+ */
+ struct GTSP1 : Register<0x130044, 32> { };
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 606
+ *
+ * also known as ECOBUS in the Linux driver
+ */
+ struct MISC_CTRL0 : Register<0x0A180, 32> { };
+
+ /*
+ * Reset control registers
+ *
+ * adds offset to given BASE address
+ */
+ template
+ struct RESET_CTRL_BASE : Register
+ {
+ using B = Register;
+
+ struct Mask_bits : B::template Bitfield<16, 15> { };
+ struct Ready_for_reset : B::template Bitfield< 1, 1> { };
+ struct Request_reset : B::template Bitfield< 0, 1> { };
+ };
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 288
+ */
+ struct CS_RESET_CTRL : RESET_CTRL_BASE<0x02000> { };
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 165
+ */
+ struct BCS_RESET_CTRL : RESET_CTRL_BASE<0x22000> { };
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 609 ff.
+ */
+ struct GDRST : Register<0x0941C, 32>
+ {
+ struct Graphics_media1_soft_reset_ctl : Bitfield<6, 1> { };
+ struct Graphics_widi_soft_reset_ctl : Bitfield<6, 1> { };
+ struct Graphics_vebox_soft_reset_ctl : Bitfield<4, 1> { };
+ struct Graphics_blitter_soft_reset_ctl : Bitfield<3, 1> { };
+ struct Graphics_media_soft_reset_ctl : Bitfield<2, 1> { };
+ struct Graphics_render_soft_reset_ctl : Bitfield<1, 1> { };
+ struct Graphics_full_soft_reset_ctl : Bitfield<0, 1> { };
+ };
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 611 ff.
+ */
+ struct FENCE_REG : Register_array<0x100000, 64, NUM_FENCES, 64>
+ {
+ struct Fence_upper_bound : Bitfield<44, 20> { };
+ struct Fence_pitch : Bitfield<32, 11> { };
+ struct Fence_lower_bound : Bitfield<12, 20> { };
+ struct Tile_walk : Bitfield< 1, 1> { };
+ struct Fence_valid : Bitfield< 0, 1> { };
+ };
+
+ /*
+ * IHD-OS-BDW-Vol 12-11.15 p. 5
+ */
+ struct GFX_FLSH_CNTL : Register<0x101008, 32> { };
+
+
+ /************************
+ ** EXECLIST registers **
+ ************************/
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 435
+ */
+ template
+ struct EXECLIST_STATUS_BASE : Register
+ {
+ using B = Register;
+
+ struct Current_context_id : B::template Bitfield<32, 32> { };
+ struct Arbitration_enable : B::template Bitfield<16, 1> { };
+ struct Current_active_element_status : B::template Bitfield<14, 2>
+ {
+ enum {
+ NO_ACTIVE_ELEMENT = 0b00,
+ ELEMENT_0_ACTIVE = 0b01,
+ ELEMENT_1_ACTIVE = 0b10,
+ RESERVERD = 0b11,
+ };
+ };
+ struct Last_context_switch_reason : B::template Bitfield< 5, 9> { };
+ struct Execlist_0_valid : B::template Bitfield< 4, 1> { };
+ struct Execlist_1_valid : B::template Bitfield< 3, 1> { };
+ struct Execlist_queue_full : B::template Bitfield< 2, 1> { };
+ struct Execlist_write_pointer : B::template Bitfield< 1, 1> { };
+ struct Current_execlist_pointer : B::template Bitfield< 0, 1> { };
+ };
+
+ struct EXECLIST_STATUS_RSCUNIT : EXECLIST_STATUS_BASE<0x02000> { };
+ struct EXECLIST_STATUS_VCSUNIT0 : EXECLIST_STATUS_BASE<0x12000> { };
+ struct EXECLIST_STATUS_VECSUNIT : EXECLIST_STATUS_BASE<0x1A000> { };
+ struct EXECLIST_STATUS_VCSUNIT1 : EXECLIST_STATUS_BASE<0x1C000> { };
+ struct EXECLIST_STATUS_BSCUNIT : EXECLIST_STATUS_BASE<0x22000> { };
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 438
+ */
+ template
+ struct EXECLIST_SUBMITPORT_BASE : Register
+ {
+ using B = Register;
+
+ struct Context_descriptor_dw : B::template Bitfield<0, 32> { };
+ };
+
+ struct EXECLIST_SUBMITPORT_RSCUNIT : EXECLIST_SUBMITPORT_BASE<0x02000> { };
+ struct EXECLIST_SUBMITPORT_VCSUNIT0 : EXECLIST_SUBMITPORT_BASE<0x12000> { };
+ struct EXECLIST_SUBMITPORT_VECSUNIT : EXECLIST_SUBMITPORT_BASE<0x1A000> { };
+ struct EXECLIST_SUBMITPORT_VCSUNIT1 : EXECLIST_SUBMITPORT_BASE<0x1C000> { };
+ struct EXECLIST_SUBMITPORT_BSCUNIT : EXECLIST_SUBMITPORT_BASE<0x22000> { };
+
+ /*
+ * IHD-OS-BDW-Vol 2c-11.15 p. 266 ff.
+ * IHD-OS-BDW-Vol 6-11.15 p. 19 ff.
+ */
+ enum {
+ CTXT_ST_BUF_QW_NUM = 8,
+ CTXT_ST_BUF_NUM = 6,
+ };
+ template
+ struct CTXT_ST_BUF_BASE : Register_array
+ {
+ using B = Register_array;
+
+ /*
+ * Judging by the documentation it seems that one context status
+ * buffer in fact contains the same DWORD twice.
+ */
+ struct Context_status_udw : B::template Bitfield<32, 32> { };
+ struct Context_status_ldw : B::template Bitfield< 0, 32> { };
+ };
+
+ struct CTXT_ST_BUF_RCSUNIT : CTXT_ST_BUF_BASE<0x2000> { };
+
+ struct PGTBL_CTL2 : Register<0x20C4, 32> { };
+
+ private:
+
+ Mmio::Delayer &_delayer;
+
+ void _fw_reset()
+ {
+ using namespace Genode;
+
+ /* "needs to be initialized by newly loaded gfx driver" */
+ write_post(FORCEWAKE_MT::RESET);
+ read();
+ }
+
+ /**
+ * Set forcewake state, i.e., prevent GT from powering down
+ */
+ void _fw_enable(unsigned id)
+ {
+ using namespace Genode;
+
+ unsigned const i = 1u << id;
+
+ FORCEWAKE_MT::access_t v = 0;
+ FORCEWAKE_MT::Multiple_force_wake_mask::set(v, i);
+ FORCEWAKE_MT::Force_wake_request_for_thread_0::set(v, i);
+
+ write(v);
+ read();
+
+ try {
+ wait_for(Attempts(50), Microseconds(1000), _delayer,
+ GTSP1::Equal(1));
+ } catch (Polling_timeout) {
+ error("could not enable force-wake engine");
+ }
+ }
+
+ /**
+ * Clear forcewake state
+ */
+ void _fw_disable(unsigned id)
+ {
+ using namespace Genode;
+
+ unsigned const i = 1u << id;
+
+ FORCEWAKE_MT::access_t v = 0;
+ FORCEWAKE_MT::Multiple_force_wake_mask::set(v, i);
+
+ write(v);
+ read();
+
+ try {
+ wait_for(Attempts(50), Microseconds(1000), _delayer,
+ GTSP1::Equal(0));
+ } catch (Polling_timeout) {
+ error("could not disable force-wake engine");
+ }
+ }
+
+ /**
+ * Reset interrupts
+ */
+ void _intr_reset()
+ {
+ write_post(0);
+
+ write_post(0xffffffff);
+ write(0);
+
+ /* do it like they do in Rome^WLinux */
+ write_post(0xffffffff);
+ write_post(0xffffffff);
+
+ /* mask all in the hardware status mask register */
+ write_post(0xffffffff);
+
+ /* TODO DE intr handling (p. 363) */
+ }
+
+ /**
+ * Enable interrupts
+ */
+ void _intr_enable()
+ {
+ using namespace Genode;
+
+ /* GT0 RCS/BCS */
+ {
+ uint32_t tmp = read();
+ if (tmp != 0) { error("GT_0_INTERRUPT_IIR not zero"); }
+
+ GT_0_INTERRUPT_IER::access_t ier = 0;
+ GT_0_INTERRUPT_IER::Cs_mi_user_interrupt::set(ier, 1);
+ GT_0_INTERRUPT_IER::Cs_ctx_switch_interrupt::set(ier, 1);
+ GT_0_INTERRUPT_IER::Bcs_mi_user_interrupt::set(ier, 1);
+ GT_0_INTERRUPT_IER::Bcs_ctx_switch_interrupt::set(ier, 1);
+ write(ier);
+
+ GT_0_INTERRUPT_IMR::access_t imr = ~0u;
+ GT_0_INTERRUPT_IMR::Cs_mi_user_interrupt::set(imr, 0);
+ GT_0_INTERRUPT_IMR::Cs_ctx_switch_interrupt::set(imr, 0);
+ GT_0_INTERRUPT_IMR::Bcs_mi_user_interrupt::set(imr, 0);
+ GT_0_INTERRUPT_IMR::Bcs_ctx_switch_interrupt::set(imr, 0);
+ write_post(imr);
+
+ /* demask also in the hardware status mask register */
+ write_post(imr);
+ }
+
+ /* GT1 VCS1/VCS2 */
+ {
+ uint32_t tmp = read();
+ if (tmp != 0) { error("GT_1_INTERRUPT_IIR not zero"); }
+
+ GT_1_INTERRUPT_IER::access_t ier = 0;
+ GT_1_INTERRUPT_IER::Vcs1_mi_user_interrupt::set(ier, 1);
+ GT_1_INTERRUPT_IER::Vcs1_ctx_switch_interrupt::set(ier, 1);
+ GT_1_INTERRUPT_IER::Vcs2_mi_user_interrupt::set(ier, 1);
+ GT_1_INTERRUPT_IER::Vcs2_ctx_switch_interrupt::set(ier, 1);
+ write(ier);
+
+ GT_1_INTERRUPT_IMR::access_t imr = ~0u;
+ GT_1_INTERRUPT_IMR::Vcs1_mi_user_interrupt::set(imr, 0);
+ GT_1_INTERRUPT_IMR::Vcs1_ctx_switch_interrupt::set(imr, 0);
+ GT_1_INTERRUPT_IMR::Vcs2_mi_user_interrupt::set(imr, 0);
+ GT_1_INTERRUPT_IMR::Vcs2_ctx_switch_interrupt::set(imr, 0);
+ write_post(imr);
+ }
+
+ /* GT2 GTPM */
+ {
+ uint32_t tmp = read();
+ if (tmp != 0) { error("GT_2_INTERRUPT_IIR not zero"); }
+
+ GT_2_INTERRUPT_IER::access_t ier = 0;
+ write(ier);
+
+ GT_2_INTERRUPT_IMR::access_t imr = ~0u;
+ write_post(imr);
+ }
+
+ /* GT3 VECS*/
+ {
+ uint32_t tmp = read();
+ if (tmp != 0) { error("GT_3_INTERRUPT_IIR not zero"); }
+
+ GT_3_INTERRUPT_IER::access_t ier = 0;
+ GT_3_INTERRUPT_IER::Vecs_mi_user_interrupt::set(ier, 1);
+ GT_3_INTERRUPT_IER::Vecs_ctx_switch_interrupt::set(ier, 1);
+ write(ier);
+
+ GT_3_INTERRUPT_IMR::access_t imr = ~0u;
+ GT_3_INTERRUPT_IMR::Vecs_mi_user_interrupt::set(imr, 0);
+ GT_3_INTERRUPT_IMR::Vecs_ctx_switch_interrupt::set(imr, 0);
+ write_post(imr);
+ }
+
+ /* finally enable interrupts */
+ write_post(1);
+ }
+
+ /**
+ * Disable Render P-states
+ */
+ void _disable_rps()
+ {
+ /*
+ * Set RC0 state -- does not matter at this point b/c
+ * we disable RC states entirely.
+ */
+ write_post(0);
+
+ write(0);
+ write(1);
+ write(0);
+ }
+
+ /**
+ * Reset fence registers
+ */
+ void _reset_fences()
+ {
+ for (int i = 0; i < NUM_FENCES; i++) {
+ write(i, 0);
+ read(i);
+ }
+ }
+
+ /**
+ * Clear fault registers of all engines
+ */
+ void _clear_fault_regs()
+ {
+ /* TODO */
+ }
+
+ /**
+ * Flush GFX TLB in the system agent
+ */
+ void _gfx_flush_cntl()
+ {
+ write_post(1);
+ }
+
+ /**
+ * Setup page attributes
+ */
+ void _set_page_attributes()
+ {
+ enum { NUM_PAT = 8, };
+ PAT_INDEX::access_t p[NUM_PAT] = {};
+
+ unsigned const mt[NUM_PAT] {
+ PAT_INDEX::Mem_type::WB,
+ PAT_INDEX::Mem_type::WC,
+ PAT_INDEX::Mem_type::WT,
+ PAT_INDEX::Mem_type::UC,
+ PAT_INDEX::Mem_type::WB,
+ PAT_INDEX::Mem_type::WB,
+ PAT_INDEX::Mem_type::WB,
+ PAT_INDEX::Mem_type::WB,
+ };
+
+ unsigned const tc[NUM_PAT] {
+ PAT_INDEX::Target_cache::LLC,
+ PAT_INDEX::Target_cache::LLCELLC,
+ PAT_INDEX::Target_cache::LLCELLC,
+ PAT_INDEX::Target_cache::ELLC,
+ PAT_INDEX::Target_cache::LLCELLC,
+ PAT_INDEX::Target_cache::LLCELLC,
+ PAT_INDEX::Target_cache::LLCELLC,
+ PAT_INDEX::Target_cache::LLCELLC,
+ };
+
+ unsigned const la[NUM_PAT] {
+ PAT_INDEX::Lru_age::UCR,
+ PAT_INDEX::Lru_age::UCR,
+ PAT_INDEX::Lru_age::UCR,
+ PAT_INDEX::Lru_age::UCR,
+ PAT_INDEX::Lru_age::UCR,
+ PAT_INDEX::Lru_age::A0,
+ PAT_INDEX::Lru_age::DNC,
+ PAT_INDEX::Lru_age::A3,
+ };
+
+ uint64_t v = 0u;
+ for (int i = 0; i < NUM_PAT; i++) {
+ PAT_INDEX::Lru_age::set( p[i], la[i]);
+ PAT_INDEX::Mem_type::set( p[i], mt[i]);
+ PAT_INDEX::Target_cache::set(p[i], tc[i]);
+ v |= (p[i] & 0xff) << (8 * i);
+ }
+
+ write(v);
+ write(v >> 32);
+ }
+
+ /**
+ * Reset engine
+ */
+ template
+ bool _reset_engine()
+ {
+ typename REG::access_t v = 0;
+ REG::Mask_bits::set(v, 1);
+ REG::Request_reset::set(v, 1);
+ write_post(v);
+ try {
+ wait_for(Attempts(35), Microseconds(20), _delayer,
+ typename REG::Ready_for_reset::Equal(1));
+ } catch (Mmio::Polling_timeout) {
+ Genode::error("could not reset engine");
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Reset engine by using hardware id
+ *
+ * \param id hardware id of the engine to be resetted
+ */
+ bool _reset_engine(unsigned id)
+ {
+ switch (id) {
+ case RCS_ID: return _reset_engine();
+ case BCS_ID: return _reset_engine();
+ default: return true;
+ }
+ }
+
+ bool _reset_engines()
+ {
+ for (int i = 0; i < NUM_ENGINES; i++) {
+ if (!_reset_engine(i)) { return false; }
+ }
+ return true;
+ }
+
+ /**
+ * Reset device
+ */
+ void _reset_device()
+ {
+ _fw_enable(FORCEWAKE_ID_RENDER);
+
+ bool res = _reset_engines();
+ if (!res) {
+ Genode::warning("cannot reset device, engines not ready");
+ return;
+ }
+
+ write(1);
+ try {
+ wait_for(Attempts(50), Microseconds(10), _delayer,
+ GDRST::Graphics_full_soft_reset_ctl::Equal(0));
+ } catch (Mmio::Polling_timeout) {
+ Genode::error("resetting device failed");
+ }
+
+ _fw_disable(FORCEWAKE_ID_RENDER);
+ }
+
+ /**
+ * Disable north DE handshake
+ */
+ void _disable_nde_handshake()
+ {
+ NDE_RSTWRN_OPT::access_t v = read