#ifndef HAL_VMM_H_
#define HAL_VMM_H_

#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include "compiler/attr.h"

#define VIRT(X) ((void *)(BOOT_INFO.hhdm_off + (uint64_t)(X)))

typedef struct VasRange {
  struct VasRange *next;

  uint8_t *virtstart;
  uint8_t *physstart;
  size_t size;
  uint8_t pgflags;
} PACKED VasRange;

enum {
  HAL_PG_PRESENT = 1<<0,
  HAL_PG_RW      = 1<<1,
  HAL_PG_USER    = 1<<2,
};

typedef struct {
  uint16_t pml4;
  uint16_t pml3;
  uint16_t pml2;
  uint16_t pml1;
} PACKED PgIndex;

typedef struct {
  bool present: 1;
  bool rw: 1;
  bool user: 1;
  bool writethrough: 1;
  bool cachedisabled: 1;
  bool accessed: 1;
  bool dirty: 1;
  bool hugepage: 1;
  bool global: 1;
  uint8_t avail: 3;
  uint64_t addr: 40;
  uint16_t osdef: 11;
  bool nx: 1;
} PACKED Pte;

typedef struct {
  Pte ents[512];
} PACKED PgTable;

extern uint64_t KERNEL_CR3;

void hal_vmm_init(void);
void hal_vmm_unmap_page(uint64_t cr3phys, uint64_t virtaddr);
void hal_vmm_map_page(uint64_t cr3phys, uint64_t virtaddr, uint64_t physaddr, uint32_t flags);
uint64_t hal_vmm_current_cr3(void);
void hal_vmm_map_range(uint64_t cr3phys, void *virtstart, void *physstart, size_t size, uint32_t flags);
void hal_vmm_unmap_range(uint64_t cr3phys, void *virtstart, void *physstart, size_t size);
uint64_t hal_vmm_userproc_pml4_phys();

#endif // HAL_VMM_H_
