// SPDX-License-Identifier: MPL-2.0
#ifndef SH_LIB_PAGE_H
#define SH_LIB_PAGE_H
#include "std/type.h"
#include "std/status.h"
#include "memory/vmem_layout.h"
#define SH_PAGE_KERNEL_PERM_VA_BASE SH_VMEM_LAYOUT_KERNEL_ALLOC_SPACE_VA
#define SH_PAGE_KERNEL_PERM_VA_END SH_VMEM_LAYOUT_KERNEL_ALLOC_SPACE_VA_END
#define SH_PAGE_MEMORY_MAP_VA SH_VMEM_LAYOUT_MEMORY_MAP_VA
#define SH_PAGE_SIZE 4096
#define SH_PAGE_MAX_MEM_COUNT 16ULL*1024*1024*1024*1024
#define SH_PAGE_MAX_PAGES_COUNT (SH_PAGE_MAX_MEM_COUNT/SH_PAGE_SIZE)
#define SH_PAGE_PTP_ALLOCATOR_PAGES_COUNT 4096
#define SH_PAGE_PTP_ALLOCATOR_BITMAP_UINT64 SH_PAGE_PTP_ALLOCATOR_PAGES_COUNT/64
typedef sh_uint32 sh_page_MEMORY_TYPE;
#define SH_PAGE_RESERVED_MEMORY_TYPE (sh_page_MEMORY_TYPE)0
#define SH_PAGE_LOADER_CODE (sh_page_MEMORY_TYPE)1
#define SH_PAGE_LOADER_DATA (sh_page_MEMORY_TYPE)2
#define SH_PAGE_BOOT_SERVICES_CODE (sh_page_MEMORY_TYPE)3
#define SH_PAGE_BOOT_SERVICES_DATA (sh_page_MEMORY_TYPE)4
#define SH_PAGE_RUNTIME_SERVICES_CODE (sh_page_MEMORY_TYPE)5
#define SH_PAGE_RUNTIME_SERVICES_DATA (sh_page_MEMORY_TYPE)6
#define SH_PAGE_CONVENTIONAL_MEMORY (sh_page_MEMORY_TYPE)7
#define SH_PAGE_UNUSABLE_MEMORY (sh_page_MEMORY_TYPE)8
#define SH_PAGE_ACPI_RECLAIM_MEMORY (sh_page_MEMORY_TYPE)9
#define SH_PAGE_ACPI_MEMORY_NVS (sh_page_MEMORY_TYPE)10
#define SH_PAGE_MEMORY_MAPPED_IO (sh_page_MEMORY_TYPE)11
#define SH_PAGE_MEMORY_MAPPED_IO_PORT_SPACE (sh_page_MEMORY_TYPE)12
#define SH_PAGE_PAL_CODE (sh_page_MEMORY_TYPE)13
#define SH_PAGE_PERSISTENT_MEMORY (sh_page_MEMORY_TYPE)14
#define SH_PAGE_RESERVED (sh_page_MEMORY_TYPE)15
typedef sh_uint64 sh_page_PHYSICAL_ADDRESS;
typedef sh_uint64 sh_page_VIRTUAL_ADDRESS;
#define SH_PAGE_NULL_PA (sh_page_PHYSICAL_ADDRESS)0
#define SH_PAGE_NULL_VA (sh_page_VIRTUAL_ADDRESS)0
#define SH_PAGE_PRESENT (1ULL<<0)
#define SH_PAGE_TABLE_FLAGS (1ULL<<1)
#define SH_PAGE_RW (1ULL<<1)
#define SH_PAGE_US (1ULL<<2)
#define SH_PAGE_PWT (1ULL<<3)
#define SH_PAGE_PCD (1ULL<<4)
#define SH_PAGE_ACCESSED (1ULL<<5)
#define SH_PAGE_DIRTY (1ULL<<6)
#define SH_PAGE_PS (1ULL<<7)
#define SH_PAGE_GLOBAL (1ULL<<8)
#define SH_PAGE_NX (1ULL<<63)
// Memory map entry structure.
#pragma pack(1)
typedef struct {
  sh_uint32 type;
  sh_uint64 physical_start;
  sh_uint64 pages_count;
  sh_uint64 attributes;
} sh_page_MEMORY_MAP_ENTRY;
#pragma pack()
// Memory map header structure.
#pragma pack(1)
typedef struct {
  sh_uint8 sig_start[8];
  sh_uint64 entry_count;
  sh_uint64 entry_size;
  sh_uint8 mmap_syntax_version;
} sh_page_MEMORY_MAP_HEADER;
#pragma pack()
// Page table pool structure.
#pragma pack(1)
typedef struct {
  sh_page_PHYSICAL_ADDRESS page_table_pa;
  sh_page_VIRTUAL_ADDRESS page_table_va;
  sh_uint64 ptp_alloc_bitmap[SH_PAGE_PTP_ALLOCATOR_BITMAP_UINT64];
  sh_uint64 ptp_pages_count;
  sh_uint64 ptp_alloc_bitmap_uint64_count;
} sh_page_PAGE_TABLE_POOL;
#pragma pack()
// Memory statistics structure
typedef struct {
  sh_uint64 memory_total_pages; // memory_total is the size of the addressable physical space
  sh_uint64 memory_total_bytes;
  sh_uint64 memory_installed_pages; // memory_installed is the sum of the size of all free regions at kernel boot
  sh_uint64 memory_installed_bytes;
  sh_uint64 free_pages;
  sh_uint64 used_pages;
  double free_ratio;
  double used_ratio;
  sh_uint64 largest_free_block;
  sh_uint64 largest_used_block;
  sh_uint64 free_blocks_count;
  sh_uint64 used_blocks_count;
  sh_uint64 physical_bitmap_size_pages;
  sh_uint64 physical_bitmap_size_bytes;
} sh_page_MEM_STATS;
// Load boot PTP VA. Intended for one usage only.
SH_STATUS sh_page_load_boot_ptp_va(sh_page_VIRTUAL_ADDRESS pt_pool_va);
// Return boot PTP VA.
sh_page_VIRTUAL_ADDRESS sh_page_get_boot_ptp_va();
// Copy memory map provided bootloader into dedicated buffer. Intended for one usage only.
SH_STATUS sh_page_copy_memory_map();
// Check for memory map signatures and read memory map header.
SH_STATUS sh_page_check_memory_map();
// Return the amount of physical memory in pages
sh_uint64 sh_page_get_physical_memory_amount_pages();
// Return the amount of physical memory in bytes
sh_uint64 sh_page_get_physical_memory_amount_bytes();
// Return the first available physical page in physical bitmap.
sh_uint64 sh_page_get_one_page_na();
// Set pages ranges into provided bitmap.
SH_STATUS sh_page_set_pages_range_bitmap(sh_uint8 *bitmap,sh_uint64 page_count_in_bitmap,sh_uint64 page_index,sh_uint64 page_count,sh_bool state);
// Return the status of a page inside the provided bitmap.
static inline sh_bool sh_page_is_allocated(sh_uint8 *bitmap,sh_uint64 page_index) {
  sh_uint64 byte_index=page_index/8;
  sh_uint8 bit_index=page_index%8;
  return (bitmap[byte_index] & (1u<<bit_index))!=0;
}
// Initialize PTP structure.
SH_STATUS sh_page_init_ptp(sh_page_PHYSICAL_ADDRESS ptp_pa,sh_page_VIRTUAL_ADDRESS ptp_va,sh_uint64 initial_fill_level,sh_page_PAGE_TABLE_POOL *page_table_pool);
// Dump provided PTP bitmap, intented for debug.
SH_STATUS sh_page_dump_ptp_bitmap(sh_page_PAGE_TABLE_POOL *ptp);
// Allocate one page from a PTP internal bitmap.
sh_page_PHYSICAL_ADDRESS sh_page_ptp_alloc_one_page(sh_page_PAGE_TABLE_POOL *pt_pool);
// Convert a PA provided by any layer of the PTP into a valid VA. Return 0 if PA isn't in expected range.
static inline sh_uint64 *sh_page_ptp_pa_to_va(sh_page_PAGE_TABLE_POOL *ptp,sh_uint64 pa) {
  sh_uint64 base=ptp->page_table_pa;
  sh_uint64 size=ptp->ptp_pages_count*4096;
  if (pa<base || pa>=base+size) return 0;
  return (sh_uint64 *)(ptp->page_table_va+(pa-base));
}
// Map one physical page to VA to provided PTP.
SH_STATUS sh_page_map_one_page_ptp(sh_page_PAGE_TABLE_POOL *ptp,sh_page_VIRTUAL_ADDRESS va,sh_page_PHYSICAL_ADDRESS pa,sh_uint64 flags);
// Return according SH_STATUS if provided VA is mapped inside provided PTP.
SH_STATUS sh_page_is_va_mapped_ptp(sh_page_PAGE_TABLE_POOL *ptp,sh_page_VIRTUAL_ADDRESS va);
// Return according SH_STATUS if provided VA is mapped inside provided PTP for all the range provided
SH_STATUS sh_page_is_va_range_mapped_ptp(sh_page_PAGE_TABLE_POOL *ptp,sh_page_VIRTUAL_ADDRESS va,sh_uint64 size_bytes);
// Search for an available amount of virtual memory inside provided range with the provided size
SH_STATUS sh_page_search_available_va_range(sh_page_PAGE_TABLE_POOL *ptp,sh_page_VIRTUAL_ADDRESS range_base,sh_page_VIRTUAL_ADDRESS range_size_bytes,sh_uint64 size_bytes,sh_page_VIRTUAL_ADDRESS *address_found);
// Map a range of pages from PA to VA. Both virtual and physic area has to be continuous. VAs availability is checked, not physical pages availability.
SH_STATUS sh_page_map_contiguous_pages_range_ptp(sh_page_PAGE_TABLE_POOL *ptp,sh_page_VIRTUAL_ADDRESS va,sh_page_PHYSICAL_ADDRESS pa,sh_uint64 flags,sh_uint64 size_bytes);
// Search for an available amount of pages inside physical bitmap
SH_STATUS sh_page_search_physical_contiguous_block_na(sh_uint64 pages_needed,sh_page_PHYSICAL_ADDRESS *pa);
// Allocate the corresponding amount of pages to size_bytes.
SH_STATUS sh_page_alloc_contiguous(sh_page_PAGE_TABLE_POOL *ptp,sh_uint64 size_bytes,sh_page_VIRTUAL_ADDRESS* va);
// Allocate the corresponding amount of pages to size_bytes. Provide support for custom flags and VA range search.
SH_STATUS sh_page_alloc_contiguous_extended(sh_page_PAGE_TABLE_POOL *ptp,sh_uint64 size_bytes,sh_page_VIRTUAL_ADDRESS* va,DEFAULT sh_uint64 flags,DEFAULT sh_page_VIRTUAL_ADDRESS va_range_start,DEFAULT sh_uint64 va_range_size_bytes);
// Unmap one page from a PTP, assume VA is mapped. Does not unallocate associed physical page
SH_STATUS sh_page_unmap_one_page_ptp(sh_page_PAGE_TABLE_POOL *ptp,sh_page_VIRTUAL_ADDRESS va);
// Map a range of pages to VA. Both virtual and physic area has to be continuous. VAs mapping is checked, not physical pages occupation.
SH_STATUS sh_page_unmap_contiguous_pages_range_ptp(sh_page_PAGE_TABLE_POOL *ptp,sh_page_VIRTUAL_ADDRESS va,sh_uint64 size_bytes);
// Convert a VA allocated in a PTP into his equivalent PA by searching inside the PTP
SH_STATUS sh_page_ptp_va_to_pa(sh_page_PAGE_TABLE_POOL *ptp,sh_page_VIRTUAL_ADDRESS va,sh_page_PHYSICAL_ADDRESS *pa);
// Unalloc one page from a VA. Check if VA is mapped or not. PA is calculted trough searching in PTP
SH_STATUS sh_page_unalloc_one_page(sh_page_PAGE_TABLE_POOL *ptp,sh_page_VIRTUAL_ADDRESS va);
// Unalloc a range of virtually contiguous pages. Check if the entire range is allocated before unallocating anything.
SH_STATUS sh_page_unalloc_contiguous(sh_page_PAGE_TABLE_POOL *ptp,sh_page_VIRTUAL_ADDRESS va,sh_uint64 size_bytes);
// Parse memory map, set all non usable pages into physical bitmap and compute available amount of physical memory. Intended for one usage only.
SH_STATUS sh_page_analyse_memory_map(sh_page_PAGE_TABLE_POOL *ptp);
// Return physical bitmap pointer
sh_page_VIRTUAL_ADDRESS sh_page_get_physical_bitmap_ptr();
// Get physical memory statistics
SH_STATUS sh_page_get_memory_stats(sh_page_MEM_STATS *mem_stats);
// Return total installed memory in bytes
sh_uint64 sh_page_get_installed_memory_bytes();
#endif
