/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-â
âvi: set net ft=c ts=8 sts=2 sw=2 fenc=utf-8                                :viâ
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ¡
â Copyright 2020 Justine Alexandra Roberts Tunney                              â
â                                                                              â
â Permission to use, copy, modify, and/or distribute this software for         â
â any purpose with or without fee is hereby granted, provided that the         â
â above copyright notice and this permission notice appear in all copies.      â
â                                                                              â
â THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL                â
â WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED                â
â WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE             â
â AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL         â
â DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR        â
â PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER               â
â TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR             â
â PERFORMANCE OF THIS SOFTWARE.                                                â
â âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ£
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
âââââââââââââ ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â Î±cÏÂµÎ±lly pÎ´rÏÎ±blÎµ ÎµxÎµcÂµÏÎ±blÎµ Â§ ibm personal computer                         â
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ*/
#ifndef COSMOPOLITAN_LIBC_RUNTIME_PC_H_
#define COSMOPOLITAN_LIBC_RUNTIME_PC_H_
#include "libc/runtime/e820.internal.h"
#include "libc/runtime/mman.internal.h"

#define BANE              -140737488355328
#define BOOTSIG           0xaa55 /* master boot record signature */
#define PC_BIOS_DATA_AREA 0x400

#define kInterruptFlag (1u << 9)

/* FPU Status Word (x87)
   @see Intel Manual V1 Â§8.1.3
   IE: Invalid Operation âââââââââââââââââ
   DE: Denormalized Operand ââââââââââââââ
   ZE: Zero Divide âââââââââââââââââââââââ
   OE: Overflow Flag âââââââââââââââââââââ
   UE: Underflow Flag ââââââââââââââââââââ
   PE: Precision Flag ââââââââââââââââââââ
   SF: Stack Fault âââââââââââââââââââââââ
   ES: Exception Summary Status ââââââââââ
   C0-3: Condition Codes âââ¬âââââ ââââââââ
   TOP of Stack Pointer ââââââ  â ââââââââ
   B: FPU Busy âââââââââââââ â  â ââââââââ
                          ââââ´âââ¼âââââââââ
                          âââ ââââââââââââ*/
#define FPU_IE 0b0000000000000000000000001
#define FPU_DE 0b0000000000000000000000010
#define FPU_ZE 0b0000000000000000000000100
#define FPU_OE 0b0000000000000000000001000
#define FPU_UE 0b0000000000000000000010000
#define FPU_PE 0b0000000000000000000100000
#define FPU_SF 0b0000000000000000001000000
#define FPU_C0 0b0000000000000000100000000
#define FPU_C1 0b0000000000000001000000000
#define FPU_C2 0b0000000000000010000000000
#define FPU_C3 0b0000000000100000000000000

#define CR0_PE 0x01       /* protected mode enabled */
#define CR0_MP 0x02       /* monitor coprocessor */
#define CR0_EM 0x04       /* no x87 fpu present if set */
#define CR0_TS 0x08       /* task switched x87 */
#define CR0_ET 0x10       /* extension type 287 or 387 */
#define CR0_NE 0x20       /* enable x87 error reporting */
#define CR0_WP 0x00010000 /* write protect read-only pages @pl0 */
#define CR0_AM 0x00040000 /* alignment mask */
#define CR0_NW 0x20000000 /* global write-through cache disable */
#define CR0_CD 0x40000000 /* global cache disable */
#define CR0_PG 0x80000000 /* paging enabled */

#define CR4_VME        0x01       /* virtual 8086 mode extension */
#define CR4_PVI        0x02       /* protected mode virtual interrupts */
#define CR4_TSD        0x04       /* time stamp disable (rdtsc) */
#define CR4_DE         0x08       /* debugging extensions */
#define CR4_PSE        0x10       /* page size extension */
#define CR4_PAE        0x20       /* physical address extension */
#define CR4_MCE        0x40       /* machine check exception */
#define CR4_PGE        0x80       /* page global enabled */
#define CR4_OSFXSR     0x0200     /* enable SSE and fxsave/fxrestor */
#define CR4_OSXMMEXCPT 0x0400     /* enable unmasked SSE exceptions */
#define CR4_LA57       0x1000     /* enable level-5 paging */
#define CR4_VMXE       0x2000     /* enable VMX operations */
#define CR4_SMXE       0x4000     /* enable SMX operations */
#define CR4_FSGSBASE   0x00010000 /* enable *FSBASE and *GSBASE instructions */
#define CR4_PCIDE      0x00020000 /* enable process-context identifiers */
#define CR4_OSXSAVE    0x00040000 /* enable XSAVE */

#define XCR0_X87       0x01
#define XCR0_SSE       0x02
#define XCR0_AVX       0x04
#define XCR0_BNDREG    0x08
#define XCR0_BNDCSR    0x10
#define XCR0_OPMASK    0x20
#define XCR0_ZMM_HI256 0x40
#define XCR0_HI16_ZMM  0x80

#define EFER     0xc0000080 /* extended feature enable register */
#define EFER_SCE 0x01       /* system call extensions */
#define EFER_LME 0x0100     /* long mode enable */
#define EFER_LMA 0x0400     /* long mode active */
#define EFER_NXE 0x0800     /* no-execute enable */

#define GDT_REAL_CODE   8
#define GDT_REAL_DATA   16
#define GDT_LEGACY_CODE 24
#define GDT_LEGACY_DATA 32
#define GDT_LONG_CODE   40
#define GDT_LONG_DATA   48
#define GDT_LONG_TSS    56

