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(); + NDE_RSTWRN_OPT::Rst_pch_handshake_en::set(v, 0); + write(v); + } + + /** + * Enable execlist + */ + void _enable_execlist() + { + GFX_MODE::access_t v = read(); + + GFX_MODE::Execlist_enable_mask::set(v, 1); + GFX_MODE::Execlist_enable::set(v, 1); + + write(v); + } + + public: + + Mmio(Mmio::Delayer &delayer, addr_t const base) + : Genode::Mmio(base), _delayer(delayer) { } + + template + void write_post(typename T::access_t const value) + { + write(value); + (void)read(); + } + + void forcewake_enable() { _fw_enable(FORCEWAKE_ID_RENDER); } + void forcewake_disable() { _fw_disable(FORCEWAKE_ID_RENDER); } + + void reset() + { + _intr_reset(); + _reset_device(); + _fw_reset(); + _reset_fences(); + + _disable_nde_handshake(); + + _set_page_attributes(); + } + + void init() + { + _disable_rps(); + _enable_execlist(); + } + + void enable_intr() + { + write(0xffffff00); + + _intr_enable(); + } + + void disable_master_irq() + { + write_post(0); + } + + void enable_master_irq() + { + write_post(1); + } + + void flush_gfx_tlb() { _gfx_flush_cntl(); } + + void clear_errors() + { + write_post(0); + } + + void update_context_status_pointer() + { + RCS_RING_CONTEXT_STATUS_PTR::access_t const rp = read(); + RCS_RING_CONTEXT_STATUS_PTR::access_t const wp = read(); + if (wp > 0x05) { + Genode::warning("ring context status write-pointer invalid"); + return; + } + + RCS_RING_CONTEXT_STATUS_PTR::access_t v = 0; + RCS_RING_CONTEXT_STATUS_PTR::Read_pointer_mask::set(v, 0xff); + RCS_RING_CONTEXT_STATUS_PTR::Read_pointer::set(v, wp); + write(v); + } + + bool csb_unread() + { + RCS_RING_CONTEXT_STATUS_PTR::access_t const r = read(); + RCS_RING_CONTEXT_STATUS_PTR::access_t const w = read(); + + return (r != w) && (r + 1) % CTXT_ST_BUF_NUM <= w; /* XXX */ + } + + + uint32_t find_free_fence() + { + uint32_t id = 0xff; + + for (int i = 0; i < NUM_FENCES; i++) { + if (!read(i)) { + id = i; + break; + } + } + + return id; + } + + uint32_t update_fence(uint32_t id, addr_t lower, addr_t upper, + uint32_t pitch, bool x) + { + FENCE_REG::access_t v = 0; + + typename FENCE_REG::access_t const l = lower >> 12; + typename FENCE_REG::access_t const u = upper >> 12; + FENCE_REG::Fence_upper_bound::set(v, u); + FENCE_REG::Fence_lower_bound::set(v, l); + FENCE_REG::Fence_pitch::set(v, pitch); + enum { MI_TILE_XMAJOR = 0b00, MI_TILE_YMAJOR = 0b01, }; + FENCE_REG::Tile_walk::set(v, x ? MI_TILE_XMAJOR : MI_TILE_YMAJOR); + FENCE_REG::Fence_valid::set(v, 1); + + write(v, id); + (void)read(id); + return id; + } + + void clear_fence(uint32_t const id) + { + write(0, id); + (void)read(id); + } + + /** + * Check if fault + */ + bool fault_regs_valid() + { + bool result = false; + + /* TODO */ + + return result; + } + + /********************* + ** DEBUG interface ** + *********************/ + + void dump(); + void power_dump(); + void error_dump(); + void intr_dump(); + void fault_dump(); + void execlist_status_dump(); + void context_status_pointer_dump(); +}; + +#endif /* _MMIO_H_ */ diff --git a/repos/os/src/drivers/gpu/intel/mmio_dump.cc b/repos/os/src/drivers/gpu/intel/mmio_dump.cc new file mode 100644 index 000000000..c13dc6d74 --- /dev/null +++ b/repos/os/src/drivers/gpu/intel/mmio_dump.cc @@ -0,0 +1,327 @@ +/* + * \brief Dump Broadwell MMIO registers + * \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. + */ + +/* local includes */ +#include +#include + + +void Igd::Mmio::dump() +{ + using namespace Genode; + + log("MMIO vaddr:", Hex(base()), " size:", Hex(Igd::Mmio::SIZE)); + log("GFX_MODE: ", Hex(read())); + log(" Privilege_check_disable: ", Hex(read())); + log(" Execlist_enable: ", Hex(read())); + log(" Virtual_addressing_enable: ", Hex(read())); + log(" Ppgtt_enable: ", Hex(read())); + log("HWS_PGA: ", Hex(read())); + log("HWSTAM: ", Hex(read())); +} + + +void Igd::Mmio::power_dump() +{ + using namespace Genode; + + log("PWR_WELL_CTL2"); + log(" Misc_io_power_state: ", Hex(read())); + log(" Misc_io_power_request: ", Hex(read())); + log(" Ddi_a_and_ddi_e_io_power_state: ", Hex(read())); + log(" Ddi_a_and_ddi_e_io_power_request: ", Hex(read())); + log(" Ddi_b_io_power_state: ", Hex(read())); + log(" Ddi_b_io_power_request: ", Hex(read())); + log(" Ddi_c_io_power_state: ", Hex(read())); + log(" Ddi_c_io_power_request: ", Hex(read())); + log(" Ddi_d_io_power_state: ", Hex(read())); + log(" Ddi_d_io_power_request: ", Hex(read())); + log(" Power_well_1_state: ", Hex(read())); + log(" Power_well_1_request: ", Hex(read())); + log(" Power_well_2_state: ", Hex(read())); + log(" Power_well_2_request: ", Hex(read())); +} + + +void Igd::Mmio::error_dump() +{ + using namespace Genode; + + log("ERROR: ", Hex(read())); + if (read()) { + log(" Ctx_fault_ctxt_not_prsnt_err: ", Hex(read())); + log(" Ctx_fault_root_not_prsnt_err: ", Hex(read())); + log(" Ctx_fault_pasid_not_prsnt_err: ", Hex(read())); + log(" Ctx_fault_pasid_ovflw_err: ", Hex(read())); + log(" Ctx_fault_pasid_dis_err: ", Hex(read())); + log(" Rstrm_fault_nowb_atomic_err: ", Hex(read())); + log(" Unloaded_pd_error: ", Hex(read())); + log(" Hws_page_fault_error: ", Hex(read())); + log(" Invalid_page_directory_entry_error: ", Hex(read())); + log(" Ctx_page_fault_error: ", Hex(read())); + log(" Tlb_fault_error: ", Hex(read())); + } + + log("ERROR_2: ", Hex(read())); + if (read()) { + log(" Tlbpend_reg_faultcnt: ", Hex(read())); + } + + log("RCS_EIR: ", Hex(read())); + log("RCS_ESR: ", Hex(read())); + log("RCS_EMR: ", Hex(read())); + log("RCS_INSTDONE: ", Hex(read())); + if (read() != RCS_INSTDONE::DEFAULT_VALUE || + read() != 0xffffffff) { + log(" Row_0_eu_0_done : ", Hex(read())); + log(" Row_0_eu_1_done : ", Hex(read())); + log(" Row_0_eu_2_done : ", Hex(read())); + log(" Row_0_eu_3_done : ", Hex(read())); + log(" Row_1_eu_0_done : ", Hex(read())); + log(" Row_1_eu_1_done : ", Hex(read())); + log(" Row_1_eu_2_done : ", Hex(read())); + log(" Row_1_eu_3_done : ", Hex(read())); + log(" Sf_done : ", Hex(read())); + log(" Se_done : ", Hex(read())); + log(" Windower_done : ", Hex(read())); + log(" Reserved1 : ", Hex(read())); + log(" Reserved2 : ", Hex(read())); + log(" Dip_done : ", Hex(read())); + log(" Pl_done : ", Hex(read())); + log(" Dg_done : ", Hex(read())); + log(" Qc_done : ", Hex(read())); + log(" Ft_done : ", Hex(read())); + log(" Dm_done : ", Hex(read())); + log(" Sc_done : ", Hex(read())); + log(" Fl_done : ", Hex(read())); + log(" By_done : ", Hex(read())); + log(" Ps_done : ", Hex(read())); + log(" Cc_done : ", Hex(read())); + log(" Map_fl_done : ", Hex(read())); + log(" Map_l2_idle : ", Hex(read())); + log(" Msg_arb_0_done : ", Hex(read())); + log(" Msg_arb_1_done : ", Hex(read())); + log(" Ic_row_0_done : ", Hex(read())); + log(" Ic_row_1_done : ", Hex(read())); + log(" Cp_done : ", Hex(read())); + log(" Ring_0_enable : ", Hex(read())); + } + log("RCS_INSTDONE_1: ", Hex(read())); + log("RCS_ACTHD: ", Hex(read())); + log("IPEHR: ", Hex(read())); + log("IPEIR: ", Hex(read())); + log("PGTBL_ER: ", Hex(read())); +} + + +void Igd::Mmio::intr_dump() +{ +#define DUMP_GT_1 +#define DUMP_GT_2 +#define DUMP_GT_3 + + using namespace Genode; + + log("MASTER_INT_CTL"); + log(" Master_interrupt_enable: ", Hex(read())); + log(" Pcu_interrupts_pending: ", Hex(read())); + log(" Audio_codec_interrupts_pending: ", Hex(read())); + log(" De_pch_interrupts_pending: ", Hex(read())); + log(" De_misc_interrupts_pending: ", Hex(read())); + log(" De_port_interrupts_pending: ", Hex(read())); + log(" De_pipe_c_interrupts_pending: ", Hex(read())); + log(" De_pipe_b_interrupts_pending: ", Hex(read())); + log(" De_pipe_a_interrupts_pending: ", Hex(read())); + log(" Vebox_interrupts_pending: ", Hex(read())); + log(" Gtpm_interrupts_pending: ", Hex(read())); + log(" Vcs2_interrupts_pending: ", Hex(read())); + log(" Vcs1_interrupts_pending: ", Hex(read())); + log(" Blitter_interrupts_pending: ", Hex(read())); + log(" Render_interrupts_pending: ", Hex(read())); + +#ifndef DUMP_GT_0 +#define DUMP_GT_0(reg) \ + log(#reg); \ + log(" Bcs_wait_on_semaphore: ", Hex(read())); \ + log(" Bcs_ctx_switch_interrupt: ", Hex(read())); \ + log(" Bcs_mi_flush_dw_notify: ", Hex(read())); \ + log(" Bcs_error_interrupt: ", Hex(read())); \ + log(" Bcs_mi_user_interrupt: ", Hex(read())); \ + log(" Cs_wait_on_semaphore: ", Hex(read())); \ + log(" Cs_l3_counter_slave: ", Hex(read())); \ + log(" Cs_ctx_switch_interrupt: ", Hex(read())); \ + log(" Page_fault_error: ", Hex(read())); \ + log(" Cs_watchdog_counter_expired: ", Hex(read())); \ + log(" L3_parity_error: ", Hex(read())); \ + log(" Cs_pipe_control_notify: ", Hex(read())); \ + log(" Cs_error_interrupt: ", Hex(read())); \ + log(" Cs_mi_user_interrupt: ", Hex(read())); + + DUMP_GT_0(GT_0_INTERRUPT_ISR); + DUMP_GT_0(GT_0_INTERRUPT_IIR); + DUMP_GT_0(GT_0_INTERRUPT_IER); + DUMP_GT_0(GT_0_INTERRUPT_IMR); +#undef DUMP_GT_0 +#endif + +#ifndef DUMP_GT_1 +#define DUMP_GT_1(reg) \ + log(#reg); \ + log(" Vcs2_wait_on_semaphore: ", Hex(read())); \ + log(" Vcs2_ctx_switch_interrupt: ", Hex(read())); \ + log(" Vcs2_watchdog_counter_expired: ", Hex(read())); \ + log(" Vcs2_mi_flush_dw_notify: ", Hex(read())); \ + log(" Vcs2_error_interrupt: ", Hex(read())); \ + log(" Vcs2_mi_user_interrupt: ", Hex(read())); \ + log(" Vcs1_wait_on_semaphore: ", Hex(read())); \ + log(" Vcs1_ctx_switch_interrupt: ", Hex(read())); \ + log(" Vcs1_watchdog_counter_expired: ", Hex(read())); \ + log(" Vcs1_pipe_control_notify: ", Hex(read())); \ + log(" Vcs1_error_interrupt: ", Hex(read())); \ + log(" Vcs1_mi_user_interrupt: ", Hex(read())); \ + + DUMP_GT_1(GT_1_INTERRUPT_ISR); + DUMP_GT_1(GT_1_INTERRUPT_IIR); + DUMP_GT_1(GT_1_INTERRUPT_IER); + DUMP_GT_1(GT_1_INTERRUPT_IMR); +#undef DUMP_GT_1 +#endif + +#ifndef DUMP_GT_2 +#define DUMP_GT_2(reg) \ + log(#reg); \ + log(" Unslice_frequency_control_up_interrupt: ", Hex(read())); \ + log(" Unslice_frequency_control_down_interrupt: ", Hex(read())); \ + log(" Nfafdl_frequency_up_interrupt: ", Hex(read())); \ + log(" Nfafdl_frequency_down_interrupt: ", Hex(read())); \ + log(" Gtpm_engines_idle_interrupt: ", Hex(read())); \ + log(" Gtpm_uncore_to_core_trap_interrupt: ", Hex(read())); \ + log(" Gtpm_render_frequency_downwards_timeout_during_rc6_interrupt: ", Hex(read())); \ + log(" Gtpm_render_p_state_up_threshold_interrupt: ", Hex(read())); \ + log(" Gtpm_render_p_state_down_threshold_interrupt: ", Hex(read())); \ + log(" Gtpm_render_geyserville_up_evaluation_interval_interrupt: ", Hex(read())); \ + log(" Gtpm_render_geyserville_down_evaluation_interval_interrupt: ", Hex(read())); + + DUMP_GT_2(GT_2_INTERRUPT_ISR); + DUMP_GT_2(GT_2_INTERRUPT_IIR); + DUMP_GT_2(GT_2_INTERRUPT_IER); + DUMP_GT_2(GT_2_INTERRUPT_IMR); +#undef DUMP_GT_2 +#endif + +#ifndef DUMP_GT_3 +#define DUMP_GT_3(reg) \ + log(#reg); \ + log(" Performance_monitoring_buffer_half_full_interrupt: ", Hex(read())); \ + log(" Vecs_wait_on_semaphore: ", Hex(read())); \ + log(" Vecs_ctx_switch_interrupt: ", Hex(read())); \ + log(" Vecs_mi_flush_dw_notify: ", Hex(read())); \ + log(" Vecs_error_interrupt: ", Hex(read())); \ + log(" Vecs_mi_user_interrupt: ", Hex(read())); \ + + DUMP_GT_3(GT_3_INTERRUPT_ISR); + DUMP_GT_3(GT_3_INTERRUPT_IIR); + DUMP_GT_3(GT_3_INTERRUPT_IER); + DUMP_GT_3(GT_3_INTERRUPT_IMR); +#undef DUMP_GT_3 +#endif +} + + +void Igd::Mmio::fault_dump() +{ + Genode::log("FAULT_TLB_RB_DATA0: ", Genode::Hex(read())); + Genode::log("FAULT_TLB_RB_DATA1: ", Genode::Hex(read())); + + using namespace Genode; + + uint64_t const addr = ((uint64_t)(read() & 0xf) << 44) | + ((uint64_t) read() << 12); + Genode::log(" ggtt: ", read(), " " + "addr: ", Genode::Hex(addr)); +} + + +void Igd::Mmio::execlist_status_dump() +{ + using namespace Genode; + +#define DUMP_EXECLIST_STATUS(reg) \ + log(#reg); \ + log(" Current_context_id: ", Hex(read())); \ + log(" Arbitration_enable: ", Hex(read())); \ + log(" Current_active_element_status: ", Hex(read())); \ + log(" Last_context_switch_reason: ", Hex(read())); \ + if (read()) { \ + Igd::Context_status_qword::access_t v = read(); \ + log(" Wait_on_scanline: ", Igd::Context_status_qword::Wait_on_scanline::get(v)); \ + log(" Wait_on_semaphore: ", Igd::Context_status_qword::Wait_on_semaphore::get(v)); \ + log(" Wait_on_v_blank: ", Igd::Context_status_qword::Wait_on_v_blank::get(v)); \ + log(" Wait_on_sync_flip: ", Igd::Context_status_qword::Wait_on_sync_flip::get(v)); \ + log(" Context_complete: ", Igd::Context_status_qword::Context_complete::get(v)); \ + log(" Active_to_idle: ", Igd::Context_status_qword::Active_to_idle::get(v)); \ + log(" Element_switch: ", Igd::Context_status_qword::Element_switch::get(v)); \ + log(" Preempted: ", Igd::Context_status_qword::Preempted::get(v)); \ + log(" Idle_to_active: ", Igd::Context_status_qword::Idle_to_active::get(v)); \ + } \ + log(" Execlist_0_valid: ", Hex(read())); \ + log(" Execlist_1_valid: ", Hex(read())); \ + log(" Execlist_queue_full: ", Hex(read())); \ + log(" Execlist_write_pointer: ", Hex(read())); \ + log(" Current_execlist_pointer: ", Hex(read())); + + DUMP_EXECLIST_STATUS(EXECLIST_STATUS_RSCUNIT); + +#undef DUMP_EXECLIST_STATUS +} + + +void Igd::Mmio::context_status_pointer_dump() +{ + using namespace Genode; + + uint32_t const v = read(); + uint32_t const wp = read(); + uint32_t const rp = read(); + + log("RCS_RING_CONTEXT_STATUS_PTR: ", Hex(v, Hex::PREFIX, Hex::PAD)); + log(" Read pointer: ", Hex(rp)); + log(" Write pointer: ", Hex(wp)); + + if (wp == 0x7) { + warning("RCS seems to be idle"); + return; + } + + uint32_t r = rp; + uint32_t w = wp; + if (r > w) { w += CTXT_ST_BUF_NUM; } + while (r < w) { + uint32_t const i = ++r % CTXT_ST_BUF_NUM; + + uint64_t const cs = read(i); + uint32_t const csu = read(i); + uint32_t const csl = read(i); + log(i, " Context_status: ", Hex(cs)); + + Igd::Context_status_qword::access_t const v = csl; + log(i, " Context_complete: ", Igd::Context_status_qword::Context_complete::get(v)); + log(i, " Active_to_idle: ", Igd::Context_status_qword::Active_to_idle::get(v)); + log(i, " Element_switch: ", Igd::Context_status_qword::Element_switch::get(v)); + log(i, " Preempted: ", Igd::Context_status_qword::Preempted::get(v)); + log(i, " Idle_to_active: ", Igd::Context_status_qword::Idle_to_active::get(v)); + + log(i, " Context_status_udw: ", Hex(csu)); + log(i, " Context_status_ldw: ", Hex(csl)); + } +} diff --git a/repos/os/src/drivers/gpu/intel/ppgtt.h b/repos/os/src/drivers/gpu/intel/ppgtt.h new file mode 100644 index 000000000..08decf1bd --- /dev/null +++ b/repos/os/src/drivers/gpu/intel/ppgtt.h @@ -0,0 +1,849 @@ +/* + * \brief Broadwell graphics translation table definitions + * \author Josef Soentgen + * \date 2017-03-15 + * + * Adapted copy of base-hw's IA-32e translation table by + * Adrian-Ken Rueegsegger et. al. + */ + +/* + * 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 _PPGTT_H_ +#define _PPGTT_H_ + +/* Genode includes */ +#include +#include +#include +#include +#include + +/* local includes */ +#include + + +namespace Genode +{ + /** + * Return an address rounded down to a specific alignment + * + * \param addr original address + * \param alignm_log2 log2 of the required alignment + */ + inline addr_t trunc(addr_t const addr, addr_t const alignm_log2) { + return (addr >> alignm_log2) << alignm_log2; } + + /** + * Return wether a pointer fullfills an alignment + * + * \param p pointer + * \param alignm_log2 log2 of the required alignment + */ + inline bool aligned(void * const p, addr_t const alignm_log2) { + return (addr_t)p == trunc((addr_t)p, alignm_log2); } + + /** + * Translation table allocator interface + */ + class Translation_table_allocator : public Genode::Allocator + { + public: + + /** + * Return physical address of given virtual page address + * + * \param addr virtual page address + */ + virtual void * phys_addr(void * addr) = 0; + + /** + * Return virtual address of given physical page address + * + * \param addr physical page address + */ + virtual void * virt_addr(void * addr) = 0; + }; + + enum Writeable { RO, RW }; + enum Executeable { NO_EXEC, EXEC }; + enum Privileged { USER, KERN }; + enum Global { NO_GLOBAL, GLOBAL }; + enum Type { RAM, DEVICE }; + + struct Page_flags + { + Writeable writeable; + }; + + /** + * IA-32e paging translates 48-bit linear addresses to 52-bit physical + * addresses. Translation structures are hierarchical and four levels + * deep. + * + * For detailed information refer to Intel SDM Vol. 3A, section 4.5. + */ + + enum { + SIZE_LOG2_4KB = 12, + SIZE_LOG2_2MB = 21, + SIZE_LOG2_1GB = 30, + SIZE_LOG2_512GB = 39, + SIZE_LOG2_256TB = 48, + }; + + class Level_4_translation_table; + class Pml4_table; + + /** + * IA-32e page directory template. + * + * Page directories can refer to paging structures of the next higher level + * or directly map page frames by using large page mappings. + * + * \param PAGE_SIZE_LOG2 virtual address range size in log2 + * of a single table entry + * \param SIZE_LOG2 virtual address range size in log2 of whole table + */ + template + class Page_directory; + + using Level_3_translation_table = + Page_directory; + using Level_2_translation_table = + Page_directory; + + /** + * IA-32e common descriptor. + * + * Table entry contains descriptor fields common to all four levels. + * + * IHD-OS-BDW-Vol 5-11.15 p. 23 ff. + */ + struct Common_descriptor : Register<64> + { + struct P : Bitfield< 0, 1> { }; /* present */ /* must always be 1, see scratch pages */ + struct Rw : Bitfield< 1, 1> { }; /* read/write */ /* not supported on Gen8 */ + struct Us : Bitfield< 2, 1> { }; /* user/supervisor */ /* not supported on Gen8 */ + struct Pwt : Bitfield< 3, 1> { }; /* write-through */ /* Pwt and Pcd are used as index into PAT */ + struct Pcd : Bitfield< 4, 1> { }; /* cache disable */ /* see Mmio::PAT_INDEX */ + struct A : Bitfield< 5, 1> { }; /* accessed */ + struct D : Bitfield< 6, 1> { }; /* dirty */ + struct Xd : Bitfield<63, 1> { }; /* execute-disable */ /* not supported on Gen8 */ + + static bool present(access_t const v) { return P::get(v); } + + static access_t create(Page_flags const &flags) + { + return P::bits(1) | Rw::bits(1); + } + + /** + * Merge access rights of descriptor with given flags. + */ + static void merge_access_rights(access_t &desc, + Page_flags const &flags) + { + Rw::set(desc, Rw::get(desc) | flags.writeable); + } + }; + + struct Scratch + { + private: + + Genode::Allocator_guard &_guard; + Utils::Backend_alloc &_backend; + + public: + + enum { + PAGE_SIZE = 4096, + MAX_ENTRIES = 512, + }; + + using Ram = Utils::Ram; + + struct Page + { + Genode::Ram_dataspace_capability ds; + addr_t addr; + Page *next; + }; + + Page page; + Page pt; + Page pd; + Page pdp; + + Scratch(Genode::Allocator_guard &guard, + Utils::Backend_alloc &backend) + : + _guard(guard), _backend(backend) + { + /* XXX addr PAT helper instead of hardcoding */ + page.ds = _backend.alloc(_guard, PAGE_SIZE); + page.addr = Genode::Dataspace_client(page.ds).phys_addr(); + page.addr |= 1; + page.addr |= 1 << 1; + page.next = nullptr; + + pt.ds = _backend.alloc(_guard, PAGE_SIZE); + pt.addr = Genode::Dataspace_client(pt.ds).phys_addr(); + pt.addr |= 1; + pt.addr |= 1 << 1; + pt.addr |= 1 << 7; + pt.next = &page; + + pd.ds = _backend.alloc(_guard, PAGE_SIZE); + pd.addr = Genode::Dataspace_client(pd.ds).phys_addr(); + pd.addr |= 1; + pd.addr |= 1 << 1; + pd.next = &pt; + + pdp.ds = _backend.alloc(_guard, PAGE_SIZE); + pdp.addr = Genode::Dataspace_client(pdp.ds).phys_addr(); + pdp.addr |= 1; + pdp.addr |= 1 << 1; + pdp.next = &pd; + } + + virtual ~Scratch() + { + _backend.free(_guard, pdp.ds); + _backend.free(_guard, pd.ds); + _backend.free(_guard, pt.ds); + _backend.free(_guard, page.ds); + } + }; +} + + +class Genode::Level_4_translation_table +{ + private: + + static constexpr size_t PAGE_SIZE_LOG2 = SIZE_LOG2_4KB; + static constexpr size_t SIZE_LOG2 = SIZE_LOG2_2MB; + static constexpr size_t MAX_ENTRIES = 1UL << (SIZE_LOG2-PAGE_SIZE_LOG2); + static constexpr size_t PAGE_SIZE = 1UL << PAGE_SIZE_LOG2; + static constexpr size_t PAGE_MASK = ~((1UL << PAGE_SIZE_LOG2) - 1); + + class Misaligned {}; + class Invalid_range {}; + class Double_insertion {}; + + struct Descriptor : Common_descriptor + { + using Common = Common_descriptor; + + struct Pat : Bitfield<7, 1> { }; /* page attribute table */ + struct G : Bitfield<8, 1> { }; /* global */ + struct Pa : Bitfield<12, 36> { }; /* physical address */ + struct Mt : Bitset_3 { }; /* memory type */ + + static access_t create(Page_flags const &flags, addr_t const pa) + { + /* XXX: Set memory type depending on active PAT */ + return Common::create(flags) | Pat::bits(1) | Pa::masked(pa); + } + + static bool scratch(typename Descriptor::access_t desc, + addr_t scratch_addr) + { + return desc == scratch_addr; + } + }; + + typename Descriptor::access_t _entries[MAX_ENTRIES]; + + struct Insert_func + { + Page_flags const &flags; + Translation_table_allocator *alloc; + Scratch::Page *scratch; + + Insert_func(Page_flags const &flags, + Translation_table_allocator *alloc, + Scratch::Page *scratch) + : flags(flags), alloc(alloc), scratch(scratch) { } + + void operator () (addr_t const vo, addr_t const pa, + size_t const size, + Descriptor::access_t &desc) + { + if ((vo & ~PAGE_MASK) || (pa & ~PAGE_MASK) || + size < PAGE_SIZE) + { + throw Invalid_range(); + } + Descriptor::access_t table_entry = + Descriptor::create(flags, pa); + + if ( Descriptor::present(desc) + && !Descriptor::scratch(desc, scratch->addr)) { + throw Double_insertion(); + } + desc = table_entry; + } + }; + + struct Remove_func + { + Translation_table_allocator *alloc; + Scratch::Page *scratch; + + Remove_func(Translation_table_allocator *alloc, + Scratch::Page *scratch) + : alloc(alloc), scratch(scratch) { } + + void operator () (addr_t const vo, addr_t const pa, + size_t const size, + Descriptor::access_t &desc) + { + desc = scratch->addr; + } + }; + + template + void _range_op(addr_t vo, addr_t pa, size_t size, FUNC &&func) + { + for (size_t i = vo >> PAGE_SIZE_LOG2; size > 0; + i = vo >> PAGE_SIZE_LOG2) { + addr_t end = (vo + PAGE_SIZE) & PAGE_MASK; + size_t sz = min(size, end-vo); + + func(vo, pa, sz, _entries[i]); + + /* check whether we wrap */ + if (end < vo) return; + + size = size - sz; + vo += sz; + pa += sz; + } + } + + public: + + static constexpr size_t MIN_PAGE_SIZE_LOG2 = SIZE_LOG2_4KB; + static constexpr size_t ALIGNM_LOG2 = SIZE_LOG2_4KB; + + /** + * IA-32e page table (Level 4) + * + * A page table consists of 512 entries that each maps a 4KB page + * frame. For further details refer to Intel SDM Vol. 3A, table 4-19. + */ + Level_4_translation_table(Scratch::Page *scratch) + { + if (!aligned(this, ALIGNM_LOG2)) { throw Misaligned(); } + + for (size_t i = 0; i < MAX_ENTRIES; i++) { + _entries[i] = scratch->addr; + } + } + + /** + * Returns True if table does not contain any page mappings. + */ + bool empty(addr_t scratch_addr) + { + for (unsigned i = 0; i < MAX_ENTRIES; i++) { + if (!Descriptor::scratch(_entries[i], scratch_addr)) { + return false; + } + } + return true; + } + + /** + * Insert translations into this table + * + * \param vo offset of the virtual region represented + * by the translation within the virtual + * region represented by this table + * \param pa base of the physical backing store + * \param size size of the translated region + * \param flags mapping flags + * \param alloc second level translation table allocator + */ + void insert_translation(addr_t vo, addr_t pa, size_t size, + Page_flags const & flags, + Translation_table_allocator *alloc, + Scratch::Page *scratch) + { + _range_op(vo, pa, size, Insert_func(flags, alloc, scratch)); + } + + /** + * Remove translations that overlap with a given virtual region + * + * \param vo region offset within the tables virtual region + * \param size region size + * \param alloc second level translation table allocator + */ + void remove_translation(addr_t vo, size_t size, + Translation_table_allocator *alloc, + Scratch::Page *scratch) + { + _range_op(vo, 0, size, Remove_func(alloc, scratch)); + } +} __attribute__((aligned(1 << ALIGNM_LOG2))); + + +template +class Genode::Page_directory +{ + private: + + static constexpr size_t MAX_ENTRIES = 1UL << (SIZE_LOG2-PAGE_SIZE_LOG2); + static constexpr size_t PAGE_SIZE = 1UL << PAGE_SIZE_LOG2; + static constexpr size_t PAGE_MASK = ~((1UL << PAGE_SIZE_LOG2) - 1); + + class Misaligned {}; + class Invalid_range {}; + class Double_insertion {}; + + struct Base_descriptor : Common_descriptor + { + using Common = Common_descriptor; + + struct Ps : Common::template Bitfield<7, 1> { }; /* page size */ + + static bool maps_page(access_t const v) { return Ps::get(v); } + }; + + struct Page_descriptor : Base_descriptor + { + using Base = Base_descriptor; + + /** + * Global attribute + */ + struct G : Base::template Bitfield<8, 1> { }; + + /** + * Page attribute table + */ + struct Pat : Base::template Bitfield<12, 1> { }; + + /** + * Physical address + */ + struct Pa : Base::template Bitfield { }; + + /** + * Memory type + */ + struct Mt : Base::template Bitset_3 { }; + + static typename Base::access_t create(Page_flags const &flags, + addr_t const pa) + { + /* XXX: Set memory type depending on active PAT */ + return Base::create(flags) | Pa::masked(pa); + } + + static bool scratch(typename Page_descriptor::access_t desc, + addr_t scratch_addr) + { + return desc == scratch_addr; + } + }; + + struct Table_descriptor : Base_descriptor + { + using Base = Base_descriptor; + + /** + * Physical address + */ + struct Pa : Base::template Bitfield<12, 36> { }; + + /** + * Memory types + */ + struct Mt : Base::template Bitset_2 { }; + + static typename Base::access_t create(Page_flags const &flags, + addr_t const pa) + { + /* XXX: Set memory type depending on active PAT */ + return Base::create(flags) | Pa::masked(pa); + } + }; + + typename Base_descriptor::access_t _entries[MAX_ENTRIES]; + + struct Insert_func + { + Page_flags const &flags; + Translation_table_allocator *alloc; + Scratch::Page *scratch; + + Insert_func(Page_flags const &flags, + Translation_table_allocator *alloc, + Scratch::Page *scratch) + : flags(flags), alloc(alloc), scratch(scratch) { } + + void operator () (addr_t const vo, addr_t const pa, + size_t const size, + typename Base_descriptor::access_t &desc) + { + /* we need to use a next level table */ + ENTRY *table; + if (Page_descriptor::scratch(desc, scratch->addr)) { + if (!alloc) { throw Allocator::Out_of_memory(); } + + /* create and link next level table */ + try { table = new (alloc) ENTRY(scratch->next); } + catch (...) { throw Allocator::Out_of_memory(); } + ENTRY * phys_addr = (ENTRY*) alloc->phys_addr(table); + + addr_t const pa = (addr_t)(phys_addr ? phys_addr : table); + desc = (typename Base_descriptor::access_t) Table_descriptor::create(flags, pa); + + } else if (Base_descriptor::maps_page(desc)) { + throw Double_insertion(); + } else { + Base_descriptor::merge_access_rights(desc, flags); + ENTRY * phys_addr = (ENTRY*) Table_descriptor::Pa::masked(desc); + table = (ENTRY*) alloc->virt_addr(phys_addr); + if (!table) { table = (ENTRY*) phys_addr; } + } + + /* insert translation */ + addr_t const table_vo = vo - (vo & PAGE_MASK); + table->insert_translation(table_vo, pa, size, flags, alloc, scratch->next); + } + }; + + struct Remove_func + { + Translation_table_allocator *alloc; + Scratch::Page *scratch; + + Remove_func(Translation_table_allocator *alloc, + Scratch::Page *scratch) + : alloc(alloc), scratch(scratch) { } + + void operator () (addr_t const vo, addr_t const pa, + size_t const size, + typename Base_descriptor::access_t &desc) + { + if (Base_descriptor::present(desc) && + !Page_descriptor::scratch(desc, scratch->addr)) { + + if (Base_descriptor::maps_page(desc)) { + desc = scratch->addr; + } else { + /* use allocator to retrieve virt address of table */ + ENTRY *phys_addr = (ENTRY*) + Table_descriptor::Pa::masked(desc); + ENTRY *table = (ENTRY*) alloc->virt_addr(phys_addr); + if (!table) { table = (ENTRY*) phys_addr; } + + addr_t const table_vo = vo - (vo & PAGE_MASK); + table->remove_translation(table_vo, size, alloc, scratch->next); + if (table->empty(scratch->next->addr)) { + destroy(alloc, table); + desc = scratch->addr; + } + } + } + } + }; + + template + void _range_op(addr_t vo, addr_t pa, size_t size, FUNC &&func) + { + for (size_t i = vo >> PAGE_SIZE_LOG2; size > 0; + i = vo >> PAGE_SIZE_LOG2) + { + addr_t end = (vo + PAGE_SIZE) & PAGE_MASK; + size_t sz = min(size, end-vo); + + func(vo, pa, sz, _entries[i]); + + /* check whether we wrap */ + if (end < vo) { return; } + + size = size - sz; + vo += sz; + pa += sz; + } + } + + public: + + static constexpr size_t MIN_PAGE_SIZE_LOG2 = SIZE_LOG2_4KB; + static constexpr size_t ALIGNM_LOG2 = SIZE_LOG2_4KB; + + Page_directory(Scratch::Page *scratch) + { + if (!aligned(this, ALIGNM_LOG2)) { throw Misaligned(); } + + for (size_t i = 0; i < MAX_ENTRIES; i++) { + _entries[i] = scratch->addr; + } + } + + /** + * Returns True if table does not contain any page mappings. + * + * \return false if an entry is present, True otherwise + */ + bool empty(addr_t scratch_addr) + { + for (unsigned i = 0; i < MAX_ENTRIES; i++) { + if (!Page_descriptor::scratch(_entries[i], scratch_addr)) { + return false; + } + } + return true; + } + + /** + * Insert translations into this table + * + * \param vo offset of the virtual region represented + * by the translation within the virtual + * region represented by this table + * \param pa base of the physical backing store + * \param size size of the translated region + * \param flags mapping flags + * \param alloc second level translation table allocator + */ + void insert_translation(addr_t vo, addr_t pa, size_t size, + Page_flags const & flags, + Translation_table_allocator *alloc, + Scratch::Page *scratch) + { + _range_op(vo, pa, size, Insert_func(flags, alloc, scratch)); + } + + /** + * Remove translations that overlap with a given virtual region + * + * \param vo region offset within the tables virtual region + * \param size region size + * \param alloc second level translation table allocator + */ + void remove_translation(addr_t vo, size_t size, + Translation_table_allocator *alloc, + Scratch::Page *scratch) + { + _range_op(vo, 0, size, Remove_func(alloc, scratch)); + } +} __attribute__((aligned(1 << ALIGNM_LOG2))); + + +class Genode::Pml4_table +{ + private: + + static constexpr size_t PAGE_SIZE_LOG2 = SIZE_LOG2_512GB; + static constexpr size_t SIZE_LOG2 = SIZE_LOG2_256TB; + static constexpr size_t MAX_ENTRIES = 512; + static constexpr uint64_t PAGE_SIZE = 1ULL << PAGE_SIZE_LOG2; + static constexpr uint64_t PAGE_MASK = ~((1ULL << PAGE_SIZE_LOG2) - 1); + + class Misaligned {}; + class Invalid_range {}; + + struct Descriptor : Common_descriptor + { + struct Pa : Bitfield<12, 36> { }; /* physical address */ + struct Mt : Bitset_2 { }; /* memory type */ + + static access_t create(Page_flags const &flags, addr_t const pa) + { + /* XXX: Set memory type depending on active PAT */ + return Common_descriptor::create(flags) | Pa::masked(pa); + } + + static bool scratch(Descriptor::access_t desc, addr_t const pa) + { + return desc == pa; + } + }; + + typename Descriptor::access_t _entries[MAX_ENTRIES]; + + using ENTRY = Level_2_translation_table; + + struct Insert_func + { + Page_flags const &flags; + Translation_table_allocator *alloc; + Scratch::Page *scratch; + + Insert_func(Page_flags const &flags, + Translation_table_allocator *alloc, + Scratch::Page *scratch) + : flags(flags), alloc(alloc), scratch(scratch) { } + + void operator () (addr_t const vo, addr_t const pa, + size_t const size, + Descriptor::access_t &desc) + { + /* we need to use a next level table */ + ENTRY *table; + if (Descriptor::scratch(desc, scratch->addr)) { + if (!alloc) { throw Allocator::Out_of_memory(); } + + /* create and link next level table */ + try { table = new (alloc) ENTRY(scratch->next); } + catch (...) { throw Allocator::Out_of_memory(); } + + ENTRY * phys_addr = (ENTRY*) alloc->phys_addr(table); + addr_t const pa = (addr_t)(phys_addr ? phys_addr : table); + desc = Descriptor::create(flags, pa); + } else { + Descriptor::merge_access_rights(desc, flags); + ENTRY * phys_addr = (ENTRY*) Descriptor::Pa::masked(desc); + table = (ENTRY*) alloc->virt_addr(phys_addr); + if (!table) { table = (ENTRY*) phys_addr; } + } + + /* insert translation */ + addr_t const table_vo = vo - (vo & PAGE_MASK); + table->insert_translation(table_vo, pa, size, flags, alloc, scratch->next); + } + }; + + struct Remove_func + { + Translation_table_allocator *alloc; + Scratch::Page *scratch; + + Remove_func(Translation_table_allocator *alloc, + Scratch::Page *scratch) + : alloc(alloc), scratch(scratch) { } + + void operator () (addr_t const vo, addr_t const pa, + size_t const size, + Descriptor::access_t &desc) + { + if (!Descriptor::scratch(desc, scratch->addr)) { + + ENTRY* phys_addr = (ENTRY*) Descriptor::Pa::masked(desc); + ENTRY *table = (ENTRY*) alloc->virt_addr(phys_addr); + if (!table) { table = (ENTRY*) phys_addr; } + + addr_t const table_vo = vo - (vo & PAGE_MASK); + table->remove_translation(table_vo, size, alloc, scratch->next); + if (table->empty(scratch->next->addr)) { + destroy(alloc, table); + desc = scratch->addr; + } + } + } + }; + + template + void _range_op(addr_t vo, addr_t pa, size_t size, FUNC &&func) + { + for (size_t i = vo >> PAGE_SIZE_LOG2; size > 0; + i = vo >> PAGE_SIZE_LOG2) { + addr_t const end = (vo + PAGE_SIZE) & PAGE_MASK; + size_t const sz = min(size, end-vo); + + func(vo, pa, sz, _entries[i]); + + /* check whether we wrap */ + if (end < vo) { return; } + + size = size - sz; + vo += sz; + pa += sz; + } + } + + public: + + static constexpr size_t MIN_PAGE_SIZE_LOG2 = SIZE_LOG2_4KB; + static constexpr size_t ALIGNM_LOG2 = SIZE_LOG2_4KB; + + Pml4_table(Scratch::Page *scratch) + { + if (!aligned(this, ALIGNM_LOG2)) { throw Misaligned(); } + + for (size_t i = 0; i < MAX_ENTRIES; i++) { + _entries[i] = scratch->addr; + } + } + + /** + * Insert translations into this table + * + * \param vo offset of the virtual region represented + * by the translation within the virtual + * region represented by this table + * \param pa base of the physical backing store + * \param size size of the translated region + * \param flags mapping flags + * \param alloc second level translation table allocator + */ + void insert_translation(addr_t vo, addr_t pa, size_t size, + Page_flags const & flags, + Translation_table_allocator *alloc, + Scratch::Page *scratch) + { + _range_op(vo, pa, size, Insert_func(flags, alloc, scratch)); + } + + /** + * Remove translations that overlap with a given virtual region + * + * \param vo region offset within the tables virtual region + * \param size region size + * \param alloc second level translation table allocator + */ + void remove_translation(addr_t vo, size_t size, + Translation_table_allocator *alloc, + Scratch::Page *scratch) + { + _range_op(vo, 0, size, Remove_func(alloc, scratch)); + } +} __attribute__((aligned(1 << ALIGNM_LOG2))); + + +namespace Igd { + + struct Ppgtt; + struct Ppgtt_scratch; +} + + +/** + * Per-process graphics translation table + */ +struct Igd::Ppgtt : public Genode::Pml4_table +{ + Ppgtt(Genode::Scratch::Page *scratch) : Pml4_table(scratch) { } +}; + + +/** + * Per-process graphics translation table scratch pages + */ +struct Igd::Ppgtt_scratch : public Genode::Scratch +{ + Ppgtt_scratch(Genode::Allocator_guard &guard, + Utils::Backend_alloc &backend) + : Scratch(guard, backend) { } +}; + +#endif /* _PPGTT_H_ */ diff --git a/repos/os/src/drivers/gpu/intel/ppgtt_allocator.h b/repos/os/src/drivers/gpu/intel/ppgtt_allocator.h new file mode 100644 index 000000000..a93010b5c --- /dev/null +++ b/repos/os/src/drivers/gpu/intel/ppgtt_allocator.h @@ -0,0 +1,98 @@ +/* + * \brief PPGTT translation table allocator + * \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 _PPGTT_ALLOCATOR_H_ +#define _PPGTT_ALLOCATOR_H_ + +/* Genode includes */ +#include + +/* local includes */ +#include +#include + + +namespace Igd { + + class Ppgtt_allocator; +} + + +class Igd::Ppgtt_allocator : public Genode::Translation_table_allocator +{ + private: + + Genode::Region_map &_rm; + Genode::Allocator_guard &_guard; + Utils::Backend_alloc &_backend; + + enum { ELEMENTS = 256, }; + Utils::Address_map _map; + + public: + + Ppgtt_allocator(Genode::Region_map &rm, + Genode::Allocator_guard &guard, + Utils::Backend_alloc &backend) + : _rm(rm), _guard(guard), _backend(backend) { } + + /************************* + ** Allocator interface ** + *************************/ + + bool alloc(size_t size, void **out_addr) + { + Genode::Ram_dataspace_capability ds = _backend.alloc(_guard, size); + if (!ds.valid()) { return false; } + + *out_addr = _rm.attach(ds); + return _map.add(ds, *out_addr); + } + + void free(void *addr, size_t size) + { + if (addr == nullptr) { return; } + + Genode::Ram_dataspace_capability cap = _map.remove(addr); + if (!cap.valid()) { + Genode::error("could not lookup capability for addr: ", addr); + return; + } + + _rm.detach(addr); + _backend.free(_guard, cap); + } + + bool need_size_for_free() const override { return false; } + size_t overhead(size_t size) const override { return 0; } + + /******************************************* + ** Translation_table_allocator interface ** + *******************************************/ + + void *phys_addr(void *va) + { + if (va == nullptr) { return nullptr; } + typename Utils::Address_map::Element *e = _map.phys_addr(va); + return e ? (void*)e->pa : nullptr; + } + + void *virt_addr(void *pa) + { + if (pa == nullptr) { return nullptr; } + typename Utils::Address_map::Element *e = _map.virt_addr(pa); + return e ? (void*)e->va : nullptr; + } +}; + +#endif /* _PPGTT_ALLOCATOR_H_ */ diff --git a/repos/os/src/drivers/gpu/intel/ring_buffer.h b/repos/os/src/drivers/gpu/intel/ring_buffer.h new file mode 100644 index 000000000..e835ad942 --- /dev/null +++ b/repos/os/src/drivers/gpu/intel/ring_buffer.h @@ -0,0 +1,207 @@ +/* + * \brief Broadwell ring buffer + * \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 _RING_BUFFER_H_ +#define _RING_BUFFER_H_ + +/* Genode includes */ +#include + +/* local includes */ +#include +#include + + +namespace Igd { + + class Ring_buffer; +} + + +/* + */ +class Igd::Ring_buffer +{ + public: + + using Index = size_t; + + private: + + uint32_t *_dwords; + Index _max; + Index _tail; + Index _head; + + public: + + Ring_buffer(addr_t base, + size_t length) + : + _dwords((uint32_t*)base), _max(length / sizeof (uint32_t)), + _tail(0), _head(0) + { + /* initial clear */ + Genode::memset(_dwords, 0, length); + } + + /** + * Clear ring buffer and reset tail + */ + void reset() + { + Genode::memset(_dwords, 0, _max * sizeof(uint32_t)); + _tail = 0; + } + + /** + * Clear remaining ring buffer and reset tail + */ + void reset_and_fill_zero() + { + Genode::size_t const bytes = (_max - _tail) * sizeof(uint32_t); + Genode::memset(_dwords + _tail, 0, bytes); + _tail = 0; + } + + /** + * Get current tail + * + * \return current tail index + */ + Index tail() const { return _tail; } + + /** + * Get current head + * + * \return current head index + */ + Index head() const { return _head; } + + /** + * Update head + * + * \param head new head index + */ + void update_head(Index head) + { + _head = head; + } + + /** + * Insert new command at given index + * + * \param cmmd new command + * \param index index for new command + */ + Index insert(Igd::Cmd_header cmd, Index index) + { + if (index < _tail) { throw -1; } + if (index > _max) { throw -2; } + + _dwords[index] = cmd.value; + _tail++; + + if (_tail > _max) { + Genode::warning("ring buffer wrapped ", + "_tail: ", _tail, " ", "_max: ", _max); + _tail = 0; + } + + if (_tail == _head) { + Genode::error("tail: ", Genode::Hex(_tail), " == head: ", + Genode::Hex(_head), " in ring buffer"); + } + + return 1; + } + + /** + * Append command to ring buffer + * + * \param cmd command + * + * \return number of command words written + */ + Index append(Igd::Cmd_header cmd) + { + return insert(cmd, _tail); + } + + /** + * Append command to ring buffer + * + * \param v dword value + * + * \return number of command words written + */ + Index append(uint32_t v) + { + return insert(Igd::Cmd_header(v), _tail); + } + + /** + * Check if remaing space is enough for number of commands + * + * \return true if space is sufficient, otherwise false is returned + */ + bool avail(Index num) const + { + return (_tail + num) < _max; + } + + /** + * Get total number of commands that fit into the ring buffer + * + * \return number of total commands fitting the ring buffer + */ + Index max() const { return _max; } + + /** + * Flush range of commands in ring buffer + * + * \param from index to start from + * \param to index to end on + */ + void flush(Index from, Index to) const + { + uint32_t *start = _dwords + from; + for (Index i = 0; i < (to - from); i++) { + uint32_t *addr = start++; + Utils::clflush(addr); + } + } + + /********************* + ** Debug interface ** + *********************/ + + void dump(size_t dw_limit = 0) const + { + using namespace Genode; + + size_t const max = dw_limit ? dw_limit : _max; + + log("Ring_buffer: ", Hex(*_dwords), " max: ", _max, " (limit: ", max, ")"); + + for (size_t i = 0; i < max; i++) { + log(Hex(i*4, Hex::PREFIX, Hex::PAD), " ", + Hex(_dwords[i], Hex::PREFIX, Hex::PAD), + i == _tail ? " T " : " ", + i == _head ? " H " : " " + ); + } + } +}; + +#endif /* _RING_BUFFER_H_ */ diff --git a/repos/os/src/drivers/gpu/intel/target.mk b/repos/os/src/drivers/gpu/intel/target.mk new file mode 100644 index 000000000..5600a10ef --- /dev/null +++ b/repos/os/src/drivers/gpu/intel/target.mk @@ -0,0 +1,7 @@ +TARGET = intel_gpu_drv +SRC_CC = main.cc mmio_dump.cc +LIBS = base + +REQUIRES = x86 + +INC_DIR += $(PRG_DIR) diff --git a/repos/os/src/drivers/gpu/intel/types.h b/repos/os/src/drivers/gpu/intel/types.h new file mode 100644 index 000000000..487e24b55 --- /dev/null +++ b/repos/os/src/drivers/gpu/intel/types.h @@ -0,0 +1,37 @@ +/* + * \brief Broadwell type 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 _TYPES_H_ +#define _TYPES_H_ + +/* Genode includes */ +#include + +namespace Igd { + + using Ram_dataspace_capability = Genode::Ram_dataspace_capability; + using Ram = Genode::Ram_dataspace_capability; + + using uint8_t = Genode::uint8_t; + using uint16_t = Genode::uint16_t; + using uint32_t = Genode::uint32_t; + using uint64_t = Genode::uint64_t; + using addr_t = Genode::addr_t; + using size_t = Genode::size_t; + + enum { PAGE_SIZE = 4096, }; + + inline void wmb() { asm volatile ("sfence": : :"memory"); } +} + +#endif /* _TYPES_H_ */ diff --git a/repos/os/src/drivers/gpu/intel/utils.h b/repos/os/src/drivers/gpu/intel/utils.h new file mode 100644 index 000000000..399c0da2e --- /dev/null +++ b/repos/os/src/drivers/gpu/intel/utils.h @@ -0,0 +1,133 @@ +/* + * \brief Helper utilities for Broadwell GPU mutliplexer + * \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 _UTILS_H_ +#define _UTILS_H_ + +/* Genode includes */ +#include + +namespace Utils { + + using size_t = Genode::size_t; + using uint32_t = Genode::uint32_t; + using Ram = Genode::Ram_dataspace_capability; + + /* + * Backend allocator interface + */ + struct Backend_alloc + { + virtual Ram alloc(Genode::Allocator_guard &, Genode::size_t) = 0; + virtual void free(Genode::Allocator_guard &, Ram) = 0; + }; + + template class Address_map; + + void clflush(volatile void *addr) + { + asm volatile("clflush %0" : "+m" (*(volatile char *)addr)); + } + +} + +template +class Utils::Address_map +{ + public: + + struct Element + { + Ram ds_cap; + void *va; + void *pa; + uint32_t index; + + Element() : ds_cap(Ram()), va(nullptr), pa(nullptr), index(0) { } + + Element(uint32_t index, Ram ds_cap, void *va) + : + ds_cap(ds_cap), + va(va), + pa((void *)Genode::Dataspace_client(ds_cap).phys_addr()), + index(index) + { } + + bool valid() { return va && pa; } + }; + + private: + + Element _map[ELEMENTS]; + + public: + + Address_map() + { + Genode::memset(&_map, 0, sizeof(_map)); + } + + ~Address_map() + { + for (uint32_t i = 0; i < ELEMENTS; i++) { + if (!_map[i].valid()) { continue; } + + Genode::error("Address_map entry ", Genode::Hex(i), " ", + "still valid (", _map[i].va, "/", _map[i].pa, ")"); + } + } + + bool add(Ram ds_cap, void *va) + { + for (uint32_t i = 0; i < ELEMENTS; i++) { + if (_map[i].valid()) { continue; } + + _map[i] = Element(i, ds_cap, va); + return true; + } + return false; + } + + Ram remove(void *va) + { + Ram cap; + + for (uint32_t i = 0; i < ELEMENTS; i++) { + if (_map[i].va != va) { continue; } + + cap = _map[i].ds_cap; + _map[i] = Element(); + break; + } + + return cap; + } + + Element *phys_addr(void *va) + { + for (uint32_t i = 0; i < ELEMENTS; i++) { + if (_map[i].va == va) { return &_map[i]; } + } + return nullptr; + } + + Element *virt_addr(void *pa) + { + for (uint32_t i = 0; i < ELEMENTS; i++) { + if (_map[i].pa == pa) { return &_map[i]; } + } + return nullptr; + } +}; + +#endif /* _UTILS_H_ */