// SPDX-License-Identifier: MPL-2.0
#include "memory/pba.h"
#include "memory/pez/pez.h"
#include "kernel/log.h"
SH_STATUS sh_pba_init(sh_pba_PAGE_BLOCK_ALLOCATOR *pba,sh_page_VIRTUAL_ADDRESS start_va,sh_uint64 area_pages_amount,sh_uint64 block_pages) {
  if (pba==SH_NULLPTR) {
    return SH_STATUS_INVALID_PARAMETER;
  }
  if (area_pages_amount%block_pages!=0) {
    return SH_STATUS_INVALID_PARAMETER;
  }
  if (start_va%(SH_PAGE_SIZE*block_pages)!=0) {
    return SH_STATUS_INVALID_PARAMETER;
  }
  pba->start_va=start_va;
  pba->block_pages=block_pages;
  pba->block_count=0;
  pba->total_pages=area_pages_amount;
  pba->max_blocks=area_pages_amount/block_pages;
  return SH_STATUS_SUCCESS;
}
SH_STATUS sh_pba_alloc(sh_pba_PAGE_BLOCK_ALLOCATOR *pba,sh_page_PAGE_TABLE_POOL *ptp,sh_page_VIRTUAL_ADDRESS *ptr) {
  if (pba==SH_NULLPTR || ptr==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER;
  if (pba->block_count==pba->max_blocks) return SH_STATUS_PBA_FULL;
  if (!sh_pez_is_available()) {
    sh_page_PHYSICAL_ADDRESS block_pa;
    SH_STATUS status=sh_page_search_physical_contiguous_block_na(pba->block_pages,&block_pa);
    if (sh_status_error(status)) return status;
    sh_page_VIRTUAL_ADDRESS temp_ptr=pba->start_va+pba->block_count*pba->block_pages*SH_PAGE_SIZE;
    status=sh_page_is_va_range_mapped_ptp(ptp,temp_ptr,pba->block_pages*SH_PAGE_SIZE);
    if (status==SH_STATUS_VA_FULLY_MAPPED || status==SH_STATUS_VA_PARTIALLY_MAPPED) {
      return status;
    } else if (sh_status_error(status)) {
      return status;
    } else if (status!=SH_STATUS_VA_NOT_MAPPED) {
      return status;
    }
    status=sh_page_set_pages_range_bitmap((sh_uint8*)sh_page_get_physical_bitmap_ptr(),sh_page_get_physical_memory_amount_pages(),block_pa/SH_PAGE_SIZE,pba->block_pages,SH_TRUE);
    if (sh_status_error(status)) return status;
    status=sh_page_map_contiguous_pages_range_ptp(ptp,temp_ptr,block_pa,SH_PAGE_RW | SH_PAGE_NX | SH_PAGE_PRESENT,pba->block_pages*SH_PAGE_SIZE);
    if (sh_status_error(status)) {
      SH_STATUS status2=sh_page_set_pages_range_bitmap((sh_uint8*)sh_page_get_physical_bitmap_ptr(),sh_page_get_physical_memory_amount_pages(),block_pa/SH_PAGE_SIZE,pba->block_pages,SH_FALSE);
      if (sh_status_error(status2)) return status2;
      return status;
    }
    *ptr=temp_ptr;
  } else {
    if (sh_pez_get_reference_phys_plane()==SH_NULLPTR) return SH_STATUS_ERROR_PEZ_INITIALIZED;
    sh_page_PHYSICAL_ADDRESS block_pa;
    SH_STATUS status=sh_pez_alloc_physical_pages(sh_pez_get_reference_phys_plane(),(sh_uint32)pba->block_pages,&block_pa);
    if (sh_status_error(status)) return status;
    sh_page_VIRTUAL_ADDRESS temp_ptr=pba->start_va+pba->block_count*pba->block_pages*SH_PAGE_SIZE;
    status=sh_page_is_va_range_mapped_ptp(ptp,temp_ptr,pba->block_pages*SH_PAGE_SIZE);
    if (status==SH_STATUS_VA_FULLY_MAPPED || status==SH_STATUS_VA_PARTIALLY_MAPPED) {
      return status;
    } else if (sh_status_error(status)) {
      return status;
    } else if (status!=SH_STATUS_VA_NOT_MAPPED) {
      return status;
    }
    status=sh_page_map_contiguous_pages_range_ptp(ptp,temp_ptr,block_pa,SH_PAGE_RW | SH_PAGE_NX | SH_PAGE_PRESENT,pba->block_pages*SH_PAGE_SIZE);
    if (sh_status_error(status)) {
      SH_STATUS status2=sh_page_set_pages_range_bitmap((sh_uint8*)sh_page_get_physical_bitmap_ptr(),sh_page_get_physical_memory_amount_pages(),block_pa/SH_PAGE_SIZE,pba->block_pages,SH_FALSE);
      if (sh_status_error(status2)) return status2;
      return status;
    }
    *ptr=temp_ptr;
  }
  pba->block_count++;
  return SH_STATUS_SUCCESS;
}
