// vim:set ft=cpp: -*- Mode: C++ -*-
/*!
 * \file
 * L4::Kip class, memory descriptors.
 *
 * \author Alexander Warg <alexander.warg@os.inf.tu-dresden.de>
 * \ingroup l4_api
 *
 */
/*
 * (c) 2008-2009 Author(s)
 *     economic rights: Technische Universität Dresden (Germany)
 *
 * License: see LICENSE.spdx (in this directory or the directories above)
 */
#pragma once

#include <l4/cxx/static_vector>
#include <l4/sys/kip.h>

/* C++ version of memory descriptors */

/**
 * \defgroup l4_kip_api Kernel Interface Page
 * \ingroup l4_api
 * Kernel Interface Page.
 *
 * C++ interface for the Kernel Interface Page:<br>
 * \includefile{l4/sys/kip}
 */

namespace L4
{
  namespace Kip
  {
    /**
     * Memory descriptors stored in the kernel interface page.
     * \ingroup l4_kip_api
     *
     * \includefile{l4/sys/kip}
     */
    class Mem_desc
    {
    public:
      /**
       * Memory types.
       */
      enum Mem_type
      {
        Undefined    = 0x0,  ///< Undefined memory
        Conventional = 0x1,  ///< Conventional memory
        Reserved     = 0x2,  ///< Reserved region, do not use this memory
        Dedicated    = 0x3,  ///< Dedicated
        Shared       = 0x4,  ///< Shared

        Info         = 0xd,  ///< Info by boot loader
        Bootloader   = 0xe,  ///< Memory belongs to the boot loader
        Arch         = 0xf   ///< Architecture specific memory
      };

      /**
       * Memory sub types for the Mem_type::Info type
       */
      enum Info_sub_type
      {
        Info_acpi_rsdp = 0, ///< Physical address of the ACPI root pointer.

        Reserved_kernel = 0, ///< Kernel image.
        Reserved_heap   = 1, ///< Kernel heap.
        Reserved_mmio   = 2, ///< MMIO range reserved by kernel.
      };

      /**
       * Common sub types across all architectures for the Mem_type::Arch type.
       */
      enum Arch_sub_type_common
      {
        Arch_acpi_tables  = 3,  ///< Firmware ACPI tables.
        Arch_acpi_nvs     = 4,  ///< Firmware reserved address space.
      };

    private:
      unsigned long _l, _h;

    public:
      /**
       * Get first memory descriptor.
       *
       * \param kip  Pointer to the kernel info page
       *
       * \return First memory descriptor stored in the kernel info page
       */
      static Mem_desc *first(l4_kernel_info_t *kip) noexcept
      {
        return reinterpret_cast<Mem_desc *>(reinterpret_cast<char *>(kip) + kip->mem_descs);
      }

      static Mem_desc const *first(l4_kernel_info_t const *kip) noexcept
      {
        char const *addr = reinterpret_cast<char const *>(kip) + kip->mem_descs;
        return reinterpret_cast<Mem_desc const *>(addr);
      }

      /**
       * Return number of memory descriptors stored in the kernel info page.
       *
       * \param kip  Pointer to the kernel info page
       *
       * \return Number of memory descriptors in the kernel info page.
       */
      static unsigned long count(l4_kernel_info_t const *kip) noexcept
      {
        return kip->mem_descs_num;
      }

      /**
       * Set number of memory descriptors.
       *
       * \param kip    Pointer to the kernel info page
       * \param count  Number of memory descriptors
       */
      static void count(l4_kernel_info_t *kip, unsigned count) noexcept
      {
        kip->mem_descs_num = count;
      }

      /**
       * Return enumerable list of memory descriptors
       *
       * \param kip  Pointer to the kernel info page.
       */
      static inline cxx::static_vector<Mem_desc const> all(l4_kernel_info_t const *kip)
      {
        return cxx::static_vector<Mem_desc const>(Mem_desc::first(kip),
                                                  Mem_desc::count(kip));
      }

      /**
       * Return enumerable list of memory descriptors
       *
       * \param kip  Pointer to the kernel info page.
       */
      static inline cxx::static_vector<Mem_desc> all(l4_kernel_info_t *kip)
      {
        return cxx::static_vector<Mem_desc>(Mem_desc::first(kip),
                                            Mem_desc::count(kip));
      }

      /**
       * Initialize memory descriptor.
       *
       * \param start  Start address
       * \param end    End address
       * \param t      Memory type
       * \param st     Memory subtype, defaults to 0
       * \param virt   True for virtual memory, false for physical memory,
       *               defaults to physical
       * \param eager  The region shall be eligible for eager mapping in sigma0
       *               or the root task. This is just an optimization to prevent
       *               on-demand paging.
       */
      Mem_desc(unsigned long start, unsigned long end,
               Mem_type t, unsigned char st = 0, bool virt = false,
               bool eager = false) noexcept
      : _l((start & ~0x3ffUL) | (t & 0x0f) | ((st << 4) & 0x0f0)
           | (virt ? 0x0200 : 0x0) | (eager ? 0x100 : 0x0)), _h(end | 0x3ffUL)
      {}

      /**
       * Return start address of memory descriptor.
       *
       * \return Start address of memory descriptor
       */
      unsigned long start() const noexcept { return _l & ~0x3ffUL; }

      /**
       * Return end address of memory descriptor.
       *
       * \return End address of memory descriptor
       */
      unsigned long end() const noexcept { return _h | 0x3ffUL; }

      /**
       * Return size of region described by the memory descriptor.
       *
       * \return Size of the region described by the memory descriptor
       */
      unsigned long size() const noexcept { return end() + 1 - start(); }

      /**
       * Return type of the memory descriptor.
       *
       * \return Type of the memory descriptor
       */
      Mem_type type() const noexcept
      {
        return static_cast<Mem_type>(_l & 0x0f);
      }

      /**
       * Return sub-type of the memory descriptor.
       *
       * \return Sub-type of the memory descriptor
       */
      unsigned char sub_type() const noexcept { return (_l >> 4) & 0x0f; }

      /**
       * Return whether the memory descriptor describes a virtual or
       * physical region.
       *
       * \return True for virtual region, false for physical region.
       */
      unsigned is_virtual() const noexcept { return _l & 0x200; }

      /**
       * Return whether the region shall be eligible for eager mapping in sigma0
       * or the root task.
       */
      unsigned eager_map() const noexcept { return _l & 0x100; }

      /**
       * Set values of a memory descriptor.
       *
       * \param start  Start address
       * \param end    End address
       * \param t      Memory type
       * \param st     Sub-type, defaults to 0
       * \param virt   Virtual or physical memory region, defaults to physical
       * \param eager  The region shall be eligible for eager mapping in sigma0
       *               or the root task. This is just an optimization to prevent
       *               on-demand paging.
       */
      void set(unsigned long start, unsigned long end,
               Mem_type t, unsigned char st = 0, bool virt = false,
               bool eager = false) noexcept
      {
        _l = (start & ~0x3ffUL) | (t & 0x0f) | ((st << 4) & 0x0f0)
             | (virt?0x0200:0x0) | (eager ? 0x0100 : 0x0);

        _h = end | 0x3ffUL;
      }

    };
  };
};
