os: add Gpu driver for Intel Gen8 HD graphics

This commit introduces a experimental 3D driver for Intel Gen8 HD
graphics devices as well as the corresponding Gpu session.

Fixes #2507.
This commit is contained in:
Josef Söntgen 2017-04-26 11:10:52 +02:00 committed by Christian Helmuth
parent 1ac7b034ba
commit 198019edca
17 changed files with 6687 additions and 0 deletions

View File

@ -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 <base/capability.h>
#include <gpu_session/gpu_session.h>
namespace Gpu { typedef Genode::Capability<Session> Session_capability; }
#endif /* _INCLUDE__GPU_SESSION__CAPABILITY_H_ */

View File

@ -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 <base/rpc_client.h>
#include <gpu_session/capability.h>
namespace Gpu { class Session_client; }
class Gpu::Session_client : public Genode::Rpc_client<Session>
{
public:
/**
* Constructor
*
* \param session session capability
*/
Session_client(Session_capability session)
: Genode::Rpc_client<Session>(session) { }
/***********************
** Session interface **
***********************/
Info info() const override {
return call<Rpc_info>(); }
void exec_buffer(Genode::Dataspace_capability cap, Genode::size_t size) override {
call<Rpc_exec_buffer>(cap, size); }
void completion_sigh(Genode::Signal_context_capability sigh) override {
call<Rpc_completion_sigh>(sigh); }
Genode::Dataspace_capability alloc_buffer(Genode::size_t size) override {
return call<Rpc_alloc_buffer>(size); }
void free_buffer(Genode::Dataspace_capability ds) override {
call<Rpc_free_buffer>(ds); }
Genode::Dataspace_capability map_buffer(Genode::Dataspace_capability ds,
bool aperture) override {
return call<Rpc_map_buffer>(ds, aperture); }
void unmap_buffer(Genode::Dataspace_capability ds) override {
call<Rpc_unmap_buffer>(ds); }
bool map_buffer_ppgtt(Genode::Dataspace_capability ds,
Gpu::addr_t va) override {
return call<Rpc_map_buffer_ppgtt>(ds, va); }
void unmap_buffer_ppgtt(Genode::Dataspace_capability ds, Gpu::addr_t va) override {
call<Rpc_unmap_buffer_ppgtt>(ds, va); }
bool set_tiling(Genode::Dataspace_capability ds, unsigned mode) override {
return call<Rpc_set_tiling>(ds, mode); }
};
#endif /* _INCLUDE__GPU_SESSION__CLIENT_H_ */

View File

@ -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 <gpu_session/client.h>
#include <base/connection.h>
#include <base/allocator.h>
namespace Gpu { struct Connection; }
struct Gpu::Connection : Genode::Connection<Session>, Session_client
{
/**
* Issue session request
*
* \noapi
*/
Capability<Gpu::Session> _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<Session>(env, _session(env.parent(), label, quota)),
Session_client(cap())
{ }
};
#endif /* _INCLUDE__GPU_SESSION__CONNECTION_H_ */

View File

@ -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 <session/session.h>
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_ */

View File

@ -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 <util/mmio.h>
#include <util/string.h>
/* local includes */
#include <types.h>
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_ */

View File

