// SPDX-License-Identifier: MPL-2.0
#ifndef SH_LIB_GDT_H
#define SH_LIB_GDT_H
#include "std/status.h"
#include "std/type.h"
#include "irq/tss.h"
// Long mode GDT part
// GDT entry 64 bits structure
#pragma pack(1)
typedef struct {
  sh_uint16 limit_low;
  sh_uint16 base_low;
  sh_uint8 base_mid;
  sh_uint8 access;
  sh_uint8 flags_limit_high;
  sh_uint8 base_high;
} sh_gdt_GDT_ENTRY_64;
#pragma pack()
// GDT entry 128 bits (special TSS) structure
#pragma pack(1)
typedef struct {
  sh_uint16 limit_low;
  sh_uint16 base_low;
  sh_uint8 base_mid;
  sh_uint8 access;
  sh_uint8 flags_limit_high;
  sh_uint8 base_high;
  sh_uint32 base_upper;
  sh_uint32 reserved;
} sh_gdt_GDT_ENTRY_128;
#pragma pack()
// GDTR structure
#pragma pack(1)
typedef struct {
  sh_uint16 limit;
  sh_uint64 base;
} sh_gdt_GDTR;
#pragma pack()
// GDT structure for bootstrap core
#pragma pack(1)
typedef struct {
  sh_gdt_GDT_ENTRY_64 null;
  sh_gdt_GDT_ENTRY_64 kernel_code;
  sh_gdt_GDT_ENTRY_64 kernel_data;
  sh_gdt_GDT_ENTRY_64 user_code;
  sh_gdt_GDT_ENTRY_64 user_data;
  sh_gdt_GDT_ENTRY_128 tss;
} sh_gdt_GDT;
#pragma pack()
// GDT structure for all APs
#pragma pack(1)
typedef struct {
  sh_gdt_GDT_ENTRY_64 null;
  sh_gdt_GDT_ENTRY_64 kernel_code;
  sh_gdt_GDT_ENTRY_64 kernel_data;
  sh_gdt_GDT_ENTRY_64 user_code;
  sh_gdt_GDT_ENTRY_64 user_data;
  sh_gdt_GDT_ENTRY_128 tss[256];
} sh_gdt_GDT_AP;
#pragma pack()
// Generate access byte
inline sh_uint8 sh_gdt_fill_access_byte(sh_bool present,sh_uint8 dpl,sh_bool descriptor_type,sh_uint8 segment_type) {
  return ((present?1:0)<<7)|((dpl&0x3)<<5)|((descriptor_type?1:0)<<4)|(segment_type&0xF);
}
// Generate flags byte (put all 4 bits in bit 0-3 of the returned byte)
inline sh_uint8 sh_gdt_fill_flags_byte(sh_bool g,sh_bool db,sh_bool l,sh_bool avl) {
  return ((g?1:0)<<3)|((db?1:0)<<2)|((l?1:0)<<1)|((avl?1:0)<<0);
}
// Fill GDT entry 64 bits, expect flags as sh_gdt_fill_flags_byte would return it
sh_gdt_GDT_ENTRY_64 sh_gdt_fill_gdt_entry_64(sh_uint32 limit,sh_uint32 base,sh_uint8 access,sh_uint8 flags);
// Fill GDT entry 128 bits, expect flags as sh_gdt_fill_flags_byte would return it
sh_gdt_GDT_ENTRY_128 sh_gdt_fill_gdt_entry_128(sh_uint32 limit,sh_uint64 base,sh_uint8 access,sh_uint8 flags);
// Fill GDT with provided TSS
SH_STATUS sh_gdt_fill_gdt(sh_tss_TSS *tss,sh_gdt_GDT *gdt);
// Fill APs common GDT with provided TSS array
SH_STATUS sh_gdt_fill_gdt_ap(sh_tss_TSS *tss,sh_uint64 tss_count,sh_gdt_GDT_AP *gdt);
// Generate GDTR for AP GDT
sh_gdt_GDTR sh_gdt_make_gdtr_ap(sh_gdt_GDT_AP *gdt);
// Fill and load GDTR
SH_STATUS sh_gdt_load_gdtr(sh_gdt_GDT *gdt);
// Reload registers
SH_STATUS sh_gdt_reload_registers();
// 16/32 bits mode GDT part
// GDT entry 32 bits structure
#pragma pack(1)
typedef struct {
  sh_uint16 limit_low;
  sh_uint16 base_low;
  sh_uint8 base_middle;
  sh_uint8 access;
  sh_uint8 granularity;
  sh_uint8 base_high;
} sh_gdt_GDT_ENTRY_32;
#pragma pack()
// GDT 32 bits structure
#pragma pack(1)
typedef struct {
  sh_gdt_GDT_ENTRY_32 null;
  sh_gdt_GDT_ENTRY_32 code_64;
  sh_gdt_GDT_ENTRY_32 code;
  sh_gdt_GDT_ENTRY_32 data;
} sh_gdt_GDT_32;
#pragma pack()
// Generate access byte
inline sh_uint8 sh_gdt_fill_access_byte_32(sh_bool present,sh_uint8 dpl,sh_bool descriptor_type,sh_uint8 segment_type) {
  sh_uint8 access=0;
  access|=(present?1:0)<<7;
  access|=(dpl&0x3)<<5;
  access|=(descriptor_type?1:0)<<4;
  access|=(segment_type&0xF);
  return access;
}
// Generate granularity byte
inline sh_uint8 sh_gdt_fill_granularity_byte_32(sh_bool granularity,sh_bool db,sh_bool l,sh_bool avl) {
  sh_uint8 g=0;
  g|=(granularity?1:0)<<7;
  g|=(db?1:0)<<6;
  g|=(l?1:0)<<5;
  g|=(avl?1:0)<<4;
  return g;
}
// Fill GDT entry 32 bits, expect granularity as sh_gdt_fill_granularity_byte_32 would return it
sh_gdt_GDT_ENTRY_32 sh_gdt_fill_gdt_entry_32(sh_uint32 limit,sh_uint32 base,sh_uint8 access,sh_uint8 granularity);
// Fill GDT 32 bits
SH_STATUS sh_gdt_fill_gdt_32(sh_gdt_GDT_32 *gdt_32);
#endif
