// SPDX-License-Identifier: MPL-2.0
#include "irq/gdt.h"
#include "cpu/asm.h"
sh_gdt_GDT_ENTRY_64 sh_gdt_fill_gdt_entry_64(sh_uint32 limit,sh_uint32 base,sh_uint8 access,sh_uint8 flags) {
  sh_gdt_GDT_ENTRY_64 entry;
  entry.limit_low=(sh_uint16)(limit & 0xFFFF);
  entry.base_low=(sh_uint16)(base & 0xFFFF);
  entry.base_mid=(sh_uint8)((base>>16)&0xFF);
  entry.access=access;
  entry.flags_limit_high=(sh_uint8)(((flags&0xF)<<4)|((limit>>16)&0xF));
  entry.base_high=(sh_uint8)((base>>24)&0xFF);
  return entry;
}
sh_gdt_GDT_ENTRY_128 sh_gdt_fill_gdt_entry_128(sh_uint32 limit,sh_uint64 base,sh_uint8 access,sh_uint8 flags) {
  sh_gdt_GDT_ENTRY_128 entry;
  entry.limit_low=(sh_uint16)(limit&0xFFFF);
  entry.base_low=(sh_uint16)(base&0xFFFF);
  entry.base_mid=(sh_uint8)((base>>16)&0xFF);
  entry.access=access;
  entry.flags_limit_high=(sh_uint8)(((flags&0xF)<<4)|((limit>>16)&0xF));
  entry.base_high=(sh_uint8)((base>>24)&0xFF);
  entry.base_upper=(sh_uint32)((base>>32)&0xFFFFFFFF);
  entry.reserved=0;
  return entry;
}
SH_STATUS sh_gdt_fill_gdt(sh_tss_TSS *tss,sh_gdt_GDT *gdt) {
  if (tss==SH_NULLPTR || gdt==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER;
  sh_mem_set_8((sh_uint8 *)gdt,0x0,sizeof(sh_gdt_GDT));
  gdt->null=sh_gdt_fill_gdt_entry_64(0,0,0,0);
  gdt->kernel_code=sh_gdt_fill_gdt_entry_64(0,0,sh_gdt_fill_access_byte(SH_TRUE,0,SH_TRUE,0xA),sh_gdt_fill_flags_byte(SH_FALSE,SH_FALSE,SH_TRUE,SH_FALSE));
  gdt->kernel_data=sh_gdt_fill_gdt_entry_64(0,0,sh_gdt_fill_access_byte(SH_TRUE,0,SH_TRUE,0x2),sh_gdt_fill_flags_byte(SH_FALSE,SH_FALSE,SH_FALSE,SH_FALSE));
  gdt->user_code=sh_gdt_fill_gdt_entry_64(0,0,sh_gdt_fill_access_byte(SH_TRUE,3,SH_TRUE,0xA),sh_gdt_fill_flags_byte(SH_FALSE,SH_FALSE,SH_TRUE,SH_FALSE));
  gdt->user_data=sh_gdt_fill_gdt_entry_64(0,0,sh_gdt_fill_access_byte(SH_TRUE,3,SH_TRUE,0x2),sh_gdt_fill_flags_byte(SH_FALSE,SH_FALSE,SH_FALSE,SH_FALSE));
  gdt->tss=sh_gdt_fill_gdt_entry_128(sizeof(sh_tss_TSS)-1,(sh_uint64)tss,sh_gdt_fill_access_byte(SH_TRUE,0,SH_FALSE,0x9),0);
  return SH_STATUS_SUCCESS;
}
SH_STATUS sh_gdt_fill_gdt_ap(sh_tss_TSS *tss,sh_uint64 tss_count,sh_gdt_GDT_AP *gdt) {
  if (tss==SH_NULLPTR || tss_count==0 || tss_count>256 || gdt==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER;
  sh_mem_set_8((sh_uint8 *)gdt,0x0,sizeof(sh_gdt_GDT));
  gdt->null=sh_gdt_fill_gdt_entry_64(0,0,0,0);
  gdt->kernel_code=sh_gdt_fill_gdt_entry_64(0,0,sh_gdt_fill_access_byte(SH_TRUE,0,SH_TRUE,0xA),sh_gdt_fill_flags_byte(SH_FALSE,SH_FALSE,SH_TRUE,SH_FALSE));
  gdt->kernel_data=sh_gdt_fill_gdt_entry_64(0,0,sh_gdt_fill_access_byte(SH_TRUE,0,SH_TRUE,0x2),sh_gdt_fill_flags_byte(SH_FALSE,SH_FALSE,SH_FALSE,SH_FALSE));
  gdt->user_code=sh_gdt_fill_gdt_entry_64(0,0,sh_gdt_fill_access_byte(SH_TRUE,3,SH_TRUE,0xA),sh_gdt_fill_flags_byte(SH_FALSE,SH_FALSE,SH_TRUE,SH_FALSE));
  gdt->user_data=sh_gdt_fill_gdt_entry_64(0,0,sh_gdt_fill_access_byte(SH_TRUE,3,SH_TRUE,0x2),sh_gdt_fill_flags_byte(SH_FALSE,SH_FALSE,SH_FALSE,SH_FALSE));
  for (sh_iter64 i=0;i<tss_count;i++) {
    gdt->tss[i]=sh_gdt_fill_gdt_entry_128(sizeof(sh_tss_TSS)-1,(sh_uint64)&tss[i],sh_gdt_fill_access_byte(SH_TRUE,0,SH_FALSE,0x9),0);
  }
  return SH_STATUS_SUCCESS;
}
sh_gdt_GDTR sh_gdt_make_gdtr_ap(sh_gdt_GDT_AP *gdt) {
  if (gdt==SH_NULLPTR) return (sh_gdt_GDTR){0};
  sh_gdt_GDTR gdtr;
  gdtr.limit=(sh_uint16)(sizeof(sh_gdt_GDT_AP)-1);
  gdtr.base=(sh_uint64)gdt;
  return gdtr;
}
SH_STATUS sh_gdt_load_gdtr(sh_gdt_GDT *gdt) {
  if (gdt==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER;
  sh_gdt_GDTR gdtr;
  gdtr.limit=(sh_uint16)(sizeof(sh_gdt_GDT)-1);
  gdtr.base=(sh_uint64)gdt;
  sh_asm_lgdt(gdtr);
  return SH_STATUS_SUCCESS;
}
extern SH_STATUS sh_gdt_reload_registers();
sh_gdt_GDT_ENTRY_32 sh_gdt_fill_gdt_entry_32(sh_uint32 limit,sh_uint32 base,sh_uint8 access,sh_uint8 granularity) {
  sh_gdt_GDT_ENTRY_32 entry;
  entry.limit_low=limit&0xFFFF;
  entry.base_low=base&0xFFFF;
  entry.base_middle=(base>>16)&0xFF;
  entry.base_high=(sh_uint8)(base>>24)&0xFF;
  entry.access=access;
  entry.granularity=(granularity&0xF0)|((limit>>16)&0x0F);
  return entry;
}
SH_STATUS sh_gdt_fill_gdt_32(sh_gdt_GDT_32 *gdt_32){
  if (!gdt_32) return SH_STATUS_INVALID_PARAMETER;
  gdt_32->null=(sh_gdt_GDT_ENTRY_32){0};
  gdt_32->code=sh_gdt_fill_gdt_entry_32(0xFFFFF,0x00000000,sh_gdt_fill_access_byte_32(SH_TRUE,0,SH_TRUE,0xA),sh_gdt_fill_granularity_byte_32(SH_TRUE,SH_TRUE,SH_FALSE,SH_FALSE));
  gdt_32->data=sh_gdt_fill_gdt_entry_32(0xFFFFF,0x00000000,sh_gdt_fill_access_byte_32(SH_TRUE,0,SH_TRUE,0x2),sh_gdt_fill_granularity_byte_32(SH_TRUE,SH_TRUE,SH_FALSE,SH_FALSE));
  gdt_32->code_64=sh_gdt_fill_gdt_entry_32(0xFFFFF,0x00000000,sh_gdt_fill_access_byte_32(SH_TRUE,0,SH_TRUE,0xA),sh_gdt_fill_granularity_byte_32(SH_TRUE,SH_FALSE,SH_TRUE,SH_FALSE));
  return SH_STATUS_SUCCESS;
}