@ -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 <util/mmio.h>
#include <util/string.h>
/* local includes */
#include <types.h>
#include <commands.h>
namespace Igd {
struct Context_status_qword;
struct Common_context_regs;
struct Hardware_status_page;
template <addr_t RING_BASE> class Execlist_context;
template <addr_t RING_BASE> 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 <long int OFFSET>
struct Common_register : Register<OFFSET * sizeof(uint32_t), 32> { };
template <long int OFFSET, size_t NUM>
struct Common_register_array : Register_array<OFFSET * sizeof(uint32_t), 32, NUM, 32> { };
addr_t _base;
Common_context_regs(addr_t base) : Genode::Mmio(base), _base(base) { }
addr_t base() const { return _base; }
template <typename T>
void write_offset(typename T::access_t const value)
{
write<T>(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 <Genode::addr_t RING_BASE>
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<Load_immediate_header>(immediate_header);
write_offset<Context_control_mmio>(RING_BASE);
{
typename Context_control_value::access_t v = read<Context_control_value>();
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<Context_control_value>(v);
}
write_offset<Ring_buffer_head_mmio>(RING_BASE);
write_offset<Ring_buffer_tail_mmio>(RING_BASE);
write_offset<Ring_buffer_start_mmio>(RING_BASE);
{
typename Ring_buffer_start_value::access_t v = read<Ring_buffer_start_value>();
/* 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<Ring_buffer_start_value>(v);
}
write_offset<Ring_buffer_control_mmio>(RING_BASE);
{
typename Ring_buffer_control_value::access_t v = read<Ring_buffer_control_value>();
/* 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<Ring_buffer_control_value>(v);
}
write_offset<Bb_addr_udw_mmio>(RING_BASE);
write_offset<Bb_addr_mmio>(RING_BASE);
write_offset<Bb_state_mmio>(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<Bb_state_value>(v);
}
write_offset<Sbb_addr_udw_mmio>(RING_BASE);
write_offset<Sbb_addr_mmio>(RING_BASE);
write_offset<Sbb_state_mmio>(RING_BASE);
}
size_t tail_offset()
{
return read<typename Ring_buffer_tail_value::Tail_offset>();
}
void tail_offset(size_t offset)
{
write<typename Ring_buffer_tail_value::Tail_offset>(offset);
}
size_t head_offset()
{
return read<typename Ring_buffer_head_value::Head_offset>();
}
/*********************
** Debug interface **
*********************/
void dump()
{
using namespace Genode;
log("Execlist_context");
log(" Load_immediate_header: ", Hex(read<Load_immediate_header>(), Hex::PREFIX, Hex::PAD));
log(" Context_control: ", Hex(read<Context_control_value>(), Hex::PREFIX, Hex::PAD));
log(" Ring_buffer_head: ", Hex(read<Ring_buffer_head_value>(), Hex::PREFIX, Hex::PAD));
log(" Wrap_count: ", Hex(read<typename Ring_buffer_head_value::Wrap_count>(), Hex::PREFIX, Hex::PAD));
log(" Ring_buffer_tail: ", Hex(read<Ring_buffer_tail_value>(), Hex::PREFIX, Hex::PAD));
log(" Ring_buffer_start: ", Hex(read<Ring_buffer_start_value>(), Hex::PREFIX, Hex::PAD));
log(" Ring_buffer_control: ", Hex(read<Ring_buffer_control_value>(), Hex::PREFIX, Hex::PAD));
log(" Bb_addr_udw: ", Hex(read<Bb_addr_udw_value>(), Hex::PREFIX, Hex::PAD));
log(" Bb_addr: ", Hex(read<Bb_addr_value>(), Hex::PREFIX, Hex::PAD));
log(" Bb_state: ", Hex(read<Bb_state_value>(), Hex::PREFIX, Hex::PAD));
log(" Sbb_addr_udw: ", Hex(read<Sbb_addr_udw_value>(), Hex::PREFIX, Hex::PAD));
log(" Sbb_addr: ", Hex(read<Sbb_addr_value>(), Hex::PREFIX, Hex::PAD));
log(" Sbb_state: ", Hex(read<Sbb_state_value>(), Hex::PREFIX, Hex::PAD));
}
};
template <Genode::addr_t RING_BASE>
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<Load_immediate_header>(0x11001011);
write_offset<Cs_ctx_timestamp_mmio>(RING_BASE);
{
write<Cs_ctx_timestamp_mmio>(0);
}
write_offset<Pdp_3_udw_mmio>(RING_BASE);
write_offset<Pdp_3_ldw_mmio>(RING_BASE);
write_offset<Pdp_2_udw_mmio>(RING_BASE);
write_offset<Pdp_2_ldw_mmio>(RING_BASE);
write_offset<Pdp_1_udw_mmio>(RING_BASE);
write_offset<Pdp_1_ldw_mmio>(RING_BASE);
write_offset<Pdp_0_udw_mmio>(RING_BASE);
{
Genode::uint32_t const udw = (Genode::uint32_t)((plm4_addr >> 16) >> 16);
write<Pdp_0_udw_value>(udw);
}
write_offset<Pdp_0_ldw_mmio>(RING_BASE);
{
Genode::uint32_t const ldw = (Genode::uint32_t)plm4_addr;
write<Pdp_0_ldw_value>(ldw);
}
write_offset<R_pwr_clk_state_mmio>(RING_BASE);
}
/*********************
** Debug interface **
*********************/
void dump()
{
using namespace Genode;
using C = Ppgtt_context<RING_BASE>;
log("Ppgtt_context");
log(" Pdp_0_udw: ", Hex(read<C::Pdp_0_udw_value>(), Hex::PREFIX, Hex::PAD));
log(" Pdp_0_ldw: ", Hex(read<C::Pdp_0_ldw_value>(), 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<RCS_RING_BASE> _execlist_context;
Ppgtt_context<RCS_RING_BASE> _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<RCS_RING_BASE>;
_execlist_context.write_offset<C::Bb_per_ctx_ptr_mmio>(RCS_RING_BASE);
{
using R = C::Bb_per_ctx_ptr_value;
typename R::access_t v = _execlist_context.read<R>();
R::Address::set(v, 0);
R::Valid::set(v, 0);
_execlist_context.write<R>(v);
}
_execlist_context.write_offset<C::Rcs_indirect_ctx_mmio>(RCS_RING_BASE);
{
using R = C::Rcs_indirect_ctx_value;
typename R::access_t v = _execlist_context.read<R>();
R::Address::set(v, 0);
R::Size::set(v, 0);
_execlist_context.write<R>(v);
}
_execlist_context.write_offset<C::Rcs_indirect_ctx_offset_mmio>(RCS_RING_BASE);
{
using R = C::Rcs_indirect_ctx_offset_value;
typename R::access_t v = _execlist_context.read<R>();
R::Offset::set(v, 0);
_execlist_context.write<R>(v);
}
using P = Ppgtt_context<RCS_RING_BASE>;
_ppgtt_context.write<P::Load_immediate_header_2>(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<RCS_RING_BASE>;
log(" Bb_per_ctx_ptr: ", Hex(_execlist_context.read<C::Bb_per_ctx_ptr_value>(), Hex::PREFIX, Hex::PAD));
log(" Rcs_indirect_ctx: ", Hex(_execlist_context.read<C::Rcs_indirect_ctx_value>(), Hex::PREFIX, Hex::PAD));
log(" Rcs_indirect_ctx_offset: ", Hex(_execlist_context.read<C::Rcs_indirect_ctx_offset_value>(), 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<H::Interrupt_status_register_storage>(),
Hex::PREFIX, Hex::PAD));
log(" Ring_head_ptr_storage: ",
Hex(_hw_status_page.read<H::Ring_head_ptr_storage>(),
Hex::PREFIX, Hex::PAD));
for (int i = 0; i < H::CONTEXT_STATUS_DWORDS_NUM; i++) {
log(" Context_status_dwords: ",
Hex(_hw_status_page.read<H::Context_status_dwords>(i),
Hex::PREFIX, Hex::PAD));
}
log(" Last_written_status_offset: ",
Hex(_hw_status_page.read<H::Last_written_status_offset>(),
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_ */

View File

@ -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 <util/register.h>
/* local includes */
#include <types.h>
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_ */

View File

@ -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 <util/bit_array.h>
/* local includes */
#include <mmio.h>
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_ */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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 <mmio.h>
#include <context.h>
void Igd::Mmio::dump()
{
using namespace Genode;
log("MMIO vaddr:", Hex(base()), " size:", Hex(Igd::Mmio::SIZE));
log("GFX_MODE: ", Hex(read<GFX_MODE>()));
log(" Privilege_check_disable: ", Hex(read<GFX_MODE::Privilege_check_disable>()));
log(" Execlist_enable: ", Hex(read<GFX_MODE::Execlist_enable>()));
log(" Virtual_addressing_enable: ", Hex(read<GFX_MODE::Virtual_addressing_enable>()));
log(" Ppgtt_enable: ", Hex(read<GFX_MODE::Ppgtt_enable>()));
log("HWS_PGA: ", Hex(read<HWS_PGA_RCSUNIT>()));
log("HWSTAM: ", Hex(read<HWSTAM>()));
}
void Igd::Mmio::power_dump()
{
using namespace Genode;
log("PWR_WELL_CTL2");
log(" Misc_io_power_state: ", Hex(read<PWR_WELL_CTL2::Misc_io_power_state>()));
log(" Misc_io_power_request: ", Hex(read<PWR_WELL_CTL2::Misc_io_power_request>()));
log(" Ddi_a_and_ddi_e_io_power_state: ", Hex(read<PWR_WELL_CTL2::Ddi_a_and_ddi_e_io_power_state>()));
log(" Ddi_a_and_ddi_e_io_power_request: ", Hex(read<PWR_WELL_CTL2::Ddi_a_and_ddi_e_io_power_request>()));
log(" Ddi_b_io_power_state: ", Hex(read<PWR_WELL_CTL2::Ddi_b_io_power_state>()));
log(" Ddi_b_io_power_request: ", Hex(read<PWR_WELL_CTL2::Ddi_b_io_power_request>()));
log(" Ddi_c_io_power_state: ", Hex(read<PWR_WELL_CTL2::Ddi_c_io_power_state>()));
log(" Ddi_c_io_power_request: ", Hex(read<PWR_WELL_CTL2::Ddi_c_io_power_request>()));
log(" Ddi_d_io_power_state: ", Hex(read<PWR_WELL_CTL2::Ddi_d_io_power_state>()));
log(" Ddi_d_io_power_request: ", Hex(read<PWR_WELL_CTL2::Ddi_d_io_power_request>()));
log(" Power_well_1_state: ", Hex(read<PWR_WELL_CTL2::Power_well_1_state>()));
log(" Power_well_1_request: ", Hex(read<PWR_WELL_CTL2::Power_well_1_request>()));
log(" Power_well_2_state: ", Hex(read<PWR_WELL_CTL2::Power_well_2_state>()));
log(" Power_well_2_request: ", Hex(read<PWR_WELL_CTL2::Power_well_2_request>()));
}
void Igd::Mmio::error_dump()
{
using namespace Genode;
log("ERROR: ", Hex(read<ERROR>()));
if (read<ERROR>()) {
log(" Ctx_fault_ctxt_not_prsnt_err: ", Hex(read<ERROR::Ctx_fault_ctxt_not_prsnt_err>()));
log(" Ctx_fault_root_not_prsnt_err: ", Hex(read<ERROR::Ctx_fault_root_not_prsnt_err>()));
log(" Ctx_fault_pasid_not_prsnt_err: ", Hex(read<ERROR::Ctx_fault_pasid_not_prsnt_err>()));
log(" Ctx_fault_pasid_ovflw_err: ", Hex(read<ERROR::Ctx_fault_pasid_ovflw_err>()));
log(" Ctx_fault_pasid_dis_err: ", Hex(read<ERROR::Ctx_fault_pasid_dis_err>()));
log(" Rstrm_fault_nowb_atomic_err: ", Hex(read<ERROR::Rstrm_fault_nowb_atomic_err>()));
log(" Unloaded_pd_error: ", Hex(read<ERROR::Unloaded_pd_error>()));
log(" Hws_page_fault_error: ", Hex(read<ERROR::Hws_page_fault_error>()));
log(" Invalid_page_directory_entry_error: ", Hex(read<ERROR::Invalid_page_directory_entry_error>()));
log(" Ctx_page_fault_error: ", Hex(read<ERROR::Ctx_page_fault_error>()));
log(" Tlb_fault_error: ", Hex(read<ERROR::Tlb_fault_error>()));
}
log("ERROR_2: ", Hex(read<ERROR_2>()));
if (read<ERROR_2>()) {
log(" Tlbpend_reg_faultcnt: ", Hex(read<ERROR_2::Tlbpend_reg_faultcnt>()));
}
log("RCS_EIR: ", Hex(read<RCS_EIR>()));
log("RCS_ESR: ", Hex(read<RCS_ESR>()));
log("RCS_EMR: ", Hex(read<RCS_EMR>()));
log("RCS_INSTDONE: ", Hex(read<RCS_INSTDONE>()));
if (read<RCS_INSTDONE>() != RCS_INSTDONE::DEFAULT_VALUE ||
read<RCS_INSTDONE>() != 0xffffffff) {
log(" Row_0_eu_0_done : ", Hex(read<RCS_INSTDONE::Row_0_eu_0_done >()));
log(" Row_0_eu_1_done : ", Hex(read<RCS_INSTDONE::Row_0_eu_1_done >()));
log(" Row_0_eu_2_done : ", Hex(read<RCS_INSTDONE::Row_0_eu_2_done >()));
log(" Row_0_eu_3_done : ", Hex(read<RCS_INSTDONE::Row_0_eu_3_done >()));
log(" Row_1_eu_0_done : ", Hex(read<RCS_INSTDONE::Row_1_eu_0_done >()));
log(" Row_1_eu_1_done : ", Hex(read<RCS_INSTDONE::Row_1_eu_1_done >()));
log(" Row_1_eu_2_done : ", Hex(read<RCS_INSTDONE::Row_1_eu_2_done >()));
log(" Row_1_eu_3_done : ", Hex(read<RCS_INSTDONE::Row_1_eu_3_done >()));
log(" Sf_done : ", Hex(read<RCS_INSTDONE::Sf_done >()));
log(" Se_done : ", Hex(read<RCS_INSTDONE::Se_done >()));
log(" Windower_done : ", Hex(read<RCS_INSTDONE::Windower_done >()));
log(" Reserved1 : ", Hex(read<RCS_INSTDONE::Reserved1 >()));
log(" Reserved2 : ", Hex(read<RCS_INSTDONE::Reserved2 >()));
log(" Dip_done : ", Hex(read<RCS_INSTDONE::Dip_done >()));
log(" Pl_done : ", Hex(read<RCS_INSTDONE::Pl_done >()));
log(" Dg_done : ", Hex(read<RCS_INSTDONE::Dg_done >()));
log(" Qc_done : ", Hex(read<RCS_INSTDONE::Qc_done >()));
log(" Ft_done : ", Hex(read<RCS_INSTDONE::Ft_done >()));
log(" Dm_done : ", Hex(read<RCS_INSTDONE::Dm_done >()));
log(" Sc_done : ", Hex(read<RCS_INSTDONE::Sc_done >()));
log(" Fl_done : ", Hex(read<RCS_INSTDONE::Fl_done >()));
log(" By_done : ", Hex(read<RCS_INSTDONE::By_done >()));
log(" Ps_done : ", Hex(read<RCS_INSTDONE::Ps_done >()));
log(" Cc_done : ", Hex(read<RCS_INSTDONE::Cc_done >()));
log(" Map_fl_done : ", Hex(read<RCS_INSTDONE::Map_fl_done >()));
log(" Map_l2_idle : ", Hex(read<RCS_INSTDONE::Map_l2_idle >()));
log(" Msg_arb_0_done : ", Hex(read<RCS_INSTDONE::Msg_arb_0_done >()));
log(" Msg_arb_1_done : ", Hex(read<RCS_INSTDONE::Msg_arb_1_done >()));
log(" Ic_row_0_done : ", Hex(read<RCS_INSTDONE::Ic_row_0_done >()));
log(" Ic_row_1_done : ", Hex(read<RCS_INSTDONE::Ic_row_1_done >()));
log(" Cp_done : ", Hex(read<RCS_INSTDONE::Cp_done >()));
log(" Ring_0_enable : ", Hex(read<RCS_INSTDONE::Ring_0_enable >()));
}
log("RCS_INSTDONE_1: ", Hex(read<RCS_INSTDONE_1>()));
log("RCS_ACTHD: ", Hex(read<RCS_ACTHD>()));
log("IPEHR: ", Hex(read<IPEHR>()));
log("IPEIR: ", Hex(read<IPEIR>()));
log("PGTBL_ER: ", Hex(read<PGTBL_ER>()));
}
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<MASTER_INT_CTL::Master_interrupt_enable>()));
log(" Pcu_interrupts_pending: ", Hex(read<MASTER_INT_CTL::Pcu_interrupts_pending>()));
log(" Audio_codec_interrupts_pending: ", Hex(read<MASTER_INT_CTL::Audio_codec_interrupts_pending>()));
log(" De_pch_interrupts_pending: ", Hex(read<MASTER_INT_CTL::De_pch_interrupts_pending>()));
log(" De_misc_interrupts_pending: ", Hex(read<MASTER_INT_CTL::De_misc_interrupts_pending>()));
log(" De_port_interrupts_pending: ", Hex(read<MASTER_INT_CTL::De_port_interrupts_pending>()));
log(" De_pipe_c_interrupts_pending: ", Hex(read<MASTER_INT_CTL::De_pipe_c_interrupts_pending>()));
log(" De_pipe_b_interrupts_pending: ", Hex(read<MASTER_INT_CTL::De_pipe_b_interrupts_pending>()));
log(" De_pipe_a_interrupts_pending: ", Hex(read<MASTER_INT_CTL::De_pipe_a_interrupts_pending>()));
log(" Vebox_interrupts_pending: ", Hex(read<MASTER_INT_CTL::Vebox_interrupts_pending>()));
log(" Gtpm_interrupts_pending: ", Hex(read<MASTER_INT_CTL::Gtpm_interrupts_pending>()));
log(" Vcs2_interrupts_pending: ", Hex(read<MASTER_INT_CTL::Vcs2_interrupts_pending>()));
log(" Vcs1_interrupts_pending: ", Hex(read<MASTER_INT_CTL::Vcs1_interrupts_pending>()));
log(" Blitter_interrupts_pending: ", Hex(read<MASTER_INT_CTL::Blitter_interrupts_pending>()));
log(" Render_interrupts_pending: ", Hex(read<MASTER_INT_CTL::Render_interrupts_pending>()));
#ifndef DUMP_GT_0
#define DUMP_GT_0(reg) \
log(#reg); \
log(" Bcs_wait_on_semaphore: ", Hex(read<reg::Bcs_wait_on_semaphore>())); \
log(" Bcs_ctx_switch_interrupt: ", Hex(read<reg::Bcs_ctx_switch_interrupt>())); \
log(" Bcs_mi_flush_dw_notify: ", Hex(read<reg::Bcs_mi_flush_dw_notify>())); \
log(" Bcs_error_interrupt: ", Hex(read<reg::Bcs_error_interrupt>())); \
log(" Bcs_mi_user_interrupt: ", Hex(read<reg::Bcs_mi_user_interrupt>())); \
log(" Cs_wait_on_semaphore: ", Hex(read<reg::Cs_wait_on_semaphore>())); \
log(" Cs_l3_counter_slave: ", Hex(read<reg::Cs_l3_counter_slave>())); \
log(" Cs_ctx_switch_interrupt: ", Hex(read<reg::Cs_ctx_switch_interrupt>())); \
log(" Page_fault_error: ", Hex(read<reg::Page_fault_error>())); \
log(" Cs_watchdog_counter_expired: ", Hex(read<reg::Cs_watchdog_counter_expired>())); \
log(" L3_parity_error: ", Hex(read<reg::L3_parity_error>())); \
log(" Cs_pipe_control_notify: ", Hex(read<reg::Cs_pipe_control_notify>())); \
log(" Cs_error_interrupt: ", Hex(read<reg::Cs_error_interrupt>())); \
log(" Cs_mi_user_interrupt: ", Hex(read<reg::Cs_mi_user_interrupt>()));
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<reg::Vcs2_wait_on_semaphore>())); \
log(" Vcs2_ctx_switch_interrupt: ", Hex(read<reg::Vcs2_ctx_switch_interrupt>())); \
log(" Vcs2_watchdog_counter_expired: ", Hex(read<reg::Vcs2_watchdog_counter_expired>())); \
log(" Vcs2_mi_flush_dw_notify: ", Hex(read<reg::Vcs2_mi_flush_dw_notify>())); \
log(" Vcs2_error_interrupt: ", Hex(read<reg::Vcs2_error_interrupt>())); \
log(" Vcs2_mi_user_interrupt: ", Hex(read<reg::Vcs2_mi_user_interrupt>())); \
log(" Vcs1_wait_on_semaphore: ", Hex(read<reg::Vcs1_wait_on_semaphore>())); \
log(" Vcs1_ctx_switch_interrupt: ", Hex(read<reg::Vcs1_ctx_switch_interrupt>())); \
log(" Vcs1_watchdog_counter_expired: ", Hex(read<reg::Vcs1_watchdog_counter_expired>())); \
log(" Vcs1_pipe_control_notify: ", Hex(read<reg::Vcs1_pipe_control_notify>())); \
log(" Vcs1_error_interrupt: ", Hex(read<reg::Vcs1_error_interrupt>())); \
log(" Vcs1_mi_user_interrupt: ", Hex(read<reg::Vcs1_mi_user_interrupt>())); \
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<reg::Unslice_frequency_control_up_interrupt>())); \
log(" Unslice_frequency_control_down_interrupt: ", Hex(read<reg::Unslice_frequency_control_down_interrupt>())); \
log(" Nfafdl_frequency_up_interrupt: ", Hex(read<reg::Nfafdl_frequency_up_interrupt>())); \
log(" Nfafdl_frequency_down_interrupt: ", Hex(read<reg::Nfafdl_frequency_down_interrupt>())); \
log(" Gtpm_engines_idle_interrupt: ", Hex(read<reg::Gtpm_engines_idle_interrupt>())); \
log(" Gtpm_uncore_to_core_trap_interrupt: ", Hex(read<reg::Gtpm_uncore_to_core_trap_interrupt>())); \
log(" Gtpm_render_frequency_downwards_timeout_during_rc6_interrupt: ", Hex(read<reg::Gtpm_render_frequency_downwards_timeout_during_rc6_interrupt>())); \
log(" Gtpm_render_p_state_up_threshold_interrupt: ", Hex(read<reg::Gtpm_render_p_state_up_threshold_interrupt>())); \
log(" Gtpm_render_p_state_down_threshold_interrupt: ", Hex(read<reg::Gtpm_render_p_state_down_threshold_interrupt>())); \
log(" Gtpm_render_geyserville_up_evaluation_interval_interrupt: ", Hex(read<reg::Gtpm_render_geyserville_up_evaluation_interval_interrupt>())); \
log(" Gtpm_render_geyserville_down_evaluation_interval_interrupt: ", Hex(read<reg::Gtpm_render_geyserville_down_evaluation_interval_interrupt>()));
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<reg::Performance_monitoring_buffer_half_full_interrupt>())); \
log(" Vecs_wait_on_semaphore: ", Hex(read<reg::Vecs_wait_on_semaphore>())); \
log(" Vecs_ctx_switch_interrupt: ", Hex(read<reg::Vecs_ctx_switch_interrupt>())); \
log(" Vecs_mi_flush_dw_notify: ", Hex(read<reg::Vecs_mi_flush_dw_notify>())); \
log(" Vecs_error_interrupt: ", Hex(read<reg::Vecs_error_interrupt>())); \
log(" Vecs_mi_user_interrupt: ", Hex(read<reg::Vecs_mi_user_interrupt>())); \
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<FAULT_TLB_RB_DATA0>()));
Genode::log("FAULT_TLB_RB_DATA1: ", Genode::Hex(read<FAULT_TLB_RB_DATA1>()));
using namespace Genode;
uint64_t const addr = ((uint64_t)(read<FAULT_TLB_RB_DATA1::Fault_cycle_va>() & 0xf) << 44) |
((uint64_t) read<FAULT_TLB_RB_DATA0>() << 12);
Genode::log(" ggtt: ", read<FAULT_TLB_RB_DATA1::Cycle_gtt_sel>(), " "
"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<reg::Current_context_id>())); \
log(" Arbitration_enable: ", Hex(read<reg::Arbitration_enable>())); \
log(" Current_active_element_status: ", Hex(read<reg::Current_active_element_status>())); \
log(" Last_context_switch_reason: ", Hex(read<reg::Last_context_switch_reason>())); \
if (read<reg::Last_context_switch_reason>()) { \
Igd::Context_status_qword::access_t v = read<reg::Last_context_switch_reason>(); \
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<reg::Execlist_0_valid>())); \
log(" Execlist_1_valid: ", Hex(read<reg::Execlist_1_valid>())); \
log(" Execlist_queue_full: ", Hex(read<reg::Execlist_queue_full>())); \
log(" Execlist_write_pointer: ", Hex(read<reg::Execlist_write_pointer>())); \
log(" Current_execlist_pointer: ", Hex(read<reg::Current_execlist_pointer>()));
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<RCS_RING_CONTEXT_STATUS_PTR>();
uint32_t const wp = read<RCS_RING_CONTEXT_STATUS_PTR::Write_pointer>();
uint32_t const rp = read<RCS_RING_CONTEXT_STATUS_PTR::Read_pointer>();
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<CTXT_ST_BUF_RCSUNIT>(i);
uint32_t const csu = read<CTXT_ST_BUF_RCSUNIT::Context_status_udw>(i);
uint32_t const csl = read<CTXT_ST_BUF_RCSUNIT::Context_status_ldw>(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));
}
}

