/*
 * \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; }

	/**
	 * 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 &)
		{
			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 = 0;
				Page   *next = nullptr;
			};

			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 /* vo */, addr_t /* pa */,
				                  size_t /* 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((addr_t)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 /* 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((addr_t)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 /* 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((addr_t)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_ */