#define PIC1         0x20 /* IO base address for master PIC */
#define PIC2         0xA0 /* IO base address for slave PIC */
#define PIC1_CMD     PIC1
#define PIC1_DATA    (PIC1 + 1)
#define PIC2_CMD     PIC2
#define PIC2_DATA    (PIC2 + 1)
#define PIC_EOI      0x20 /* End-of-interrupt command code */
#define PIC_READ_IRR 0x0a /* OCW3 irq ready next CMD read */
#define PIC_READ_ISR 0x0b /* OCW3 irq service next CMD read */

/* Long Mode Paging
   @see Intel Manual V.3A Â§4.1 Â§4.5
                                          IsValid (ignored on CR3) Vâ
     âXD:No Inst. Fetches (if NXE)   IsWritable (ignored on CR3) RWââ
     â                                 Permit User-Mode Access - uâââ
     â                             Page-level Write-Through - PWTââââ
     â                            Page-level Cache Disable - PCDâââââ
     â                          Set if has been read - Accessedââââââ
     â                         Set if has been written - Dirtyâââââââ
     â           IsPage 2MB/1GB (if PDPTE/PDE) or PAT (if PT)ââââââââ
     â                (If this maps page and CR4.PGE) Globalâââââââââ
     â      (If IsPage 2MB/1GB, see Intel V3A Â§ 11.12) PAT  âââââââââ
     â                                                  â   âââââââââ
     â            âââââââââââââââââââââââââââââââââââââââ¤   âââââââââ
     â  Must Be 0ââ Next Page Table Address (!IsPage)   â   âââââââââ
     â           ââââââââââââââââââââââââââââââââââââââââ¤   âââââââââ
     â           ââ Physical Address 4KB                â   âââââââââ
     âââââââââââââââââââââââââââââââââââââââââââ        âignâââââââââ
     ââPKââ ign âââ Physical Address 2MB       â        âââ´ââââââââââ
     ââ  ââ     âââââââââââââââââââââââ        â        ââ ââââââââââ
     ââ  ââ     âââ Phys. Addr. 1GB   â        â        ââ ââââââââââ
     ââ  ââ     âââ                   â        â        ââ ââââââââââ
     6666555555555544444444443333333333222222222211111111110000000000
     3210987654321098765432109876543210987654321098765432109876543210*/
#define PAGE_V    /*                                */ 0b000000000001
#define PAGE_RW   /*                                */ 0b000000000010
#define PAGE_U    /*                                */ 0b000000000100
#define PAGE_PS   /*                                */ 0b000010000000
#define PAGE_G    /*                                */ 0b000100000000
#define PAGE_IGN1 /*                                */ 0b111000000000
#define PAGE_RSRV /* blinkenlights/libc reservation */ 0b001000000000
#define PAGE_GROD /* blinkenlights MAP_GROWSDOWN    */ 0b010000000000
#define PAGE_TA   0x00007ffffffff000
#define PAGE_PA2  0x00007fffffe00000
#define PAGE_IGN2 0x07f0000000000000
#define PAGE_REFC PAGE_IGN2          /* libc reference counting */
#define PAGE_1REF 0x0010000000000000 /* libc reference counting */
#define PAGE_XD   0x8000000000000000

#if !(__ASSEMBLER__ + __LINKER__ + 0)

#define invlpg(p) asm volatile("invlpg\t(%0)" : : "r"(p) : "memory")

struct IdtDescriptor {
  uint16_t offset_1; /* offset bits 0..15 */
  uint16_t selector; /* a code segment selector in GDT or LDT */
  uint8_t ist;       /* bits 0..2 hold stack table offset, rest are zero */
  uint8_t type_attr; /* type and attributes */
  uint16_t offset_2; /* offset bits 16..31 */
  uint32_t offset_3; /* offset bits 32..63 */
  uint32_t zero;     /* reserved */
};

uint64_t *__get_virtual(struct mman *, uint64_t *, int64_t, bool);
uint64_t __clear_page(uint64_t);
uint64_t __new_page(struct mman *);
uint64_t *__invert_memory_area(struct mman *, uint64_t *, uint64_t, uint64_t,
                               uint64_t);
void __map_phdrs(struct mman *, uint64_t *, uint64_t, uint64_t);
void __reclaim_boot_pages(struct mman *, uint64_t, uint64_t);
void __ref_page(struct mman *, uint64_t *, uint64_t);
void __ref_pages(struct mman *, uint64_t *, uint64_t, uint64_t);
void __unref_page(struct mman *, uint64_t *, uint64_t);

forceinline unsigned char inb(unsigned short port) {
  unsigned char al;
  asm volatile("inb\t%1,%0" : "=a"(al) : "dN"(port));
  return al;
}

forceinline void outb(unsigned short port, unsigned char byte) {
  asm volatile("outb\t%0,%1"
               : /* no inputs */
               : "a"(byte), "dN"(port));
}

#define __clear_page(page)                               \
  ({                                                     \
    long di, cx;                                         \
    uintptr_t Page = (uintptr_t)(page);                  \
    asm("rep stosb"                                      \
        : "=D"(di), "=c"(cx), "=m"(*(char(*)[4096])Page) \
        : "0"(Page), "1"(4096), "a"(0));                 \
    Page;                                                \
  })

#define __get_pml4t()                 \
  ({                                  \
    intptr_t cr3;                     \
    asm("mov\t%%cr3,%0" : "=r"(cr3)); \
    (uint64_t *)(BANE + cr3);         \
  })

#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_RUNTIME_PC_H_ */
