#include <stdint.h>
#include "stdio.h"
#include "fs/vfs.h"

#define MSR_EFER   0xC0000080
#define MSR_STAR   0xC0000081
#define MSR_LSTAR  0xC0000082
#define MSR_SFMASK 0xC0000084

#define EFER_SCE (1 << 0)

static inline void wrmsr(uint32_t msr, uint64_t value)
{
    uint32_t lo = value & 0xFFFFFFFF;
    uint32_t hi = value >> 32;

    asm volatile("wrmsr"
                 :
                 : "c"(msr), "a"(lo), "d"(hi));
}

static inline uint64_t rdmsr(uint32_t msr)
{
    uint32_t lo, hi;
    asm volatile("rdmsr"
                 : "=a"(lo), "=d"(hi)
                 : "c"(msr));
    return ((uint64_t)hi << 32) | lo;
}

void syscall_handler(uint64_t rax, // syscall num
                     uint64_t rdi, // arg1
                     uint64_t rsi, // arg2
                     uint64_t rdx, // arg3
                     uint64_t r10, // arg4
                     uint64_t r8,  // arg5
                     uint64_t r9)  // arg6
{
    switch (rax)
    {
        case 1: // write (int fd, const void *buf, size_t count);
            int c = (char)rdi;
            printf("%c", c);
            break;

        case 2:
            // exit
            break;

        default:
            break;
    }
}

void syscall_init(void)
{
    extern void syscall_entry(void);

    uint64_t kernel_cs = 0x08;
    uint64_t user_cs   = 0x1B;

    uint64_t star =
        ((uint64_t)kernel_cs << 32) |
        ((uint64_t)user_cs << 48);

    wrmsr(MSR_STAR, star);

    wrmsr(MSR_LSTAR, (uint64_t)syscall_entry);

    uint64_t efer = rdmsr(MSR_EFER);
    efer |= (1 << 0); // SYSCALL/SYSRET enable
    wrmsr(MSR_EFER, efer);

    // disable interrupts on syscall entry
    wrmsr(MSR_SFMASK, (1 << 9));
}