View File

@ -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 <base/allocator.h>
#include <base/cache.h>
#include <base/output.h>
#include <util/misc_math.h>
#include <util/register.h>
/* local includes */
#include <utils.h>
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 <typename ENTRY, unsigned PAGE_SIZE_LOG2, unsigned SIZE_LOG2>
class Page_directory;
using Level_3_translation_table =
Page_directory<Level_4_translation_table,
SIZE_LOG2_2MB, SIZE_LOG2_1GB>;
using Level_2_translation_table =
Page_directory<Level_3_translation_table,
SIZE_LOG2_1GB, SIZE_LOG2_512GB>;
/**
* 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<Pwt, Pcd, Pat> { }; /* 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 <typename FUNC>
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 <typename ENTRY, unsigned PAGE_SIZE_LOG2, unsigned SIZE_LOG2>
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<PAGE_SIZE_LOG2,
48 - PAGE_SIZE_LOG2> { };
/**
* Memory type
*/
struct Mt : Base::template Bitset_3<Base::Pwt,
Base::Pcd, Pat> { };
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<Base::Pwt,
Base::Pcd> { };
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 <typename FUNC>
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<Pwt, Pcd> { }; /* 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 <typename FUNC>
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_ */

View File

@ -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 <base/allocator_guard.h>
/* local includes */
#include <types.h>
#include <utils.h>
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<ELEMENTS> _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<ELEMENTS>::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<ELEMENTS>::Element *e = _map.virt_addr(pa);
return e ? (void*)e->va : nullptr;
}
};
#endif /* _PPGTT_ALLOCATOR_H_ */

View File

@ -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 <base/log.h>
/* local includes */
#include <types.h>
#include <commands.h>
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_ */

View File

@ -0,0 +1,7 @@
TARGET = intel_gpu_drv
SRC_CC = main.cc mmio_dump.cc
LIBS = base
REQUIRES = x86
INC_DIR += $(PRG_DIR)

View File

@ -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 <ram_session/ram_session.h>
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_ */

View File

@ -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 <ram_session/ram_session.h>
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 <unsigned int ELEMENTS> class Address_map;
void clflush(volatile void *addr)
{
asm volatile("clflush %0" : "+m" (*(volatile char *)addr));
}
}
template <unsigned int ELEMENTS>
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_ */